TPCCLIB
Loading...
Searching...
No Matches
simcmdk.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 "tpcfileutil.h"
17#include "tpcift.h"
18#include "tpctac.h"
19#include "tpcpar.h"
20#include "tpctacmod.h"
21#include "tpccm.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "Simulation of PET tissue time-radioactivity concentration curve (TAC)",
27 "based on two-tissue compartment model. Vascular volume is not considered.",
28 " ",
29 " dC1(t)/dt = K1*Ca(t) - (k2+k3)*C1(t) + k2*C2(t)",
30 " dC2(t)/dt = k3*C1(t) - k4*C2(t)",
31 " Ct(t) = C1(t) + C2(t) ",
32 " ",
33 "Usage: @P [options] inputfile simfile",
34 " ",
35 "Options:",
36 " -fa | -fe",
37 " If input file contains frame start and end times, simulated TAC is",
38 " either saved with frame start and end times and frame concentration",
39 " average (option -fa, default), or with start/end times and concentrations",
40 " at those times (option -fe).",
41 " -sub",
42 " Save simulated C1 and C2 curves; by default only their sum is saved.",
43 " -stdoptions", // List standard options like --help, -v, etc
44 " ",
45 "Model parameters are specified as additional columns of the input file.",
46 "The first one or two columns must contain the times or frame start and end",
47 "times, and after that the input function concentrations.",
48 " ",
49 "See also: sim_3tcm, p2t_3c, tacadd, simframe, taccbv",
50 " ",
51 "Keywords: TAC, simulation, compartmental model",
52 0};
53/*****************************************************************************/
54
55/*****************************************************************************/
56/* Turn on the globbing of the command line, since it is disabled by default in
57 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
58 In Unix&Linux wildcard command line processing is enabled by default. */
59/*
60#undef _CRT_glob
61#define _CRT_glob -1
62*/
63int _dowildcard = -1;
64/*****************************************************************************/
65
66/*****************************************************************************/
70int main(int argc, char **argv)
71{
72 int ai, help=0, version=0, verbose=1;
73 char inpfile[FILENAME_MAX], simfile[FILENAME_MAX];
74 int framemode=0; // 0=frame start and end times with average concentration;
75 // 1=frame start/end times with concentration of the moment.
76 int savesub=0;
77
78
79
80
81 /*
82 * Get arguments
83 */
84 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
85 inpfile[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(strcasecmp(cptr, "FA")==0) {
91 framemode=0; continue;
92 } else if(strcasecmp(cptr, "FE")==0) {
93 framemode=1; continue;
94 } else if(strcasecmp(cptr, "SUB")==0) {
95 savesub=1; 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(inpfile, argv[ai++], FILENAME_MAX);
112 if(ai<argc) strlcpy(simfile, argv[ai++], FILENAME_MAX);
113 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
114
115 /* Is something missing? */
116 if(!simfile[0]) {fprintf(stderr, "Error: missing file name.\n"); return(1);}
117
118
119 /* In verbose mode print arguments and options */
120 if(verbose>1) {
121 printf("inpfile := %s\n", inpfile);
122 printf("simfile := %s\n", simfile);
123 printf("framemode := %d\n", framemode);
124 printf("savesub := %d\n", savesub);
125 fflush(stdout);
126 }
127
128
129
130 /*
131 * Read input TAC
132 */
133 if(verbose>1) fprintf(stdout, "reading input TAC\n");
134 TAC tac; tacInit(&tac);
135 if(tacRead(&tac, inpfile, &status)!=TPCERROR_OK) {
136 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
137 tacFree(&tac); return(2);
138 }
139 if(verbose>2) {
140 printf("fileformat := %s\n", tacFormattxt(tac.format));
141 printf("tacNr := %d\n", tac.tacNr);
142 printf("sampleNr := %d\n", tac.sampleNr);
143 printf("frames := %d\n", tac.isframe);
144 printf("xunit := %s\n", unitName(tac.tunit));
145 printf("yunit := %s\n", unitName(tac.cunit));
146 }
147 if(tac.sampleNr<3) {
148 fprintf(stderr, "Error: too few samples in input TAC.\n");
149 tacFree(&tac); return(2);
150 }
151 if(tac.tacNr<2) {
152 fprintf(stderr, "Error: model parameters missing from input TAC.\n");
153 tacFree(&tac); return(2);
154 }
155 /* Check NaNs */
156 if(tacNaNs(&tac)>0) {
157 fprintf(stderr, "Error: missing values in %s.\n", inpfile);
158 tacFree(&tac); return(2);
159 }
160 /* Sort data by sample time */
161 tacSortByTime(&tac, &status);
162 /* Fix small frame gaps and overlaps */
163 if(tac.isframe==1) {
164 if(tacCorrectFrameOverlap(&tac, &status)!=TPCERROR_OK) {
165 fprintf(stderr, "Error: times frames not consecutive in %s.\n", inpfile);
166 tacFree(&tac); return(2);
167 }
168 }
169
170
171 /*
172 * Find the model parameter columns
173 */
174 int ik1, ik2, ik3, ik4;
175 ik1=ik2=ik3=ik4=0;
176 if(tacSelectTACs(&tac, "K1", 1, NULL)==1) ik1=tacFirstSelected(&tac);
177 if(tacSelectTACs(&tac, "k2", 1, NULL)==1) ik2=tacFirstSelected(&tac);
178 if(tacSelectTACs(&tac, "k3", 1, NULL)==1) ik3=tacFirstSelected(&tac);
179 if(tacSelectTACs(&tac, "k4", 1, NULL)==1) ik4=tacFirstSelected(&tac);
180 if(ik1==0) {
181 fprintf(stderr, "Error: missing K1 in %s.\n", inpfile);
182 tacFree(&tac); return(3);
183 }
184 if(ik3==0) ik4=0;
185 if(verbose>1) {
186 printf("K1 column := %d\n", 1+ik1);
187 if(ik2>0) printf("k2 column := %d\n", 1+ik2);
188 if(ik3>0) printf("k3 column := %d\n", 1+ik3);
189 if(ik4>0) printf("k4 column := %d\n", 1+ik4);
190 }
191
192 /* Allocate space for simulated data */
193 if((status.error=tacAllocateMore(&tac, 3))!=TPCERROR_OK) {
194 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
195 tacFree(&tac); return(3);
196 }
197 int iCt=tac.tacNr;
198 int iC1=tac.tacNr+1;
199 int iC2=tac.tacNr+2;
200 tac.tacNr+=3;
201 strcpy(tac.c[iCt].name, "Ct");
202 strcpy(tac.c[iC1].name, "C1");
203 strcpy(tac.c[iC2].name, "C2");
204
205
206 /*
207 * Simulate
208 */
209 if(tac.isframe==0) {
210 for(int i=0; i<tac.sampleNr; i++) {
211 double K1=0.0, k2=0.0, k3=0.0, k4=0.0;
212 if(i==0) {
213 tac.c[iCt].y[0]=tac.c[iC1].y[0]=tac.c[iC2].y[0]=0.0;
214 continue;
215 }
216 K1=tac.c[ik1].y[i-1];
217 if(ik2>0) k2=tac.c[ik2].y[i-1];
218 if(ik3>0) k3=tac.c[ik3].y[i-1];
219 if(ik4>0) k4=tac.c[ik4].y[i-1];
220 double dt=(tac.x[i]-tac.x[i-1]);
221 if(!(dt>0.0)) {
222 tac.c[iCt].y[i]=tac.c[iCt].y[i-1];
223 tac.c[iC1].y[i]=tac.c[iC1].y[i-1];
224 tac.c[iC2].y[i]=tac.c[iC2].y[i-1];
225 continue;
226 }
227 double dt2=0.5*dt;
228 tac.c[iC1].y[i]= (
229 K1*(1.0+k4*dt2)*dt2*(tac.c[0].y[i-1]+tac.c[0].y[i]) +
230 (1.0 - dt2*(k2+k3-k4+k2*k4*dt2))*tac.c[iC1].y[i-1] +
231 k4*dt*tac.c[iC2].y[i-1]
232 ) / (
233 1.0 + dt2*(k2+k3+k4+k2*k4*dt2)
234 );
235 tac.c[iC2].y[i]= (
236 k3*dt2*(tac.c[iC1].y[i-1]+tac.c[iC1].y[i]) + (1.0 - k4*dt2)*tac.c[iC2].y[i-1]
237 ) / (
238 1.0 + dt2*k4
239 );
240 tac.c[iCt].y[i]=tac.c[iC1].y[i]; if(finite(tac.c[iC2].y[i])) tac.c[iCt].y[i]+=tac.c[iC2].y[i];
241 if(verbose>3 && i==tac.sampleNr-1) printf("K1=%g k2=%g k3=%g k4=%g\n", K1, k2, k3, k4);
242 }
243 } else {
244 for(int i=0; i<tac.sampleNr; i++) {
245 double K1=0.0, k2=0.0, k3=0.0, k4=0.0;
246 K1=tac.c[ik1].y[i];
247 if(ik2>0) k2=tac.c[ik2].y[i];
248 if(ik3>0) k3=tac.c[ik3].y[i];
249 if(ik4>0) k4=tac.c[ik4].y[i];
250 double dt=tac.x2[i]-tac.x1[i];
251 if(!(dt>0.0)) {
252 if(i==0) {
253 tac.c[iCt].y[0]=tac.c[iC1].y[0]=tac.c[iC2].y[0]=0.0;
254 } else {
255 tac.c[iCt].y[i]=tac.c[iCt].y[i-1];
256 tac.c[iC1].y[i]=tac.c[iC1].y[i-1];
257 tac.c[iC2].y[i]=tac.c[iC2].y[i-1];
258 }
259 continue;
260 }
261 double dt2=0.5*dt;
262 if(i==0) {
263 tac.c[iC1].y[i]= (
264 K1*(1.0+k4*dt2)*dt*tac.c[0].y[i]
265 ) / (
266 1.0 + dt2*(k2+k3+k4+k2*k4*dt2)
267 );
268 tac.c[iC2].y[i]= (
269 k3*dt2*(tac.c[iC1].y[i])
270 ) / (
271 1.0 + dt2*k4
272 );
273 } else {
274 tac.c[iC1].y[i]= (
275 K1*(1.0+k4*dt2)*dt*tac.c[0].y[i] +
276 (1.0 - dt2*(k2+k3-k4+k2*k4*dt2))*tac.c[iC1].y[i-1] +
277 k4*dt*tac.c[iC2].y[i-1]
278 ) / (
279 1.0 + dt2*(k2+k3+k4+k2*k4*dt2)
280 );
281 tac.c[iC2].y[i]= (
282 k3*dt2*(tac.c[iC1].y[i-1]+tac.c[iC1].y[i]) + (1.0 - k4*dt2)*tac.c[iC2].y[i-1]
283 ) / (
284 1.0 + dt2*k4
285 );
286 }
287 tac.c[iCt].y[i]=tac.c[iC1].y[i]; if(finite(tac.c[iC2].y[i])) tac.c[iCt].y[i]+=tac.c[iC2].y[i];
288 if(verbose>3 && i==tac.sampleNr-1) printf("K1=%g k2=%g k3=%g k4=%g\n", K1, k2, k3, k4);
289 }
290 }
291 if(tac.isframe) {
292 if(framemode==0) {
293 /* Calculate frame averages, if requested */
294 for(int i=tac.sampleNr-1; i>0; i--) {
295 tac.c[iCt].y[i]=0.5*(tac.c[iCt].y[i-1]+tac.c[iCt].y[i]);
296 tac.c[iC1].y[i]=0.5*(tac.c[iC1].y[i-1]+tac.c[iC1].y[i]);
297 tac.c[iC2].y[i]=0.5*(tac.c[iC2].y[i-1]+tac.c[iC2].y[i]);
298 }
299 tac.c[iCt].y[0]*=0.5;
300 tac.c[iC1].y[0]*=0.5;
301 tac.c[iC2].y[0]*=0.5;
302 } else {
303 /* Save concentrations at frame end times, like they were computed,
304 and set the times to match that. */
305 for(int i=0; i<tac.sampleNr; i++) tac.x[i]=tac.x2[i];
306 tac.isframe=0;
307 /* Add also the start time of the first frame. Function tacAddZeroSample() does not add zero
308 time, in the unlikely case it already exists, and in that case we are better of without. */
309 int prevNr=tac.sampleNr;
310 tacAddZeroSample(&tac, &status);
311 if(tac.sampleNr>prevNr) tac.x[0]=tac.x1[1];
312 }
313 }
314
315
316 /*
317 * Save simulated data
318 */
319 if(verbose>1) printf("writing %s\n", simfile);
320 tacMoveTACC(&tac, iCt, 0);
321 tac.tacNr=1;
322 if(savesub) {
323 tacMoveTACC(&tac, iC1, 1);
324 tacMoveTACC(&tac, iC2, 2);
325 tac.tacNr=3;
326 }
327 FILE *fp; fp=fopen(simfile, "w");
328 if(fp==NULL) {
329 fprintf(stderr, "Error: cannot open file for writing (%s)\n", simfile);
330 tacFree(&tac); return(11);
331 }
332 tacWrite(&tac, fp, TAC_FORMAT_PMOD, 1, &status);
333 fclose(fp); tacFree(&tac);
334 if(status.error!=TPCERROR_OK) {
335 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
336 return(12);
337 }
338 if(verbose>=0) {printf("%s saved.\n", simfile); fflush(stdout);}
339
340 return(0);
341}
342/*****************************************************************************/
343
344/*****************************************************************************/
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 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 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 tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacMoveTACC(TAC *d, int from, int to)
Definition tacorder.c:250
int tacSelectTACs(TAC *d, const char *region_name, int reset, TPCSTATUS *status)
Definition tacselect.c:24
int tacFirstSelected(TAC *d)
Definition tacselect.c:122
int tacCorrectFrameOverlap(TAC *d, TPCSTATUS *status)
Correct PET frame start and end times if frames are slightly overlapping or have small gaps in betwee...
Definition tacx.c:65
int tacAddZeroSample(TAC *d, TPCSTATUS *status)
Add an initial sample to TAC(s) with zero time and concentration.
Definition tacx.c:366
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 libtpcfileutil.
Header file for library libtpcift.
Header file for libtpcpar.
Header file for library libtpctac.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Definition tpctac.h:33
Header file for libtpctacmod.