8#include "tpcclibConfig.h"
23static char *info[] = {
24 "Calculates standardized uptake values (SUV, DUR, DAR) or PID/L from PET",
25 "time-activity curves (TAC), injected radioligand dose and subject weight.",
26 "SUV is calculated as a mean value between specified sample times, but",
27 "optionally TACs can be saved in SUV units.",
28 "Information on SUV in:",
29 "https://www.turkupetcentre.net/petanalysis/model_suv.html",
31 "Usage: @P [Options] tacfile starttime endtime dose weight [resultfile]",
34 " -curve=<Filename for SUV curve>",
35 " Save regional SUVs from whole measurement time range.",
36 " -density=<Tissue density (g/ml)>",
37 " Calculate results per tissue mass instead of tissue volume.",
40 "SUV calculation start and stop time must be entered in minutes;",
41 "set both to zero to use the whole time range from TAC data.",
43 "Injected dose must be given in units MBq at the time of injection.",
45 "Subject weight must be given in kg or liters.",
46 "Instead of SUV, the percentage of injected dose per tissue volume ",
47 "(PID/L, %i.d./L) is calculated, if the subject weight is set to 0 (or below).",
49 "TAC file must be correct for physical decay to the injection time.",
50 "If the units of radioactivity concentrations and sample times are not",
51 "specified inside the TAC file, the units are assumed to be in kBq/mL and min;",
52 "units can be specified by adding line(s) to the end of the TAC file,",
57 "Results are saved in result file format (.res) by default, but if",
58 "file name extension is set to .dft, .csv, .dat, or .html, results are saved",
62 "Calculate SUV40-60 and save SUV TAC with command",
63 " @P -curve=uia15suv.dat uia15.dat 40 60 330 77 uia15suv40-60.res",
65 "Calculate SUV from available time range with command",
66 " @P uia15.dat 0 0 330 77 uia15suv.res",
68 "See also: imgsuv, regfur, tactime, taccalc, tacunit, tac2suv, rescoll",
70 "Keywords: TAC, SUV, DUR, DAR, PID, dose, modelling",
89int main(
int argc,
char **argv)
91 int ai, help=0, version=0, verbose=1;
93 char dfile[FILENAME_MAX], rfile[FILENAME_MAX], sfile[FILENAME_MAX];
97 double time1=-1.0, time2=-1.0, weight=nan(
""), dose=-1.0, density=-1.0, f;
104 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
105 dfile[0]=rfile[0]=sfile[0]=(char)0;
108 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
109 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
111 if(strncasecmp(cptr,
"CURVE=", 6)==0) {
112 cptr+=6;
if(strlen(cptr)>0) {
strlcpy(sfile, cptr, FILENAME_MAX);
continue;}
113 }
else if(strncasecmp(cptr,
"C=", 2)==0) {
114 cptr+=2;
if(strlen(cptr)>0) {
strlcpy(sfile, cptr, FILENAME_MAX);
continue;}
115 }
else if(strncasecmp(cptr,
"DENSITY=", 8)==0) {
116 cptr+=8; density=
atof_dpi(cptr);
if(density>0.0 && density<3.0)
continue;
117 }
else if(strncasecmp(cptr,
"D=", 2)==0) {
118 cptr+=2; density=
atof_dpi(cptr);
if(density>0.0 && density<3.0)
continue;
120 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
125 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
130 if(ai<argc) {
strlcpy(dfile, argv[ai], FILENAME_MAX); ai++;}
133 if(ret!=0 || !(time1>=0.0)) {
134 fprintf(stderr,
"Error: invalid start time '%s'.\n", argv[ai]);
141 if(ret!=0 || !(time2>=time1)) {
142 fprintf(stderr,
"Error: invalid end time '%s'.\n", argv[ai]);
149 if(ret!=0 || !(dose>0.0)) {
150 fprintf(stderr,
"Error: invalid dose '%s'.\n", argv[ai]);
158 if(ret!=0 || !(v>=0.0)) {
159 fprintf(stderr,
"Error: invalid weight '%s'.\n", argv[ai]);
162 if(v>1.0E-100) weight=v;
165 if(ai<argc) {
strlcpy(rfile, argv[ai], FILENAME_MAX); ai++;}
167 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
173 for(; ai<argc; ai++) {
175 strlcpy(dfile, argv[ai], FILENAME_MAX);
continue;
178 if(ret==0 && time1>=0.0)
continue;
181 if(ret==0 && time2>=time1)
continue;
184 if(ret==0 && dose>0.0)
continue;
185 }
else if(isnan(weight)) {
188 }
else if(!rfile[0]) {
189 strlcpy(rfile, argv[ai], FILENAME_MAX);
continue;
191 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);
196 fprintf(stderr,
"Error: missing result file name.\n");
199 if(weight<1.0E-100) weight=nan(
"");
204 fprintf(stderr,
"Error: missing dose.\n");
207 if(!rfile[0] && !sfile[0]) {
208 fprintf(stderr,
"Error: missing result file name.\n");
214 printf(
"dfile := %s\n", dfile);
215 printf(
"rfile := %s\n", rfile);
216 printf(
"sfile := %s\n", sfile);
217 printf(
"Time range := %g-%g min\n", time1, time2);
218 printf(
"dose := %g MBq\n", dose);
219 if(!isnan(weight)) printf(
"weight := %g kg\n", weight);
220 printf(
"density := %g g/ml\n", density);
227 if(verbose>1) printf(
"reading TAC data in %s\n", dfile);
229 fprintf(stderr,
"Error in reading '%s': %s\n", dfile,
dfterrmsg);
233 printf(
"tacNr := %d\n", dft.
voiNr);
234 printf(
"sampleNr := %d\n", dft.
frameNr);
238 fprintf(stderr,
"Error: missing values in %s\n", dfile);
246 if(verbose>2) fprintf(stdout,
"checking frame overlap in %s\n", dfile);
249 fprintf(stderr,
"Error: %s has overlapping frame times.\n", dfile);
263 fprintf(stderr,
"Error: activity unit %s is not supported.\n", dft.
unit);
266 if(verbose>0) printf(
"Data converted to %s\n", dft.
unit);
272 time1*=60.; time2*=60.;
273 if(verbose>0) fprintf(stderr,
"Note: start and end times converted to seconds.\n");
274 }
else if(dft.
timeunit==TUNIT_UNKNOWN) {
275 fprintf(stderr,
"Warning: unknown data time units.\n");
276 fprintf(stderr,
"Warning: assuming that data and time range are in same units.\n");
287 printf(
"tac_starttime := %g\n", t1);
288 printf(
"tac_endtime := %g\n", t2);
296 allow_dt=0.19*(t2-t1);
297 if(time2>t2+allow_dt) {
298 fprintf(stderr,
"Error: time range %g-%g is outside measurement range %g-%g.\n",
299 time1, time2, t1, t2);
310 if(verbose>1) fprintf(stdout,
"calculating SUV TAC\n");
313 if(verbose>1) fprintf(stdout,
"calculating PID TAC\n");
319 if(density>0.0) f*=density;
320 if(verbose>2) printf(
"conversion factor := %g\n", f);
322 for(ri=0; ri<dft.
voiNr; ri++)
323 for(fi=0; fi<dft.
frameNr; fi++)
324 dft.
voi[ri].
y[fi]/=f;
327 strcpy(dft.
unit,
"g/");
328 if(density>0) strcat(dft.
unit,
"g");
else strcat(dft.
unit,
"mL");
330 strcpy(dft.
unit,
"%i.d./");
331 if(density>0) strcat(dft.
unit,
"kg");
else strcat(dft.
unit,
"L");
342 if(verbose>1) printf(
"saving converted TAC(s) in %s\n", sfile);
344 if(isnan(weight)) f=0.0;
else f=weight;
345 sprintf(dft.
comments,
"# %s: %s %g %g %g %g\n", tmp, dfile, time1, time2, dose, f);
347 fprintf(stderr,
"Error in writing '%s': %s\n", sfile,
dfterrmsg);
351 if(!isnan(weight)) strcpy(tmp,
"SUV");
else strcpy(tmp,
"%i.d.");
352 if(verbose>0) fprintf(stdout,
"%s curves written in %s\n", tmp, sfile);
358 if(!rfile[0]) {
dftEmpty(&dft);
return(0);}
364 if(verbose>1) printf(
"calculating average\n");
367 fprintf(stderr,
"Error: %s.\n", tmp);
369 printf(
"dftTimeIntegral(suvtac, %g, %g, suvavg, 1, tmp) := %d\n", time1, time2, ret);
373 if(verbose>1) fprintf(stdout,
"%s.\n", tmp);
382 if(verbose>1) printf(
"saving average\n");
385 cptr=strrchr(rfile,
'.');
386 if(cptr!=NULL && strcasecmp(cptr,
".DFT")==0) {
390 fprintf(stderr,
"Error in writing %s: %s\n", rfile,
dfterrmsg);
393 }
else if(cptr!=NULL && strcasecmp(cptr,
".CSV")==0) {
397 fprintf(stderr,
"Error in writing %s: %s\n", rfile,
dfterrmsg);
400 }
else if(cptr!=NULL && strcasecmp(cptr,
".DAT")==0) {
404 fprintf(stderr,
"Error in writing %s: %s\n", rfile,
dfterrmsg);
411 fprintf(stderr,
"Error: %s.\n", tmp);
417 sprintf(res.
reffile,
"Dose %g MBq", dose);
419 sprintf(tmp,
" ; %g kg", weight);
422 if(density>0.0) res.
density=density;
424 if(isnan(weight) && density<=0.0) {
426 }
else if(isnan(weight) && density>0.0) {
428 }
else if(density<=0.0) {
435 ret=
resWrite(&res, rfile, verbose-3);
437 fprintf(stderr,
" Error (%d) in writing file %s\n", ret, rfile);
443 if(!isnan(weight)) strcpy(tmp,
"SUV");
else strcpy(tmp,
"%i.d.");
444 if(verbose>0) fprintf(stdout,
"%s written in %s\n", tmp, rfile);
int atof_with_check(char *double_as_string, double *result_value)
double atof_dpi(char *str)
int dftDeleteFrameOverlap(DFT *dft)
void dftSetComments(DFT *dft)
int dftSortByFrame(DFT *dft)
int dft_nr_of_NA(DFT *dft)
int dftTimeIntegral(DFT *dft, double t1, double t2, DFT *idft, int calc_mode, char *status, int verbose)
int dftRead(char *filename, DFT *data)
int dftWrite(DFT *data, char *filename)
int dftToResult(DFT *dft, RES *res, char *status)
int dftUnitConversion(DFT *dft, int dunit)
void filenameRmPath(char *s)
Header file for libtpccurveio.
#define DFT_FORMAT_CSV_UK
#define DFT_FORMAT_STANDARD
int resWrite(RES *res, char *filename, int verbose)
#define DFT_TIME_STARTEND
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
int petCunitId(const char *unit)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
int tpcHtmlUsage(const char *program, char *text[], const char *path)
void tpcPrintBuild(const char *program, FILE *fp)
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Header file for libtpcmodel.
Header file for libtpcmodext.
char comments[_DFT_COMMENT_LEN+1]
char unit[MAX_UNITS_LEN+1]
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char datafile[FILENAME_MAX]
char reffile[FILENAME_MAX]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]