8#include "tpcclibConfig.h"
30static char *info[] = {
31 "Fitting of a compartmental model to regional myocardial PET data, where",
32 "radiotracer is assumed to be trapped into cells at first pass,",
33 "as chemical microspheres.",
34 "The multilinear model is",
35 " Cpet(t) = Vb*Cb(t) + ((1-Vb)*K1+Vb*k2)*Integral[Cb(t)]",
36 " - k2*Integral[Cpet(t)]",
38 "BTAC from LV cavity is used as input function.",
40 "Usage: @P [Options] tacfile BTAC [parfile]",
43 " -k2=<value> | -k2=<MTAC> | -k2=median",
44 " By default, full three-parameter model is fitted to all regional TACs",
45 " in tacfile are fitted independently. With these options, k2 can be",
46 " constrained to given value, or to value from a given large muscle",
47 " region, or to the median of k2 estimates of all regions.",
49 " By default, weights in data file are used, if available.",
50 " With these options all weights can be set to 1.0 (no weighting)",
51 " or based on frame durations.",
53 " Fitted and measured TACs are plotted in specified SVG file.",
55 " Fitted TACs are written in specified TAC file.",
57 " Input (1-HCT)*PTAC is written in specified file.",
59 " Input curves are plotted in specified SVG file.",
62 "See also: imgmtrap, b2ptrap, fitk2",
64 "Keywords: myocardium, perfusion",
83int main(
int argc,
char **argv)
85 int ai, help=0, version=0, verbose=1;
86 char tacfile[FILENAME_MAX], parfile[FILENAME_MAX], svgfile[FILENAME_MAX];
88 char fitfile[FILENAME_MAX], icorfile[FILENAME_MAX], isvgfile[FILENAME_MAX];
90 double fixedk2=nan(
"");
100 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
101 tacfile[0]=parfile[0]=svgfile[0]=fitfile[0]=icorfile[0]=isvgfile[0]=(char)0;
102 bname[0]=mname[0]=(char)0;
104 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
106 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
107 if(strcasecmp(cptr,
"W1")==0) {
109 }
else if(strcasecmp(cptr,
"WF")==0) {
111 }
else if(strncasecmp(cptr,
"SVG=", 4)==0 && strlen(cptr)>4) {
112 strlcpy(svgfile, cptr+4, FILENAME_MAX);
continue;
113 }
else if(strncasecmp(cptr,
"ISVG=", 5)==0 && strlen(cptr)>4) {
114 strlcpy(isvgfile, cptr+5, FILENAME_MAX);
continue;
115 }
else if(strncasecmp(cptr,
"FIT=", 4)==0 && strlen(cptr)>4) {
116 strlcpy(fitfile, cptr+4, FILENAME_MAX);
continue;
117 }
else if(strncasecmp(cptr,
"INPUT=", 6)==0 && strlen(cptr)>4) {
118 strlcpy(icorfile, cptr+6, FILENAME_MAX);
continue;
119 }
else if(strcasecmp(cptr,
"K2=MEDIAN")==0) {
120 fixk2=2; fixedk2=nan(
"");
continue;
121 }
else if(strncasecmp(cptr,
"K2=", 3)==0) {
123 if(ret==0) {fixk2=1;}
127 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
136 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
141 if(ai<argc)
strlcpy(tacfile, argv[ai++], FILENAME_MAX);
143 if(ai<argc)
strlcpy(parfile, argv[ai++], FILENAME_MAX);
145 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
149 if(!tacfile[0] || !bname[0]) {
150 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
156 printf(
"tacfile := %s\n", tacfile);
157 printf(
"bname := %s\n", bname);
158 printf(
"fixk2 := %d\n", fixk2);
159 if(fixk2==1) printf(
"fixedk2 := %g\n", fixedk2);
160 if(fixk2==3) printf(
"mname := %s\n", mname);
161 if(parfile[0]) printf(
"parfile := %s\n", parfile);
162 if(svgfile[0]) printf(
"svgfile := %s\n", svgfile);
163 if(isvgfile[0]) printf(
"isvgfile := %s\n", isvgfile);
164 if(fitfile[0]) printf(
"fitfile := %s\n", fitfile);
165 if(icorfile[0]) printf(
"icorfile := %s\n", icorfile);
166 printf(
"weights := %d\n",
weights);
174 if(verbose>1) printf(
"reading data\n");
183 printf(
"tacNr := %d\n", tac.
tacNr);
184 printf(
"sampleNr := %d\n", tac.
sampleNr);
187 printf(
"isframe := %d\n", tac.
isframe);
190 fprintf(stderr,
"Error: invalid file.\n");
192 }
else if(tac.
tacNr<2) {
193 if(verbose>0) fprintf(stderr,
"Error: file contains just one TAC.\n");
197 fprintf(stderr,
"Error: too few samples.\n");
202 fprintf(stderr,
"Error: data contains missing values.\n");
212 fprintf(stderr,
"Error: invalid data sample times.\n");
216 printf(
"orig_xmin := %g\n", xmin);
217 printf(
"orig_xmax := %g\n", xmax);
220 fprintf(stderr,
"Error: negative sample times.\n");
232 fprintf(stderr,
"Error: invalid data sample times.\n");
236 printf(
"xmin := %g\n", xmin);
237 printf(
"xmax := %g\n", xmax);
252 fprintf(stderr,
"Error: specified BTAC not found in %s.\n", tacfile);
255 fprintf(stderr,
"Error: %d TACs match '%s' in %s.\n", n, bname, tacfile);
260 printf(
"BTAC name := %s\n", tac.
c[ib].
name);
261 printf(
"BTAC index := %d\n", ib);
270 fprintf(stderr,
"Error: specified MTAC not found in %s.\n", tacfile);
273 fprintf(stderr,
"Error: %d TACs match '%s' in %s.\n", n, mname, tacfile);
278 printf(
"MTAC name := %s\n", tac.
c[im].
name);
279 printf(
"MTAC index := %d\n", im);
286 for(
int i=0; i<tac.
sampleNr; i++) tac.
w[i]=1.0;
301 if(verbose>1) printf(
"initializing parameter data\n");
314 iftPut(&par.
h,
"program", buf, 0, NULL);
316 iftPut(&par.
h,
"datafile", tacfile, 0, NULL);
318 iftPut(&par.
h,
"fitmethod",
"NNLS", 0, NULL);
320 iftPut(&par.
h,
"model",
"trap", 0, NULL);
325 else if(fixk2==2)
iftPut(&par.
h,
"k2-constraint",
"median", 0, NULL);
326 else if(fixk2==3)
iftPut(&par.
h,
"k2-constraint", tac.
c[im].
name, 0, NULL);
328 for(
int i=0; i<par.
tacNr; i++) {
337 strcpy(par.
n[0].
name,
"Vb");
338 strcpy(par.
n[1].
name,
"K1");
339 strcpy(par.
n[2].
name,
"k2");
349 if(verbose>1) printf(
"full model fitting\n");
360 double *llsq_mat=(
double*)malloc((2*llsq_n*llsq_m)*
sizeof(
double));
362 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
366 double **llsq_a=(
double**)malloc(llsq_n*
sizeof(
double*));
371 for(
int ni=0; ni<llsq_n; ni++) llsq_a[ni]=llsq_mat+ni*llsq_m;
372 double r2, llsq_b[llsq_m], llsq_x[llsq_n], llsq_wp[llsq_n], llsq_zz[llsq_m];
374 double *matbackup=llsq_mat+llsq_n*llsq_m;
376 for(
int ti=0; ti<tac.
tacNr; ti++) {
381 if(fixk2==3 && ti!=im)
continue;
382 if(verbose>1) {printf(
"Region %d %s\n", 1+ti, tac.
c[ti].
name); fflush(stdout);}
385 for(
int mi=0; mi<llsq_m; mi++)
386 llsq_b[mi]=tac.
c[ti].
y[mi];
387 for(
int mi=0; mi<llsq_m; mi++) {
388 llsq_mat[mi]=tac.
c[ib].
y[mi];
389 llsq_mat[mi+llsq_m]=taci.
c[ib].
y[mi];
390 llsq_mat[mi+2*llsq_m]=-taci.
c[ti].
y[mi];
393 printf(
"Matrix A and vector B:\n");
394 for(
int mi=0; mi<llsq_m; mi++) {
395 printf(
"%.2e", llsq_a[0][mi]);
396 for(
int ni=1; ni<llsq_n; ni++) printf(
", %.2e", llsq_a[ni][mi]);
397 printf(
"; %.3e\n", llsq_b[mi]);
401 for(
int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
407 if(verbose>3) printf(
"starting NNLS...\n");
408 int ret=
nnls(llsq_a, llsq_m, llsq_n, llsq_b, llsq_x, &r2, llsq_wp, llsq_zz, indexp);
409 if(verbose>3) printf(
" ... done.\n");
411 fprintf(stderr,
"Warning: no NNLS solution for %s\n", tac.
c[ti].
name);
412 for(
int ni=0; ni<llsq_n; ni++) llsq_x[ni]=0.0;
415 fprintf(stderr,
"Warning: NNLS iteration max exceeded for %s\n", tac.
c[ti].
name);
418 printf(
"solution_vector: %g", llsq_wp[0]);
419 for(
int ni=1; ni<llsq_n; ni++) printf(
", %g", llsq_wp[ni]);
421 printf(
"x coefficient_vector: %g", llsq_x[0]);
422 for(
int ni=1; ni<llsq_n; ni++) printf(
", %g", llsq_x[ni]);
425 for(
int ni=0; ni<llsq_n; ni++) par.
r[ti].
p[ni]=llsq_x[ni];
431 for(
int mi=0; mi<llsq_m; mi++) {
432 taci.
c[ti].
y[mi]=0.0;
433 for(
int ni=0; ni<llsq_n; ni++) taci.
c[ti].
y[mi]+=llsq_x[ni]*matbackup[mi+ni*llsq_m];
438 free(llsq_a); free(llsq_mat);
441 if(verbose>3 && par.
tacNr<50)
446 double k2list[tac.
tacNr];
448 for(
int ti=0; ti<par.
tacNr; ti++) {
450 if(fixk2==3 && ti!=im)
continue;
451 double p1=par.
r[ti].
p[0];
452 double p2=par.
r[ti].
p[1];
453 double p3=par.
r[ti].
p[2];
454 p2-=p1*p3;
if(p1<0.999) p2/=(1.0-p1);
455 if(p2>=0.0) par.
r[ti].
p[1]=p2;
else par.
r[ti].
p[1]=0.0;
456 if(par.
r[ti].
p[0]<0.80 && par.
r[ti].
p[1]>0.0) k2list[k2nr++]=p3;
471 if(verbose>1) printf(
"model fitting with k2 fixed to %g\n", fixedk2);
472 if(!(fixedk2>=0.0)) {
473 fprintf(stderr,
"Error: invalid k2.\n");
487 double *llsq_mat=(
double*)malloc((2*llsq_n*llsq_m)*
sizeof(
double));
489 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
493 double **llsq_a=(
double**)malloc(llsq_n*
sizeof(
double*));
498 for(
int ni=0; ni<llsq_n; ni++) llsq_a[ni]=llsq_mat+ni*llsq_m;
499 double r2, llsq_b[llsq_m], llsq_x[llsq_n], llsq_wp[llsq_n], llsq_zz[llsq_m];
501 double *matbackup=llsq_mat+llsq_n*llsq_m;
503 for(
int ti=0; ti<tac.
tacNr; ti++) {
507 if(verbose>1) {printf(
"Region %d %s\n", 1+ti, tac.
c[ti].
name); fflush(stdout);}
510 for(
int mi=0; mi<llsq_m; mi++)
511 llsq_b[mi]=tac.
c[ti].
y[mi]+fixedk2*taci.
c[ti].
y[mi];
512 for(
int mi=0; mi<llsq_m; mi++) {
513 llsq_mat[mi]=tac.
c[ib].
y[mi]+fixedk2*taci.
c[ib].
y[mi];
514 llsq_mat[mi+llsq_m]=taci.
c[ib].
y[mi];
517 printf(
"Matrix A and vector B:\n");
518 for(
int mi=0; mi<llsq_m; mi++) {
519 printf(
"%.2e", llsq_a[0][mi]);
520 for(
int ni=1; ni<llsq_n; ni++) printf(
", %.2e", llsq_a[ni][mi]);
521 printf(
"; %.3e\n", llsq_b[mi]);
525 for(
int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
531 if(verbose>3) printf(
"starting NNLS...\n");
532 int ret=
nnls(llsq_a, llsq_m, llsq_n, llsq_b, llsq_x, &r2, llsq_wp, llsq_zz, indexp);
533 if(verbose>3) printf(
" ... done.\n");
535 fprintf(stderr,
"Warning: no NNLS solution for %s\n", tac.
c[ti].
name);
536 for(
int ni=0; ni<llsq_n; ni++) llsq_x[ni]=0.0;
539 fprintf(stderr,
"Warning: NNLS iteration max exceeded for %s\n", tac.
c[ti].
name);
542 printf(
"solution_vector: %g", llsq_wp[0]);
543 for(
int ni=1; ni<llsq_n; ni++) printf(
", %g", llsq_wp[ni]);
545 printf(
"x coefficient_vector: %g", llsq_x[0]);
546 for(
int ni=1; ni<llsq_n; ni++) printf(
", %g", llsq_x[ni]);
549 for(
int ni=0; ni<llsq_n; ni++) par.
r[ti].
p[ni]=llsq_x[ni];
556 for(
int mi=0; mi<llsq_m; mi++) {
557 taci.
c[ti].
y[mi]*=-fixedk2;
558 for(
int ni=0; ni<llsq_n; ni++) taci.
c[ti].
y[mi]+=llsq_x[ni]*matbackup[mi+ni*llsq_m];
564 free(llsq_a); free(llsq_mat);
567 if(verbose>3 && par.
tacNr<50)
571 for(
int ti=0; ti<par.
tacNr; ti++) {
573 double p1=par.
r[ti].
p[0];
574 double p2=par.
r[ti].
p[1];
575 if(p1<0.999) p2/=(1.0-p1);
576 if(p2>=0.0) par.
r[ti].
p[1]=p2;
else par.
r[ti].
p[1]=0.0;
577 par.
r[ti].
p[2]=fixedk2;
586 if(icorfile[0] || isvgfile[0]) {
589 fprintf(stderr,
"Error: cannot plot input curves.\n");
594 fprintf(stderr,
"Error: cannot allocate memory.\n");
599 strcpy(inp.
c[0].
name,
"Blood");
600 strcpy(inp.
c[1].
name,
"Plasma");
601 strcpy(inp.
c[2].
name,
"RBC");
603 double *cb=inp.
c[0].
y;
604 double *cp=inp.
c[1].
y;
606 double t_last=0.0, cp_last=0.0, cpi_last=0.0;
609 double dt2=0.5*(inp.
x[i]-t_last);
612 cp[i] = (cb[i] - fixedk2*(cpi_last + dt2*cp_last)) / (1.0 + dt2*fixedk2);
614 cpi+=(cp[i]+cp_last)*dt2;
615 }
else {cp[i]=cp_last;}
622 inp.
c[2].
y[i]=inp.
c[0].
y[i] - inp.
c[1].
y[i];
625 if(verbose>1) printf(
"plotting input TAC\n");
627 fprintf(stderr,
"Error: cannot plot input curves.\n");
631 if(verbose>0) printf(
"Input curves plotted in %s.\n", isvgfile);
634 if(verbose>1) printf(
"saving (1-HCT)*Cp TAC\n");
638 FILE *fp; fp=fopen(icorfile,
"w");
640 fprintf(stderr,
"Error: cannot open file for writing '%s'.\n", icorfile);
651 if(verbose>0) printf(
"Corrected input saved in %s.\n", icorfile);
661 if(svgfile[0] || fitfile[0]) {
665 for(
int i=0; i<taci.
sampleNr; i++) taci.
c[ib].
y[i]=tac.
c[ib].
y[i];
671 if(verbose>1) printf(
"saving SVG plot\n");
673 sprintf(buf,
"NNLS trap");
676 if(i>=0) {strcat(buf,
": "); strcat(buf, tac.
h.
item[i].
value);}
682 if(verbose>0) printf(
"Plots written in %s.\n", svgfile);
689 if(verbose>1) printf(
"writing %s\n", fitfile);
691 FILE *fp; fp=fopen(fitfile,
"w");
693 fprintf(stderr,
"Error: cannot open file for writing fitted TTACs.\n");
704 if(verbose>0) printf(
"fitted TACs saved in %s.\n", fitfile);
715 if(fixk2==2 || fixk2==3)
iftPutDouble(&par.
h,
"k2", fixedk2, 0, NULL);
732 if(verbose>1) printf(
" saving %s\n", parfile);
734 FILE *fp; fp=fopen(parfile,
"w");
736 fprintf(stderr,
"Error: cannot open file for writing results.\n");
747 if(verbose>0) printf(
"parameters saved in %s\n", parfile);
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 iftPutDouble(IFT *ift, const char *key, const double value, char comment, TPCSTATUS *status)
int iftFindKey(IFT *ift, const char *key, int start_index)
int tacIntegrate(TAC *inp, TAC *out, TPCSTATUS *status)
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 tacExtract(TAC *d1, TAC *d2, const int i)
Extract the specified TAC from existing TAC structure into a new TAC.
int tacAllocateMore(TAC *tac, int tacNr)
int tacPlotLineSVG(TAC *tac, const char *main_title, const double x1, const double x2, const double y1, const double y2, const char *fname, TPCSTATUS *status)
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 tacSwapTACCs(TAC *d, int i1, int i2)
int tacSelectTACs(TAC *d, const char *region_name, int reset, TPCSTATUS *status)
int tacFirstSelected(TAC *d)
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 tacSetX(TAC *d, TPCSTATUS *status)
Set TAC x values based on x1 and x2 values, or guess x1 and x2 values based on x values.
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).
#define MAX_TACNAME_LEN
Max length of TAC ID name (not including trailing zero).
@ UNIT_UNKNOWN
Unknown unit.
@ 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_UNKNOWN
Unknown format.
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Header file for libtpcrand.
Header file for libtpcstatist.
Header file for library libtpctac.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Header file for libtpctacmod.