TPCCLIB
Loading...
Searching...
No Matches
fitmbf.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <math.h>
14#include <unistd.h>
15#include <string.h>
16#include <time.h>
17/*****************************************************************************/
18#include "libtpcmisc.h"
19#include "libtpcmodel.h"
20#include "libtpccurveio.h"
21#include "libtpcsvg.h"
22#include "libtpcmodext.h"
23/*****************************************************************************/
24
25/*****************************************************************************/
26int parNr=3;
27DFT input;
28double *petmeas, *petsim, *weight;
29double pc=0.9464, Beta=0.91;
30double pmin[MAX_PARAMETERS], pmax[MAX_PARAMETERS];
31int fitframeNr;
32double wss_wo_penalty=0.0;
33/*****************************************************************************/
35enum parameter {
36 CM_FLOW, CM_PTF, CM_VA, CM_RMBF, CM_WSS
37};
38/*****************************************************************************/
39/* Local functions */
40double mbfFunc(int parNr, double *p, void*);
41double mbfFunc2(int parNr, double *p, void*);
42/*****************************************************************************/
43
44/*****************************************************************************/
45static char *info[] = {
46 "Non-linear fitting of Iida's MBF model (1, 2) as represented in (3) to",
47 "regional dynamic PET [O-15]H2O study data.",
48 "The model parameters are myocardial blood flow in perfusable tissue (ptMBF),",
49 "perfusable tissue fraction (PTF), and arterial blood volume and spillover",
50 "(Va); in addition, mean blood flow in the myocardial region (rMBF), and",
51 "weighted sum-of-squares (WSS) are reported.",
52 " ",
53 "The same method is applied in Carimas, and for clinical work use of Carimas",
54 "is recommended; however, it is possible to save regional TACs in Carimas or",
55 "other software and use those with this program.",
56 " ",
57 "User must provide the regional TAC file (tacfile, in DFT or PMOD format),",
58 "and the names or numbers of LV cavity (lvcav) and whole myocardial (myoc)",
59 "TAC inside the TAC file, and filename for the results.",
60 "LV cavity and whole myocardial ROI TACs are used to estimate a spill-in",
61 "corrected arterial blood TAC, which is then used as model input for",
62 "the smaller myocardial regions; to omit this step and use the LV cavity TAC",
63 "directly as input, enter 'none' in place of the myocardial ROI name.",
64 " ",
65 "Usage: @P [Options] tacfile lvcav myoc resultfile",
66 " ",
67 "Options:",
68 " -lim[=<filename>]",
69 " Specify the constraints for model parameters;",
70 " This file with default values can be created by giving this",
71 " option as the only command-line argument to this program.",
72 " Without filename the default values are printed on screen.",
73 " Parameter can be fixed to a certain value by setting its",
74 " lower and upper limit to that value.",
75 " -beta=<Beta value>",
76 " Enter the Beta value (from [O-15]CO study); by default 0.91.",
77 " -pH2O=<Partition coefficient for water>",
78 " Enter the partition coefficient of water; 0.9464 by default.",
79 " -end=<Fit end time (sec)>",
80 " By default line is fitted to the end of data. Use this option to enter",
81 " the fit end time.",
82 " -SD[=<y|N>]",
83 " Standard deviations are calculated and saved in results (Y, default),",
84 " or not calculated (n).",
85 " Program runs a lot faster if SD and CL are not calculated.",
86 " -CL[=<y|N>]",
87 " 95% Confidence limits are calculated and saved in results (y), or",
88 " not calculated (N, default).",
89 " -input=<Filename>",
90 " Save arterial concentration curves, estimated from LV cavity and whole",
91 " myocardial TACs, into specified TAC file.",
92 " -fit=<Filename>",
93 " Fitted regional TACs are written in DFT format.",
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 " -svg=<Filename>",
98 " Fitted and measured TACs are plotted in specified SVG file.",
99 " -stdoptions", // List standard options like --help, -v, etc
100 " ",
101 "Example:",
102 " @P -beta=0.91 s2345.tac 'lv Pl06' 'whole' s2345mbf.res",
103 " ",
104 "References:",
105 "1. Iida H, Rhodes CG, de Silva R, Yamamoto Y, Araujo LI, Maseri A, Jones T.",
106 " Myocardial tissue fraction - correction for partial volume effects and",
107 " measure of tissue viability. J Nucl Med 1991; 32:2169-2175.",
108 "2. Iida H, Rhodes CG, de Silva R, Araujo LI, Bloomfield P, Lammertsma AA,",
109 " Jones T. Use of the left ventricular time-activity curve as a noninvasive",
110 " input function in dynamic oxygen-15-water positron emission tomography.",
111 " J Nucl Med 1992; 33:1669-1677.",
112 "3. Oikonen V. Model equations for myocardial perfusion studies with [15O]H2O",
113 " PET. https://www.turkupetcentre.net/reports/tpcmod0005.pdf",
114 " ",
115 "See also: sim_mbf, b2t_h2o, simimyoc, fit_h2o, tacweigh, rescoll",
116 " ",
117 "Keywords: TAC, modelling, myocardium, perfusion, radiowater, 1TCM",
118 0};
119/*****************************************************************************/
120
121/*****************************************************************************/
122/* Turn on the globbing of the command line, since it is disabled by default in
123 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
124 In Unix&Linux wildcard command line processing is enabled by default. */
125/*
126#undef _CRT_glob
127#define _CRT_glob -1
128*/
129int _dowildcard = -1;
130/*****************************************************************************/
131
132/*****************************************************************************/
136int main(int argc, char **argv)
137{
138 int ai, help=0, version=0, verbose=1;
139 double def_pmin[MAX_PARAMETERS], def_pmax[MAX_PARAMETERS];
140 char tacfile[FILENAME_MAX], resfile[FILENAME_MAX],
141 fitfile[FILENAME_MAX], svgfile[FILENAME_MAX],
142 lvcavname[FILENAME_MAX], myocname[FILENAME_MAX],
143 limfile[FILENAME_MAX], inputfile[FILENAME_MAX];
144 int doBootstrap=0, doSD=0, doCL=0; // 0=no, 1=yes
145 double fittime=-1.0;
146 int fittedparNr=0;
147 int ret, originallyMinutes=0;
148
149
150
151 /* Set parameter initial values and constraints */
152 /* ptMBF */ def_pmin[0]=0.00; def_pmax[0]=10.0; // mL/(mL*min)
153 /* PTF */ def_pmin[1]=0.05; def_pmax[1]=1.0; // mL/mL
154 /* Va */ def_pmin[2]=0.05; def_pmax[2]=0.99; // mL/mL
155
156 /*
157 * Get arguments
158 */
159 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
160 tacfile[0]=resfile[0]=limfile[0]=inputfile[0]=(char)0;
161 svgfile[0]=fitfile[0]=lvcavname[0]=myocname[0]=(char)0;
162 /* Get options first, because may affect what arguments are read */
163 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
164 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
165 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
166 if(strncasecmp(cptr, "CL", 2)==0) {
167 if(strlen(cptr)==2) {doCL=1; continue;}
168 cptr+=2; if(*cptr=='=') {
169 cptr++;
170 if(*cptr=='Y' || *cptr=='y') {doCL=1; continue;}
171 if(*cptr=='N' || *cptr=='n') {doCL=0; continue;}
172 }
173 } else if(strncasecmp(cptr, "SD", 2)==0) {
174 if(strlen(cptr)==2) {doSD=1; continue;}
175 cptr+=2; if(*cptr=='=') {
176 cptr++;
177 if(*cptr=='Y' || *cptr=='y') {doSD=1; continue;}
178 if(*cptr=='N' || *cptr=='n') {doSD=0; continue;}
179 }
180 } else if(strncasecmp(cptr, "LIM=", 4)==0 && strlen(cptr)>4) {
181 strlcpy(limfile, cptr+4, FILENAME_MAX); continue;
182 } else if(strcasecmp(cptr, "LIM")==0) {
183 strcpy(limfile, "stdout"); continue;
184 } else if(strncasecmp(cptr, "BETA=", 5)==0 && strlen(cptr)>5) {
185 if(!atof_with_check(cptr+5, &Beta) && Beta>0.0 && Beta<=1.0) continue;
186 } else if(strncasecmp(cptr, "PH2O=", 5)==0) {
187 if(!atof_with_check(cptr+5, &pc) && pc>0.0 && pc<=1.0) continue;
188 } if(strncasecmp(cptr, "INPUT=", 6)==0) {
189 strlcpy(inputfile, cptr+6, FILENAME_MAX);
190 if(strlen(inputfile)>0) continue;
191 } else if(strncasecmp(cptr, "SVG=", 4)==0) {
192 strlcpy(svgfile, cptr+4, FILENAME_MAX);
193 if(strlen(svgfile)>0) continue;
194 } else if(strncasecmp(cptr, "FIT=", 4)==0) {
195 strlcpy(fitfile, cptr+4, FILENAME_MAX);
196 if(strlen(fitfile)>0) continue;
197 } else if(strncasecmp(cptr, "END=", 4)==0) {
198 if(!atof_with_check(cptr+4, &fittime) && fittime>10.0) continue;
199 }
200 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
201 return(1);
202 } else break;
203
204 /* Print help or version? */
205 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
206 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
207 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
208
209 /* Process other arguments, starting from the first non-option */
210 if(ai<argc) {strlcpy(tacfile, argv[ai++], FILENAME_MAX);}
211 if(ai<argc) {strlcpy(lvcavname, argv[ai++], FILENAME_MAX);}
212 if(ai<argc) {
213 strlcpy(myocname, argv[ai++], FILENAME_MAX);
214 if(!strcasecmp(myocname, "NONE") || !strcasecmp(myocname, "'NONE'") ||
215 !strcasecmp(myocname, "NO") || !strcasecmp(myocname, "0"))
216 myocname[0]=(char)0;
217 }
218 if(ai<argc) {strlcpy(resfile, argv[ai++], FILENAME_MAX);}
219 if(ai<argc) {
220 /* we should never get this far */
221 fprintf(stderr, "Error: too many arguments: '%s'.\n", argv[ai]);
222 return(1);
223 }
224 if(doSD || doCL) doBootstrap=1; else doBootstrap=0;
225
226 /* If only filename for initial values was given, then write one
227 with default contents, and exit */
228 if(limfile[0] && !tacfile[0]) {
229 IFT ift; iftInit(&ift);
230 /* Check that initial value file does not exist */
231 if(strcasecmp(limfile, "stdout")!=0 && access(limfile, 0) != -1) {
232 fprintf(stderr, "Error: parameter constraint file %s exists.\n", limfile);
233 return(9);
234 }
235 if(verbose>1) printf("writing parameter constraints file\n");
236 /* Create parameter file */
237 iftPutDouble(&ift, "ptMBF_lower", def_pmin[0], NULL, 0);
238 iftPutDouble(&ift, "ptMBF_upper", def_pmax[0], NULL, 0);
239 iftPutDouble(&ift, "PTF_lower", def_pmin[1], NULL, 0);
240 iftPutDouble(&ift, "PTF_upper", def_pmax[1], NULL, 0);
241 iftPutDouble(&ift, "Va_lower", def_pmin[2], NULL, 0);
242 iftPutDouble(&ift, "Va_upper", def_pmax[2], NULL, 0);
243 if(iftWrite(&ift, limfile, 0)) {
244 fprintf(stderr, "Error in writing '%s': %s\n", limfile, ift.status);
245 iftEmpty(&ift); return(9);
246 }
247 if(strcasecmp(limfile, "stdout")!=0)
248 fprintf(stdout, "Parameter file %s with initial values written.\n", limfile);
249 iftEmpty(&ift); return(0);
250 }
251
252 /* In verbose mode print arguments and options */
253 if(verbose>1) {
254 printf("limfile := %s\n", limfile);
255 printf("tacfile := %s\n", tacfile);
256 printf("lvcavname := %s\n", lvcavname);
257 printf("myocname := %s\n", myocname);
258 printf("resfile := %s\n", resfile);
259 printf("fitfile := %s\n", fitfile);
260 printf("svgfile := %s\n", svgfile);
261 printf("inputfile := %s\n", inputfile);
262 printf("beta := %g\n", Beta);
263 printf("pH2O := %g\n", pc);
264 printf("doBootstrap := %d\n", doBootstrap);
265 printf("doSD := %d\n", doSD);
266 printf("doCL := %d\n", doCL);
267 if(fittime>0.0) printf("requested_fittime := %g\n", fittime);
268 fflush(stdout);
269 }
270
271 /* Did we get all the information that we need? */
272 if(!resfile[0]) {
273 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
274 return(1);
275 }
276
277
278 /*
279 * Read model parameter initial values and upper and lower limits
280 * if file for that was given
281 */
282 if(limfile[0]) {
283 IFT ift; iftInit(&ift);
284 double v;
285 if(verbose>1) printf("reading %s\n", limfile);
286 if(iftRead(&ift, limfile, 1, 0)) {
287 fprintf(stderr, "Error in reading '%s': %s\n", limfile, ift.status);
288 return(9);
289 }
290 if(verbose>10) iftWrite(&ift, "stdout", 0);
291 int n=0;
292 /* ptMBF */
293 if(iftGetDoubleValue(&ift, 0, "ptMBF_lower", &v, 0)>=0) {def_pmin[0]=v; n++;}
294 if(iftGetDoubleValue(&ift, 0, "ptMBF_upper", &v, 0)>=0) {def_pmax[0]=v; n++;}
295 if(iftGetDoubleValue(&ift, 0, "MBF_lower", &v, 0)>=0) {def_pmin[0]=v; n++;}
296 if(iftGetDoubleValue(&ift, 0, "MBF_upper", &v, 0)>=0) {def_pmax[0]=v; n++;}
297 /* PTF */
298 if(iftGetDoubleValue(&ift, 0, "PTF_lower", &v, 0)>=0) {def_pmin[1]=v; n++;}
299 if(iftGetDoubleValue(&ift, 0, "PTF_upper", &v, 0)>=0) {def_pmax[1]=v; n++;}
300 /* Va */
301 if(iftGetDoubleValue(&ift, 0, "Va_lower", &v, 0)>=0) {def_pmin[2]=v; n++;}
302 if(iftGetDoubleValue(&ift, 0, "Va_upper", &v, 0)>=0) {def_pmax[2]=v; n++;}
303 iftEmpty(&ift);
304 if(n==0) {fprintf(stderr, "Error: invalid parameter file.\n"); return(9);}
305 }
306 /* Check that these limits are ok */
307 {
308 int pi, ret=0;
309 fittedparNr=0;
310 for(pi=0; pi<parNr; 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]) fittedparNr++;
314 }
315 if(ret) {
316 fprintf(stderr, "Error: invalid parameter constraints.\n");
317 return(9);
318 }
319 if(fittedparNr==0) {
320 fprintf(stderr, "Error: no model parameters left free for fitting.\n");
321 return(9);
322 }
323 }
324 if(verbose>1) {
325 fflush(stdout); 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 printf("fittedParNr := %d\n", fittedparNr);
331 fflush(stdout);
332 }
333 /* Convert MBF constraints to per second */
334 def_pmin[0]/=60.0; def_pmax[0]/=60.0;
335
336
337 /*
338 * Read data file
339 */
340 if(verbose>1) printf("reading '%s'.\n", tacfile);
341 DFT dft; dftInit(&dft);
342 ret=dftRead(tacfile, &dft);
343 if(ret) {
344 fprintf(stderr, "Error in reading '%s': %s\n", tacfile, dfterrmsg);
345 if(verbose>1) printf(" ret :=%d\n", ret);
346 return(2);
347 }
348 /* Check for NaN's */
349 ret=dft_nr_of_NA(&dft); if(ret>0) {
350 fprintf(stderr, "Error: missing sample(s) in %s.\n", tacfile);
351 dftEmpty(&dft); return(2);
352 }
353 /* Sort the data by increasing sample times */
354 dftSortByFrame(&dft);
355 /* Guess time units, if necessary */
356 if(dft.timeunit==TUNIT_UNKNOWN) {
357 if(dft.x[dft.frameNr-1]>20.0) {
358 if(verbose>1) printf("Note: assuming that times are in seconds.\n");
359 dft.timeunit=TUNIT_SEC;
360 } else {
361 if(verbose>1) printf("Note: assuming that times are in minutes.\n");
362 dft.timeunit=TUNIT_MIN;
363 }
364 }
365 /* Set weights to one for data that did not contain weights */
366 if(dft.isweight==0) for(int i=0; i<dft.frameNr; i++) dft.w[i]=1.0;
367 if(verbose>3) {
368 fprintf(stdout, "common_data_weights := %g", dft.w[0]);
369 for(int i=1; i<dft.frameNr; i++) fprintf(stdout, ", %g", dft.w[i]);
370 fprintf(stdout, "\n");
371 }
372 /* Convert time to sec if necessary */
373 if(dft.timeunit==TUNIT_MIN) {
374 dftMin2sec(&dft); originallyMinutes=1;
375 }
376 /* Check region number */
377 if(dft.voiNr<2) {
378 fprintf(stderr, "Error: check the contents of datafile.\n");
379 dftEmpty(&dft); return(2);
380 }
381 /* Make sure that there is no overlap in frame times */
382 if(dft.timetype==DFT_TIME_STARTEND) {
383 if(dftDeleteFrameOverlap(&dft)) {
384 fprintf(stderr, "Error: file has overlapping frame times.\n");
385 dftEmpty(&dft); return(2);
386 }
387 }
388 /* Set fit duration and get the number of fitted sample points */
389 double starttime=0;
390 double endtime=1.0E+90; if(fittime>0.0) endtime=fittime/60.0;
391 int first, last;
392 fitframeNr=fittime_from_dft(&dft, &starttime, &endtime,
393 &first, &last, verbose-1);
394 if(verbose>2) {
395 printf("frameNr := %d\n", dft.frameNr);
396 printf("starttime := %g\n", starttime);
397 printf("endtime := %g\n", endtime);
398 printf("first := %d\n", first);
399 printf("last := %d\n", last);
400 printf("fitframeNr := %d\n", fitframeNr);
401 fflush(stdout);
402 }
403 fittime=60.0*endtime;
404 /* Check frame number */
405 if(dftValidNr(&dft, 0.0, fittime, -1)<4) {
406 fprintf(stderr, "Error: check the contents of datafile.\n");
407 dftEmpty(&dft); return(2);
408 }
409
410
411 /*
412 * Find myocardial TAC and set wmroi = its index
413 * Find LV TAC and set lvroi = its index
414 */
415 int wmroi=-1;
416 if(myocname[0]) {
417 if(verbose>1) printf("searching for (whole) myocardium ROI.\n");
418 int n, i;
419 n=dftSelectRegions(&dft, myocname, 1);
420 if(verbose>1) printf("nr of myoc regions := %d/%d\n", n, dft.voiNr);
421 if(n<=0) {
422 fprintf(stderr, "Error: cannot find myoc region.\n");
423 dftEmpty(&dft); return(2);
424 }
425 if(n==dft.voiNr) {
426 fprintf(stderr, "Error: all regions match myoc name.\n");
427 dftEmpty(&dft); return(2);
428 }
429 /* Try to select the best match */
430 i=dftSelectBestReference(&dft); if(i<0) {
431 fprintf(stderr, "Error: cannot select the best myoc region.\n");
432 dftEmpty(&dft); return(2);
433 }
434 wmroi=i;
435 }
436
437 int lvroi=-1;
438 {
439 if(verbose>1) printf("searching for LV cavity ROI.\n");
440 int n, i;
441 n=dftSelectRegions(&dft, lvcavname, 1);
442 if(verbose>1) printf("nr of lvcav regions := %d/%d\n", n, dft.voiNr);
443 if(n<=0) {
444 fprintf(stderr, "Error: cannot find lvcav region.\n");
445 dftEmpty(&dft); return(2);
446 }
447 if(n==dft.voiNr) {
448 fprintf(stderr, "Error: all regions match lvcav name.\n");
449 dftEmpty(&dft); return(2);
450 }
451 /* Try to select the best match */
452 i=dftSelectBestReference(&dft); if(i<0) {
453 fprintf(stderr, "Error: cannot select the best lvcav region.\n");
454 dftEmpty(&dft); return(2);
455 }
456 lvroi=i;
457 }
458
459 if(wmroi==lvroi) {
460 fprintf(stderr, "Error: cannot determine lvcav or myoc TAC.\n");
461 dftEmpty(&dft); return(2);
462 }
463 if(verbose>1) {
464 printf("selected lvcav region := %s\n", dft.voi[lvroi].name);
465 if(wmroi>=0) printf("selected myoc region := %s\n", dft.voi[wmroi].name);
466 }
467
468 /* Allocate an extra TAC for the bootstrap */
469 int bs_index=0;
470 if(doBootstrap) {
471 ret=dftAddmem(&dft, 1); if(ret) {
472 fprintf(stderr, "Error: cannot allocate more memory.\n");
473 dftEmpty(&dft); return(3);
474 }
475 bs_index=dft.voiNr;
476 strcpy(dft.voi[bs_index].voiname, "BS");
477 strcpy(dft.voi[bs_index].name, "BS");
478 }
479
480
481
482 /*
483 * Prepare the room for results
484 */
485 if(verbose>1) printf("initializing result data\n");
486 RES res; resInit(&res);
487 if(res_allocate_with_dft(&res, &dft)!=0) {
488 fprintf(stderr, "Error: cannot setup memory for results.\n");
489 dftEmpty(&dft); return(4);
490 }
491 /* Copy titles & filenames */
492 tpcProgramName(argv[0], 1, 1, res.program, 256);
493 strcpy(res.datafile, tacfile);
494 if(wmroi>=0) strlcpy(res.refroi, dft.voi[wmroi].name, 64);
495 strcpy(res.fitmethod, "TGO");
496 /* Constants */
497 res.beta=Beta; res.Vb=-1.0;
498 res.isweight=dft.isweight;
499 /* Set data range */
500 sprintf(res.datarange, "%g - %g min", starttime, endtime);
501 res.datanr=fitframeNr;
502 /* Set current time to results */
503 res.time=time(NULL);
504
505
506 /* Set parameter number, including also the extra "parameters"
507 and the parameter names and units */
508 res.parNr=5;
509 {
510 int pi;
511 pi=0; strcpy(res.parname[pi], "ptMBF");
512 strcpy(res.parunit[pi], "mL/(min*mL)");
513 pi++; strcpy(res.parname[pi], "PTF"); strcpy(res.parunit[pi], "mL/mL");
514 pi++; strcpy(res.parname[pi], "Va");
515 strcpy(res.parunit[pi], "mL/mL");
516 pi++; strcpy(res.parname[pi], "rMBF");
517 strcpy(res.parunit[pi], "mL/(min*mL)");
518 pi++; strcpy(res.parname[pi], "WSS"); strcpy(res.parunit[pi], "");
519 }
520
521 /*
522 * Allocate memory for input curve (arterial blood concentration)
523 */
524 dftInit(&input);
525 ret=dftSetmem(&input, dft.frameNr, 1);
526 if(ret) {
527 fprintf(stderr, "Error: cannot allocate memory for input TAC.\n");
528 dftEmpty(&dft); resEmpty(&res); return(4);
529 }
530 input.voiNr=1; input.frameNr=dft.frameNr;
531 (void)dftCopymainhdr(&dft, &input);
532 (void)dftCopyvoihdr(&dft, lvroi, &input, 0);
533 for(int i=0; i<input.frameNr; i++) {
534 input.x[i]=dft.x[i]; input.x1[i]=dft.x1[i]; input.x2[i]=dft.x2[i];
535 }
536
537 /*
538 * Allocate memory for fitted curves
539 */
540 DFT fit; dftInit(&fit);
541 ret=dftdup(&dft, &fit);
542 if(ret) {
543 fprintf(stderr, "Error: cannot allocate memory for fitted curves.\n");
544 if(verbose>1) printf(" ret :=%d\n", ret);
545 dftEmpty(&dft); dftEmpty(&input); resEmpty(&res);
546 return(4);
547 }
548
549
550 /*
551 * Determine the arterial input TAC, either by directly using LV cavity,
552 * or by using whole myocardial TAC and MBF model
553 */
554 for(int i=0; i<input.frameNr; i++) {
555 input.voi[0].y[i]=dft.voi[lvroi].y[i];
556 }
557 if(wmroi<0) {
558 if(verbose>1) printf("Note: using LV cavity directly as the input.\n");
559 } else {
560 if(verbose>1) printf("starting myoc fitting\n");
561 int tgoNr, neighNr, iterNr;
562 double *sd, *cl1, *cl2;
563 double wss;
564
565 /* Set parameter constraints */
566 for(int pi=0; pi<parNr; pi++) {
567 pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];
568 }
569
570 /* set data pointers */
571 petmeas=dft.voi[wmroi].y; petsim=fit.voi[wmroi].y;
572 weight=dft.w;
573
574 /* Fit */
575 if(verbose>2) printf(" fitting\n");
578 tgoNr=200;
579 neighNr=20;
580 iterNr=0;
581 ret=tgo(
582 pmin, pmax, mbfFunc, NULL, parNr, neighNr,
583 &wss, res.voi[wmroi].parameter, tgoNr, iterNr,
584 verbose-8);
585 if(ret>0) {
586 fprintf(stderr, "\nError in optimization (%d).\n", ret);
587 dftEmpty(&dft); dftEmpty(&input); dftEmpty(&fit); resEmpty(&res);
588 return(5);
589 }
590 /* Correct fitted parameters to match constraints like inside function */
591 (void)modelCheckParameters(parNr, pmin, pmax, res.voi[wmroi].parameter,
592 res.voi[wmroi].parameter, NULL);
593 wss=wss_wo_penalty;
594 res.voi[wmroi].parameter[res.parNr-1]=wss;
595
596 /* Print measured and fitted muscle TAC */
597 if(verbose>5) {
598 printf(" Measured Fitted Weight:\n");
599 for(int fi=0; fi<fitframeNr; fi++)
600 printf(" %2d %8.2e %8.2e %8.2e\n", fi+1, petmeas[fi], petsim[fi],
601 weight[fi]);
602 }
603
604 /* Bootstrap */
605 if(doBootstrap) {
606 if(verbose>1) printf(" bootstrapping\n");
607 char buf[64];
608 /* bootstrap needs and changes petmeas[] and petsim[], */
609 /* therefore reset these pointers */
610 petmeas=dft.voi[bs_index].y;
611 petsim=dft.voi[bs_index].y2;
612 /* set pointer for SD and CL arrays */
613 if(doSD) sd=res.voi[wmroi].sd; else sd=NULL;
614 if(doCL) {cl1=res.voi[wmroi].cl1; cl2=res.voi[wmroi].cl2;} else cl1=cl2=NULL;
615 ret=bootstrap(0, cl1, cl2, sd, res.voi[wmroi].parameter, pmin, pmax,
616 fitframeNr, dft.voi[wmroi].y, fit.voi[wmroi].y, petmeas,
617 parNr, dft.w, mbfFunc, buf, verbose-6);
618 if(ret) {
619 fprintf(stderr, "\nError in bootstrap: %s\n", buf);
620 for(int pi=0; pi<parNr; pi++) {
621 if(doSD) sd[pi]=nan("");
622 if(doCL) cl1[pi]=cl2[pi]=nan("");
623 }
624 }
625 // return data pointers back to what they were
626 petmeas=dft.voi[wmroi].y; petsim=fit.voi[wmroi].y;
627 }
628
629 /*
630 * Calculate arterial blood curve to be used as input with other regions
631 */
632 {
633 double alpha, Va;
634 alpha=res.voi[wmroi].parameter[1];
635 Va=res.voi[wmroi].parameter[2];
636 for(int fi=0; fi<dft.frameNr; fi++) {
637 /*
638 input.voi[0].y[fi]=
639 ((1.0-Beta)*petmeas[fi]-alpha*input[fi])/(Va*(1.0-Beta)-alpha*Beta);
640 */
641 /* This is the way the input was calculated in fitmbf 2.0 */
642 input.voi[0].y[fi]=
643 ((1.0-Beta)*petsim[fi]-alpha*dft.voi[lvroi].y[fi])
644 / (Va*(1.0-Beta)-alpha*Beta);
645 }
646 }
647 }
648
649
650 /*
651 * Fit myocardial regions with arterial blood input
652 * originating from fit of whole myocardial ROI with LV input
653 */
654
655 /* One region at a time */
656 for(int ri=0; ri<dft.voiNr; ri++) {
657
658 /* Do not fit LV TAC */
659 if(ri==lvroi) continue;
660 /* Do not fit whole myocardium again */
661 if(ri==wmroi) continue;
662
663 if(verbose>1) printf("starting %s fitting\n", dft.voi[ri].name);
664 int tgoNr, neighNr, iterNr;
665 double *sd, *cl1, *cl2;
666 double wss;
667
668 /* Set parameter constraints */
669 for(int pi=0; pi<parNr; pi++) {
670 pmin[pi]=def_pmin[pi]; pmax[pi]=def_pmax[pi];
671 }
672
673 /* set data pointers */
674 petmeas=dft.voi[ri].y; petsim=fit.voi[ri].y;
675 weight=dft.w;
676
677 /* Fit */
678 if(verbose>2) printf(" fitting\n");
681 tgoNr=200;
682 neighNr=20;
683 iterNr=0;
684 ret=tgo(
685 pmin, pmax, mbfFunc2, NULL, parNr, neighNr,
686 &wss, res.voi[ri].parameter, tgoNr, iterNr,
687 verbose-8);
688 if(ret>0) {
689 fprintf(stderr, "\nError in optimization (%d).\n", ret);
690 dftEmpty(&dft); dftEmpty(&input); dftEmpty(&fit); resEmpty(&res);
691 return(5);
692 }
693 /* Correct fitted parameters to match constraints like inside function */
694 (void)modelCheckParameters(parNr, pmin, pmax, res.voi[ri].parameter,
695 res.voi[ri].parameter, NULL);
696 wss=wss_wo_penalty;
697 res.voi[ri].parameter[res.parNr-1]=wss;
698
699 /* Print measured and fitted muscle TAC */
700 if(verbose>5) {
701 printf(" Measured Fitted Weight:\n");
702 for(int fi=0; fi<fitframeNr; fi++)
703 printf(" %2d %8.2e %8.2e %8.2e\n", fi+1, petmeas[fi], petsim[fi],
704 weight[fi]);
705 }
706
707 /* Bootstrap */
708 if(doBootstrap) {
709 if(verbose>1) printf(" bootstrapping\n");
710 char buf[64];
711 /* bootstrap needs and changes petmeas[] and petsim[], */
712 /* therefore reset these pointers */
713 petmeas=dft.voi[bs_index].y;
714 petsim=dft.voi[bs_index].y2;
715 /* set pointer for SD and CL arrays */
716 if(doSD) sd=res.voi[ri].sd; else sd=NULL;
717 if(doCL) {cl1=res.voi[ri].cl1; cl2=res.voi[ri].cl2;} else cl1=cl2=NULL;
718 ret=bootstrap(0, cl1, cl2, sd, res.voi[ri].parameter, pmin, pmax,
719 fitframeNr, dft.voi[ri].y, fit.voi[ri].y, petmeas,
720 parNr, dft.w, mbfFunc2, buf, verbose-6);
721 if(ret) {
722 fprintf(stderr, "\nError in bootstrap: %s\n", buf);
723 for(int pi=0; pi<parNr; pi++) {
724 if(doSD) sd[pi]=nan("");
725 if(doCL) cl1[pi]=cl2[pi]=nan("");
726 }
727 }
728 // return data pointers back to what they were
729 petmeas=dft.voi[ri].y; petsim=fit.voi[ri].y;
730 }
731
732 } // next TAC
733
734
735
736
737 /* Delete LV region from results */
738 (void)resDelete(&res, lvroi);
739
740 /* Convert MBF estimates from 1/sec to 1/min values */
741 for(int ri=0; ri<res.voiNr; ri++) {
742 res.voi[ri].parameter[0]*=60.;
743 res.voi[ri].sd[0]*=60.;
744 res.voi[ri].cl1[0]*=60.;
745 res.voi[ri].cl2[0]*=60.;
746 }
747 /* Calculate rMBF from ptMBF and PTF */
748 for(int ri=0; ri<res.voiNr; ri++) {
749 res.voi[ri].parameter[3]=res.voi[ri].parameter[0]*res.voi[ri].parameter[1];
750 }
751
752
753
754 /*
755 * Print results on screen
756 */
757 if(verbose>0) {resPrint(&res); fprintf(stdout, "\n");}
758
759
760 /*
761 * Save results
762 */
763 if(verbose>1) printf("saving results\n");
764 if(resWrite(&res, resfile, verbose-3)!=0) {
765 fprintf(stderr, "Error in writing '%s': %s\n", resfile, reserrmsg);
766 dftEmpty(&dft); dftEmpty(&input); dftEmpty(&fit); resEmpty(&res);
767 return(11);
768 }
769 if(verbose>1) fprintf(stdout, "Model parameters written in %s\n", resfile);
770
771
772
773 /*
774 * Convert TAC time units back to minutes, if necessary
775 */
776 if(originallyMinutes) {
777 dftSec2min(&dft); dftSec2min(&input); dftSec2min(&fit);
778 }
779
780
781 /*
782 * Saving and/or plotting of fitted TACs
783 */
784 if(svgfile[0] || fitfile[0]) {
785
786 /* Save fitted TACs */
787 if(fitfile[0]) {
788 if(verbose>1) printf("saving fitted curves\n");
789 if(dftWrite(&fit, fitfile)) {
790 fprintf(stderr, "Error in writing '%s': %s\n", fitfile, dfterrmsg);
791 } else if(verbose>0) printf("fitted TACs written in %s\n", fitfile);
792 }
793
794 /* Save SVG plot of fitted and original data */
795 if(svgfile[0]) {
796 if(verbose>1) printf("saving SVG plot\n");
797 char tmp[64];
798 sprintf(tmp, "MBF fit ");
799 if(strlen(dft.studynr)>0) strlcat(tmp, dft.studynr, 64);
800 ret=plot_fitrange_svg(&dft, &fit, tmp, 0.0, 1.03*dft.x[fitframeNr-1],
801 0.0, nan(""), svgfile, verbose-8);
802 if(ret) {
803 fprintf(stderr, "Error (%d) in writing '%s'.\n", ret, svgfile);
804 } else if(verbose>0) printf("plots written in %s\n", svgfile);
805 }
806
807 }
808
809
810 /*
811 * Save Ca in a file
812 */
813 if(inputfile[0]) {
814 if(verbose>1) printf("saving arterial blood data in %s\n", inputfile);
815 if(dftWrite(&input, inputfile)) {
816 fprintf(stderr, "Error in writing %s: %s\n", inputfile, dfterrmsg);
817 dftEmpty(&dft); dftEmpty(&input); dftEmpty(&fit); resEmpty(&res);
818 return(11);
819 }
820 if(verbose>0) printf("Estimated arterial blood TAC saved in %s\n", inputfile);
821 }
822
823
824 dftEmpty(&dft); dftEmpty(&input); dftEmpty(&fit); resEmpty(&res);
825
826 return(0);
827}
828/*****************************************************************************/
829
830/*****************************************************************************
831 *
832 * Functions to be minimized
833 *
834 *****************************************************************************/
835/* Function for whole myocardial muscle and LV */
836double mbfFunc(int parNr, double *p, void *fdata)
837{
838 int fi, ret;
839 double wss=0.0, d, K1, k2, Vfit, flow, Va, alpha;
840 double pa[MAX_PARAMETERS], penalty=1.0;
841
842 /* Check parameters against the constraints */
843 ret=modelCheckParameters(parNr, pmin, pmax, p, pa, &penalty);
844 if(fdata) {}
845
846 /* Calculate K1, k2 and Vfit */
847 flow=pa[0]; alpha=pa[1]; Va=pa[2];
848 K1=(flow/Beta)*(alpha+Va/pc);
849 k2=flow*(1.0/pc+(1.0-Beta)/Beta);
850 Vfit=Va/Beta;
851
852 /* Simulate whole muscle curve and compute weighted SS */
853 ret=simMBF(input.x, input.voi[0].y, input.frameNr, K1, k2, Vfit, petsim);
854 if(ret) {
855 fprintf(stderr, "error %d in simulation\n", ret);
856 return(nan(""));
857 }
858
859 for(fi=0; fi<fitframeNr; fi++) if(weight[fi]>0.0) {
860 d=petmeas[fi]-petsim[fi]; wss+=weight[fi]*d*d;
861 }
862 wss_wo_penalty=wss;
863 wss*=penalty;
864
865 return(wss);
866}
867/* function for smaller myocardial regions and Ca */
868double mbfFunc2(int parNr, double *p, void *fdata)
869{
870 int fi, ret;
871 double wss=0.0, d, K1, k2, Vfit, flow, Va, alpha;
872 double pa[MAX_PARAMETERS], penalty=1.0;
873
874 /* Check parameters against the constraints */
875 ret=modelCheckParameters(parNr, pmin, pmax, p, pa, &penalty);
876 if(fdata) {}
877
878 /* Calculate K1, k2 and Vfit */
879 flow=pa[0]; alpha=pa[1]; Va=pa[2];
880 K1=flow*(alpha+Va/pc);
881 k2=flow/pc;
882 Vfit=Va;
883
884 /* Simulate muscle curve and compute weighted SS */
885 ret=simMBF(input.x, input.voi[0].y, input.frameNr, K1, k2, Vfit, petsim);
886 if(ret) {
887 fprintf(stderr, "error %d in simulation\n", ret);
888 return(nan(""));
889 }
890
891 for(fi=0; fi<fitframeNr; fi++) if(weight[fi]>0.0) {
892 d=petmeas[fi]-petsim[fi]; wss+=weight[fi]*d*d;
893 }
894 wss_wo_penalty=wss;
895 wss*=penalty;
896
897 return(wss);
898}
899/*****************************************************************************/
900
901/*****************************************************************************/
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
int dftCopyvoihdr(DFT *dft1, int from, DFT *dft2, int to)
Definition dft.c:623
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 dftSelectBestReference(DFT *dft)
Definition dft.c:314
int dftValidNr(DFT *dft, double tstart, double tstop, int index)
Definition dft.c:1638
int dftSetmem(DFT *data, int frameNr, int voiNr)
Definition dft.c:57
void dftEmpty(DFT *data)
Definition dft.c:20
int dft_nr_of_NA(DFT *dft)
Definition dft.c:905
int dftSelectRegions(DFT *dft, char *region_name, int reset)
Definition dft.c:285
int dftCopymainhdr(DFT *dft1, DFT *dft2)
Definition dft.c:561
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
void dftSec2min(DFT *dft)
Definition dftunit.c:160
void dftMin2sec(DFT *dft)
Definition dftunit.c:145
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
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
size_t strlcat(char *dst, const char *src, size_t dstsize)
Definition strext.c:206
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 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
int simMBF(double *t, double *ci, int nr, double k1, double k2, double Vfit, double *ct)
Definition simulate.c:1253
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
char studynr[MAX_STUDYNR_LEN+1]
double * w
double * x1
int voiNr
double * x2
int frameNr
int isweight
double * x
const char * status
Definition libtpcmisc.h:277
int parNr
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
int voiNr
int datanr
double beta
ResVOI * voi
double Vb
char program[1024]
char datarange[128]
int isweight
char datafile[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]
double * y
char name[MAX_REGIONNAME_LEN+1]