TPCCLIB
Loading...
Searching...
No Matches
fvar4tac.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 "tpcrand.h"
21/*****************************************************************************/
22
23/*****************************************************************************/
24static char *info[] = {
25 "Program for adding Gaussian noise to dynamic PET time-activity curve (TAC)",
26 "or TACs using equations (1, 2):",
27 " SD(t) = TAC(t) * sqrt(Pc/(TAC(t)*exp(-lambda*t)*deltat)",
28 " TAC_noisy(t) = TAC(t) + SD(t)*G(0,1) ,",
29 "where Pc is the proportionality constant that determines the level of noise,",
30 "TAC(t) is the mean activity concentration in the image frame,",
31 "Deltat is the scan frame length, and G(0,1) is a pseudo random number from",
32 "a Gaussian distribution with zero mean and SD of one.",
33 " ",
34 "Usage: @P [Options] tacfile Pc isotope outputfile ",
35 " ",
36 "Options:",
37 " -minsd=<SD>",
38 " Set a minimum SD to add a minimum level of noise also to the time",
39 " frames with little or no activity.",
40 " -R=<nr of repeats>",
41 " Specified number of output files (*_NNNN.*) with different set of",
42 " noise are created.",
43 " -common=<y|N>",
44 " Common noise SD based on TAC mean is used (y), or SD is based on each TAC",
45 " separately (n, default).",
46 " -sd or -cv",
47 " Noise is not added to TAC but calculated values of SD or CV (for noise)",
48 " are saved instead in the output file.",
49 " -sec",
50 " If datafile does not contain time unit, times are by default assumed to",
51 " be in minutes. Use this option to set time unit to sec.",
52// " -seed=<seed for random number generator>",
53// " Computer clock and other information is used by default.",
54 " -stdoptions", // List standard options like --help, -v, etc
55 " ",
56 "TAC data must contain frame start and end times (PMOD or DFT format).",
57 "TACs are assumed to be decay corrected to zero time.",
58 "Accepted isotope codes include at least O-15, C-11, F-18, Ga-68, N-13, Br-76,",
59 "Rb-82, and Cu-62.",
60 " ",
61 "Example:",
62 " @P simulated.tac 5.0 C-11 noisy.tac",
63 " ",
64 "References:",
65 "1. Chen K, Huang SC, Yu DC. Phys Med Biol 1991;36:1183-1200.",
66 "2. Varga J, Szabo Z. J Cereb Blood Flow Metab 2002;22:240-244.",
67 " ",
68 "See also: fvar4img, var4tac, sim_3tcm, simframe, tacadd, avgttac",
69 " ",
70 "Keywords: TAC, simulation, noise",
71 0};
72/*****************************************************************************/
73
74/*****************************************************************************/
75/* Turn on the globbing of the command line, since it is disabled by default in
76 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
77 In Unix&Linux wildcard command line processing is enabled by default. */
78/*
79#undef _CRT_glob
80#define _CRT_glob -1
81*/
82int _dowildcard = -1;
83/*****************************************************************************/
84
85/*****************************************************************************/
89int main(int argc, char **argv)
90{
91 int ai, help=0, version=0, verbose=1;
92 char tacfile[FILENAME_MAX], simfile[FILENAME_MAX];
93 int mode=0; // 0=add noise, 1=save SD curve, 2=save CV curve
94 unsigned int repeatNr=1;
95 int commonSD=0;
96 int tunit=UNIT_MIN;
97 long int seed=0L;
98 double noiseLevel=nan(""), minsd=nan("");
100
101
102
103 /*
104 * Get arguments
105 */
106 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
107 tacfile[0]=simfile[0]=(char)0;
108 /* Options */
109 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
110 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
111 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
112 if(strncasecmp(cptr, "SEED=", 5)==0) {
113 seed=atol(cptr+5); if(seed>0L) continue;
114 } else if(strcasecmp(cptr, "SD")==0 || strcasecmp(cptr, "STD")==0) {
115 mode=1; continue;
116 } else if(strcasecmp(cptr, "CV")==0) {
117 mode=2; continue;
118 } else if(strcasecmp(cptr, "SEC")==0) {
119 tunit=UNIT_SEC; continue;
120 } else if(strncasecmp(cptr, "R=", 2)==0) {
121 int rn=atoi(cptr+2); if(rn>0) {repeatNr=(unsigned int)rn; continue;}
122 } else if(strncasecmp(cptr, "MINSD=", 6)==0) {
123 minsd=atofVerified(cptr+6); if(!isnan(minsd)) continue;
124 } else if(strncasecmp(cptr, "COMMON=", 7)==0) {
125 if(strncasecmp(cptr+7, "YES", 1)==0) {commonSD=1; continue;}
126 if(strncasecmp(cptr+7, "NO", 1)==0) {commonSD=0; continue;}
127 }
128 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
129 return(1);
130 } else break; // tac name argument may start with '-'
131
132 TPCSTATUS status; statusInit(&status);
133 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
134 status.verbose=verbose-1;
135
136 /* Print help or version? */
137 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
138 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
139 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
140
141 /* Process other arguments, starting from the first non-option */
142 if(ai<argc) {strlcpy(tacfile, argv[ai++], FILENAME_MAX);}
143 if(ai<argc) {
144 noiseLevel=atofVerified(argv[ai]);
145 if(isnan(noiseLevel) || noiseLevel<0.0) {
146 fprintf(stderr, "Error: invalid noise level (Pc) '%s'.\n", argv[ai]); return(1);}
147 if(noiseLevel<1.0E-22) fprintf(stderr, "Warning: noise level is zero.\n");
148 ai++;
149 }
150 if(ai<argc) {
151 isotope=isotopeIdentify(argv[ai]);
153 fprintf(stderr, "Error: invalid isotope '%s'\n", argv[ai]); return(1);}
154 ai++;
155 }
156 if(ai<argc) {strlcpy(simfile, argv[ai++], FILENAME_MAX);}
157 if(ai<argc) {
158 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
159 return(1);
160 }
161
162 /* Is something missing? */
163 if(!simfile[0]) {
164 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
165 return(1);
166 }
167 /* or otherwise wrong? */
168 if(repeatNr>9999) {fprintf(stderr, "Error: too many repeats.\n"); return(1);}
169 if(mode!=0 && repeatNr>1) {
170 fprintf(stderr, "Error: do not use -R when calculating SD or CV curves.\n"); return(1);
171 }
172
173 /* In verbose mode print arguments and options */
174 if(verbose>1) {
175 printf("tacfile := %s\n", tacfile);
176 printf("simfile := %s\n", simfile);
177 printf("proportionality_constant := %g\n", noiseLevel);
178 printf("isotope := %s\n", isotopeName(isotope));
179 if(!isnan(minsd)) printf("minsd := %g\n", minsd);
180 printf("commonSD := %d\n", commonSD);
181 printf("backup_tunit := %s\n", unitName(tunit));
182 printf("mode := %d\n", mode);
183 printf("repeatNr := %u\n", repeatNr);
184 }
185
186
187 /*
188 * Read original TAC
189 */
190 if(verbose>1) fprintf(stdout, "reading %s\n", tacfile);
191 TAC tac; tacInit(&tac);
192 if(tacRead(&tac, tacfile, &status)!=TPCERROR_OK) {
193 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), tacfile);
194 tacFree(&tac); return(2);
195 }
196 if(verbose>2) {
197 printf("fileformat := %s\n", tacFormattxt(tac.format));
198 printf("tacNr := %d\n", tac.tacNr);
199 printf("sampleNr := %d\n", tac.sampleNr);
200 printf("xunit := %s\n", unitName(tac.tunit));
201 printf("yunit := %s\n", unitName(tac.cunit));
202 }
203 /* Set time unit, if not set in the file */
204 if(tac.tunit==UNIT_UNKNOWN) {
205 tac.tunit=tunit;
206 if(verbose>0) printf("time unit set to %s\n", unitName(tac.tunit));
207 }
208 /* Check frame times */
209 if(tac.isframe==0) {
210 fprintf(stderr, "Error: missing frame lengths.\n");
211 tacFree(&tac); return(3);
212 }
213 if(tacXNaNs(&tac)>0) {
214 fprintf(stderr, "Error: missing frame times.\n");
215 tacFree(&tac); return(3);
216 }
217 /* Check for missing concentrations */
218 if(tacYNaNs(&tac, -1)>0) {
219 fprintf(stderr, "Error: missing concentrations.\n");
220 tacFree(&tac); return(3);
221 }
222 /* Mean TAC calculation is not necessary if only one TAC */
223 if(tac.tacNr==1 && commonSD!=0) {
224 if(verbose>0) fprintf(stderr, "Note: only one TAC in datafile.\n");
225 commonSD=0;
226 }
227
228
229 /*
230 * Make a copy of the data to be used as output data
231 */
232 TAC tac2; tacInit(&tac2);
233 if(tacDuplicate(&tac, &tac2)!=TPCERROR_OK) {
234 fprintf(stderr, "Error: cannot make output data.\n");
235 tacFree(&tac); return(4);
236 }
237 /* Turn weighting off */
239 if(verbose>10) {
240 printf("fileformat := %s\n", tacFormattxt(tac2.format));
241 printf("tacNr := %d\n", tac2.tacNr);
242 printf("sampleNr := %d\n", tac2.sampleNr);
243 printf("xunit := %s\n", unitName(tac2.tunit));
244 printf("yunit := %s\n", unitName(tac2.cunit));
245 }
246 /* Convert time units into minutes, only in original data for SD computation */
247 if(tacXUnitConvert(&tac, UNIT_MIN, &status)!=TPCERROR_OK) {
248 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
249 tacFree(&tac); tacFree(&tac2); return(5);
250 }
251 /* Calculate mean TAC, if necessary */
252 if(commonSD!=0) {
253 for(int fi=0; fi<tac.sampleNr; fi++) {
254 tac2.w[fi]=0.0;
255 for(int ri=0; ri<tac.tacNr; ri++) tac2.w[fi]+=tac.c[ri].y[fi];
256 tac2.w[fi]/=(double)tac.tacNr;
257 }
258 }
259
260 /* Compute SD for each sample as sample value in tac2 */
261 int errcount=0;
262 if(commonSD==0) {
263 for(int fi=0; fi<tac.sampleNr; fi++) {
264 for(int ri=0; ri<tac.tacNr; ri++) {
265 tac2.c[ri].y[fi]=
266 noiseSD4Frame(tac.c[ri].y[fi], tac.x1[fi], tac.x2[fi]-tac.x1[fi], isotope, noiseLevel);
267 if(isnan(tac2.c[ri].y[fi])) errcount++;
268 }
269 }
270 } else {
271 for(int fi=0; fi<tac.sampleNr; fi++) {
272 tac2.w[fi]=noiseSD4Frame(tac2.w[fi], tac.x1[fi], tac.x2[fi]-tac.x1[fi], isotope, noiseLevel);
273 if(isnan(tac2.w[fi])) errcount++;
274 }
275 for(int fi=0; fi<tac.sampleNr; fi++)
276 for(int ri=0; ri<tac.tacNr; ri++)
277 tac2.c[ri].y[fi]=tac2.w[fi];
278 }
279 if(errcount>0) {
280 fprintf(stderr, "Error: cannot calculate SD from the data.\n");
281 tacFree(&tac); tacFree(&tac2); return(6);
282 }
283
284 /* Set min SD, if specified by user */
285 if(!isnan(minsd) && minsd>0.0) {
286 for(int fi=0; fi<tac.sampleNr; fi++)
287 for(int ri=0; ri<tac.tacNr; ri++)
288 if(minsd>tac2.c[ri].y[fi]) tac2.c[ri].y[fi]=minsd;
289 }
290
291
292 /* If user wanted to save the SDs then this is it */
293 if(mode==1) {
294 if(verbose>1) printf("writing SDs in %s\n", simfile);
295 FILE *fp; fp=fopen(simfile, "w");
296 if(fp==NULL) {
297 fprintf(stderr, "Error: cannot open file for writing (%s)\n", simfile);
298 tacFree(&tac); tacFree(&tac2); return(11);
299 }
300 int ret=tacWrite(&tac2, fp, TAC_FORMAT_UNKNOWN, 1, &status);
301 fclose(fp); tacFree(&tac); tacFree(&tac2);
302 if(ret!=TPCERROR_OK) {
303 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
304 return(12);
305 }
306 if(verbose>=0) printf("SDs saved in %s\n", simfile);
307 return(0);
308 }
309
310 /* If user wanted to save the CVs then divide SDs by concentrations and save */
311 if(mode==2) {
312 if(verbose>1) printf("calculating CVs\n");
313 for(int fi=0; fi<tac.sampleNr; fi++)
314 for(int ri=0; ri<tac.tacNr; ri++) {
315 tac2.c[ri].y[fi]/=tac.c[ri].y[fi];
316 if(!isfinite(tac2.c[ri].y[fi])) tac2.c[ri].y[fi]=0.0;
317 }
318 if(verbose>1) printf("writing CVs in %s\n", simfile);
319 FILE *fp; fp=fopen(simfile, "w");
320 if(fp==NULL) {
321 fprintf(stderr, "Error: cannot open file for writing (%s)\n", simfile);
322 tacFree(&tac); tacFree(&tac2); return(13);
323 }
324 int ret=tacWrite(&tac2, fp, TAC_FORMAT_UNKNOWN, 1, &status);
325 fclose(fp); tacFree(&tac); tacFree(&tac2);
326 if(ret!=TPCERROR_OK) {
327 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
328 return(14);
329 }
330 if(verbose>=0) printf("CVs saved in %s\n", simfile);
331 return(0);
332 }
333
334
335 /*
336 * Add noise
337 */
338 MERTWI mt; mertwiInit(&mt);
339 if(seed>0) mertwiInitWithSeed64(&mt, (uint64_t)seed);
341
342 if(repeatNr==1) {
343 if(verbose>1) printf("adding noise\n");
344 /* Simply add noise to each concentration */
345 for(int fi=0; fi<tac.sampleNr; fi++)
346 for(int ri=0; ri<tac.tacNr; ri++)
347 tac2.c[ri].y[fi] = tac.c[ri].y[fi] + tac2.c[ri].y[fi]*mertwiRandomGaussian(&mt);
348 if(verbose>1) printf("writing noisy data in %s\n", simfile);
349 FILE *fp; fp=fopen(simfile, "w");
350 if(fp==NULL) {
351 fprintf(stderr, "Error: cannot open file for writing (%s)\n", simfile);
352 tacFree(&tac); tacFree(&tac2); return(15);
353 }
354 if(verbose>9) {
355 printf("fileformat := %s\n", tacFormattxt(tac2.format));
356 printf("tacNr := %d\n", tac2.tacNr);
357 printf("sampleNr := %d\n", tac2.sampleNr);
358 printf("xunit := %s\n", unitName(tac2.tunit));
359 printf("yunit := %s\n", unitName(tac2.cunit));
360 }
361 int ret=tacWrite(&tac2, fp, TAC_FORMAT_UNKNOWN, 1, &status);
362 fclose(fp); tacFree(&tac); tacFree(&tac2);
363 if(ret!=TPCERROR_OK) {
364 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
365 return(16);
366 }
367 if(verbose>=0) printf("Noisy data saved in %s\n", simfile);
368 return(0);
369 }
370
371 /*
372 * Create required number of noisy curves from each TAC
373 */
374 if(verbose>0 && repeatNr>10) printf("making %d TAC files with Gaussian noise\n", repeatNr);
375 /* Get the basis of file name and extension */
376 char basisname[FILENAME_MAX], fnextens[FILENAME_MAX];
377 strcpy(basisname, simfile); filenameRmExtensions(basisname);
378 strcpy(fnextens, filenameGetExtensions(simfile));
379 if(verbose>2) {
380 printf("basis_of_filename := %s\n", basisname);
381 printf("extensions := %s\n", fnextens);
382 }
383 if(strlen(basisname)<1 || ((5+strlen(basisname)+strlen(fnextens))>=FILENAME_MAX)) {
384 fprintf(stderr, "Error: invalid output file name.\n");
385 tacFree(&tac); tacFree(&tac2); return(1);
386 }
387 /* Make a copy of the SDs */
388 TAC sd; tacInit(&sd);
389 if(tacDuplicate(&tac2, &sd)!=TPCERROR_OK) {
390 fprintf(stderr, "Error: cannot make copy of SDs.\n");
391 tacFree(&tac); tacFree(&tac2); return(4);
392 }
393 /* Make repeatNr noisy datasets */
394 while(repeatNr>0) {
395 /* Make output file name */
396 snprintf(simfile, FILENAME_MAX, "%s_%04u%s", basisname, repeatNr, fnextens);
397 if(verbose>2) printf(" %s\n", simfile);
398 else if(verbose>0) {fprintf(stdout, "."); fflush(stdout);}
399 /* Simulate noise */
400 for(int fi=0; fi<tac.sampleNr; fi++)
401 for(int ri=0; ri<tac.tacNr; ri++)
402 tac2.c[ri].y[fi] = tac.c[ri].y[fi] + sd.c[ri].y[fi]*mertwiRandomGaussian(&mt);
403 /* Save data */
404 FILE *fp; fp=fopen(simfile, "w");
405 if(fp==NULL) {
406 fprintf(stderr, "\nError: cannot open file for writing (%s)\n", simfile);
407 tacFree(&tac); tacFree(&tac2); tacFree(&sd); return(17);
408 }
409 int ret=tacWrite(&tac2, fp, TAC_FORMAT_UNKNOWN, 1, &status);
410 fclose(fp);
411 if(ret!=TPCERROR_OK) {
412 fprintf(stderr, "\nError (%d): %s\n", ret, errorMsg(status.error));
413 tacFree(&tac); tacFree(&tac2); tacFree(&sd); return(18);
414 }
415 repeatNr--;
416 } /* next file */
417 if(verbose>2) printf("done.\n"); else if(verbose>0) fprintf(stdout, "\n");
418 fflush(stdout);
419 tacFree(&tac); tacFree(&tac2); tacFree(&sd);
420
421 return(0);
422}
423/*****************************************************************************/
425/*****************************************************************************/
double atofVerified(const char *s)
Definition decpoint.c:75
char * filenameGetExtensions(const char *s)
Get all extensions of a file name.
Definition filename.c:203
void filenameRmExtensions(char *s)
Definition filename.c:89
char * isotopeName(int isotope_code)
Definition isotope.c:101
int isotopeIdentify(const char *isotope)
Definition isotope.c:145
void mertwiInitWithSeed64(MERTWI *mt, uint64_t seed)
Initialize the state vector mt[] inside data structure for Mersenne Twister MT19937 pseudo-random num...
Definition mertwi.c:94
double mertwiRandomGaussian(MERTWI *mt)
Generate a 64-bit double precision floating point pseudo-random number with normal (Gaussian) distrib...
Definition mertwi.c:354
uint64_t mertwiSeed64(void)
Make uint64_t seed for pseudo-random number generators.
Definition mertwi.c:76
void mertwiInit(MERTWI *mt)
Definition mertwi.c:28
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
double noiseSD4Frame(double y, double t1, double dt, int isotope, double a)
Definition ranoise.c:26
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
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
double * w
Definition tpctac.h:111
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
weights weighting
Definition tpctac.h:115
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
int tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
Definition tac.c:356
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 tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
Header file for library libtpcextensions.
@ WEIGHTING_OFF
Not weighted or weights not available (weights for all included samples are 1.0).
@ UNIT_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_SEC
seconds
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
isotope
Definition tpcisotope.h:50
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for libtpcrand.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28