TPCCLIB
Loading...
Searching...
No Matches
fit_h2o.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 <math.h>
13#include <unistd.h>
14#include <string.h>
15#include <time.h>
16/*****************************************************************************/
17#include "libtpcmisc.h"
18#include "libtpcmodel.h"
19#include "libtpccurveio.h"
20#include "libtpcsvg.h"
21#include "libtpcmodext.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25int parNr=4;
26DFT btac, ttac;
27double *petmeas, *petsim;
28double fVa=-1.0, fK1k2=-1.0, fDelay;
29double pmin[MAX_PARAMETERS], pmax[MAX_PARAMETERS];
30int fitframeNr;
31double wss_wo_penalty=0.0;
32/* pointer to data, not allocated */
33double *ca, *cs, *cs2, *simdc, *petdc, *weight, *cpet, *csim, *wres;
34/* Local functions */
35double waterFunc(int parNr, double *p, void*);
36/*****************************************************************************/
37
38/*****************************************************************************/
39static char *info[] = {
40 "Non-linear fitting of one-tissue compartmental water model to regional",
41 "dynamic PET [O-15]H2O study data.",
42 "The model parameters are blood flow (F), partition coefficient",
43 "(pWater, K1/k2), arterial blood volume (Va) and time delay (delayT) between",
44 "tissue and blood time-activity curves (TTAC and BTAC, respectively).",
45 "Venous radioactivity is assumed to be the same as the tissue concentration.",
46 "Extraction factor of water is assumed to be 1.0.",
47 " ",
48 " ______ ______ ",
49 " | | K1 | | ",
50 " | Ca | ---> | C1 | ",
51 " |______| |______| ",
52 " | k2 ",
53 " V ",
54 " ",
55 "The blood flow obtained using PET [O-15]H2O techniques reflects tissue",
56 "perfusion, since the method is dependent on the tissue exchange of",
57 "labelled water. Non-nutritive flow (blood flowing through arteriovenous",
58 "shunts) is not measured (Lammertsma & Jones, 1983).",
59 " ",
60 "Usage: @P [Options] btacfile ttacfile endtime resultfile",
61 " ",
62 "Options:",
63 " -lim[=<filename>]",
64 " Specify the constraints for model parameters;",
65 " This file with default values can be created by giving this",
66 " option as the only command-line argument to this program.",
67 " Without filename the default values are printed on screen.",
68 " Parameter can be fixed to a certain value by setting its",
69 " lower and upper limit to that value.",
70 " -ml or -dl",
71 " In results the units of F and Va will be given per mL or per dL,",
72 " respectively. By default, units are per dL.",
73 " -fpt",
74 " Blood flow (perfusion) is reported per tissue volume (PET volume minus",
75 " vascular volume), that is, F=K1. By default, perfusion is reported",
76 " per PET volume, that is, F=(1-Va)*K1.",
77 " -SD[=<y|N>]",
78 " Standard deviations are calculated and saved in results (Y, default),",
79 " or not calculated (n).",
80 " Program runs a lot faster if SD and CL are not calculated.",
81 " -CL[=<y|N>]",
82 " 95% Confidence limits are calculated and saved in results (y), or",
83 " not calculated (N, default).",
84 " -Va=<Va(%)>",
85 " Enter a fixed Va; fitted by default.",
86 " -Delay=<Time delay (s)>",
87 " Enter a fixed time delay; fitted by default.",
88 " Positive time delay means that BTAC is moved forward in time.",
89 " -pH2O=<Partition coefficient for water>",
90 " Enter a fixed pH2O; fitted by default.",
91 " -fit=<Filename>",
92 " Fitted regional TACs are written in DFT format.",
93 " -input=<Filename>",
94 " Input TAC sample times are corrected by the median of fitted time",
95 " delay values and saved; resulting input file can be used with imgflow,",
96 " or as input to this program to have common time delay for all regions.",
97 " -resid=<Filename>",
98 " Weighted residual curves are written in DFT format.",
99 " -svg=<Filename>",
100 " Fitted and measured TACs are plotted in specified SVG file.",
101 " -k2",
102 " Save k2 in result file.",
103 " -stdoptions", // List standard options like --help, -v, etc
104 " ",
105 "Example:",
106 " @P uo1234bl.kbq uo1234.dft 999 uo1234f.res",
107 " ",
108 "See also: b2t_h2o, imgflow, arlkup, water_input, fitk2, bfmh2o",
109 " ",
110 "Keywords: TAC, modelling, perfusion, blood flow, 1TCM",
111 0};
112/*****************************************************************************/
113
114/*****************************************************************************/
115/* Turn on the globbing of the command line, since it is disabled by default in
116 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
117 In Unix&Linux wildcard command line processing is enabled by default. */
118/*
119#undef _CRT_glob
120#define _CRT_glob -1
121*/
122int _dowildcard = -1;
123/*****************************************************************************/
124
125/*****************************************************************************/
129int main(int argc, char **argv)
130{
131 int ai, help=0, version=0, verbose=1;
132 double def_pmin[MAX_PARAMETERS], def_pmax[MAX_PARAMETERS];
133 char btacfile[FILENAME_MAX], ttacfile[FILENAME_MAX],
134 limfile[FILENAME_MAX], resfile[FILENAME_MAX],
135 fitfile[FILENAME_MAX], residfile[FILENAME_MAX],
136 inputfile[FILENAME_MAX], svgfile[FILENAME_MAX];
137 int doBootstrap=0, doSD=0, doCL=0; // 0=no, 1=yes
138 int per_dl=1; // 0 or 1
139 int flow_per_tissue=0; // 0: f=(1-Va)K1 ; 1: f=K1
140 int save_k2=0;
141 double fitdur=-1.0;
142 int fittedparNr=0;
143
144 IFT ift;
145 RES res;
146 char *cptr, tmp[FILENAME_MAX];
147 int times_changed=0;
148 int sim_index, simpet_index, bs_index;
149 double lambda=0.0;
150 double wss;
151
152
153 /* Set lambda for O-15 */
154 lambda=hl2lambda(HL_O15*60.0);
155
156 /* Set parameter initial values and constraints */
157 fDelay=nan("");
158 /* Flow */ def_pmin[0]=0.0; def_pmax[0]=600.0;
159 /* pH2O */ def_pmin[1]=0.30; def_pmax[1]=1.0;
160 /* Va */ def_pmin[2]=0.0; def_pmax[2]=25.0;
161 /* Delay */ def_pmin[3]=-60.0; def_pmax[3]=+60.0;
162
163 /*
164 * Get arguments
165 */
166 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
167 limfile[0]=btacfile[0]=ttacfile[0]=resfile[0]=inputfile[0]=(char)0;
168 svgfile[0]=fitfile[0]=residfile[0]=(char)0;
169 iftInit(&ift);
170 dftInit(&btac); dftInit(&ttac);
171 resInit(&res);
172 /* Get options first, because it affects what arguments are read */
173 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
174 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
175 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
176 if(strncasecmp(cptr, "CL", 2)==0) {
177 if(strlen(cptr)==2) {doCL=1; continue;}
178 cptr+=2; if(*cptr=='=') {
179 cptr++;
180 if(*cptr=='Y' || *cptr=='y') {doCL=1; continue;}
181 if(*cptr=='N' || *cptr=='n') {doCL=0; continue;}
182 }
183 } else if(strncasecmp(cptr, "SD", 2)==0) {
184 if(strlen(cptr)==2) {doSD=1; continue;}
185 cptr+=2; if(*cptr=='=') {
186 cptr++;
187 if(*cptr=='Y' || *cptr=='y') {doSD=1; continue;}
188 if(*cptr=='N' || *cptr=='n') {doSD=0; continue;}
189 }
190 } else if(strncasecmp(cptr, "LIM=", 4)==0 && strlen(cptr)>4) {
191 strlcpy(limfile, cptr+4, FILENAME_MAX); continue;
192 } else if(strncasecmp(cptr, "I=", 2)==0 && strlen(cptr)>2) { // deprecated
193 strlcpy(limfile, cptr+2, FILENAME_MAX); continue;
194 } else if(strcasecmp(cptr, "LIM")==0) {
195 strcpy(limfile, "stdout"); continue;
196 } else if(strcasecmp(cptr, "I")==0) { // deprecated
197 strcpy(limfile, "stdout"); continue;
198 } else if(strcasecmp(cptr, "DL")==0) {
199 per_dl=1; continue;
200 } else if(strcasecmp(cptr, "ML")==0) {
201 per_dl=0; continue;
202 } else if(strcasecmp(cptr, "FPT")==0) {
203 flow_per_tissue=1; continue;
204 } else if(strcasecmp(cptr, "K2")==0) {
205 save_k2=1; continue;
206 } if(strncasecmp(cptr, "INPUT=", 6)==0) {
207 strlcpy(inputfile, cptr+6, FILENAME_MAX);
208 if(strlen(inputfile)>0) continue;
209 } else if(strncasecmp(cptr, "SVG=", 4)==0) {
210 strlcpy(svgfile, cptr+4, FILENAME_MAX);
211 if(strlen(svgfile)>0) continue;
212 } else if(strncasecmp(cptr, "FIT=", 4)==0) {
213 strlcpy(fitfile, cptr+4, FILENAME_MAX);
214 if(strlen(fitfile)>0) continue;
215 } else if(strncasecmp(cptr, "RESID=", 6)==0) {
216 strlcpy(residfile, cptr+6, FILENAME_MAX);
217 if(strlen(residfile)>0) continue;
218 } else if((strncasecmp(cptr, "Va=", 3)==0 || strncasecmp(cptr, "Vb=", 3)==0) && strlen(cptr)>3) {
219 fVa=atof_dpi(cptr+3);
220 if(fVa>=0.0 && fVa<100.0) {
221 def_pmin[2]=def_pmax[2]=fVa;
222 if(fVa>0.0 && fVa<0.5)
223 fprintf(stderr, "Warning: Va was set to %g%%\n", fVa);
224 continue;
225 }
226 } else if(strncasecmp(cptr, "pH2O=", 5)==0 && strlen(cptr)>5) {
227 fK1k2=atof_dpi(cptr+5); if(fK1k2>0.0) continue;
228 } else if(strncasecmp(cptr, "delay=", 6)==0 && strlen(cptr)>6) {
229 fDelay=atof_dpi(cptr+6);
230 if(fDelay>-120.0 && fDelay<120.0) {
231 if(fDelay!=0.0 && fabs(fDelay)<1.0)
232 fprintf(stderr, "Warning: Delay was set to %g sec\n", fDelay);
233 continue;
234 }
235 }
236 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
237 return(1);
238 } else break;
239
240 /* Print help or version? */
241 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
242 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
243 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
244
245 /* Process other arguments, starting from the first non-option */
246 if(ai<argc) {strlcpy(btacfile, argv[ai++], FILENAME_MAX);}
247 if(ai<argc) {strlcpy(ttacfile, argv[ai++], FILENAME_MAX);}
248 if(ai<argc) {
249 if(atof_with_check(argv[ai], &fitdur) || fitdur<0.0) {
250 fprintf(stderr, "Error: invalid fit time '%s'.\n", argv[ai]); return(1);}
251 ai++;
252 }
253 if(ai<argc) {strlcpy(resfile, argv[ai++], FILENAME_MAX);}
254 if(ai<argc) {fprintf(stderr, "Error: too many arguments: '%s'.\n", argv[ai]); return(1);}
255 if(doSD || doCL) doBootstrap=1; else doBootstrap=0;
256
257 /* In verbose mode print arguments and options */
258 if(verbose>1) {
259 printf("limfile := %s\n", limfile);
260 printf("btacfile := %s\n", btacfile);
261 printf("ttacfile := %s\n", ttacfile);
262 printf("resfile := %s\n", resfile);
263 printf("fitfile := %s\n", fitfile);
264 printf("svgfile := %s\n", svgfile);
265 printf("residfile := %s\n", residfile);
266 printf("inputfile := %s\n", inputfile);
267 printf("per_dl := %d\n", per_dl);
268 printf("flow_per_tissue := %d\n", flow_per_tissue);
269 printf("doBootstrap := %d\n", doBootstrap);
270 printf("doSD := %d\n", doSD);
271 printf("doCL := %d\n", doCL);
272 if(fitdur>=0.0) printf("required_fitdur := %g\n", fitdur);
273 if(fVa>=0.0) printf("required_fVa := %g\n", fVa);
274 if(fK1k2>=0.0) printf("required_fK1k2 := %g\n", fK1k2);
275 if(!isnan(fDelay)) printf("required_fDelay := %g\n", fDelay);
276 printf("save_k2 := %d\n", save_k2);
277 printf("lambda := %g [1/s]\n", lambda);
278 fflush(stdout);
279 }
280
281 /* If only filename for initial values was given, then write one
282 with default contents, and exit */
283 if(limfile[0] && !btacfile[0]) {
284 /* Check that initial value file does not exist */
285 if(strcasecmp(limfile, "stdout")!=0 && access(limfile, 0) != -1) {
286 fprintf(stderr, "Error: parameter constraint file %s exists.\n", limfile);
287 return(9);
288 }
289 if(verbose>1) printf("writing parameter constraints file\n");
290 /* Create parameter file */
291 iftPutDouble(&ift, "K1_lower", def_pmin[0], NULL, 0);
292 iftPutDouble(&ift, "K1_upper", def_pmax[0], NULL, 0);
293 iftPutDouble(&ift, "K1k2_lower", def_pmin[1], NULL, 0);
294 iftPutDouble(&ift, "K1k2_upper", def_pmax[1], NULL, 0);
295 iftPutDouble(&ift, "Va_lower", def_pmin[2], NULL, 0);
296 iftPutDouble(&ift, "Va_upper", def_pmax[2], NULL, 0);
297 iftPutDouble(&ift, "Delay_lower", def_pmin[3], NULL, 0);
298 iftPutDouble(&ift, "Delay_upper", def_pmax[3], NULL, 0);
299 if(iftWrite(&ift, limfile, 0)) {
300 fprintf(stderr, "Error in writing '%s': %s\n", limfile, ift.status);
301 iftEmpty(&ift); return(9);
302 }
303 if(strcasecmp(limfile, "stdout")!=0)
304 fprintf(stdout, "Parameter file %s with initial values written.\n", limfile);
305 iftEmpty(&ift); return(0);
306 }
307
308 /* Did we get all the information that we need? */
309 if(fitdur==0) fitdur=1.0E+100;
310 else if(fitdur<0) {tpcPrintUsage(argv[0], info, stderr); return(1);}
311 if(!resfile[0]) {
312 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
313 return(1);
314 }
315
316 /*
317 * Read model parameter initial values and upper and lower limits
318 * if file for that was given
319 */
320 if(limfile[0]) {
321 double v;
322 if(verbose>1) printf("reading %s\n", limfile);
323 if(iftRead(&ift, limfile, 1, 0)) {
324 fprintf(stderr, "Error in reading '%s': %s\n", limfile, ift.status);
325 return(9);
326 }
327 if(verbose>10) iftWrite(&ift, "stdout", 0);
328 int n=0;
329 /* K1 */
330 if(iftGetDoubleValue(&ift, 0, "K1_lower", &v, 0)>=0) {def_pmin[0]=v; n++;}
331 if(iftGetDoubleValue(&ift, 0, "K1_upper", &v, 0)>=0) {def_pmax[0]=v; n++;}
332 /* K1/k2 */
333 if(iftGetDoubleValue(&ift, 0, "K1k2_lower", &v, 0)>=0) {def_pmin[1]=v; n++;}
334 if(iftGetDoubleValue(&ift, 0, "K1k2_upper", &v, 0)>=0) {def_pmax[1]=v; n++;}
335 /* Va */
336 if(iftGetDoubleValue(&ift, 0, "Va_lower", &v, 0)>=0) {def_pmin[2]=v; n++;}
337 if(iftGetDoubleValue(&ift, 0, "Va_upper", &v, 0)>=0) {def_pmax[2]=v; n++;}
338 /* Delay */
339 if(iftGetDoubleValue(&ift, 0, "Delay_lower", &v, 0)>=0) {def_pmin[3]=v; n++;}
340 if(iftGetDoubleValue(&ift, 0, "Delay_upper", &v, 0)>=0) {def_pmax[3]=v; n++;}
341 iftEmpty(&ift);
342 if(n==0) {fprintf(stderr, "Error: invalid parameter file.\n"); return(9);}
343 }
344 /* Check that these limits are ok */
345 {
346 int n=0, pi, ret=0;
347 for(pi=0; pi<parNr; pi++) {
348 if(def_pmin[pi]<0.0 && pi!=3) ret++;
349 if(def_pmax[pi]<def_pmin[pi]) ret++;
350 if(def_pmax[pi]>def_pmin[pi]) n++;
351 }
352 if(ret) {
353 fprintf(stderr, "Error: invalid parameter constraints.\n");
354 return(9);
355 }
356 if(n==0) {
357 fprintf(stderr, "Error: no model parameters left free for fitting.\n");
358 return(9);
359 }
360 }
361 /* Fixed/fitted Va */
362 if(fVa>=0.0) def_pmin[2]=def_pmax[2]=fVa;
363 else if(def_pmin[2]==def_pmax[2]) fVa=def_pmin[2];
364 if(verbose>1) {
365 if(fVa>=0.0) printf("fVa := %g\n", fVa);
366 }
367 /* Fixed/fitted pH2O */
368 if(fK1k2>=0.0) def_pmin[1]=def_pmax[1]=fK1k2;
369 else if(def_pmin[1]==def_pmax[1]) fK1k2=def_pmin[1];
370 if(verbose>1) {
371 if(fK1k2>=0.0) printf("fK1k2 := %g\n", fK1k2);
372 }
373 /* Fixed/fitted time delay */
374 if(!isnan(fDelay)) def_pmin[3]=def_pmax[3]=fDelay;
375 else if(def_pmin[3]==def_pmax[3]) fDelay=def_pmin[3];
376 if(verbose>1) {
377 if(!isnan(fDelay)) printf("fDelay := %g\n", fDelay);
378 }
379 /* Convert parameter settings */
380 /* F mL/min/dL -> mL/sec/mL */ def_pmin[0]/=6000.; def_pmax[0]/=6000.;
381 /* Va mL/dL -> mL/mL */ def_pmin[2]/=100.; def_pmax[2]/=100.;
382 if(verbose>1) {
383 fflush(stdout); printf("Parameter constraints:\n");
384 for(int pi=0; pi<parNr; pi++) {
385 printf("def_pmin[%d] := %g\n", pi+1, def_pmin[pi]);
386 printf("def_pmax[%d] := %g\n", pi+1, def_pmax[pi]);
387 }
388 fflush(stdout);
389 }
390
391 /*
392 * Read tissue and input data
393 */
394 if(verbose>1) printf("reading tissue and input data\n");
395 fitdur/=60.0; // sec -> min
396 /* Read blofile twice, the copy is used to store simulated TAC */
397 if(dftReadModelingData(ttacfile, btacfile, btacfile, NULL, &fitdur,
398 &fitframeNr, &ttac, &btac, stdout, verbose-2, tmp)!=0)
399 {
400 fprintf(stderr, "Error: %s\n", tmp);
401 return(2);
402 }
403 if(fitframeNr<4 || btac.frameNr<4) {
404 if(ttac.timeunit==TUNIT_UNKNOWN || btac.timeunit==TUNIT_UNKNOWN)
405 fprintf(stdout, "Error: check the time units in data files.\n");
406 else
407 fprintf(stderr, "Error: too few samples in specified fit duration.\n");
408 dftEmpty(&btac); dftEmpty(&ttac); return(3);
409 }
410 sim_index=btac.voiNr-1;
411 /* Prepare place for simulated tissue TAC and for bootstrap;
412 do this before changing frameNr */
413 if(dftAddmem(&ttac, 2)) {
414 fprintf(stderr, "Error: cannot setup memory for simulation.\n");
415 dftEmpty(&btac); dftEmpty(&ttac); return(3);
416 }
417 simpet_index=ttac.voiNr; bs_index=ttac.voiNr+1;
418 strcpy(ttac.voi[simpet_index].voiname, "Sim");
419 strcpy(ttac.voi[simpet_index].name, "Sim");
420 strcpy(ttac.voi[bs_index].voiname, "BS");
421 strcpy(ttac.voi[bs_index].name, "BS");
422 /* Times should be in minutes, change them to sec */
423 if(ttac.timeunit==TUNIT_MIN) {
424 if(verbose>1) printf("converting data times to sec\n");
425 dftMin2sec(&ttac); times_changed=1; dftMin2sec(&btac);
426 fitdur*=60.; // min -> sec
427 } else {
428 if(verbose>0) {
429 printf("BTAC timeunit := %s\n", petTunit(btac.timeunit));
430 printf("TTAC timeunit := %s\n", petTunit(ttac.timeunit));
431 }
432 if(verbose>1) printf("assuming that data times are in min.\n");
433 ttac.timeunit=btac.timeunit=TUNIT_MIN;
434 dftMin2sec(&ttac); times_changed=1; dftMin2sec(&btac);
435 fitdur*=60.; // min -> sec
436 }
437 /* Check that there is not any significant delay in the beginning of data */
438 if(ttac.timetype==DFT_TIME_STARTEND) {
439 if(ttac.x1[0]>27.0 && ttac.voi[0].y[0]>0.0) {
440 fprintf(stderr, "Error: TACs must start at time zero.\n");
441 dftEmpty(&btac); dftEmpty(&ttac); return(4);
442 }
443 if(ttac.x1[0]>5.0 && ttac.voi[0].y[0]>0.0) {
444 fprintf(stderr, "Warning: TACs should start at time zero.\n");
445 }
446 }
447 if(btac.x[0]>90.0 && ttac.voi[0].y[0]>0.0) {
448 fprintf(stderr, "Error: input TAC must start at time zero.\n");
449 dftEmpty(&btac); dftEmpty(&ttac); return(4);
450 }
451 if(btac.x[0]>20.0 && ttac.voi[0].y[0]>0.0) {
452 fprintf(stderr, "Warning: input TAC should start at time zero.\n");
453 }
454 /* Check weighting */
455 if(ttac.isweight==0 && verbose>0)
456 fprintf(stderr, "Note: data is not weighted.\n");
457 /* Print the weights */
458 if(verbose>1) {
459 int fi=0;
460 fprintf(stdout, "common_data_weights := %.3f", ttac.w[fi++]);
461 for(; fi<ttac.frameNr; fi++) fprintf(stdout, ", %.3f", ttac.w[fi]);
462 fprintf(stdout, "\n");
463 }
464
465
466
467 /*
468 * Prepare the room for results
469 */
470 if(verbose>1) printf("initializing result data\n");
471 if(res_allocate_with_dft(&res, &ttac)!=0) {
472 fprintf(stderr, "Error: cannot setup memory for results.\n");
473 dftEmpty(&btac); dftEmpty(&ttac); return(6);
474 }
475 /* Copy titles & filenames */
476 tpcProgramName(argv[0], 1, 1, res.program, 256);
477 strcpy(res.bloodfile, btacfile);
478 strcpy(res.datafile, ttacfile);
479 strcpy(res.fitmethod, "TGO");
480 /* Constants */
481 if(fVa>=0.0) res.Vb=fVa;
482 res.isweight=ttac.isweight;
483 /* Set data range */
484 sprintf(res.datarange, "%g - %g %s", 0.0, fitdur, petTunit(ttac.timeunit));
485 res.datanr=fitframeNr;
486 /* Set current time to results */
487 res.time=time(NULL);
488 /* Set parameter number, including also the extra "parameters"
489 and the parameter names and units */
490 res.parNr=parNr+1; // WSS
491 if(save_k2) res.parNr++;
492 {
493 int pi;
494 pi=0; strcpy(res.parname[pi], "Flow");
495 if(per_dl==0) strcpy(res.parunit[pi], "ml/(min*ml)");
496 else strcpy(res.parunit[pi], "ml/(min*dl)");
497 pi++; strcpy(res.parname[pi], "pWater"); strcpy(res.parunit[pi], "");
498 pi++; strcpy(res.parname[pi], "Va");
499 if(per_dl==0) strcpy(res.parunit[pi], "ml/ml");
500 else strcpy(res.parunit[pi], "%");
501 pi++; strcpy(res.parname[pi], "delayT"); strcpy(res.parunit[pi], "sec");
502 if(save_k2) {pi++; strcpy(res.parname[pi], "k2"); strcpy(res.parunit[pi], "1/min");}
503 pi++; strcpy(res.parname[pi], "WSS"); strcpy(res.parunit[pi], "");
504 }
505
506
507 /*
508 * Fit regional TACs
509 */
510 if(verbose>1) printf("starting regional fitting\n");
511 int tgoNr=0, neighNr=5, iterNr=0;
512 double *sd, *cl1, *cl2;
513 if(verbose>4) {
514 if(per_dl)
515 fprintf(stdout, "%-20s %-6s %-6s %-6s %-6s %-9s\n",
516 "Region", "Flow", "pWater", "Va(%)", "delayT", "WSS");
517 else
518 fprintf(stdout, "%-20s %-6s %-6s %-6s %-6s %-9s\n",
519 "Region", "Flow", "pWater", "Va", "delayT", "WSS");
520 }
521 /* Calculate decay correction factors for removing decay correction from
522 simulated tissue TAC (at input sample times) */
523 simdc=btac.voi[sim_index].y3;
524 for(int fi=0; fi<btac.frameNr; fi++)
525 simdc[fi]=hlLambda2factor(-lambda, btac.x[fi], -1.0);
526 /* Calculate decay correction factors for making decay correction for
527 simulated tissue TAC (at PET sample times) */
528 petdc=ttac.voi[simpet_index].y;
529 for(int fi=0; fi<ttac.frameNr; fi++) {
531 petdc[fi]=hlLambda2factor(lambda, ttac.x1[fi], ttac.x2[fi]-ttac.x1[fi]);
532 else
533 petdc[fi]=hlLambda2factor(lambda, ttac.x[fi], -1.0);
534 }
535 /* One region at a time */
536 if(verbose>0) {
537 fprintf(stdout, "fitting regional TACs: ");
538 if(verbose>1) fprintf(stdout, "\n");
539 fflush(stdout);
540 }
541 for(int ri=0; ri<ttac.voiNr; ri++) {
542 int pi, ret;
543 if(verbose>2) fprintf(stdout, "\n%s [%d] :\n", ttac.voi[ri].name, ri+1);
544
545 /* Set data pointers */
546 ca=btac.voi[0].y;
547 cs=btac.voi[sim_index].y; cs2=btac.voi[sim_index].y2;
548 weight=ttac.w;
549 cpet=ttac.voi[ri].y; csim=ttac.voi[ri].y2; wres=ttac.voi[ri].y3;
550
551 /* Set parameter constraints */
552 for(pi=0; pi<parNr; pi++) {
553 pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];
554 }
555 for(pi=fittedparNr=0; pi<parNr; pi++) if(pmax[pi]>pmin[pi]) fittedparNr++;
556 if(verbose>6) {
557 printf(" constraints :=");
558 for(pi=0; pi<parNr; pi++) printf(" [%g,%g]", pmin[pi], pmax[pi]);
559 printf("\n");
560 printf("fittedparNr := %d\n", fittedparNr);
561 }
562
563 /* Fit */
564 if(verbose>2) printf(" fitting\n");
567 //tgoNr=0; neighNr=5;
568 tgoNr=50+25*fittedparNr; /* 0, 100, 50+25 */
569 neighNr=6*fittedparNr; /* 6, 10 */
570 iterNr=0;
571 ret=tgo(
572 pmin, pmax, waterFunc, NULL, parNr, neighNr,
573 &wss, res.voi[ri].parameter, tgoNr, iterNr,
574 verbose-8);
575 if(ret>0) {
576 fprintf(stderr, "\nError in optimization (%d).\n", ret);
577 dftEmpty(&btac); dftEmpty(&ttac); resEmpty(&res); return(8);
578 }
579 /* Correct fitted parameters to match constraints like inside function */
580 (void)modelCheckParameters(parNr, pmin, pmax, res.voi[ri].parameter,
581 res.voi[ri].parameter, NULL);
582 wss=wss_wo_penalty;
583 if(verbose>3) {
584 for(pi=0; pi<MAX_PARAMS; pi++) printf(" %g", res.voi[ri].parameter[pi]);
585 printf(" -> WSS=%g\n", wss); fflush(stdout);
586 }
587 res.voi[ri].parameter[res.parNr-1]=wss;
588
589 /* Bootstrap */
590 if(doBootstrap) {
591 if(verbose>2) printf("\n bootstrapping\n");
592 /* bootstrap changes measured and simulated data, therefore use copies */
593 cpet=ttac.voi[bs_index].y;
594 csim=ttac.voi[bs_index].y2;
595 wres=ttac.voi[bs_index].y3;
596 if(doSD) sd=res.voi[ri].sd; else sd=NULL;
597 if(doCL) {cl1=res.voi[ri].cl1; cl2=res.voi[ri].cl2;} else cl1=cl2=NULL;
598 ret=bootstrap(
599 0, cl1, cl2, sd, res.voi[ri].parameter, pmin, pmax, fitframeNr,
600 // measured original TAC, not modified
601 ttac.voi[ri].y,
602 // fitted TAC, not modified
603 ttac.voi[ri].y2,
604 // tissue TAC noisy data is written to be used by objf
605 cpet,
606 parNr, ttac.w, waterFunc, tmp, verbose-5
607 );
608 if(ret) {
609 fprintf(stderr, "\nError in bootstrap: %s\n", tmp);
610 for(pi=0; pi<parNr; pi++) {
611 if(doSD) sd[pi]=nan("");
612 if(doCL) cl1[pi]=cl2[pi]=nan("");
613 }
614 }
615 // return pointers back to what they were, in case somebody adds some
616 // code later after this
617 cpet=ttac.voi[ri].y; csim=ttac.voi[ri].y2;
618 }
619
620 /* Set fitted parameters to understandable units */
621 {
622 double F, pWater, Va, delayT, f=60.;
623 /* k2, if requested */
624 if(save_k2)
625 res.voi[ri].parameter[4]=60.0*res.voi[ri].parameter[0]/res.voi[ri].parameter[1];
626 /* flow */
627 if(flow_per_tissue==0) f*=(1.0-res.voi[ri].parameter[2]);
628 res.voi[ri].parameter[0]*=f;
629 if(!isnan(res.voi[ri].cl1[0])) res.voi[ri].cl1[0]*=f;
630 if(!isnan(res.voi[ri].cl2[0])) res.voi[ri].cl2[0]*=f;
631 if(!isnan(res.voi[ri].sd[0])) res.voi[ri].sd[0]*=f;
632 if(per_dl) {
633 res.voi[ri].parameter[0]*=100.;
634 if(!isnan(res.voi[ri].cl1[0])) res.voi[ri].cl1[0]*=100.;
635 if(!isnan(res.voi[ri].cl2[0])) res.voi[ri].cl2[0]*=100.;
636 if(!isnan(res.voi[ri].sd[0])) res.voi[ri].sd[0]*=100.;
637 }
638 F=res.voi[ri].parameter[0]; // for printing
639 /* pWater as such */
640 pWater=res.voi[ri].parameter[1];
641 /* Va */
642 if(per_dl) {
643 res.voi[ri].parameter[2]*=100.;
644 if(!isnan(res.voi[ri].cl1[2])) res.voi[ri].cl1[2]*=100.;
645 if(!isnan(res.voi[ri].cl2[2])) res.voi[ri].cl2[2]*=100.;
646 if(!isnan(res.voi[ri].sd[2])) res.voi[ri].sd[2]*=100.;
647 }
648 Va=res.voi[ri].parameter[2];
649 /* time delay as such */
650 delayT=res.voi[ri].parameter[3];
651 /* Print fit results */
652 if(verbose>4)
653 fprintf(stdout, "%-20s %6.2f %6.3f %6.2f %6.2f %8.2e\n",
654 ttac.voi[ri].name, F, pWater, Va, delayT, wss);
655 }
656
657 /* done with this region */
658 if(ttac.voiNr>2 && verbose==1) {fprintf(stdout, "."); fflush(stdout);}
659
660 } /* Next region */
661 if(verbose>0) {fprintf(stdout, "\n"); fflush(stdout);}
662
663 /*
664 * Print results on screen
665 */
666 if(verbose>0) {resPrint(&res); fprintf(stdout, "\n");}
667
668
669 /*
670 * Save results
671 */
672 if(verbose>1) printf("saving results\n");
673 if(resWrite(&res, resfile, verbose-3)!=0) {
674 fprintf(stderr, "Error in writing '%s': %s\n", resfile, reserrmsg);
675 dftEmpty(&btac); dftEmpty(&ttac); resEmpty(&res);
676 return(11);
677 }
678 if(verbose>1) fprintf(stdout, "Model parameters written in %s\n", resfile);
679
680
681 /*
682 * Saving and/or plotting of fitted TACs
683 */
684 if(svgfile[0] || fitfile[0]) {
685
686 if(times_changed) dftSec2min(&ttac);
687
688 /* Create a DFT containing fitted TACs */
689 int ri, fi, ret;
690 char tmp[64];
691 DFT dft2;
692 dftInit(&dft2); ret=dftdup(&ttac, &dft2);
693 if(ret) {
694 fprintf(stderr, "Error: cannot save fitted curves.\n");
695 dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
696 return(21);
697 }
698 for(ri=0; ri<ttac.voiNr; ri++) for(fi=0; fi<fitframeNr; fi++)
699 dft2.voi[ri].y[fi]=dft2.voi[ri].y2[fi];
700 dft2.frameNr=fitframeNr;
701
702 /* Save SVG plot of fitted and original data */
703 if(svgfile[0]) {
704 if(verbose>1) printf("saving SVG plot\n");
705 sprintf(tmp, "Radiowater fit ");
706 if(strlen(res.studynr)>0) strcat(tmp, res.studynr);
707 ret=plot_fitrange_svg(&ttac, &dft2, tmp, 0.0, 1.02*dft2.x[fitframeNr-1],
708 0.0, nan(""), svgfile, verbose-8);
709 if(ret) {
710 fprintf(stderr, "Error (%d) in writing '%s'.\n", ret, svgfile);
711 dftEmpty(&dft2); dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
712 return(30+ret);
713 }
714 if(verbose>0) printf("Plots written in %s\n", svgfile);
715 }
716
717 /* Save fitted TACs */
718 if(fitfile[0]) {
719 if(verbose>1) printf("saving fitted curves\n");
720 tpcProgramName(argv[0], 1, 0, tmp, 64);
721 sprintf(dft2.comments, "# program := %s\n", tmp);
722 if(dftWrite(&dft2, fitfile)) {
723 fprintf(stderr, "Error in writing '%s': %s\n", fitfile, dfterrmsg);
724 dftEmpty(&dft2); dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
725 return(22);
726 }
727 if(verbose>0) printf("Fitted TACs written in %s\n", fitfile);
728 }
729
730 dftEmpty(&dft2);
731 }
732
733 /*
734 * Save residual curves, if required
735 */
736 if(residfile[0]) {
737 if(verbose>1) printf("saving weighted residuals\n");
738 int ri, fi;
739 char tmp[64];
740 for(ri=0; ri<ttac.voiNr; ri++) for(fi=0; fi<fitframeNr; fi++)
741 ttac.voi[ri].y[fi]=ttac.voi[ri].y3[fi];
742 tpcProgramName(argv[0], 1, 0, tmp, 64);
743 sprintf(ttac.comments, "# Residual curves from %s\n", tmp);
744 ttac.frameNr=fitframeNr;
745 if(dftWrite(&ttac, residfile)) {
746 fprintf(stderr, "Error in writing '%s': %s\n", residfile, dfterrmsg);
747 dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
748 return(13);
749 }
750 if(verbose>0) fprintf(stdout, "Residual curves written in %s\n", residfile);
751 }
752
753
754 /*
755 * Calculate and save time delay corrected input curve, if required
756 */
757 if(inputfile[0]) {
758 int ri, fi;
759 double *darr, time_diff;
760 char tmp[64];
761 if(verbose>1) printf("saving time delay corrected input curve\n");
762 /* Calculate the median of delay times */
763 darr=(double*)calloc(res.voiNr, sizeof(double));
764 for(ri=0; ri<res.voiNr; ri++) {
765 darr[ri]=res.voi[ri].parameter[3];
766 if(verbose>4) printf("darr[%d]=%g\n", ri, darr[ri]);
767 }
768 time_diff=dmedian(darr, res.voiNr);
769 free(darr);
770 if(verbose>1) printf("time_diff := %g\n", time_diff);
771 /* Change the sample times */
772 for(fi=0; fi<btac.frameNr; fi++) {
773 btac.x[fi]+=time_diff;
774 btac.x1[fi]+=time_diff; btac.x2[fi]+=time_diff;
775 }
776 /* 'remove' extra columns */
777 btac.voiNr=1;
778 /* Replace old comments, because those may contain wrong units */
779 dftSetComments(&btac);
780 sprintf(tmp, "# time_delay := %g [s]\n", time_diff);
781 strcat(btac.comments, tmp);
782 /* Write the file */
783 if(dftWrite(&btac, inputfile)) {
784 fprintf(stderr, "Error in writing '%s': %s\n", inputfile, dfterrmsg);
785 dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
786 return(15);
787 }
788 if(verbose>0) fprintf(stdout, "Input TAC written in %s\n", inputfile);
789 }
790
791
792 /* Free memory */
793 dftEmpty(&ttac); dftEmpty(&btac); resEmpty(&res);
794
795 return(0);
796}
797/*****************************************************************************/
798
799/*****************************************************************************/
800/*
801 * Function to be minimized
802 */
803double waterFunc(int parNr, double *p, void *fdata)
804{
805 int fi, ret;
806 double wss, d, k1, pWater, k2, Va, delayT;
807 double pa[MAX_PARAMETERS], penalty=1.0;
808
809
810 /* Check parameters against the constraints */
811 ret=modelCheckParameters(parNr, pmin, pmax, p, pa, &penalty);
812 if(fdata) {}
813
814 /* Get parameters */
815 k1=pa[0]; //k1/=(1.0-pa[2]);
816 pWater=pa[1]; Va=pa[2]; delayT=pa[3];
817 if(pWater>0.0) k2=k1/pWater; else k2=0.; //k2-=lambda;
818 //printf("f=%g pWater=%g Va=%g\n", 6000.*k1, pWater, 100.*Va);
819
820 /* Simulate the tissue curve (k1 and k2 per perfusable tissue) */
821 ret=simC3vs(btac.x, ca, ca, btac.frameNr, k1, k2, 0., 0., 0., 0.,
822 0.0, Va, 1.0,
823 cs, NULL, NULL, NULL, NULL, NULL);
824 if(ret) {
825 printf("error %d in simulation\n", ret);
826 return(nan(""));
827 }
828
829 /* Correct simulated tissue curve for delay */
830 if(fabs(delayT)>1.0E-06) {
831 double dx[btac.frameNr];
832 for(fi=0; fi<btac.frameNr; fi++) dx[fi]=btac.x[fi]+delayT;
833 ret=interpolate(dx, cs, btac.frameNr, btac.x,
834 cs2, NULL, NULL, btac.frameNr);
835 if(ret) {
836 printf("error %d in interpolation\n", ret);
837 return(nan(""));
838 }
839 for(fi=0; fi<btac.frameNr; fi++) cs[fi]=cs2[fi];
840 }
841 /* Remove decay correction */
842 for(fi=0; fi<btac.frameNr; fi++) cs[fi]*=simdc[fi];
843 /* Interpolate simulated TAC to PET frames */
845 ret=interpolate4pet(btac.x, cs, btac.frameNr, ttac.x1, ttac.x2,
846 csim, NULL, NULL, fitframeNr);
847 else
848 ret=interpolate(btac.x, cs, btac.frameNr, ttac.x,
849 csim, NULL, NULL, fitframeNr);
850 if(ret) {
851 printf("error %d in interpolation\n", ret);
852 return(nan(""));
853 }
854 /* Correct simulated PET curve for physical decay */
855 for(fi=0; fi<fitframeNr; fi++) csim[fi]*=petdc[fi];
856
857 /* Calculate wss */
858 for(fi=0, wss=0.; fi<fitframeNr; fi++) {
859 /* residual */
860 d=cpet[fi]-csim[fi];
861 /* error */
862 if(weight[fi]>0.0) wss+=weight[fi]*d*d;
863 /* weighted residual */
864 if(weight[fi]>0.0) wres[fi]=sqrt(weight[fi])*d; else wres[fi]=0.0;
865 }
866 wss_wo_penalty=wss;
867 wss*=penalty;
868
869 //printf("f=%g pWater=%g Va=%g\n", 6000.*k1, pWater, 100.*Va);
870
871 return(wss);
872}
873/*****************************************************************************/
874
875/*****************************************************************************/
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 dftAddmem(DFT *dft, int voiNr)
Definition dft.c:107
void dftSetComments(DFT *dft)
Definition dft.c:1326
void dftEmpty(DFT *data)
Definition dft.c:20
int dftReadModelingData(char *tissuefile, char *inputfile1, char *inputfile2, char *inputfile3, double *fitdur, int *fitframeNr, DFT *tis, DFT *inp, FILE *loginfo, int verbose, char *status)
Definition dftinput.c:342
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int res_allocate_with_dft(RES *res, DFT *dft)
Definition dftres.c:14
void dftSec2min(DFT *dft)
Definition dftunit.c:160
void dftMin2sec(DFT *dft)
Definition dftunit.c:145
double hl2lambda(double halflife)
Definition halflife.c:84
double hlLambda2factor(double lambda, double frametime, double framedur)
Definition halflife.c:98
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
int interpolate(double *x, double *y, int nr, double *newx, double *newy, double *newyi, double *newyii, int newnr)
Linear interpolation and integration.
Definition integr.c:28
int interpolate4pet(double *x, double *y, int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, int newnr)
Interpolate and integrate TAC to PET frames.
Definition integr.c:510
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
#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
#define HL_O15
Definition libtpcmisc.h:43
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 simC3vs(double *t, double *ca, double *cb, int nr, double k1, double k2, double k3, double k4, double k5, double k6, double f, double vb, double fa, double *cpet, double *cta, double *ctb, double *ctc, double *ctab, double *ctvb)
Definition simulate.c:243
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 dmedian(double *data, int n)
Definition median.c:48
Header file for libtpcmodext.
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
double * w
double * x1
char comments[_DFT_COMMENT_LEN+1]
int voiNr
double * x2
int frameNr
int isweight
double * x
const char * status
Definition libtpcmisc.h:277
char studynr[MAX_STUDYNR_LEN+1]
int parNr
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
int voiNr
int datanr
ResVOI * voi
double Vb
char program[1024]
char datarange[128]
int isweight
char datafile[FILENAME_MAX]
char fitmethod[128]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char bloodfile[FILENAME_MAX]
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]
double * y
char name[MAX_REGIONNAME_LEN+1]
double * y3