10#include "tpcclibConfig.h"
29double *t, *cr, *ct, *tis, *w;
31double wss_wo_penalty=0.0;
33double trtmFunc(
int parNr,
double *p,
void*);
37static char *info[] = {
38 "NLLSQ estimation of R1 (=K1/K1'), k2', and k3 applying the transport-limited",
39 "reference tissue compartment model, TRTM (1). This model is based on",
40 "the reference tissue compartment model (2), but here it is assumed that",
41 "metabolism of tracer is irreversible (k4=0) during the PET scanning, and that",
42 "in (positive) reference tissue k3'>>k2', and thus the uptake in it is limited",
43 "only by transport into tissue (3,4).",
45 "Usage: @P [Options] ttacfile reference endtime resultfile",
47 "TTAC file can be in DFT or PMOD format. Sample times must be in minutes.",
48 "If TTAC file contains weights, those are used in the NLLSQ fitting.",
49 "Reference region TAC can be given separate TAC file or as the name or number",
50 "of the reference region in TTAC file.",
53 " -rk2=<<value>|mean|median>",
54 " Constrain k2' to specified <value>, or to mean or median of regional",
55 " k2' values excluding reference region(s)",
57 " Specify the constraints for model parameters;",
58 " This file with default values can be created by giving this option",
59 " as the only command-line argument to this program.",
61 " Standard deviations are calculated and saved in results (y), or",
62 " not calculated (n).",
64 " 95% Confidence limits are calculated and saved in results (y), or",
65 " not calculated (n).",
67 " All weights are set to 1.0 (no weighting); by default, weights in",
68 " TTAC file are used, if available.",
70 " Weight by sampling interval.",
72 " Fitted regional TACs are written in file.",
74 " Fitted and measured TACs are plotted in specified SVG file.",
78 "Values of R1, k2, and k3 are written in the specified result file.",
79 "Fitted curves are written in DFT format, if file name is given.",
81 "Example 1: file a789.tac contains regions-of-interest and positive reference",
82 "region, with name 'putam'. The whole time range is used in the fit.",
83 "Fitted TACs are plotted in SVG format in file a789trtm.svg.",
84 " @P -svg=a789trtm.svg a789.tac putam 999 a789.res",
86 "Example 2: Reference region TAC is in a separate file, a789ref.tac.",
87 "Standard deviations and confidence limits are also estimated.",
88 "TAC data from injection to 60 min is used in the fitting.",
89 " @P -SD=y -CL=y a789.tac a789ref.tac 60 a789.res",
91 "Example 3a: Create a file containing default parameter limits:",
94 "Example 3b: Apply user-defined parameter constraints specified in trtm.lim:",
95 " @P -lim=trtm.lim a789.tac 'putam mean' 999 a789.tac",
98 "1. Oikonen V. Model equations for reference tissue compartmental models.",
99 " https://www.turkupetcentre.net/reports/tpcmod0002.pdf",
100 "2. Cunningham VJ, Hume SP, Price GR, Ahier RG, Cremer JE, Jones AKP.",
101 " Compartmental analysis of diprenorphine binding to opiate receptors",
102 " in the rat in vivo and its comparison with equilibrium data in vitro.",
103 " J Cereb Blood Flow Metab 1991;11:1-9.",
104 "3. Herholz K, Lercher M, Wienhard K, Bauer B, Lenz O, Heiss W-D.",
105 " PET measurement of cerebral acetylcholine esterase activity without",
106 " blood sampling. Eur J Nucl Med 2001;28:472-477.",
107 "4. Nagatsuka S, Fukushi K, Shinotoh H, Namba H, Iyo M, Tanaka N, Aotsuka A,",
108 " Ota T, Tanada S, Irie T. Kinetic analysis of [11C]MP4A using a high-",
109 " radioactivity brain region that represents an integrated input function",
110 " for measurement of cerebral acetylcholinesterase activity without",
111 " arterial blood sampling. J Cereb Blood Flow Metab 2001; 21: 1354-1366.",
113 "See also: fitk3, tacweigh, fit_rrtm, lhtrtm, sim_rtcm, rescoll",
115 "Keywords: TAC, modelling, irreversible uptake, RTCM, reference input",
134int main(
int argc,
char **argv)
136 int ai, help=0, version=0, verbose=1;
137 char rtacfile[FILENAME_MAX], ttacfile[FILENAME_MAX], resfile[FILENAME_MAX],
138 fitfile[FILENAME_MAX], svgfile[FILENAME_MAX], limfile[FILENAME_MAX];
142 char *cptr, tmp[256];
143 double fitdur=nan(
"");
145 int doBootstrap=0, doSD=0, doCL=0;
146 double *sd, *cl1, *cl2;
152 def_pmin[0]=0.001; def_pmax[0]=10.0;
153 def_pmin[1]=0.000001; def_pmax[1]=1.0;
154 def_pmin[2]=0.0; def_pmax[2]=10.0;
159 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
160 rtacfile[0]=ttacfile[0]=resfile[0]=fitfile[0]=svgfile[0]=limfile[0]=(char)0;
162 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
163 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
165 if(strncasecmp(cptr,
"CL", 2)==0) {
166 if(strlen(cptr)==2) {doCL=1;
continue;}
167 cptr+=2;
if(*cptr==
'=') {
169 if(*cptr==
'Y' || *cptr==
'y') {doCL=1;
continue;}
170 if(*cptr==
'N' || *cptr==
'n') {doCL=0;
continue;}
172 }
else if(strncasecmp(cptr,
"SD", 2)==0) {
173 if(strlen(cptr)==2) {doSD=1;
continue;}
174 cptr+=2;
if(*cptr==
'=') {
176 if(*cptr==
'Y' || *cptr==
'y') {doSD=1;
continue;}
177 if(*cptr==
'N' || *cptr==
'n') {doSD=0;
continue;}
179 }
else if(strncasecmp(cptr,
"LIM=", 4)==0 && strlen(cptr)>4) {
180 strlcpy(limfile, cptr+4, FILENAME_MAX);
continue;
181 }
else if(strcasecmp(cptr,
"LIM")==0) {
182 strcpy(limfile,
"stdout");
continue;
183 }
else if(strcasecmp(cptr,
"W1")==0) {
185 }
else if(strcasecmp(cptr,
"WF")==0) {
187 }
else if(strncasecmp(cptr,
"FIT=", 4)==0) {
188 strlcpy(fitfile, cptr+4, FILENAME_MAX);
if(strlen(fitfile)>0)
continue;
189 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
190 strlcpy(svgfile, cptr+4, FILENAME_MAX);
if(strlen(svgfile)>0)
continue;
191 }
else if(strncasecmp(cptr,
"RK2=", 4)==0 && strlen(cptr)>4) {
193 if(strcasecmp(cptr,
"MEAN")==0) {fix_rk2=2;
continue;}
194 else if(strncasecmp(cptr,
"AVERAGE", 2)==0) {fix_rk2=2;
continue;}
195 if(strcasecmp(cptr,
"MEDIAN")==0) {fix_rk2=3;
continue;}
196 fix_rk2=1; def_pmin[1]=def_pmax[1]=
atof_dpi(cptr);
197 if(def_pmin[1]<=0.0) {
198 fprintf(stderr,
"Error: invalid value with option '%s'.\n", argv[ai]);
203 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
208 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
213 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
214 if(ai<argc)
strlcpy(rtacfile, argv[ai++], FILENAME_MAX);
217 fprintf(stderr,
"Error: invalid fit time: '%s'.\n", argv[ai]);
220 if(fitdur==0) fitdur=1.0E+10;
223 if(ai<argc)
strlcpy(resfile, argv[ai++], FILENAME_MAX);
225 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
228 if(doSD || doCL) doBootstrap=1;
else doBootstrap=0;
232 printf(
"ttacfile := %s\n", ttacfile);
233 printf(
"reference := %s\n", rtacfile);
234 printf(
"resfile := %s\n", resfile);
235 printf(
"fitfile := %s\n", fitfile);
236 printf(
"svgfile := %s\n", svgfile);
237 printf(
"limfile := %s\n", limfile);
238 printf(
"required_fittime := %g min\n", fitdur);
239 printf(
"weights := %d\n", weights);
240 printf(
"doBootstrap := %d\n", doBootstrap);
241 printf(
"doSD := %d\n", doSD);
242 printf(
"doCL := %d\n", doCL);
243 printf(
"fix_rk2 := %d\n", fix_rk2);
244 if(fix_rk2==1) printf(
"fixed_rk2 := %g\n", def_pmin[1]);
249 if(limfile[0] && !ttacfile[0]) {
251 if(strcasecmp(limfile,
"stdout")!=0 && access(limfile, 0) != -1) {
252 fprintf(stderr,
"Error: parameter constraint file %s exists.\n", limfile);
255 if(verbose>1 && strcasecmp(limfile,
"stdout")!=0)
256 printf(
"writing parameter constraints file\n");
267 fprintf(stderr,
"Error in writing '%s': %s\n", limfile, ift.
status);
270 if(strcasecmp(limfile,
"stdout")!=0)
271 fprintf(stdout,
"Parameter file %s with initial values written.\n", limfile);
278 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
288 if(verbose>1) printf(
"reading %s\n", limfile);
291 ret=
iftRead(&ift, limfile, 1, 0);
293 fprintf(stderr,
"Error in reading '%s': %s\n", limfile, ift.
status);
296 if(verbose>10)
iftWrite(&ift,
"stdout", 0);
300 if(
iftGetDoubleValue(&ift, 0,
"rk2_lower", &v, 0)>=0) {n++;
if(fix_rk2!=1) def_pmin[1]=v;}
301 if(
iftGetDoubleValue(&ift, 0,
"rk2_upper", &v, 0)>=0) {n++;
if(fix_rk2!=1) def_pmax[1]=v;}
305 if(n==0) {fprintf(stderr,
"Error: invalid parameter file.\n");
return(9);}
309 for(
int pi=0; pi<parNr; pi++) {
310 if(verbose>3) printf(
" %d %g %g\n", pi+1, def_pmin[pi], def_pmax[pi]);
311 if(def_pmin[pi]<0.0) ret++;
312 if(def_pmax[pi]<def_pmin[pi]) ret++;
313 if(def_pmax[pi]>def_pmin[pi]) n++;
314 if(verbose>3 && ret>0) printf(
" -> invalid\n");
317 fprintf(stderr,
"Error: invalid parameter constraints.\n");
321 fprintf(stderr,
"Error: no model parameters left free for fitting.\n");
325 printf(
"Parameter constraints:\n");
326 for(
int pi=0; pi<parNr; pi++) {
327 printf(
"def_pmin[%d] := %g\n", pi+1, def_pmin[pi]);
328 printf(
"def_pmax[%d] := %g\n", pi+1, def_pmax[pi]);
337 if(verbose>1) printf(
"reading %s\n", ttacfile);
340 fprintf(stderr,
"Error in reading '%s': %s\n", ttacfile,
dfterrmsg);
345 fprintf(stderr,
"Error: missing sample(s) in %s\n", ttacfile);
352 if(ret) fprintf(stderr,
"Warning: check that regional data times are in minutes.\n");
355 if(verbose>2) fprintf(stdout,
"checking frame overlap in %s\n", ttacfile);
358 fprintf(stderr,
"Error: %s has overlapping frame times.\n", ttacfile);
364 double starttime, endtime;
365 starttime=0.0; endtime=fitdur;
366 fitframeNr=
fittime_from_dft(&dft, &starttime, &endtime, &first, &last, verbose-2);
368 fprintf(stderr,
"Error: too few data points for a decent fit.\n");
372 printf(
"dft.frameNr := %d\n", dft.
frameNr);
373 printf(
"starttime := %g\n", starttime);
374 printf(
"endtime := %g\n", endtime);
375 printf(
"first := %d\n", first);
376 printf(
"last := %d\n", last);
377 printf(
"fitframeNr := %d\n", fitframeNr);
383 fprintf(stderr,
"Error: TACs must start at time zero.\n");
386 if(dft.
x1[0]>0.0833333) {
387 fprintf(stderr,
"Warning: TACs should start at time zero.\n");
390 if(verbose>2) printf(
"Tissue calibration unit := %s\n", dft.
unit);
395 for(
int i=0; i<dft.
frameNr; i++) dft.
w[i]=1.0;
396 }
else if(weights==2) {
398 fprintf(stderr,
"Error: cannot set data weights.\n");
402 fprintf(stderr,
"Warning: data is not weighted.\n");
406 fprintf(stdout,
"common_data_weights := %g", dft.
w[0]);
407 for(
int i=1; i<dft.
frameNr; i++) fprintf(stdout,
", %g", dft.
w[i]);
408 fprintf(stdout,
"\n");
415 if(verbose>1) printf(
"\nreading reference\n");
416 int inputtype=-1, ref=-1;
419 fprintf(stderr,
"Error in reading reference input: %s\n", tmp);
420 dftEmpty(&dft);
if(verbose>1) printf(
"ret := %d\n", ret);
424 fprintf(stderr,
"Warning: several reference regions found: %s selected.\n", dft.
voi[ref].
name);
425 }
else if(verbose>1) printf(
"reference_region := %s\n", dft.
voi[ref].
name);
426 if(verbose>2) printf(
"inputtype := %d\n", inputtype);
432 fprintf(stderr,
"Error: cannot allocate more memory.\n");
437 strcpy(dft.
voi[bsi].
name,
"BS");
445 if(verbose>1) printf(
"initializing result data\n");
448 fprintf(stderr,
"Error: cannot set-up memory for results.\n");
454 if(inputtype!=5 && rtacfile[0]) strcpy(res.
reffile, rtacfile);
470 pi++; strcpy(res.
parname[pi],
"rk2"); strcpy(res.
parunit[pi],
"1/min");
471 pi++; strcpy(res.
parname[pi],
"k3"); strcpy(res.
parunit[pi],
"1/min");
472 pi++; strcpy(res.
parname[pi],
"WSS"); strcpy(res.
parunit[pi],
"");
479 int tgoNr=0, neighNr=0, iterNr=0;
482 double rk2median, rk2mean, rk2sd, rk2list[res.
voiNr], v;
485 if(fix_rk2==2) printf(
"\nfitting k2' for fixing to its regional mean...\n");
486 else printf(
"\nfitting k2' for fixing to its regional median...\n");
494 for(
int ri=0; ri<dft.
voiNr; ri++)
if(dft.
voi[ri].
sw==0) {
495 if(verbose>1) printf(
"Region %d %s\n", ri+1, dft.
voi[ri].
name);
497 tis=dft.
voi[ri].
y; ct=dft.
voi[ri].
y2; w=dft.
w;
500 for(
int pi=0; pi<parNr; pi++) {pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];}
502 printf(
" constraints :=");
503 for(
int pi=0; pi<parNr; pi++) printf(
" [%g,%g]", pmin[pi], pmax[pi]);
507 if(verbose>2) printf(
" fitting curve...\n");
512 ret=
tgo(pmin, pmax, trtmFunc, NULL, parNr, neighNr, &wss, p, tgoNr, iterNr, verbose-8);
514 fprintf(stderr,
"Error in optimization (%d).\n", ret);
518 for(
int pi=0; pi<parNr; pi++) printf(
" %g", p[pi]);
519 printf(
" -> WSS=%g\n", p[parNr]); fflush(stdout);
523 p[parNr]=wss=wss_wo_penalty;
524 if(verbose>2) printf(
"wss := %g\nfitframeNr := %d\n", wss, fitframeNr);
528 for(
int ri=0, n=0; ri<res.
voiNr; ri++)
if(dft.
voi[ri].
sw==0) {
533 rk2mean=
dmean(rk2list, n, &rk2sd);
536 if(verbose>2) printf(
"n_rk2 := %d\n", n);
537 printf(
"mean_rk2 := %6.4f +- %6.4f\n", rk2mean, rk2sd);
538 printf(
"median_rk2 := %6.4f\n", rk2median);
542 if(fix_rk2==2) v=rk2mean;
else v=rk2median;
543 if(v<def_pmin[1] || v>def_pmax[1]) {
544 fprintf(stderr,
"Error: k2' could not be fixed to regional mean or median\n");
547 def_pmin[1]=def_pmax[1]=v;
548 if(verbose>1) printf(
"fixed_rk2 := %g\n", def_pmin[1]);
555 if(verbose>0) printf(
"\nfitting...\n");
557 t=dft.
x; cr=dft.
voi[ref].
y;
559 for(
int ri=0; ri<dft.
voiNr; ri++)
if(ri!=ref) {
561 if(verbose>1) printf(
"Region %d %s\n", ri+1, dft.
voi[ri].
name);
563 tis=dft.
voi[ri].
y; ct=dft.
voi[ri].
y2; w=dft.
w;
567 for(
int pi=0; pi<parNr; pi++) {pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];}
569 printf(
"Parameter constraints:\n");
570 for(
int pi=0; pi<parNr; pi++) printf(
" %10.3E - %10.3E\n", pmin[pi], pmax[pi]);
574 if(verbose>2) printf(
" fitting curve...\n");
584 ret=
tgo(pmin, pmax, trtmFunc, NULL, parNr, neighNr, &wss, p, tgoNr, iterNr, verbose-8);
586 fprintf(stderr,
"Error in optimization (%d).\n", ret);
590 for(
int pi=0; pi<parNr; pi++) printf(
" %g", p[pi]);
591 printf(
" -> WSS=%g\n", p[parNr]); fflush(stdout);
595 p[parNr]=wss=wss_wo_penalty;
596 if(verbose>2) printf(
"wss := %g\nfitframeNr := %d\n", wss, fitframeNr);
600 if(verbose>2) printf(
" bootstrapping...\n");
603 if(doSD) sd=res.
voi[ri].
sd;
else sd=NULL;
604 if(doCL) {cl1=res.
voi[ri].
cl1; cl2=res.
voi[ri].
cl2;}
else cl1=cl2=NULL;
606 0, cl1, cl2, sd, p, pmin, pmax, fitframeNr,
611 parNr, w, trtmFunc, tmp, verbose-5
614 fprintf(stderr,
"Error in bootstrap: %s\n", tmp);
615 for(
int pi=0; pi<parNr; pi++) {
616 if(doSD) sd[pi]=nan(
"");
617 if(doCL) cl1[pi]=cl2[pi]=nan(
"");
625 if(verbose>0) {fprintf(stdout,
"\n"); fflush(stdout);}
637 if(verbose>0) {
resPrint(&res); fprintf(stdout,
"\n");}
643 if(verbose>1) printf(
"saving results in %s\n", resfile);
644 ret=
resWrite(&res, resfile, verbose-5);
646 fprintf(stderr,
"Error in writing '%s': %s\n", resfile,
reserrmsg);
650 if(verbose>0) fprintf(stdout,
"Model parameters written in %s\n", resfile);
657 if(svgfile[0] || fitfile[0]) {
664 fprintf(stderr,
"Error: cannot save fitted curves.\n");
668 for(
int ri=0; ri<dft.
voiNr; ri++)
670 for(
int fi=0; fi<fitframeNr; fi++)
676 if(verbose>1) printf(
"saving SVG plot\n");
677 sprintf(tmp,
"TRTM fit ");
680 0.0, nan(
""), svgfile, verbose-8);
682 fprintf(stderr,
"Error (%d) in writing '%s'.\n", ret, svgfile);
686 if(verbose>0) printf(
"Plots written in %s\n", svgfile);
696 if(verbose>1) printf(
"saving fitted curves\n");
698 sprintf(dft2.
comments,
"# program := %s\n", tmp);
700 fprintf(stderr,
"Error in writing '%s': %s\n", fitfile,
dfterrmsg);
704 if(verbose>0) printf(
"Fitted TACs written in %s\n", fitfile);
723double trtmFunc(
int parNr,
double *p,
void *fdata)
726 double R1, k2, rk2, k3, d, wss=0.0;
734 R1=pa[0]; rk2=pa[1]; k3=pa[2];
738 ret=
simTRTM(t, cr, fitframeNr, R1, k2, k3, ct);
740 fprintf(stderr,
" error %d in simulation\n", ret);
745 for(
int i=0; i<fitframeNr; i++)
if(w[i]>0.0) {
751 if(0) printf(
"R1=%g k2=%g k3=%g => %g\n", R1, k2, k3, wss);
int bootstrap(int iterNr, double *cLim1, double *cLim2, double *SD, double *parameter, double *lowlim, double *uplim, int frameNr, double *origTac, double *fitTac, double *bsTac, int parNr, double *weight, double(*objf)(int, double *, void *), char *status, int verbose)
int modelCheckParameters(int par_nr, double *lower_p, double *upper_p, double *test_p, double *accept_p, double *penalty)
int atof_with_check(char *double_as_string, double *result_value)
double atof_dpi(char *str)
int dftdup(DFT *dft1, DFT *dft2)
int dftDeleteFrameOverlap(DFT *dft)
int dftAddmem(DFT *dft, int voiNr)
int dftSortByFrame(DFT *dft)
int dftDelete(DFT *dft, int voi)
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)
int dftTimeunitConversion(DFT *dft, int tunit)
int fittime_from_dft(DFT *dft, double *startTime, double *endTime, int *first, int *last, int verbose)
int iftPutDouble(IFT *ift, char *key, double value, char *cmt_type, int verbose)
int iftRead(IFT *ift, char *filename, int is_key_required, int verbose)
int iftWrite(IFT *ift, char *filename, int verbose)
int iftGetDoubleValue(IFT *ift, int si, const char *key, double *value, int verbose)
Header file for libtpccurveio.
int resWrite(RES *res, char *filename, int verbose)
int resDelete(RES *res, int voi)
#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)
char * petTunit(int tunit)
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 simTRTM(double *t, double *cr, int nr, double R1, double k2, double k3, double *ct)
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)
double dmean(double *data, int n, double *sd)
double dmedian(double *data, int n)
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 comments[_DFT_COMMENT_LEN+1]
char unit[MAX_UNITS_LEN+1]
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char datafile[FILENAME_MAX]
char reffile[FILENAME_MAX]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
double parameter[MAX_RESPARAMS]
double cl2[MAX_RESPARAMS]
double cl1[MAX_RESPARAMS]
char voiname[MAX_REGIONSUBNAME_LEN+1]
char name[MAX_REGIONNAME_LEN+1]