10#include "tpcclibConfig.h"
27static char *info[] = {
28 "LLSQ fitting of reference tissue input model in case of one tissue",
29 "compartment. Compartmental model is transformed into general linear least",
32 "Radioactivity concentration in region(s)-of-interest is given in TTAC file;",
33 "data must be corrected for physical decay.",
34 "Radioactivity concentration in reference region (RTAC) is given in separate",
35 "file, or as name or number of the reference region inside the TTAC file.",
36 "Sample times must be in minutes in all data files, unless specified inside",
37 "the files. TTAC file should include weights.",
39 "Usage: @P [options] TTAC RTAC results",
42 " -end=<Fit end time (min)>",
43 " Use data from 0 to end time; by default, model is fitted to all frames.",
45 " Sample weights are set to 1 (-w1) or to frame lengths (-wf);",
46 " by default weights in TTAC file are used, if available.",
48 " Use two-parameter model (R1, default), or three-parameter model (R2).",
56 " Fitted and measured TACs are plotted in specified SVG file.",
58 " Fitted regional TTACs are written in specified file.",
62 "1. Blomqvist G. On the construction of functional maps in positron emission",
63 " tomography. J Cereb Blood Flow Metab 1984;4:629-632.",
64 "2. Gjedde A, Wong DF. Modeling neuroreceptor binding of radioligands",
65 " in vivo. In: Quantitative imaging: neuroreceptors, neurotransmitters,",
66 " and enzymes. (Eds. Frost JJ, Wagner HM Jr). Raven Press, 1990, 51-79.",
67 "3. Lawson CL & Hanson RJ. Solving least squares problems.",
68 " Prentice-Hall, 1974.",
70 "See also: lhsol, bfmsrtm, taccbv",
72 "Keywords: TAC, modelling, compartmental model, LLSQ",
89enum {METHOD_UNKNOWN, METHOD_NNLS, METHOD_BVLS};
90static char *method_str[] = {
"unknown",
"NNLS",
"BVLS", 0};
92enum {MODEL_UNKNOWN, MODEL_R1, MODEL_R1R2};
93static char *model_str[] = {
"unknown",
"R1",
"R1R2", 0};
100int main(
int argc,
char **argv)
102 int ai, help=0, version=0, verbose=1;
103 char ttacfile[FILENAME_MAX], rtacfile[FILENAME_MAX], resfile[FILENAME_MAX],
104 fitfile[FILENAME_MAX], svgfile[FILENAME_MAX];
106 double fitdur=nan(
"");
107 double fixed_k2r=nan(
"");
108 int method=METHOD_UNKNOWN;
109 int model=MODEL_UNKNOWN;
115 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
116 ttacfile[0]=rtacfile[0]=resfile[0]=fitfile[0]=svgfile[0]=(char)0;
118 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
120 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
121 if(strncasecmp(cptr,
"SVG=", 4)==0) {
122 strlcpy(svgfile, cptr+4, FILENAME_MAX);
if(strlen(svgfile)>0)
continue;
123 }
else if(strncasecmp(cptr,
"FIT=", 4)==0) {
124 strlcpy(fitfile, cptr+4, FILENAME_MAX);
if(strlen(fitfile)>0)
continue;
125 }
else if(strcasecmp(cptr,
"W1")==0) {
127 }
else if(strcasecmp(cptr,
"WF")==0) {
129 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
131 if(fitdur<=0.0) fitdur=1.0E+99;
134 fprintf(stderr,
"Error: invalid fit time '%s'.\n", argv[ai]);
137 }
else if(strncasecmp(cptr,
"K2R=", 4)==0) {
138 if(
atofCheck(cptr+4, &fixed_k2r)==0 && fixed_k2r>0.0)
continue;
139 fprintf(stderr,
"Error: invalid k2r '%s'.\n", argv[ai]);
return(1);
140 }
else if(strcasecmp(cptr,
"NNLS")==0) {
141 method=METHOD_NNLS;
continue;
142 }
else if(strcasecmp(cptr,
"BVLS")==0) {
143 method=METHOD_BVLS;
continue;
144 }
else if(strncasecmp(cptr,
"MODEL=", 6)==0) {
146 if(strcasecmp(cptr,
"R1") ==0) {model=MODEL_R1;
continue;}
147 if(strcasecmp(cptr,
"R2") ==0) {model=MODEL_R1R2;
continue;}
148 if(strcasecmp(cptr,
"R1R2")==0) {model=MODEL_R1R2;
continue;}
149 fprintf(stderr,
"Error: invalid model '%s'.\n", cptr);
152 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
161 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
166 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
167 if(ai<argc)
strlcpy(rtacfile, argv[ai++], FILENAME_MAX);
168 if(ai<argc)
strlcpy(resfile, argv[ai++], FILENAME_MAX);
170 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
175 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
179 if(isnan(fitdur)) fitdur=1.0E+99;
180 if(method==METHOD_UNKNOWN) method=METHOD_NNLS;
181 if(model==MODEL_UNKNOWN) model=MODEL_R1;
183 if(!isnan(fixed_k2r) && model==MODEL_R1) {
187 if(!isnan(fixed_k2r) && method==METHOD_NNLS) {
194 printf(
"ttacfile := %s\n", ttacfile);
195 printf(
"rtacfile := %s\n", rtacfile);
196 printf(
"resfile := %s\n", resfile);
197 printf(
"fitfile := %s\n", fitfile);
198 printf(
"svgfile := %s\n", svgfile);
199 printf(
"method := %s\n", method_str[method]);
200 printf(
"model := %s\n", model_str[model]);
201 printf(
"required_fittime := %g min\n", fitdur);
202 printf(
"weights := %d\n",
weights);
203 if(!isnan(fixed_k2r)) printf(
"fixed_k2r := %g\n", fixed_k2r);
215 if(verbose>1) printf(
"reading %s\n", ttacfile);
216 ret=
tacRead(&ttac, ttacfile, &status);
223 printf(
"tacNr := %d\n", ttac.
tacNr);
224 printf(
"sampleNr := %d\n", ttac.
sampleNr);
227 printf(
"midxrange := %g - %g\n", ttac.
x[0], ttac.
x[ttac.
sampleNr-1]);
235 if(verbose>1) {printf(
"reading %s\n", rtacfile); fflush(stdout);}
239 fprintf(stderr,
"Error: %s\n",
errorMsg(status.
error)); fflush(stderr);
243 fprintf(stderr,
"Warning: several reference regions found: %s selected.\n",
244 ttac.
c[refindex].
name);
245 }
else if(verbose>1) {
246 printf(
"reference_region := %s\n", ttac.
c[refindex].
name); fflush(stderr);
251 fprintf(stderr,
"Error: missing sample(s) in data.\n"); fflush(stderr);
257 fprintf(stderr,
"Warning: %s\n",
errorMsg(status.
error)); fflush(stderr);
263 fprintf(stderr,
"Error: %s\n",
errorMsg(status.
error)); fflush(stderr);
268 printf(
"tacNr := %d\n", ttac.
tacNr);
269 printf(
"sampleNr := %d\n", ttac.
sampleNr);
272 printf(
"midxrange := %g - %g\n", ttac.
x[0], ttac.
x[ttac.
sampleNr-1]);
280 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
287 if(verbose>0) fprintf(stderr,
"Warning: data is not weighted.\n");
292 double starttime=0.0;
293 double endtime=fitdur;
294 int fitSampleNr=
tacFittime(&ttac, &starttime, &endtime, NULL, NULL, &status);
298 }
else if(fitSampleNr<4) {
299 fprintf(stderr,
"Error: too few data points for a decent fit.\n");
303 printf(
"starttime := %g\n", starttime);
304 printf(
"endtime := %g\n", endtime);
307 printf(
"fitSampleNr := %d\n", fitSampleNr);
308 printf(
"origSampleNr := %d\n", origSampleNr);
325 printf(
"\nAUC 0-%g:\n", auc.
x[fitSampleNr-1]);
326 for(
int ti=0; ti<auc.
tacNr; ti++)
327 printf(
" %s\t%g\n", auc.
c[ti].
name, auc.
c[ti].
y[fitSampleNr-1]);
336 case MODEL_R1: llsq_n=2;
break;
337 case MODEL_R1R2: llsq_n=3;
break;
340 if(verbose>2) printf(
"llsq_n := %d\n", llsq_n);
346 if(verbose>1) printf(
"initializing LLSQ parameter data\n");
360 iftPut(&lp.
h,
"program", buf, 0, NULL);
362 iftPut(&lp.
h,
"datafile", ttacfile, 0, NULL);
363 iftPut(&lp.
h,
"refname", ttac.
c[refindex].
name, 0, NULL);
365 iftPut(&lp.
h,
"fitmethod", method_str[method], 0, NULL);
367 iftPut(&lp.
h,
"model", model_str[model], 0, NULL);
371 for(i=0; i<lp.
tacNr; i++) {
379 for(i=0; i<MAX_LLSQ_N; i++) {
380 sprintf(lp.
n[i].
name,
"P%d", 1+i);
386 if(method==METHOD_NNLS) {
391 if(verbose>1) printf(
"allocating memory for NNLS\n");
392 int llsq_m=fitSampleNr;
393 double *llsq_mat=(
double*)malloc((2*llsq_n*llsq_m)*
sizeof(
double));
395 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
398 double **llsq_a=(
double**)malloc(llsq_n*
sizeof(
double*));
400 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
404 for(
int ni=0; ni<llsq_n; ni++) llsq_a[ni]=llsq_mat+ni*llsq_m;
405 double r2, llsq_b[llsq_m], llsq_x[llsq_n], llsq_wp[llsq_n], llsq_zz[llsq_m];
407 double *matbackup=llsq_mat+llsq_n*llsq_m;
412 if(verbose>1) printf(
"solving NNLS\n");
413 for(
int ti=0; ti<ttac.
tacNr; ti++)
if(ti!=refindex) {
415 if(verbose>2 && ttac.
tacNr>2) {
416 printf(
"Region %d %s\n", 1+ti, ttac.
c[ti].
name); fflush(stdout);}
419 for(
int mi=0; mi<llsq_m; mi++)
420 llsq_b[mi]=ttac.
c[ti].
y[mi];
421 if(model==MODEL_R1) {
422 for(
int mi=0; mi<llsq_m; mi++) {
423 llsq_mat[mi]=ttac.
c[refindex].
y[mi];
424 llsq_mat[mi+llsq_m]=auc.
c[refindex].
y[mi]-auc.
c[ti].
y[mi];
427 for(
int mi=0; mi<llsq_m; mi++) {
428 llsq_mat[mi]=ttac.
c[refindex].
y[mi];
429 llsq_mat[mi+llsq_m]=auc.
c[refindex].
y[mi];
430 llsq_mat[mi+2*llsq_m]=-auc.
c[ti].
y[mi];
434 printf(
"Matrix A and vector B:\n");
435 for(
int mi=0; mi<llsq_m; mi++) {
436 printf(
"%.2e", llsq_a[0][mi]);
437 for(
int ni=1; ni<llsq_n; ni++) printf(
", %.2e", llsq_a[ni][mi]);
438 printf(
"; %.3e\n", llsq_b[mi]);
442 for(
int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
447 if(verbose>3) printf(
"starting NNLS...\n");
448 ret=
nnls(llsq_a, llsq_m, llsq_n, llsq_b, llsq_x, &r2, llsq_wp, llsq_zz, indexp);
449 if(verbose>3) printf(
" ... done.\n");
451 fprintf(stderr,
"Warning: no NNLS solution for %s\n", ttac.
c[ti].
name);
452 for(
int ni=0; ni<llsq_n; ni++) llsq_x[ni]=0.0;
455 fprintf(stderr,
"Warning: NNLS iteration max exceeded for %s\n", ttac.
c[ti].
name);
458 printf(
"solution_vector: %g", llsq_wp[0]);
459 for(
int ni=1; ni<llsq_n; ni++) printf(
", %g", llsq_wp[ni]);
462 for(
int ni=0; ni<llsq_n; ni++) lp.
r[ti].
p[ni]=llsq_x[ni];
468 for(
int mi=0; mi<llsq_m; mi++) {
470 for(
int ni=0; ni<llsq_n; ni++) auc.
c[ti].
y[mi]+=llsq_x[ni]*matbackup[mi+ni*llsq_m];
476 free(llsq_a); free(llsq_mat);
478 }
else if(method==METHOD_BVLS) {
483 if(verbose>1) printf(
"allocating memory for BVLS\n");
484 int llsq_m=fitSampleNr;
485 double *llsq_mat=(
double*)malloc((2*llsq_n*llsq_m)*
sizeof(
double));
487 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
490 double b[llsq_m], x[MAX_LLSQ_N], bl[MAX_LLSQ_N], bu[MAX_LLSQ_N], w[llsq_n], zz[llsq_m];
491 double act[llsq_m*(llsq_n+2)], r2;
492 int istate[llsq_n+1], iterNr;
493 double *matbackup=llsq_mat+llsq_n*llsq_m;
498 if(verbose>1) printf(
"solving BVLS\n");
499 for(
int ti=0; ti<ttac.
tacNr; ti++)
if(ti!=refindex) {
501 if(verbose>2 && ttac.
tacNr>2) {
502 printf(
"Region %d %s\n", 1+ti, ttac.
c[ti].
name); fflush(stdout);}
505 for(
int mi=0; mi<llsq_m; mi++)
506 b[mi]=ttac.
c[ti].
y[mi];
507 if(model==MODEL_R1) {
508 for(
int mi=0; mi<llsq_m; mi++) {
509 llsq_mat[mi]=ttac.
c[refindex].
y[mi];
510 llsq_mat[mi+llsq_m]=auc.
c[refindex].
y[mi]-auc.
c[ti].
y[mi];
513 for(
int mi=0; mi<llsq_m; mi++) {
514 llsq_mat[mi]=ttac.
c[refindex].
y[mi];
515 llsq_mat[mi+llsq_m]=auc.
c[refindex].
y[mi];
516 llsq_mat[mi+2*llsq_m]=-auc.
c[ti].
y[mi];
520 printf(
"Matrix A and vector B:\n");
521 for(
int mi=0; mi<llsq_m; mi++) {
522 printf(
"%.2e", llsq_mat[mi]);
523 for(
int ni=1; ni<llsq_n; ni++) printf(
", %.2e", llsq_mat[mi+ni*llsq_m]);
524 printf(
"; %.3e\n", b[mi]);
528 for(
int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
532 istate[llsq_n]=0;
for(
int ni=0; ni<llsq_n; ni++) istate[ni]=1+ni;
533 if(!isnan(fixed_k2r)) {
541 if(model==MODEL_R1) {
542 bl[0]=0.0; bu[0]=100.0;
543 bl[1]=0.0; bu[1]=10.0;
545 bl[0]=0.0; bu[0]=100.0;
546 bl[1]=0.0; bu[1]=10.0;
547 bl[2]=0.0; bu[2]=10.0;
548 if(!isnan(fixed_k2r)) {
549 bl[1]=bu[1]=fixed_k2r;
555 if(verbose>3) printf(
"starting BVLS...\n");
557 ret=
bvls(0, llsq_m, llsq_n, llsq_mat, b, bl, bu, x, w, act, zz, istate, &iterNr, verbose-3);
559 ret=
bvls(1, llsq_m, llsq_n, llsq_mat, b, bl, bu, x, w, act, zz, istate, &iterNr, verbose-3);
560 if(verbose>3) printf(
" ... done.\n");
563 if(ret==-1) fprintf(stderr,
"Warning: BVLS iteration max exceeded for %s\n", ttac.
c[ti].
name);
564 else fprintf(stderr,
"Warning: no BVLS solution for %s\n", ttac.
c[ti].
name);
565 for(
int ni=0; ni<llsq_n; ni++) x[ni]=0.0;
569 printf(
"solution_vector: %d", istate[0]);
570 for(
int ni=1; ni<llsq_n; ni++) printf(
", %d", istate[ni]);
573 for(
int ni=0; ni<llsq_n; ni++) lp.
r[ti].
p[ni]=x[ni];
579 for(
int mi=0; mi<llsq_m; mi++) {
581 for(
int ni=0; ni<llsq_n; ni++) auc.
c[ti].
y[mi]+=x[ni]*matbackup[mi+ni*llsq_m];
591 fprintf(stderr,
"Error: selected method not available.");
601 if(verbose>2 && lp.
tacNr<50) {
610 if(verbose>1) printf(
"initializing CM parameter data\n");
626 iftPut(&cmpar.
h,
"program", buf, 0, NULL);
628 iftPut(&cmpar.
h,
"datafile", ttacfile, 0, NULL);
629 iftPut(&cmpar.
h,
"refname", ttac.
c[refindex].
name, 0, NULL);
632 iftPut(&cmpar.
h,
"study_number", buf, 0, NULL);
634 iftPut(&cmpar.
h,
"fitmethod", method_str[method], 0, NULL);
636 iftPut(&cmpar.
h,
"model", model_str[model], 0, NULL);
640 for(i=0; i<cmpar.
tacNr; i++) {
650 if(model==MODEL_R1) {
652 i=0; strcpy(cmpar.
n[i].
name,
"R1");
656 i=0; strcpy(cmpar.
n[i].
name,
"R1");
660 i++; strcpy(cmpar.
n[i].
name,
"R2");
661 i++; strcpy(cmpar.
n[i].
name,
"pRatio");
664 if(model==MODEL_R1) {
665 for(i=0; i<cmpar.
tacNr; i++) {
666 cmpar.
r[i].
p[0]=lp.
r[i].
p[0];
667 cmpar.
r[i].
p[1]=lp.
r[i].
p[1];
670 for(i=0; i<cmpar.
tacNr; i++) {
671 double R1, k2, k2r, R2, pRatio;
672 R1=lp.
r[i].
p[0]; k2=lp.
r[i].
p[2]; k2r=lp.
r[i].
p[1]/lp.
r[i].
p[0];
673 R2=k2/k2r; pRatio=R1/R2;
678 cmpar.
r[i].
p[4]=pRatio;
695 if(verbose>1) printf(
"writing %s\n", resfile);
699 FILE *fp; fp=fopen(resfile,
"w");
701 fprintf(stderr,
"Error: cannot open file for writing parameter file.\n");
711 if(verbose>0) printf(
"Results saved in %s.\n", resfile);
715 for(
int i=0; i<auc.
sampleNr; i++) auc.
c[refindex].
y[i]=ttac.
c[refindex].
y[i];
722 if(verbose>1) printf(
"saving SVG plot\n");
725 sprintf(buf,
"%s %s", method_str[method], model_str[model]);
728 if(i>=0) {strcat(buf,
": "); strcat(buf, ttac.
h.
item[i].
value);}
729 ret=
tacPlotFitSVG(&ttac, &auc, buf, 0.0, nan(
""), 0.0, nan(
""), svgfile, &status);
735 if(verbose>0) printf(
"Plots written in %s.\n", svgfile);
743 if(verbose>1) printf(
"writing %s\n", fitfile);
744 FILE *fp; fp=fopen(fitfile,
"w");
746 fprintf(stderr,
"Error: cannot open file for writing fitted TTACs.\n");
757 if(verbose>0) printf(
"fitted TACs saved in %s.\n", fitfile);
int bvls(int key, const int m, const int n, double *a, double *b, double *bl, double *bu, double *x, double *w, double *act, double *zz, int *istate, int *iter, int verbose)
Bounded-value least-squares method to solve the linear problem A x ~ b , subject to limit1 <= x <= li...
int llsqWght(int N, int M, double **A, double *a, double *b, double *weight)
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)
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 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,...
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *wp, double *zzp, int *indexp)
int nnlsWght(int N, int M, double **A, double *b, double *weight)
int parDeleteTAC(PAR *par, int ti)
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)
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 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 tacGetHeaderStudynr(IFT *h, char *s, 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 tacVerifyTimeOrder(TAC *d, 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 tacCorrectFrameOverlap(TAC *d, TPCSTATUS *status)
Correct PET frame start and end times if frames are slightly overlapping or have small gaps in betwee...
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).
@ 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 libtpcpar.
@ 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.