8#include "tpcclibConfig.h"
25double func_exp(
int parNr,
double *p,
void*);
27double *x, *ymeas, *yfit, *w;
30int start_index, end_index;
34static char *info[] = {
35 "Non-linear fitting of the sum of 1-3 exponential functions to PET plasma and",
36 "tissue time-activity curves (TACs).",
40 " f(x) = p1*exp(p2*x) + p3*exp(p4*x) + p5*exp(p6*x)",
42 "Usage: @P [Options] tacfile fitfile",
45 " -3[e] | -2[e] | -1[e]",
46 " Fit to sum of three (default), two, or single exponential function.",
48 " p1, p3 and p5 are constrained to values >= 0.",
50 " p2, p4 and p6 are constrained to values <= 0.",
51 " -starttime=<<Time (min)>|Peak>",
52 " Set the sample time from where fitting is started (0 by default),",
53 " or 'peak' to start fitting from common peak time.",
54 " -endtime=<Time (min)>",
55 " Set the sample time where fitting is stopped (last sample by default).",
57 " Fitted and measured TACs are plotted in specified SVG file.",
59 " Names for fit file and fitted TAC file are set automatically",
61 " All weights are set to 1.0 (no weighting); by default, weights in",
62 " data file are used, if available.",
64 " Weight by sampling interval.",
67 "Function parameters are written in fit (2) or result (3) format,",
68 "depending on filename extension (.fit or .res).",
71 "1. https://www.turkupetcentre.net/petanalysis/doc/format_tpc_dft.html",
72 "2. https://www.turkupetcentre.net/petanalysis/doc/format_tpc_fit.html",
73 "3. https://www.turkupetcentre.net/petanalysis/doc/format_tpc_res.html",
75 "See also: fit_fexp, fit_dexp, llsqe3, fit_ratf, fit2dat, extrapol, tacln",
77 "Keywords: TAC, curve fitting, input, simulation, extrapolation",
96int main(
int argc,
char **argv)
98 int ai, help=0, version=0, verbose=1;
99 int n, fi, pi, ri, type=303, ret;
100 int is_nonneg=0, is_expneg=0;
103 char datfile[FILENAME_MAX], outfile[FILENAME_MAX], svgfile[FILENAME_MAX],
107 double a, b, c, tstart, tstop;
108 double starttime=-1.0, endtime=-1.0;
110 int tgo_nr, neigh_nr, iter_nr;
116 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
117 datfile[0]=outfile[0]=svgfile[0]=(char)0;
121 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
124 if(strcasecmp(cptr,
"3")==0 || strcasecmp(cptr,
"3e")==0) {
125 type=303; parNr=6;
continue;
126 }
else if(strcasecmp(cptr,
"2")==0 || strcasecmp(cptr,
"2e")==0) {
127 type=302; parNr=4;
continue;
128 }
else if(strcasecmp(cptr,
"1")==0 || strcasecmp(cptr,
"1e")==0) {
129 type=301; parNr=2;
continue;
130 }
else if(strncasecmp(cptr,
"NONNEG", 2)==0) {
131 is_nonneg=1;
continue;
132 }
else if(strncasecmp(cptr,
"EXPNEG", 2)==0) {
133 is_expneg=1;
continue;
134 }
else if(strcasecmp(cptr,
"W1")==0) {
135 weighting=1;
continue;
136 }
else if(strcasecmp(cptr,
"WF")==0) {
137 weighting=2;
continue;
138 }
else if(strncasecmp(cptr,
"STARTTIME=", 10)==0) {
139 if(strcasecmp(cptr+10,
"PEAK")==0) {peak_start=1;
continue;}
140 starttime=
atof_dpi(cptr+10);
if(starttime>=0.0)
continue;
141 }
else if(strncasecmp(cptr,
"ENDTIME=", 8)==0) {
142 endtime=
atof_dpi(cptr+8);
if(endtime>0.0)
continue;
143 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
144 strcpy(svgfile, cptr+4);
continue;
146 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
151 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
156 for(; ai<argc; ai++) {
157 if(!datfile[0]) {strcpy(datfile, argv[ai]);
continue;}
158 else if(!outfile[0]) {strcpy(outfile, argv[ai]);
continue;}
159 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
163 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
170 printf(
"datfile := %s\n", datfile);
171 printf(
"outfile := %s\n", outfile);
172 printf(
"svgfile := %s\n", svgfile);
173 printf(
"type := %d\nparNr := %d\n", type, parNr);
174 printf(
"weighting := %d\n", weighting);
175 if(is_nonneg) printf(
"non-negativity constraint was set for p1, p3 and p5\n");
176 if(is_expneg) printf(
"negativity constraint was set for p2, p4 and p6\n");
178 printf(
"starttime := %g min\n", starttime);
180 printf(
"starttime := peak\n");
181 printf(
"endtime := %g min\n", endtime);
190 pmin[0]=-2.0e2; pmax[0]=5.0e2;
if(is_nonneg) {pmin[0]=0.0; pmax[0]*=5.0;}
191 pmin[1]=-5.0e2; pmax[1]=5.0e2;
if(is_expneg) {pmax[1]=0.0; pmin[1]*=2.0;}
192 pmin[2]=-1.0e2; pmax[2]=5.0e1;
if(is_nonneg) {pmin[2]=0.0; pmax[2]*=5.0;}
193 pmin[3]=-0.9; pmax[3]=+0.9;
if(is_expneg) {pmin[3]=0.0;}
194 pmin[4]=-2.0e2; pmax[4]=5.0e1;
if(is_nonneg) {pmin[4]=0.0; pmax[4]*=5.0;}
195 pmin[5]=-0.9; pmax[5]=+0.9;
if(is_expneg) {pmin[5]=0.0;}
201 if(verbose>1) printf(
"reading %s\n", datfile);
203 fprintf(stderr,
"Error in reading '%s': %s\n", datfile,
dfterrmsg);
211 for(ri=0, n=0; ri<dft.
voiNr; ri++) {
213 if(strcasecmp(dft.
voi[ri].
voiname,
"SD")==0) {
214 dft.
voi[ri].
sw2=1; n++;
continue;}
216 dft.
voi[ri].
sw2=1; n++;
continue;}
218 if(n>0 && n<dft.
voiNr) {
219 if(verbose>0) printf(
"%d TAC(s) with SDs are not used.\n", n);
221 while(ri<dft.
voiNr) {
228 fprintf(stderr,
"Error: missing sample(s) in %s\n", datfile);
238 NULL, NULL, verbose-10);
240 fprintf(stderr,
"Error: can not determine peak time.\n");
241 if(verbose>1) printf(
"ret := %d\n", ret);
245 if(dft.
timeunit==TUNIT_SEC) starttime/=60.;
246 if(verbose>1) printf(
"peak_time := %g min\n", starttime);
250 if(endtime<=0.0) endtime=1.0E20;
251 dataNr=
fittime_from_dft(&dft, &starttime, &endtime, &start_index, &end_index, verbose-10);
253 fprintf(stderr,
"Error: check the fit time range.\n");
254 if(verbose>1) printf(
"ret := %d\n", -dataNr);
258 printf(
"start_index := %d\n", start_index);
259 printf(
"end_index := %d\n", end_index);
262 fprintf(stderr,
"Error: too few samples in the fit range.\n");
267 tstart=dft.
x1[start_index]; tstop=dft.
x2[end_index];
269 tstart=dft.
x[start_index]; tstop=dft.
x[end_index];
272 if(dataNr<3 || tstop<=tstart) {
273 fprintf(stderr,
"Error: bad time scale in %s\n", datfile);
281 }
else if(weighting==1) {
283 }
else if(weighting==2) {
287 printf(
"data_weights := %g", dft.
w[0]);
288 for(fi=1; fi<dft.
frameNr; fi++) printf(
", %g", dft.
w[fi]);
296 if(verbose>1) printf(
"allocating memory for fits.\n");
298 fprintf(stderr,
"Error: cannot prepare memory for fit parameters (%d)", ret);
301 strncpy(fit.
datafile, datfile, FILENAME_MAX);
305 for(ri=0; ri<dft.
voiNr; ri++) {
315 double xscale, yscale[dft.
voiNr];
317 for(ri=0; ri<dft.
voiNr; ri++) {
319 for(fi=1; fi<dft.
frameNr; fi++)
320 if(dft.
voi[ri].
y[fi]>a) a=dft.
voi[ri].
y[fi];
324 printf(
"xscale=%g yscale=", xscale);
325 for(ri=0; ri<dft.
voiNr; ri++) printf(
"%g ", yscale[ri]);
330 scaled_data=malloc(
sizeof(
double)*3*dft.
frameNr);
331 if(scaled_data==NULL) {
332 fprintf(stderr,
"Error: out of memory.\n");
337 x=scaled_data; ymeas=scaled_data+dft.
frameNr; yfit=ymeas+dft.
frameNr;
340 for(fi=0; fi<dft.
frameNr; fi++) x[fi]=xscale*dft.
x[fi];
347 if(verbose>1 || (verbose>0 && dft.
voiNr>1)) printf(
"fitting TACs\n");
348 for(ri=0; ri<dft.
voiNr; ri++) {
349 if(verbose>0 && dft.
voiNr>1) printf(
"%s\n", dft.
voi[ri].
name);
352 for(fi=0; fi<dft.
frameNr; fi++)
353 ymeas[fi]=yscale[ri]*dft.
voi[ri].
y[fi];
355 tgo_nr=100+200*parNr;
362 fit.
voi[ri].
wss=3.402823e+38;
365 ret=
tgo(pmin, pmax, func_exp, NULL, parNr, neigh_nr, &fit.
voi[ri].
wss,
366 fit.
voi[ri].
p, tgo_nr, iter_nr, verbose-8);
368 fprintf(stderr,
"Error %d in TGO.\n", ret);
375 printf(
" Measured Fitted:\n");
376 for(fi=0; fi<dft.
frameNr; fi++)
377 printf(
" %2d: %8.2e %8.2e %8.2e\n", fi+1, x[fi], ymeas[fi], yfit[fi]);
381 printf(
" fitted parameters (scaled), with limits:\n");
382 for(pi=0; pi<parNr; pi++)
383 printf(
" %g [%g, %g]\n", fit.
voi[ri].
p[pi], pmin[pi], pmax[pi]);
384 printf(
"\n"); fflush(stdout);
393 if(ret==0) {fprintf(stdout,
"ok\n");}
394 else fprintf(stderr,
"fit collided with the limits.\n");
398 func_exp(parNr, fit.
voi[ri].
p, NULL);
401 for(fi=0; fi<dft.
frameNr; fi++) dft.
voi[ri].
y2[fi]=yfit[fi]/yscale[ri];
405 for(fi=0; fi<dft.
frameNr; fi++) {
408 if(fi==0 || fabs(b)>c) c=fabs(b);
411 if(verbose>1) printf(
" max_dev := %g\n", c);
412 if(verbose>4) printf(
"\n last_y := %g\n\n", dft.
voi[ri].
y2[dft.
frameNr-1]);
415 for(pi=1; pi<parNr; pi+=2) {
416 fit.
voi[ri].
p[pi-1]/=yscale[ri];
418 fit.
voi[ri].
p[1]*=xscale;
420 if(parNr>3) fit.
voi[ri].
p[3]*=fit.
voi[ri].
p[1];
421 if(parNr>5) fit.
voi[ri].
p[5]*=fit.
voi[ri].
p[3];
430 if(verbose>1 || strcasecmp(outfile,
"stdout")==0) {
440 if(outfile[0] && strcasecmp(outfile,
"stdout")!=0) {
441 if(verbose>1) printf(
"writing fit results in %s\n", outfile);
442 cptr=strrchr(outfile,
'.');
if(cptr!=NULL) cptr++;
443 if(strcasecmp(cptr,
"FIT")==0) {
445 fprintf(stderr,
"Error in writing '%s': %s\n", outfile,
fiterrmsg);
452 fprintf(stderr,
"Error in making results: %s\n", temp);
455 if(parNr==6) strcpy(res.
titleline,
"Func p1 p2 p3 p4 p5 p6 WSS");
456 else if(parNr==4) strcpy(res.
titleline,
"Func a1 c1 a2 c2 WSS");
457 else strcpy(res.
titleline,
"Func a1 c1 WSS");
459 sprintf(res.
datarange,
"%g - %g", tstart, tstop);
461 if(
resWrite(&res, outfile, verbose-3)) {
462 fprintf(stderr,
"Error in writing '%s': %s\n", outfile,
fiterrmsg);
474 if(verbose>1) printf(
"saving SVG plot\n");
482 fprintf(stderr,
"Error: cannot save fitted curves.\n");
486 for(fi=start_index, fj=0; fi<=end_index; fi++) {
487 dft2.
x[fj]=dft.
x[fi]; dft2.
x1[fj]=dft.
x1[fi]; dft2.
x2[fj]=dft.
x2[fi];
488 for(ri=0; ri<dft.
voiNr; ri++) dft2.
voi[ri].
y[fj]=dft.
voi[ri].
y2[fi];
496 ret=
plot_fit_svg(&dft, &dft2, tmp, svgfile, verbose-10);
498 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
502 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
521double func_exp(
int parNr,
double *p,
void *fdata)
533 for(i=start_index, wss=0.0; i<=end_index; i++) {
534 if(isnan(ymeas[i]) || isnan(x[i]))
continue;
535 yfit[i]=pa[0]*exp(pa[1]*x[i]) +
536 pa[2]*exp(pa[1]*pa[3]*x[i]) +
537 pa[4]*exp(pa[1]*pa[3]*pa[5]*x[i]);
538 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
540 }
else if(parNr==4) {
541 for(i=start_index, wss=0.0; i<=end_index; i++) {
542 if(isnan(ymeas[i]) || isnan(x[i]))
continue;
543 yfit[i]=pa[0]*exp(pa[1]*x[i]) +
544 pa[2]*exp(pa[1]*pa[3]*x[i]);
545 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
547 }
else if(parNr==2) {
548 for(i=start_index, wss=0.0; i<=end_index; i++) {
549 if(isnan(ymeas[i]) || isnan(x[i]))
continue;
550 yfit[i]=pa[0]*exp(pa[1]*x[i]);
551 v=yfit[i]-ymeas[i]; wss+=w[i]*v*v;
553 }
else {printf(
"Program error: parNr=%d\n", parNr); exit(10);}
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 dftDelete(DFT *dft, int voi)
int dft_nr_of_NA(DFT *dft)
int dftAllocateWithHeader(DFT *dft, int frameNr, int voiNr, DFT *dft_from)
int dftRead(char *filename, DFT *data)
int fitToResult(FIT *fit, RES *res, char *status)
int fit_allocate_with_dft(FIT *fit, DFT *dft)
int fittime_from_dft(DFT *dft, double *startTime, double *endTime, int *first, int *last, int verbose)
Header file for libtpccurveio.
int fitWrite(FIT *fit, char *filename)
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)
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)
Header file for libtpcmodext.
int plot_fit_svg(DFT *dft1, DFT *dft2, char *main_title, char *fname, int verbose)
int dftWeightByFreq(DFT *dft)
Header file for libtpcsvg.
char studynr[MAX_STUDYNR_LEN+1]
char unit[MAX_UNITS_LEN+1]
char datafile[FILENAME_MAX]
char unit[MAX_UNITS_LEN+1]
char voiname[MAX_REGIONSUBNAME_LEN+1]
char name[MAX_REGIONNAME_LEN+1]
char hemisphere[MAX_REGIONSUBNAME_LEN+1]