TPCCLIB
Loading...
Searching...
No Matches
liverpv.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <math.h>
14/*****************************************************************************/
15#include "tpcextensions.h"
16#include "tpcift.h"
17#include "tpctac.h"
18#include "tpcli.h"
19#include "tpccm.h"
20#include "tpctacmod.h"
21/*****************************************************************************/
22
23/*****************************************************************************/
24static char *info[] = {
25 "Input for liver models consists of two parts: portal vein and hepatic artery.",
26 "This program generates portal vein TAC from arterial TAC using the model",
27 "proposed by Munk et al. (2003).",
28 "In a pig study, Winterdahl et al. (2011) measured beta values for",
29 "[F-18]FDG (0.50), [O-15]H2O (2.17), [O-15]CO (0.10), [C-11]methylglucose",
30 "(0.57), and [F-18]FDGal (0.82).",
31 " ",
32 "Usage: @P [Options] arteryfile beta outputfile",
33 " ",
34 "Options:",
35 " -i=<Interval>",
36 " Sample time interval in convolution; by default the shortest interval",
37 " in the data, but restricted to range 0.0001*beta - 0.02*beta;",
38 " too long interval leads to bias.",
39 " -si",
40 " Output is written with sample intervals used in convolution; by default",
41 " data is interpolated back to the sample times of the artery data.",
42 " -stdoptions", // List standard options like --help, -v, etc
43 " ",
44 "Beta must be given in minutes, and optional interval in seconds.",
45 " ",
46 "Example:",
47 " @P ue65ap.bld 0.50 ue65pv.bld",
48 " ",
49 "References:",
50 "1. Munk OL, Keiding S, Bass L. Impulse-response function of splanchnic",
51 " circulation with model-independent constraints: theory and experimental",
52 " validation. Am J Physiol Gastrointest Liver Physiol. 2003;285:G671-G680.",
53 "2. Winterdahl M, Keiding S, Sorensen M, Mortensen FV, Alstrup AKO, Munk AL.",
54 " Tracer input for kinetic modelling of liver physiology determined without",
55 " sampling portal venous blood in pigs.",
56 " Eur J Nucl Med Mol Imaging 2011;38:263-270.",
57 " ",
58 "See also: liverinp, taccalc, taccat, b2plasma",
59 " ",
60 "Keywords: input, blood, TAC, modelling, simulation, liver",
61 0};
62/*****************************************************************************/
63
64/*****************************************************************************/
65/* Turn on the globbing of the command line, since it is disabled by default in
66 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
67 In Unix&Linux wildcard command line processing is enabled by default. */
68/*
69#undef _CRT_glob
70#define _CRT_glob -1
71*/
72int _dowildcard = -1;
73/*****************************************************************************/
74
75/*****************************************************************************/
79int main(int argc, char **argv)
80{
81 int ai, help=0, version=0, verbose=1;
82 char pfile[FILENAME_MAX], afile[FILENAME_MAX];
83 double beta=nan("");
84 double interval=nan("");
85 int output_sampling=0; // 0=artery, 1=convolution
86
87
88 /*
89 * Get arguments
90 */
91 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
92 pfile[0]=afile[0]=(char)0;
93 /* Options */
94 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
95 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
96 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
97 if(strncasecmp(cptr, "I=", 2)==0) {
98 interval=atofVerified(cptr+2); if(interval>0.0) continue;
99 } else if(strcasecmp(cptr, "SI")==0) {
100 output_sampling=1; continue;
101 }
102 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
103 return(1);
104 } else break; // tac name argument may start with '-'
105
106 TPCSTATUS status; statusInit(&status);
107 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
108 status.verbose=verbose-1;
109
110 /* Print help or version? */
111 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
112 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
113 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
114
115 /* Arguments */
116 if(ai<argc) {strlcpy(afile, argv[ai++], FILENAME_MAX);}
117 if(ai<argc) {
118 if(atofCheck(argv[ai], &beta)!=0 || beta<=0.0) {
119 fprintf(stderr, "Error: invalid beta value.\n");
120 return(1);
121 }
122 ai++;
123 }
124 if(ai<argc) {strlcpy(pfile, argv[ai++], FILENAME_MAX);}
125 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
126
127 /* Is something missing? */
128 if(!pfile[0]) {
129 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
130 return(1);
131 }
132
133
134 /* In verbose mode print arguments and options */
135 if(verbose>1) {
136 for(ai=0; ai<argc; ai++) printf("%s ", argv[ai]);
137 printf("\n");
138 printf("afile := %s\n", afile);
139 printf("beta := %g\n", beta);
140 printf("pfile := %s\n", pfile);
141 printf("output_sampling := %d\n", output_sampling);
142 if(!isnan(interval)) printf("interval := %g\n", interval);
143 fflush(stdout);
144 }
145
146
147 /*
148 * Read TAC file
149 */
150
151 /* Read arterial data */
152 if(verbose>0) printf("reading %s\n", afile);
153 TAC atac; tacInit(&atac);
154 if(tacRead(&atac, afile, &status)!=TPCERROR_OK) {
155 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
156 tacFree(&atac); return(2);
157 }
158 if(verbose>2) {
159 printf("fileformat := %s\n", tacFormattxt(atac.format));
160 printf("tacNr := %d\n", atac.tacNr);
161 printf("sampleNr := %d\n", atac.sampleNr);
162 printf("xunit := %s\n", unitName(atac.tunit));
163 printf("yunit := %s\n", unitName(atac.cunit));
164 }
165 if(atac.tacNr>1) {
166 fprintf(stderr, "Warning: only first TAC in arterial data is used.\n");
167 atac.tacNr=1;
168 }
169 /* Check for missing sample times */
170 if(tacXNaNs(&atac)>0) {
171 fprintf(stderr, "Error: missing frame times.\n");
172 tacFree(&atac); return(2);
173 }
174 /* Check for missing concentrations */
175 if(tacYNaNs(&atac, -1)>0) {
176 fprintf(stderr, "Error: missing concentrations.\n");
177 tacFree(&atac); return(2);
178 }
179 if(atac.sampleNr<3) {
180 fprintf(stderr, "Error: too few samples in plasma data.\n");
181 tacFree(&atac); return(2);
182 }
183
184 /* Sort by sample times */
185 if(tacSortByTime(&atac, &status)!=TPCERROR_OK) {
186 fprintf(stderr, "Error: invalid sample times.\n");
187 tacFree(&atac); return(2);
188 }
189 if(atac.isframe!=0 && verbose>0) {
190 if(tacSetX(&atac, &status)!=TPCERROR_OK) { // make sure that frame middle times are set
191 fprintf(stderr, "Error: invalid sample times.\n");
192 tacFree(&atac); return(2);
193 }
194 fprintf(stderr, "Warning: frame durations are ignored.\n");
195 }
196
197
198
199 /* Beta is in minutes; convert it to seconds if necessary */
200 /* Interval is in sec; convert to minutes if necessary */
201 if(atac.tunit==UNIT_SEC) {
202 beta*=60.0;
203 if(verbose>1) printf("beta is converted to %g s.\n", beta);
204 } else if(atac.tunit==UNIT_MIN) {
205 if(!isnan(interval) && interval>0.0) {
206 interval/=60.0;
207 if(verbose>1) printf("interval is converted to %g min.\n", interval);
208 }
209 } else {
210 fprintf(stderr, "Warning: time units are assumed to be in minutes.\n");
211 if(!isnan(interval) && interval>0.0) {
212 interval/=60.0;
213 if(verbose>1) printf("interval is converted to %g min.\n", interval);
214 }
215 }
216
217 /* Check user-defined interval time */
218 if(!isnan(interval) && interval>0.0) {
219 if(interval>0.01*atac.x[atac.sampleNr-1] || interval>0.2*beta) {
220 fprintf(stderr, "Error: too long interval time.\n");
221 tacFree(&atac); return(2);
222 }
223 if(interval>0.001*atac.x[atac.sampleNr-1] || interval>0.05*beta) {
224 if(verbose>0) fprintf(stderr, "Warning: interval time may be too long.\n");
225 }
226 }
227
228 /* Calculate kernel function integral at the end of data */
229 if(verbose>2) {
230 double kernel_integral=atac.x[atac.sampleNr-1]/(atac.x[atac.sampleNr-1]+beta);
231 if(verbose>1) printf("kernel integral at end of data: %g\n", kernel_integral);
232 }
233 /* Calculate the time when 99.9% of kernel function integral is reached */
234 if(verbose>2) {
235 double kernel_time=999.*beta;
236 double kernel_integral=kernel_time/(kernel_time+beta);
237 if(verbose>1) printf("kernel integral at %g: %g\n", kernel_time, kernel_integral);
238 }
239
240 /* Interpolate data with even sample intervals for convolution */
241 TAC itac; tacInit(&itac);
242 int ret=0;
243 if(!isnan(interval)) {
244 /* user-defined interpolation sample frequency */
245 ret=tacInterpolateToEqualLengthFrames(&atac, interval, interval, &itac, &status);
246 } else {
247 /* automatic interpolation sample frequency */
248 ret=tacInterpolateToEqualLengthFrames(&atac, 0.0001*beta, 0.02*beta, &itac, &status);
249 }
250 if(ret!=TPCERROR_OK) {
251 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
252 tacFree(&atac); tacFree(&itac); return(3);
253 }
254 /* Get the sample interval in interpolated data */
255 double freq=itac.x2[0]-itac.x1[0];
256 if(verbose>1) {
257 printf("sample_intervals_in_convolution := %g\n", freq);
258 printf("interpolated data range: %g - %g\n", itac.x1[0], itac.x2[itac.sampleNr-1]);
259 }
260
261
262 /*
263 * Calculate the response function for convolution.
264 *
265 * Function is f(x)=beta/(beta+x)^2 ;
266 * its indefinite integral is Constant - beta/(beta+x),
267 * definite integral from 0 to +inf is 1,
268 * definite integral from 0 to a is a/(a+beta),
269 * and definite integral from t to t+dt is beta*dt/((beta+t-dt/2)*(beta+t+dt/2)).
270 */
271 if(verbose>0) fprintf(stdout, "allocate memory for kernel...\n");
272 double *kernel=(double*)malloc(2*itac.sampleNr*sizeof(double));
273 if(kernel==NULL) {
274 fprintf(stderr, "Error: out of memory.\n");
275 tacFree(&atac); tacFree(&itac); return(4);
276 }
277 double *cy=kernel+itac.sampleNr;
278
279 if(verbose>0) fprintf(stdout, "computing the kernel...\n");
280 double ksum=0.0;
281 if(verbose>6) printf("\nData\tKernel:\n");
282 for(int i=0; i<itac.sampleNr; i++) {
283 kernel[i]=beta*freq/((beta+itac.x1[i])*(beta+itac.x2[i])); // integral, not mean
284 ksum+=kernel[i]; // sum of stepwise integrals
285 if(verbose>6) printf("%g\t%g\n", itac.c[0].y[i], kernel[i]);
286 }
287 if(verbose>4) printf("last_kernel_value := %g\n", kernel[itac.sampleNr-1]);
288 if(verbose>2) printf("Sum of response function := %g\n", ksum);
289 if(!isnormal(ksum)) {
290 fprintf(stderr, "Error: invalid kernel contents.\n");
291 tacFree(&atac); tacFree(&itac); free(kernel); return(5);
292 }
293
294
295 /*
296 * Convolution
297 */
298 if(verbose>0) {fprintf(stdout, "convolution...\n"); fflush(stdout);}
299 ret=convolve1D(itac.c[0].y, itac.sampleNr, kernel, itac.sampleNr, cy);
300 if(ret!=0) {
301 fprintf(stderr, "Error: cannot convolve the data.\n");
302 tacFree(&atac); tacFree(&itac); free(kernel); return(6);
303 }
304 if(verbose>6) {
305 printf("\nData x\ty\tKernel\tConvolved\n");
306 for(int i=0; i<itac.sampleNr; i++)
307 printf("%g\t%g\t%g\t%g\n", itac.x[i], itac.c[0].y[i], kernel[i], cy[i]);
308 }
309
310 /* Copy convoluted curve over interpolated curve */
311 for(int i=0; i<itac.sampleNr; i++) itac.c[0].y[i]=cy[i];
312 /* No need for kernel or working space */
313 free(kernel);
314
315 /*
316 * If user wants to save data with sample intervals used in convolution, then do that and exit
317 */
318 if(output_sampling==1) {
319
320 /* No need for arterial data */
321 tacFree(&atac);
322
323 /* Change TAC name */
324 if( strcasestr(itac.c[0].name, "art")!=NULL || strcasestr(itac.c[0].name, "aort")!=NULL ||
325 strcasestr(itac.c[0].name, "LV")!=NULL || strcasestr(itac.c[0].name, "cav")!=NULL )
326 {
327 strcpy(itac.c[0].name, "PV");
328 } else if(strcasestr(itac.c[0].name, "CA")!=NULL) {
329 strcpy(itac.c[0].name, "Cpv");
330 }
331
332 if(verbose>1) printf("writing portal vein data in %s\n", pfile);
333 FILE *fp; fp=fopen(pfile, "w");
334 if(fp==NULL) {
335 fprintf(stderr, "Error: cannot open file for writing (%s)\n", pfile);
336 tacFree(&itac); return(11);
337 }
338 ret=tacWrite(&itac, fp, TAC_FORMAT_UNKNOWN, 1, &status);
339 fclose(fp); tacFree(&itac);
340 if(ret!=TPCERROR_OK) {
341 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
342 return(12);
343 }
344 if(verbose>=0) printf("Portal vein TAC saved in %s\n", pfile);
345 return(0);
346 }
347
348
349 /*
350 * Interpolate convolved data into original sample times
351 */
352 if(verbose>1) fprintf(stdout, "interpolating to original sample times...\n");
353 /* Transform sample interval data into dot-to-dot data */
354 TAC dtac; tacInit(&dtac);
355 ret=tacFramesToSteps(&itac, &dtac, &status);
356 if(ret!=TPCERROR_OK) {
357 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
358 tacFree(&atac); tacFree(&itac); return(10);
359 }
360 /* Interpolate to original times */
361 if(atac.isframe==0)
362 ret=liInterpolate(dtac.x, dtac.c[0].y, dtac.sampleNr, atac.x, atac.c[0].y, NULL, NULL,
363 atac.sampleNr, 0, 0, verbose-10);
364 else
365 ret=liInterpolateForPET(dtac.x, dtac.c[0].y, dtac.sampleNr, atac.x1, atac.x2, atac.c[0].y,
366 NULL, NULL, atac.sampleNr, 0, 1, verbose-10);
367 tacFree(&dtac);
368 if(ret!=0) {
369 fprintf(stderr, "Error: cannot interpolate back.\n");
370 tacFree(&atac); tacFree(&itac); return(7);
371 }
372
373 /* No need for interpolated data */
374 tacFree(&itac);
375
376 /* Change TAC name */
377 if( strcasestr(atac.c[0].name, "art")!=NULL || strcasestr(atac.c[0].name, "aort")!=NULL ||
378 strcasestr(atac.c[0].name, "LV")!=NULL || strcasestr(atac.c[0].name, "cav")!=NULL )
379 {
380 strcpy(atac.c[0].name, "PV");
381 } else if(strcasestr(atac.c[0].name, "CA")!=NULL) {
382 strcpy(atac.c[0].name, "Cpv");
383 }
384
385 /*
386 * Write the file
387 */
388 if(verbose>1) printf("writing portal vein data in %s\n", pfile);
389 FILE *fp; fp=fopen(pfile, "w");
390 if(fp==NULL) {
391 fprintf(stderr, "Error: cannot open file for writing (%s)\n", pfile);
392 tacFree(&atac); return(11);
393 }
394 ret=tacWrite(&atac, fp, TAC_FORMAT_UNKNOWN, 1, &status);
395 fclose(fp); tacFree(&atac);
396 if(ret!=TPCERROR_OK) {
397 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
398 return(12);
399 }
400 if(verbose>=0) printf("Portal vein TAC saved in %s\n", pfile);
401
402 return(0);
403}
404/*****************************************************************************/
406/*****************************************************************************/
int convolve1D(double *data, const int n, double *kernel, const int m, double *out)
Calculates the convolution sum of a discrete real data set data[0..n-1] and a discretized response fu...
Definition convolut.c:27
double atofVerified(const char *s)
Definition decpoint.c:75
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 liInterpolateForPET(double *x, double *y, const int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear TAC interpolation and/or integration to PET frames.
int tacInterpolateToEqualLengthFrames(TAC *inp, double minfdur, double maxfdur, TAC *tac, TPCSTATUS *status)
Definition lisim.c:214
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
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:632
char * strcasestr(const char *haystack, const char *needle)
Definition stringext.c:155
char name[MAX_TACNAME_LEN+1]
Definition tpctac.h:81
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
double * x2
Definition tpctac.h:101
unit tunit
Definition tpctac.h:109
double * x1
Definition tpctac.h:99
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
void tacInit(TAC *tac)
Definition tac.c:24
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 tacYNaNs(TAC *tac, const int i)
Definition tacnan.c:47
int tacXNaNs(TAC *tac)
Definition tacnan.c:23
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacFramesToSteps(TAC *inp, TAC *out, TPCSTATUS *status)
Transform TAC with frames into TAC with frames represented with stepwise changing dot-to-dot data.
Definition tacx.c:942
int tacSetX(TAC *d, TPCSTATUS *status)
Set TAC x values based on x1 and x2 values, or guess x1 and x2 values based on x values.
Definition tacx.c:653
Header file for libtpccm.
Header file for library libtpcextensions.
@ UNIT_MIN
minutes
@ UNIT_SEC
seconds
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
Header file for libtpcli.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28
Header file for libtpctacmod.