9#include "tpcclibConfig.h"
23double *x, *ymeas, *yfit, *w;
24int fitdataNr=0, parNr=3;
27double func351(
int parNr,
double *p,
void*);
28double funcMono(
int parNr,
double *p,
void*);
32static char *info[] = {
33 "Fits the following exponential function to plasma parent (unchanged) tracer",
34 "fraction curve, where fractions are between 0 and 1 (1,2).",
37 " f(x) = 1 - A * (2 - e -e ) , where 0<A<=1 , B>0 , C>0 ",
39 "Optionally a monoexponential function, where fractions are between A and B,",
40 "can be applied instead:",
42 " f(x) = (A-B) * e + B , where 0<A<=1 , B>=0 , C>0 ",
44 "Usage: @P [Options] fractionfile [fitfile]",
48 " Use monoexponential function.",
49 " -a=<value> or -a=zero",
50 " Parent fraction at time zero (A) is fixed to given value, or to",
51 " the value of zero sample, if available.",
52 " Applicable only with monoexponential function.",
54 " The last data column contains sample weights.",
56 " All weights are set to 1.0 (no weighting)",
58 " Less frequent samples are given more weight.",
60 " Some fractions are known to exceed 1, not divided by 100.",
62 " Error is returned if MRL check is not passed.",
64 " Fitted and measured TACs are plotted in specified SVG file.",
67 "Fraction datafile must contain two columns, sample times (min) and fractions",
68 "of parent tracer. Weights can be specified as specified in DFT format (4);",
69 "any additional columns are ignored.",
70 "Lines that start with a '#' are not read from the datafile.",
71 "Program writes the fit start and end times, nr of points, WSS/n,",
72 "and parameters of the fitted function to the FIT file (5).",
75 "1. Fitting the fractions of parent tracer in plasma.",
76 " https://www.turkupetcentre.net/petanalysis/input_parent_fitting.html",
77 "2. Kropholler MA, Boellaard R, Schuitemaker A, van Berckel BNM, Luurtsema G,",
78 " Windhorst AD, Lammertsma AA. Development of a tracer kinetic plasma input",
79 " model for (R)-[11C]PK11195 brain studies. J Cereb Blood Flow Metab. 2005;",
81 "4. https://www.turkupetcentre.net/petanalysis/format_tpc_dft.html",
82 "5. https://www.turkupetcentre.net/petanalysis/format_tpc_fit.html",
84 "See also: fit2dat, metabcor, avgfract, fit_ppf, fit_dexp, tac2svg",
86 "Keywords: input, plasma, modelling, simulation, metabolite correction",
105int main(
int argc,
char **argv)
107 int ai, help=0, version=0, verbose=1;
108 int fi, pi, ri, type=351, ret, m, n;
111 double fixed_a=nan(
"");
114 char dfile[FILENAME_MAX], ffile[FILENAME_MAX], svgfile[FILENAME_MAX];
117 double a, tstart, tstop, d, maxfract, aic;
120 int last_col_is_weight=0;
124 _set_output_format(_TWO_DIGIT_EXPONENT);
132 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
134 dfile[0]=ffile[0]=svgfile[0]=(char)0;
136 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
137 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
139 if(strcasecmp(cptr,
"ND")==0) {
140 noDivide=1;
continue;
141 }
else if(strcasecmp(cptr,
"MONO")==0) {
142 type=302; parNr=4;
continue;
143 }
else if(strcasecmp(cptr,
"A=ZERO")==0 || strcasecmp(cptr,
"A=0")==0) {
145 }
else if(strncasecmp(cptr,
"A=", 2)==0) {
146 fixed_a=
atof_dpi(cptr+2);
if(fixed_a>0.0)
continue;
147 }
else if(strcasecmp(cptr,
"W1")==0) {
148 weighting=1;
continue;
149 }
else if(strcasecmp(cptr,
"WF")==0) {
150 weighting=2;
continue;
151 }
else if(strcasecmp(cptr,
"WC")==0) {
152 last_col_is_weight=1;
continue;
153 }
else if(strcasecmp(cptr,
"MRL")==0) {
154 MRL_check=1;
continue;
155 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
156 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
159 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
164 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
169 if(ai<argc)
strlcpy(dfile, argv[ai++], FILENAME_MAX);
170 if(ai<argc)
strlcpy(ffile, argv[ai++], FILENAME_MAX);
171 if(ai<argc) {fprintf(stderr,
"Error: too many arguments: '%s'.\n", argv[ai]);
return(1);}
175 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
178 if(!ffile[0]) strcpy(ffile,
"stdout");
185 printf(
"dfile := %s\n", dfile);
186 printf(
"ffile := %s\n", ffile);
187 printf(
"svgfile := %s\n", svgfile);
188 printf(
"noDivide := %d\n", noDivide);
189 printf(
"weighting := %d\n", weighting);
190 printf(
"last_col_is_weight := %d\n", last_col_is_weight);
191 printf(
"fixed0 := %d\n", fixed0);
192 printf(
"MRL_check := %d\n", MRL_check);
193 printf(
"type := %d\n", type);
194 printf(
"parNr := %d\n", parNr);
201 if(verbose>1) printf(
"reading %s\n", dfile);
203 fprintf(stderr,
"Error in reading '%s': %s\n", dfile,
dfterrmsg);
207 fprintf(stderr,
"Error: too few samples for fitting in %s\n", dfile);
213 if(last_col_is_weight!=0) {
214 if(verbose>1) printf(
"reading weights from the last column.\n");
216 fprintf(stderr,
"Error: no column for weights in %s\n", dfile);
219 for(fi=0; fi<dft.
frameNr; fi++) {
221 if(isnan(dft.
w[fi])) dft.
w[fi]=0.0;
228 printf(
"study_number := %s\n", dft.
studynr);
229 printf(
"tacNr := %d\n", dft.
voiNr);
235 fprintf(stderr,
"Warning: extra columns in %s are ignored.\n", dfile);
244 if(verbose>1) printf(
"Sample times are converted to min\n");
252 if(strlen(dft.
voi[0].
name)==0) {
253 strcpy(dft.
voi[0].
name,
"Parent");
258 ret=
dftMinMax(&dft, &tstart, &tstop, NULL, &maxfract);
260 fprintf(stderr,
"Error: invalid contents in %s\n", dfile);
264 if(tstart<0.0 || !(tstop>tstart)) {
265 fprintf(stderr,
"Error: invalid sample times.\n");
269 if(maxfract>100.0 || maxfract<0.001) {
270 fprintf(stderr,
"Error: invalid fraction values.\n");
273 if(noDivide==0 && maxfract>1.0) {
274 fprintf(stderr,
"Warning: converting percentages to fractions.\n");
275 for(fi=0; fi<dft.
frameNr; fi++)
for(ri=0; ri<dft.
voiNr; ri++)
276 if(!isnan(dft.
voi[ri].
y[fi])) dft.
voi[ri].
y[fi]/=100.0;
283 if(dft.
isweight==0 || weighting==1) {
285 for(fi=0; fi<dft.
frameNr; fi++) dft.
w[fi]=1.0;
291 for(fi=0; fi<dft.
frameNr; fi++)
if(isnan(dft.
voi[0].
y[fi])) dft.
w[fi]=0;
293 printf(
"data_weights := %g", dft.
w[0]);
294 for(fi=1; fi<dft.
frameNr; fi++) printf(
", %g", dft.
w[fi]);
300 if(fabs(tstart)<1.0E-08) {
303 for(
int i=0; i<fitdataNr; i++)
if(fabs(dft.
x[i])<1.0E-08) {y0+=dft.
voi[0].
y[i]; n++;}
304 if(n>1) y0/=(double)n;
305 if(y0>0.0 && y0<=1.0) {
306 if(verbose>1) {printf(
"initial_fraction := %g\n", y0); fflush(stdout);}
316 if(verbose>1) printf(
"allocating memory for fits.\n");
319 fprintf(stderr,
"Error: cannot allocate space for fit results (%d).\n", ret);
327 for(fi=n=0; fi<fitdataNr; fi++)
328 if(dft.
x[fi]>=0.0 && !isnan(dft.
voi[0].
y[fi])) n++;
329 for(ri=0; ri<fit.
voiNr; ri++) {
340 if(verbose>1) printf(
"fitting\n");
343 x=dft.
x; ymeas=dft.
voi[ri].
y; yfit=dft.
voi[ri].
y2; w=dft.
w;
345 fit.
voi[ri].
wss=3.402823e+38;
349 pmin[0]=0.0; pmax[0]=1.0;
350 pmin[1]=0.0; pmax[1]=1.0;
351 pmin[2]=0.0; pmax[2]=1.5;
352 if(!isnan(fixed_a)) {pmin[0]=pmax[0]=fixed_a; pmax[1]=fixed_a;}
353 else if(fixed0 && !isnan(y0)) {pmin[0]=pmax[0]=y0; pmax[1]=y0;}
355 pmin[0]=0.0; pmax[0]=0.5;
356 pmin[1]=0.0; pmax[1]=1.0;
357 pmin[2]=0.0; pmax[2]=0.5;
360 printf(
"Constraints for the 1st fit:\n");
361 for(pi=0; pi<parNr; pi++)
362 printf(
" %g - %g\n", pmin[pi], pmax[pi]);
370 for(fi=n=0; fi<fitdataNr; fi++) {
371 if(dft.
x[fi]<0.0)
continue;
372 if(dft.
w[fi]>0.0) n++;
374 for(pi=m=0; pi<parNr; pi++)
if(pmin[pi]<pmax[pi]) m++;
375 if(verbose>1) printf(
"Comparison of nr of samples and params to fit: %d / %d\n", n, m);
377 fprintf(stderr,
"Error: too few samples for fitting in %s\n", dfile);
378 if(verbose>0) printf(
"n := %d\nm := %d\n", n, m);
388 int neighNr=5, iterNr=1, sampleNr=1000;
390 ret=
tgo(pmin, pmax, func351, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
391 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
393 ret=
tgo(pmin, pmax, funcMono, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
394 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
396 fprintf(stderr,
"Error %d in TGO.\n", ret);
401 printf(
"Results from tgo():\n");
402 for(pi=0; pi<parNr; pi++)
403 printf(
" parameter[%d] := %g\n", pi, fit.
voi[ri].
p[pi]);
404 printf(
"WSS := %g\n", fit.
voi[ri].
wss);
407 if(verbose>5) printf(
"lastWSS := %g\n", lastWSS);
415 if(fit.
voi[ri].
p[1]<fit.
voi[ri].
p[2]) {
417 a=pmin[1]; pmin[1]=pmin[2]; pmin[2]=a;
418 a=pmax[1]; pmax[1]=pmax[2]; pmax[2]=a;
422 d=fabs(fit.
voi[ri].
p[1]-fit.
voi[ri].
p[2]);
423 a=0.5*(fit.
voi[ri].
p[1]+fit.
voi[ri].
p[2]);
424 if(verbose>1) printf(
"|b-c|=%g mean(b, c)=%g\n", d, a);
425 if(a>0.0 && d/a<0.01) {
426 if(verbose>1) printf(
"Fitting again with only one exponential...\n");
427 pmin[0]=0.0; pmax[0]=1.0;
428 pmin[1]=0.0; pmax[1]=1.5;
429 pmin[2]=0.0; pmax[2]=0.0;
431 printf(
"Constraints for the 2nd fit:\n");
432 for(pi=0; pi<parNr; pi++)
433 printf(
" %g - %g\n", pmin[pi], pmax[pi]);
435 neighNr=4, iterNr=1, sampleNr=1000;
436 ret=
tgo(pmin, pmax, func351, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
437 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
439 fprintf(stderr,
"Error %d in TGO.\n", ret);
450 if(ret==0) {
if(verbose>2) fprintf(stdout,
"ok\n");}
451 else fprintf(stderr,
"warning, fit collided with the limits.\n");
453 if(verbose>1) fprintf(stdout,
"WSS := %g\n", fit.
voi[ri].
wss);
462 double a=fit.
voi[ri].
p[0]-fit.
voi[ri].
p[1];
463 double b=-fit.
voi[ri].
p[2];
464 double c=fit.
voi[ri].
p[1];
474 printf(
" Measured Fitted:\n");
475 for(fi=0; fi<fitdataNr; fi++)
476 if(!isnan(ymeas[fi]))
477 printf(
" %2d: %9.2e %9.2e %9.2e\n", fi+1, x[fi], ymeas[fi], yfit[fi]);
479 printf(
" %2d: %9.2e %-9.9s %9.2e\n", fi+1, x[fi],
".", yfit[fi]);
480 printf(
" fitted parameters:");
481 for(pi=0; pi<parNr; pi++) printf(
" %g", fit.
voi[ri].
p[pi]);
486 if(verbose>1) printf(
"Checking the MRL.\n");
488 if(fi>3 && fi>fitdataNr/3) {
490 fprintf(stderr,
"Error: bad fit.\n");
491 fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
496 if(fi>2 && fi>fitdataNr/4) {
497 fprintf(stderr,
"Warning: bad fit.\n");
498 fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
499 }
else if(verbose>1) {
500 printf(
"MRL test passed.\n");
501 if(verbose>2) fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
508 for(fi=0, m=0; fi<fitdataNr; fi++)
if(w[fi]>0.0) m++;
509 for(pi=0, n=0; pi<parNr; pi++)
if(pmax[pi]>pmin[pi]) n++;
510 printf(
"nr_of_fitted_parameters := %d\n", n);
512 printf(
"AIC := %g\n", aic);
519 if(verbose>1 && strcmp(ffile,
"stdout")!=0) printf(
"saving results\n");
520 ret=
fitWrite(&fit, ffile);
if(ret) {
521 fprintf(stderr,
"Error (%d) in writing '%s': %s\n", ret, ffile,
fiterrmsg);
524 if(strcmp(ffile,
"stdout")!=0 && verbose>0)
525 printf(
"Function parameters written in %s\n", ffile);
532 if(verbose>1) printf(
"creating SVG plot\n");
534 char tmp[FILENAME_MAX];
536 if(verbose>1) printf(
"calculating fitted curve at automatically generated sample times\n");
540 fprintf(stderr,
"Error %d in memory allocation for fitted curves.\n", ret);
544 for(fi=0; fi<adft.
frameNr; fi++) adft.
w[fi]=1.0;
545 for(ri=0, ret=0; ri<adft.
voiNr; ri++) {
546 for(fi=0; fi<adft.
frameNr; fi++) {
547 ret=
fitEval(&fit.
voi[ri], adft.
x[fi], &a);
if(ret!=0)
break;
548 adft.
voi[ri].
y[fi]=a;
553 fprintf(stderr,
"Error: cannot calculate fitted curve for '%s'.\n", svgfile);
559 if(verbose>1) printf(
"writing %s\n", svgfile);
562 ret=
plot_fitrange_svg(&dft, &adft, tmp, 0.0, nan(
""), 0.0, 1.0, svgfile, verbose-8);
564 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
568 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
586double func351(
int parNr,
double *p,
void *fdata)
593 for(i=0; i<parNr; i++) printf(
" p[%d]=%g", i, p[i]);
598 a=pa[0]; b=pa[1]; c=pa[2];
600 for(i=0, wss=0.0; i<fitdataNr; i++)
if(w[i]>0.0) {
601 yfit[i]= 1.0 - a*(2.0-exp(-b*x[i])-exp(-c*x[i]));
602 if(yfit[i]<0.0 || yfit[i]>1.0) penalty*=2.0;
603 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
607 if(0) printf(
"%g %g %g -> %g\n", a, b, c, wss);
613double funcMono(
int parNr,
double *p,
void *fdata)
620 for(i=0; i<parNr; i++) printf(
" p[%d]=%g", i, p[i]);
625 a=pa[0]; b=pa[1]; c=pa[2];
627 for(i=0, wss=0.0; i<fitdataNr; i++)
if(w[i]>0.0) {
628 yfit[i]= (a-b)*exp(-c*x[i]) + b;
629 if(yfit[i]<0.0 || yfit[i]>1.0) penalty*=2.0;
630 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
634 if(0) printf(
"%g %g %g -> %g\n", a, b, c, wss);
double aicSS(double ss, const int n, const int k)
int modelCheckParameters(int par_nr, double *lower_p, double *upper_p, double *test_p, double *accept_p, double *penalty)
int modelCheckLimits(int par_nr, double *lower_p, double *upper_p, double *test_p)
double atof_dpi(char *str)
int dftSortByFrame(DFT *dft)
int dftMinMax(DFT *dft, double *minx, double *maxx, double *miny, double *maxy)
int dftRead(char *filename, DFT *data)
void dftUnitToDFT(DFT *dft, int dunit)
void dftSec2min(DFT *dft)
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)
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)
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 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 voiname[MAX_REGIONSUBNAME_LEN+1]
char name[MAX_REGIONNAME_LEN+1]