8#include "tpcclibConfig.h"
30double func_wpul(
int parNr,
double *p,
void*);
32typedef struct FITDATA {
61static char *info[] = {
62 "Non-linear fitting of the radiowater model to pulmonary TTACs using BTAC",
63 "from RV cavity as the input function. Small delay and dispersion of input",
64 "function prior to lungs is allowed and fitted by default. Regional",
65 "radioactivity concentration is modelled as Cpet(t) = Vb*Cb(t) + Ct(t),",
66 "and thus the estimated K1 = (1-Va-Vb)*f. Air volume fraction (Va) could",
67 "be estimated from transmission or CT scan.",
69 "Usage: @P [Options] btacfile ttacfile [parfile]",
73 " Dispersion time constant is constrained to given value (sec).",
75 " Blood volume is constrained to given value (mL/mL of lung).",
77 " All weights are set to 1.0 (no weighting), or based on TTAC frame",
78 " lengths; by default, weights in TTAC file are used, if available.",
80 " Fitted and measured TACs are plotted in specified SVG file.",
82 " Fitted TTACs at BTAC sample times are saved in specified TAC file.",
85 "Sample times must be in seconds, unless units are specified in the file.",
87 "See also: fit_h2o, bfmh2o, fitk2, fit_wrlv, fit_disp",
89 "Keywords: TAC, modelling, perfusion, lungs, radiowater",
108int main(
int argc,
char **argv)
110 int ai, help=0, version=0, verbose=1;
111 char btacfile[FILENAME_MAX], ttacfile[FILENAME_MAX], parfile[FILENAME_MAX],
112 svgfile[FILENAME_MAX], simfile[FILENAME_MAX];
114 double fixed_tau=nan(
"");
115 double fixed_vb=nan(
"");
123 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
124 btacfile[0]=ttacfile[0]=parfile[0]=svgfile[0]=simfile[0]=(char)0;
126 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
128 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
129 if(strcasecmp(cptr,
"W1")==0) {
131 }
else if(strcasecmp(cptr,
"WF")==0) {
133 }
else if(strncasecmp(cptr,
"TAU=", 4)==0 && strlen(cptr)>4) {
134 if(!
atofCheck(cptr+4, &fixed_tau) && fixed_tau>=0.0)
continue;
135 }
else if(strncasecmp(cptr,
"VB=", 3)==0 && strlen(cptr)>3) {
136 if(!
atofCheck(cptr+3, &fixed_vb) && fixed_vb>=0.0 && fixed_vb<1.0)
continue;
137 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
138 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
139 }
else if(strncasecmp(cptr,
"SIM=", 4)==0 && strlen(cptr)>4) {
140 strlcpy(simfile, cptr+4, FILENAME_MAX);
continue;
142 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
151 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
156 if(ai<argc)
strlcpy(btacfile, argv[ai++], FILENAME_MAX);
157 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
158 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
160 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
165 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
171 printf(
"btacfile := %s\n", btacfile);
172 printf(
"ttacfile := %s\n", ttacfile);
173 if(parfile[0]) printf(
"parfile := %s\n", parfile);
174 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
175 if(simfile[0]) printf(
"simfile := %s\n", simfile);
176 printf(
"weights := %d\n",
weights);
177 if(!isnan(fixed_tau)) printf(
"fixed_tau := %g\n", fixed_tau);
178 if(!isnan(fixed_vb)) printf(
"fixed_vb := %g\n", fixed_vb);
186 if(verbose>1) printf(
"reading TACs\n");
196 printf(
"ttacNr := %d\n", ttac.
tacNr);
197 printf(
"ttac.sampleNr := %d\n", ttac.
sampleNr);
207 printf(
"btacNr := %d\n", btac.
tacNr);
208 printf(
"btac.sampleNr := %d\n", btac.
sampleNr);
212 if(verbose>0) fprintf(stdout,
"Note: BTAC file assumed to contain RV and LV BTACs.\n");
213 }
else if(btac.
tacNr>2) {
214 if(verbose>0) fprintf(stderr,
"Warning: BTAC file contains more than two TACs.\n");
219 fprintf(stderr,
"Error: too few samples.\n");
224 fprintf(stderr,
"Error: data contains missing values.\n");
232 fprintf(stderr,
"Error: invalid data sample times.\n");
244 fprintf(stderr,
"Error: check and set the data units.\n");
250 fprintf(stderr,
"Error: invalid data sample times.\n");
254 printf(
"xmin := %g\n", xmin);
255 printf(
"xmax := %g\n", xmax);
271 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
275 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
288 fprintf(stderr,
"Error: invalid input sample times.\n");
293 fprintf(stderr,
"Error: invalid input sample times.\n");
307 for(
int i=0; i<btac.
sampleNr; i++) x[i]=btac.
x[i]+4.0;
308 if(
liInterpolate(x, btac.
c[1].
y, btac.
sampleNr, btac.
x, y, NULL, NULL, btac.
sampleNr, 3, 1, 0))
310 fprintf(stderr,
"Error: invalid input sample times.\n");
313 for(
int i=0; i<btac.
sampleNr; i++) btac.
c[0].
y[i]=0.95*btac.
c[0].
y[i]+0.05*y[i];
321 if(verbose>1) printf(
"preparing space for parameters\n");
334 iftPut(&par.
h,
"program", buf, 0, NULL);
338 for(
int i=0; i<par.
tacNr; i++) {
352 iftPut(&par.
h,
"inputfile", btacfile, 0, NULL);
353 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
359 if(verbose==1) {printf(
"\nfitting...\n"); fflush(stdout);}
362 for(
int ri=0; ri<ttac.
tacNr; ri++) {
363 if(verbose>1) {printf(
"\nfitting %s\n", ttac.
c[ri].
name); fflush(stdout);}
368 fitdata.yi=btac.
c[0].
y;
379 fitdata.yt=ttac.
c[ri].
y;
381 fitdata.syt=ttac.
c[ttac.
tacNr+ri].
y;
382 if(verbose>10) fitdata.verbose=verbose-10;
else fitdata.verbose=0;
386 fprintf(stderr,
"Error: cannot initiate NLLS.\n"); fflush(stderr); failed++;
394 if(isnan(fixed_tau)) {
402 if(isnan(fixed_vb)) {
418 printf(
"\nTime\tTTAC\tsimTTAC\n");
419 for(
unsigned int i=0; i<fitdata.nt; i++)
420 printf(
"%g\t%g\t%g\n", ttac.
x[i], ttac.
c[ri].
y[i], ttac.
c[ri+ttac.
tacNr].
y[i]);
421 printf(
"\n"); fflush(stdout);
423 if(verbose>1) {printf(
"\twss := %g\n", wss); fflush(stdout);}
428 printf(
"initial guess\n");
437 fprintf(stderr,
"Error: %s\n",
errorMsg(status.
error)); fflush(stderr); failed++;
443 printf(
"\nTime\tTTAC\tsimTTAC\n");
444 for(
unsigned int i=0; i<fitdata.nt; i++)
445 printf(
"%g\t%g\t%g\n", ttac.
x[i], ttac.
c[ri].
y[i], ttac.
c[ri+ttac.
tacNr].
y[i]);
446 printf(
"\n"); fflush(stdout);
449 if(verbose>2) printf(
" wss := %g\n", wss);
460 par.
r[ri].
p[5]=par.
r[ri].
p[3]/par.
r[ri].
p[4];
475 if(verbose>1) printf(
" saving %s\n", parfile);
476 FILE *fp=fopen(parfile,
"w");
478 fprintf(stderr,
"Error: cannot open file for writing.\n");
487 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
495 if(verbose>1) printf(
"plotting measured and fitted data\n");
498 for(
int r=0; r<ttac.
tacNr; r++)
508 if(verbose>0) printf(
"Measured and fitted data plotted in %s\n", svgfile);
517 if(verbose>1) printf(
"calculating simulated TTACs\n");
522 fprintf(stderr,
"Error: cannot allocate space for simulated TTACs\n");
535 fitdata.yi=btac.
c[0].
y;
549 for(
int i=0; i<sim.
tacNr; i++) {
550 fitdata.yt=fitdata.syt=sim.
c[i].
y;
551 func_wpul(5, par.
r[i].
p, &fitdata);
555 if(verbose>1) printf(
"writing %s\n", simfile);
556 FILE *fp; fp=fopen(simfile,
"w");
558 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", simfile);
564 fprintf(stderr,
"Error (%d): %s\n", ret,
errorMsg(status.
error));
567 if(verbose>=0) {printf(
"%s saved.\n", simfile); fflush(stdout);}
581double func_wpul(
int parNr,
double *p,
void *fdata)
583 FITDATA *d=(FITDATA*)fdata;
585 if(d->verbose>0) {printf(
"%s()\n", __func__); fflush(stdout);}
586 if(parNr!=5 || p==NULL || fdata==NULL || d->ni<1 || d->nt<1)
return(nan(
""));
588 printf(
"p[]: %g", p[0]);
589 for(
int i=1; i<parNr; i++) printf(
" %g", p[i]);
590 printf(
"\n"); fflush(stdout);
594 double tau=p[0];
if(tau<0.0) tau=0.0;
596 double Vb=p[2];
if(Vb<0.0) Vb=0.0;
597 double K1=p[3]/60.0;
if(K1<0.0) K1=0.0;
598 double k2=p[4]/60.0;
if(k2<0.0) k2=0.0;
601 double x[d->ni], y[d->ni], sy[d->ni];
602 for(
unsigned int i=0; i<d->ni; i++) x[i]=d->xi[i]+deltaT;
603 for(
unsigned int i=0; i<d->ni; i++) y[i]=d->yi[i];
604 if(
simDispersion(x, y, d->ni, tau, 0.0, sy)!=0)
return(nan(
""));
607 if(
simC1(x, y, d->ni, K1, k2, sy)!=0)
return(nan(
""));
608 for(
unsigned int i=0; i<d->ni; i++) sy[i]+=Vb*y[i];
611 if(d->xt1==NULL || d->xt2==NULL) {
612 if(
liInterpolate(x, sy, d->ni, d->xt, d->syt, NULL, NULL, d->nt, 3, 1, 0))
615 if(
liInterpolateForPET(x, sy, d->ni, d->xt1, d->xt2, d->syt, NULL, NULL, d->nt, 3, 1, 0))
620 if(d->verbose>2) {fprintf(stdout,
"computing WSS...\n"); fflush(stdout);}
622 for(
unsigned i=0; i<d->nt; i++) {
623 double v=d->syt[i] - d->yt[i];
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)
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.
int liInterpolateForPET(double *x, double *y, const int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear TAC interpolation and/or integration to PET frames.
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)
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 simC1(double *t, double *ca, const int nr, const double k1, const double k2, double *ct)
int simDispersion(double *x, double *y, const int n, const double tau1, const double tau2, double *tmp)
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 tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
int tacAllocateMoreSamples(TAC *tac, int addNr)
Allocate memory for more samples in TAC data.
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 tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
int tacSortByTime(TAC *d, TPCSTATUS *status)
int tacYUnitConvert(TAC *tac, const int u, 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 tacFramesToSteps(TAC *inp, TAC *out, TPCSTATUS *status)
Transform TAC with frames into TAC with frames represented with stepwise changing dot-to-dot data.
int tacXCopy(TAC *tac1, TAC *tac2, int i1, int i2)
int tacSetXContiguous(TAC *d)
Set PET TAC frame times contiguous, without even tiny overlap or gaps in between.
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
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_ML_PER_ML_MIN
mL/(mL*min)
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.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Header file for libtpctacmod.