8#include "tpcclibConfig.h"
24int fitframeNr=0, parNr=0;
28double func(
int parNr,
double *p,
void*);
32static char *info[] = {
33 "Non-linear fitting of compartmental model for whole body production of",
34 "labelled water during [O-15]O2 bolus PET studies.",
35 "Co(t) and Cw(t) represent the concentrations of [O-15]O2 and [O-15]H2O in",
36 "arterial blood, respectively, and Ct(t) represents the whole body tissue",
37 "compartment for [O-15]H2O.",
39 "The model is based on the following articles:",
40 "1. Huang S-C et al. Modelling approach for separating blood time-activity",
41 " curves in positron emission tomographic studies.",
42 " Phys Med Biol. 1991;36(6):749-761.",
43 "2. Iida H et al. Modelling approach to eliminate the need to separate",
44 " arterial plasma in oxygen-15 inhalation positron emission tomography.",
45 " J Nucl Med. 1993;34:1333-1340.",
46 "3. Kudomi N et al. A physiologic model for recirculation water correction",
47 " in CMRO2 assessment with 15O2 inhalation PET.",
48 " J Cereb Blood Flow Metab. 2009;29(2):355-364.",
49 " ______ ______ ______ ",
50 " | | k1 | | k3 | | ",
51 " | Co | ---> | Cw | ---> | Ct | ",
53 " |______| |______| k4 |______| ",
55 "The corresponding differential equations are: ",
56 " dCw(t)/dt = k1*Cb(t-delay) - (k1+k3)*Cw(t) + k4*Ct(t) ",
57 " dCt(t)/dt = k3*Cw(t) - k4*Ct(t) ",
58 ", where Cb(t)=Co(t)+Cw(t) ",
59 "If k4 is assumed to zero, then:",
60 " dCw(t)/dt = k1*Cb(t-delay) - (k1+k3)*Cw(t)",
62 "Total arterial blood curve Cb(t), and Cw(t) are required for the fitting.",
63 "TAC files must be corrected for physical decay.",
64 "Cw(t) can be calculated from measured arterial plasma curve Ca(t), because",
65 "oxygen concentration in plasma is negligible, by multiplying it with",
66 "[O-15]H2O blood-to-plasma ratio, for example using program o2_p2w.",
68 "The estimated model parameters, or population averages, can then be used to",
69 "calculate the [O-15]O2 and [O-15]H2O blood curves using program o2metab.",
71 "Usage: @P [Options] cbtacfile cwtacfile resultfile",
75 " Select the model: with k4 the full model is fitted (default);",
76 " with k3 it is assumed that k4=0.",
77 " Time delay is fitted in both models by default.",
79 " Constrain delay time to specified value (sec); fitted by default.",
81 " Constrain k3 to specified value (1/sec); fitted by default.",
83 " Constrain k3/k4 to specified value (unitless); fitted by default.",
84 " -w1 All weights are set to 1.0 (no weighting); by default, weights in",
85 " data file are used, if available.",
87 " Weight by sampling interval.",
89 " Fitted and measured TACs are plotted in specified SVG file.",
91 " Fitted Cw(t) TAC is written in specified TAC file.",
94 "Results can be saved in RES, IFT, or HTML format.",
95 "File will contain the parameters k1, k1+k3, and delay (model k3), or, ",
96 "k1, k3, k3/k4, and delay (model k4).",
98 "See also: o2metab, o2_p2w, sim_o2bl, fit_mo2, b2t_mo2",
100 "Keywords: input, oxygen, blood, metabolite correction",
119int main(
int argc,
char **argv)
121 int ai, help=0, version=0, verbose=1;
123 btacfile[FILENAME_MAX], wtacfile[FILENAME_MAX],
124 parfile[FILENAME_MAX], ftacfile[FILENAME_MAX],
125 svgfile[FILENAME_MAX];
132 int ret, times_changed=0;
139 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
141 btacfile[0]=wtacfile[0]=parfile[0]=ftacfile[0]=svgfile[0]=(char)0;
142 fixedDelay=fixedk3=fixedk3k4=nan(
"");
144 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
146 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
147 if(strncasecmp(cptr,
"MODEL=", 6)==0) {
149 if(strncasecmp(cptr,
"K3", 2)==0) {model=1;
continue;}
150 if(strncasecmp(cptr,
"K4", 2)==0) {model=2;
continue;}
151 fprintf(stderr,
"Error: invalid model setting.\n");
return(1);
152 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
153 strlcpy(svgfile, cptr+4, FILENAME_MAX);
154 if(strlen(svgfile)>0)
continue;
155 }
else if(strncasecmp(cptr,
"FIT=", 4)==0) {
156 strlcpy(ftacfile, cptr+4, FILENAME_MAX);
157 if(strlen(ftacfile)>0)
continue;
158 }
else if(strcasecmp(cptr,
"W1")==0) {
160 }
else if(strcasecmp(cptr,
"WF")==0) {
162 }
else if(strncasecmp(cptr,
"DELAY=", 6)==0) {
163 fixedDelay=
atof_dpi(cptr+6);
if(!isnan(fixedDelay))
continue;
164 }
else if(strncasecmp(cptr,
"K3=", 3)==0) {
165 fixedk3=
atof_dpi(cptr+3);
if(!isnan(fixedk3))
continue;
166 }
else if(strncasecmp(cptr,
"K3K4=", 5)==0) {
167 fixedk3k4=
atof_dpi(cptr+5);
if(!isnan(fixedk3k4))
continue;
169 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
174 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
179 for(; ai<argc; ai++) {
180 if(!btacfile[0]) {
strlcpy(btacfile, argv[ai], FILENAME_MAX);
continue;}
181 if(!wtacfile[0]) {
strlcpy(wtacfile, argv[ai], FILENAME_MAX);
continue;}
182 if(!parfile[0]) {
strlcpy(parfile, argv[ai], FILENAME_MAX);
continue;}
183 fprintf(stderr,
"Error: too many arguments: '%s'.\n", argv[ai]);
189 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
192 fitparNr=parNr=model+2;
193 if(!isnan(fixedDelay)) fitparNr--;
194 if(!isnan(fixedk3)) fitparNr--;
195 if(!isnan(fixedk3k4)) fitparNr--;
199 printf(
"btacfile := %s\n", btacfile);
200 printf(
"wtacfile := %s\n", wtacfile);
201 printf(
"parfile := %s\n", parfile);
202 if(ftacfile[0]) printf(
"ftacfile := %s\n", ftacfile);
203 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
204 printf(
"model := %d\n", model);
205 printf(
"parNr := %d\n", parNr);
206 if(!isnan(fixedDelay)) printf(
"fixedDelay := %g\n", fixedDelay);
207 if(!isnan(fixedk3)) printf(
"fixedk3 := %g\n", fixedk3);
208 if(!isnan(fixedk3k4)) printf(
"fixedk3k4 := %g\n", fixedk3k4);
209 printf(
"weights := %d\n", weights);
215 if(verbose>1) printf(
"reading '%s'\n", btacfile);
216 if(
dftRead(btacfile, &blood)) {
217 fprintf(stderr,
"Error in reading '%s': %s\n", btacfile,
dfterrmsg);
221 fprintf(stderr,
"Error: not enough input samples for decent fitting.\n");
228 fprintf(stderr,
"Error: missing sample(s) in %s\n", btacfile);
232 fprintf(stderr,
"Warning: BTAC file may not be valid.\n");
240 if(verbose>0) fprintf(stderr,
"Note: BTAC sample times are converted to sec.\n");
248 if(verbose>1) printf(
"reading '%s'\n", wtacfile);
249 if(
dftRead(wtacfile, &water)) {
250 fprintf(stderr,
"Error in reading '%s': %s\n", wtacfile,
dfterrmsg);
254 fprintf(stderr,
"Error: not enough samples for decent fitting.\n");
260 fprintf(stderr,
"Warning: [O-15]H2O BTAC file may not be valid.\n");
268 if(verbose>0) fprintf(stderr,
"Note: [O-15]H2O BTAC sample times are converted to sec.\n");
273 double tstart, tstop, miny, maxy;
274 ret=
dftMinMax(&water, &tstart, &tstop, &miny, &maxy);
275 if(ret!=0 || tstop<=0.0 || maxy<=0.0) {
276 fprintf(stderr,
"Error: invalid contents in %s\n", wtacfile);
279 if(tstart<0.0) fprintf(stderr,
"Warning: negative x value(s).\n");
280 if(miny<0.0) fprintf(stderr,
"Warning: negative y value(s).\n");
284 printf(
"fitframeNr := %d\n", fitframeNr);
285 printf(
"tstart := %g\n", tstart);
286 printf(
"tstop := %g\n", tstop);
287 printf(
"miny := %g\n", miny);
288 printf(
"maxy := %g\n", maxy);
295 }
else if(weights==1) {
297 }
else if(weights==2) {
303 for(i=0; i<water.
frameNr; i++)
if(isnan(water.
voi[0].
y[i])) water.
w[i]=0.0;
else n++;
305 fprintf(stderr,
"Error: not enough good samples for decent fitting.\n");
314 if(verbose>1) printf(
"initializing result data\n");
316 fprintf(stderr,
"Error: cannot setup memory for results.\n");
327 sprintf(res.
datarange,
"%g - %g %s", tstart, tstop, dftTimeunit(water.
timeunit));
336 i=0; strcpy(res.
parname[i],
"k1"); strcpy(res.
parunit[i],
"1/sec");
338 i++; strcpy(res.
parname[i],
"k1+k3"); strcpy(res.
parunit[i],
"1/sec");
340 i++; strcpy(res.
parname[i],
"k3"); strcpy(res.
parunit[i],
"1/sec");
345 i++; strcpy(res.
parname[i],
"delay"); strcpy(res.
parunit[i],
"sec");
353 if(verbose>2) printf(
"preparing for fitting\n");
356 pmin[0]=-0.2*tstop; pmax[0]=0.2*tstop;
357 pmin[1]=0.000; pmax[1]=0.005;
358 pmin[2]=0.000; pmax[2]=0.010;
359 pmin[3]=0.500; pmax[3]=100.0;
360 if(!isnan(fixedDelay)) pmin[0]=pmax[0]=fixedDelay;
361 if(!isnan(fixedk3)) pmin[2]=pmax[2]=fixedk3;
362 if(!isnan(fixedk3k4)) pmin[3]=pmax[3]=fixedk3k4;
367 pmin, pmax, func, NULL, parNr, 6,
368 &wss, p, 1000, 0, verbose-8);
370 fprintf(stderr,
"Error in optimization (%d).\n", ret);
374 printf(
"fitted parameters:");
375 for(
int i=0; i<parNr; i++) printf(
" %g", p[i]);
376 printf(
"\nwss := %g\n", wss);
397 if(verbose>0) {
resPrint(&res); fprintf(stdout,
"\n");}
403 if(verbose>1) printf(
"writing results in %s\n", parfile);
406 if(strcasecmp(cptr,
".RES")==0) {
407 ret=
resWrite(&res, parfile, verbose-4);
409 fprintf(stderr,
"Error in writing '%s': %s\n", parfile,
reserrmsg);
415 ret=
res2ift(&res, &ift, verbose-3);
417 fprintf(stderr,
"Error in converting results.\n");
423 fprintf(stderr,
"Error in writing '%s'.\n", parfile);
429 if(verbose>0) fprintf(stdout,
"Model parameters written in %s\n", parfile);
435 if(svgfile[0] || ftacfile[0]) {
442 fprintf(stderr,
"Error: cannot save fitted curves.\n");
446 for(
int ri=0; ri<water.
voiNr; ri++)
for(
int fi=0; fi<fitframeNr; fi++)
453 if(verbose>1) printf(
"saving SVG plot\n");
454 sprintf(tmp,
"Fitted [O-15]H2O BTAC");
456 strcat(tmp,
": "); strcat(tmp, water.
studynr);
458 ret=
plot_fitrange_svg(&water, &fit, tmp, 0.0, 1.02*tstop, 0.0, nan(
""), svgfile, verbose-8);
460 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
464 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
469 if(verbose>1) printf(
"saving fitted curves\n");
472 fprintf(stderr,
"Error in writing '%s': %s\n", ftacfile,
dfterrmsg);
476 if(verbose>0) printf(
"Fitted TACs written in %s\n", ftacfile);
482 if(verbose>0) printf(
"\n");
493double func(
int parNr,
double *p,
void *fdata)
496 double dt, k1, k2, k3, k4=0.0, wss=0.0;
504 dt=pa[0]; k1=k2=pa[1]; k3=pa[2];
if(parNr>=4) k4=k3/pa[3];
509 k1, k2, k3, k4, 0.0, 0.0,
510 blood.
voi[0].
y3, blood.
voi[0].
y2, NULL, NULL);
512 printf(
"error %d in simulation\n", ret);
517 for(i=0; i<blood.
frameNr; i++) blood.
x1[i]=blood.
x[i]+dt;
523 water.
x1, water.
x2, water.
voi[0].
y2, NULL, NULL, fitframeNr);
527 water.
x, water.
voi[0].
y2, NULL, NULL, fitframeNr);
529 printf(
"error %d in interpolation\n", ret);
534 for(i=0, wss=0.0; i<fitframeNr; i++)
if(water.
w[i]>0.0) {
540 if(0) printf(
"k1=%g k2=%g k3=%g k4=%g dt=%g => %g\n",
541 k1, k2, k3, k4, dt, wss);
int modelCheckParameters(int par_nr, double *lower_p, double *upper_p, double *test_p, double *accept_p, double *penalty)
double atof_dpi(char *str)
int dftdup(DFT *dft1, DFT *dft2)
int dftSortByFrame(DFT *dft)
int dftMinMax(DFT *dft, double *minx, double *maxx, double *miny, double *maxy)
int dft_nr_of_NA(DFT *dft)
int dftRead(char *filename, DFT *data)
int dftWrite(DFT *data, char *filename)
int res_allocate_with_dft(RES *res, DFT *dft)
void dftMin2sec(DFT *dft)
char * filenameGetExtension(char *s)
Get the last extension of a filename.
int iftWrite(IFT *ift, char *filename, int verbose)
int interpolate(double *x, double *y, int nr, double *newx, double *newy, double *newyi, double *newyii, int newnr)
Linear interpolation and integration.
int interpolate4pet(double *x, double *y, int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, int newnr)
Interpolate and integrate TAC to PET frames.
Header file for libtpccurveio.
int resWrite(RES *res, char *filename, int verbose)
int res2ift(RES *res, IFT *ift, int verbose)
#define DFT_TIME_STARTEND
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
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)
Header file for libtpcmodel.
int tgo(double *lowlim, double *uplim, double(*objf)(int, double *, void *), void *objfData, int dim, int neighNr, double *fmin, double *gmin, int samNr, int tgoNr, int verbose)
int simC3s(double *t, double *ca, int nr, double k1, double k2, double k3, double k4, double k5, double k6, double *ct, double *cta, double *ctb, double *ctc)
Header file for libtpcmodext.
int dftWeightByFreq(DFT *dft)
int plot_fitrange_svg(DFT *dft1, DFT *dft2, char *main_title, double x1, double x2, double y1, double y2, char *fname, int verbose)
Header file for libtpcsvg.
char studynr[MAX_STUDYNR_LEN+1]
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char datafile[FILENAME_MAX]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char bloodfile[FILENAME_MAX]
double parameter[MAX_RESPARAMS]