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