TPCCLIB
Loading...
Searching...
No Matches
taccalc.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15/*****************************************************************************/
16#include "tpcextensions.h"
17#include "tpcift.h"
18#include "tpctac.h"
19#include "tpcli.h"
20/*****************************************************************************/
21
22/*****************************************************************************/
23static char *info[] = {
24 "Make arithmetic calculations with PET time-activity data.",
25 "Operations can be made between two TAC files or between one TAC file and",
26 "a constant value.",
27 " ",
28 "Usage: @P [options] file1 operation constant|file2 outputfile",
29 " ",
30 "Options:",
31 " --force",
32 " Program does not mind if the time or calibration units",
33 " cannot be converted to match.",
34 " -stdoptions", // List standard options like --help, -v, etc
35 " ",
36 "Example 1: Add constant 2 to all activity concentrations in pet.dat",
37 " @P pet.dat + 2 sum.dat",
38 "Example 2: Subtract one TAC from all TACs in the first data file",
39 " @P cortex.dat - reference.dat bound.dat",
40 " ",
41 "The first TAC file can have one or more TACs. Second TAC file may",
42 "contain either only one TAC or equally many TACs as the first file",
43 "(if the TAC number is different, only the first TAC is used).",
44 "Data is interpolated, if necessary, to the times of the first file.",
45 "If the second file contains only one sample time (frame), the value",
46 "from that sample will be used for all the samples of the first file.",
47 " ",
48 "The following characters are accepted as operators: +, -, x, and :.",
49 " ",
50 "See also: tacunit, metabcor, dftsuv, dftratio, taccbv, fit2dat, tacinv",
51 " ",
52 "Keywords: TAC, modelling, simulation, tool",
53 0};
54/*****************************************************************************/
55
56/*****************************************************************************/
57/* Turn on the globbing of the command line, since it is disabled by default in
58 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
59 In Unix&Linux wildcard command line processing is enabled by default. */
60/*
61#undef _CRT_glob
62#define _CRT_glob -1
63*/
64int _dowildcard = -1;
65/*****************************************************************************/
66
67/*****************************************************************************/
71int main(int argc, char **argv)
72{
73 int ai, help=0, version=0, verbose=1;
74 int ret;
75 int operation=0; // 1=+, 2=-, 3=*, 4=:
76 int isvalue=0;
77 int checkUnits=1;
78 char *cptr, d1file[FILENAME_MAX], d2file[FILENAME_MAX], rfile[FILENAME_MAX];
79 double value=0.0;
80 TAC tac1, tac2, itac2;
81
82
83 /*
84 * Get arguments
85 */
86 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
87 tacInit(&tac1); tacInit(&tac2); tacInit(&itac2);
88 d1file[0]=d2file[0]=rfile[0]=(char)0;
89 /* Options */
90 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
91 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
92 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
93 if(strcasecmp(cptr, "F")==0 || strcasecmp(cptr, "FORCE")==0) {
94 checkUnits=0; continue;
95 } else if(strcasecmp(cptr, "NT")==0) {
96 // just for compatibility with prev version
97 checkUnits=0; continue;
98 }
99 fprintf(stderr, "Error: invalid option '%s'\n", argv[ai]);
100 return(1);
101 } else break; // tac name argument may start with '-'
102
103 TPCSTATUS status; statusInit(&status);
104 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
105 status.verbose=verbose-1;
106
107 /* Print help or version? */
108 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
109 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
110 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
111
112 /* Arguments */
113 for(; ai<argc; ai++) {
114 if(verbose>3) printf("argv[%d] := '%s'\n", ai, argv[ai]);
115 if(!d1file[0]) {
116 strcpy(d1file, argv[ai]); continue;
117 } else if(operation==0) {
118 if(strcasecmp(argv[ai], "DIV")==0) {operation=4; continue;}
119 switch(*argv[ai]) {
120 case '+' : operation=1; break;
121 case '-' : operation=2; break;
122 case '*' : // this might be hard to use, but accept it anyway
123 case '.' :
124 case 'X' :
125 case 'x' : operation=3; break;
126 case '/' : // MinGW does not work with '/', it requires '//'
127 case ':' : operation=4; break;
128 }
129 if(strlen(argv[ai])>1 || operation==0) {
130 fprintf(stderr, "Error: invalid operator.\n");
131 return(1);
132 }
133 continue;
134 } else if(isvalue==0 && !d2file[0]) {
135 ret=atofCheck(argv[ai], &value);
136 if(ret==0) {isvalue=1; continue;}
137 else strcpy(d2file, argv[ai]);
138 continue;
139 } else if(!rfile[0]) {
140 strcpy(rfile, argv[ai]); continue;
141 }
142 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
143 return(1);
144 }
145
146 /* Is something missing? */
147 if(!rfile[0]) {tpcPrintUsage(argv[0], info, stdout); return(1);}
148
149
150 /* In verbose mode print arguments and options */
151 if(verbose>1) {
152 for(ai=0; ai<argc; ai++) printf("%s ", argv[ai]);
153 printf("\n");
154 printf("d1file := %s\n", d1file);
155 printf("operation := %d\n", operation);
156 if(isvalue) printf("value := %g\n", value);
157 else printf("d2file := %s\n", d2file);
158 printf("rfile := %s\n", rfile);
159 printf("checkUnits = %d\n", checkUnits);
160 fflush(stdout);
161 }
162
163
164 /*
165 * Read the file #1
166 */
167 if(verbose>1) printf("reading %s\n", d1file);
168 ret=tacRead(&tac1, d1file, &status);
169 if(ret!=TPCERROR_OK) {
170 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
171 tacFree(&tac1); return(2);
172 }
173 if(verbose>2) {
174 printf("fileformat := %s\n", tacFormattxt(tac1.format));
175 printf("tacNr := %d\n", tac1.tacNr);
176 printf("sampleNr := %d\n", tac1.sampleNr);
177 printf("xunit := %s\n", unitName(tac1.tunit));
178 printf("yunit := %s\n", unitName(tac1.cunit));
179 }
180
181
182 /*
183 * Read data file #2, if necessary
184 */
185 if(isvalue==0 && d2file[0]) {
186
187 if(verbose>1) printf("reading %s\n", d1file);
188 ret=tacRead(&tac2, d2file, &status);
189 if(ret!=TPCERROR_OK) {
190 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
191 tacFree(&tac1); tacFree(&tac2); return(3);
192 }
193 if(verbose>2) {
194 printf("fileformat := %s\n", tacFormattxt(tac2.format));
195 printf("tacNr := %d\n", tac2.tacNr);
196 printf("sampleNr := %d\n", tac2.sampleNr);
197 printf("xunit := %s\n", unitName(tac2.tunit));
198 printf("yunit := %s\n", unitName(tac2.cunit));
199 }
200 /* If file contains only one sample, then use that as a constant */
201 if(tac2.sampleNr==1 && tac2.tacNr==1) {
202 if(verbose>2) printf("file #2 contains the constant.\n");
203 /* Convert conc units, if necessary and possible */
204 ret=tacYUnitConvert(&tac2, tac1.cunit, &status);
205 if(ret!=TPCERROR_OK && verbose>1) {
206 fprintf(stderr, "Note: %s and %s have different or unknown concentration units.\n",
207 d1file, d2file);
208 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
209 }
210 /* Error does not matter, if user told so */
211 if(!checkUnits) ret=TPCERROR_OK;
212 /* Neither does it matter if unitless, and multiplication or division */
213 if(tac2.cunit==UNIT_UNITLESS && (operation==3 || operation==4)) ret=TPCERROR_OK;
214 /* If none of the previous excuses can be accepted, then exit */
215 if(ret!=TPCERROR_OK) {
216 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
217 tacFree(&tac1); tacFree(&tac2); return(3);
218 }
219 isvalue=1;
220 value=tac2.c[0].y[0];
221 if(verbose>1) printf("using %g as the constant value.\n", value);
222 tacFree(&tac2);
223 }
224 }
225
226 /* Continue processing data # 2, if necessary */
227 if(isvalue==0) {
228
229 /* Check if the TAC numbers are different */
230 if(tac1.tacNr!=tac2.tacNr) {
231 if(tac2.sampleNr<2) {
232 /* Different TAC nr and only one sample in data #2; probably user
233 gave files in wrong order or wrong files */
234 fprintf(stderr, "Error: cannot operate the files.\n");
235 tacFree(&tac1); tacFree(&tac2); return(4);
236 }
237 /* We have >1 samples in data #2, that seems fair */
238 if(tac2.tacNr>1) {
239 /* We will use just the first TAC in data #2; note the user only if
240 file #2 contains more than one TAC */
241 if(verbose>0) fprintf(stderr, "Note: using only the first TAC.\n");
242 tac2.tacNr=1;
243 }
244 }
245
246 /* Try to convert time units in data #2 to time units in data #1 */
247 ret=tacXUnitConvert(&tac2, tac1.tunit, &status);
248 if(ret!=TPCERROR_OK && verbose>1) {
249 fprintf(stderr, "Note: %s and %s have different or unknown time units.\n", d1file, d2file);
250 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
251 }
252 /* Error does not matter, if user told so */
253 if(!checkUnits) ret=TPCERROR_OK;
254 /* .. or if data #2 has only one sample time and data #1 has more */
255 if(tac1.tacNr>1 && tac2.tacNr==1) {
256 if(ret!=TPCERROR_OK && verbose>0) // but give a warning
257 fprintf(stderr, "Warning: unknown time units.\n");
258 ret=TPCERROR_OK;
259 }
260 /* If none of the previous excuses can be accepted, then exit */
261 if(ret!=TPCERROR_OK) {
262 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
263 tacFree(&tac1); tacFree(&tac2); return(3);
264 }
265
266 /* Try to convert conc units in data #2 to conc units in data #1 */
267 ret=tacYUnitConvert(&tac2, tac1.cunit, &status);
268 if(ret!=TPCERROR_OK && verbose>1) {
269 fprintf(stderr, "Note: %s and %s have different or unknown concentration units.\n",
270 d1file, d2file);
271 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
272 }
273 /* Error does not matter, if user told so */
274 if(!checkUnits) ret=TPCERROR_OK;
275 /* .. or if either is unitless, and multiplication or division */
276 if(tac1.cunit==UNIT_UNITLESS || tac2.cunit==UNIT_UNITLESS)
277 if(operation==3 || operation==4)
278 ret=TPCERROR_OK;
279 /* If none of the previous excuses can be accepted, then exit */
280 if(ret!=TPCERROR_OK) {
281 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
282 tacFree(&tac1); tacFree(&tac2); return(3);
283 }
284
285 /* Allocate memory for interpolated data #2 */
286 // tacNr in interpolated data will be either 1 or equal to tacNr in data #1
287 ret=tacAllocate(&itac2, tac1.sampleNr, tac2.tacNr);
288 if(ret!=TPCERROR_OK) {
289 fprintf(stderr, "Error: cannot interpolate TAC.\n");
290 tacFree(&tac1); tacFree(&tac2); tacFree(&itac2); return(4);
291 }
292 itac2.sampleNr=tac1.sampleNr; itac2.tacNr=tac2.tacNr;
293 /* Copy the headers from file #2 */
294 ret=tacCopyHdr(&tac2, &itac2);
295 for(int i=0; i<tac2.tacNr && ret==TPCERROR_OK ; i++)
296 ret=tacCopyTacchdr(&tac2.c[i], &itac2.c[i]);
297 /* Copy sample times from data #1 */
298 tacXCopy(&tac1, &itac2, 0, itac2.sampleNr-1);
299 itac2.isframe=tac1.isframe;
300
301 /*
302 * Copy or interpolate concentrations
303 */
304 if(tac2.sampleNr==1) {
305 /* If just one sample time, then use that for all samples in data #1 */
306 if(verbose>1) printf("using first sample in %s for all samples in %s\n", d2file, d1file);
307 // we have already checked that we now have same nr of TACs
308 for(int fi=0; fi<itac2.sampleNr; fi++)
309 for(int ri=0; ri<itac2.tacNr; ri++)
310 itac2.c[ri].y[fi]=tac2.c[ri].y[0];
311 } else {
312 /* more than one sample times */
313 int ri, fi;
314 /* Check whether sample times in data #1 and data #2 are the same,
315 so that we do not interpolate unnecessarily */
316 if(itac2.sampleNr<=tac2.sampleNr && tacXMatch(&tac1, &tac2, verbose-1)) {
317 if(verbose>1) printf("sample times match; no interpolation necessary\n");
318 for(ri=0; ri<itac2.tacNr; ri++)
319 for(fi=0; fi<itac2.sampleNr; fi++)
320 itac2.c[ri].y[fi]=tac2.c[ri].y[fi];
321 } else {
322 if(verbose>1) printf("interpolating\n");
323
324 /* Check the time range */
325 double xmin1, xmin2, xmax1, xmax2, xmin, xmax, xrange;
326 tacXRange(&tac1, &xmin1, &xmax1);
327 tacXRange(&tac2, &xmin2, &xmax2);
328 xmin=xmin1; if(xmin2<xmin) xmin=xmin2;
329 xmax=xmax1; if(xmax2>xmax) xmax=xmax2;
330 xrange=xmax-xmin;
331 if((xmin2>xmin1 && (xmin2-xmin1)/xrange>0.05) || (xmax2<xmax1 && (xmin1-xmax2)/xrange>0.10)) {
332 fprintf(stderr, "Warning: check the time ranges.\n");
333 }
334
335 /* Interpolate */
336 for(ri=0, ret=0; ri<itac2.tacNr && ret==0; ri++) {
337 if(itac2.isframe)
338 ret=liInterpolateForPET(tac2.x, tac2.c[ri].y, tac2.sampleNr,
339 itac2.x1, itac2.x2, itac2.c[ri].y, NULL, NULL, itac2.sampleNr, 4, 1, verbose-6);
340 if(itac2.isframe==0 || ret!=0)
341 ret=liInterpolate(tac2.x, tac2.c[ri].y, tac2.sampleNr,
342 itac2.x, itac2.c[ri].y, NULL, NULL, itac2.sampleNr, 4, 1, verbose-6);
343 }
344 if(ret!=0) {
345 fprintf(stderr, "Error: cannot interpolate TAC %d.\n", 1+ri);
346 if(verbose>1) fprintf(stderr, "error_code := %d\n", ret);
347 tacFree(&tac1); tacFree(&tac2); tacFree(&itac2); return(5);
348 }
349 }
350 }
351 if(verbose>4) tacWrite(&itac2, stdout, TAC_FORMAT_PMOD, 0, NULL);
352 /* data #2 is not needed later */
353 tacFree(&tac2);
354 }
355
356 /*
357 * Check the constant value
358 */
359 if(isvalue) {
360 if(!isfinite(value)) {
361 fprintf(stderr, "Error: invalid value of constant.\n");
362 tacFree(&tac1); tacFree(&itac2); return(1);
363 }
364 if(operation==4 && fabs(value)<1.0E-12) {
365 fprintf(stderr, "Error: invalid constant for division.\n");
366 tacFree(&tac1); tacFree(&itac2); return(1);
367 }
368 }
369
370
371 /*
372 * Do the operation (not with NaNs)
373 */
374 if(isvalue) {
375 if(verbose>1) printf("computing with constant\n");
376 int ri, fi;
377 switch(operation) {
378 case 1: /* Add */
379 for(ri=0; ri<tac1.tacNr; ri++) for(fi=0; fi<tac1.sampleNr; fi++)
380 if(!isnan(tac1.c[ri].y[fi])) tac1.c[ri].y[fi]+=value;
381 break;
382 case 2: /* Subtract */
383 for(ri=0; ri<tac1.tacNr; ri++) for(fi=0; fi<tac1.sampleNr; fi++)
384 if(!isnan(tac1.c[ri].y[fi])) tac1.c[ri].y[fi]-=value;
385 break;
386 case 3: /* Multiply */
387 for(ri=0; ri<tac1.tacNr; ri++) for(fi=0; fi<tac1.sampleNr; fi++)
388 if(!isnan(tac1.c[ri].y[fi])) tac1.c[ri].y[fi]*=value;
389 break;
390 case 4: /* Divide; already verified that !=0 */
391 for(ri=0; ri<tac1.tacNr; ri++) for(fi=0; fi<tac1.sampleNr; fi++)
392 if(!isnan(tac1.c[ri].y[fi])) tac1.c[ri].y[fi]/=value;
393 break;
394 }
395 } else {
396 if(verbose>1) printf("computing with TACs\n");
397 int ri, fi;
398 double *y1, *y2;
399 switch(operation) {
400 case 1: /* Add */
401 for(ri=0; ri<tac1.tacNr; ri++) {
402 y1=tac1.c[ri].y;
403 if(itac2.tacNr==1) y2=itac2.c[0].y; else y2=itac2.c[ri].y;
404 for(fi=0; fi<tac1.sampleNr; fi++)
405 if(isfinite(y1[fi]) && isfinite(y2[fi])) y1[fi]+=y2[fi]; else y1[fi]=nan("");
406 }
407 break;
408 case 2: /* Subtract */
409 for(ri=0; ri<tac1.tacNr; ri++) {
410 y1=tac1.c[ri].y;
411 if(itac2.tacNr==1) y2=itac2.c[0].y; else y2=itac2.c[ri].y;
412 for(fi=0; fi<tac1.sampleNr; fi++)
413 if(isfinite(y1[fi]) && isfinite(y2[fi])) y1[fi]-=y2[fi]; else y1[fi]=nan("");
414 }
415 break;
416 case 3: /* Multiply */
417 for(ri=0; ri<tac1.tacNr; ri++) {
418 y1=tac1.c[ri].y;
419 if(itac2.tacNr==1) y2=itac2.c[0].y; else y2=itac2.c[ri].y;
420 for(fi=0; fi<tac1.sampleNr; fi++)
421 if(isfinite(y1[fi]) && isfinite(y2[fi])) y1[fi]*=y2[fi]; else y1[fi]=nan("");
422 }
423 break;
424 case 4: /* Divide */
425 for(ri=0; ri<tac1.tacNr; ri++) {
426 y1=tac1.c[ri].y;
427 if(itac2.tacNr==1) y2=itac2.c[0].y; else y2=itac2.c[ri].y;
428 for(fi=0; fi<tac1.sampleNr; fi++) {
429 if(!isfinite(y1[fi]) || !isfinite(y2[fi])) {y1[fi]=nan(""); continue;}
430 if(fabs(y2[fi])<1.0E-12) y1[fi]=0.0; else y1[fi]/=y2[fi];
431 }
432 }
433 tac1.cunit=UNIT_UNITLESS;
434 break;
435 }
436 }
437 tacFree(&itac2);
438
439
440 /*
441 * Save data
442 */
443 if(verbose>1) printf("writing %s\n", rfile);
444 FILE *fp; fp=fopen(rfile, "w");
445 if(fp==NULL) {
446 fprintf(stderr, "Error: cannot open file for writing (%s)\n", rfile);
447 tacFree(&tac1); return(11);
448 }
449 ret=tacWrite(&tac1, fp, TAC_FORMAT_UNKNOWN, 1, &status);
450 fclose(fp); tacFree(&tac1);
451 if(ret!=TPCERROR_OK) {
452 fprintf(stderr, "Error (%d): %s\n", ret, errorMsg(status.error));
453 return(12);
454 }
455 if(verbose>=0) printf("%s saved.\n", rfile);
456
457 return(0);
458}
459/*****************************************************************************/
460
461/*****************************************************************************/
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 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
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
int tacAllocate(TAC *tac, int sampleNr, int tacNr)
Definition tac.c:130
void tacInit(TAC *tac)
Definition tac.c:24
int tacCopyTacchdr(TACC *d1, TACC *d2)
Definition tac.c:282
int tacCopyHdr(TAC *tac1, TAC *tac2)
Copy TAC header data from tac1 to tac2.
Definition tac.c:310
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 tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:72
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacXMatch(TAC *d1, TAC *d2, const int verbose)
Check whether sample (frame) times are the same (or very close to) in two TAC structures.
Definition tacx.c:249
int tacXCopy(TAC *tac1, TAC *tac2, int i1, int i2)
Definition tacx.c:24
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124
Header file for library libtpcextensions.
@ UNIT_UNITLESS
Unitless.
@ 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
@ TAC_FORMAT_PMOD
PMOD TAC format.
Definition tpctac.h:33