TPCCLIB
Loading...
Searching...
No Matches
fitmtrap.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <math.h>
14/*****************************************************************************/
15#include "tpcextensions.h"
16#include "tpcift.h"
17#include "tpctac.h"
18#include "tpcpar.h"
19#include "tpcli.h"
20#include "tpcstatist.h"
21#include "tpccm.h"
22#include "tpctacmod.h"
23#include "tpclinopt.h"
24#include "tpcrand.h"
25#include "tpcnlopt.h"
26#include "tpcbfm.h"
27/*****************************************************************************/
28
29/*****************************************************************************/
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)]",
37 " ",
38 "BTAC from LV cavity is used as input function.",
39 " ",
40 "Usage: @P [Options] tacfile BTAC [parfile]",
41 " ",
42 "Options:",
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.",
48 " -w1 | -wf",
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.",
52 " -svg=<Filename>",
53 " Fitted and measured TACs are plotted in specified SVG file.",
54 " -fit=<Filename>",
55 " Fitted TACs are written in specified TAC file.",
56 " -input=<Filename>",
57 " Input (1-HCT)*PTAC is written in specified file.",
58 " -isvg=<Filename>",
59 " Input curves are plotted in specified SVG file.",
60 " -stdoptions", // List standard options like --help, -v, etc
61 " ",
62 "See also: imgmtrap, b2ptrap, fitk2",
63 " ",
64 "Keywords: myocardium, perfusion",
65 0};
66/*****************************************************************************/
67
68/*****************************************************************************/
69/* Turn on the globbing of the command line, since it is disabled by default in
70 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
71 In Unix&Linux wildcard command line processing is enabled by default. */
72/*
73#undef _CRT_glob
74#define _CRT_glob -1
75*/
76int _dowildcard = -1;
77/*****************************************************************************/
78
79/*****************************************************************************/
83int main(int argc, char **argv)
84{
85 int ai, help=0, version=0, verbose=1;
86 char tacfile[FILENAME_MAX], parfile[FILENAME_MAX], svgfile[FILENAME_MAX];
87 char bname[MAX_TACNAME_LEN+1], mname[MAX_TACNAME_LEN+1];
88 char fitfile[FILENAME_MAX], icorfile[FILENAME_MAX], isvgfile[FILENAME_MAX];
89 int weights=0; // 0=default, 1=no weighting, 2=frequency
90 double fixedk2=nan("");
91 /* Fix k2: 0=no; 1=fix to value given by user; 2=fix to median; 3=fix from large ROI */
92 int fixk2=0;
93
94
95
96
97 /*
98 * Get arguments
99 */
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;
103 /* Options */
104 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
105 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
106 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
107 if(strcasecmp(cptr, "W1")==0) {
108 weights=1; continue;
109 } else if(strcasecmp(cptr, "WF")==0) {
110 weights=2; continue;
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) {
122 int ret=atofCheck(cptr+3, &fixedk2);
123 if(ret==0) {fixk2=1;}
124 else {strlcpy(mname, cptr+3, MAX_TACNAME_LEN+1); fixk2=3;}
125 continue;
126 }
127 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
128 return(1);
129 } else break;
130
131 TPCSTATUS status; statusInit(&status);
132 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
133 status.verbose=verbose-3;
134
135 /* Print help or version? */
136 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
137 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
138 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
139
140 /* Process other arguments, starting from the first non-option */
141 if(ai<argc) strlcpy(tacfile, argv[ai++], FILENAME_MAX);
142 if(ai<argc) strlcpy(bname, argv[ai++], MAX_TACNAME_LEN+1);
143 if(ai<argc) strlcpy(parfile, argv[ai++], FILENAME_MAX);
144 if(ai<argc) {
145 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
146 return(1);
147 }
148 /* Did we get all the information that we need? */
149 if(!tacfile[0] || !bname[0]) { // note that parameter file is optional
150 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
151 return(1);
152 }
153
154 /* In verbose mode print arguments and options */
155 if(verbose>1) {
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);
167 fflush(stdout);
168 }
169
170
171 /*
172 * Read the data
173 */
174 if(verbose>1) printf("reading data\n");
175 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
176 TAC tac; tacInit(&tac);
177 if(tacRead(&tac, tacfile, &status)!=TPCERROR_OK) {
178 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
179 tacFree(&tac); return(2);
180 }
181 if(verbose>1) {
182 printf("fileformat := %s\n", tacFormattxt(tac.format));
183 printf("tacNr := %d\n", tac.tacNr);
184 printf("sampleNr := %d\n", tac.sampleNr);
185 printf("xunit := %s\n", unitName(tac.tunit));
186 printf("yunit := %s\n", unitName(tac.cunit));
187 printf("isframe := %d\n", tac.isframe);
188 }
189 if(tac.tacNr<1) {
190 fprintf(stderr, "Error: invalid file.\n");
191 tacFree(&tac); return(2);
192 } else if(tac.tacNr<2) {
193 if(verbose>0) fprintf(stderr, "Error: file contains just one TAC.\n");
194 tacFree(&tac); return(2);
195 }
196 if(tac.sampleNr<5) {
197 fprintf(stderr, "Error: too few samples.\n");
198 tacFree(&tac); return(2);
199 }
200 /* Check NaNs */
201 if(tacNaNs(&tac)>0) {
202 fprintf(stderr, "Error: data contains missing values.\n");
203 tacFree(&tac); return(2);
204 }
205 /* Sort data by sample time */
206 tacSortByTime(&tac, &status);
207 /* Make sure that frame mid times are set, otherwise plotting may not work */
208 tacSetX(&tac, NULL);
209 /* Get x range */
210 double xmin, xmax;
211 if(tacXRange(&tac, &xmin, &xmax)!=0) {
212 fprintf(stderr, "Error: invalid data sample times.\n");
213 tacFree(&tac); return(2);
214 }
215 if(verbose>2) {
216 printf("orig_xmin := %g\n", xmin);
217 printf("orig_xmax := %g\n", xmax);
218 }
219 if(!(xmin>=0.0)) {
220 fprintf(stderr, "Error: negative sample times.\n");
221 tacFree(&tac); return(2);
222 }
223 /* Convert sample times into minutes */
224 if(tac.tunit==UNIT_UNKNOWN) {
225 if(xmax<10.0) tac.tunit=UNIT_MIN; else tac.tunit=UNIT_SEC;
226 }
227 if(tacXUnitConvert(&tac, UNIT_MIN, &status)!=TPCERROR_OK) {
228 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
229 tacFree(&tac); return(2);
230 }
231 if(tacXRange(&tac, &xmin, &xmax)!=0) {
232 fprintf(stderr, "Error: invalid data sample times.\n");
233 tacFree(&tac); return(2);
234 }
235 if(verbose>1) {
236 printf("xmin := %g\n", xmin);
237 printf("xmax := %g\n", xmax);
238 printf("final_xunit := %s\n", unitName(tac.tunit));
239 }
240 /* Convert concentration units into kBq/mL, if possible */
241 if(tac.cunit==UNIT_BQ_PER_ML) {
242 tacYUnitConvert(&tac, UNIT_KBQ_PER_ML, NULL);
243 }
244
245
246
247 /* Select the LV BTAC */
248 int ib=-1;
249 {
250 int n=tacSelectTACs(&tac, bname, 1, NULL);
251 if(n<1) {
252 fprintf(stderr, "Error: specified BTAC not found in %s.\n", tacfile);
253 tacFree(&tac); return(3);
254 } else if(n>1) {
255 fprintf(stderr, "Error: %d TACs match '%s' in %s.\n", n, bname, tacfile);
256 tacFree(&tac); return(3);
257 }
258 ib=tacFirstSelected(&tac);
259 if(verbose>1) {
260 printf("BTAC name := %s\n", tac.c[ib].name);
261 printf("BTAC index := %d\n", ib);
262 }
263 }
264
265 /* Select the Big muscle TTAC */
266 int im=-1;
267 if(fixk2==3) {
268 int n=tacSelectTACs(&tac, mname, 1, NULL);
269 if(n<1) {
270 fprintf(stderr, "Error: specified MTAC not found in %s.\n", tacfile);
271 tacFree(&tac); return(3);
272 } else if(n>1) {
273 fprintf(stderr, "Error: %d TACs match '%s' in %s.\n", n, mname, tacfile);
274 tacFree(&tac); return(3);
275 }
276 im=tacFirstSelected(&tac);
277 if(verbose>1) {
278 printf("MTAC name := %s\n", tac.c[im].name);
279 printf("MTAC index := %d\n", im);
280 }
281 }
282
283 /* Add data weights, if requested */
284 if(weights==1) {
286 for(int i=0; i<tac.sampleNr; i++) tac.w[i]=1.0;
287 } else if(weights==2) {
288 if(tacWByFreq(&tac, ISOTOPE_UNKNOWN, &status)!=TPCERROR_OK) {
289 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
290 tacFree(&tac); return(3);
291 }
292 }
293
294 /* Set place for TAC integrals / fitted TACs */
295 TAC taci; tacInit(&taci);
296
297
298 /*
299 * Prepare the room for results
300 */
301 if(verbose>1) printf("initializing parameter data\n");
302 PAR par; parInit(&par);
303 if(parAllocateWithTAC(&par, &tac, 3, &status) != TPCERROR_OK) {
304 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
305 tacFree(&tac); //tacFree(&taci);
306 return(5);
307 }
308 /* Copy titles & file names */
309 {
310 char buf[256];
311 time_t t=time(NULL);
312 /* set program name */
313 tpcProgramName(argv[0], 1, 1, buf, 256);
314 iftPut(&par.h, "program", buf, 0, NULL);
315 /* set file names */
316 iftPut(&par.h, "datafile", tacfile, 0, NULL);
317 /* Fit method */
318 iftPut(&par.h, "fitmethod", "NNLS", 0, NULL);
319 /* Model */
320 iftPut(&par.h, "model", "trap", 0, NULL);
321 /* Set current time to results */
322 iftPut(&par.h, "analysis_time", ctime_r_int(&t, buf), 0, NULL);
323 /* Fixed k2 */
324 if(fixk2==1) iftPutDouble(&par.h, "k2", fixedk2, 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);
327 /* Set fit times for each TAC */
328 for(int i=0; i<par.tacNr; i++) {
329 par.r[i].dataNr=tac.sampleNr;
330 par.r[i].start=xmin;
331 par.r[i].end=xmax;
332 /* and nr of fitted parameters */
333 par.r[i].fitNr=3; // May be set to 2 before saving if k2 is constrained
334 }
335 /* Set the parameter names and units */
336 par.parNr=3; // May be set to 2 before saving if k2 is constrained
337 strcpy(par.n[0].name, "Vb");
338 strcpy(par.n[1].name, "K1");
339 strcpy(par.n[2].name, "k2");
340 }
341 /* Delete BTAC from results before saving into file */
342
343
344 /*
345 * Full 3-parameter model fit to all TACs if k2 is not fixed
346 * or to big muscle if that was asked for
347 */
348 if(fixk2!=1) {
349 if(verbose>1) printf("full model fitting\n");
350
351 /* TAC integrals */
352 if(tacIntegrate(&tac, &taci, &status) != TPCERROR_OK) {
353 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
354 tacFree(&tac); return(5);
355 }
356
357 /* Allocate matrices for NNLS */
358 int llsq_m=tac.sampleNr;
359 int llsq_n=3;
360 double *llsq_mat=(double*)malloc((2*llsq_n*llsq_m)*sizeof(double));
361 if(llsq_mat==NULL) {
362 fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
363 tacFree(&tac); tacFree(&taci); parFree(&par);
364 return(6);
365 }
366 double **llsq_a=(double**)malloc(llsq_n*sizeof(double*));
367 if(llsq_a==NULL) {
368 tacFree(&tac); tacFree(&taci); parFree(&par);
369 return(6);
370 }
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];
373 int indexp[llsq_n];
374 double *matbackup=llsq_mat+llsq_n*llsq_m;
375
376 for(int ti=0; ti<tac.tacNr; ti++) {
377
378 /* BTAC obviously not fitted */
379 if(ti==ib) continue;
380 /* If requested, none other than big muscle fitted */
381 if(fixk2==3 && ti!=im) continue;
382 if(verbose>1) {printf("Region %d %s\n", 1+ti, tac.c[ti].name); fflush(stdout);}
383
384 /* Setup data matrix A and vector B */
385 for(int mi=0; mi<llsq_m; mi++)
386 llsq_b[mi]=tac.c[ti].y[mi]; // TTAC
387 for(int mi=0; mi<llsq_m; mi++) {
388 llsq_mat[mi]=tac.c[ib].y[mi]; // BTAC
389 llsq_mat[mi+llsq_m]=taci.c[ib].y[mi]; // BTAC integral
390 llsq_mat[mi+2*llsq_m]=-taci.c[ti].y[mi]; // TTAC integral
391 }
392 if(verbose>5) {
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]);
398 }
399 }
400 /* Make a copy of A matrix for later use */
401 for(int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
402
403 /* Apply data weights */
404 if(tacIsWeighted(&tac)) nnlsWght(llsq_n, llsq_m, llsq_a, llsq_b, tac.w);
405
406 /* Compute NNLS */
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");
410 if(ret>1) {
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;
413 r2=0.0;
414 } else if(ret==1) {
415 fprintf(stderr, "Warning: NNLS iteration max exceeded for %s\n", tac.c[ti].name);
416 }
417 if(verbose>4) {
418 printf("solution_vector: %g", llsq_wp[0]);
419 for(int ni=1; ni<llsq_n; ni++) printf(", %g", llsq_wp[ni]);
420 printf("\n");
421 printf("x coefficient_vector: %g", llsq_x[0]);
422 for(int ni=1; ni<llsq_n; ni++) printf(", %g", llsq_x[ni]);
423 printf("\n");
424 }
425 for(int ni=0; ni<llsq_n; ni++) par.r[ti].p[ni]=llsq_x[ni];
426 par.r[ti].wss=r2;
427 par.r[ti].dataNr=tacWSampleNr(&tac);
428 par.r[ti].fitNr=llsq_n;
429
430 /* Compute fitted TAC (into taci since it is otherwise not needed) */
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];
434 }
435 } // next TAC
436
437 /* Free allocated memory */
438 free(llsq_a); free(llsq_mat);
439
440 /* Print original LLSQ parameters on screen */
441 if(verbose>3 && par.tacNr<50)
442 parWrite(&par, stdout, PAR_FORMAT_TSV_UK, 0, &status);
443
444 /* Convert LLSQ parameters to CM model parameters */
445 /* Calculate median of k2 values */
446 double k2list[tac.tacNr];
447 int k2nr=0;
448 for(int ti=0; ti<par.tacNr; ti++) {
449 if(ti==ib) continue;
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;
457 }
458 if(k2nr==1) {
459 fixedk2=k2list[0];
460 } else if(k2nr>1) {
461 fixedk2=statMedian(k2list, k2nr);
462 }
463
464 }
465
466 /*
467 * Simple 2-parameter model fit to all TACs with fixed k2, if requested
468 */
469 if(fixk2!=0) {
470
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");
474 tacFree(&tac); tacFree(&taci); parFree(&par);
475 return(7);
476 }
477
478 /* TAC integrals */
479 if(tacIntegrate(&tac, &taci, &status) != TPCERROR_OK) {
480 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
481 tacFree(&tac); return(5);
482 }
483
484 /* Allocate matrices for NNLS */
485 int llsq_m=tac.sampleNr;
486 int llsq_n=2;
487 double *llsq_mat=(double*)malloc((2*llsq_n*llsq_m)*sizeof(double));
488 if(llsq_mat==NULL) {
489 fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
490 tacFree(&tac); tacFree(&taci); parFree(&par);
491 return(6);
492 }
493 double **llsq_a=(double**)malloc(llsq_n*sizeof(double*));
494 if(llsq_a==NULL) {
495 tacFree(&tac); tacFree(&taci); parFree(&par);
496 return(6);
497 }
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];
500 int indexp[llsq_n];
501 double *matbackup=llsq_mat+llsq_n*llsq_m;
502
503 for(int ti=0; ti<tac.tacNr; ti++) {
504
505 /* BTAC obviously not fitted */
506 if(ti==ib) continue;
507 if(verbose>1) {printf("Region %d %s\n", 1+ti, tac.c[ti].name); fflush(stdout);}
508
509 /* Setup data matrix A and vector B */
510 for(int mi=0; mi<llsq_m; mi++)
511 llsq_b[mi]=tac.c[ti].y[mi]+fixedk2*taci.c[ti].y[mi]; // TTAC + k2 * TTAC integral
512 for(int mi=0; mi<llsq_m; mi++) {
513 llsq_mat[mi]=tac.c[ib].y[mi]+fixedk2*taci.c[ib].y[mi]; // BTAC + k2 * BTAC integral
514 llsq_mat[mi+llsq_m]=taci.c[ib].y[mi]; // BTAC integral
515 }
516 if(verbose>5) {
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]);
522 }
523 }
524 /* Make a copy of A matrix for later use */
525 for(int i=0; i<llsq_n*llsq_m; i++) matbackup[i]=llsq_mat[i];
526
527 /* Apply data weights */
528 if(tacIsWeighted(&tac)) nnlsWght(llsq_n, llsq_m, llsq_a, llsq_b, tac.w);
529
530 /* Compute NNLS */
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");
534 if(ret>1) {
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;
537 r2=0.0;
538 } else if(ret==1) {
539 fprintf(stderr, "Warning: NNLS iteration max exceeded for %s\n", tac.c[ti].name);
540 }
541 if(verbose>4) {
542 printf("solution_vector: %g", llsq_wp[0]);
543 for(int ni=1; ni<llsq_n; ni++) printf(", %g", llsq_wp[ni]);
544 printf("\n");
545 printf("x coefficient_vector: %g", llsq_x[0]);
546 for(int ni=1; ni<llsq_n; ni++) printf(", %g", llsq_x[ni]);
547 printf("\n");
548 }
549 for(int ni=0; ni<llsq_n; ni++) par.r[ti].p[ni]=llsq_x[ni];
550 par.r[ti].wss=r2;
551 par.r[ti].dataNr=tacWSampleNr(&tac);
552 par.r[ti].fitNr=llsq_n;
553
554 /* Compute fitted TAC (into taci since it is otherwise not needed) */
555 /* Subtract k2 * TTAC integral so that fitted TACs are comparable to measured TACs */
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];
559 }
560
561 } // next TAC
562
563 /* Free allocated memory */
564 free(llsq_a); free(llsq_mat);
565
566 /* Print original LLSQ parameters on screen */
567 if(verbose>3 && par.tacNr<50)
568 parWrite(&par, stdout, PAR_FORMAT_TSV_UK, 0, &status);
569
570 /* Convert LLSQ parameters to CM model parameters */
571 for(int ti=0; ti<par.tacNr; ti++) {
572 if(ti==ib) continue;
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;
578 }
579 }
580
581
582 /*
583 * Calculate input curves for plotting/saving:
584 * Cb, (1-HCT)*Cp, and HCT*Crbc
585 */
586 if(icorfile[0] || isvgfile[0]) {
587 TAC inp; tacInit(&inp);
588 if(tacExtract(&tac, &inp, ib)!=TPCERROR_OK) {
589 fprintf(stderr, "Error: cannot plot input curves.\n");
590 tacFree(&tac); tacFree(&taci); parFree(&par);
591 return(101);
592 }
593 if(tacAllocateMore(&inp, 2)!=TPCERROR_OK) {
594 fprintf(stderr, "Error: cannot allocate memory.\n");
595 tacFree(&tac); tacFree(&taci); parFree(&par);
596 return(102);
597 }
598 inp.tacNr=3;
599 strcpy(inp.c[0].name, "Blood");
600 strcpy(inp.c[1].name, "Plasma");
601 strcpy(inp.c[2].name, "RBC");
602 /* Simulate (1-HCT)*Cp curve */
603 double *cb=inp.c[0].y;
604 double *cp=inp.c[1].y;
605 double cpi=0.0;
606 double t_last=0.0, cp_last=0.0, cpi_last=0.0;
607 for(int i=0; i<inp.sampleNr; i++) {
608 /* delta time / 2 */
609 double dt2=0.5*(inp.x[i]-t_last);
610 if(dt2>0.0) {
611 /* plasma */
612 cp[i] = (cb[i] - fixedk2*(cpi_last + dt2*cp_last)) / (1.0 + dt2*fixedk2);
613 /* integral */
614 cpi+=(cp[i]+cp_last)*dt2;
615 } else {cp[i]=cp_last;}
616 t_last=inp.x[i];
617 cp_last=cp[i];
618 cpi_last=cpi;
619 }
620 /* Calculate HCT*Crbc */
621 for(int i=0; i<inp.sampleNr; i++) {
622 inp.c[2].y[i]=inp.c[0].y[i] - inp.c[1].y[i];
623 }
624 if(isvgfile[0]) {
625 if(verbose>1) printf("plotting input TAC\n");
626 if(tacPlotLineSVG(&inp, "", 0.0, nan(""), nan(""), nan(""), isvgfile, NULL)!=TPCERROR_OK) {
627 fprintf(stderr, "Error: cannot plot input curves.\n");
628 tacFree(&tac); tacFree(&taci); parFree(&par); tacFree(&inp);
629 return(104);
630 }
631 if(verbose>0) printf("Input curves plotted in %s.\n", isvgfile);
632 }
633 if(icorfile[0]) {
634 if(verbose>1) printf("saving (1-HCT)*Cp TAC\n");
635 tacSwapTACCs(&inp, 0, 1);
636 inp.tacNr=1;
637 int ret=TPCERROR_OK;
638 FILE *fp; fp=fopen(icorfile, "w");
639 if(fp==NULL) {
640 fprintf(stderr, "Error: cannot open file for writing '%s'.\n", icorfile);
641 ret=TPCERROR_FAIL;
642 } else {
643 ret=tacWrite(&inp, fp, TAC_FORMAT_PMOD, 1, &status);
644 fclose(fp);
645 if(ret!=TPCERROR_OK) fprintf(stderr, "Error: %s\n", errorMsg(status.error));
646 }
647 if(ret!=TPCERROR_OK) {
648 tacFree(&tac); tacFree(&taci); parFree(&par); tacFree(&inp);
649 return(105);
650 }
651 if(verbose>0) printf("Corrected input saved in %s.\n", icorfile);
652 inp.tacNr=3;
653 }
654 tacFree(&inp);
655 }
656
657
658 /*
659 * Save and/or plot fitted TACs, if requested
660 */
661 if(svgfile[0] || fitfile[0]) {
662
663 /* Since fitted TACs are now in place of integral curves, but BTAC obviously was not fitted,
664 copy the original BTAC in there, so that plots won't be messed up */
665 for(int i=0; i<taci.sampleNr; i++) taci.c[ib].y[i]=tac.c[ib].y[i];
666
667 /*
668 * SVG plot of fitted and original data
669 */
670 if(svgfile[0]) {
671 if(verbose>1) printf("saving SVG plot\n");
672 char buf[128];
673 sprintf(buf, "NNLS trap");
674 int i=iftFindKey(&tac.h, "studynr", 0);
675 if(i<0) i=iftFindKey(&tac.h, "study_number", 0);
676 if(i>=0) {strcat(buf, ": "); strcat(buf, tac.h.item[i].value);}
677 if(tacPlotFitSVG(&tac, &taci, buf, 0.0, nan(""), 0.0, nan(""), svgfile, &status)!=TPCERROR_OK) {
678 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
679 tacFree(&tac); tacFree(&taci); parFree(&par);
680 return(201);
681 }
682 if(verbose>0) printf("Plots written in %s.\n", svgfile);
683 }
684
685 /*
686 * Save fitted TTACs
687 */
688 if(fitfile[0]) {
689 if(verbose>1) printf("writing %s\n", fitfile);
690 int ret=TPCERROR_OK;
691 FILE *fp; fp=fopen(fitfile, "w");
692 if(fp==NULL) {
693 fprintf(stderr, "Error: cannot open file for writing fitted TTACs.\n");
694 ret=TPCERROR_FAIL;
695 } else {
696 ret=tacWrite(&taci, fp, TAC_FORMAT_PMOD, 1, &status);
697 fclose(fp);
698 if(ret!=TPCERROR_OK) fprintf(stderr, "Error: %s\n", errorMsg(status.error));
699 }
700 if(ret!=TPCERROR_OK) {
701 tacFree(&tac); tacFree(&taci); parFree(&par);
702 return(202);
703 }
704 if(verbose>0) printf("fitted TACs saved in %s.\n", fitfile);
705 }
706
707 }
708
709
710 /* Remove BTAC from parameters */
711 parDeleteTAC(&par, ib);
712
713 /* If k2 was constrained to median or large muscle region, write it in header
714 and remove it from parameters */
715 if(fixk2==2 || fixk2==3) iftPutDouble(&par.h, "k2", fixedk2, 0, NULL);
716 if(fixk2!=0) {
717 par.parNr=2;
718 for(int i=0; i<par.tacNr; i++) par.r[i].fitNr=2;
719 }
720
721 /* Show the parameters */
722 if(verbose>0) parWrite(&par, stdout, PAR_FORMAT_TSV_UK, 1, NULL);
723
724 /*
725 * Save the parameters, if requested
726 */
727 if(parfile[0]) {
728 par.format=parFormatFromExtension(parfile);
729 if(verbose>2) printf("parameter file format := %s\n", parFormattxt(par.format));
731 /* Save file */
732 if(verbose>1) printf(" saving %s\n", parfile);
733 int ret=TPCERROR_OK;
734 FILE *fp; fp=fopen(parfile, "w");
735 if(fp==NULL) {
736 fprintf(stderr, "Error: cannot open file for writing results.\n");
737 ret=TPCERROR_FAIL;
738 } else {
739 ret=parWrite(&par, fp, PAR_FORMAT_UNKNOWN, 1, &status);
740 fclose(fp);
741 if(ret!=TPCERROR_OK) fprintf(stderr, "Error: %s\n", errorMsg(status.error));
742 }
743 if(ret!=TPCERROR_OK) {
744 tacFree(&tac); tacFree(&taci); parFree(&par);
745 return(21);
746 }
747 if(verbose>0) printf("parameters saved in %s\n", parfile);
748 }
749
750
751 parFree(&par);
752 tacFree(&taci);
753 tacFree(&tac);
754
755 return(0);
756}
757/*****************************************************************************/
758
759/*****************************************************************************/
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,...
Definition datetime.c:119
int atofCheck(const char *s, double *v)
Definition decpoint.c:94
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
Definition ift.c:63
int iftPutDouble(IFT *ift, const char *key, const double value, char comment, TPCSTATUS *status)
Definition ift.c:128
int iftFindKey(IFT *ift, const char *key, int start_index)
Definition iftfind.c:30
int tacIntegrate(TAC *inp, TAC *out, TPCSTATUS *status)
Integrate TACs from one TAC structure into a new TAC structure.
Definition litac.c:27
double statMedian(double *a, const int n)
Definition median.c:25
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *wp, double *zzp, int *indexp)
Definition nnls.c:43
int nnlsWght(int N, int M, double **A, double *b, double *weight)
Definition nnls.c:259
void parFree(PAR *par)
Definition par.c:75
void parInit(PAR *par)
Definition par.c:25
int parDeleteTAC(PAR *par, int ti)
Definition par.c:519
char * parFormattxt(parformat c)
Definition pario.c:59
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
Definition pario.c:148
int parFormatFromExtension(const char *s)
Definition pario.c:102
int parAllocateWithTAC(PAR *par, TAC *tac, int parNr, TPCSTATUS *status)
Allocate PAR based on data in TAC.
Definition partac.c:90
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
Definition proginfo.c:406
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:632
char * value
Definition tpcift.h:37
IFT_ITEM * item
Definition tpcift.h:57
Definition tpcpar.h:100
int format
Definition tpcpar.h:102
IFT h
Optional (but often useful) header information.
Definition tpcpar.h:147
int parNr
Definition tpcpar.h:108
int tacNr
Definition tpcpar.h:104
PARR * r
Definition tpcpar.h:114
PARN * n
Definition tpcpar.h:112
char name[MAX_PARNAME_LEN+1]
Definition tpcpar.h:82
double wss
Definition tpcpar.h:72
int fitNr
Definition tpcpar.h:58
int dataNr
Definition tpcpar.h:62
double * p
Definition tpcpar.h:64
double start
Definition tpcpar.h:52
double end
Definition tpcpar.h:54
char name[MAX_TACNAME_LEN+1]
Definition tpctac.h:81
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
IFT h
Optional (but often useful) header information.
Definition tpctac.h:141
double * w
Definition tpctac.h:111
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
weights weighting
Definition tpctac.h:115
unit tunit
Definition tpctac.h:109
int tacNr
Definition tpctac.h:91
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.
Definition tac.c:396
void tacFree(TAC *tac)
Definition tac.c:106
void tacInit(TAC *tac)
Definition tac.c:24
int tacAllocateMore(TAC *tac, int tacNr)
Definition tac.c:178
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)
Definition tacfitplot.c:273
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)
Definition tacfitplot.c:27
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
char * tacFormattxt(tacformat c)
Definition tacio.c:98
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
Definition tacio.c:332
int tacNaNs(TAC *tac)
Definition tacnan.c:71
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacSwapTACCs(TAC *d, int i1, int i2)
Definition tacorder.c:282
int tacSelectTACs(TAC *d, const char *region_name, int reset, TPCSTATUS *status)
Definition tacselect.c:24
int tacFirstSelected(TAC *d)
Definition tacselect.c:122
int tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:72
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacIsWeighted(TAC *tac)
Definition tacw.c:24
unsigned int tacWSampleNr(TAC *tac)
Definition tacw.c:219
int tacWByFreq(TAC *tac, isotope isot, TPCSTATUS *status)
Definition tacw.c:134
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.
Definition tacx.c:653
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124
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_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_KBQ_PER_ML
kBq/mL
@ UNIT_SEC
seconds
@ UNIT_BQ_PER_ML
Bq/mL.
@ TPCERROR_FAIL
General error.
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for libtpcli.
Header file for libtpclinopt.
Header file for library libtpcnlopt.
Header file for libtpcpar.
@ PAR_FORMAT_UNKNOWN
Unknown format.
Definition tpcpar.h:28
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Definition tpcpar.h:35
Header file for libtpcrand.
Header file for libtpcstatist.
Header file for library libtpctac.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Definition tpctac.h:33
Header file for libtpctacmod.