10#include "tpcclibConfig.h"
27#define DEFAULT_LC 1.00
29#ifndef DEFAULT_DENSITY
30#define DEFAULT_DENSITY 1.00
33#define BAD_FIT 9.999E19
36#define MAX_PARAMETERS 6
41static char *info[] = {
42 "Calculates the net influx (uptake) rate constant Ki (ml/(min*ml)) as slope of",
43 "the Patlak plot (1,2,3) from regional PET time-activity curves (TTACs).",
45 "Usage: @P [Options] ttac_file input start_time end_time result_file",
47 "Input can be either a TAC file containing arterial plasma PTAC or reference",
48 "region TAC, or name or number of reference region in the TTAC file.",
49 "Start and end times of the line fit to the plot must be given in minutes.",
51 "Options for calculation of metabolic rate:",
53 " Concentration of native substrate in arterial plasma (mM),",
54 " for example plasma glucose in [18F]FDG studies.",
55 " With this option the metabolic rate (umol/(min*100 g)) is calculated.",
57 " Lumped Constant in MR calculation; default is 1.0.",
59 " Tissue density in MR calculation; default is 1.0 g/ml.",
62 " -ic=<y axis intercept>",
63 " Force y axis intercept to specified value; for FUR or Ri calculation",
66 " Standard deviations are saved (y, default) or not saved (n) in results.",
68 " Frame middle times are used (y), or not used (n, default), even if frame",
69 " start and end times are available. For compatibility with old software.",
71 " Plots are written in specified file in Scalable Vector Graphics (SVG) 1.1",
72 " format; specification in https://www.w3.org/TR/SVG/",
74 " Black-and-white plot.",
75 " -plotdata=<Filename>",
76 " Data for plots is written in specified file in XHTML table format for",
77 " easy importing in Excel or OpenOffice spreadsheet, where the data can",
78 " be viewed; if filename extension is .dft, data is written in DFT format.",
81 "Options for selecting the least-squares line fit method:",
82 " -C Traditional regression model (default)",
83 " -M Median of two-point slopes and intercepts (Cornish-Bowden)",
84 " -P Perpendicular regression model (4)",
85 " -R Iterative method (York 1966, Lybanon 1984, Reed 1992)",
86 " In the present software version, the weights are not used in the fit.",
88 "Example 1: tissue curves are in ut1234.tac and plasma curve in ut1234ap.dat;",
89 "fitted data range is from 10 to 60 min; standard deviations are not needed;",
90 "plot is saved in file ut2345patlak.svg",
91 " @P -sd=n -svg=ut1234patlak.svg ut1234.tac ut1234ap.dat 10 60 ut1234.res",
93 "Example 2: tissue curves in ut1234.tac, including reference region 'cer'",
94 " @P ut1234.tac cer 10 60 ut1234.res",
97 "1. Gjedde A. Calculation of cerebral glucose phosphorylation from brain",
98 " uptake of glucose analogs in vivo: a re-examination. Brain Res. 1982;",
100 "2. Patlak CS, Blasberg RG, Fenstermacher JD. Graphical evaluation of",
101 " blood-to-brain transfer constants from multiple-time uptake data.",
102 " J Cereb Blood Flow Metab 1983;3:1-7.",
103 "3. Patlak CS, Blasberg RG. Graphical evaluation of blood-to-brain transfer",
104 " constants from multiple-time uptake data. Generalizations.",
105 " J Cereb Blood Flow Metab 1985;5:584-590.",
106 "4. Varga J & Szabo Z. Modified regression model for the Logan plot.",
107 " J Cereb Blood Flow Metab 2002; 22:240-244.",
109 "See also: imgki, fitk3, fitkloss, lhsolki, regfur, logan, rescoll",
111 "Keywords: TAC, MTGA, Patlak plot, modelling, Ki",
130int main(
int argc,
char **argv)
132 int ai, help=0, version=0, verbose=1;
134 int save_stat=1, always_mid=0;
135 double LC=-1.0, Ca=-1.0, density=-1.0;
136 double fixed_Ic=-9.E99;
138 int ri, fi, pi, ret, inputtype, llsq_model=0;
139 char dfile[FILENAME_MAX], ifile[FILENAME_MAX], rfile[FILENAME_MAX],
140 pfile[FILENAME_MAX], sfile[FILENAME_MAX], tmp[1024], *cptr;
141 double tstart, tstop, Ki, KiSD, Ic, IcSD, SWSS;
143 double f, xm, ym, xs, ys;
147 double *t, *theta, *dv, *ci, *ici, *ct;
148 int dataNr=0, first, last;
155 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
156 dfile[0]=ifile[0]=rfile[0]=pfile[0]=sfile[0]=(char)0; tstart=tstop=-1;
158 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
159 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
162 if(strncasecmp(cptr,
"SD=", 3)==0) {
163 save_stat=1; cptr+=3;
if(strlen(cptr)<1)
continue;
164 if(strncasecmp(cptr,
"yes", 1)==0) {save_stat=1;
continue;}
165 else if(strncasecmp(cptr,
"no", 1)==0) {save_stat=0;
continue;}
166 }
else if(strcasecmp(cptr,
"LOSS")==0 || strcasecmp(cptr,
"KLOSS")==0) {
167 fprintf(stderr,
"Error: estimation of kloss is removed in this version.\n");
170 }
else if(strncasecmp(cptr,
"CA=", 3)==0) {
171 Ca=
atof_dpi(cptr+3);
if(Ca>0.0)
continue;
172 }
else if(strncasecmp(cptr,
"LC=", 3)==0) {
173 LC=
atof_dpi(cptr+3);
if(LC>0.0)
continue;
174 }
else if(strncasecmp(cptr,
"D=", 2)==0) {
175 density=
atof_dpi(cptr+2);
if(density>0.0)
continue;
176 }
else if(strncasecmp(cptr,
"DENSITY=", 8)==0) {
177 density=
atof_dpi(cptr+8);
if(density>0.0)
continue;
178 }
else if(strncasecmp(cptr,
"IC=", 3)==0) {
180 if(fixed_Ic==0.0 && verbose>=0)
181 fprintf(stdout,
"Suggestion: for FUR calculation use regfur.\n");
183 }
else if(strncasecmp(cptr,
"MID", 3)==0) {
184 always_mid=1; cptr+=3;
if(strlen(cptr)<2)
continue;
185 if(*cptr==
'=') cptr++;
186 if(strncasecmp(cptr,
"yes", 1)==0) {
187 always_mid=1;
continue;
188 }
else if(strncasecmp(cptr,
"no", 1)==0) {
189 always_mid=0;
continue;
191 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
192 strlcpy(sfile, cptr+4, FILENAME_MAX);
if(strlen(sfile)>0)
continue;
193 }
else if(strncasecmp(cptr,
"PLOTDATA=", 9)==0) {
194 strlcpy(pfile, cptr+9, FILENAME_MAX);
if(strlen(pfile)>0)
continue;
196 }
else if(strcasecmp(cptr,
"C")==0) {
197 llsq_model=0;
continue;
198 }
else if(strcasecmp(cptr,
"R")==0) {
199 llsq_model=1;
continue;
200 }
else if(strcasecmp(cptr,
"P")==0) {
201 llsq_model=2;
continue;
202 }
else if(strcasecmp(cptr,
"M")==0) {
203 llsq_model=3;
continue;
204 }
else if(strcasecmp(cptr,
"BW")==0) {
205 color_scale=2;
continue;
207 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]); fflush(stderr);
212 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
217 for(; ai<argc; ai++) {
218 if(!dfile[0]) {
strlcpy(dfile, argv[ai], FILENAME_MAX);
continue;}
219 if(!ifile[0]) {
strlcpy(ifile, argv[ai], FILENAME_MAX);
continue;}
220 if(tstart<0) {tstart=
atof_dpi(argv[ai]);
continue;}
221 if(tstop<0) {tstop=
atof_dpi(argv[ai]);
continue;}
222 if(!rfile[0]) {
strlcpy(rfile, argv[ai], FILENAME_MAX);
continue;}
223 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]); fflush(stderr);
229 fprintf(stderr,
"Error: missing result file name.\n");
236 fprintf(stderr,
"Warning: LC not set, using default %g\n", LC);
239 density=DEFAULT_DENSITY;
240 fprintf(stderr,
"Warning: tissue density not set, using default %g\n", density);
243 if(LC>0.0) fprintf(stderr,
"Warning: LC was set but is not used.\n");
244 if(density>0.0) fprintf(stderr,
"Warning: tissue density was set but is not used.\n");
248 if(tstop<tstart || (tstop==tstart && fixed_Ic<=-1.E99)) {
249 fprintf(stderr,
"Error: invalid time range %g-%g.\n", tstart, tstop);
250 fflush(stderr);
return(1);
252 if(fixed_Ic>-1.E99) {
259 printf(
"%s", argv[0]);
for(ai=1; ai<argc; ai++) printf(
" %s", argv[ai]);
263 printf(
"llsq_model := %d\nsave_stat := %d\n", llsq_model, save_stat);
264 printf(
"dfile := %s\n", dfile);
265 printf(
"pfile := %s\n", pfile);
266 printf(
"rfile := %s\n", rfile);
267 printf(
"ifile := %s\n", ifile);
268 printf(
"sfile := %s\n", sfile);
269 printf(
"tstart := %g\ntstop := %g\n", tstart, tstop);
271 printf(
"Ca := %g\n", Ca);
272 printf(
"LC := %g\n", LC);
273 printf(
"density := %g\n", density);
275 printf(
"always_mid := %d\n", always_mid);
276 if(fixed_Ic>-1.E99) printf(
"fixed_Ic := %g\n", fixed_Ic);
277 if(color_scale==2) printf(
"color_scale := black-and-white\n");
285 if(verbose>1) printf(
"reading %s\n", dfile);
287 fprintf(stderr,
"Error in reading '%s': %s\n", dfile,
dfterrmsg);
290 if(data.
frameNr==1 && fixed_Ic<=-1.E99) {
293 if(verbose>=0) fprintf(stdout,
"Suggestion: for FUR calculation use regfur.\n");
296 fprintf(stderr,
"Error: missing values in %s\n", dfile);
300 data.
x2[0]=data.
x1[0]=data.
x[0];
310 if(verbose>1) fprintf(stdout,
"checking frame overlap in %s\n", dfile);
313 fprintf(stderr,
"Error: %s has overlapping frame times.\n", dfile);
314 dftEmpty(&data); fflush(stderr);
return(2);
319 if(ret) fprintf(stderr,
"Warning: check that regional data times are in minutes.\n");
323 printf(
"dataNr_in_range := %d\n", dataNr);
324 printf(
"first_in_range := %d\n", first);
325 printf(
"last_in_range := %d\n", last);
328 fprintf(stderr,
"Error: data does not contain the specified time range.\n");
330 }
else if(dataNr<2 && fixed_Ic<=-1.E99) {
331 fprintf(stderr,
"Error: cannot make plot from less than 2 points.\n");
333 }
else if(dataNr==2 && fixed_Ic<=-1.E99) {
334 fprintf(stderr,
"Warning: only two samples in the time range.\n");
337 printf(
"dataNr := %d\n", dataNr);
338 printf(
"tstart := %g\ntstop := %g\n", tstart, tstop);
339 printf(
"first := %d\nlast := %d\n", first, last);
345 if(verbose>1) printf(
"reading input\n");
346 if((ret=
dftReadinput(&input, &data, ifile, &inputtype, &istart, NULL, 1, tmp, verbose-6))) {
348 if(inputtype==5) fprintf(stderr,
"Error: %s.\n", tmp);
349 else fprintf(stderr,
"Error in reading '%s': %s\n", ifile, tmp);
350 dftEmpty(&data); fflush(stderr);
return(3);
354 if(verbose>1) printf(
"trying to fix tissue frame nr.\n");
356 if((ret=
dftReadinput(&input, &data, ifile, &inputtype, &istart, NULL, 1, tmp, verbose-6))) {
357 fprintf(stderr,
"Error in reading '%s': %s\n", ifile, tmp);
358 if(verbose>0) printf(
"dftReadinput() := %d\n", ret);
360 dftEmpty(&data); fflush(stderr);
return(3);
364 printf(
"\nInput data:\n");
366 printf(
"\nTissue data:\n");
370 if(verbose>0) fprintf(stdout,
"selected reference region := %s\n", input.
voi[0].
name);
371 for(ri=1; ri<input.
voiNr; ri++)
372 fprintf(stderr,
"Warning: reference region %s unused.\n", input.
voi[ri].
name);
374 if(input.
voiNr>1) fprintf(stderr,
"Warning: only the first of input curves is used.\n");
380 fprintf(stderr,
"Warning: input TAC should start at time zero.\n");
387 if(verbose>1) printf(
"initializing result data\n");
389 fprintf(stderr,
"Error: cannot setup memory for results.\n");
394 if(verbose>10) printf(
"res.program := '%s'\n", res.
program);
405 if(llsq_model==0) strcpy(res.
fitmethod,
"Traditional regression model");
406 else if(llsq_model==1) strcpy(res.
fitmethod,
"Iterative method");
407 else if(llsq_model==2) strcpy(res.
fitmethod,
"Perpendicular regression model");
408 else if(llsq_model==3) strcpy(res.
fitmethod,
"Median of two-point slopes");
427 if(llsq_model==1 || llsq_model==3) {
428 strcpy(res.
parname[pi],
"SqrtWSS");
430 }
else if(llsq_model==0) {
433 }
else if(llsq_model==2) {
434 strcpy(res.
parname[pi],
"SSD");
453 for(ri=0; ri<data.
voiNr; ri++) {
454 if(verbose>0) {printf(
"calculating %s\n", data.
voi[ri].
name); fflush(stdout);}
457 theta=data.
voi[ri].
y2;
460 ct=data.
voi[ri].
y; t=input.
x;
463 for(fi=data.
frameNr-1; fi>=0; fi--)
if(ci[fi]!=0.0) {
465 printf(
"%03d %8.3f : ici=%g ci=%g ct=%g\n", fi+1, t[fi], ici[fi], ci[fi], ct[fi] );
468 theta[fi]=ici[fi]/ci[fi]; wx[fi]=1.0;
471 dv[fi]=ct[fi]/ci[fi]; wy[fi]=1.0;
473 if(data.
x[fi]<0.1*data.
x[data.
frameNr-1]) {
474 if(theta[fi]>theta[data.
frameNr-1] || dv[fi]>dv[data.
frameNr-1]) {
476 printf(
"Possible close-to-zero plot point at %g -> set to zero.\n", data.
x[fi]);
477 theta[fi]=dv[fi]=wx[fi]=wy[fi]=0.0;
480 }
else if(fixed_Ic>-1.E99) {
481 theta[fi]=ici[fi]; dv[fi]=ct[fi]; wx[fi]=wy[fi]=1.0;
482 }
else theta[fi]=dv[fi]=wx[fi]=wy[fi]=0.0;
485 for(fi=input.
frameNr-1; fi>=0; fi--)
if(input.
voi[0].
y2[fi]<=0.0)
break;
486 for(; fi>=0; fi--) wx[fi]=0.0;
489 for(fi=first; fi<=last; fi++)
490 printf(
"%03d %8.3f : %g %g (%g %g)\n",
491 fi+1, data.
x[fi], theta[fi], dv[fi], wx[fi], wy[fi] );
496 KiSD=Ki=Ic=IcSD=SWSS=0.0;
498 if(fixed_Ic>-1.E99) {
501 ret=
mean(&theta[first], &dv[first], dataNr, &xm, &xs, &ym, &ys);
503 if(fixed_Ic>-1.E99) Ic=fixed_Ic;
else Ic=0.0;
504 Ki=(ym-Ic)/xm;
if(xm!=0.0) KiSD=ys/xm; SWSS=1.0;
506 }
else if(llsq_model==1) {
511 &theta[first], &dv[first], dataNr, &wx[first], &wy[first],
516 &Ic, &Ki, &SWSS, &IcSD, &KiSD, cx, cy
519 printf(
"%s:\n", data.
voi[ri].
name);
520 for(fi=first; fi<=last; fi++)
521 printf(
"%03d %8.3f : %g %g (%g %g -> %g)\n",
522 fi+1, data.
x[fi], theta[fi], dv[fi], wx[fi], wy[fi], w[fi] );
525 }
else if(llsq_model==2) {
528 for(fi=first; fi<=last; fi++)
529 if(wx[fi]<=0.0 || wy[fi]<=0.0) theta[fi]=dv[fi]=nan(
"");
534 &theta[first], &dv[first], dataNr,
539 }
else if(llsq_model==0) {
542 for(fi=first; fi<=last; fi++)
543 if(wx[fi]<=0.0 || wy[fi]<=0.0) theta[fi]=dv[fi]=nan(
"");
545 if(verbose>9)
for(fi=first; fi<=last; fi++)
546 printf(
" %d %g %g\n", fi, theta[fi], dv[fi]);
551 &theta[first], &dv[first], dataNr,
553 &Ki, &KiSD, &Ic, &IcSD, &SWSS, &f
555 if(verbose>9) {printf(
"Ki=%g Ic=%g\n", Ki, Ic); fflush(stdout);}
557 }
else if(llsq_model==3) {
560 for(fi=first; fi<=last; fi++)
561 if(wx[fi]<=0.0 || wy[fi]<=0.0) theta[fi]=dv[fi]=nan(
"");
565 &theta[first], &dv[first], dataNr, &Ki, &Ic
573 if(verbose>1) {printf(
"Ki := %g (%g)\n", Ki, KiSD); fflush(stdout);}
576 fprintf(stderr,
"Error (%d) in linear fit of %s\n", ret, data.
voi[ri].
name);
587 if(verbose>0) printf(
"saving plots\n");
588 sprintf(tmp,
"Patlak-plot ");
590 ret=
plotdata(&data, &res, first, last, tmp,
"Input integral / Input",
"Tissue / Input", pfile);
592 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, pfile);
596 if(verbose>=0) {printf(
"Plot data written in %s\n", pfile); fflush(stdout);}
600 if(verbose>0) printf(
"saving SVG plot\n");
601 sprintf(tmp,
"Patlak-plot ");
603 ret=
plot_svg(&data, &res, first, last, tmp,
"Input integral / Input",
"Tissue / Input",
604 color_scale, sfile, verbose-8);
606 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, sfile);
610 if(verbose>=0) {printf(
"Plots written in %s\n", sfile); fflush(stdout);}
619 if(verbose>0) printf(
"calculating metabolic rates\n");
620 f=100.*Ca/(density*LC);
621 for(ri=0; ri<res.
voiNr; ri++) {
623 for(fi=res.
parNr; fi>0; fi--) {
625 if(save_stat) res.
voi[ri].
sd[fi]=res.
voi[ri].
sd[fi-1];
629 if(save_stat) res.
voi[ri].
sd[0]=f*res.
voi[ri].
sd[1];
639 else fprintf(stdout,
"Patlak plot calculated from %d regional TACs.\n", res.
voiNr);
645 for(fi=first; fi<=last; fi++)
646 printf(
"%03d %8.3f : %g %g\n",
647 fi+1, data.
x[fi], data.
voi[ri].
y2[fi], data.
voi[ri].
y3[fi] );
653 if(verbose>0) printf(
"saving results\n");
654 ret=
resWrite(&res, rfile, verbose-3);
656 fprintf(stderr,
"Error in writing '%s': %s\n", rfile,
reserrmsg);
double atof_dpi(char *str)
int dftDeleteFrameOverlap(DFT *dft)
int dftSortByFrame(DFT *dft)
int dft_nr_of_NA(DFT *dft)
int dftRead(char *filename, DFT *data)
int res_allocate_with_dft(RES *res, DFT *dft)
int dftTimeunitConversion(DFT *dft, int tunit)
int fittime_from_dft(DFT *dft, double *startTime, double *endTime, int *first, int *last, int verbose)
Header file for libtpccurveio.
int resWrite(RES *res, char *filename, int verbose)
#define DFT_TIME_STARTEND
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
int studynr_from_fname2(char *fname, char *studynr, int force)
char * petCunit(int cunit)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
char * petTunit(int tunit)
int tpcHtmlUsage(const char *program, char *text[], const char *path)
void tpcPrintBuild(const char *program, FILE *fp)
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Header file for libtpcmodel.
int llsqperp3(double *x, double *y, int nr, double *slope, double *ic, double *ssd)
int medianline(double *x, double *y, int nr, double *slope, double *ic)
int llsqwt(double *x, double *y, int n, double *wx, double *wy, double tol, double *w, double *ic, double *slope, double *nwss, double *sic, double *sslope, double *cx, double *cy)
int pearson3(double *x, double *y, int nr, double *k, double *kSD, double *b, double *bSD, double *r, double *ySD)
int mean(double *x, double *y, int nr, double *xmean, double *xsd, double *ymean, double *ysd)
Header file for libtpcmodext.
int plotdata(DFT *dft, RES *res, int first, int last, char *mtitle, char *xtitle, char *ytitle, char *fname)
int plot_svg(DFT *dft, RES *res, int first, int last, char *main_title, char *x_title, char *y_title, int color_scale, char *fname, int verbose)
Header file for libtpcsvg.
char unit[MAX_UNITS_LEN+1]
char studynr[MAX_STUDYNR_LEN+1]
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char plasmafile[FILENAME_MAX]
char datafile[FILENAME_MAX]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
double parameter[MAX_RESPARAMS]
char name[MAX_REGIONNAME_LEN+1]