8#include "tpcclibConfig.h"
34double func_wcbf(
int parNr,
double *p,
void*);
36typedef struct FITDATA {
68static char *info[] = {
69 "Non-linear fitting of the radiowater model with PVC to brain TTACs using BTAC",
70 "as the input function (Iida et al., 2000).",
72 "Usage: @P [Options] btacfile ttacfile [parfile]",
76 " Arterial blood volume is constrained to given value (mL/mL).",
78 " Delay time (activity appearance time in tissue minus that in blood)",
79 " in seconds. Dispersion in BTAC is corrected in relation to delay",
80 " as tau=0.31*dt-0.30 (Iida et al., 1989), when tau>0.",
82 " Partition coefficient of water in grey matter; by default 1.03 mL/mL.",
84 " Partition coefficient of water in white matter; by default 0.86 mL/L.",
85 " -fWM=<value> | fit",
86 " Blood flow in white matter; by default 0.21 mL/(min*mL).",
87 " Enter 'fit' to estimate fW as a free parameter.",
89 " By default, weights in data file are used, if available.",
90 " With these options all weights can be set to 1.0 (no weighting)",
91 " or based on frame durations.",
93 " Fitted and measured TACs are plotted in specified SVG file.",
95 " Fitted TTACs are written in specified TAC file.",
97 " Fitted TTACs at BTAC sample times are saved in specified TAC file.",
100 "Sample times must be in seconds, unless units are specified in the file.",
103 "1. Iida H et al., Quantitation of regional cerebral blood flow corrected",
104 " for partial volume effect using O-15 water and PET: I. Theory, error",
105 " analysis, and stereologic comparison.",
106 " J Cereb Blood Flow Metab. 2000;20(8):1237-1251.",
107 " https://doi.org/10.1097/00004647-200008000-00009",
108 "2. Iida et al., A determination of the regional brain/blood partition",
109 " coefficient of water using dynamic positron emission tomography.",
110 " J Cereb Blood Flow Metab. 1989;9(6):874-885.",
111 " https://doi.org/10.1038/jcbfm.1989.121",
113 "See also: fit_h2o, bfmh2o, fitk2",
115 "Keywords: TAC, modelling, perfusion, brain, radiowater",
134int main(
int argc,
char **argv)
136 int ai, help=0, version=0, verbose=1;
137 char btacfile[FILENAME_MAX], ttacfile[FILENAME_MAX], parfile[FILENAME_MAX],
138 svgfile[FILENAME_MAX], simfile[FILENAME_MAX], fitfile[FILENAME_MAX];
140 double fixed_dt=nan(
"");
141 double fixed_va=nan(
"");
142 double fixed_pg=1.03;
143 double fixed_pw=0.86;
144 double fixed_fw=0.21;
153 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
154 btacfile[0]=ttacfile[0]=parfile[0]=svgfile[0]=fitfile[0]=simfile[0]=(char)0;
156 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
158 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
159 if(strcasecmp(cptr,
"W1")==0) {
161 }
else if(strcasecmp(cptr,
"WF")==0) {
163 }
else if(strncasecmp(cptr,
"VA=", 3)==0 && strlen(cptr)>3) {
164 if(!
atofCheck(cptr+3, &fixed_va) && fixed_va>=0.0 && fixed_va<1.0)
continue;
165 }
else if(strncasecmp(cptr,
"DT=", 3)==0 && strlen(cptr)>3) {
166 if(!
atofCheck(cptr+3, &fixed_dt) && fixed_dt>-50.0 && fixed_dt<50.0)
continue;
167 }
else if(strncasecmp(cptr,
"DELAY=", 6)==0 && strlen(cptr)>6) {
168 if(!
atofCheck(cptr+6, &fixed_dt) && fixed_dt>-50.0 && fixed_dt<50.0)
continue;
169 }
else if(strncasecmp(cptr,
"PGM=", 4)==0 && strlen(cptr)>4) {
170 if(!
atofCheck(cptr+4, &fixed_pg) && fixed_pg>0.0 && fixed_pg<1.05)
continue;
171 }
else if(strncasecmp(cptr,
"PWM=", 4)==0 && strlen(cptr)>4) {
172 if(!
atofCheck(cptr+4, &fixed_pw) && fixed_pw>0.0 && fixed_pw<1.05)
continue;
173 }
else if(strncasecmp(cptr,
"FWM=FIT", 5)==0) {
174 fixed_fw=nan(
"");
continue;
175 }
else if(strncasecmp(cptr,
"FWM=", 4)==0 && strlen(cptr)>4) {
176 if(!
atofCheck(cptr+4, &fixed_fw) && fixed_fw>=0.0 && fixed_fw<1.0)
continue;
177 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
178 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
179 }
else if(strncasecmp(cptr,
"FIT=", 4)==0 && strlen(cptr)>4) {
180 strlcpy(fitfile, cptr+4, FILENAME_MAX);
continue;
181 }
else if(strncasecmp(cptr,
"SIM=", 4)==0 && strlen(cptr)>4) {
182 strlcpy(simfile, cptr+4, FILENAME_MAX);
continue;
184 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
193 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
198 if(ai<argc)
strlcpy(btacfile, argv[ai++], FILENAME_MAX);
199 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
200 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
202 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
207 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
213 printf(
"btacfile := %s\n", btacfile);
214 printf(
"ttacfile := %s\n", ttacfile);
215 if(parfile[0]) printf(
"parfile := %s\n", parfile);
216 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
217 if(fitfile[0]) printf(
"fitfile := %s\n", fitfile);
218 if(simfile[0]) printf(
"simfile := %s\n", simfile);
219 printf(
"weights := %d\n",
weights);
220 printf(
"fixed_pg := %g\n", fixed_pg);
221 printf(
"fixed_pw := %g\n", fixed_pw);
222 if(!isnan(fixed_va)) printf(
"fixed_va := %g\n", fixed_va);
223 if(!isnan(fixed_fw)) printf(
"fixed_fw := %g\n", fixed_fw);
224 if(!isnan(fixed_dt)) printf(
"fixed_dt := %g\n", fixed_dt);
232 if(verbose>1) printf(
"reading TACs\n");
242 printf(
"ttacNr := %d\n", ttac.
tacNr);
243 printf(
"ttac.sampleNr := %d\n", ttac.
sampleNr);
253 printf(
"btacNr := %d\n", btac.
tacNr);
254 printf(
"btac.sampleNr := %d\n", btac.
sampleNr);
258 if(verbose>0) fprintf(stderr,
"Warning: BTAC file contains more than two TACs.\n");
263 fprintf(stderr,
"Error: too few samples.\n");
268 fprintf(stderr,
"Error: data contains missing values.\n");
276 fprintf(stderr,
"Error: invalid data sample times.\n");
288 fprintf(stderr,
"Error: check and set the data units.\n");
294 fprintf(stderr,
"Error: invalid data sample times.\n");
298 printf(
"xmin := %g\n", xmin);
299 printf(
"xmax := %g\n", xmax);
316 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
320 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
332 if(verbose>1) printf(
"preparing space for parameters\n");
345 iftPut(&par.
h,
"program", buf, 0, NULL);
349 for(
int i=0; i<par.
tacNr; i++) {
363 iftPut(&par.
h,
"inputfile", btacfile, 0, NULL);
364 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
373 if(verbose==1) {printf(
"\nfitting...\n"); fflush(stdout);}
376 for(
int ri=0; ri<ttac.
tacNr; ri++) {
377 if(verbose>1) {printf(
"\nfitting %s\n", ttac.
c[ri].
name); fflush(stdout);}
382 fitdata.yi=btac.
c[0].
y;
393 fitdata.yt=ttac.
c[ri].
y;
395 fitdata.syt=ttac.
c[ttac.
tacNr+ri].
y;
396 fitdata.pGM=fixed_pg;
397 fitdata.pWM=fixed_pw;
398 if(verbose>10) fitdata.verbose=verbose-10;
else fitdata.verbose=0;
402 fprintf(stderr,
"Error: cannot initiate NLLS.\n"); fflush(stderr); failed++;
416 if(isnan(fixed_fw)) {
422 if(isnan(fixed_va)) {
428 if(isnan(fixed_dt)) {
440 printf(
"\nTime\tTTAC\tsimTTAC\n");
441 for(
unsigned int i=0; i<fitdata.nt; i++)
442 printf(
"%g\t%g\t%g\n", ttac.
x[i], ttac.
c[ri].
y[i], ttac.
c[ri+ttac.
tacNr].
y[i]);
443 printf(
"\n"); fflush(stdout);
445 if(verbose>1) {printf(
"\twss := %g\n", wss); fflush(stdout);}
450 printf(
"initial guess\n");
460 fprintf(stderr,
"Error: %s\n",
errorMsg(status.
error)); fflush(stderr); failed++;
466 printf(
"\nTime\tTTAC\tsimTTAC\n");
467 for(
unsigned int i=0; i<fitdata.nt; i++)
468 printf(
"%g\t%g\t%g\n", ttac.
x[i], ttac.
c[ri].
y[i], ttac.
c[ri+ttac.
tacNr].
y[i]);
469 printf(
"\n"); fflush(stdout);
472 if(verbose>2) printf(
" wss := %g\n", wss);
478 for(
int i=0; i<par.
parNr; i++) par.
r[ri].
p[i]=nlo.
xfull[i];
479 par.
r[ri].
p[2]*=1.0-par.
r[ri].
p[0]-par.
r[ri].
p[4];
494 if(verbose>1) printf(
" saving %s\n", parfile);
495 FILE *fp=fopen(parfile,
"w");
497 fprintf(stderr,
"Error: cannot open file for writing.\n");
506 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
513 if(svgfile[0] || fitfile[0]) {
517 for(
int r=0; r<ttac.
tacNr; r++)
525 if(verbose>1) printf(
"plotting measured and fitted data\n");
532 if(verbose>0) printf(
"Measured and fitted data plotted in %s\n", svgfile);
539 if(verbose>1) printf(
"writing %s\n", fitfile);
541 FILE *fp; fp=fopen(fitfile,
"w");
543 fprintf(stderr,
"Error: cannot open file for writing fitted TTACs.\n");
554 if(verbose>0) printf(
"fitted TACs saved in %s.\n", fitfile);
565 if(verbose>1) printf(
"calculating simulated TTACs\n");
570 fprintf(stderr,
"Error: cannot allocate space for simulated TTACs\n");
583 fitdata.yi=btac.
c[0].
y;
595 fitdata.pGM=fixed_pg;
596 fitdata.pWM=fixed_pw;
602 for(
int ri=0; ri<sim.
tacNr; ri++) {
603 fitdata.yt=fitdata.syt=sim.
c[ri].
y;
604 for(
int j=0; j<6; j++) pp[j]=par.
r[ri].
p[j];
605 pp[2]=par.
r[ri].
p[2]/(1.0-par.
r[ri].
p[0]-par.
r[ri].
p[4]);
607 func_wcbf(6, pp, &fitdata);
611 if(verbose>1) printf(
"writing %s\n", simfile);
612 FILE *fp; fp=fopen(simfile,
"w");
614 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", simfile);
620 fprintf(stderr,
"Error (%d): %s\n", ret,
errorMsg(status.
error));
623 if(verbose>=0) {printf(
"%s saved.\n", simfile); fflush(stdout);}
637double func_wcbf(
int parNr,
double *p,
void *fdata)
639 FITDATA *d=(FITDATA*)fdata;
641 if(d->verbose>0) {printf(
"%s() %d\n", __func__, d->verbose); fflush(stdout);}
642 if(parNr!=6 || p==NULL || fdata==NULL || d->ni<1 || d->nt<1)
return(nan(
""));
644 printf(
"p[]: %g", p[0]);
645 for(
int i=1; i<parNr; i++) printf(
" %g", p[i]);
646 printf(
"\n"); fflush(stdout);
651 double fGM=p[1]/60.0;
652 double fWM=p[3]/60.0;
655 double tau=0.31*dT-0.30;
if(!(tau>=0.0)) tau=0.0;
656 double aWM=p[2]*(1.0-aGM-Va);
661 double x[d->ni], y[d->ni], sy[d->ni];
662 for(
unsigned int i=0; i<d->ni; i++) x[i]=d->xi[i]+dT;
663 for(
unsigned int i=0; i<d->ni; i++) y[i]=d->yi[i];
664 if(
simDispersion(x, y, d->ni, tau, 0.0, sy)!=0)
return(nan(
""));
668 if(
simC1_d(x, y, d->ni, fGM, fGM/d->pGM, cgm)!=0)
return(nan(
""));
670 if(
simC1_d(x, y, d->ni, fWM, fWM/d->pWM, cwm)!=0)
return(nan(
""));
671 for(
unsigned int i=0; i<d->ni; i++)
672 sy[i] = Va*y[i] + aGM*cgm[i] + aWM*cwm[i];
675 if(d->xt1==NULL || d->xt2==NULL) {
676 if(
liInterpolate(x, sy, d->ni, d->xt, d->syt, NULL, NULL, d->nt, 3, 1, 0))
679 if(
liInterpolateForPET(x, sy, d->ni, d->xt1, d->xt2, d->syt, NULL, NULL, d->nt, 3, 1, 0))
684 if(d->verbose>2) {fprintf(stdout,
"computing WSS...\n"); fflush(stdout);}
686 for(
unsigned i=0; i<d->nt; i++) {
687 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 iftPutDouble(IFT *ift, const char *key, const double 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_d(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 nloptSimplexMS(NLOPT *nlo, unsigned int spNr, 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 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)
@ TPCERROR_FAIL
General error.
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.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Header file for libtpctacmod.