TPCCLIB
Loading...
Searching...
No Matches
regfur.c
Go to the documentation of this file.
1
9/******************************************************************************/
10#include "tpcclibConfig.h"
11/******************************************************************************/
12#include <stdio.h>
13#include <stdlib.h>
14#include <math.h>
15#include <string.h>
16#include <time.h>
17/******************************************************************************/
18#include "libtpcmisc.h"
19#include "libtpcmodel.h"
20#include "libtpccurveio.h"
21#include "libtpcmodext.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "Calculates Fractional Uptake Rate (FUR) or FUR-based Metabolic Rate (MR)",
27 "from regional PET TTACs. Information on FUR in:",
28 "https://www.turkupetcentre.net/petanalysis/model_fur.html",
29 " ",
30 "Usage: @P [Options] inputfile ttacfile starttime endtime resultfile",
31 " ",
32 "FUR calculation start and stop time must be entered in minutes;",
33 "set both to zero to use the whole time range from TTAC data.",
34 " ",
35 "Options:",
36 " -Ca=<value>",
37 " Concentration of native substrate in arterial plasma (mM),",
38 " for example plasma glucose in [18F]FDG studies.",
39 " With this option the metabolic rate (umol/(min*100 g)) is calculated.",
40 " -LC=<value>",
41 " Lumped Constant in MR calculation; default is 1.0.",
42 " -density=<value>",
43 " Tissue density in MR calculation; default is 1.0 g/ml.",
44 " -curve=<filename>",
45 " FUR as a function of time is written in specified file; this can be",
46 " used to study the time-dependence of FUR estimates.",
47 " -it=<Time (min)>",
48 " Input AUC is normally calculated from 0 to the middle time of FUR",
49 " calculation time; in special cases this option can be used to",
50 " calculate it from 0 to the specified time instead.",
51 " -deriv[ative]",
52 " Tentative option for calculating FUR as ratio of tissue derivative and",
53 " plasma. This does not affect the FUR curve made with option -curve.",
54 " -stdoptions", // List standard options like --help, -v, etc
55 " ",
56 "Example 1. Calculation of FUR from dynamic PET data from 45 to 60 min:",
57 " @P ua2918ap.kbq ua2918dy1.dft 45 60 ua2918fur.res",
58 " ",
59 "Example 2. Calculation of glucose uptake, when tissue density is 1.04,",
60 "plasma glucose concentration is 5.2 mM, lumped constant is 0.52, from",
61 "a static (one frame) scan:",
62 " @P -Ca=5.2 -LC=0.52 -d=1.04 a864ap.kbq a864dy1.dft 0 0 a864mrglu.res",
63 " ",
64 "FUR and MR results are saved in result file format by default, but if",
65 "filename extension is set to .dft, .csv, .dat, or .html, results are saved",
66 "in those formats.",
67 " ",
68 "The unit of FUR is (mL plasma)/(min*(mL tissue)) by default, and",
69 "umol/(min*100 g) if metabolic rate is calculated.",
70 " ",
71 "See also: patlak, dftinteg, taccalc, tactime, dftsuv, rescoll, imgfur",
72 " ",
73 "Keywords: TAC, modelling, FUR, retention index, irreversible uptake",
74 0};
75/*****************************************************************************/
76
77/*****************************************************************************/
78/* Turn on the globbing of the command line, since it is disabled by default in
79 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
80 In Unix&Linux wildcard command line processing is enabled by default. */
81/*
82#undef _CRT_glob
83#define _CRT_glob -1
84*/
85int _dowildcard = -1;
86/*****************************************************************************/
87
88/*****************************************************************************/
89#ifndef DEFAULT_LC
90#define DEFAULT_LC 1.00
91#endif
92#ifndef DEFAULT_DENSITY
93#define DEFAULT_DENSITY 1.00
94#endif
95/*****************************************************************************/
96
97/*****************************************************************************/
101int main(int argc, char **argv)
102{
103 int ai, help=0, version=0, verbose=1;
104 int ret;
105 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX];
106 char curfile[FILENAME_MAX], outfile[FILENAME_MAX], tmp[1024], *cptr;
107 DFT input, auc, pet, avg;
108 RES res;
109 double startTime=-1.0, endTime=-1.0, aucTime=-1.0;
110 double LC=-1.0, Ca=-1.0, density=-1.0;
111 int fur_mode=0; // 0=traditional Ct/iCp, 1=derivative dCt/Cp
112
113
114 /*
115 * Get arguments
116 */
117 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
118 inpfile[0]=petfile[0]=outfile[0]=curfile[0]=(char)0;
119 dftInit(&input); dftInit(&auc); dftInit(&pet); dftInit(&avg); resInit(&res);
120 /* Get options */
121 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
122 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
123 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
124 if(strncasecmp(cptr, "CA=", 3)==0) {
125 Ca=atof_dpi(cptr+3); if(Ca>0.0) continue;
126 } else if(strncasecmp(cptr, "LC=", 3)==0) {
127 LC=atof_dpi(cptr+3); if(LC>0.0) continue;
128 } else if(strncasecmp(cptr, "D=", 2)==0) {
129 density=atof_dpi(cptr+2); if(density>0.0) continue;
130 } else if(strncasecmp(cptr, "DENSITY=", 8)==0) {
131 density=atof_dpi(cptr+8); if(density>0.0) continue;
132 } else if(strncasecmp(cptr, "DERIVATIVE", 5)==0) {
133 fur_mode=1; continue;
134 } else if(strncasecmp(cptr, "CURVE=", 6)==0 && strlen(cptr)>6) {
135 strcpy(curfile, cptr+6); continue;
136 } else if(strncasecmp(cptr, "IT=", 3)==0) {
137 aucTime=atof_dpi(cptr+3); if(aucTime>0.0) continue;
138 }
139 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
140 return(1);
141 } else break;
142
143 /* Print help or version? */
144 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
145 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
146 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
147
148 /* Process other arguments, starting from the first non-option */
149 for(; ai<argc; ai++) {
150 if(!inpfile[0]) {
151 strcpy(inpfile, argv[ai]); continue;
152 } else if(!petfile[0]) {
153 strcpy(petfile, argv[ai]); continue;
154 } else if(startTime<0.0) {
155 startTime=atof_dpi(argv[ai]); if(startTime<0.0) startTime=0.0;
156 continue;
157 } else if(endTime<0.0) {
158 endTime=atof_dpi(argv[ai]); if(endTime<0.0) endTime=0.0;
159 continue;
160 } else if(!outfile[0]) {
161 strcpy(outfile, argv[ai]); continue;
162 }
163 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
164 return(1);
165 }
166
167 /* Is something missing? */
168 if(!outfile[0]) {
169 fprintf(stderr, "Error: missing result file name.\n");
170 return(1);
171 }
172
173 /* If MR will be calculated, set defaults and give warnings as necessary */
174 if(Ca>0.0) {
175 if(LC<0.0) {
176 LC=DEFAULT_LC;
177 fprintf(stderr, "Warning: LC not set, using default %g\n", LC);
178 }
179 if(density<0.0) {
180 density=DEFAULT_DENSITY;
181 fprintf(stderr, "Warning: tissue density not set, using default %g\n",
182 density);
183 }
184 } else { /* Warnings if density or LC set when MR will not be calculated */
185 if(LC>0.0) fprintf(stderr, "Warning: LC was set but is not used.\n");
186 if(density>0.0)
187 fprintf(stderr, "Warning: tissue density was set but is not used.\n");
188 }
189 /* Check the time range */
190 if(endTime<startTime) {
191 fprintf(stderr, "Error: invalid time range.\n");
192 return(1);
193 }
194 if(startTime==endTime && startTime>0.5) {
195 startTime-=0.5; endTime+=0.5;
196 }
197
198
199 /* In verbose mode print arguments and options */
200 if(verbose>1) {
201 printf("inpfile := %s\n", inpfile);
202 printf("petfile := %s\n", petfile);
203 printf("outfile := %s\n", outfile);
204 printf("curfile := %s\n", curfile);
205 if(endTime>0.0) {
206 printf("startTime := %g min\n", startTime);
207 printf("endTime := %g min\n", endTime);
208 }
209 if(aucTime>0.0) {
210 printf("aucTime := %g min\n", aucTime);
211 }
212 if(Ca>0.0) {
213 printf("Ca := %g\n", Ca);
214 printf("LC := %g\n", LC);
215 printf("density := %g\n", density);
216 }
217 printf("fur_mode := %d\n", fur_mode);
218 fflush(stdout);
219 }
220
221
222 /*
223 * Read regional data
224 */
225 if(verbose>1) printf("reading regional data %s\n", petfile);
226 if(dftRead(petfile, &pet)) {
227 fprintf(stderr, "Error in reading '%s': %s\n", petfile, dfterrmsg);
228 return(2);
229 }
230 if(dft_nr_of_NA(&pet)>0) { // check if file contains NAs (missing values)
231 fprintf(stderr, "Error: missing values in %s\n", petfile);
232 dftEmpty(&pet); return(2);
233 }
234
235 /* Sort the samples by time in case data is catenated from several curves */
236 (void)dftSortByFrame(&pet);
237
238 /* Make sure that there is no overlap in frame times */
239 if(pet.timetype==DFT_TIME_STARTEND) {
240 if(verbose>2) fprintf(stdout, "checking frame overlap in %s\n", petfile);
241 ret=dftDeleteFrameOverlap(&pet);
242 if(ret) {
243 fprintf(stderr, "Error: %s has overlapping frame times.\n", petfile);
244 dftEmpty(&pet); return(2);
245 }
246 }
247
248 /* Set time unit to min */
249 ret=dftTimeunitConversion(&pet, TUNIT_MIN);
250 if(ret) fprintf(stderr, "Warning: check that regional data times are in minutes.\n");
251 /* If user did not specify start and end times, then get those from data */
252 if(endTime<=1.0E-02) {
253 if(pet.timetype==DFT_TIME_STARTEND) {
254 startTime=pet.x1[0]; endTime=pet.x2[pet.frameNr-1];
255 } else {
256 startTime=pet.x[0]; endTime=pet.x[pet.frameNr-1];
257 }
258 if(verbose>1) {
259 printf("startTime := %g min\n", startTime);
260 printf("endTime := %g min\n", endTime);
261 }
262 }
263
264
265 /*
266 * Calculate the average (or slope) over specified time range
267 */
268 if(verbose>1) printf("calculating average\n");
269 ret=dftTimeIntegral(&pet, startTime, endTime, &avg, 1, tmp, verbose-3);
270 if(ret!=0) {
271 fprintf(stderr, "Error: %s.\n", tmp);
272 if(verbose>2)
273 printf("dftTimeIntegral(pet, %g, %g, avg, 1, tmp) := %d\n", startTime, endTime, ret);
274 dftEmpty(&pet); dftEmpty(&avg); return(3);
275 }
276 if(verbose>1) fprintf(stdout, "%s.\n", tmp);
277 if(fur_mode==1) {
278 int ri;
279 double k, ksd, b, bsd, r, ysd;
280 if(verbose>0) {
281 fprintf(stdout, "calculating slope\n"); fflush(stdout);}
282 for(ri=0; ri<pet.voiNr; ri++) {
283 ret=pearson4(pet.x, pet.voi[ri].y, pet.frameNr, startTime, endTime,
284 &k, &ksd, &b, &bsd, &r, &ysd);
285 if(ret!=0) break;
286 avg.voi[ri].y[0]=k;
287 }
288 if(ret!=0) {
289 fprintf(stderr, "Error: tissue slope calculation not successful.\n");
290 if(verbose>1) printf(" ret := %d\n", ret);
291 dftEmpty(&pet); dftEmpty(&avg); return(3);
292 }
293 }
294 if(verbose>2) {
295 printf("Regional tissue value or derivative\n");
296 for(int ri=0; ri<avg.voiNr; ri++)
297 printf("%s : %g\n", avg.voi[ri].name, avg.voi[ri].y[0]);
298 }
299
300 /*
301 * Read input data
302 */
303 if(verbose>1) printf("Reading input file %s\n", inpfile);
304 if(dftRead(inpfile, &input)) {
305 fprintf(stderr, "Error in reading '%s': %s\n", inpfile, dfterrmsg);
306 dftEmpty(&pet); dftEmpty(&avg); return(4);
307 }
308 if(input.voiNr>1) {
309 fprintf(stderr, "Warning: only first TAC is used as input.\n");
310 input.voiNr=1;
311 }
312 if(dft_nr_of_NA(&input)>0) { // check if file contains NAs (missing values)
313 fprintf(stderr, "Error: missing values in %s\n", inpfile);
314 dftEmpty(&pet); dftEmpty(&avg); dftEmpty(&input); return(4);
315 }
316
317 /* Sort the samples by time in case data is catenated from several curves */
318 (void)dftSortByFrame(&pet);
319
320 /* Set time unit to min */
321 ret=dftTimeunitConversion(&input, TUNIT_MIN);
322 if(ret) fprintf(stderr, "Warning: check that input times are in minutes.\n");
323
324 /*
325 * Check the regional and plasma TAC concentration units
326 */
327 ret=dftUnitConversion(&input, dftUnitId(pet.unit));
328 if(ret!=0) {
329 fprintf(stderr, "Warning: check the units of input and regional data.\n");
330 }
331
332
333 /*
334 * Calculate and save FUR curve, if required;
335 * this is not used in calculation of regional FUR
336 */
337 if(curfile[0]) {
338 if(verbose>1) {printf("calculating FUR curve\n"); fflush(stdout);}
339 DFT fur;
340 int fi, ri;
341 dftInit(&fur); ret=dftdup(&pet, &fur);
342 if(ret!=0) {
343 fprintf(stderr, "Warning: cannot allocate memory for FUR curves\n");
344 goto failed;
345 }
346 fur.frameNr=0;
347 for(fi=0; fi<pet.frameNr; fi++) {
348 if(pet.x[fi]<startTime || pet.x[fi]>endTime) continue;
349 /* AUC 0-t for each frame */
350 if(pet.x[fi]>0.0) {
351 ret=dftTimeIntegral(&input, 0.0, pet.x[fi], &auc, 0, tmp, verbose-4);
352 if(ret!=0) {fprintf(stderr, "Warning (%d): %s\n", ret, tmp); break;}
353 if(auc.voi[0].y[0]<1.0E-006) continue;
354 } else continue;
355 fur.x1[fur.frameNr]=pet.x1[fi]; fur.x2[fur.frameNr]=pet.x2[fi];
356 fur.x[fur.frameNr]=pet.x[fi]; fur.w[fur.frameNr]=pet.w[fi];
357 /* Divide each region by AUC */
358 for(ri=0; ri<fur.voiNr; ri++)
359 fur.voi[ri].y[fur.frameNr]= pet.voi[ri].y[fi] / auc.voi[0].y[0];
360 fur.frameNr++;
361 }
362 if(ret!=0) goto failed;
363 /* Write FUR curve */
364 dftUnitToDFT(&fur, CUNIT_PER_MIN);
365 dftSetComments(&fur);
366 if(dftWrite(&fur, curfile))
367 fprintf(stderr, "Error in writing %s: %s\n", curfile, dfterrmsg);
368 failed:
369 dftEmpty(&fur); fflush(stderr);
370 }
371
372
373 /*
374 * Calculate input integral from 0 to PET middle time point,
375 * or input value at PET middle time point
376 */
377 if(aucTime<=0.0) aucTime=0.5*(startTime+endTime);
378 if(fur_mode==0)
379 ret=dftTimeIntegral(&input, 0.0, aucTime, &auc, 0, tmp, verbose-3);
380 else
381 ret=dftTimeIntegral(&input, startTime, endTime, &auc, 1, tmp, verbose-3);
382 if(ret!=0) {
383 fprintf(stderr, "Error (%d): %s\n", ret, tmp);
384 dftEmpty(&avg); dftEmpty(&input); dftEmpty(&auc); dftEmpty(&pet);
385 return(6);
386 }
387 if(verbose>1) {
388 if(fur_mode==0)
389 printf("AUC[%g-%g] := %g\n", auc.x1[0], auc.x2[0], auc.voi[0].y[0]);
390 else
391 printf("Input[%g-%g] := %g\n", auc.x1[0], auc.x2[0], auc.voi[0].y[0]);
392 }
393 /* Original input curve is not needed anymore */
394 dftEmpty(&input);
395
396
397 /*
398 * Divide average regional data by input AUC, or
399 * regional concentration derivative by input
400 */
401 for(int ri=0; ri<avg.voiNr; ri++) avg.voi[ri].y[0]/=auc.voi[0].y[0];
402 dftUnitToDFT(&avg, CUNIT_PER_MIN);
403
404 /* Input AUC is not needed anymore */
405 dftEmpty(&auc);
406
407
408 /*
409 * Calculate metabolic rate, if necessary
410 */
411 if(Ca>0.0) {
412 double MRf;
413 MRf=100.*Ca/(density*LC);
414 if(verbose>1)
415 fprintf(stdout, "converting FUR to metabolic rate with factor %g\n", MRf);
416 for(int ri=0; ri<avg.voiNr; ri++) avg.voi[ri].y[0]*=MRf;
417 dftUnitToDFT(&avg, CUNIT_UMOL_PER_MIN_PER_100G);
418 }
419
420
421 /*
422 * Save FUR/MR data
423 */
424 ret=backupExistingFile(outfile, NULL, tmp); if(ret!=0) {
425 fprintf(stderr, "Error: %s\n", tmp);
426 dftEmpty(&avg); dftEmpty(&pet); return(11);
427 }
428 if(verbose>2) printf("%s\n", tmp);
429 /* If filename extension is .dft, .csv, or .dat, then save in DFT, CSV-UK, or
430 simple format, respectively, otherwise as RES */
431 cptr=strrchr(outfile, '.');
432 if(cptr!=NULL && strcasecmp(cptr, ".DFT")==0) {
433 dftSetComments(&avg);
434 if(dftWrite(&avg, outfile)) {
435 fprintf(stderr, "Error in writing %s: %s\n", outfile, dfterrmsg);
436 dftEmpty(&avg); dftEmpty(&pet); return(12);
437 }
438 } else if(cptr!=NULL && strcasecmp(cptr, ".CSV")==0) {
441 if(dftWrite(&avg, outfile)) {
442 fprintf(stderr, "Error in writing %s: %s\n", outfile, dfterrmsg);
443 dftEmpty(&avg); dftEmpty(&pet); return(12);
444 }
445 } else if(cptr!=NULL && strcasecmp(cptr, ".DAT")==0) {
448 if(dftWrite(&avg, outfile)) {
449 fprintf(stderr, "Error in writing %s: %s\n", outfile, dfterrmsg);
450 dftEmpty(&avg); dftEmpty(&pet); return(12);
451 }
452 } else {
453 /* Copy DFT data into RES */
454 ret=dftToResult(&avg, &res, tmp);
455 if(ret!=0) {
456 fprintf(stderr, "Error: %s.\n", tmp);
457 dftEmpty(&avg); resEmpty(&res); dftEmpty(&pet); return(13);
458 }
459 /* Set more header information */
460 tpcProgramName(argv[0], 1, 1, res.program, 256);
461 cptr=strrchr(petfile, '/'); if(cptr==NULL) cptr=strrchr(petfile, '\\');
462 if(cptr==NULL) cptr=petfile; else cptr++; strcpy(res.datafile, cptr);
463 cptr=strrchr(inpfile, '/'); if(cptr==NULL) cptr=strrchr(inpfile, '\\');
464 if(cptr==NULL) cptr=inpfile; else cptr++; strcpy(res.plasmafile, cptr);
465 if(Ca>0.0) {
466 res.concentration=Ca;
467 res.lc=LC;
468 res.density=density;
469 }
470 if(Ca>0.0) strcpy(res.parname[0], "MR"); else strcpy(res.parname[0], "FUR");
471 /* Save RES */
472 ret=resWrite(&res, outfile, verbose-3);
473 if(ret) {
474 fprintf(stderr, " Error (%d) in writing file %s\n", ret, outfile);
475 dftEmpty(&avg); resEmpty(&res); dftEmpty(&pet); return(11);
476 }
477 resEmpty(&res);
478 }
479 if(verbose>0) {
480 if(Ca<=0.0) fprintf(stdout, "FUR(s) saved in %s.\n", outfile);
481 else fprintf(stdout, "MRs saved in %s.\n", outfile);
482 }
483
484 /* Free memory */
485 dftEmpty(&avg);
486 dftEmpty(&pet);
487
488 return(0);
489}
490/*****************************************************************************/
491
492/*****************************************************************************/
int backupExistingFile(char *filename, char *backup_ext, char *status)
Definition backup.c:14
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
int dftdup(DFT *dft1, DFT *dft2)
Definition dft.c:655
char dfterrmsg[64]
Definition dft.c:6
int dftDeleteFrameOverlap(DFT *dft)
Definition dft.c:1237
void dftSetComments(DFT *dft)
Definition dft.c:1326
int dftSortByFrame(DFT *dft)
Definition dft.c:1169
void dftEmpty(DFT *data)
Definition dft.c:20
int dft_nr_of_NA(DFT *dft)
Definition dft.c:905
int dftTimeIntegral(DFT *dft, double t1, double t2, DFT *idft, int calc_mode, char *status, int verbose)
Definition dftint.c:382
int dftRead(char *filename, DFT *data)
Definition dftio.c:22
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int dftToResult(DFT *dft, RES *res, char *status)
Definition dftres.c:60
void dftUnitToDFT(DFT *dft, int dunit)
Definition dftunit.c:11
int dftUnitConversion(DFT *dft, int dunit)
Definition dftunit.c:25
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
Header file for libtpccurveio.
void resInit(RES *res)
Definition result.c:52
#define DFT_FORMAT_CSV_UK
#define DFT_TIME_MIDDLE
int resWrite(RES *res, char *filename, int verbose)
Definition result.c:565
#define DFT_FORMAT_PLAIN
#define DFT_TIME_STARTEND
void resEmpty(RES *res)
Definition result.c:22
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
Definition proginfo.c:101
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
Header file for libtpcmodel.
int pearson4(double *x, double *y, int nr, double start, double end, double *k, double *kSD, double *b, double *bSD, double *r, double *ySD)
Calculate slope and intercept of a line and Pearson's correlation coefficient.
Definition pearson.c:198
Header file for libtpcmodext.
int _type
int timetype
Voi * voi
double * w
double * x1
int voiNr
double * x2
int frameNr
double * x
char unit[MAX_UNITS_LEN+1]
double density
double concentration
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char program[1024]
char plasmafile[FILENAME_MAX]
double lc
char datafile[FILENAME_MAX]
double * y
char name[MAX_REGIONNAME_LEN+1]