TPCCLIB
Loading...
Searching...
No Matches
fit_trtm.c
Go to the documentation of this file.
1
9/*****************************************************************************/
10#include "tpcclibConfig.h"
11/*****************************************************************************/
12#include <stdio.h>
13#include <stdlib.h>
14#include <math.h>
15#include <unistd.h>
16#include <string.h>
17#include <time.h>
18/*****************************************************************************/
19#include "libtpcmisc.h"
20#include "libtpcmodel.h"
21#include "libtpccurveio.h"
22#include "libtpcsvg.h"
23#include "libtpcmodext.h"
24/*****************************************************************************/
25
26/*****************************************************************************/
27const int parNr=3;
28int fitframeNr=0;
29double *t, *cr, *ct, *tis, *w; /* These are pointers, not allocated */
30double pmin[MAX_PARAMS], pmax[MAX_PARAMS];
31double wss_wo_penalty=0.0;
32/* Local functions */
33double trtmFunc(int parNr, double *p, void*);
34/*****************************************************************************/
35
36/*****************************************************************************/
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).",
44 " ",
45 "Usage: @P [Options] ttacfile reference endtime resultfile",
46 " ",
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.",
51 " ",
52 "Options:",
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)",
56 " -lim=<filename>",
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.",
60 " -SD[=<y|N>]",
61 " Standard deviations are calculated and saved in results (y), or",
62 " not calculated (n).",
63 " -CL[=<y|N>]",
64 " 95% Confidence limits are calculated and saved in results (y), or",
65 " not calculated (n).",
66 " -w1",
67 " All weights are set to 1.0 (no weighting); by default, weights in",
68 " TTAC file are used, if available.",
69 " -wf",
70 " Weight by sampling interval.",
71 " -fit=<Filename>",
72 " Fitted regional TACs are written in file.",
73 " -svg=<Filename>",
74 " Fitted and measured TACs are plotted in specified SVG file.",
75 " -stdoptions", // List standard options like --help, -v, etc
76 " ",
77 " ",
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.",
80 " ",
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",
85 " ",
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",
90 " ",
91 "Example 3a: Create a file containing default parameter limits:",
92 " @P -lim=trtm.lim",
93 " ",
94 "Example 3b: Apply user-defined parameter constraints specified in trtm.lim:",
95 " @P -lim=trtm.lim a789.tac 'putam mean' 999 a789.tac",
96 " ",
97 "References:",
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.",
112 " ",
113 "See also: fitk3, tacweigh, fit_rrtm, lhtrtm, sim_rtcm, rescoll",
114 " ",
115 "Keywords: TAC, modelling, irreversible uptake, RTCM, reference input",
116 0};
117/*****************************************************************************/
118
119/*****************************************************************************/
120/* Turn on the globbing of the command line, since it is disabled by default in
121 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
122 In Unix&Linux wildcard command line processing is enabled by default. */
123/*
124#undef _CRT_glob
125#define _CRT_glob -1
126*/
127int _dowildcard = -1;
128/*****************************************************************************/
129
130/*****************************************************************************/
134int main(int argc, char **argv)
135{
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];
141 int fix_rk2=0;
142 char *cptr, tmp[256];
143 double fitdur=nan("");
144 int weights=0; // 0=default, 1=no weighting, 2=frequency
145 int doBootstrap=0, doSD=0, doCL=0;
146 double *sd, *cl1, *cl2;
147 double def_pmin[MAX_PARAMETERS], def_pmax[MAX_PARAMETERS];
148 int ret, n;
149
150
151 /* Set parameter initial values and constraints */
152 /* R1 */ def_pmin[0]=0.001; def_pmax[0]=10.0;
153 /* rk2 */ def_pmin[1]=0.000001; def_pmax[1]=1.0;
154 /* k3 */ def_pmin[2]=0.0; def_pmax[2]=10.0;
155
156 /*
157 * Get arguments
158 */
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;
161 /* Get options first, because it affects what arguments are read */
162 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
163 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
164 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
165 if(strncasecmp(cptr, "CL", 2)==0) {
166 if(strlen(cptr)==2) {doCL=1; continue;}
167 cptr+=2; if(*cptr=='=') {
168 cptr++;
169 if(*cptr=='Y' || *cptr=='y') {doCL=1; continue;}
170 if(*cptr=='N' || *cptr=='n') {doCL=0; continue;}
171 }
172 } else if(strncasecmp(cptr, "SD", 2)==0) {
173 if(strlen(cptr)==2) {doSD=1; continue;}
174 cptr+=2; if(*cptr=='=') {
175 cptr++;
176 if(*cptr=='Y' || *cptr=='y') {doSD=1; continue;}
177 if(*cptr=='N' || *cptr=='n') {doSD=0; continue;}
178 }
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) {
184 weights=1; continue;
185 } else if(strcasecmp(cptr, "WF")==0) {
186 weights=2; continue;
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) {
192 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]);
199 return(1);
200 }
201 continue;
202 }
203 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
204 return(1);
205 } else break;
206
207 /* Print help or version? */
208 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
209 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
210 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
211
212 /* Process other arguments, starting from the first non-option */
213 if(ai<argc) strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
214 if(ai<argc) strlcpy(rtacfile, argv[ai++], FILENAME_MAX);
215 if(ai<argc) {
216 if(atof_with_check(argv[ai], &fitdur)!=0 || fitdur<0.0) {
217 fprintf(stderr, "Error: invalid fit time: '%s'.\n", argv[ai]);
218 return(1);
219 }
220 if(fitdur==0) fitdur=1.0E+10;
221 ai++;
222 }
223 if(ai<argc) strlcpy(resfile, argv[ai++], FILENAME_MAX);
224 if(ai<argc) {
225 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
226 return(1);
227 }
228 if(doSD || doCL) doBootstrap=1; else doBootstrap=0;
229
230 /* In verbose mode print arguments and options */
231 if(verbose>1) {
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]);
245 }
246
247 /* If only file name for parameter constraints was given, then write one
248 with default contents, and exit */
249 if(limfile[0] && !ttacfile[0]) {
250 /* Check that initial value file does not exist */
251 if(strcasecmp(limfile, "stdout")!=0 && access(limfile, 0) != -1) {
252 fprintf(stderr, "Error: parameter constraint file %s exists.\n", limfile);
253 return(9);
254 }
255 if(verbose>1 && strcasecmp(limfile, "stdout")!=0)
256 printf("writing parameter constraints file\n");
257 /* Create parameter file */
258 IFT ift; iftInit(&ift);
259 iftPutDouble(&ift, "R1_lower", def_pmin[0], NULL, 0);
260 iftPutDouble(&ift, "R1_upper", def_pmax[0], NULL, 0);
261 iftPutDouble(&ift, "rk2_lower", def_pmin[1], NULL, 0);
262 iftPutDouble(&ift, "rk2_upper", def_pmax[1], NULL, 0);
263 iftPutDouble(&ift, "k3_lower", def_pmin[2], NULL, 0);
264 iftPutDouble(&ift, "k3_upper", def_pmax[2], NULL, 0);
265 ret=iftWrite(&ift, limfile, 0);
266 if(ret) {
267 fprintf(stderr, "Error in writing '%s': %s\n", limfile, ift.status);
268 iftEmpty(&ift); return(9);
269 }
270 if(strcasecmp(limfile, "stdout")!=0)
271 fprintf(stdout, "Parameter file %s with initial values written.\n", limfile);
272 iftEmpty(&ift); return(0);
273 }
274
275
276 /* Did we get all the information that we need? */
277 if(!resfile[0]) {
278 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
279 return(1);
280 }
281
282
283 /*
284 * Read model parameter upper and lower limits
285 * if file for that was given
286 */
287 if(limfile[0]) {
288 if(verbose>1) printf("reading %s\n", limfile);
289 IFT ift; iftInit(&ift);
290 double v;
291 ret=iftRead(&ift, limfile, 1, 0);
292 if(ret) {
293 fprintf(stderr, "Error in reading '%s': %s\n", limfile, ift.status);
294 return(9);
295 }
296 if(verbose>10) iftWrite(&ift, "stdout", 0);
297 int n=0;
298 if(iftGetDoubleValue(&ift, 0, "R1_lower", &v, 0)>=0) {def_pmin[0]=v; n++;}
299 if(iftGetDoubleValue(&ift, 0, "R1_upper", &v, 0)>=0) {def_pmax[0]=v; n++;}
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;}
302 if(iftGetDoubleValue(&ift, 0, "k3_lower", &v, 0)>=0) {def_pmin[2]=v; n++;}
303 if(iftGetDoubleValue(&ift, 0, "k3_upper", &v, 0)>=0) {def_pmax[2]=v; n++;}
304 iftEmpty(&ift);
305 if(n==0) {fprintf(stderr, "Error: invalid parameter file.\n"); return(9);}
306 }
307 /* Check that these are ok */
308 n=0; ret=0;
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");
315 }
316 if(ret!=0) {
317 fprintf(stderr, "Error: invalid parameter constraints.\n");
318 return(9);
319 }
320 if(n==0) {
321 fprintf(stderr, "Error: no model parameters left free for fitting.\n");
322 return(9);
323 }
324 if(verbose>1) {
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]);
329 }
330 }
331
332
333
334 /*
335 * Read TTAC file
336 */
337 if(verbose>1) printf("reading %s\n", ttacfile);
338 DFT dft; dftInit(&dft);
339 if(dftRead(ttacfile, &dft)) {
340 fprintf(stderr, "Error in reading '%s': %s\n", ttacfile, dfterrmsg);
341 return(2);
342 }
343 /* Check for NA's */
344 if(dft_nr_of_NA(&dft) > 0) {
345 fprintf(stderr, "Error: missing sample(s) in %s\n", ttacfile);
346 dftEmpty(&dft); return(2);
347 }
348 /* Sort the data by increasing sample times */
349 dftSortByFrame(&dft);
350 /* Set time unit to min */
351 ret=dftTimeunitConversion(&dft, TUNIT_MIN);
352 if(ret) fprintf(stderr, "Warning: check that regional data times are in minutes.\n");
353 /* Remove frame overlaps and gaps */
354 if(dft.timetype==DFT_TIME_STARTEND) {
355 if(verbose>2) fprintf(stdout, "checking frame overlap in %s\n", ttacfile);
356 ret=dftDeleteFrameOverlap(&dft);
357 if(ret) {
358 fprintf(stderr, "Error: %s has overlapping frame times.\n", ttacfile);
359 dftEmpty(&dft); return(2);
360 }
361 }
362 /* Set fit duration */
363 int first, last;
364 double starttime, endtime;
365 starttime=0.0; endtime=fitdur;
366 fitframeNr=fittime_from_dft(&dft, &starttime, &endtime, &first, &last, verbose-2);
367 if(fitframeNr<5) {
368 fprintf(stderr, "Error: too few data points for a decent fit.\n");
369 dftEmpty(&dft); return(2);
370 }
371 if(verbose>2) {
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);
378 }
379 fitdur=endtime;
380 /* Check that there is not any significant delay in the beginning of the data */
381 if(dft.timetype==DFT_TIME_STARTEND) {
382 if(dft.x1[0]>0.45) {
383 fprintf(stderr, "Error: TACs must start at time zero.\n");
384 dftEmpty(&dft); return(2);
385 }
386 if(dft.x1[0]>0.0833333) {
387 fprintf(stderr, "Warning: TACs should start at time zero.\n");
388 }
389 }
390 if(verbose>2) printf("Tissue calibration unit := %s\n", dft.unit);
391
392 /* Add data weights, if requested */
393 if(weights==1) {
394 dft.isweight=0;
395 for(int i=0; i<dft.frameNr; i++) dft.w[i]=1.0;
396 } else if(weights==2) {
397 if(dftWeightByFreq(&dft)!=0) {
398 fprintf(stderr, "Error: cannot set data weights.\n");
399 dftEmpty(&dft); return(2);
400 }
401 } else if(dft.isweight==0) {
402 fprintf(stderr, "Warning: data is not weighted.\n");
403 }
404 /* Print the weights */
405 if(verbose>2) {
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");
409 }
410
411
412 /*
413 * Read reference TAC
414 */
415 if(verbose>1) printf("\nreading reference\n");
416 int inputtype=-1, ref=-1;
417 ret=dftReadReference(&dft, rtacfile, &inputtype, &ref, tmp, verbose-1);
418 if(ret<=0) {
419 fprintf(stderr, "Error in reading reference input: %s\n", tmp);
420 dftEmpty(&dft); if(verbose>1) printf("ret := %d\n", ret);
421 return(3);
422 }
423 if(ret>1) {
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);
427
428 /* Allocate an extra TAC for the bootstrap */
429 int bsi=-1;
430 if(doBootstrap) {
431 ret=dftAddmem(&dft, 1); if(ret) {
432 fprintf(stderr, "Error: cannot allocate more memory.\n");
433 dftEmpty(&dft); return(4);
434 }
435 bsi=dft.voiNr;
436 strcpy(dft.voi[bsi].voiname, "BS");
437 strcpy(dft.voi[bsi].name, "BS");
438 }
439
440
441
442 /*
443 * Prepare the room for results
444 */
445 if(verbose>1) printf("initializing result data\n");
446 RES res; resInit(&res);
447 ret=res_allocate_with_dft(&res, &dft); if(ret!=0) {
448 fprintf(stderr, "Error: cannot set-up memory for results.\n");
449 dftEmpty(&dft); return(4);
450 }
451 /* Copy titles & filenames */
452 tpcProgramName(argv[0], 1, 1, res.program, 256);
453 strcpy(res.datafile, ttacfile);
454 if(inputtype!=5 && rtacfile[0]) strcpy(res.reffile, rtacfile);
455 if(ref>=0) strcpy(res.refroi, dft.voi[ref].name);
456 strcpy(res.fitmethod, "TGO");
457 /* Constants */
458 res.isweight=dft.isweight;
459 /* Set data range */
460 sprintf(res.datarange, "%g - %g %s", 0.0, fitdur, petTunit(dft.timeunit));
461 res.datanr=fitframeNr;
462 /* Set current time to results */
463 res.time=time(NULL);
464 /* Set parameter number, including also the extra "parameters"
465 and the parameter names and units */
466 res.parNr=4;
467 {
468 int pi;
469 pi=0; strcpy(res.parname[pi], "R1"); strcpy(res.parunit[pi], "");
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], "");
473 }
474
475
476 /*
477 * Determine average and median k2' by fitting, if regional k2' will be fixed to it
478 */
479 int tgoNr=0, neighNr=0, iterNr=0;
480 double wss;
481 if(fix_rk2 >= 2) {
482 double rk2median, rk2mean, rk2sd, rk2list[res.voiNr], v;
483
484 if(verbose>0) {
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");
487 }
488
489 /* Set common data pointers */
490 t=dft.x;
491 cr=dft.voi[ref].y;
492
493 /* Fit regions */
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);
496 /* Set data pointers */
497 tis=dft.voi[ri].y; ct=dft.voi[ri].y2; w=dft.w;
498 double *p=res.voi[ri].parameter;
499 /* Set parameter constraints */
500 for(int pi=0; pi<parNr; pi++) {pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];}
501 if(verbose>30) {
502 printf(" constraints :=");
503 for(int pi=0; pi<parNr; pi++) printf(" [%g,%g]", pmin[pi], pmax[pi]);
504 printf("\n");
505 }
506 /* Fit */
507 if(verbose>2) printf(" fitting curve...\n");
510 tgoNr=220;
511 neighNr=20;
512 ret=tgo(pmin, pmax, trtmFunc, NULL, parNr, neighNr, &wss, p, tgoNr, iterNr, verbose-8);
513 if(ret>0) {
514 fprintf(stderr, "Error in optimization (%d).\n", ret);
515 dftEmpty(&dft); resEmpty(&res); return(6);
516 }
517 if(verbose>3) {
518 for(int pi=0; pi<parNr; pi++) printf(" %g", p[pi]);
519 printf(" -> WSS=%g\n", p[parNr]); fflush(stdout);
520 }
521 /* Correct fitted parameters to match constraints like inside the function */
522 (void)modelCheckParameters(parNr, pmin, pmax, p, p, NULL);
523 p[parNr]=wss=wss_wo_penalty;
524 if(verbose>2) printf("wss := %g\nfitframeNr := %d\n", wss, fitframeNr);
525 } // next region
526
527 /* Calculate average and median k2' */
528 for(int ri=0, n=0; ri<res.voiNr; ri++) if(dft.voi[ri].sw==0) {
529 /* check that k2 is reasonable */
530 if(isnan(res.voi[ri].parameter[1]) || res.voi[ri].parameter[1]<=0.010) continue;
531 rk2list[n++]=res.voi[ri].parameter[1];
532 }
533 rk2mean=dmean(rk2list, n, &rk2sd);
534 rk2median=dmedian(rk2list, n);
535 if(verbose>1) {
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);
539 }
540
541 /* Fix rk2' */
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");
545 dftEmpty(&dft); resEmpty(&res); return(7);
546 }
547 def_pmin[1]=def_pmax[1]=v;
548 if(verbose>1) printf("fixed_rk2 := %g\n", def_pmin[1]);
549 }
550
551
552 /*
553 * Fit one VOI at a time
554 */
555 if(verbose>0) printf("\nfitting...\n");
556 /* Set common data pointers */
557 t=dft.x; cr=dft.voi[ref].y;
558 /* Fit model to one TAC at a time */
559 for(int ri=0; ri<dft.voiNr; ri++) if(ri!=ref) {
560
561 if(verbose>1) printf("Region %d %s\n", ri+1, dft.voi[ri].name);
562 /* Set data pointers */
563 tis=dft.voi[ri].y; ct=dft.voi[ri].y2; w=dft.w;
564 double *p=res.voi[ri].parameter;
565
566 /* Set common parameter constraints */
567 for(int pi=0; pi<parNr; pi++) {pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];}
568 if(verbose>30) {
569 printf("Parameter constraints:\n");
570 for(int pi=0; pi<parNr; pi++) printf(" %10.3E - %10.3E\n", pmin[pi], pmax[pi]);
571 }
572
573 /* Fit */
574 if(verbose>2) printf(" fitting curve...\n");
577 if(fix_rk2==1) {
578 tgoNr=220;
579 neighNr=20;
580 } else {
581 tgoNr=180;
582 neighNr=16;
583 }
584 ret=tgo(pmin, pmax, trtmFunc, NULL, parNr, neighNr, &wss, p, tgoNr, iterNr, verbose-8);
585 if(ret>0) {
586 fprintf(stderr, "Error in optimization (%d).\n", ret);
587 dftEmpty(&dft); resEmpty(&res); return(6);
588 }
589 if(verbose>3) {
590 for(int pi=0; pi<parNr; pi++) printf(" %g", p[pi]);
591 printf(" -> WSS=%g\n", p[parNr]); fflush(stdout);
592 }
593 /* Correct fitted parameters to match constraints like inside the function */
594 (void)modelCheckParameters(parNr, pmin, pmax, p, p, NULL);
595 p[parNr]=wss=wss_wo_penalty;
596 if(verbose>2) printf("wss := %g\nfitframeNr := %d\n", wss, fitframeNr);
597
598 /* Bootstrap */
599 if(doBootstrap) {
600 if(verbose>2) printf(" bootstrapping...\n");
601 /* bootstrap changes measured and simulated data, therefore use copies */
602 tis=dft.voi[bsi].y; ct=dft.voi[bsi].y2;
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;
605 ret=bootstrap(
606 0, cl1, cl2, sd, p, pmin, pmax, fitframeNr,
607 // measured and fitted original TAC, not modified
608 dft.voi[ri].y, dft.voi[ri].y2,
609 // tissue TAC noisy data is written to be used by objf
610 tis,
611 parNr, w, trtmFunc, tmp, verbose-5
612 );
613 if(ret) {
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("");
618 }
619 }
620 // back to what pointers were
621 tis=dft.voi[ri].y; tis=dft.voi[ri].y2;
622 }
623
624 } /* Next VOI */
625 if(verbose>0) {fprintf(stdout, "\n"); fflush(stdout);}
626
627 /* Delete the reference region from the results */
628 resDelete(&res, ref);
629 /* If reference data file was given and it contained several TACs then delete those too */
630 if(inputtype!=5) {
631 for(int i=dft.voiNr-1; i>=0; i--) if(dft.voi[i].sw!=0) resDelete(&res, i);
632 }
633
634 /*
635 * Print results on screen
636 */
637 if(verbose>0) {resPrint(&res); fprintf(stdout, "\n");}
638
639
640 /*
641 * Save results
642 */
643 if(verbose>1) printf("saving results in %s\n", resfile);
644 ret=resWrite(&res, resfile, verbose-5);
645 if(ret) {
646 fprintf(stderr, "Error in writing '%s': %s\n", resfile, reserrmsg);
647 resEmpty(&res); dftEmpty(&dft);
648 return(11);
649 }
650 if(verbose>0) fprintf(stdout, "Model parameters written in %s\n", resfile);
651
652
653
654 /*
655 * Saving and/or plotting of fitted TACs
656 */
657 if(svgfile[0] || fitfile[0]) {
658
659 /* Create a DFT containing fitted TACs */
660 char tmp[64];
661 DFT dft2;
662 dftInit(&dft2); ret=dftdup(&dft, &dft2);
663 if(ret) {
664 fprintf(stderr, "Error: cannot save fitted curves.\n");
665 dftEmpty(&dft); resEmpty(&res);
666 return(21);
667 }
668 for(int ri=0; ri<dft.voiNr; ri++)
669 if(ri!=ref)
670 for(int fi=0; fi<fitframeNr; fi++)
671 dft2.voi[ri].y[fi]=dft2.voi[ri].y2[fi];
672 dft2.frameNr=fitframeNr;
673
674 /* Save SVG plot of fitted and original data */
675 if(svgfile[0]) {
676 if(verbose>1) printf("saving SVG plot\n");
677 sprintf(tmp, "TRTM fit ");
678 if(strlen(dft.studynr)>0) strcat(tmp, dft.studynr);
679 ret=plot_fitrange_svg(&dft, &dft2, tmp, 0.0, 1.02*dft.x[fitframeNr-1],
680 0.0, nan(""), svgfile, verbose-8);
681 if(ret) {
682 fprintf(stderr, "Error (%d) in writing '%s'.\n", ret, svgfile);
683 dftEmpty(&dft2); dftEmpty(&dft); resEmpty(&res);
684 return(30+ret);
685 }
686 if(verbose>0) printf("Plots written in %s\n", svgfile);
687 }
688
689 /* Delete reference region(s) from the data, unless it already existed in data */
690 if(inputtype!=5) {
691 for(int i=dft2.voiNr-1; i>=0; i--) if(dft2.voi[i].sw!=0) dftDelete(&dft2, i);
692 }
693
694 /* Save fitted TACs */
695 if(fitfile[0]) {
696 if(verbose>1) printf("saving fitted curves\n");
697 tpcProgramName(argv[0], 1, 0, tmp, 128);
698 sprintf(dft2.comments, "# program := %s\n", tmp);
699 if(dftWrite(&dft2, fitfile)) {
700 fprintf(stderr, "Error in writing '%s': %s\n", fitfile, dfterrmsg);
701 dftEmpty(&dft2); dftEmpty(&dft); resEmpty(&res);
702 return(22);
703 }
704 if(verbose>0) printf("Fitted TACs written in %s\n", fitfile);
705 }
706
707 dftEmpty(&dft2);
708 }
709
710
711 resEmpty(&res);
712 dftEmpty(&dft);
713
714 return(0);
715}
716/*****************************************************************************/
717
718/*****************************************************************************
719 *
720 * Functions to be minimized
721 *
722 *****************************************************************************/
723double trtmFunc(int parNr, double *p, void *fdata)
724{
725 int ret;
726 double R1, k2, rk2, k3, d, wss=0.0;
727 double pa[MAX_PARAMETERS], penalty=1.0;
728
729
730 /* Check parameters against the constraints */
731 ret=modelCheckParameters(parNr, pmin, pmax, p, pa, &penalty);
732 if(fdata) {}
733 /* Get parameters */
734 R1=pa[0]; rk2=pa[1]; k3=pa[2];
735 k2=R1*rk2;
736
737 /* Simulate the tissue PET TAC */
738 ret=simTRTM(t, cr, fitframeNr, R1, k2, k3, ct);
739 if(ret) {
740 fprintf(stderr, " error %d in simulation\n", ret);
741 return(nan(""));
742 }
743
744 /* Calculate error */
745 for(int i=0; i<fitframeNr; i++) if(w[i]>0.0) {
746 d=ct[i]-tis[i];
747 wss+=w[i]*d*d;
748 }
749 wss_wo_penalty=wss;
750 wss*=penalty;
751 if(0) printf("R1=%g k2=%g k3=%g => %g\n", R1, k2, k3, wss);
752
753 return(wss);
754}
755/*****************************************************************************/
756
757/*****************************************************************************/
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)
Definition bootstrap.c:55
int modelCheckParameters(int par_nr, double *lower_p, double *upper_p, double *test_p, double *accept_p, double *penalty)
Definition constraints.c:15
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
int dftdup(DFT *dft1, DFT *dft2)
Definition dft.c:655
char dfterrmsg[64]
Definition dft.c:6
int dftDeleteFrameOverlap(DFT *dft)
Definition dft.c:1237
int dftAddmem(DFT *dft, int voiNr)
Definition dft.c:107
int dftSortByFrame(DFT *dft)
Definition dft.c:1169
int dftDelete(DFT *dft, int voi)
Definition dft.c:538
void dftEmpty(DFT *data)
Definition dft.c:20
int dft_nr_of_NA(DFT *dft)
Definition dft.c:905
int dftReadReference(DFT *tissue, char *filename, int *filetype, int *ref_index, char *status, int verbose)
Definition dftinput.c:204
int dftRead(char *filename, DFT *data)
Definition dftio.c:22
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int res_allocate_with_dft(RES *res, DFT *dft)
Definition dftres.c:14
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
int fittime_from_dft(DFT *dft, double *startTime, double *endTime, int *first, int *last, int verbose)
Definition fittime.c:16
int iftPutDouble(IFT *ift, char *key, double value, char *cmt_type, int verbose)
Definition ift.c:145
void iftEmpty(IFT *ift)
Definition ift.c:60
void iftInit(IFT *ift)
Definition ift.c:45
int iftRead(IFT *ift, char *filename, int is_key_required, int verbose)
Definition iftfile.c:24
int iftWrite(IFT *ift, char *filename, int verbose)
Definition iftfile.c:282
int iftGetDoubleValue(IFT *ift, int si, const char *key, double *value, int verbose)
Definition iftsrch.c:268
Header file for libtpccurveio.
char reserrmsg[64]
Definition result.c:6
void resInit(RES *res)
Definition result.c:52
void resPrint(RES *res)
Definition result.c:186
int resWrite(RES *res, char *filename, int verbose)
Definition result.c:565
int resDelete(RES *res, int voi)
Definition result.c:1342
#define DFT_TIME_STARTEND
void resEmpty(RES *res)
Definition result.c:22
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
Definition proginfo.c:101
char * petTunit(int tunit)
Definition petunits.c:226
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
Header file for libtpcmodel.
int simTRTM(double *t, double *cr, int nr, double R1, double k2, double k3, double *ct)
Definition simulate.c:986
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)
Definition tgo.c:39
int TGO_LOCAL_INSIDE
Definition tgo.c:29
#define MAX_PARAMETERS
Definition libtpcmodel.h:31
int TGO_SQUARED_TRANSF
Definition tgo.c:27
#define MAX_PARAMS
Definition libtpcmodel.h:35
double dmean(double *data, int n, double *sd)
Definition median.c:73
double dmedian(double *data, int n)
Definition median.c:48
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)
Definition plotfit.c:16
Header file for libtpcsvg.
int timetype
Voi * voi
int timeunit
char studynr[MAX_STUDYNR_LEN+1]
double * w
double * x1
char comments[_DFT_COMMENT_LEN+1]
int voiNr
int frameNr
int isweight
double * x
char unit[MAX_UNITS_LEN+1]
const char * status
Definition libtpcmisc.h:277
int parNr
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
int voiNr
int datanr
ResVOI * voi
char program[1024]
char datarange[128]
int isweight
char datafile[FILENAME_MAX]
char reffile[FILENAME_MAX]
char fitmethod[128]
char refroi[64]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
time_t time
double parameter[MAX_RESPARAMS]
double cl2[MAX_RESPARAMS]
double cl1[MAX_RESPARAMS]
double sd[MAX_RESPARAMS]
double * y2
char voiname[MAX_REGIONSUBNAME_LEN+1]
char sw
double * y
char name[MAX_REGIONNAME_LEN+1]