8#include "tpcclibConfig.h"
27double func_ipop(
int parNr,
double *p,
void*);
28double func_ifengm2(
int parNr,
double *p,
void *);
29double func_ifengm2s(
int parNr,
double *p,
void *);
33typedef struct FITDATA {
51static char *info[] = {
52 "Non-linear fitting of AIF integral function to bladder time-activity",
53 "curves (TACs). Bladder VOI must include all of the urine in the bladder when",
54 "it is largest in the last time frame.",
56 "Usage: @P [Options] tacfile [parfile]",
59 " -model=<M2S | M2 | LATEFDG>",
60 " Select the function; default is M2S.",
61 " M2S and M2 are the functions proposed by Feng et al (1993), including",
62 " two and three exponential terms, respectively.",
63 " LATEFDG is a simplified version, proposed by Phillips et al (1995),",
64 " which can only fit the late part of FDG input TAC, but is designed to",
65 " have two of its parameters fixed to population means.",
67 " All weights are set to 1.0 (no weighting); by default, weights in",
68 " data file are used, if available.",
70 " Weight by sampling interval.",
72 " Delay time (dT) is constrained to specified value (>=0); by default",
73 " delay time is fitted.",
75 " Samples before specified time are set to zero for the fit.",
77 " Fitted and measured TACs are plotted in specified SVG file.",
79 " Parameter m of LATEFDG function is constrained to given value.",
81 " Parameter n of LATEFDG function is constrained to given value.",
84 "See also: fit_sinf, fit_feng, fit2dat",
86 "Keywords: TAC, input, curve fitting",
105int main(
int argc,
char **argv)
107 int ai, help=0, version=0, verbose=1;
108 char tacfile[FILENAME_MAX], parfile[FILENAME_MAX], svgfile[FILENAME_MAX];
110 unsigned int model=0;
111 double fixed_delay=nan(
"");
112 double fixed_m=nan(
"");
113 double fixed_n=nan(
"");
114 double start_t=nan(
"");
120 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
121 tacfile[0]=parfile[0]=svgfile[0]=(char)0;
124 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
126 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
127 if(strncasecmp(cptr,
"MODEL=", 6)==0) {
129 if(strcasecmp(cptr,
"M2")==0) {model=
modelCodeIndex(
"fengm2");
continue;}
130 if(strcasecmp(cptr,
"FENGM2")==0) {model=
modelCodeIndex(
"fengm2");
continue;}
131 if(strcasecmp(cptr,
"M2S")==0) {model=
modelCodeIndex(
"fengm2s");
continue;}
132 if(strcasecmp(cptr,
"FENG")==0) {model=
modelCodeIndex(
"fengm2s");
continue;}
133 if(strcasecmp(cptr,
"LATEFDG")==0) {model=
modelCodeIndex(
"surgefdgaif");
continue;}
134 if(strcasecmp(cptr,
"surgefdgaif")==0) {model=
modelCodeIndex(
"surgefdgaif");
continue;}
135 fprintf(stderr,
"Error: invalid model selection.\n");
return(1);
136 }
else if(strcasecmp(cptr,
"W1")==0) {
138 }
else if(strcasecmp(cptr,
"WF")==0) {
140 }
else if(strncasecmp(cptr,
"DELAY=", 6)==0 && strlen(cptr)>6) {
141 if(!
atofCheck(cptr+6, &fixed_delay) && fixed_delay>=0.0)
continue;
142 }
else if(strncasecmp(cptr,
"START=", 6)==0 && strlen(cptr)>6) {
143 if(!
atofCheck(cptr+6, &start_t) && start_t>=0.0)
continue;
144 }
else if(strncasecmp(cptr,
"DT=", 3)==0 && strlen(cptr)>3) {
145 if(!
atofCheck(cptr+3, &fixed_delay) && fixed_delay>=0.0)
continue;
146 }
else if(strncasecmp(cptr,
"M=", 2)==0 && strlen(cptr)>2) {
147 if(!
atofCheck(cptr+2, &fixed_m) && fixed_m>0.0)
continue;
148 }
else if(strncasecmp(cptr,
"N=", 2)==0 && strlen(cptr)>2) {
149 if(!
atofCheck(cptr+2, &fixed_n) && fixed_n>0.0)
continue;
150 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
151 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
153 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
162 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
167 if(ai<argc)
strlcpy(tacfile, argv[ai++], FILENAME_MAX);
168 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
170 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
176 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
182 if(tacfile[0]) printf(
"tacfile := %s\n", tacfile);
183 if(parfile[0]) printf(
"parfile := %s\n", parfile);
184 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
185 if(!isnan(fixed_m)) printf(
"fixed_m := %g\n", fixed_m);
186 if(!isnan(fixed_n)) printf(
"fixed_n := %g\n", fixed_n);
187 if(!isnan(fixed_delay)) printf(
"fixed_delay := %g\n", fixed_delay);
188 if(!isnan(start_t)) printf(
"start_t := %g\n", start_t);
189 printf(
"model := %s\n",
modelCode(model));
190 printf(
"weights := %d\n",
weights);
195 fprintf(stderr,
"Error: cannot initiate parameter list.\n");
return(1);}
198 strcpy(plim.
n[0].
name,
"dT"); plim.
n[0].
lim1=0.0; plim.
n[0].
lim2=100.0;
199 strcpy(plim.
n[1].
name,
"A"); plim.
n[1].
lim1=1.0E-08; plim.
n[1].
lim2=0.5;
200 strcpy(plim.
n[2].
name,
"B"); plim.
n[2].
lim1=1.0E-06; plim.
n[2].
lim2=1.0E+06;
201 strcpy(plim.
n[3].
name,
"M"); plim.
n[3].
lim1=1.0E-06; plim.
n[3].
lim2=1.0E+06;
202 strcpy(plim.
n[4].
name,
"N"); plim.
n[4].
lim1=1.0E-08; plim.
n[4].
lim2=1.0E+01;
203 if(!isnan(fixed_delay)) plim.
n[0].
lim1=plim.
n[0].
lim2=fixed_delay;
204 if(!isnan(fixed_m)) plim.
n[3].
lim1=plim.
n[3].
lim2=fixed_m;
205 if(!isnan(fixed_n)) plim.
n[4].
lim1=plim.
n[4].
lim2=fixed_n;
208 strcpy(plim.
n[0].
name,
"dT"); plim.
n[0].
lim1=0.0; plim.
n[0].
lim2=100.0;
209 strcpy(plim.
n[1].
name,
"A1"); plim.
n[1].
lim1=1.0E-06; plim.
n[1].
lim2=1.0E+06;
210 strcpy(plim.
n[2].
name,
"L1"); plim.
n[2].
lim1=1.0E-02; plim.
n[2].
lim2=1.0E+00;
211 strcpy(plim.
n[3].
name,
"A2"); plim.
n[3].
lim1=1.0E-06; plim.
n[3].
lim2=1.0E+06;
212 strcpy(plim.
n[4].
name,
"L2"); plim.
n[4].
lim1=1.0E-03; plim.
n[4].
lim2=1.0E-01;
213 strcpy(plim.
n[3].
name,
"A3"); plim.
n[5].
lim1=1.0E-06; plim.
n[5].
lim2=1.0E+06;
214 strcpy(plim.
n[4].
name,
"L3"); plim.
n[6].
lim1=1.0E-08; plim.
n[6].
lim2=1.0E-02;
217 strcpy(plim.
n[0].
name,
"dT"); plim.
n[0].
lim1=0.0; plim.
n[0].
lim2=100.0;
218 strcpy(plim.
n[1].
name,
"A1"); plim.
n[1].
lim1=1.0E-06; plim.
n[1].
lim2=1.0E+06;
219 strcpy(plim.
n[2].
name,
"L1"); plim.
n[2].
lim1=1.0E-02; plim.
n[2].
lim2=1.0E+00;
220 strcpy(plim.
n[3].
name,
"A2"); plim.
n[3].
lim1=1.0E-06; plim.
n[3].
lim2=1.0E+06;
221 strcpy(plim.
n[4].
name,
"L2"); plim.
n[4].
lim1=1.0E-06; plim.
n[4].
lim2=1.0E-01;
223 fprintf(stderr,
"Error: invalid model.\n");
231 if(verbose>1) printf(
"reading %s\n", tacfile);
239 printf(
"tacNr := %d\n", tac.
tacNr);
240 printf(
"sampleNr := %d\n", tac.
sampleNr);
243 printf(
"isframe := %d\n", tac.
isframe);
246 fprintf(stderr,
"Error: file contains no data.\n");
252 fprintf(stderr,
"Error: too few samples for fit.\n");
260 fprintf(stderr,
"Error: invalid data sample times.\n");
264 printf(
"xmin := %g\n", xmin);
265 printf(
"xmax := %g\n", xmax);
271 if(!isnan(start_t)) {
272 for(
int i=0; i<tac.
sampleNr; i++)
if(tac.
x[i]<start_t)
273 for(
int j=0; j<tac.
tacNr; j++) tac.
c[j].
y[i]=0.0;
280 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
284 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
296 if(verbose>1) printf(
"preparing space for parameters\n");
304 for(
int i=0; i<tac.
tacNr; i++) {
318 iftPut(&par.
h,
"program", buf, 0, NULL);
321 iftPut(&par.
h,
"datafile", tacfile, 0, NULL);
327 if(verbose>1) printf(
"preparing for fitting\n");
331 if(verbose>0) {printf(
"fitting...\n"); fflush(stdout);}
332 for(
int ci=0; ci<tac.
tacNr; ci++) {
333 if(verbose>1) printf(
"TAC %d: %s\n", 1+ci, tac.
c[ci].
name);
335 double ymax;
int ymaxi;
336 if(
tacYRange(&tac, ci, NULL, &ymax, NULL, &ymaxi, NULL, NULL)!=0) {
337 fprintf(stderr,
"Error: invalid y (concentration) values.\n");
340 if(verbose>3) printf(
" ymax := %g\n", ymax);
360 fitdata.ymeas=tac.
c[ci].
y;
363 if(verbose>10) fitdata.verbose=verbose-10;
else fitdata.verbose=0;
367 fprintf(stderr,
"Error: cannot initiate NLLS.\n");
375 for(
int i=0; i<par.
parNr; i++) {
379 for(
int i=0; i<par.
parNr; i++) {
392 nlo.
xfull[1]=0.5*ymax/6.6;
394 nlo.
xfull[3]=0.5*ymax/6.6;
396 nlo.
xfull[5]=0.02*ymax/6.6;
400 nlo.
xfull[1]=ymax/6.6;
424 printf(
"measured and fitted TAC:\n");
426 printf(
"\t%g\t%g\t%g\n", tac.
x[i], tac.
c[ci].
y[i], yfit[i]);
433 for(
int i=0; i<par.
parNr; i++) par.
r[ci].
p[i]=nlo.
xfull[i];
456 if(verbose>1) printf(
" saving %s\n", parfile);
457 FILE *fp=fopen(parfile,
"w");
459 fprintf(stderr,
"Error: cannot open file for writing.\n");
468 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
477 if(verbose>1) {printf(
"plotting measured and fitted data\n"); fflush(stdout);}
483 if(verbose>3) {printf(
" allocating memory for %d samples\n", snr); fflush(stdout);}
486 fprintf(stderr,
"Error: %s\n",
errorMsg(ret));
490 if(verbose>3) {printf(
" copying TAC headers\n"); fflush(stdout);}
495 fprintf(stderr,
"Error: %s\n",
errorMsg(ret));
504 if(verbose>3) {printf(
" computing function(s)\n"); fflush(stdout);}
505 for(
int ci=0; ci<tac.
tacNr && ret==0; ci++)
509 fprintf(stderr,
"Error: cannot calculate function values.\n");
515 if(verbose>3) {printf(
" plotting function(s)\n"); fflush(stdout);}
517 ret=
tacPlotFitSVG(&tac, &ftac,
"", nan(
""), nan(
""), nan(
""), nan(
""), svgfile, &status);
519 fprintf(stderr,
"Error: %s\n",
errorMsg(ret));
524 if(verbose>0) printf(
"Measured and fitted data plotted in %s\n", svgfile);
540double func_ipop(
int parNr,
double *p,
void *fdata)
543 FITDATA *d=(FITDATA*)fdata;
550 for(
unsigned int i=0; i<d->n; i++) {
552 if(isnan(d->ymeas[i]) || isnan(d->x[i]))
continue;
553 double x=d->x[i]-p[0];
555 d->ysim[i] = m*(1.0 - (n*a*x + 1.0)*exp(-n*a*x))/(n*n*a*a);
556 d->ysim[i] += (1.0 - exp(-a*x))/a;
559 v=d->ysim[i]-d->ymeas[i];
563 for(
unsigned int i=0; i<(
unsigned int)parNr; i++) printf(
" %g", p[i]);
564 printf(
" -> %g\n", wss);
569double func_ifengm2(
int parNr,
double *p,
void *fdata)
572 FITDATA *d=(FITDATA*)fdata;
581 for(
unsigned int i=0; i<d->n; i++) {
583 if(isnan(d->ymeas[i]) || isnan(d->x[i]))
continue;
584 double x=d->x[i]-p[0];
589 v=(A1-L1*(A2+A3))*(1.0-a)/(L1*L1);
if(isfinite(v)) d->ysim[i]+=v;
590 v=(A1/L1)*x*a;
if(isfinite(v)) d->ysim[i]-=v;
592 if(L2!=0.0) v=(A2/L2)*(1.0-exp(-L2*x));
else v=A2*x;
593 if(isfinite(v)) d->ysim[i]+=v;
594 if(L3!=0.0) v=(A3/L3)*(1.0-exp(-L3*x));
else v=A3*x;
595 if(isfinite(v)) d->ysim[i]+=v;
597 v=d->ysim[i]-d->ymeas[i];
601 for(
unsigned int i=0; i<(
unsigned int)parNr; i++) printf(
" %g", p[i]);
602 printf(
" -> %g\n", wss);
607double func_ifengm2s(
int parNr,
double *p,
void *fdata)
610 FITDATA *d=(FITDATA*)fdata;
617 for(
unsigned int i=0; i<d->n; i++) {
619 if(isnan(d->ymeas[i]) || isnan(d->x[i]))
continue;
620 double x=d->x[i]-p[0];
625 v=(A1-L1*A2)*(1.0-a)/(L1*L1);
if(isfinite(v)) d->ysim[i]+=v;
626 v=(A1/L1)*x*a;
if(isfinite(v)) d->ysim[i]-=v;
628 if(L2!=0.0) v=(A2/L2)*(1.0-exp(-L2*x));
else v=A2*x;
629 if(isfinite(v)) d->ysim[i]+=v;
631 v=d->ysim[i]-d->ymeas[i];
635 for(
unsigned int i=0; i<(
unsigned int)parNr; i++) printf(
" %g", p[i]);
636 printf(
" -> %g\n", wss);
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,...
int atofCheck(const char *s, double *v)
int mfEvalInt(const char *fid, const int parNr, const double *p, const int sampleNr, const double *x, double *v, const int verbose)
unsigned int drandSeed(short int seed)
Make and optionally set the seed for rand(), drand, drandRange, and drandGaussian().
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)
char * modelCode(const unsigned int i)
unsigned int modelParNr(const unsigned int code)
unsigned int modelCodeIndex(const char *s)
void nloptInit(NLOPT *nlo)
int nloptAllocate(NLOPT *nlo, unsigned int parNr)
void nloptFree(NLOPT *nlo)
void nloptWrite(NLOPT *d, FILE *fp)
int parAllocate(PAR *par, int parNr, int tacNr)
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
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 nloptSimplexARRS(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]
char name[MAX_TACNAME_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 tacIsWeighted(TAC *tac)
unsigned int tacWSampleNr(TAC *tac)
int tacWByFreq(TAC *tac, isotope isot, TPCSTATUS *status)
int tacDeleteMissingSamples(TAC *d)
Delete those samples (time frames) from TAC structure, which contain only missing y values,...
int tacGetSampleInterval(TAC *d, double ilimit, double *minfdur, double *maxfdur)
Get the shortest and longest sampling intervals or frame lengths in 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.
int nloptIATGO(NLOPT *nlo, const int doLocal, unsigned int maxIterNr, double neighFract, TPCSTATUS *status)
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 libtpcfileutil.
Header file for library libtpcift.
@ ISOTOPE_UNKNOWN
Unknown.
Header file for library libtpcnlopt.
Header file for libtpcpar.
@ PAR_FORMAT_FIT
Function fit format of Turku PET Centre.
@ 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.