TPCCLIB
Loading...
Searching...
No Matches
arlkup.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 <string.h>
15#include <math.h>
16/*****************************************************************************/
17#include "tpcextensions.h"
18#include "tpcift.h"
19#include "tpctac.h"
20#include "tpcli.h"
21#include "tpccm.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "Calculation of perfusion look-up table from [O-15]H2O blood curve",
27 "for usage with PET in vivo autoradiographic (ARG) method.",
28 " ",
29 "Usage: @P [options] BTAC p fMax start dur lkupfile",
30 " ",
31 "Options:",
32 " -static=<y|N>",
33 " The look-up table integral can be corrected for physical decay as",
34 " single frame when static PET scan was performed (y), or dynamically",
35 " (N, the default).",
36 " -nr=<value>",
37 " Set the size of look-up table; 5000 by default.",
38 " -stdoptions", // List standard options like --help, -v, etc
39 " ",
40 "Blood TAC (BTAC) must be calibrated and corrected for decay. Sample times",
41 "must be in sec unless correctly specified inside the BTAC file.",
42 "Concentrations must be in the same units as the units of PET tissue data.",
43 "Partition coefficient of water (p) must be given in units mL/mL,",
44 "and maximal perfusion (fMax) in units mL/(min*100mL).",
45 "Integration start time and duration is given in sec.",
46 " ",
47 "Look-up table will contain 2 columns: TTAC integral [sec*kBq/mL] and",
48 "blood flow [mL/(min*100mL)].",
49 " ",
50 "Example:",
51 " @P -nr=2000 s1456_blo.fit 0.8 50 0 120 s1456.lkup",
52 " ",
53 "References:",
54 "1. Raichle ME. Quantitative in vivo autoradiography with positron emission",
55 " tomography. Brain Res Rev. 1979;1:47-68.",
56 "2. Herscovitch P, Markham J, Raichle ME. Brain blood flow measured with",
57 " intravenous H215O. I. Theory and error analysis.",
58 " J Nucl Med. 1983;24:782-789.",
59 "3. Raichle ME, Martin WRW, Herscovitch P, Mintun MA, Markham J. Brain blood",
60 " flow measured with intravenous H215O. II. Implementation and validation.",
61 " J Nucl Med. 1983;24:790-798.",
62 "4. Ruotsalainen U, Raitakari M, Nuutila P, Oikonen V, Sipila H, Teras M,",
63 " Knuuti J, Bloomfield PM, Iida H. Quantitative blood flow measurement of",
64 " skeletal muscle using oxygen-15-water and PET. ",
65 " J Nucl Med. 1997; 38:314-319.",
66 " ",
67 "See also: fitdelay, imginteg, imglkup, taclkup, tacunit, imgbfbp, imgflow",
68 " ",
69 "Keywords: perfusion, blood flow, radiowater, autoradiography, ARG, look-up table",
70 0};
71/*****************************************************************************/
72
73/*****************************************************************************/
74/* Turn on the globbing of the command line, since it is disabled by default in
75 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
76 In Unix&Linux wildcard command line processing is enabled by default. */
77/*
78#undef _CRT_glob
79#define _CRT_glob -1
80*/
81int _dowildcard = -1;
82/*****************************************************************************/
83
84/*****************************************************************************/
88int main(int argc, char **argv)
89{
90 int ai, help=0, version=0, verbose=1;
91 char btacfile[FILENAME_MAX], lkupfile[FILENAME_MAX];
92 char *cptr;
93 double pWater, maxFlow, start, dur, end;
94 int tableSize=5000;
95 int singleFrame=0;
96 int ret;
97
98
99 /*
100 * Get arguments
101 */
102 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
103 btacfile[0]=lkupfile[0]=(char)0;
104 /* Options */
105 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
106 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
107 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
108 if(strncasecmp(cptr, "NR=", 3)==0) {
109 if(atoiCheck(cptr+3, &tableSize)==0 && tableSize>5) continue;
110 } else if(strncasecmp(cptr, "STATIC=", 7)==0) {
111 cptr+=7;
112 if(strncasecmp(cptr, "YES", 1)==0) {singleFrame=1; continue;}
113 else if(strncasecmp(cptr, "NO", 1)==0) {singleFrame=0; continue;}
114 }
115 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
116 return(1);
117 } else break;
118
119 TPCSTATUS status; statusInit(&status);
120 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
121 status.verbose=verbose-3;
122
123 /* Print help or version? */
124 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
125 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
126 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
127
128 /* Process other arguments, starting from the first non-option */
129 if(ai<argc) strlcpy(btacfile, argv[ai++], FILENAME_MAX);
130 if(ai<argc) {
131 if(atofCheck(argv[ai], &pWater) || pWater<=0.0 || pWater>1.25) {
132 fprintf(stderr, "Error: invalid partition coefficient '%s'.\n", argv[ai]);
133 return(1);
134 }
135 ai++;
136 }
137 if(ai<argc) {
138 if(atofCheck(argv[ai], &maxFlow) || maxFlow<=0.0) {
139 fprintf(stderr, "Error: invalid maximum blood flow '%s'.\n", argv[ai]);
140 return(1);
141 }
142 ai++;
143 }
144 if(ai<argc) {
145 if(atofCheck(argv[ai], &start)) {
146 fprintf(stderr, "Error: invalid integration start time '%s'.\n", argv[ai]);
147 return(1);
148 }
149 ai++;
150 }
151 if(ai<argc) {
152 if(atofCheck(argv[ai], &dur) || dur<=0.0) {
153 fprintf(stderr, "Error: invalid integration duration '%s'.\n", argv[ai]);
154 return(1);
155 }
156 ai++;
157 }
158 if(ai<argc) strlcpy(lkupfile, argv[ai++], FILENAME_MAX);
159 if(ai<argc) {
160 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
161 return(1);
162 }
163 /* Did we get all the information that we need? */
164 if(!lkupfile[0]) {
165 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
166 return(1);
167 }
168 end=start+dur;
169 if(end<=0.0) {
170 fprintf(stderr, "Error: invalid integration times.\n");
171 return(1);
172 }
173 if(tableSize<50) {
174 fprintf(stderr,
175 "Warning: look-up table may be too small to provide accurate results.\n");
176 }
177
178
179 /* In verbose mode print arguments and options */
180 if(verbose>1) {
181 printf("btacfile := %s\n", btacfile);
182 printf("pWater := %g\n", pWater);
183 printf("maxFlow := %g\n", maxFlow);
184 printf("start := %g\n", start);
185 printf("dur := %g\n", dur);
186 printf("end := %g\n", end);
187 printf("tableSize := %d\n", tableSize);
188 printf("lkupfile := %s\n", lkupfile);
189 printf("singleFrame := %d\n", singleFrame);
190 }
191
192
193 /*
194 * Read BTAC
195 */
196 if(verbose>1) printf("reading %s\n", btacfile);
197 TAC btac; tacInit(&btac);
198 ret=tacRead(&btac, btacfile, &status);
199 if(ret!=TPCERROR_OK) {
200 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
201 tacFree(&btac); return(2);
202 }
203 if(verbose>2) {
204 printf("fileformat := %s\n", tacFormattxt(btac.format));
205 printf("tacNr := %d\n", btac.tacNr);
206 printf("sampleNr := %d\n", btac.sampleNr);
207 printf("xunit := %s\n", unitName(btac.tunit));
208 printf("yunit := %s\n", unitName(btac.cunit));
209 }
210 if(btac.tacNr>1) {
211 fprintf(stderr, "Warning: only first TAC in input file is used.\n");
212 btac.tacNr=1;
213 }
214
215 /* Sort by sample times */
216 tacSortByTime(&btac, NULL);
217
218 /* Check for missing values */
219 ret=tacNaNs(&btac);
220 if(ret>0) {
221 if(verbose>1) printf("missing blood concentrations.\n");
222 /* Try to fix missing concentrations */
223 ret=tacFixNaNs(&btac);
224 if(ret!=0) {
225 fprintf(stderr, "Error: missing concentrations in %s.\n", btacfile);
226 tacFree(&btac); return(2);
227 }
228 }
229
230
231 /* Check and convert time units */
232 double xmin, xmax;
233 ret=tacXRange(&btac, &xmin, &xmax);
234 if(ret!=0) {
235 fprintf(stderr, "Error: invalid time range in btac file.\n");
236 tacFree(&btac); return(3);
237 }
238 if(verbose>2) {
239 printf("btac.xmin := %g\n", xmin);
240 printf("btac.xmax := %g\n", xmax);
241 }
242 if(btac.tunit==UNIT_UNKNOWN) {
243 if(verbose>0) printf("Note: time units had to be guessed.\n");
244 if(xmin<1.0 && xmax<30.0) btac.tunit=UNIT_MIN;
245 else btac.tunit=UNIT_SEC;
246 }
247 ret=tacXUnitConvert(&btac, UNIT_SEC, &status);
248 if(ret!=TPCERROR_OK && verbose>0) {
249 fprintf(stderr, "Error: invalid time units.\n");
250 tacFree(&btac); return(3);
251 }
252 /* Check that BTAC extends to the end of requested integration period */
253 if(xmax<0.99*end) {
254 fprintf(stderr, "Error: blood data only upto %g s.\n", xmax);
255 tacFree(&btac); return(3);
256 }
257 /* Check if integration end time seems to be too short */
258 if(end<=xmin) {
259 fprintf(stderr, "Error: invalid integration time.\n");
260 tacFree(&btac); return(3);
261 }
262 if(end<(0.25*xmin+0.75*xmax)) {
263 fprintf(stderr, "Warning: integration time is short compared to BTAC.\n");
264 }
265 if(btac.sampleNr<5) {
266 fprintf(stderr, "Error: only %d samples in %s\n", btac.sampleNr, btacfile);
267 tacFree(&btac); return(2);
268 } else if(btac.sampleNr<50 && btac.isframe==0) {
269 // no warning if image-derived input containing frame times
270 fprintf(stderr, "Warning: small blood sample nr in %s\n", btacfile);
271 }
272
273
274 /* Check and convert concentration units */
275 double ymin, ymax;
276 ret=tacYRange(&btac, 0, &ymin, &ymax, NULL, NULL, NULL, NULL);
277 if(ret!=0) {
278 fprintf(stderr, "Error: invalid concentration range in btac file.\n");
279 tacFree(&btac); return(4);
280 }
281 if(verbose>2) {
282 printf("btac.ymin := %g\n", ymin);
283 printf("btac.ymax := %g\n", ymax);
284 }
285 if(btac.cunit==UNIT_UNKNOWN) {
286 fprintf(stderr, "Warning: concentration units are not known.\n");
287 }
288
289 /* Add extra column to BTAC data struct to store simulated tissue TAC */
290 ret=tacAllocateMore(&btac, 1);
291 if(ret!=TPCERROR_OK) {
292 fprintf(stderr, "Error: cannot allocate memory.\n");
293 tacFree(&btac); return(5);
294 }
295
296
297 /*
298 * Make the look-up table
299 */
300
301 /* Allocate memory for the lookup table */
302 if(verbose>1) printf("allocating memory for table\n");
303 TAC lkup; tacInit(&lkup);
304 ret=tacAllocate(&lkup, tableSize, 1);
305 if(ret!=TPCERROR_OK) {
306 fprintf(stderr, "Error: %s\n", errorMsg(ret));
307 tacFree(&btac); tacFree(&lkup); return(6);
308 }
309 lkup.sampleNr=tableSize; lkup.tacNr=1;
310
311 /* Set look-up table units if possible */
314 else if(btac.cunit==UNIT_BQ_PER_ML) lkup.tunit=UNIT_SEC_BQ_PER_ML;
315 else lkup.tunit=UNIT_UNKNOWN;
316
317 /* Calculate the K1 (blood flow) in the look-up table */
318 if(verbose>1) printf("setting the perfusion in look-up table\n");
319 {
320 double K1step=maxFlow/(double)(tableSize-1);
321 if(verbose>2) printf("K1step := %E\n", K1step);
322 lkup.c[0].y[0]=0.0;
323 for(int i=1; i<tableSize; i++) lkup.c[0].y[i]=lkup.c[0].y[i-1]+K1step;
324 if(verbose>2) printf("K1 range: %g - %g\n",
325 lkup.c[0].y[0], lkup.c[0].y[lkup.sampleNr-1]);
326 }
327
328 /* Calculate the table */
329 if(verbose>1) printf("calculating the tissue integrals in look-up table\n");
330 {
331 double dc=1.0;
332 if(singleFrame!=0) {
333 /* If PET data is collected as a single frame (static scan), then
334 calculate decay correction factor during the frame */
335 dc=decayCorrectionFactorFromIsotope(ISOTOPE_O_15, start/60.0, dur/60.0);
336 }
337
338 double K1, k2, intx[2], inty[2];
339 intx[0]=start; intx[1]=end;
340 lkup.x[0]=0.0;
341 ret=0;
342 for(int i=1; i<tableSize && !ret; i++) {
343 K1=lkup.c[0].y[i]/6000.0; // mL/(min*100mL) -> mL/(sec*mL)
344 k2=K1/pWater;
345 /* Simulate the tissue curve with these K1 and k2 values */
346 ret=simC1(btac.x, btac.c[0].y, btac.sampleNr, K1, k2, btac.c[1].y);
347 if(ret) break;
348 /* Remove decay correction from tissue curve, if static PET scan */
349 if(singleFrame!=0) {
350 for(int j=0; j<btac.sampleNr; j++)
352 ISOTOPE_O_15, btac.x[j]/60.0, 0.0);
353 }
354 /* Integrate the simulated tissue curve */
355 ret=liInterpolate(btac.x, btac.c[1].y, btac.sampleNr,
356 intx, NULL, inty, NULL, 2, 4, 1, 0);
357 if(ret) break;
358 /* Set the table integral values */
359 lkup.x[i]=inty[1]-inty[0];
360 /* Correct for physical decay again, as single frame, if required */
361 if(singleFrame!=0) lkup.x[i]*=dc;
362 } // next look-up table row
363 if(ret) {
364 fprintf(stderr, "Error: cannot compute simulated tissue AUC.\n");
365 tacFree(&btac); tacFree(&lkup); return(7);
366 }
367 }
368
369 /* BTAC is not needed anymore */
370 tacFree(&btac);
371
372
373 /*
374 * Write look-up table
375 */
376 if(verbose>1) printf("writing %s\n", lkupfile);
377 FILE *fp; fp=fopen(lkupfile, "w");
378 if(fp==NULL) {
379 fprintf(stderr, "Error: cannot open file for writing (%s)\n", lkupfile);
380 tacFree(&lkup); return(11);
381 }
382 ret=tacWrite(&lkup, fp, TAC_FORMAT_PMOD, 0, &status);
383 fclose(fp); tacFree(&lkup);
384 if(ret!=TPCERROR_OK) {
385 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
386 return(12);
387 }
388 if(verbose>0) printf("Look-up table saved in %s.\n", lkupfile);
389
390
391 return(0);
392}
393/*****************************************************************************/
394
395/*****************************************************************************/
double decayCorrectionFactorFromIsotope(int isotope, double starttime, double duration)
Definition decay.c:107
int atofCheck(const char *s, double *v)
Definition decpoint.c:94
int liInterpolate(double *x, double *y, const int nr, double *newx, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear interpolation and/or integration with trapezoidal method.
int atoiCheck(const char *s, int *v)
Definition intutil.c:25
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
int simC1(double *t, double *ca, const int nr, const double k1, const double k2, double *ct)
Definition sim1cm.c:93
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:632
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
unit tunit
Definition tpctac.h:109
int tacNr
Definition tpctac.h:91
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
void tacFree(TAC *tac)
Definition tac.c:106
int tacAllocate(TAC *tac, int sampleNr, int tacNr)
Definition tac.c:130
void tacInit(TAC *tac)
Definition tac.c:24
int tacAllocateMore(TAC *tac, int tacNr)
Definition tac.c:178
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
char * tacFormattxt(tacformat c)
Definition tacio.c:98
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
Definition tacio.c:332
int tacNaNs(TAC *tac)
Definition tacnan.c:71
int tacFixNaNs(TAC *tac)
Definition tacnan.c:121
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124
int tacYRange(TAC *d, int i, double *ymin, double *ymax, int *smin, int *smax, int *imin, int *imax)
Get the range of y values (concentrations) in TAC struct.
Definition tacy.c:26
Header file for libtpccm.
Header file for library libtpcextensions.
@ UNIT_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_SEC_BQ_PER_ML
s*Bq/mL
@ UNIT_KBQ_PER_ML
kBq/mL
@ UNIT_ML_PER_DL_MIN
mL/(dL*min)
@ UNIT_SEC
seconds
@ UNIT_SEC_KBQ_PER_ML
s*kBq/mL
@ UNIT_BQ_PER_ML
Bq/mL.
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
@ ISOTOPE_O_15
O-15.
Definition tpcisotope.h:69
Header file for libtpcli.
Header file for library libtpctac.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Definition tpctac.h:33