8#include "tpcclibConfig.h"
30double func_disp(
int parNr,
double *p,
void*);
31double func_wrlv(
int parNr,
double *p,
void*);
33typedef struct FITDATA {
60static char *info[] = {
61 "Non-linear fitting of exponential input function with paired, repeated",
62 "eigenvalues, incorporating injection schedule, to RV and LV cavity BTACs",
63 "from a radiowater PET study, where LV BTAC is modelled with simple",
64 "delay and dispersion of the RV BTAC.",
66 "Usage: @P [Options] tacfile [parfile]",
69 " -Ti=<infusion time>",
70 " Duration of intravenous infusion in seconds; 0, if short bolus.",
72 " All weights are set to 1.0 (no weighting); by default, weights in",
73 " output data file are used, if available.",
75 " Weight by output TAC sampling interval.",
77 " Fitted and measured TACs are plotted in specified SVG file.",
80 "TAC file must contain two TACs, for RV and LV, in this order.",
81 "Sample times must be in seconds, unless units are specified in the file.",
83 "See also: fit_disp, fit_sinf, fit_wpul, disp4dft, convexpf",
85 "Keywords: input, curve fitting, myocardium, radiowater",
104int main(
int argc,
char **argv)
106 int ai, help=0, version=0, verbose=1;
107 char tacfile[FILENAME_MAX], parfile[FILENAME_MAX], svgfile[FILENAME_MAX];
109 double fixedTi=nan(
"");
119 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
120 tacfile[0]=parfile[0]=svgfile[0]=(char)0;
122 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
124 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
125 if(strcasecmp(cptr,
"W1")==0) {
127 }
else if(strcasecmp(cptr,
"WF")==0) {
129 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
130 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
131 }
else if(strncasecmp(cptr,
"Ti=", 3)==0) {
132 fixedTi=
atofVerified(cptr+3);
if(fixedTi>=0.0)
continue;
134 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
143 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
148 if(ai<argc)
strlcpy(tacfile, argv[ai++], FILENAME_MAX);
149 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
151 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
156 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
162 printf(
"tacfile := %s\n", tacfile);
163 if(parfile[0]) printf(
"parfile := %s\n", parfile);
164 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
165 if(!isnan(fixedTi)) printf(
"fixedTi := %g\n", fixedTi);
166 printf(
"weights := %d\n",
weights);
174 if(verbose>1) printf(
"reading BTAC data\n");
183 printf(
"tacNr := %d\n", tac.
tacNr);
184 printf(
"sampleNr := %d\n", tac.
sampleNr);
187 printf(
"isframe := %d\n", tac.
isframe);
190 fprintf(stderr,
"Error: file does not contain RV and LV TACs.\n");
192 }
else if(tac.
tacNr>2) {
193 if(verbose>0) fprintf(stderr,
"Warning: file contains more than two TACs.\n");
197 fprintf(stderr,
"Error: too few samples.\n");
202 fprintf(stderr,
"Error: data contains missing values.\n");
210 fprintf(stderr,
"Error: invalid data sample times.\n");
214 printf(
"orig_xmin := %g\n", xmin);
215 printf(
"orig_xmax := %g\n", xmax);
218 fprintf(stderr,
"Error: negative sample times.\n");
230 fprintf(stderr,
"Error: invalid data sample times.\n");
234 printf(
"xmin := %g\n", xmin);
235 printf(
"xmax := %g\n", xmax);
240 double ymin, ymax;
int imax;
241 ret=
tacYRange(&tac, 0, &ymin, &ymax, NULL, &imax, NULL, NULL);
243 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
246 double tmax=tac.
x[imax];
248 printf(
"peak_x := %g\n", tmax);
249 printf(
"peak_y := %g\n", ymax);
250 printf(
"peak_index := %d\n", imax);
252 if((tac.
sampleNr-imax)<3 || !(ymin<0.5*ymax)) {
253 fprintf(stderr,
"Error: invalid TAC shape in %s\n", tacfile);
261 ret=
tacYRange(&tac, 1, NULL, NULL, NULL, &imax, NULL, NULL);
263 fprintf(stderr,
"Error: invalid contents in %s\n", tacfile);
268 printf(
"LV_peak_x := %g\n", lv_tmax);
284 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
288 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
302 if(verbose>1) printf(
"fitting dispersion and delay between RV and LV TACs\n");
307 fitdata.yrv=tac.
c[0].
y;
308 fitdata.ylv=tac.
c[1].
y;
309 fitdata.fyrv=tac.
c[2].
y;
310 fitdata.fylv=tac.
c[3].
y;
312 if(verbose>10) fitdata.verbose=verbose-10;
else fitdata.verbose=0;
316 fprintf(stderr,
"Error: cannot initiate NLLS.\n");
323 double peakdif=lv_tmax-tmax;
324 if(!(peakdif>0.0)) peakdif=0.0;
325 if(peakdif>50.0) peakdif=50.0;
329 if(verbose>2) printf(
"non-linear optimization\n");
338 for(
unsigned int i=0; i<fitdata.n; i++) {
339 double v=fitdata.fylv[i]-fitdata.ylv[i];
340 wss+=fitdata.w[i]*v*v;
342 printf(
" wss := %g\n", wss);
347 printf(
" fitted_tau := %g\n", tau);
348 printf(
" fitted_delay := %g\n", delay);
357 if(verbose>1) printf(
"slope method for estimating appearance time\n");
363 fprintf(stderr,
"Error: invalid TAC shapes.\n");
366 if(verbose>1) printf(
" initial_Ta := %g\n", Ta);
374 if(verbose>1) printf(
"initial exponential fitting\n");
377 double init_a[enr], init_k[enr];
381 fprintf(stderr,
"Error: invalid sample times.\n");
384 if(verbose>1) printf(
"shortest frame length := %g\n", sdist);
388 double *y=tac.
c[0].
y+imax+1;
389 double *w=tac.
w+imax+1;
390 double x[sampleNr];
for(
int i=0; i<sampleNr; i++) x[i]=tac.
x[i+imax+1]-tmax;
392 printf(
"\n\tRV data for decaying exponential fitting\n");
393 for(
int i=0; i<sampleNr; i++) printf(
"\t%g\t%g\t%g\n", x[i], y[i], w[i]);
394 printf(
"\n"); fflush(stdout);
397 if(verbose>4) printf(
"estimating initial k value range\n");
400 double xmin, xmax;
doubleRange(x, sampleNr, &xmin, &xmax);
401 kmin=1.0E-06/xmax; kmax=3.0/(xmin+sdist);
if(kmax<100.0*kmin) kmax=100.0*kmin;
405 if(verbose>2) printf(
" kmin := %g\n kmax := %g\n", kmin, kmax);
406 double pa[bfNr], pk[bfNr], yfit[sampleNr];
407 ret=
spectralDExp(x, y, w, sampleNr, kmin, kmax, bfNr, pk, pa, yfit, &status);
413 printf(
"\n\tSolutions for each k:\n");
414 for(
int bi=0; bi<bfNr; bi++) printf(
"\t%g\t%g\n", pk[bi], pa[bi]);
417 if(!(pa[0]>0.0 && pa[bfNr-1]>0.0)) {
418 if(verbose>4) printf(
"refine k value range\n");
424 ret=
spectralDExp(x, y, w, sampleNr, kmin, kmax, bfNr, pk, pa, yfit, &status);
430 printf(
"solutions for each k:\n");
431 for(
int bi=0; bi<bfNr; bi++) printf(
"\t%g\t%g\n", pk[bi], pa[bi]);
435 printf(
"\n\tMeasured and fitted TAC:\n");
436 for(
int i=0; i<sampleNr; i++) printf(
"\t%g\t%g\n", y[i], yfit[i]);
437 printf(
"\n"); fflush(stdout);
442 fprintf(stderr,
"Error: cannot estimate initial parameters.\n");
446 printf(
"\n\tInitial a and k values\n");
447 for(
int i=0; i<enr; i++) printf(
"\t%g\t%g\n", init_a[i], init_k[i]);
448 printf(
"\n"); fflush(stdout);
457 if(verbose>1) printf(
"preparing for fitting\n");
461 fitdata.verbose=verbose-10;
464 if(tac.
isframe) {fitdata.x1=tac.
x1; fitdata.x2=tac.
x2;}
else {fitdata.x1=NULL; fitdata.x1=NULL;}
465 fitdata.yrv=tac.
c[0].
y;
466 fitdata.ylv=tac.
c[1].
y;
467 fitdata.fyrv=tac.
c[2].
y;
468 fitdata.fylv=tac.
c[3].
y;
473 if(verbose>2) printf(
"process initial parameter values\n");
521 for(
int i=0; i<2; i++) {
525 nlo.
xfull[ai]=init_a[i];
526 nlo.
xlower[ai]=0.02*init_a[i];
527 nlo.
xupper[ai]=10.0*init_a[i];
528 nlo.
xdelta[ai]=1.0E-02*init_a[i];
529 nlo.
xtol[ai]=1.0E-04*init_a[i];
531 nlo.
xfull[ki]=init_k[i];
532 nlo.
xlower[ki]=0.011*init_k[i];
533 nlo.
xupper[ki]=1.5*init_k[i];
534 nlo.
xdelta[ki]=1.0E-02*init_k[i];
if(!(nlo.
xdelta[ki]>1.0E-05)) nlo.
xdelta[ki]=1.0E-05;
535 nlo.
xtol[ki]=1.0E-06*init_k[i];
if(!(nlo.
xtol[ki]>1.0E-06)) nlo.
xtol[ki]=1.0E-06;
541 if(verbose>2) printf(
"fixedNr := %u\n", fixedNr);
548 printf(
"\nTime\tRV\tLV\tsimRV\tsimLV\n");
549 for(
unsigned int i=0; i<fitdata.n; i++) printf(
"%g\t%g\t%g\t%g\t%g\n", fitdata.x[i],
550 fitdata.yrv[i], fitdata.ylv[i], fitdata.fyrv[i], fitdata.fylv[i]);
551 printf(
"\n"); fflush(stdout);
553 if(verbose>1) {printf(
"\twss := %g\n", wss); fflush(stdout);}
563 if(verbose>0) {printf(
"fitting\n"); fflush(stdout);}
571 printf(
"\nTime\tRV\tLV\tsimRV\tsimLV\n");
572 for(
int i=0; i<tac.
sampleNr; i++) printf(
"%g\t%g\t%g\t%g\t%g\n", tac.
x[i],
573 tac.
c[0].
y[i], tac.
c[1].
y[i], tac.
c[2].
y[i], tac.
c[3].
y[i]);
576 if(verbose>2) printf(
" wss := %g\n", nlo.
funval);
579 for(
unsigned int i=0; i<nlo.
totalNr; i++) {
581 nlo.
xtol[i]*=1.0E-02;
589 printf(
"\nTime\tRV\tLV\tsimRV\tsimLV\n");
590 for(
int i=0; i<tac.
sampleNr; i++) printf(
"%g\t%g\t%g\t%g\t%g\n", tac.
x[i],
591 tac.
c[0].
y[i], tac.
c[1].
y[i], tac.
c[2].
y[i], tac.
c[3].
y[i]);
594 if(verbose>2) printf(
" wss := %g\n", nlo.
funval);
597 for(
unsigned int i=0; i<nlo.
totalNr; i++) {
599 nlo.
xtol[i]*=1.5E-02;
607 printf(
"\nTime\tRV\tLV\tsimRV\tsimLV\n");
608 for(
int i=0; i<tac.
sampleNr; i++) printf(
"%g\t%g\t%g\t%g\t%g\n", tac.
x[i],
609 tac.
c[0].
y[i], tac.
c[1].
y[i], tac.
c[2].
y[i], tac.
c[3].
y[i]);
612 if(verbose>2) printf(
" wss := %g\n", nlo.
funval);
632 iftPut(&par.
h,
"program", buf, 0, NULL);
634 iftPut(&par.
h,
"datafile", tacfile, 0, NULL);
657 par.
r[0].
p[0]=par.
r[1].
p[0]=nlo.
xfull[0];
658 par.
r[0].
p[1]=par.
r[1].
p[1]=nlo.
xfull[1];
659 par.
r[0].
p[2]=par.
r[1].
p[2]=nlo.
xfull[2];
660 par.
r[0].
p[3]=par.
r[1].
p[3]=+nlo.
xfull[3];
661 par.
r[0].
p[4]=par.
r[1].
p[4]=nlo.
xfull[4];
662 par.
r[0].
p[5]=par.
r[1].
p[5]=+nlo.
xfull[5];
663 par.
r[0].
p[6]=0.0; par.
r[1].
p[6]=nlo.
xfull[6];
664 par.
r[0].
p[7]=0.0; par.
r[1].
p[7]=nlo.
xfull[7];
672 if(verbose>1) printf(
" saving %s\n", parfile);
673 FILE *fp=fopen(parfile,
"w");
675 fprintf(stderr,
"Error: cannot open file for writing.\n");
684 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
692 if(verbose>1) printf(
"plotting measured and fitted data\n");
695 for(
int i=0; i<tac.
sampleNr; i++) {fit.
c[0].
y[i]=tac.
c[2].
y[i]; fit.
c[1].
y[i]=tac.
c[3].
y[i];}
697 ret=
tacPlotFitSVG(&tac, &fit,
"", nan(
""), nan(
""), nan(
""), nan(
""), svgfile, &status);
703 if(verbose>0) printf(
"Measured and fitted data plotted in %s\n", svgfile);
720double func_disp(
int parNr,
double *p,
void *fdata)
722 FITDATA *d=(FITDATA*)fdata;
724 if(d->verbose>0) {printf(
"%s()\n", __func__); fflush(stdout);}
725 if(parNr!=2)
return(nan(
""));
726 if(d->verbose>20) printf(
"p[]: %g %g\n", p[0], p[1]);
730 double *buf=dtac+d->n;
731 for(
unsigned int i=0; i<d->n; i++) dtac[i]=d->yrv[i];
732 if(
simDispersion(d->x, dtac, d->n, p[0], 0.0, buf))
return(nan(
""));
735 for(
unsigned int i=0; i<d->n; i++) buf[i]=d->x[i]+p[1];
736 if(
liInterpolate(buf, dtac, d->n, d->x, d->fylv, NULL, NULL, d->n, 0, 1, 0)!=0)
return(nan(
""));
740 for(
unsigned int i=0; i<d->n; i++) {
741 double v=d->fylv[i]-d->ylv[i];
751double func_wrlv(
int parNr,
double *p,
void *fdata)
753 FITDATA *d=(FITDATA*)fdata;
755 if(d->verbose>0) {printf(
"%s()\n", __func__); fflush(stdout);}
756 if(parNr!=8 || p==NULL || fdata==NULL || d->n<1)
return(nan(
""));
758 printf(
"p[]: %g", p[0]);
759 for(
int i=1; i<parNr; i++) printf(
" %g", p[i]);
760 printf(
"\n"); fflush(stdout);
766 double A[3], L[3], ApL[3];
767 A[0]=p[2]; L[0]=p[3]; A[1]=p[4]; L[1]=p[5]; A[2]=0.0; L[2]=0.0;
771 double one_per_Ti;
if(Ti>0.0) one_per_Ti=1.0/Ti;
else one_per_Ti=1.0;
772 for(
int i=0; i<3; i++)
if(L[i]>1.0E-12) ApL[i]=A[i]/L[i];
else ApL[i]=A[i];
775 for(
unsigned int fi=0; fi<d->n; fi++) {
781 for(
int i=0; i<3; i++) {
782 double e2=-L[i]*(t-Ta);
783 if(e2>-0.000001) e2=1.0;
else if(e2<-50) e2=0.0;
else e2=exp(e2);
786 if(Ti>0.0) f*=one_per_Ti;
790 for(
int i=0; i<3; i++) {
791 double e1=-L[i]*(t-Ta-Ti);
792 if(e1>-0.000001) e1=1.0;
else if(e1<-50) e1=0.0;
else e1=exp(e1);
793 double e2=-L[i]*(t-Ta);
794 if(e2>-0.000001) e2=1.0;
else if(e2<-50) e2=0.0;
else e2=exp(e2);
797 if(Ti>0.0) f*=one_per_Ti;
805 for(
unsigned int fi=0; fi<d->n; fi++) xt[fi]=d->x[fi]+deltaT;
806 if(
liInterpolate(xt, d->fyrv, d->n, d->x, d->fylv, NULL, NULL, d->n, 0, 1, 0)!=0)
return(nan(
""));
808 for(
unsigned int fi=0; fi<d->n; fi++) d->fylv[fi]=d->fyrv[fi];
812 if(tau>0.0) (void)
simDispersion(d->x, d->fylv, d->n, tau, 0.0, NULL);
815 if(d->verbose>2) {fprintf(stdout,
"computing WSS...\n"); fflush(stdout);}
817 for(
unsigned i=0; i<d->n; i++) {
818 double v1=d->fyrv[i] - d->yrv[i];
819 double v2=d->fylv[i] - d->ylv[i];
820 wss+=d->w[i]*(v1*v1 + v2*v2);
823 for(
int i=0; i<parNr; i++) printf(
" %g", p[i]);
824 printf(
" -> %g\n", wss); fflush(stdout);
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,...
double atofVerified(const char *s)
unsigned int doubleRange(double *a, const unsigned int n, double *amin, double *amax)
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)
int liInterpolate(double *x, double *y, const int nr, double *newx, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear interpolation and/or integration with trapezoidal method.
unsigned int modelCodeIndex(const char *s)
void nloptInit(NLOPT *nlo)
int nloptAllocate(NLOPT *nlo, unsigned int parNr)
unsigned int nloptFixedNr(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 highestSlope(double *x, double *y, const int n, const int slope_n, double x_start, double *m, double *yi, double *xi, double *xh)
int simDispersion(double *x, double *y, const int n, const double tau1, const double tau2, double *tmp)
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 tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
int tacAllocateMore(TAC *tac, int tacNr)
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 tacSortByTime(TAC *d, TPCSTATUS *status)
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacIsWeighted(TAC *tac)
unsigned int tacWSampleNr(TAC *tac)
int tacWByFreq(TAC *tac, isotope isot, TPCSTATUS *status)
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.
Header file for libtpcbfm.
Header file for libtpccm.
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).
@ UNIT_UNKNOWN
Unknown unit.
char * unitName(int unit_code)
Header file for library libtpcift.
@ ISOTOPE_UNKNOWN
Unknown.
Header file for libtpcli.
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.
Header file for libtpctacmod.