8#include "tpcclibConfig.h"
28double func_exp(
int parNr,
double *p,
void*);
30typedef struct FITDATA {
45static char *info[] = {
46 "Non-linear fitting of the sum of 1-5 exponential functions to decaying",
47 "time-activity curve (TAC).",
51 " f(x) = a1*exp(k1*x) + a2*exp(k2*x) + a3*exp(k3*x) + ...",
53 ", where a's > 0 and k's < 0.",
55 "Usage: @P [Options] tacfile [parfile]",
58 " -5 | -4 | -3 | -2 | -1 | -1BL",
59 " Fit to sum of specified number of exponential functions, or",
60 " monoexponential with baseline; by default determined automatically.",
62 " All weights are set to 1.0 (no weighting); by default, weights in",
63 " data file are used, if available.",
65 " Weight by sampling interval.",
67 " Fitted and measured TACs are plotted in specified SVG file.",
69 " Number of basis functions, used in determination of initial parameter",
70 " estimates and number of exponentials for nonlinear fitting;",
73 " Fit is started only at the TAC peak, or at the next sample after peak.",
76 "If TAC file contains more than one TAC, only the first is used.",
77 "Function parameters are written in fit format.",
79 "See also: extrapol, fit_exp, fit_fexp, fit2dat, fit2auc, paucinf, tacln",
81 "Keywords: TAC, curve fitting, extrapolation",
100int main(
int argc,
char **argv)
102 int ai, help=0, version=0, verbose=1;
103 char tacfile[FILENAME_MAX], parfile[FILENAME_MAX], svgfile[FILENAME_MAX];
109 unsigned int model=0;
116 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
117 tacfile[0]=parfile[0]=svgfile[0]=(char)0;
119 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
121 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
122 if(strcasecmp(cptr,
"W1")==0) {
124 }
else if(strcasecmp(cptr,
"WF")==0) {
126 }
else if(strncasecmp(cptr,
"NR=", 3)==0) {
127 if(
atoiCheck(cptr+3, &bfNr)==0 && bfNr>5)
continue;
128 }
else if(strcasecmp(cptr,
"5")==0 || strcasecmp(cptr,
"5e")==0) {
130 }
else if(strcasecmp(cptr,
"4")==0 || strcasecmp(cptr,
"4e")==0) {
132 }
else if(strcasecmp(cptr,
"3")==0 || strcasecmp(cptr,
"3e")==0) {
134 }
else if(strcasecmp(cptr,
"2")==0 || strcasecmp(cptr,
"2e")==0) {
136 }
else if(strcasecmp(cptr,
"1")==0 || strcasecmp(cptr,
"1e")==0) {
138 }
else if(strcasecmp(cptr,
"1BL")==0 || strcasecmp(cptr,
"1eBL")==0) {
140 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
141 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
142 }
else if(strcasecmp(cptr,
"PEAK")==0) {
143 peakMode=1;
continue;
144 }
else if(strcasecmp(cptr,
"PEAK1")==0) {
145 peakMode=2;
continue;
147 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
156 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
161 if(ai<argc)
strlcpy(tacfile, argv[ai++], FILENAME_MAX);
162 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
164 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
169 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
176 printf(
"tacfile := %s\n", tacfile);
177 if(parfile[0]) printf(
"parfile := %s\n", parfile);
178 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
179 if(expnr>0) printf(
"expnr := %d\n", expnr);
180 if(expnr==2) printf(
"baseline := %d\n", baseline);
181 printf(
"bfNr := %d\n", bfNr);
182 printf(
"weights := %d\n",
weights);
183 printf(
"peakMode := %d\n", peakMode);
192 if(verbose>1) printf(
"reading %s\n", tacfile);
194 ret=
tacRead(&tac, tacfile, &status);
201 printf(
"tacNr := %d\n", tac.
tacNr);
202 printf(
"sampleNr := %d\n", tac.
sampleNr);
205 printf(
"isframe := %d\n", tac.
isframe);
208 fprintf(stderr,
"Error: file contains no data.\n");
212 fprintf(stderr,
"Warning: file contains %d TACs; using the first one.\n", tac.
tacNr);
216 fprintf(stderr,
"Error: too few samples.\n");
221 fprintf(stderr,
"Error: data contains missing values.\n");
225 fprintf(stderr,
"Error: too few samples.\n");
234 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
238 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
253 fprintf(stderr,
"Error: cannot process possible duplicates.\n");
257 fprintf(stderr,
"Error: too few sample times.\n");
261 double ymin, ymax;
int imax;
262 if(
tacYRange(&ptac, 0, &ymin, &ymax, NULL, &imax, NULL, NULL)!=0) {
263 fprintf(stderr,
"Error: no maximum found for TAC.\n");
267 printf(
" minv := %g\n", ymin);
268 printf(
" maxv := %g\n", ymax);
269 printf(
" maxx := %g\n", ptac.
x[imax]);
271 if(imax>0 && peakMode==0 && verbose>0) {
272 fprintf(stderr,
"Warning: TAC does not start with peak.\n");
274 if(!((ymax-ymin)>0.0) || !(ymax>0.0)) {
275 fprintf(stderr,
"Error: invalid TAC value range.\n");
280 fprintf(stderr,
"Error: too few sample times after the peak.\n");
284 double tstart=ptac.
x[imax];
if(peakMode==2) tstart=ptac.
x[imax+1];
285 if(verbose>1) printf(
"starting fit from %g\n", tstart);
288 fprintf(stderr,
"Error: cannot exclude pre-peak data.\n");
302 sdist=tac.
x[i]-tac.
x[i-1];
306 fprintf(stderr,
"Error: invalid sample times.\n");
311 printf(
"sdist := %g\n", sdist);
314 if(verbose>1) printf(
"determining initial parameter estimates\n");
315 double ymin, ymax, xmin, xmax;
316 tacYRange(&tac, 0, &ymin, &ymax, NULL, NULL, NULL, NULL);
319 printf(
" fitrange_minv := %g\n", ymin);
320 printf(
" fitrange_maxv := %g\n", ymax);
321 printf(
" fitrange_minx := %g\n", xmin);
322 printf(
" fitrange_maxx := %g\n", xmax);
328 if(verbose>2) printf(
"estimating initial k value range\n");
331 kmax=3.0/(xmin+sdist);
332 if(kmax<100.0*kmin) kmax=100.0*kmin;
337 if(verbose>2) printf(
" kmin := %g\n kmax := %g\n", kmin, kmax);
338 double pa[bfNr], pk[bfNr], yfit[tac.
sampleNr];
340 pk, pa, yfit, &status);
346 printf(
"solutions for each k:\n");
347 for(
int bi=0; bi<bfNr; bi++) printf(
"\t%g\t%g\n", pk[bi], pa[bi]);
350 printf(
"measured and fitted TAC:\n");
352 printf(
"\t%g\t%g\n", tac.
c[0].
y[i], yfit[i]);
356 if(!(pa[0]>0.0 && pa[bfNr-1]>0.0)) {
357 if(verbose>2) printf(
"refine k value range\n");
363 if(kmax<2.0*kmin) {kmax=2.0*kmin; kmin*=0.5;}
364 if(verbose>2) printf(
" refined kmin := %g\n kmax := %g\n", kmin, kmax);
366 pk, pa, yfit, &status);
372 printf(
"solutions for each k:\n");
373 for(
int bi=0; bi<bfNr; bi++) printf(
"\t%g\t%g\n", pk[bi], pa[bi]);
376 printf(
"measured and fitted TAC:\n");
378 printf(
"\t%g\t%g\n", tac.
c[0].
y[i], yfit[i]);
382 double v, init_wss=0.0;
384 v=tac.
c[0].
y[i]-yfit[i];
385 init_wss+=tac.
w[i]*v*v;
387 printf(
" initial_wss := %g\n", init_wss);
392 if(verbose>3) printf(
"max_a := %g\n", amax);
397 if(verbose>2) printf(
"fbfNr := %d\n", fbfNr);
399 fprintf(stderr,
"Error: cannot estimate initial parameters.\n");
408 if(verbose>1) printf(
"expnr := %d\n", expnr);
409 char s[8]; sprintf(s,
"%dexp", expnr);
414 double init_k[expnr], init_a[expnr];
417 fprintf(stderr,
"Error: cannot estimate initial parameters.\n");
421 printf(
"\n\tClustered a and k values\n");
422 for(
int i=0; i<expnr; i++) printf(
"\t%g\t%g\n", init_a[i], init_k[i]);
423 printf(
"\n"); fflush(stdout);
434 for(
int ei=0; ei<expnr; ei++) {
437 ipar.
xfull[pi]=init_a[ei];
439 ipar.
xupper[pi]=10.0*init_a[ei];
444 ipar.
xfull[pi]=init_k[ei];
450 if(expnr==2 && baseline!=0) {
451 int pi=3;
if(fabs(ipar.
xfull[1])<fabs(ipar.
xfull[3])) pi=1;
456 printf(
"Initial parameters for optimization:\n");
469 if(verbose>1) printf(
"starting nonlinear optimization\n");
476 fitdata.ymeas=tac.
c[0].
y;
480 if(verbose>2) printf(
"1st non-linear optimization\n");
488 printf(
"measured and fitted TAC:\n");
490 printf(
"\t%g\t%g\n", tac.
c[0].
y[i], yfit[i]);
493 double v, final_wss=0.0;
495 v=tac.
c[0].
y[i]-yfit[i];
496 final_wss+=tac.
w[i]*v*v;
498 printf(
" final_wss := %g\n", final_wss);
501 for(
unsigned int i=0; i<ipar.
totalNr; i++) {
502 if(fabs(ipar.
xdelta[i])<1.0E-100)
continue;
504 else ipar.
xdelta[i]*=0.0103;
506 if(verbose>2) printf(
"non-linear optimization, 2nd time\n");
514 printf(
"measured and fitted TAC:\n");
516 printf(
"\t%g\t%g\n", tac.
c[0].
y[i], yfit[i]);
519 double v, final_wss=0.0;
521 v=tac.
c[0].
y[i]-yfit[i];
522 final_wss+=tac.
w[i]*v*v;
524 printf(
" final_final_wss := %g\n", final_wss);
527 for(
unsigned int i=0; i<ipar.
totalNr; i++) {
530 if(verbose>2) printf(
"non-linear optimization, 3rd time\n");
540 printf(
"measured and fitted TAC:\n");
542 printf(
"\t%g\t%g\n", tac.
c[0].
y[i], yfit[i]);
544 double final_wss=0.0;
548 v=tac.
c[0].
y[i]-yfit[i];
549 final_wss+=tac.
w[i]*v*v;
551 if(verbose>3) printf(
" final_final_final_wss := %g\n", final_wss);
570 par.
r[0].
wss=final_wss;
573 for(
int i=0; i<par.
parNr; i+=2) {
574 sprintf(par.
n[i].
name,
"a%d", 1+i/2);
575 sprintf(par.
n[i+1].
name,
"k%d", 1+i/2);
580 for(
unsigned int i=0; i<ipar.
totalNr; i+=2) {
593 iftPut(&par.
h,
"datafile", tacfile, 0, NULL);
599 iftPut(&par.
h,
"program", buf, 0, NULL);
603 if(verbose>1) printf(
" saving %s\n", parfile);
604 FILE *fp=fopen(parfile,
"w");
606 fprintf(stderr,
"Error: cannot open file for writing.\n");
615 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
623 if(verbose>1) printf(
"plotting measured and fitted data\n");
648 fprintf(stderr,
"Error: cannot calculate function values.\n");
654 ret=
tacPlotFitSVG(&tac, &ftac,
"", nan(
""), nan(
""), nan(
""), nan(
""), svgfile, &status);
661 if(verbose>0) printf(
"Measured and fitted data plotted in %s\n", svgfile);
679double func_exp(
int parNr,
double *p,
void *fdata)
683 FITDATA *d=(FITDATA*)fdata;
686 for(i=0, wss=0.0; i<d->n; i++) {
688 if(isnan(d->ymeas[i]) || isnan(d->x[i]))
continue;
689 for(ei=0; ei<(
unsigned int)parNr/2; ei++)
690 d->ysim[i]+=p[2*ei]*exp(-p[2*ei+1]*d->x[i]);
691 v=d->ysim[i]-d->ymeas[i];
695 for(i=0; i<(
unsigned int)parNr; i++) printf(
" %g", p[i]);
696 printf(
" -> %g\n", wss);
int spectralBFNr(double *k, double *a, const int n)
int spectralDExp(const double *x, const double *y, double *w, const int snr, const double kmin, const double kmax, const int bnr, double *k, double *a, double *yfit, TPCSTATUS *status)
int spectralKRange(double *k, double *a, const int n, double *kmin, double *kmax, TPCSTATUS *status)
int spectralBFExtract(double *k, double *a, const int n, double *ke, double *ae, const int ne)
char * ctime_r_int(const time_t *t, char *buf)
Convert calendar time t into a null-terminated string of the form YYYY-MM-DD hh:mm:ss,...
unsigned int doubleMaxIndex(double *a, const unsigned int n)
int mfEvalY(const char *fid, const int parNr, const double *p, const int sampleNr, const double *x, double *y, const int verbose)
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
unsigned int simSamples(double initStep, double maxStep, double endTime, int mode, double *x)
int atoiCheck(const char *s, int *v)
char * modelCode(const unsigned int i)
unsigned int modelCodeIndex(const char *s)
void nloptInit(NLOPT *nlo)
int nloptAllocate(NLOPT *nlo, unsigned int parNr)
unsigned int nloptFixedNr(NLOPT *d)
void nloptRemoveEmpties(NLOPT *d)
void nloptFree(NLOPT *nlo)
void nloptWrite(NLOPT *d, FILE *fp)
char * parFormattxt(parformat c)
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
int parFormatFromExtension(const char *s)
int parAllocateWithTAC(PAR *par, TAC *tac, int parNr, TPCSTATUS *status)
Allocate PAR based on data in TAC.
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)
int nloptSimplex(NLOPT *nlo, unsigned int maxIter, TPCSTATUS *status)
void statusInit(TPCSTATUS *s)
char * errorMsg(tpcerror e)
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
double(* _fun)(int, double *, void *)
IFT h
Optional (but often useful) header information.
char name[MAX_PARNAME_LEN+1]
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
int tacAllocate(TAC *tac, int sampleNr, int tacNr)
int tacCopyTacchdr(TACC *d1, TACC *d2)
int tacCopyHdr(TAC *tac1, TAC *tac2)
Copy TAC header data from tac1 to tac2.
int tacPlotFitSVG(TAC *tac1, TAC *tac2, const char *main_title, const double x1, const double x2, const double y1, const double y2, const char *fname, TPCSTATUS *status)
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
char * tacFormattxt(tacformat c)
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
int tacSortByTime(TAC *d, TPCSTATUS *status)
int tacMultipleSamples(TAC *d1, const int fixMode, TAC *d2, const int verbose)
Check TAC data for multiple samples with the same sample time. Optionally replace the multiple sample...
int tacIsWeighted(TAC *tac)
unsigned int tacWSampleNr(TAC *tac)
int tacWByFreq(TAC *tac, isotope isot, TPCSTATUS *status)
int tacExtractRange(TAC *d1, TAC *d2, double startT, double endT)
Extract the specified time (x) range from TAC structure.
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
int tacYRange(TAC *d, int i, double *ymin, double *ymax, int *smin, int *smax, int *imin, int *imax)
Get the range of y values (concentrations) in TAC struct.
Header file for libtpcbfm.
Header file for library libtpcextensions.
weights
Is data weighted, or are weight factors available with data?
@ WEIGHTING_OFF
Not weighted or weights not available (weights for all included samples are 1.0).
char * unitName(int unit_code)
Header file for library libtpcift.
@ ISOTOPE_UNKNOWN
Unknown.
Header file for libtpclinopt.
Header file for library libtpcnlopt.
Header file for libtpcpar.
@ PAR_FORMAT_FIT
Function fit format of Turku PET Centre.
@ PAR_FORMAT_UNKNOWN
Unknown format.
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Header file for libtpcrand.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Header file for libtpctacmod.