EGS Brachy
An egs++ user code for rapid brachytherapy calculations
Loading...
Searching...
No Matches
/Users/marc/Developer/EGSnrc/HEN_HOUSE/user_codes/egs_brachy/egs_brachy/spec_scoring.cpp
Go to the documentation of this file.
1/*
2################################################################################
3#
4# egs_brachy spec_scoring.cpp
5# Copyright (C) 2016 Rowan Thomson, Dave Rogers, Randle Taylor, and Marc
6# Chamberland
7#
8# This file is part of egs_brachy
9#
10# egs_brachy is free software: you can redistribute it and/or modify it
11# under the terms of the GNU Affero General Public License as published
12# by the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# egs_brachy is distributed in the hope that it will be useful, but
16# WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18# Affero General Public License for more details:
19# <http://www.gnu.org/licenses/>.
20#
21################################################################################
22#
23# When egs_brachy is used for publications, please cite our paper:
24# M. J. P. Chamberland, R. E. P. Taylor, D. W. O. Rogers, and R. M. Thomson,
25# egs brachy: a versatile and fast Monte Carlo code for brachytherapy,
26# Phys. Med. Biol. 61, 8214-8231 (2016).
27#
28################################################################################
29#
30# Author: Randle Taylor, 2016
31#
32# Contributors: Marc Chamberland
33# Dave Rogers
34# Rowan Thomson
35#
36################################################################################
37*/
38
43#include "spec_scoring.h"
44#include <fstream>
45#include <stdarg.h> // For va_start, etc.
46
47std::string string_format(const std::string fmt, ...) {
48 int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
49 std::string str;
50 va_list ap;
51 while (1) { // Maximum two passes on a POSIX system...
52 str.resize(size);
53 va_start(ap, fmt);
54 int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
55 va_end(ap);
56 if (n > -1 && n < size) { // Everything worked
57 str.resize(n);
58 return str;
59 }
60 if (n > -1) { // Needed size returned
61 size = n + 1; // For null char
62 } else {
63 size *= 2; // Guess at a larger size (OS specific)
64 }
65 }
66 return str;
67}
68
69string getFileNameFromPath(const string &s) {
70
71 char sep = '/';
72
73#ifdef _WIN32
74 sep = '\\';
75#endif
76
77 size_t i = s.rfind(sep, s.length());
78 if (i != string::npos) {
79 return (s.substr(i+1, s.length() - i));
80 }
81
82 return ("");
83}
84
85BaseSpectrumScorer *BaseSpectrumScorer::getSpectrumScorer(EGS_Input *inp, EGS_BaseSource *source, GeomInfo *ginfo,
86 Publisher *publisher) {
87
88 vector<string> types;
89 types.push_back("surface count");
90 types.push_back("energy weighted surface");
91 types.push_back("fluence in region");
92 types.push_back("energy fluence in region");
93
94 string type = types[inp->getInput("type", types, 0)];
95
96 BaseSpectrumScorer *scorer = 0;
97 if (type == "surface count") {
98 scorer = new SurfaceCountSpectrum(inp, source, ginfo, publisher);
99 } else if (type == "energy weighted surface") {
100 scorer = new EnergyWeightedSurfaceSpectrum(inp, source, ginfo, publisher);
101 } else if (type == "fluence in region") {
102 scorer = new FluenceSpectrumInVoxel(inp, source, ginfo, publisher);
103 } else if (type == "energy fluence in region") {
104 scorer = new EnergyFluenceSpectrumInVoxel(inp, source, ginfo, publisher);
105 }
106
107 if (!scorer || !scorer->isValid()) {
108 return 0;
109 }
110
111 return scorer;
112
113}
114
115BaseSpectrumScorer::BaseSpectrumScorer(EGS_Input *input, EGS_BaseSource *src, GeomInfo *ginfo, Publisher *publisher) {
116
117 valid = true;
118 source = src;
119
120
121 cur_history = 0;
122 eff_history = 0;
123 total_scored = 0;
124 egsnrc_mode = 0;
125
126 int err = input->getInput("minimum energy", e_min);
127 if (err) {
128 e_min = 0.001;
129 }
130
131 err = input->getInput("maximum energy", e_max);
132 if (err) {
133 e_max = source->getEmax();
134 }
135
136 err = input->getInput("number of bins", nbins);
137 if (err || nbins < 1) {
139 }
140
141 vector<string> particle_choices;
142 particle_choices.push_back("photon");
143 particle_choices.push_back("electron");
144 particle_choices.push_back("positron");
145 particle_type = input->getInput("particle type", particle_choices, 0);
146
147 //convert particle type to particle charge
148 if (particle_type == 1) {
149 particle_type = -1;
150 } else if (particle_type == 2) {
151 particle_type = 1;
152 }
153
154
155 err = input->getInput("file extension", fextension);
156 if (err) {
157 fextension = "";
158 }
159
160 vector<string> choices;
161 choices.push_back("xmgr");
162 choices.push_back("csv");
163 choices.push_back("egsnrc");
164 int choice = input->getInput("output format", choices, 0);
165 format = choices[choice];
166
167 if (format == "egsnrc") {
168 err = input->getInput("egsnrc format mode", egsnrc_mode);
169 if (err) {
170 egsnrc_mode = 0;
171 }
172 if (egsnrc_mode < 0 || egsnrc_mode > 2) {
173 egsWarning("Unsupported egsnrc spectrum output format mode. Defaulting to mode 0\n");
174 }
175
176 }
177
178
179
180 bins = new EGS_ScoringArray(nbins);
181
183
184 publisher->subscribe(this, NEW_HISTORY);
185}
186
187
189 if (bins) {
190 delete bins;
191 }
192}
193
195 eff_history = (EGS_I64)effh;
196}
197
198int BaseSpectrumScorer::getBin(EGS_Float E) const {
199 return min(nbins-1, (int)((E - e_min)/bin_width));
200}
201
202
204 return bin_width;
205}
206
207double BaseSpectrumScorer::getParticleEnergy(const EGS_Particle *p) const {
208 double rest_mass = p->q == 0 ? 0 : the_useful->prm;
209 return (p->E - rest_mass);
210}
211
213
214 string fname;
215 if (format == "csv") {
216 fname = outputCSV(rootname);
217 } else if (format == "egsnrc") {
218 fname = outputEGSnrc(rootname);
219 } else if (format == "xmgr") {
220 fname = outputXMGR(rootname);
221 } else {
222 return 1;
223 }
224
225 string sep(80,'-');
226 egsInformation("\n%s\n",sep.c_str());
227 egsInformation("%s\n", getTitle().c_str());
228 egsInformation("Output written to : %s\n", getFileNameFromPath(fname).c_str());
229 egsInformation("Particle type : %s\n", getParticleName().c_str());
230 egsInformation(
231 "Energy range : %.3FMeV <= E <= %.3FMeV (%d bins of width %.3GMeV)\n",
233 );
234 outputTotal();
235 egsInformation("%s\n",sep.c_str());
236
237 return 0;
238}
239
240string BaseSpectrumScorer::outputCSV(string rootname) {
241 ofstream out;
242 string fname = getFileName(rootname)+".csv";
243 out.open(fname.c_str());
244 out << "E,F,dF\n";
245 double mid, r, dr;
246 for (int i=0; i < nbins; i++) {
247 mid = e_min + (i+0.5)*bin_width;
248 getResult(i, r, dr);
249 out << string_format("%.5E,%.5E,%.5E\n", mid, r, dr);
250 }
251
252 out.close();
253 return fname;
254}
255
256string BaseSpectrumScorer::outputEGSnrc(string rootname) {
257
258 ofstream out;
259 string fname = getFileName(rootname)+".spectrum";
260 out.open(fname.c_str());
261 out << "egs_brachy scored spectrum"<< "\n";
262
263
264 if (egsnrc_mode == 0 || egsnrc_mode == 1) {
265 // egsnrc modes
266 out << nbins <<","<<e_min<<","<< egsnrc_mode << "\n";
267
268 double top, prob, r, dr;
269 for (int i=0; i < nbins; i++) {
270
271 getResult(i, r, dr);
272
273 top = e_min + (i + 1)*bin_width;
274 prob = egsnrc_mode == 0 ? r : r/bin_width;
275
276 out << string_format("%.5E,%.5E\n", top, prob);
277 }
278
279 } else if (egsnrc_mode == 2) {
280 // egs++ line spectrum
281
282 double r, dr, norm=0;
283 vector<double> mids;
284 vector<double> probabilities;
285
286 // get non zero bins
287 for (int i=0; i < nbins; i++) {
288 getResult(i, r, dr);
289
290 if (r > 0) {
291 mids.push_back(e_min + (i+0.5)*bin_width);
292 probabilities.push_back(r);
293 norm += r;
294 }
295 }
296
297 for (size_t i = 0; i < mids.size(); i++) {
298 if (i == 0) {
299 out << string_format("%d,%.5E,%d\n", mids.size(), mids[i], egsnrc_mode);
300 }
301 out << string_format("%.5E,%.5E\n", mids[i], probabilities[i]/norm);
302 }
303 }
304 out.close();
305
306 return fname;
307}
308
309string BaseSpectrumScorer::outputXMGR(string rootname) {
310
311 ofstream out;
312 string fname = getFileName(rootname)+".agr";
313 out.open(fname.c_str());
314 out << "# EGS_Brachy spectrum\n";
315 out << "# \n";
316 out << "@ legend 0.2, 0.8\n";
317 out << "@ legend box linestyle 0\n";
318 out << "@ legend font 4\n";
319 out << "@ xaxis label \""+getXAxisLabel()+"\"\n";
320 out << "@ xaxis label char size 1.3\n";
321 out << "@ xaxis label font 4\n";
322 out << "@ xaxis ticklabel font 4\n";
323 out << "@ yaxis label \""+getYAxisLabel()+"\"\n";
324 out << "@ yaxis label char size 1.3\n";
325 out << "@ yaxis label font 4\n";
326 out << "@ yaxis ticklabel font 4\n";
327 out << "@ title \"" << getTitle() << "\"\n";
328 out << "@ title font 4\n";
329 out << "@ title size 1.500000\n";
330 out << "@ subtitle \"" << getSubTitle() << "\"\n";
331 out << "@ subtitle font 4\n";
332 out << "@ subtitle size 1.000000\n";
333
334 out<<"@ s0 errorbar linestyle 0\n";
335 out<<"@ s0 legend \"Spectrum\"\n";
336 out<<"@target G0.S0\n";
337 out<<"@type xydy\n";
338
339 double mid, bot, top, r, dr;
340 for (int i=0; i < nbins; i++) {
341
342 bot = e_min + i*bin_width;
343 mid = e_min + (i+0.5)*bin_width;
344 top = e_min + (i+ 1)*bin_width;
345
346 getResult(i, r, dr);
347
348 if (i == 0) {
349 out << string_format("%.5E %.5E %.5E\n", bot, 0., 0.);
350 }
351
352
353 out << string_format("%.5E %.5E %.5E\n", bot, r, 0.);
354 out << string_format("%.5E %.5E %.5E\n", mid, r, dr);
355 out << string_format("%.5E %.5E %.5E\n", top, r, 0.);
356
357 if (i == nbins-1) {
358 out << string_format("%.5E %.5E %.5E\n", top, 0., 0.);
359 }
360 }
361
362 out.close();
363 return fname;
364}
365
366void BaseSpectrumScorer::update(EB_Message message, void *data) {
367
368 if (message == NEW_HISTORY) {
369 cur_history = *(EGS_I64 *)(data);
370 bins->setHistory(cur_history);
371 } else {
372 score(message, data);
373 }
374
375}
376
377void BaseSpectrumScorer::getResult(int i, EGS_Float &r, EGS_Float &dr) {
378 bins->currentResult(i, r, dr);
379}
380
382
383 (*ofile) << cur_history << " " << eff_history << "\n";
384 (*ofile) << total_scored << "\n";
385 bool ok = bins->storeState(*ofile);
386 if (!ok) {
387 return 1;
388 }
389
390 return 0;
391
392}
393
394int BaseSpectrumScorer::readData(istream *ifile) {
395
396 (*ifile) >> cur_history >> eff_history;
397 (*ifile) >> total_scored;
398 bool ok = bins->setState(*ifile);
399 if (!ok) {
400 return 1;
401 }
402
403 return 0;
404
405}
406
408
409 cur_history = 0;
411 total_scored = 0;
412 bins->reset();
413
414}
415
417
418 EGS_I64 cur_history_tmp, eff_history_tmp;
419 EGS_Float total_scored_tmp;
420 EGS_ScoringArray tmp_bins(bins->regions());
421
422 data >> cur_history_tmp >> eff_history_tmp;
423 data >> total_scored_tmp;
424 bool ok = tmp_bins.setState(data);
425 if (!ok) {
426 return 1;
427 }
428
429 cur_history += cur_history_tmp;
430 eff_history += eff_history_tmp;
431 total_scored += total_scored_tmp;
432 (*bins) += tmp_bins;
433
434 return 0;
435}
436
437
438/*********************************************************************************
439 * Surface Count Spectrum */
440
441void SurfaceCountSpectrum::score(EB_Message message, void *data) {
442
443 if (message == PARTICLE_ESCAPING_SOURCE) {
444 EGS_Particle *p= static_cast<EGS_Particle *>(data);
445
446 double energy = getParticleEnergy(p);
447
448 if (e_min <= energy && energy <= e_max && p->q == particle_type) {
449 bins->score(getBin(energy), 1.);
450 total_scored += 1.;
451 }
452 }
453
454}
455
456void SurfaceCountSpectrum::getResult(int i, EGS_Float &r, EGS_Float &dr) {
457
458 bins->currentResult(i, r, dr);
459 EGS_Float norm = bin_width * total_scored / cur_history;
460 r /= norm;
461 dr /= norm;
462
463}
464
466
467
468 egsInformation("Scoring metric : absolute photon counts on surface of source\n") ;
469 egsInformation("Total counts : %.5G \n", total_scored);
470}
471
472
473/*********************************************************************************
474 * Energy weighted surface spectrum */
475
477
478 if (message == PARTICLE_ESCAPING_SOURCE) {
479 EGS_Particle *p= static_cast<EGS_Particle *>(data);
480
481 double energy = getParticleEnergy(p);
482
483 if ((e_min <= energy) && (energy <= e_max) && (p->q == particle_type)) {
484 bins->score(getBin(energy), energy*p->wt);
485 total_scored += energy*p->wt;
486 }
487 }
488
489}
490
491void EnergyWeightedSurfaceSpectrum::getResult(int i, EGS_Float &r, EGS_Float &dr) {
492
493 bins->currentResult(i, r, dr);
494 EGS_Float norm = bin_width * total_scored / cur_history;
495 r /= norm;
496 dr /= norm;
497
498}
499
501
502 egsInformation("Scoring metric : Energy weighted spectrum on surface of source\n") ;
503 egsInformation("Total Radiant E : %.5G MeV\n", total_scored);
504
505}
506
507/*********************************************************************************
508 * Fluence spectrum in Voxel */
509
510/* Note: this only works if there are no other geometries overlapping
511 * the scoring region! */
512void FluenceSpectrumInVoxel::score(EB_Message message, void *data) {
513
514 if (message == PARTICLE_TAKING_STEP) {
515 EGS_Particle *p= static_cast<EGS_Particle *>(data);
516
517 bool in_scoring_region = p->ir == scoring_region;
518 bool in_energy_scoring_range = (e_min <= p->E) && (p->E <= e_max);
519 bool correct_particle = p->q == particle_type;
520
521 if (in_scoring_region && in_energy_scoring_range && correct_particle) {
522 EGS_Float score = the_epcont->tvstep*p->wt;
523 bins->score(getBin(p->E), score);
525 }
526 }
527
528}
529
530void FluenceSpectrumInVoxel::getResult(int i, EGS_Float &r, EGS_Float &dr) {
531
532 double eff_history_norm = eff_history / cur_history;
533 bins->currentResult(i, r, dr);
534 EGS_Float norm = bin_width * region_volume * eff_history_norm;
535 r /= norm;
536 dr /= norm;
537
538}
539
541
542 egsInformation("Scoring metric : Fluence in region\n") ;
543 egsInformation("Scoring region (global) : %d \n", scoring_region) ;
544 egsInformation("Scoring region (local) : %d of %s\n", local_scoring_region, geometry->getName().c_str());
545 egsInformation("Region Volume : %.3G cm^3\n", region_volume) ;
546 egsInformation("Total E fluence : %.5G MeV/cm^2\n", total_scored);
547
548}
549
550/*********************************************************************************
551 * Energy fluence spectrum in Voxel */
552
553/* Note: this only works if there are no other geometries overlapping
554 * the scoring region! */
556
557 if (message == PARTICLE_TAKING_STEP) {
558 EGS_Particle *p= static_cast<EGS_Particle *>(data);
559
560 bool in_scoring_region = p->ir == scoring_region;
561 bool in_energy_scoring_range = (e_min <= p->E) && (p->E <= e_max);
562 bool correct_particle = p->q == particle_type;
563
564 if (in_scoring_region && in_energy_scoring_range && correct_particle) {
565 EGS_Float score = p->E*the_epcont->tvstep*p->wt;
566 bins->score(getBin(p->E), score);
568 }
569 }
570
571}
572
573void EnergyFluenceSpectrumInVoxel::getResult(int i, EGS_Float &r, EGS_Float &dr) {
574
575 double eff_history_norm = eff_history / cur_history;
576 bins->currentResult(i, r, dr);
577 EGS_Float norm = bin_width * region_volume * eff_history_norm;
578 r /= norm;
579 dr /= norm;
580
581}
582
584
585 egsInformation("Scoring metric : Energy fluence in region\n") ;
586 egsInformation("Scoring region (global) : %d \n", scoring_region) ;
587 egsInformation("Scoring region (local) : %d of %s\n", local_scoring_region, geometry->getName().c_str());
588 egsInformation("Region Volume : %.3G cm^3\n", region_volume) ;
589 egsInformation("Total E fluence : %.5G MeV/cm^2\n", total_scored);
590
591}
abstract base class for scoring spectrum information
virtual string getSubTitle() const
EGS_BaseSource * source
EGS_ScoringArray * bins
spectrum scoring array
int readData(istream *ifile)
string getParticleName() const
Return name of particle we are scoring.
EGS_Float getBinWidth() const
void setEffectiveHistories(EGS_Float effective_histories)
string outputCSV(string filename)
output results to csv file
int addState(istream &data)
int nbins
number of bins to score spectrum in
virtual void getResult(int bin, EGS_Float &r, EGS_Float &dr)
set r & dr to result/uncertainty for given bin. Normalization can be done in this routine
string outputXMGR(string filename)
output results to xmgrace file
static BaseSpectrumScorer * getSpectrumScorer(EGS_Input *inp, EGS_BaseSource *source, GeomInfo *ginfo, Publisher *publisher)
factory method for creating one of the derived spectrum scoring classes
bool isValid() const
virtual void score(EB_Message message, void *data=0)=0
virtual string getTitle() const
int getBin(EGS_Float E) const
get index of bin that that E falls in
virtual void update(EB_Message message, void *data=0)
Receives messages from application. This method handles NEW_HISTORY messages but delegates all other ...
virtual void outputTotal()
BaseSpectrumScorer(EGS_Input *input, EGS_BaseSource *src, GeomInfo *ginfo, Publisher *publisher)
int outputData(ostream *ofile)
string getFileName(string root) const
virtual ~BaseSpectrumScorer()
static const int DEFAULT_NBINS
virtual string getXAxisLabel() const
EGS_Float e_max
maximum spectrum energy to score
virtual string getYAxisLabel() const
double getParticleEnergy(const EGS_Particle *p) const
string outputEGSnrc(string filename)
output results to EGSnrc tabulated spectrum format
EGS_Float e_min
minimum spectrum energy to score
int outputResults(string root_name)
, write the scored spectrum to file with filename. Returns 0 on success, all other values indicate a ...
A class for scoring the energy fluence of particles enter a voxel of the scoring phantom geometry.
EGS_BaseGeometry * geometry
virtual void score(EB_Message message, void *data=0)
void getResult(int bin, EGS_Float &r, EGS_Float &dr)
set r & dr to result/uncertainty for given bin. Normalization can be done in this routine
A class for scoring the energy weighted spectrum (normalized to total radiant energy) of particles on...
void getResult(int bin, EGS_Float &r, EGS_Float &dr)
set r & dr to result/uncertainty for given bin. Normalization can be done in this routine
virtual void score(EB_Message message, void *data=0)
A class for scoring the fluence of particles enter a voxel of the scoring phantom geometry.
virtual void score(EB_Message message, void *data=0)
void getResult(int bin, EGS_Float &r, EGS_Float &dr)
set r & dr to result/uncertainty for given bin. Normalization can be done in this routine
EGS_BaseGeometry * geometry
a container for organizing meta data about the geometries
Definition ginfo.h:99
void subscribe(Subscriber *s, EB_Message message)
Definition pubsub.h:83
A class for scoring a histogram of the number of particles escaping a source geometry.
void getResult(int bin, EGS_Float &r, EGS_Float &dr)
set r & dr to result/uncertainty for given bin. Normalization can be done in this routine
virtual void score(EB_Message message, void *data=0)
EB_Message
Definition pubsub.h:54
@ PARTICLE_ESCAPING_SOURCE
Definition pubsub.h:60
@ NEW_HISTORY
Definition pubsub.h:56
@ PARTICLE_TAKING_STEP
Definition pubsub.h:58
std::string string_format(const std::string fmt,...)
string getFileNameFromPath(const string &s)
Definition of spectrum scoring classes.