10#include "tpcclibConfig.h"
27static char *info[] = {
28 "Basis function method (BFM) fitting of one-tissue compartmental water model",
29 "to regional PET [O-15]H2O study data (Koeppe et al., 1985; Boellaard et al.,",
30 "2005). The model parameters are blood flow (F), partition coefficient",
31 "(pWater), and arterial blood volume (Va). Venous radioactivity is assumed to",
32 "be the same as the tissue concentration.",
33 "Extraction factor of water is assumed to be 1.0.",
35 "The blood flow obtained using PET [O-15]H2O techniques reflects tissue",
36 "perfusion, since the method is dependent on the tissue exchange of",
37 "labelled water. Non-nutritive flow (blood flowing through arteriovenous",
38 "shunts) is not measured (Lammertsma & Jones, 1983).",
40 "Radioactivity concentration in arterial blood is given in BTAC file, and",
41 "data must be corrected for physical decay and time delay.",
42 "Radioactivity concentration in tissue region(s) is given in TTAC file, and",
43 "data must be corrected for physical decay.",
44 "Sample times must be in seconds in all data files, unless specified inside",
45 "the files. Fitting time must be given in sec.",
47 "Usage: @P [options] BTAC TTAC fittime results",
51 " In results the units of F and Va will be given per mL or per dL,",
52 " respectively. By default, units are per dL.",
54 " Report blood flow (perfusion) per perfusable tissue volume",
55 " (PET volume minus vascular volume). By default perfusion is reported",
58 " Enter a fixed Va; fitted by default.",
59 " -pH2O=<Partition coefficient for water>",
60 " Enter the true partition coefficient for water to report perfusion as",
61 " F=k2*pH2O instead of default F=K1; apparent pH2O is fitted in any case.",
63 " Plots of original and fitted TTACs are written in specified file in",
66 " Fitted regional TTACs are written in specified file.",
67 " -k2min=<Min k2> and -k2max=<Max k2>",
68 " Enter the minimum and maximum k2 for BFM in units 1/min (for example",
70 " -fmin=<Min K1> and -fmax=<Max K1>",
71 " Enter the minimum and maximum perfusion value for BFM; defaults are",
72 " 0.5 and 400 ml/(dl*min), respectively.",
73 " -pmin=<Min p> and -max=<pmax>",
74 " Enter the minimum and maximum value for apparent partition coefficient",
75 " for water; defaults are 0.3 and 1.0 ml/ml, respectively.",
77 " Set number of basis functions; default is 5000.",
79 " Basis function curves are written in specified file.",
83 " @P uo1234bl.bld uo1234.tac 999 uo1234f.res",
86 "1. Koeppe RA et al. J Cereb Blood Flow metab. 1985;5:224-234.",
87 "2. Boellaard R et al. Mol Imaging Biol. 2005;7:273-285.",
88 "3. Lammertsma AA, Jones T. J Cereb Blood Flow Metab. 1983;3:416-424.",
90 "See also: fitdelay, b2t_h2o, fit_h2o, imgbfh2o, arlkup, fitk2",
92 "Keywords: TAC, modelling, perfusion, blood flow, radiowater, 1TCM",
111int main(
int argc,
char **argv)
113 int ai, help=0, version=0, verbose=1;
114 char btacfile[FILENAME_MAX], ttacfile[FILENAME_MAX], resfile[FILENAME_MAX],
115 fitfile[FILENAME_MAX], svgfile[FILENAME_MAX], bffile[FILENAME_MAX];
118 double fVaFixed=nan(
"");
119 double fitdur=nan(
"");
120 double pWaterTrue=nan(
"");
121 double flowMin=0.005, flowMax=4.0;
122 double pWaterMin=0.3, pWaterMax=1.0;
123 double k2min=nan(
""), k2max=nan(
"");;
125 int flow_per_perfusable_tissue=0;
132 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
133 btacfile[0]=ttacfile[0]=resfile[0]=fitfile[0]=svgfile[0]=bffile[0]=(char)0;
135 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
137 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
138 if(strcasecmp(cptr,
"DL")==0) {per_dl=1;
continue;}
139 else if(strcasecmp(cptr,
"ML")==0) {per_dl=0;
continue;}
140 else if(strcasecmp(cptr,
"FPT")==0) {
141 flow_per_perfusable_tissue=1;
continue;
142 }
else if(strncasecmp(cptr,
"Va=", 3)==0) {
144 if(!isnan(fVaFixed) && fVaFixed>=0.0 && fVaFixed<1.0) {
145 if(fVaFixed>0.0 && fVaFixed<0.01)
146 fprintf(stderr,
"Warning: Va was set to %g%%\n", 100.*fVaFixed);
149 }
else if(strncasecmp(cptr,
"pH2O=", 5)==0 && strlen(cptr)>5) {
151 if(!isnan(pWaterTrue) && pWaterTrue>0.0 && pWaterTrue<=1.25)
continue;
152 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
153 strlcpy(svgfile, cptr+4, FILENAME_MAX);
if(strlen(svgfile)>0)
continue;
154 }
else if(strncasecmp(cptr,
"FIT=", 4)==0) {
155 strlcpy(fitfile, cptr+4, FILENAME_MAX);
if(strlen(fitfile)>0)
continue;
156 }
else if(strncasecmp(cptr,
"NR=", 3)==0) {
157 if(
atoiCheck(cptr+3, &bfNr)==0 && bfNr>5)
continue;
158 }
else if(strncasecmp(cptr,
"BF=", 3)==0) {
159 strlcpy(bffile, cptr+3, FILENAME_MAX);
if(strlen(bffile)>0)
continue;
160 }
else if(strncasecmp(cptr,
"k2min=", 6)==0) {
161 if(
atofCheck(cptr+6, &k2min)==0 && k2min>=0.0)
continue;
162 }
else if(strncasecmp(cptr,
"k2max=", 6)==0) {
163 if(
atofCheck(cptr+6, &k2max)==0 && k2max>=0.0)
continue;
164 }
else if(strncasecmp(cptr,
"fmin=", 5)==0) {
165 if(
atofCheck(cptr+5, &flowMin)==0 && flowMin>=0.0) {flowMin*=0.01;
continue;}
166 }
else if(strncasecmp(cptr,
"fmax=", 5)==0) {
167 if(
atofCheck(cptr+5, &flowMax)==0 && flowMax>=0.0) {flowMax*=0.01;
continue;}
168 }
else if(strncasecmp(cptr,
"pmin=", 5)==0) {
169 if(
atofCheck(cptr+5, &pWaterMin)==0 && pWaterMin>0.0)
continue;
170 }
else if(strncasecmp(cptr,
"pmax=", 5)==0) {
171 if(
atofCheck(cptr+5, &pWaterMax)==0 && pWaterMax<1.25)
continue;
173 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
182 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
187 if(ai<argc)
strlcpy(btacfile, argv[ai++], FILENAME_MAX);
188 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
191 fprintf(stderr,
"Error: invalid fit time '%s'.\n", argv[ai]);
194 if(fitdur<=0.0) fitdur=1.0E+99;
197 if(ai<argc)
strlcpy(resfile, argv[ai++], FILENAME_MAX);
199 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
204 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
211 printf(
"btacfile := %s\n", btacfile);
212 printf(
"ttacfile := %s\n", ttacfile);
213 printf(
"resfile := %s\n", resfile);
214 printf(
"fitfile := %s\n", fitfile);
215 printf(
"svgfile := %s\n", svgfile);
216 printf(
"bffile := %s\n", bffile);
217 printf(
"per_dl := %d\n", per_dl);
218 printf(
"flow_per_perfusable_tissue := %d\n", flow_per_perfusable_tissue);
219 printf(
"required_fitdur := %g s\n", fitdur);
220 if(!isnan(fVaFixed)) printf(
"fVaFixed := %g%%\n", 100.*fVaFixed);
221 if(!isnan(pWaterTrue)) printf(
"pWaterTrue := %g mL/mL\n", pWaterTrue);
222 if(!isnan(k2min)) printf(
"k2min := %g min-1\n", k2min);
223 if(!isnan(k2max)) printf(
"k2max := %g min-1\n", k2max);
224 printf(
"flowMin := %g mL/(min*mL)\n", flowMin);
225 printf(
"flowMax := %g mL/(min*mL)\n", flowMax);
226 printf(
"pWaterMin := %g mL/mL\n", pWaterMin);
227 printf(
"pWaterMax := %g mL/mL\n", pWaterMax);
228 printf(
"bfNr := %d\n", bfNr);
232 if(flowMin>=flowMax) {
233 fprintf(stderr,
"Error: invalid range for perfusion (%g - %g).\n",
234 100.*flowMin, 100.*flowMax);
237 if(pWaterMin>=pWaterMax) {
238 fprintf(stderr,
"Error: invalid range for p (%g - %g).\n",
239 pWaterMin, pWaterMax);
243 k2min=flowMin/pWaterMax;
244 if(verbose>1) printf(
"k2min := %g min-1\n", k2min);
247 k2max=flowMax/pWaterMin;
248 if(verbose>1) printf(
"k2max := %g min-1\n", k2max);
250 if(k2max<=k2min || k2min<=0.) {
251 fprintf(stderr,
"Error: invalid range for k2 (%g - %g).\n", k2min, k2max);
255 k2min/=60.; k2max/=60.0;
257 printf(
"k2min := %g sec-1\n", k2min);
258 printf(
"k2max := %g sec-1\n", k2max);
265 if(verbose>1) printf(
"reading tissue and input data\n");
271 &fitSampleNr, &ttac, &btac, &status);
279 printf(
"tacNr := %d\n", ttac.
tacNr);
280 printf(
"ttac.sampleNr := %d\n", ttac.
sampleNr);
281 printf(
"btac.sampleNr := %d\n", btac.
sampleNr);
282 printf(
"fitSampleNr := %d\n", fitSampleNr);
285 printf(
"fitdur := %g s\n", fitdur);
291 if(fitSampleNr<4 || btac.
sampleNr<4) {
292 fprintf(stderr,
"Error: too few samples in specified fit duration.\n");
299 if(!
tacIsWeighted(&ttac) && verbose>0) fprintf(stderr,
"Note: data is not weighted.\n");
302 if(verbose>1) printf(
"interpolating BTAC to TTAC sample times\n");
311 if(!isnan(fVaFixed) && fVaFixed>0.0) {
312 if(verbose>1) printf(
"subtracting Va*BTAC from TTACs\n");
313 for(
int i=0; i<fitSampleNr; i++) {
314 if(isnan(tbtac.
c[0].
y[i]))
continue;
315 for(
int j=0; j<ttac.
tacNr; j++) {
316 if(isnan(ttac.
c[j].
y[i]))
continue;
317 ttac.
c[j].
y[i]-=fVaFixed*tbtac.
c[0].
y[i];
326 if(verbose>1) printf(
"calculating basis functions\n");
328 ret=
bfm1TCM(&btac, &ttac, bfNr, k2min, k2max, 0, &bf, &status);
330 if(verbose>1) fprintf(stderr,
"Error: cannot calculate basis functions.\n");
336 if(verbose>1) printf(
"writing %s\n", bffile);
337 FILE *fp; fp=fopen(bffile,
"w");
339 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", bffile);
350 if(verbose>0) printf(
"basis functions saved in %s.\n", bffile);
358 if(verbose>1) printf(
"initializing result data\n");
373 iftPut(&par.
h,
"program", buf, 0, NULL);
375 iftPut(&par.
h,
"bloodfile", btacfile, 0, NULL);
376 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
378 iftPut(&par.
h,
"fitmethod",
"BFM", 0, NULL);
381 sprintf(buf,
"%g %%", 100.0*fVaFixed);
382 iftPut(&par.
h,
"Vb", buf, 0, NULL);
385 sprintf(buf,
"%g", pWaterTrue);
386 iftPut(&par.
h,
"pH2O", buf, 0, NULL);
391 for(i=0; i<par.
tacNr; i++) {
394 par.
r[i].
end=fitdur/60.0;
399 i=0; strcpy(par.
n[i].
name,
"Flow");
403 i++; strcpy(par.
n[i].
name,
"Va");
413 if(fitfile[0] || svgfile[0]) {
414 if(verbose>1) printf(
"allocating space for fitted TTACs\n");
417 fprintf(stderr,
"Error: cannot allocate space for fitted TACs.\n");
429 if(verbose>1) printf(
"allocating memory for QR\n");
431 colNr=2;
if(fVaFixed>0.0) colNr--;
434 printf(
"QR_colNr := %d\n", colNr);
435 printf(
"QR_rowNr := %d\n", rowNr);
437 double *buf, **mat, *rhs, *sol, r2;
438 buf=(
double*)calloc(colNr*rowNr+rowNr+colNr,
sizeof(
double));
439 mat=(
double**)calloc(rowNr,
sizeof(
double*));
440 if(buf==NULL || mat==NULL) {
441 fprintf(stderr,
"Error: cannot allocate memory for QR\n");
446 for(
int i=0; i<rowNr; i++) mat[i]=buf+(i*colNr);
447 rhs=buf+(rowNr*colNr); sol=buf+(rowNr*colNr+rowNr);
452 if(verbose>1) printf(
"BFM fitting to TACs.\n");
455 for(
int i=0; i<ttac.
tacNr; i++) {
457 if(verbose>1 && ttac.
tacNr>1) printf(
"Region %d %s\n", i+1, ttac.
c[i].
name);
460 bi_min=-1; r2_min=nan(
"");
461 for(bi=0; bi<bf.
tacNr; bi++) {
463 if(verbose>5) printf(
"bi=%d\n", bi);
466 for(
int j=0; j<rowNr; j++) {
467 mat[j][0]=bf.
c[bi].
y[j];
468 if(colNr>1) mat[j][1]=tbtac.
c[0].
y[j];
469 rhs[j]=ttac.
c[i].
y[j];
472 if(verbose>5) printf(
" weighting\n");
477 if(verbose>5) printf(
" QR\n");
478 ret=
qrLSQ(mat, rhs, sol, rowNr, colNr, &r2);
480 fprintf(stderr,
"Error: no QR solution for BFM\n");
483 free(buf); free(mat);
487 printf(
"solution (%d):", bi);
488 for(
int j=0; j<colNr; j++) printf(
" %g", sol[j]);
489 printf(
"\nR^2= %g R=%g\n", r2, sqrt(r2));
492 if(isnan(r2_min) || r2_min>r2) {
493 r2_min=r2; bi_min=bi;
496 if(verbose>2) printf(
"Min basis function nr %d with R2=%g\n", bi_min+1, r2_min);
501 for(
int j=0; j<rowNr; j++) {
502 mat[j][0]=bf.
c[bi].
y[j];
503 if(colNr>1) mat[j][1]=tbtac.
c[0].
y[j];
504 rhs[j]=ttac.
c[i].
y[j];
507 if(verbose>5) printf(
" weighting\n");
511 if(verbose>5) printf(
" QR\n");
512 ret=
qrLSQ(mat, rhs, sol, rowNr, colNr, &r2);
514 fprintf(stderr,
"Error: no QR solution for BFM\n");
517 free(buf); free(mat);
522 printf(
"best_solution (%d):", bi);
523 for(
int j=0; j<colNr; j++) printf(
" %g", sol[j]);
524 printf(
"\nR^2= %g R=%g\n", r2, sqrt(r2));
529 if(fitfile[0] || svgfile[0]) {
530 for(
int j=0; j<ftac.
sampleNr; j++) ftac.
c[i].
y[j]=rhs[j];
533 double Flow, pH2O, Va, k2;
534 k2=bf.
c[bi_min].
size;
535 if(isnan(fVaFixed)) Va=sol[1];
else Va=fVaFixed;
541 Flow=sol[0]/(1.0-Va);
545 pH2O=sol[0]/((1.0-Va)*k2);
548 if(flow_per_perfusable_tissue==0) Flow*=(1.0-Va);
549 if(per_dl!=0) {Flow*=100.0; Va*=100;}
550 par.
r[i].
p[0]=60.0*Flow;
557 free(buf); free(mat);
569 if(verbose>1) printf(
"writing %s\n", resfile);
574 FILE *fp; fp=fopen(resfile,
"w");
576 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", resfile);
590 if(verbose>0) printf(
"Results saved in %s.\n", resfile);
599 if(verbose>1) printf(
"saving SVG plot\n");
602 sprintf(buf,
"Radiowater BFM fit");
605 if(i>=0) {strcat(buf,
": "); strcat(buf, ttac.
h.
item[i].
value);}
606 ret=
tacPlotFitSVG(&ttac, &ftac, buf, 0.0, nan(
""), 0.0, nan(
""), svgfile, &status);
613 if(verbose>0) printf(
"Plots written in %s.\n", svgfile);
620 if(verbose>1) printf(
"writing %s\n", fitfile);
621 FILE *fp; fp=fopen(fitfile,
"w");
623 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", fitfile);
636 if(verbose>0) printf(
"fitted TACs saved in %s.\n", fitfile);
int bfm1TCM(TAC *input, TAC *tissue, int bfNr, const double k2min, const double k2max, const int distr, TAC *bf, TPCSTATUS *status)
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)
int atofCheck(const char *s, double *v)
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
int iftFindKey(IFT *ift, const char *key, int start_index)
int atoiCheck(const char *s, int *v)
int tacInterpolate(TAC *inp, TAC *xinp, TAC *tac, TAC *itac, TAC *iitac, TPCSTATUS *status)
Interpolate and/or integrate TACs from one TAC structure into a new TAC structure,...
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 qrWeightRm(int N, int M, double **A, double *b, double *weight, double *ws)
int qrLSQ(double **mat, double *rhs, double *sol, const unsigned int rows, const unsigned int cols, double *r2)
QR least-squares solving routine.
int qrWeight(int N, int M, double **A, double *b, double *weight, double *ws)
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)
IFT h
Optional (but often useful) header information.
char name[MAX_PARNAME_LEN+1]
char name[MAX_TACNAME_LEN+1]
IFT h
Optional (but often useful) header information.
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 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)
char * tacFormattxt(tacformat c)
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacIsWeighted(TAC *tac)
Header file for libtpcbfm.
Header file for library libtpcextensions.
@ UNIT_ML_PER_ML_MIN
mL/(mL*min)
@ UNIT_ML_PER_DL_MIN
mL/(dL*min)
@ UNIT_PERCENTAGE
Percentage (%).
char * unitName(int unit_code)
Header file for library libtpcift.
Header file for libtpclinopt.
Header file for libtpcpar.
@ PAR_FORMAT_RES
Model result format of Turku PET Centre.
@ PAR_FORMAT_UNKNOWN
Unknown format.
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Header file for libtpctacmod.