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;
127 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
129 dfile[0]=ffile[0]=svgfile[0]=(char)0;
131 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
132 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
134 if(strcasecmp(cptr,
"ND")==0) {
135 noDivide=1;
continue;
136 }
else if(strcasecmp(cptr,
"MONO")==0) {
137 type=302; parNr=4;
continue;
138 }
else if(strcasecmp(cptr,
"A=ZERO")==0 || strcasecmp(cptr,
"A=0")==0) {
140 }
else if(strncasecmp(cptr,
"A=", 2)==0) {
141 fixed_a=
atof_dpi(cptr+2);
if(fixed_a>0.0)
continue;
142 }
else if(strcasecmp(cptr,
"W1")==0) {
143 weighting=1;
continue;
144 }
else if(strcasecmp(cptr,
"WF")==0) {
145 weighting=2;
continue;
146 }
else if(strcasecmp(cptr,
"WC")==0) {
147 last_col_is_weight=1;
continue;
148 }
else if(strcasecmp(cptr,
"MRL")==0) {
149 MRL_check=1;
continue;
150 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
151 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
154 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
159 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
164 if(ai<argc)
strlcpy(dfile, argv[ai++], FILENAME_MAX);
165 if(ai<argc)
strlcpy(ffile, argv[ai++], FILENAME_MAX);
166 if(ai<argc) {fprintf(stderr,
"Error: too many arguments: '%s'.\n", argv[ai]);
return(1);}
170 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
173 if(!ffile[0]) strcpy(ffile,
"stdout");
180 printf(
"dfile := %s\n", dfile);
181 printf(
"ffile := %s\n", ffile);
182 printf(
"svgfile := %s\n", svgfile);
183 printf(
"noDivide := %d\n", noDivide);
184 printf(
"weighting := %d\n", weighting);
185 printf(
"last_col_is_weight := %d\n", last_col_is_weight);
186 printf(
"fixed0 := %d\n", fixed0);
187 printf(
"MRL_check := %d\n", MRL_check);
188 printf(
"type := %d\n", type);
189 printf(
"parNr := %d\n", parNr);
196 if(verbose>1) printf(
"reading %s\n", dfile);
198 fprintf(stderr,
"Error in reading '%s': %s\n", dfile,
dfterrmsg);
202 fprintf(stderr,
"Error: too few samples for fitting in %s\n", dfile);
208 if(last_col_is_weight!=0) {
209 if(verbose>1) printf(
"reading weights from the last column.\n");
211 fprintf(stderr,
"Error: no column for weights in %s\n", dfile);
214 for(fi=0; fi<dft.
frameNr; fi++) {
216 if(isnan(dft.
w[fi])) dft.
w[fi]=0.0;
223 printf(
"study_number := %s\n", dft.
studynr);
224 printf(
"tacNr := %d\n", dft.
voiNr);
230 fprintf(stderr,
"Warning: extra columns in %s are ignored.\n", dfile);
239 if(verbose>1) printf(
"Sample times are converted to min\n");
247 if(strlen(dft.
voi[0].
name)==0) {
248 strcpy(dft.
voi[0].
name,
"Parent");
253 ret=
dftMinMax(&dft, &tstart, &tstop, NULL, &maxfract);
255 fprintf(stderr,
"Error: invalid contents in %s\n", dfile);
259 if(tstart<0.0 || !(tstop>tstart)) {
260 fprintf(stderr,
"Error: invalid sample times.\n");
264 if(maxfract>100.0 || maxfract<0.001) {
265 fprintf(stderr,
"Error: invalid fraction values.\n");
268 if(noDivide==0 && maxfract>1.0) {
269 fprintf(stderr,
"Warning: converting percentages to fractions.\n");
270 for(fi=0; fi<dft.
frameNr; fi++)
for(ri=0; ri<dft.
voiNr; ri++)
271 if(!isnan(dft.
voi[ri].
y[fi])) dft.
voi[ri].
y[fi]/=100.0;
278 if(dft.
isweight==0 || weighting==1) {
280 for(fi=0; fi<dft.
frameNr; fi++) dft.
w[fi]=1.0;
286 for(fi=0; fi<dft.
frameNr; fi++)
if(isnan(dft.
voi[0].
y[fi])) dft.
w[fi]=0;
288 printf(
"data_weights := %g", dft.
w[0]);
289 for(fi=1; fi<dft.
frameNr; fi++) printf(
", %g", dft.
w[fi]);
295 if(fabs(tstart)<1.0E-08) {
298 for(
int i=0; i<fitdataNr; i++)
if(fabs(dft.
x[i])<1.0E-08) {y0+=dft.
voi[0].
y[i]; n++;}
299 if(n>1) y0/=(double)n;
300 if(y0>0.0 && y0<=1.0) {
301 if(verbose>1) {printf(
"initial_fraction := %g\n", y0); fflush(stdout);}
311 if(verbose>1) printf(
"allocating memory for fits.\n");
314 fprintf(stderr,
"Error: cannot allocate space for fit results (%d).\n", ret);
322 for(fi=n=0; fi<fitdataNr; fi++)
323 if(dft.
x[fi]>=0.0 && !isnan(dft.
voi[0].
y[fi])) n++;
324 for(ri=0; ri<fit.
voiNr; ri++) {
335 if(verbose>1) printf(
"fitting\n");
338 x=dft.
x; ymeas=dft.
voi[ri].
y; yfit=dft.
voi[ri].
y2; w=dft.
w;
340 fit.
voi[ri].
wss=3.402823e+38;
344 pmin[0]=0.0; pmax[0]=1.0;
345 pmin[1]=0.0; pmax[1]=1.0;
346 pmin[2]=0.0; pmax[2]=1.5;
347 if(!isnan(fixed_a)) {pmin[0]=pmax[0]=fixed_a; pmax[1]=fixed_a;}
348 else if(fixed0 && !isnan(y0)) {pmin[0]=pmax[0]=y0; pmax[1]=y0;}
350 pmin[0]=0.0; pmax[0]=0.5;
351 pmin[1]=0.0; pmax[1]=1.0;
352 pmin[2]=0.0; pmax[2]=0.5;
355 printf(
"Constraints for the 1st fit:\n");
356 for(pi=0; pi<parNr; pi++)
357 printf(
" %g - %g\n", pmin[pi], pmax[pi]);
365 for(fi=n=0; fi<fitdataNr; fi++) {
366 if(dft.
x[fi]<0.0)
continue;
367 if(dft.
w[fi]>0.0) n++;
369 for(pi=m=0; pi<parNr; pi++)
if(pmin[pi]<pmax[pi]) m++;
370 if(verbose>1) printf(
"Comparison of nr of samples and params to fit: %d / %d\n", n, m);
372 fprintf(stderr,
"Error: too few samples for fitting in %s\n", dfile);
373 if(verbose>0) printf(
"n := %d\nm := %d\n", n, m);
383 int neighNr=5, iterNr=1, sampleNr=1000;
385 ret=
tgo(pmin, pmax, func351, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
386 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
388 ret=
tgo(pmin, pmax, funcMono, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
389 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
391 fprintf(stderr,
"Error %d in TGO.\n", ret);
396 printf(
"Results from tgo():\n");
397 for(pi=0; pi<parNr; pi++)
398 printf(
" parameter[%d] := %g\n", pi, fit.
voi[ri].
p[pi]);
399 printf(
"WSS := %g\n", fit.
voi[ri].
wss);
402 if(verbose>5) printf(
"lastWSS := %g\n", lastWSS);
410 if(fit.
voi[ri].
p[1]<fit.
voi[ri].
p[2]) {
412 a=pmin[1]; pmin[1]=pmin[2]; pmin[2]=a;
413 a=pmax[1]; pmax[1]=pmax[2]; pmax[2]=a;
417 d=fabs(fit.
voi[ri].
p[1]-fit.
voi[ri].
p[2]);
418 a=0.5*(fit.
voi[ri].
p[1]+fit.
voi[ri].
p[2]);
419 if(verbose>1) printf(
"|b-c|=%g mean(b, c)=%g\n", d, a);
420 if(a>0.0 && d/a<0.01) {
421 if(verbose>1) printf(
"Fitting again with only one exponential...\n");
422 pmin[0]=0.0; pmax[0]=1.0;
423 pmin[1]=0.0; pmax[1]=1.5;
424 pmin[2]=0.0; pmax[2]=0.0;
426 printf(
"Constraints for the 2nd fit:\n");
427 for(pi=0; pi<parNr; pi++)
428 printf(
" %g - %g\n", pmin[pi], pmax[pi]);
430 neighNr=4, iterNr=1, sampleNr=1000;
431 ret=
tgo(pmin, pmax, func351, NULL, parNr, neighNr, &fit.
voi[ri].
wss,
432 fit.
voi[ri].
p, sampleNr, iterNr, verbose-8);
434 fprintf(stderr,
"Error %d in TGO.\n", ret);
445 if(ret==0) {
if(verbose>2) fprintf(stdout,
"ok\n");}
446 else fprintf(stderr,
"warning, fit collided with the limits.\n");
448 if(verbose>1) fprintf(stdout,
"WSS := %g\n", fit.
voi[ri].
wss);
457 double a=fit.
voi[ri].
p[0]-fit.
voi[ri].
p[1];
458 double b=-fit.
voi[ri].
p[2];
459 double c=fit.
voi[ri].
p[1];
469 printf(
" Measured Fitted:\n");
470 for(fi=0; fi<fitdataNr; fi++)
471 if(!isnan(ymeas[fi]))
472 printf(
" %2d: %9.2e %9.2e %9.2e\n", fi+1, x[fi], ymeas[fi], yfit[fi]);
474 printf(
" %2d: %9.2e %-9.9s %9.2e\n", fi+1, x[fi],
".", yfit[fi]);
475 printf(
" fitted parameters:");
476 for(pi=0; pi<parNr; pi++) printf(
" %g", fit.
voi[ri].
p[pi]);
481 if(verbose>1) printf(
"Checking the MRL.\n");
483 if(fi>3 && fi>fitdataNr/3) {
485 fprintf(stderr,
"Error: bad fit.\n");
486 fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
491 if(fi>2 && fi>fitdataNr/4) {
492 fprintf(stderr,
"Warning: bad fit.\n");
493 fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
494 }
else if(verbose>1) {
495 printf(
"MRL test passed.\n");
496 if(verbose>2) fprintf(stderr,
"MRL := %d / %d\n", fi, fitdataNr);
503 for(fi=0, m=0; fi<fitdataNr; fi++)
if(w[fi]>0.0) m++;
504 for(pi=0, n=0; pi<parNr; pi++)
if(pmax[pi]>pmin[pi]) n++;
505 printf(
"nr_of_fitted_parameters := %d\n", n);
507 printf(
"AIC := %g\n", aic);
514 if(verbose>1 && strcmp(ffile,
"stdout")!=0) printf(
"saving results\n");
515 ret=
fitWrite(&fit, ffile);
if(ret) {
516 fprintf(stderr,
"Error (%d) in writing '%s': %s\n", ret, ffile,
fiterrmsg);
519 if(strcmp(ffile,
"stdout")!=0 && verbose>0)
520 printf(
"Function parameters written in %s\n", ffile);
527 if(verbose>1) printf(
"creating SVG plot\n");
529 char tmp[FILENAME_MAX];
531 if(verbose>1) printf(
"calculating fitted curve at automatically generated sample times\n");
535 fprintf(stderr,
"Error %d in memory allocation for fitted curves.\n", ret);
539 for(fi=0; fi<adft.
frameNr; fi++) adft.
w[fi]=1.0;
540 for(ri=0, ret=0; ri<adft.
voiNr; ri++) {
541 for(fi=0; fi<adft.
frameNr; fi++) {
542 ret=
fitEval(&fit.
voi[ri], adft.
x[fi], &a);
if(ret!=0)
break;
543 adft.
voi[ri].
y[fi]=a;
548 fprintf(stderr,
"Error: cannot calculate fitted curve for '%s'.\n", svgfile);
554 if(verbose>1) printf(
"writing %s\n", svgfile);
557 ret=
plot_fitrange_svg(&dft, &adft, tmp, 0.0, nan(
""), 0.0, 1.0, svgfile, verbose-8);
559 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
563 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
581double func351(
int parNr,
double *p,
void *fdata)
588 for(i=0; i<parNr; i++) printf(
" p[%d]=%g", i, p[i]);
593 a=pa[0]; b=pa[1]; c=pa[2];
595 for(i=0, wss=0.0; i<fitdataNr; i++)
if(w[i]>0.0) {
596 yfit[i]= 1.0 - a*(2.0-exp(-b*x[i])-exp(-c*x[i]));
597 if(yfit[i]<0.0 || yfit[i]>1.0) penalty*=2.0;
598 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
602 if(0) printf(
"%g %g %g -> %g\n", a, b, c, wss);
608double funcMono(
int parNr,
double *p,
void *fdata)
615 for(i=0; i<parNr; i++) printf(
" p[%d]=%g", i, p[i]);
620 a=pa[0]; b=pa[1]; c=pa[2];
622 for(i=0, wss=0.0; i<fitdataNr; i++)
if(w[i]>0.0) {
623 yfit[i]= (a-b)*exp(-c*x[i]) + b;
624 if(yfit[i]<0.0 || yfit[i]>1.0) penalty*=2.0;
625 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
629 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]