10#include "tpcclibConfig.h"
26static char *info[] = {
27 "Non-linear fitting of the exponential based function (Wagner, 1976)",
28 "to the PET plasma or blood time-activity curve (TAC). This function can",
29 "be used when PET tracer is introduced into venous blood as a short infusion.",
33 " if t>Ta and t<Ta+Ti :",
34 " (1/Ti)*Sum[i=1..n, (Ai/Li)*(1-exp(-Li*(t-Ta)))]",
36 " (1/Ti)*Sum[i=1..n, (Ai/Li)*(exp(-Li*(t-Ta-Ti)) - exp(-Li*(t-Ta)))]",
38 "Usage: @P [Options] tacfile [parfile]",
45 " -Ti=<infusion time>",
46 " Duration of tracer infusion; 0, if short bolus.",
47 " -Ta=<appearance time>",
48 " Time when tracer concentration starts ascending.",
49 " -tau1=<Dispersion time constant (s)>",
50 " -tau2=<2nd dispersion time constant (s)>",
51 " Dispersion is added to function, also to saved curve and plot.",
53 " The nr of summed functions; A=determined automatically (default).",
54 " Changing the default may lead to a bad fit.",
56 " Weight by sampling interval.",
58 " Error is returned if MRL check is not passed.",
60 " Fitted parameters are also written in result file format.",
62 " Fitted and measured TACs are plotted in specified SVG file.",
64 " Initial part of fitted and measured TACs are plotted in SVG file",
66 " Lower part of fitted and measured TACs are plotted in SVG file",
68 " Fitted TACs are written in TAC format.",
70 " Fitted dispersion corrected TAC is written in given file;",
71 " same output as with -fit when tau1 and tau2 are not set.",
74 "TAC file must contain two columns, sample times (x) and concentrations (y);",
75 "any additional columns are ignored. For a good fit, TAC should be corrected",
76 "for physical decay and circulating metabolites.",
77 "Function parameters (Ta, Ti, A1, L1, ...) will be written in the parfile.",
80 "1. Wagner JG. J Pharmacokin Biopharm. 1976;4:443-467.",
81 "2. Kudomi N et al. Eur J Nucl Med Mol Imaging 2008;35:1899-1911.",
83 "See also: fit2dat, fit_feng, fit_exp, fit_ratf, extrapol, disp4dft",
85 "Keywords: curve fitting, input, modelling, simulation, dispersion",
102double tau1=0.0, tau2=0.0;
105double *x, *ymeas, *yfit, *w, *ytmp;
106double func331(
int parNr,
double *p,
void*);
113int main(
int argc,
char **argv)
115 int ai, help=0, version=0, verbose=1;
117 char *cptr, progname[256];
118 char tacfile[FILENAME_MAX], fitfile[FILENAME_MAX], limfile[FILENAME_MAX],
119 parfile[FILENAME_MAX], resfile[FILENAME_MAX], svgfile[FILENAME_MAX];
120 char svgfile1[FILENAME_MAX], svgfile2[FILENAME_MAX], dcfile[FILENAME_MAX];
123 double fixedTi, fixedTa;
126 double (*func)(
int parNr,
double *p,
void*);
132 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
133 tacfile[0]=fitfile[0]=dcfile[0]=limfile[0]=parfile[0]=resfile[0]=(char)0;
134 svgfile[0]=svgfile1[0]=svgfile2[0]=(char)0;
135 fixedTa=fixedTi=nan(
"");
139 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
140 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
142 if(strcasecmp(cptr,
"W1")==0) {
144 }
else if(strcasecmp(cptr,
"WF")==0) {
146 }
else if(strcasecmp(cptr,
"MRL")==0) {
147 MRL_check=1;
continue;
148 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
149 if(
strlcpy(svgfile, cptr+4, FILENAME_MAX)>0)
continue;
150 }
else if(strncasecmp(cptr,
"SVG1=", 5)==0) {
151 if(
strlcpy(svgfile1, cptr+5, FILENAME_MAX)>0)
continue;
152 }
else if(strncasecmp(cptr,
"SVG2=", 5)==0) {
153 if(
strlcpy(svgfile2, cptr+5, FILENAME_MAX)>0)
continue;
154 }
else if(strncasecmp(cptr,
"FIT=", 4)==0 && strlen(cptr)>4) {
155 strlcpy(fitfile, cptr+4, FILENAME_MAX);
continue;
156 }
else if(strncasecmp(cptr,
"DCFIT=", 6)==0 && strlen(cptr)>4) {
157 strlcpy(dcfile, cptr+6, FILENAME_MAX);
continue;
158 }
else if(strncasecmp(cptr,
"RES=", 4)==0 && strlen(cptr)>4) {
159 strlcpy(resfile, cptr+4, FILENAME_MAX);
continue;
160 }
else if(strncasecmp(cptr,
"N=", 2)==0) {
161 cptr+=2;
if(strcasecmp(cptr,
"A")==0) {sumn=0;
continue;}
162 sumn=atoi(cptr);
if(sumn>=1 && sumn<=MAX_FUNC_NR)
continue;
163 }
else if(strncasecmp(cptr,
"tau=", 4)==0) {
165 }
else if(strncasecmp(cptr,
"tau1=", 5)==0) {
167 }
else if(strncasecmp(cptr,
"tau2=", 5)==0) {
169 }
else if(strncasecmp(cptr,
"Ta=", 3)==0) {
171 }
else if(strncasecmp(cptr,
"Ti=", 3)==0) {
174 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
179 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
184 if(ai<argc) {
strlcpy(tacfile, argv[ai], FILENAME_MAX); ai++;}
185 if(ai<argc) {
strlcpy(parfile, argv[ai], FILENAME_MAX); ai++;}
187 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
190 if(!parfile[0]) strcpy(parfile,
"stdout");
194 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
200 printf(
"tacfile := %s\n", tacfile);
201 printf(
"parfile := %s\n", parfile);
202 printf(
"fitfile := %s\n", fitfile);
203 printf(
"dcfile := %s\n", dcfile);
204 printf(
"resfile := %s\n", resfile);
205 printf(
"svgfile := %s\n", svgfile);
206 printf(
"svgfile1 := %s\n", svgfile1);
207 printf(
"svgfile2 := %s\n", svgfile2);
208 printf(
"MRL_check := %d\n", MRL_check);
209 printf(
"weights := %d\n", weights);
210 if(sumn>0) printf(
"sumn := %d\n", sumn);
211 if(!isnan(fixedTa)) printf(
"fixedTa := %g\n", fixedTa);
212 if(!isnan(fixedTi)) printf(
"fixedTi := %g\n", fixedTi);
220 if(verbose>1) printf(
"reading %s\n", tacfile);
222 fprintf(stderr,
"Error in reading '%s': %s\n", tacfile,
dfterrmsg);
225 if(verbose>1) printf(
"checking %s\n", tacfile);
229 fprintf(stderr,
"Warning: %s contains more than one TAC; first is used.\n", tacfile);
238 double tstart, tstop, miny, maxy;
239 ret=
dftMinMax(&tac, &tstart, &tstop, &miny, &maxy);
241 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
244 if(tstop<=0.0 || maxy<=0.0) {
245 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
248 if(tstart<0.0) fprintf(stderr,
"Warning: negative x value(s).\n");
249 if(miny<0.0) fprintf(stderr,
"Warning: negative y value(s).\n");
251 printf(
"x_range := %g - %g\n", tstart, tstop);
252 printf(
"sample_number := %d\n", tac.
frameNr);
257 fprintf(stderr,
"Error: missing sample(s) in %s\n", tacfile);
263 int i;
for(i=tac.
frameNr-1; i>=0; i--)
if(tac.
voi[0].
y[i]>0.0)
break;
268 fprintf(stderr,
"Error: %s does not contain enough valid samples.\n", tacfile);
280 }
else if(weights==1) {
282 }
else if(weights==2) {
286 printf(
"data_weights := %g", tac.
w[0]);
287 for(
int i=1; i<tac.
frameNr; i++) printf(
", %g", tac.
w[i]);
295 if(tau1>0.0 || tau2>0.0) {
297 if(verbose>3) printf(
"data and tau's are in the same units.\n");
298 }
else if(tac.
timeunit==TUNIT_MIN) {
299 tau1/=60.0; tau2/=60.0;
300 if(verbose>2) printf(
"tau's are converted to minutes.\n");
302 fprintf(stderr,
"Warning: tau's could not be converted to sample units.\n");
306 printf(
"tau1 := %g\n", tau1);
307 printf(
"tau2 := %g\n", tau2);
315 double tmax;
int imax;
316 ret=
dftMinMaxTAC(&tac, 0, NULL, &tmax, NULL, NULL, NULL, NULL, NULL, &imax);
318 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
322 printf(
"peak_x := %g\n", tmax);
323 printf(
"peak_y := %g\n", maxy);
324 printf(
"peak_index := %d\n", imax);
327 fprintf(stderr,
"Error: invalid TAC shape in %s\n", tacfile);
337 double p1[MAX_FUNC_NR], p2[MAX_FUNC_NR];
344 x=(
double*)malloc(2*n*
sizeof(
double));
346 fprintf(stderr,
"Error: out of memory.\n");
350 for(i=imax, j=0; i<tac.
frameNr && j<n; i++) {
351 x[j]=tac.
x[i]-tmax; y[j]=tac.
voi[0].
y[i];
352 if(!isnan(x[j]) && !isnan(y[j])) j++;
356 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
359 ret=
fitExpDecayNNLS(x, y, n, -1.0, 1.0E-06, 5.0E+01, MAX_FUNC_NR, p1, p2, &n, verbose-2);
361 fprintf(stderr,
"Error: cannot determine constraints from the data.\n");
366 printf(
"Initial guesses for exp parameters:\n");
367 for(i=0; i<n; i++) printf(
"\t%g\t%g\n", p1[i], p2[i]);
377 if(verbose>1) printf(
"allocating memory for fit parameters.\n");
381 fprintf(stderr,
"Error: cannot allocate memory for fit parameters.\n");
400 pmin[0]=0.0; pmax[0]=tmax;
402 pmin[0]=pmax[0]=fixedTa;
406 pmin[1]=0.02*tmax; pmax[1]=1.5*tmax;
408 pmin[1]=pmax[1]=fixedTi;
411 if(sumn==proposed_sumn) {
414 for(i=0, pi=2; i<sumn; i++, pi+=2) {
415 pmin[pi]=0.25*p1[i]; pmax[pi]=2.5*p1[i];
417 pmin[pi+1]=0.15*(-p2[i]); pmax[pi+1]=2.0*(-p2[i]);
420 }
else if(sumn>proposed_sumn) {
423 for(i=0, pi=2; i<proposed_sumn; i++, pi+=2) {
424 pmin[pi]=0.25*p1[i]; pmax[pi]=2.5*p1[i];
425 pmin[pi+1]=0.15*(-p2[i]); pmax[pi+1]=2.0*(-p2[i]);
427 for(j=1; i<sumn; i++, j++) {
428 pmin[pi]=0.0; pmax[pi]=0.2*maxy*(double)j;
429 pmin[pi+1]=1.0E-10; pmax[pi+1]=0.1*(double)j;
434 for(i=0, pi=2; i<sumn; i++, pi+=2) {
435 pmin[pi]=0.05*p1[i]; pmax[pi]=10.0*p1[i];
436 pmin[pi+1]=0.05*(-p2[i]); pmax[pi+1]=5.0*(-p2[i]);
447 if(verbose>2) printf(
"fitting\n");
449 x=tac.
x; ymeas=tac.
voi[0].
y; yfit=tac.
voi[0].
y2; ytmp=tac.
voi[0].
y3; w=tac.
w;
457 ret=
tgo(pmin, pmax, func, NULL, parNr, neigh_nr, &wss, p, tgo_nr, iter_nr, verbose-5);
459 fprintf(stderr,
"Error %d in TGO.\n", ret);
464 printf(
"fitted parameters:\n");
465 for(
int i=0; i<parNr; i++)
466 printf(
"\tp%d\t%g\t(%g - %g)\n", 1+i, p[i], pmin[i], pmax[i]);
467 printf(
"\tWSS\t:=\t%g\n", wss);
470 printf(
"Sample\tX\tY\tYfit\tWeight\n");
471 for(
int i=0; i<dataNr; i++) printf(
"%d\t%9.2e\t%9.2e\t%9.2e\t%7.1e\n",
472 1+i, x[i], ymeas[i], yfit[i], w[i]);
476 if(verbose>1) printf(
"Checking the MRL.\n");
478 if(m>3 && m>dataNr/3) {
479 fprintf(stderr,
"Error: bad fit.\n");
480 fprintf(stderr,
"MRL := %d / %d\n", m, dataNr);
483 }
else if(m>2 && m>dataNr/4) {
484 fprintf(stderr,
"Warning: bad fit.\n");
485 fprintf(stderr,
"MRL := %d / %d\n", m, dataNr);
486 }
else if(verbose>0) {
487 printf(
"MRL test passed.\n");
488 if(verbose>1) fprintf(stderr,
"MRL := %d / %d\n", m, dataNr);
495 for(
int i=0; i<parNr; i++) fit.
voi[0].
p[i]=p[i];
501 if(verbose>1) printf(
"saving results in %s\n", parfile);
502 ret=
fitWrite(&fit, parfile);
if(ret) {
503 fprintf(stderr,
"Error (%d) in writing '%s': %s\n", ret, parfile,
fiterrmsg);
506 if(verbose>0) printf(
"Function parameters written in %s\n", parfile);
510 if(verbose>1) printf(
"allocating memory for results.\n");
516 fprintf(stderr,
"Error in making results: %s\n", buf);
519 n=0; strcpy(res.
parname[n++],
"Function");
521 strcpy(res.
parname[n++],
"Ta");
522 strcpy(res.
parname[n++],
"Ti");
523 for(
int i=1; i<=sumn; i++) {
524 sprintf(res.
parname[n++],
"A%d", i);
525 sprintf(res.
parname[n++],
"L%d", i);
528 strcpy(res.
parname[n++],
"WSS");
530 sprintf(res.
datarange,
"%g - %g", tstart, tstop);
533 if(verbose>1) printf(
"saving results in %s\n", resfile);
534 if(
resWrite(&res, resfile, verbose-3)) {
535 fprintf(stderr,
"Error in writing '%s': %s\n", resfile,
reserrmsg);
539 if(verbose>1) printf(
"Function parameters written in %s\n", resfile);
548 if(svgfile[0] || svgfile1[0] || svgfile2[0]) {
551 printf(
"calculating fitted curve at automatically generated sample times\n");
555 fprintf(stderr,
"Error %d in memory allocation for fitted curves.\n", ret);
560 for(
int i=0; i<adft.
frameNr; i++) {
566 fprintf(stderr,
"Error: cannot calculate fitted curve for '%s'.\n", svgfile);
571 if(tau1>0.0 || tau2>0.0) {
574 fprintf(stderr,
"Error: cannot add dispersion to fitted curve for '%s'.\n", svgfile);
584 if(verbose>1) printf(
"writing %s\n", svgfile);
585 ret=
plot_fitrange_svg(&tac, &adft, tmp, 0.0, nan(
""), 0.0, nan(
""), svgfile, verbose-10);
587 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
591 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
595 if(verbose>1) printf(
"writing %s\n", svgfile1);
596 ret=
plot_fitrange_svg(&tac, &adft, tmp, 0.0, 2.*tmax, 0.0, nan(
""), svgfile1, verbose-10);
598 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile1);
602 if(verbose>0) printf(
"Plots written in %s\n", svgfile1);
606 if(verbose>1) printf(
"writing %s\n", svgfile2);
608 nan(
""), 0.0, nan(
""), svgfile2, verbose-10);
610 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile2);
614 if(verbose>0) printf(
"Plots written in %s\n", svgfile2);
625 if(fitfile[0] || dcfile[0]) {
626 if(verbose>1) printf(
"writing fitted TAC(s)\n");
629 for(
int i=0; i<tac.
frameNr; i++) {
635 fprintf(stderr,
"Error: cannot calculate fitted curve for '%s'.\n", fitfile);
641 if(verbose>1) printf(
"writing %s\n", dcfile);
644 fprintf(stderr,
"Error: cannot write '%s'.\n", dcfile);
651 if(tau1>0.0 || tau2>0.0) {
652 if(verbose>1) printf(
" adding dispersion...\n");
655 fprintf(stderr,
"Error: cannot add dispersion to fitted curve for '%s'.\n", fitfile);
660 if(verbose>1) printf(
"writing %s\n", fitfile);
663 fprintf(stderr,
"Error: cannot write '%s'.\n", fitfile);
681double func331(
int parNr,
double *p,
void *fdata)
684 double d, wss=0.0, f, t, e1, e2;
685 double Ta, Ti, one_per_Ti, A[MAX_FUNC_NR], L[MAX_FUNC_NR], AL[MAX_FUNC_NR];
695 Ti=pa[1];
if(Ti>0.0) one_per_Ti=1.0/Ti;
else one_per_Ti=1.0;
700 for(i=0; i<n; i++)
if(L[i]>1.0E-12) AL[i]=A[i]/L[i];
else AL[i]=A[i];
703 for(fi=0; fi<dataNr; fi++) {
708 for(i=0, f=0.0; i<n; i++) {
710 if(e2>-0.000001) e2=1.0;
else if(e2<-50) e2=0.0;
else e2=exp(e2);
713 if(Ti>0.0) f*=one_per_Ti;
716 for(i=0, f=0.0; i<n; i++) {
718 if(e1>-0.000001) e1=1.0;
else if(e1<-50) e1=0.0;
else e1=exp(e1);
720 if(e2>-0.000001) e2=1.0;
else if(e2<-50) e2=0.0;
else e2=exp(e2);
723 if(Ti>0.0) f*=one_per_Ti;
729 if(tau1>0.0 || tau2>0.0)
733 for(fi=0; fi<dataNr; fi++) {
734 d=ymeas[fi]-yfit[fi];
if(isnan(ymeas[fi]))
continue;
int fitExpDecayNNLS(double *x, double *y, int n, double fittime, double kmin, double kmax, int pnr, double *a, double *k, int *fnr, int verbose)
Estimate initial values for sum of exponentials to be fitted on decaying x,y-data.
int modelCheckParameters(int par_nr, double *lower_p, double *upper_p, double *test_p, double *accept_p, double *penalty)
int atof_with_check(char *double_as_string, double *result_value)
int dftMinMaxTAC(DFT *dft, int tacindex, double *minx, double *maxx, double *miny, double *maxy, int *mini, int *maxi, int *mins, int *maxs)
int dftSortByFrame(DFT *dft)
int dftMinMax(DFT *dft, double *minx, double *maxx, double *miny, double *maxy)
int dft_nr_of_NA(DFT *dft)
int dftRead(char *filename, DFT *data)
int dftWrite(DFT *data, char *filename)
int fitToResult(FIT *fit, RES *res, char *status)
int fit_allocate_with_dft(FIT *fit, DFT *dft)
Header file for libtpccurveio.
int fitEval(FitVOI *r, double x, double *y)
int fitWrite(FIT *fit, char *filename)
int resWrite(RES *res, char *filename, int verbose)
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
int tpcHtmlUsage(const char *program, char *text[], const char *path)
int studynr_from_fname(char *fname, char *studynr)
void tpcPrintBuild(const char *program, FILE *fp)
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Header file for libtpcmodel.
int tgo(double *lowlim, double *uplim, double(*objf)(int, double *, void *), void *objfData, int dim, int neighNr, double *fmin, double *gmin, int samNr, int tgoNr, int verbose)
int simDispersion(double *x, double *y, int n, double tau1, double tau2, double *tmp)
int mrl_between_tacs(double y1[], double y2[], int n)
Header file for libtpcmodext.
int dftWeightByFreq(DFT *dft)
int plot_fitrange_svg(DFT *dft1, DFT *dft2, char *main_title, double x1, double x2, double y1, double y2, char *fname, int verbose)
Header file for libtpcsvg.
char studynr[MAX_STUDYNR_LEN+1]
char datafile[FILENAME_MAX]
char studynr[MAX_STUDYNR_LEN+1]
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]