TPCCLIB
Loading...
Searching...
No Matches
convexpf.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 "Convolution of PET time-activity curve (TAC) with response function h(t)",
26 "consisting of sum of three decaying exponential functions",
27 " h(t) = a1*exp(-b1*t) + a2*exp(-b2*t) + a3*exp(-b3*t)",
28 "Function integral from 0 to infinity is a1/b1 + a2/b2 + a3/b3.",
29 "Output TAC, Co(t), is calculated from input TAC, Ci(t), as",
30 "Co(T) = Ci(T) (x) h(T)",
31 ", where (x) denotes the operation of convolution.",
32 "To use sum of two exponentials as the response function, enter zeroes for",
33 "a3 and b3. To use a mono-exponential, set a2 and b2 to zero, too.",
34 " ",
35 "Usage: @P [Options] tacfile a1 b1 a2 b2 a3 b3 outputfile ",
36 " ",
37 "Options:",
38 " -i=<Interval>",
39 " Sample time interval in convolution; by default the shortest interval",
40 " in the plasma data; too long interval as compared to b's leads to bias.",
41 " -auc=1",
42 " Scale response function to have integral from 0 to infinity to unity;",
43 " that will lead to similar AUC for the input and output TACs.",
44 " -h0=1",
45 " Scale response function to h(0)=1.",
46 " -stdoptions", // List standard options like --help, -v, etc
47 " ",
48 "The units of a's and b's must be compatible with units of the TAC, and",
49 "the optional sample interval.",
50 "For accurate results, input TAC should have very short sampling intervals.",
51 "Frame durations are not used, even if available in the TAC file.",
52 " ",
53 "Example:",
54 " @P -auc=1 plasma.tac 1 0.05 0 0 0 0 simulated.tac",
55 " ",
56 "See also: sim_3tcm, fit2dat, tacadd, simdisp, simframe, sim_av, convsurg",
57 " ",
58 "Keywords: TAC, simulation, modelling, convolution",
59 0};
60/*****************************************************************************/
61
62/*****************************************************************************/
63/* Turn on the globbing of the command line, since it is disabled by default in
64 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
65 In Unix&Linux wildcard command line processing is enabled by default. */
66/*
67#undef _CRT_glob
68#define _CRT_glob -1
69*/
70int _dowildcard = -1;
71/*****************************************************************************/
72
73/*****************************************************************************/
77int main(int argc, char **argv)
78{
79 int ai, help=0, version=0, verbose=1;
80 char tacfile[FILENAME_MAX], simfile[FILENAME_MAX];
81 double interval=nan(""), h0=nan(""), auc=nan("");
82 double a1=nan(""), b1=nan(""), a2=nan(""), b2=nan(""), a3=nan(""), b3=nan("");
83
84
85 /*
86 * Get arguments
87 */
88 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
89 tacfile[0]=simfile[0]=(char)0;
90 /* Options */
91 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
92 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
93 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
94 if(strncasecmp(cptr, "I=", 2)==0) {
95 interval=atofVerified(cptr+2); if(interval>0.0) continue;
96 } else if(strcasecmp(cptr, "AUC=1")==0) {
97 auc=1.0; continue;
98 } else if(strncasecmp(cptr, "AUC=", 4)==0) {
99 auc=atofVerified(cptr+4); if(auc>0.0) continue;
100 } else if(strcasecmp(cptr, "h0=1")==0) {
101 h0=1.0; continue;
102 } else if(strncasecmp(cptr, "h0=", 3)==0) {
103 h0=atofVerified(cptr+3); if(h0>0.0) continue;
104 }
105 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
106 return(1);
107 } else break; // tac name argument may start with '-'
108
109 TPCSTATUS status; statusInit(&status);
110 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
111 status.verbose=verbose-1;
112
113 /* Print help or version? */
114 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
115 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
116 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
117
118 /* Process other arguments, starting from the first non-option */
119 if(ai<argc) {strlcpy(tacfile, argv[ai++], FILENAME_MAX);}
120 if(ai<argc) {
121 a1=atofVerified(argv[ai]);
122 if(!(a1>0.0)) {fprintf(stderr, "Error: invalid a1 '%s'.\n", argv[ai]); return(1);}
123 ai++;
124 }
125 if(ai<argc) {
126 b1=atofVerified(argv[ai]);
127 if(!(b1>0.0)) {fprintf(stderr, "Error: invalid b1 '%s'.\n", argv[ai]); return(1);}
128 ai++;
129 }
130 if(ai<argc) {
131 a2=atofVerified(argv[ai]);
132 if(!(a2>=0.0)) {fprintf(stderr, "Error: invalid a2 '%s'.\n", argv[ai]); return(1);}
133 ai++;
134 }
135 if(ai<argc) {
136 b2=atofVerified(argv[ai]);
137 if(!(b2>=0.0)) {fprintf(stderr, "Error: invalid b2 '%s'.\n", argv[ai]); return(1);}
138 ai++;
139 }
140 if(ai<argc) {
141 a3=atofVerified(argv[ai]);
142 if(!(a3>=0.0)) {fprintf(stderr, "Error: invalid a3 '%s'.\n", argv[ai]); return(1);}
143 ai++;
144 }
145 if(ai<argc) {
146 b3=atofVerified(argv[ai]);
147 if(!(b3>=0.0)) {fprintf(stderr, "Error: invalid b3 '%s'.\n", argv[ai]); return(1);}
148 ai++;
149 }
150 if(ai<argc) {strlcpy(simfile, argv[ai++], FILENAME_MAX);}
151 if(ai<argc) {
152 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
153 return(1);
154 }
155
156 /* Is something missing? */
157 if(!simfile[0]) {
158 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
159 return(1);
160 }
161
162 /* Check parameters */
163 if(a2==0.0 || b2==0.0) {a2=b2=a3=b3=nan("");}
164 else if(a3==0.0 || b3==0.0) {a3=b3=nan("");}
165
166 /* In verbose mode print arguments and options */
167 if(verbose>1) {
168 printf("tacfile := %s\n", tacfile);
169 printf("simfile := %s\n", simfile);
170 printf("a1 := %g\n", a1); printf("b1 := %g\n", b1);
171 if(a2>0.0) {printf("a2 := %g\n", a2); printf("b2 := %g\n", b2);}
172 if(a3>0.0) {printf("a3 := %g\n", a3); printf("b3 := %g\n", b3);}
173 if(!isnan(auc)) printf("auc := %g\n", auc);
174 if(!isnan(h0)) printf("h0 := %g\n", h0);
175 if(!isnan(interval)) printf("interval := %g\n", interval);
176 fflush(stdout);
177 }
178
179
180 /*
181 * Read plasma TAC
182 */
183 if(verbose>1) fprintf(stdout, "reading %s\n", tacfile);
184 TAC tac; tacInit(&tac);
185 if(tacRead(&tac, tacfile, &status)!=TPCERROR_OK) {
186 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), tacfile);
187 tacFree(&tac); return(2);
188 }
189 if(verbose>2) {
190 printf("fileformat := %s\n", tacFormattxt(tac.format));
191 printf("tacNr := %d\n", tac.tacNr);
192 printf("sampleNr := %d\n", tac.sampleNr);
193 printf("xunit := %s\n", unitName(tac.tunit));
194 printf("yunit := %s\n", unitName(tac.cunit));
195 if(tacIsWeighted(&tac)) printf("weighting := yes\n");
196 }
197 /* Check for missing sample times */
198 if(tacXNaNs(&tac)>0) {
199 fprintf(stderr, "Error: missing frame times.\n");
200 tacFree(&tac); return(2);
201 }
202 /* Check for missing concentrations */
203 if(tacYNaNs(&tac, -1)>0) {
204 fprintf(stderr, "Error: missing concentrations.\n");
205 tacFree(&tac); return(2);
206 }
207 if(tac.sampleNr<3) {
208 fprintf(stderr, "Error: too few samples in plasma data.\n");
209 tacFree(&tac); return(2);
210 }
211 if(tac.tacNr>1) {
212 fprintf(stderr, "Warning: only first TAC in %s is used.\n", tacfile);
213 tac.tacNr=1;
214 }
215 if(tacSortByTime(&tac, &status)!=TPCERROR_OK) {
216 fprintf(stderr, "Error: invalid sample times.\n");
217 tacFree(&tac); return(2);
218 }
219 if(tac.isframe!=0 && verbose>0) {
220 if(tacSetX(&tac, &status)!=TPCERROR_OK) { // make sure that frame middle times are set
221 fprintf(stderr, "Error: invalid sample times.\n");
222 tacFree(&tac); return(2);
223 }
224 fprintf(stderr, "Warning: frame durations are ignored.\n");
225 }
226 if(interval>0.1*tac.x[tac.sampleNr-1]) {
227 fprintf(stderr, "Error: too long interval time.\n");
228 tacFree(&tac); return(2);
229 }
230 if(interval>0.01*tac.x[tac.sampleNr-1]) {
231 if(verbose>0) fprintf(stderr, "Warning: interval time may be too long.\n");
232 }
233
234
235 /*
236 * Interpolate data with even sample intervals for convolution
237 */
238 TAC itac; tacInit(&itac);
239 if(tacInterpolateToEqualLengthFrames(&tac, interval, interval, &itac, &status)!=TPCERROR_OK) {
240 fprintf(stderr, "Error: cannot interpolate data to even sample times.\n");
241 tacFree(&tac); return(3);
242 }
243 /* Get the sample interval in interpolated data */
244 double freq=itac.x2[0]-itac.x1[0];
245 if(verbose>1) {
246 printf("sample_intervals_in_convolution := %g\n", freq);
247 printf("interpolated data range: %g - %g\n", itac.x1[0], itac.x2[itac.sampleNr-1]);
248 }
249
250
251 if(verbose>0) fprintf(stdout, "allocate memory for kernel...\n");
252 double *kernel=(double*)malloc(2*itac.sampleNr*sizeof(double));
253 if(kernel==NULL) {
254 fprintf(stderr, "Error: out of memory.\n");
255 tacFree(&tac); tacFree(&itac); return(5);
256 }
257 double *cy=kernel+itac.sampleNr;
258
259
260 /*
261 * Calculate the response function for convolution
262 */
263 if(verbose>1) fprintf(stdout, "computing the kernel...\n");
264 double ksum=0.0;
265 if(verbose>6) printf("\nData\tKernel:\n");
266 for(int i=0; i<itac.sampleNr; i++) {
267 kernel[i] = (a1/b1) * (exp(b1*freq) - 1.0) * exp(-b1*(itac.x[i]+0.5*freq));
268 if(a2>0.0) {
269 kernel[i] += (a2/b2) * (exp(b2*freq) - 1.0) * exp(-b2*(itac.x[i]+0.5*freq));
270 if(a3>0.0) kernel[i] += (a3/b3) * (exp(b3*freq) - 1.0) * exp(-b3*(itac.x[i]+0.5*freq));
271 }
272 ksum+=kernel[i];
273 if(verbose>6) printf("%g\t%g\n", itac.c[0].y[i], kernel[i]);
274 }
275 if(verbose>2) printf("Sum of unscaled response function := %g\n", ksum);
276 if(!isnormal(ksum)) {
277 fprintf(stderr, "Error: invalid kernel contents.\n");
278 tacFree(&tac); tacFree(&itac); free(kernel); return(6);
279 }
280 /* If requested, scale the response function */
281 double sc=1.0;
282 if(auc>0.0) {
283 double hauc=a1/b1;
284 if(a2>0.0) hauc+=a2/b2;
285 if(a3>0.0) hauc+=a3/b3;
286 sc=auc/hauc;
287 } else if(h0>0.0) { // if both scalings are set then use only AUC because h(0) scaling would
288 // cancel out.
289 double asum=a1;
290 if(a2>0.0) asum+=a2;
291 if(a3>0.0) asum+=a3;
292 sc*=h0/asum;
293 }
294 if(sc>0.0 && sc!=1.0) {
295 ksum=0.0;
296 if(verbose>7) printf("\nData\tKernel:\n");
297 for(int i=0; i<itac.sampleNr; i++) {
298 kernel[i]*=sc;
299 ksum+=kernel[i];
300 if(verbose>7) printf("%g\t%g\n", itac.c[0].y[i], kernel[i]);
301 }
302 if(verbose>2) printf("Sum of scaled response function := %g\n", ksum);
303 }
304
305
306 /*
307 * Convolution
308 */
309 if(verbose>1) fprintf(stdout, "convolution...\n");
310 if(convolve1D(itac.c[0].y, itac.sampleNr, kernel, itac.sampleNr, cy)!=0) {
311 fprintf(stderr, "Error: cannot convolve the data.\n");
312 tacFree(&tac); tacFree(&itac); free(kernel); return(7);
313 }
314 if(verbose>4) {
315 printf("\nData x\ty\tKernel\tConvolved\n");
316 for(int i=0; i<itac.sampleNr; i++)
317 printf("%g\t%g\t%g\t%g\n", itac.x[i], itac.c[0].y[i], kernel[i], cy[i]);
318 }
319
320 /* Copy convoluted curve over interpolated curve */
321 for(int i=0; i<itac.sampleNr; i++) itac.c[0].y[i]=cy[i];
322 /* No need for kernel or working space */
323 free(kernel);
324
325
326 /*
327 * Interpolate convolved data into original sample times
328 */
329 if(verbose>1) fprintf(stdout, "interpolating to original sample times...\n");
330 int ret=0;
331 if(tac.isframe==0)
332 ret=liInterpolate(itac.x, itac.c[0].y, itac.sampleNr, tac.x, tac.c[0].y, NULL, NULL,
333 tac.sampleNr, 4, 1, verbose-10);
334 else
335 ret=liInterpolateForPET(itac.x, itac.c[0].y, itac.sampleNr, tac.x1, tac.x2, tac.c[0].y,
336 NULL, NULL, tac.sampleNr, 4, 1, verbose-10);
337 tacFree(&itac);
338 if(ret!=0) {
339 fprintf(stderr, "Error: cannot interpolate back.\n");
340 tacFree(&tac); return(9);
341 }
342
343
344 /*
345 * Write the file
346 */
347 if(verbose>1) printf("writing convolved data in %s\n", simfile);
348 FILE *fp; fp=fopen(simfile, "w");
349 if(fp==NULL) {
350 fprintf(stderr, "Error: cannot open file for writing (%s)\n", simfile);
351 tacFree(&tac); return(11);
352 }
353 ret=tacWrite(&tac, fp, TAC_FORMAT_UNKNOWN, 1, &status);
354 fclose(fp); tacFree(&tac);
355 if(ret!=TPCERROR_OK) {
356 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
357 return(12);
358 }
359 if(verbose>=0) printf("Convolved TAC saved in %s\n", simfile);
360
361 return(0);
362}
363/*****************************************************************************/
365/*****************************************************************************/
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 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
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 tacIsWeighted(TAC *tac)
Definition tacw.c:24
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.
@ 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.