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/phantom.cpp
Go to the documentation of this file.
1/*
2################################################################################
3#
4# egs_brachy phantom.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
44#include <algorithm>
45#include <vector>
46#include <fstream>
47#include "phantom.h"
48#include <iomanip>
49#include "gzstream.h"
50#include "latch.h"
51#include "egs_run_control.h"
52
53
54
55/* Used internally for keeping track of results for regions, allowing us
56 * to sort by dose etc */
58
59 int reg;
60 double volume;
61 double volume_unc;
62
63 double tlen;
64 double tlen_err;
65
66 double edep;
67 double edep_err;
68
69};
70
71
72/* comparison function useful for sorting vectors of RegionResults */
73bool tlenRegTopResultCompare(const RegionResult &firstElem, const RegionResult &secondElem) {
74 return firstElem.tlen > secondElem.tlen;
75}
76
77bool edepRegTopResultCompare(const RegionResult &firstElem, const RegionResult &secondElem) {
78 return firstElem.edep > secondElem.edep;
79}
80
81
82
83/* utility function for replacing spaces with underscores in a string */
84string space2underscore(std::string text) {
85 for (std::string::iterator it = text.begin(); it != text.end(); ++it) {
86 if (*it == ' ') {
87 *it = '_';
88 }
89 }
90 return text;
91}
92
93/* geometries that support getting regional volume/mass can be used as phantoms
94 * without specifying their volumes. Other geometry type need to have their
95 * volumes specified using `phantom region volumes` inputs.
96 * */
97const string EB_Phantom::autovol_phantom_geom_types[] = {"EGS_cSpheres", "EGS_cSphericalShell", "EGS_XYZGeometry", "EGS_XYZGeometryT", "EGS_RZ"};
98
99
100/* geometries that support 3ddose files */
101const string EB_Phantom::threeddose_geom_types[] = {"EGS_cSpheres", "EGS_cSphericalShell", "EGS_XYZGeometry", "EGS_XYZGeometryT", "EGS_RZ"};
102
103/* initializer for phantom class */
104EB_Phantom::EB_Phantom(EGS_Application *parent, EGS_BaseGeometry *geom, set<int> global_regions, int nsource,
105 Publisher *pub) :
106 app(parent), tlen_score(0), edep_score(0),
107 prim_score(0), sscat_score(0), mscat_score(0),
108 nsources(nsource),
109 dose_scale(1.),
110 total_radiant_e(0),
111 cur_history(0),
112 publisher(pub),
113 geometry(geom), global_regions(global_regions) {
114
117 needs_user_geoms = needsUserVolumes(geom->getType());
118 can_write_3ddose = canWrite3ddose(geom->getType());
119
121
122}
123
124void EB_Phantom::update(EB_Message message, void *data) {
125 if (message == NEW_HISTORY) {
126 setHistory(*((EGS_I64 *)data));
127 } else if (message == PARTICLE_ESCAPING_SOURCE) {
128 EGS_Particle *p = static_cast<EGS_Particle *>(data);
129 total_radiant_e += p->E*p->wt;
130 }
131}
132
133void EB_Phantom::scoreTlen(int ir, EGS_Float dose, EGS_Particle *p) {
134 if (ir < 0 || ir >= geometry->regions()) {
135 egsFatal("bad %d \n", ir);
136 }
137
138 if (tlen_score) {
139 tlen_score->score(ir, dose);
140 }
141
142 if (prim_score) {
143
144 if (Latch::isPrimary(p)) {
145 prim_score->score(ir, dose);
146 } else if (Latch::isSingleScat(p)) {
147 sscat_score->score(ir, dose);
148 } else if (Latch::isMultScat(p)) {
149 mscat_score->score(ir, dose);
150 }
151 }
152}
153
155void EB_Phantom::scoreEdep(int ir, EGS_Float dose) {
156 if (edep_score) {
157 edep_score->score(ir, dose);
158 }
159}
160
162void EB_Phantom::setDoseScale(EGS_Float scale) {
163 dose_scale = scale;
164}
165
166bool EB_Phantom::needsUserVolumes(const string &geom_type) {
167 // Check if the input geometry is one that egs_brachy can handle
168
169 int end = (int)(sizeof(autovol_phantom_geom_types)/sizeof(string));
170
171 for (int i=0; i<end; i++) {
172 if (autovol_phantom_geom_types[i] == geom_type) {
173 return false;
174 }
175 }
176
177 return true;
178}
179
180bool EB_Phantom::canWrite3ddose(const string &geom_type) {
181 // Check if the input geometry is one that egs_brachy can handle
182
183 int end = (int)(sizeof(threeddose_geom_types)/sizeof(string));
184
185 for (int i=0; i<end; i++) {
186 if (threeddose_geom_types[i] == geom_type) {
187 return true;
188 }
189 }
190
191 return false;
192}
193
195 vector<int> regs;
196 for (std::map<int, double>::iterator it=corrected_volumes.begin(); it != corrected_volumes.end(); ++it) {
197 regs.push_back(it->first);
198 }
199 return regs;
200}
201
202void EB_Phantom::setCorrectedVolume(int ir, double corrected_vol, double unc) {
203 corrected_volumes[ir] = corrected_vol;
204 volume_uncertainty[ir] = unc;
205}
206
207/* turn on interaction scoring */
209 if (!tlen_score) {
210 tlen_score = new EGS_ScoringArray(geometry->regions());
211 }
212}
213
214/* turn on interaction scoring */
216 if (!edep_score) {
217 edep_score = new EGS_ScoringArray(geometry->regions());
218 }
219}
220
221/* turn on interaction scoring */
223 if (!prim_score) {
224 prim_score = new EGS_ScoringArray(geometry->regions());
225 sscat_score= new EGS_ScoringArray(geometry->regions());
226 mscat_score= new EGS_ScoringArray(geometry->regions());
227
229 }
230}
231
232/* needs to be called at start of every history for every phantom for
233 * proper scoring of statistics */
234void EB_Phantom::setHistory(EGS_I64 current_case) {
235
236 cur_history = current_case;
237
238 if (tlen_score) {
239 tlen_score->setHistory(current_case);
240 }
241
242 if (edep_score) {
243 edep_score->setHistory(current_case);
244 }
245
246 if (prim_score) {
247 prim_score->setHistory(current_case);
248 sscat_score->setHistory(current_case);
249 mscat_score->setHistory(current_case);
250 }
251}
252
254
255 effective_histories = neff;
256}
257
258/* call to tell phantom to output its results */
259void EB_Phantom::outputResults(int top_n, string output_3ddose, string output_egsphant, string output_voxinfo,
260 string output_volcor) {
261
262 if (output_voxinfo == "text" || output_voxinfo == "gzip") {
263 outputVoxelInfo(output_voxinfo);
264 }
265
266 if (output_volcor == "text" || output_volcor == "gzip") {
267 outputVolumeCorrection(output_volcor);
268 }
269
270 vector<RegionResult> region_results = getRegionResults();
271
272 if (top_n) {
273 outputTopDoses(top_n, region_results);
274 }
275
276 if (can_write_3ddose && (output_3ddose == "text" || output_3ddose == "gzip")) {
277 output3ddoseResults(output_3ddose);
278 }
279
280 if (can_write_3ddose && (output_egsphant== "text" || output_egsphant == "gzip")) {
281 outputEGSPhant(output_egsphant);
282 }
283
284}
285EGS_Float EB_Phantom::getRealRho(int ireg) {
286 int med = geometry->medium(ireg);
287 return geometry->getRelativeRho(ireg)*the_media->rho[med];
288}
289
290
291EGS_Float EB_Phantom::getRealMass(int ireg) {
292 if (needs_user_geoms && !hasVolCor(ireg)) {
293 return -1;
294 }
295 return getCorrectedVolume(ireg)*getRealRho(ireg);
296}
297
299 return needs_user_geoms ? -1 : getUncorrectedVolume(ireg)*getRealRho(ireg);
300}
301
303 if (needs_user_geoms) {
304 return -1;
305 }
306 EGS_Float volume = geometry->getVolume(ireg);
307 return volume;
308}
309
311 return corrected_volumes.find(ireg) != corrected_volumes.end();
312}
313
315 bool has_correction = hasVolCor(ireg);
316
317 if (needs_user_geoms && !has_correction) {
318 return -1;
319 }
320 return has_correction ? corrected_volumes[ireg] : getUncorrectedVolume(ireg);
321}
322
324 bool has_correction = hasVolCor(ireg);
325
326 if (needs_user_geoms && !has_correction) {
327 return -1;
328 }
329 return has_correction ? volume_uncertainty[ireg] : 0;
330}
331
332void EB_Phantom::getCurrentScore(int ireg, double &sum, double &sum2) {
333 if (tlen_score) {
334 tlen_score->currentScore(ireg, sum, sum2);
335 } else if (edep_score) {
336 edep_score->currentScore(ireg, sum, sum2);
337 } else {
338 sum = 0;
339 sum2 = 0;
340 }
341
342}
343
344double EB_Phantom::getTlenNorm(int ireg) {
345 double eff_hist_norm = (double)effective_histories/cur_history;
346 return 1.60217662E-10*nsources*dose_scale/eff_hist_norm;
347}
348
349void EB_Phantom::getResult(EGS_ScoringArray *score, int ireg, string type, EGS_Float &r, EGS_Float &dr) {
350
351 score->currentResult(ireg, r, dr);
352 dr = r > 0 ? dr/r: 1;
353
354 // scoring arrays are already normalized to number of statistically
355 // independent histories
356
357 // correct norm from per history to per effective history
358 double eff_hist_norm = (double)effective_histories/cur_history;
359
360 //egsInformation("Normalization: effective_histories %d, cur_history %d, eff_hist_norm %f\n",effective_histories, cur_history, eff_hist_norm);
361
362
363 if (type == "tlen") {
364 r *= 1.60217662E-10*nsources*dose_scale/eff_hist_norm;
365 } else if (type == "edep") {
366 r *= 1.60217662E-10*nsources*dose_scale/eff_hist_norm;
367 } else { // scatter norm
368 r *= (cur_history/total_radiant_e)*nsources*dose_scale/eff_hist_norm;
369 }
370
371
372}
373
374// Note: this could be made more memory efficient by using an ordered set;
375// For every iteration, insert new region result and then if the
376// set has more than TOP_N results, reduce the size to TOP_N
377vector<RegionResult> EB_Phantom::getRegionResults() {
378
379 int nreg = geometry->regions();
380 vector<RegionResult> region_results;
381
382 // retrieve results and add to region_results to allow for sorting by top doses
383 for (int ireg = 0; ireg < nreg; ireg++) {
384
385 double result=0, dr=0;
386 if (tlen_score) {
387 getResult(tlen_score, ireg, "tlen", result, dr);
388 }
389
390 double result_edep=0, result_edep_dr=0;
391 if (edep_score) {
392 getResult(edep_score, ireg, "edep", result_edep, result_edep_dr);
393 }
394
395 EGS_Float volume = getCorrectedVolume(ireg);
396 EGS_Float dv = getVolumeUncertainty(ireg);
397
398 RegionResult regr = { ireg, volume, dv, result, dr, result_edep, result_edep_dr };
399 region_results.push_back(regr);
400
401 }
402
403 return region_results;
404
405}
406
407/* output statistics about dose arrays */
408void EB_Phantom::outputDoseStats(EGS_ScoringArray *score, string type) {
409
410 int nreg = score->regions();
411
412 EGS_Float avg_dose=0, // average dose
413 avg_dose_err=0, // average uncertainty on dose
414 avg_tot_err=0, // average uncertainty on dose
415
416 avg_dose_gt_0=0, // average non zero dose
417 avg_dose_gt_0_err=0, // average uncertainty on non zero doses
418 avg_tot_gt_0_err=0, // average total error on non zero doses
419
420 avg_dose_gt_20=0, // average dose > 20% max dose
421 avg_dose_gt_20_err=0, // average uncertainty on doses > 20% max dose
422 avg_tot_gt_20_err=0, // average total uncertainty on doses > 20% max dose
423
424 avg_vol_err=0, // average uncertainty of volume corrections (only includes regions which have volume corrections)
425 avg_vol_err_0=0, // average uncertainty of volume corrections (only includes regions which have volume corrections)
426 avg_vol_err_99=0, // average uncertainty of volume corrections where Vcor / Vnominal > 0.01
427 avg_vol_err_50=0, // average uncertainty of volume corrections where Vcor / Vnominal > 0.001
428 avg_vol_err_d20=0 // average uncertainty of volume corrections where D > 0.2*Dmax
429 ;
430
431 int n_gt_0=0, // # of voxels with non zero dose
432 n_gt_20=0, // # of voxels with dose >= 20% Dmax
433 n_vol_cor=0, // # of voxels with a volume correction
434 n_vol_cor_no_dose=0, // # of voxels with a volume correction but no dose scored
435 n_vol_cor_d20=0, // # of voxels with a volume correction and D > 0.2*Dmax
436 n_vol_cor_0=0, // # of voxels with a volume correction and dose scored
437 n_vol_cor_99=0, // # of voxels with less than 99% coverage
438 n_vol_cor_50=0, // # of voxels with less than 99.9% coverage
439 n_vol_cor_1000=0; // # of voxels with less than 100% coverage
440
441
442 // first find max dose so we can check voxels for >= 20% Dmax
443 EGS_Float max_dose = 0;
444 // find max dose so e
445 for (int i=0; i < nreg; i++) {
446 EGS_Float r, dr;
447 getResult(score, i, type, r, dr);
448 max_dose = max(max_dose, r);
449 }
450
451
452 for (int i=0; i < nreg; i++) {
453
454 EGS_Float dose, dose_err;
455
456 getResult(score, i, type, dose, dose_err);
457
458
459 double vol_nom = getUncorrectedVolume(i);
460 double vol_cor = getCorrectedVolume(i);
461 double vol_err = getVolumeUncertainty(i);
462 bool has_vol_cor = hasVolCor(i);
463
464 if (vol_cor <= 0){
465 n_vol_cor_1000++;
466 }
467
468 // vol cor stats
469 if (has_vol_cor){
470
471 n_vol_cor++;
472 avg_vol_err += vol_err;
473
474 if (dose > 0){
475 n_vol_cor_0++;
476 avg_vol_err_0 += vol_err;
477 }else{
478 n_vol_cor_no_dose++;
479 }
480
481 if (dose > 0 && (vol_cor / vol_nom <= 0.500)){
482 n_vol_cor_50++;
483 avg_vol_err_50 += vol_err;
484 }
485
486 if (dose > 0 && (vol_cor / vol_nom <= 0.01)){
487 n_vol_cor_99++;
488 avg_vol_err_99 += vol_err;
489 }
490
491 }
492
493 // all voxels including zero doses
494 avg_dose += dose;
495 avg_dose_err += dose_err;
496 avg_tot_err += sqrt(dose_err*dose_err + vol_err*vol_err);
497
498 // all non zero doses
499 if (dose > 0){
500 n_gt_0++;
501 avg_dose_gt_0 += dose;
502 avg_dose_gt_0_err += dose_err;
503 avg_tot_gt_0_err += sqrt(dose_err*dose_err + vol_err*vol_err);
504 }
505
506 // Doses >= 0.2
507 if (dose >= max_dose*0.20) {
508 n_gt_20++;
509 avg_dose_gt_20 += dose;
510 avg_dose_gt_20_err += dose_err;
511 avg_tot_gt_20_err += sqrt(dose_err*dose_err + vol_err*vol_err);
512 if (has_vol_cor){
513 avg_vol_err_d20 += vol_err;
514 n_vol_cor_d20++;
515 }
516 }
517
518 }
519
520 string units = "Gy/hist";
521 if (type == "pr" || type == "ss" || type == "ms" || type == "to") {
522 units = "Gy/R";
523 egsInformation("\n Total radiant energy = %.3G MeV\n", total_radiant_e);
524 }
525
526 egsInformation("\n Dose Scaling = %.3G\n", dose_scale);
527
528 egsInformation("\n # of voxels with vol corrections = %d / %d (%.3F%)\n", n_vol_cor, nreg, 100.*n_vol_cor/nreg);
529 egsInformation(" # of voxels with vol correction & D > 0 = %d / %d (%.3F%)\n", n_vol_cor_0, nreg, 100.*n_vol_cor_0/nreg);
530 egsInformation(" # of voxels with vol correction & D = 0 = %d / %d (%.3F%)\n", n_vol_cor_no_dose, nreg, 100.*n_vol_cor_no_dose/nreg);
531
532 egsInformation(" Average error on vol correction where:\n");
533 egsInformation(" D > 0 & at least 0%% of voxel covered = %.2F% (%d voxels)\n",
534 n_vol_cor_0 > 0 ? 100.*avg_vol_err_0/n_vol_cor_0 : 0, n_vol_cor_0);
535 egsInformation(" Average error on vol corrections where:\n");
536 egsInformation(" D > 0 & at least 50%% of voxel covered = %.2F% (%d voxels)\n",
537 n_vol_cor_50 > 0 ? 100.*avg_vol_err_50/n_vol_cor_50 : 0, n_vol_cor_50);
538 egsInformation(" Average error on vol corrections where:\n");
539 egsInformation(" D > 0 & at least 99%% of voxel covered = %.2F% (%d voxels)\n",
540 n_vol_cor_99 > 0 ? 100.*avg_vol_err_99/n_vol_cor_99 : 0, n_vol_cor_99);
541
542 egsInformation("\n Max dose = %.5E %s\n", max_dose, units.c_str());
543 egsInformation(" # of voxels with D > 0 = %d / %d (%.3G%)\n", n_gt_0, nreg, 100.*n_gt_0/nreg);
544 egsInformation(" # of voxels with D = 0 = %d / %d (%.3G%)\n", nreg - n_gt_0, nreg, 100.*(nreg - n_gt_0)/nreg);
545 egsInformation(" Average dose for D > 0 = %.5E %s\n", avg_dose_gt_0 / n_gt_0, units.c_str());
546 egsInformation(" Average dose for D >= 0 = %.5E %s\n", avg_dose / nreg, units.c_str());
547 egsInformation(" Average error on dose for D > 0 = %.2F%\n",
548 n_gt_0 ? 100.*avg_dose_gt_0_err / n_gt_0 : 0);
549 egsInformation(" Average error on vol cors for D > 0 = %.2F% (%d voxels)\n",
550 n_vol_cor_0 ? 100.*avg_vol_err_0 / n_vol_cor_0 : 0, n_vol_cor_0);
551 egsInformation(" Average total error on dose for D > 0 = %.2F%\n",
552 n_gt_0 ? 100.*avg_tot_gt_0_err / n_gt_0 : 0);
553
554 egsInformation("\n 20%% of Dmax = %.5E %s\n", 0.2*max_dose, units.c_str());
555 egsInformation(" # of voxels with D >= 20%%*Dmax = %d / %d (%.3G%)\n", n_gt_20, nreg, 100.*n_gt_20/nreg);
556 egsInformation(" Average dose for D > 20%%*Dmax = %.5E %s\n", avg_dose_gt_20 / n_gt_20, units.c_str());
557 egsInformation(" Average error on doses for D >= 20%%*Dmax = %.2F%\n",
558 n_gt_20 ? 100.*avg_dose_gt_20_err / n_gt_20 : 0);
559 egsInformation(" Average error on vol cors for D >= 20%%*Dmax = %.2F% (%d voxels)\n",
560 n_vol_cor_d20 ? 100.*avg_vol_err_d20 / n_vol_cor_d20 : 0, n_vol_cor_d20);
561 egsInformation(" Average total error on dose for D >= 20%%*Dmax = %.2F%\n",
562 n_gt_20 ? 100.*avg_tot_gt_20_err / n_gt_20 : 0);
563
564}
565
566/* output Top N doses for tracklength and interaction scoring */
567void EB_Phantom::outputTopDoses(int top_n, vector<RegionResult> region_results) {
568
569 top_n = min(top_n, geometry->regions());
570
571 string fmt = "\n\nTop %d dose results for geometry named '%s'";
572 if (fabs(dose_scale -1) > 1E-10) {
573 fmt += " (scaled)";
574 }
575
576 egsInformation((fmt+"\n").c_str(), top_n, (geometry->getName()).c_str());
577
578 string title = (
579 " Region Med # Volume (Nominal) / cm^3 Tracklength Dose / Gy/hist (Derr, Verr) "
580 "Interaction Dose / Gy/hist (Derr, Verr)\n"
581 );
582 egsInformation("%s\n", string(title.size()-1, '=').c_str());
583 egsInformation(title.c_str());
584
585 string sep(title.size()-1, '-');
586 egsInformation((sep+"\n").c_str());
587
588 // display top n results
589 vector<RegionResult> top_results(top_n);
590 if (!tlen_score && edep_score) {
591 partial_sort_copy(region_results.begin(), region_results.end(), top_results.begin(), top_results.end(),
593 } else {
594 partial_sort_copy(region_results.begin(), region_results.end(), top_results.begin(), top_results.end(),
596 }
597
598 for (vector<RegionResult>::iterator rrit=top_results.begin(); rrit!=top_results.end(); rrit++) {
599
600 RegionResult rr = *rrit;
601
602 double vol = getCorrectedVolume(rr.reg);
603 double dv = getVolumeUncertainty(rr.reg);
604 double uncorvol = getUncorrectedVolume(rr.reg);
605 double total_tlen_unc = std::sqrt(dv*dv + rr.tlen_err*rr.tlen_err);
606 double total_edep_unc = std::sqrt(dv*dv + rr.edep_err*rr.edep_err);
607
608 int med = geometry->medium(rr.reg);
609
610 string fmt;
611 if (total_tlen_unc > 0.001){
612 fmt = "%7d %5d %11.3E(%8.3E) +/- %6.2f%% %12.3E +/- %6.2f%% (%6.2f%%, %6.2f%%) %10.3E +/- %6.2f%% (%6.2f%%, %6.2f%%)\n";
613 }else{
614 fmt = "%7d %5d %11.3E(%8.3E) +/- %6.3f%% %11.4E +/- %6.3f%% (%6.3f%%, %6.3f%%) %10.4E +/- %7.3f%% (%6.3f%%, %6.3f%%)\n";
615 }
616 egsInformation(
617 fmt.c_str(),
618 rr.reg,
619 med,
620 vol,
621 uncorvol,
622 100*dv,
623 rr.tlen,
624 100*total_tlen_unc,
625 100*rr.tlen_err,
626 100*dv,
627 edep_score ? rr.edep : 0,
628 edep_score ? 100*total_edep_unc : 0,
629 edep_score ? 100*rr.edep_err : 0,
630 edep_score ? 100*dv : 0
631 );
632 }
633
634}
635
636void EB_Phantom::getScoringArrays(vector<EGS_ScoringArray *> &scores, vector<string> &types,
637 vector<string> &descriptions) {
638
639 if (tlen_score) {
640 scores.push_back(tlen_score);
641 types.push_back("tlen");
642 descriptions.push_back("tracklength");
643 }
644
645 if (edep_score) {
646 scores.push_back(edep_score);
647 types.push_back("edep");
648 descriptions.push_back("energy deposition");
649 }
650
651 if (prim_score) {
652 scores.push_back(tlen_score);
653 scores.push_back(prim_score);
654 scores.push_back(sscat_score);
655 scores.push_back(mscat_score);
656 types.push_back("to");
657 types.push_back("pr");
658 types.push_back("ss");
659 types.push_back("ms");
660 descriptions.push_back("total primary + scattered");
661 descriptions.push_back("primary");
662 descriptions.push_back("single scattered");
663 descriptions.push_back("multiple scattered");
664 }
665
666}
667
668void EB_Phantom::getEGSdatScoringArrays(vector<EGS_ScoringArray *> &scores) {
669
670 if (tlen_score) {
671 scores.push_back(tlen_score);
672 }
673
674 if (edep_score) {
675 scores.push_back(edep_score);
676 }
677
678 if (prim_score) {
679 scores.push_back(prim_score);
680 scores.push_back(sscat_score);
681 scores.push_back(mscat_score);
682 }
683
684}
685
686
687/* create 3ddose file and write bounds/doses to it */
689
690 vector<EGS_ScoringArray *> scores;
691 vector<string> types;
692 vector<string> descriptions;
693 getScoringArrays(scores, types, descriptions);
694
695 egsInformation("\nDose Output Files for %s\n%s\n", geometry->getName().c_str(), string(80,'=').c_str());
696 for (size_t s=0; s < scores.size(); s++) {
697
698 string type = types[s];
699
700 string sep = type == "tlen" ? "" : "."+type;
701 string extension = space2underscore("."+geometry->getName()) + sep + ".3ddose";
702 extension += (format == "gzip") ? ".gz" : "";
703 string fname = app->constructIOFileName(extension.c_str(), true);
704 egsInformation(
705 "\nWriting %s dose to %s%s:",
706 descriptions[s].c_str(),
707 app->getOutputFile().c_str(),
708 extension.c_str()
709 );
710
711 outputDoseStats(scores[s], type);
712 if (format == "gzip") {
713 ogzstream out;
714 out.open(fname.c_str());
715 output3DBounds(out);
716 output3DDoses(out, scores[s], type);
717 out.close();
718
719 } else {
720 ofstream out;
721 out.open(fname.c_str());
722 output3DBounds(out);
723 output3DDoses(out, scores[s], type);
724 out.close();
725 }
726
727 }
728
729}
730
731
732/* write the boundaries section for a 3dose files. This will work for arbitrary EGS_BaseGeometry
733 * classes, but will of course only be accurate for geometries which have implemented getNRegDir
734 * and getBound. */
735void EB_Phantom::output3DBounds(ostream &out) {
736
737 int nx = max(1, geometry->getNRegDir(XDIR));
738 int ny = max(1, geometry->getNRegDir(YDIR));
739 int nz = max(1, geometry->getNRegDir(ZDIR));
740
741 out << nx << " " << ny << " " << nz << "\n";
742
743 for (int dir=XDIR; dir <= ZDIR; dir++) {
744
745 int nbounds = geometry->getNRegDir(dir) + 1;
746
747 if (nbounds <= 1) {
748 out << -999 << "\t" << 999 << "\n";
749 } else {
750 for (int idx=0; idx < nbounds; idx++) {
751 string delim = (idx == nbounds - 1) ? "\n" : "\t";
752 out<< geometry->getBound(dir, idx) << delim ;
753 }
754 }
755 }
756}
757
758void EB_Phantom::output3DDoses(ostream &out, EGS_ScoringArray *score, string type) {
759
760
761 // write doses on first loop, uncertainty on second
762 for (int loop = 0; loop < 2; loop++) {
763 for (int ireg=0; ireg < score->regions(); ireg++) {
764
765 double result, dr, dv;
766 getResult(score, ireg, type, result, dr);
767
768 if (loop == 0) {
769 out << result << "\t";
770 } else {
771 dv = getVolumeUncertainty(ireg);
772 out << sqrt(dr*dr + dv*dv) << "\t";
773 }
774
775 }
776 out << "\n";
777 }
778
779}
780
781void EB_Phantom::writeEGSPhant(ostream &out) {
782
783 int nmed = EGS_BaseGeometry::nMedia();
784
785 out << nmed << "\n";
786 for (int i=0; i < nmed; i++) {
787 out << EGS_BaseGeometry::getMediumName(i) << "\n";
788 }
789
790 for (int i=0; i < nmed; i++) {
791 out << the_etcontrol->estepe << "\t";
792 }
793 out << "\n";
794
795 output3DBounds(out);
796
797 int nx = max(1, geometry->getNRegDir(XDIR));
798 int ny = max(1, geometry->getNRegDir(YDIR));
799 int nz = max(1, geometry->getNRegDir(ZDIR));
800 int nreg = 0;
801
802 for (int loop = 0; loop < 2; loop++) {
803 for (int kk=0; kk < nz; kk++) {
804 for (int jj=0; jj < ny; jj++) {
805 for (int ii=0; ii < nx; ii++) {
806 int ireg = ii + jj*nx + kk*nx*ny;
807
808 if (loop == 0) {
809 nreg = geometry->medium(ireg)+1;
810 out << char(49 + nreg + (nreg>8?7:0) + (nreg>34?6:0));
811 } else {
812 out << getRealRho(ireg) << "\t";
813 }
814 }
815 out << "\n";
816 }
817 out << "\n";
818 }
819 }
820
821}
822
823string EB_Phantom::medIndex(int medium){
824
825 string medIdx = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
826 if (medium >= medIdx.length()){
827 egsFatal("Too many media to write egsPhant");
828 }
829
830 return string(1, medIdx[medium]);
831}
832
833void EB_Phantom::outputEGSPhant(string format) {
834
835 egsInformation("\nEGSPhant Output File for %s\n%s\n", geometry->getName().c_str(), string(80,'=').c_str());
836
837 string extension = space2underscore("."+geometry->getName()) + ".egsphant";
838 extension += (format == "gzip") ? ".gz" : "";
839 string out_name = app->getOutputFile()+extension;
840 string fname = app->constructIOFileName(extension.c_str(), true);
841
842 egsInformation("Writing egsphant file to %s\n", (out_name).c_str());
843
844 if (format == "gzip") {
845 ogzstream ogout(fname.c_str());
846 writeEGSPhant(ogout);
847 ogout.close();
848 } else {
849 ofstream out(fname.c_str());
850 writeEGSPhant(out);
851 out.close();
852 }
853}
854
856
857 EGS_Float total = 0;
858 int nreg;
859 for (nreg = 0; nreg < geometry->regions(); nreg++) {
860 total += getCorrectedVolume(nreg);
861 }
862 return nreg > 0 ? total / nreg : 0;
863}
864
865void EB_Phantom::writeVoxelInfo(ostream &out) {
866
867 out << std::setprecision(6) << std::scientific;
868 string header = (
869 "Region, Volume / cm^3, Nominal Volume / cm^3, Vol Cor Unc, Mass / g, Density / g/cm^3, Med, "
870 "Dose (tracklength) / Gy/hist, Total Dose Unc (tracklength), Dose Unc (tracklength)"
871 );
872 if (edep_score){
873 header += ", Dose (interaction) / Gy/hist, Total Dose Unc (interaction), Dose Unc (interaction)";
874 }
875 out << header << "\n";
876
877 for (int i=0; i < geometry->regions(); i++) {
878 EGS_Float r=0, dr=0, r_edep, dr_edep, dv;
879 if (tlen_score) {
880 getResult(tlen_score, i, "tlen", r, dr);
881 }
882
883 if (edep_score) {
884 getResult(edep_score, i, "edep", r_edep, dr_edep);
885 }
886 dv = getVolumeUncertainty(i);
887 out << i << ", " << getCorrectedVolume(i) << ", " << getUncorrectedVolume(i) << ", " << dv << ", ";
888 out << getRealMass(i) << ", " << getRealRho(i) << ", " << EGS_BaseGeometry::getMediumName(geometry->medium(i)) << ", ";
889 out << r << ", " << sqrt(dr*dr + dv*dv) << ", " << dr;
890 if (edep_score){
891 out << ", " << r_edep << ", " << sqrt(dr_edep*dr_edep + dv*dv) << ", " << dr_edep;
892 }
893 out << "\n";
894 }
895}
896
897void EB_Phantom::outputVoxelInfo(string format) {
898
899 egsInformation("\nVoxel Info File Output File for %s\n%s\n", geometry->getName().c_str(), string(80,'=').c_str());
900 string extension = space2underscore("."+geometry->getName()) + ".voxels";
901 extension += (format == "gzip") ? ".gz" : "";
902 string fname = app->constructIOFileName(extension.c_str(), true);
903
904 egsInformation(
905 "Writing voxel info file to %s\n",
906 (app->getOutputFile() + extension).c_str()
907 );
908
909 if (format == "gzip") {
910 ogzstream out;
911 out.open(fname.c_str());
912 writeVoxelInfo(out);
913 out.close();
914 } else {
915 ofstream out;
916 out.open(fname.c_str());
917 writeVoxelInfo(out);
918 out.close();
919 }
920}
921
923 size_t nrecords = corrected_volumes.size();
924
925 out << nrecords << "\n";
926
927 for (int i=0; i < geometry->regions(); i++) {
928 if (hasVolCor(i)) {
929 out << i << " " << getCorrectedVolume(i) << " " << volume_uncertainty[i] << "\n";
930 }
931 }
932}
933
935
936 egsInformation("\nVolume Correction Output File for %s\n%s\n", geometry->getName().c_str(), string(80,'=').c_str());
937 string extension = space2underscore("."+geometry->getName()) + ".volcor";
938 extension += (format == "gzip") ? ".gz" : "";
939 string fname = app->constructIOFileName(extension.c_str(), true);
940 string gname = geometry->getName();
941
942 egsInformation(
943 "Writing volume correction file for %s to %s file %s\n",
944 gname.c_str(), format.c_str(), (app->getOutputFile()+"."+gname+".volcor").c_str());
945
946 if (format == "gzip") {
947 ogzstream out(fname.c_str());
949 out.close();
950 } else {
951 ofstream out;
952 out.open(fname.c_str());
954 out.close();
955 }
956}
957
958int EB_Phantom::outputData(ostream *ofile) {
959
960 (*ofile) << total_radiant_e << "\n";
961
962 vector<EGS_ScoringArray *> scores;
964
965 vector<EGS_ScoringArray *>::iterator it = scores.begin();
966 for (; it != scores.end(); ++it) {
967 bool ok = (*it)->storeState(*ofile);
968 if (!ok) {
969 return 1;
970 }
971 }
972
973 return 0;
974
975}
976
977int EB_Phantom::readData(istream *ifile) {
978
979
980 (*ifile) >> total_radiant_e;
981
982 vector<EGS_ScoringArray *> scores;
984
985 vector<EGS_ScoringArray *>::iterator it = scores.begin();
986 for (; it != scores.end(); ++it) {
987 bool ok = (*it)->setState(*ifile);
988 if (!ok) {
989 return 1;
990 }
991 }
992 return 0;
993
994}
995
996int EB_Phantom::addState(istream &data) {
997
998 EGS_Float total_radiant_e_tmp;
999 data >> total_radiant_e_tmp;
1000 total_radiant_e += total_radiant_e_tmp;
1001
1002 vector<EGS_ScoringArray *> scores, tmp_scores;
1003 getEGSdatScoringArrays(scores);
1004
1005 vector<EGS_ScoringArray *>::iterator it = scores.begin();
1006 for (; it != scores.end(); ++it) {
1007
1008 EGS_ScoringArray tmp((*it)->regions());
1009
1010 bool ok = tmp.setState(data);
1011 if (!ok) {
1012 return 1;
1013 }
1014
1015 (**it) += tmp;
1016 }
1017
1018 return 0;
1019
1020}
1021
1023 total_radiant_e = 0;
1024
1025 vector<EGS_ScoringArray *> scores;
1026 getEGSdatScoringArrays(scores);
1027
1028 vector<EGS_ScoringArray *>::iterator it = scores.begin();
1029 for (; it != scores.end(); ++it) {
1030 (*it)->reset();
1031 }
1032
1033
1034}
@ YDIR
YDIR=1 y dir for rectilinear, r dir for cylindrical.
Definition phantom.h:137
@ XDIR
XDIR=0 x dir for rectilinear, r dir for spherical, z dir for cylindrical.
Definition phantom.h:136
@ ZDIR
ZDIR=2 z dir for rectilinear.
Definition phantom.h:138
void getResult(EGS_ScoringArray *, int ireg, string type, EGS_Float &r, EGS_Float &dr)
get result for region from scoring array and normalize based on the type requested.
Definition phantom.cpp:349
void outputEGSPhant(string)
initialize and write an egsphant file for this phantom
Definition phantom.cpp:833
void scoreTlen(int ir, EGS_Float dose, EGS_Particle *p)
add tracklength dose to region ir
Definition phantom.cpp:133
bool hasVolCor(int ireg)
Definition phantom.cpp:310
void output3ddoseResults(string)
write the phantom boundaries, doses and uncertainties to 3ddose file
Definition phantom.cpp:688
void outputVoxelInfo(string format)
write voxel volumes, mass, desnity etc
Definition phantom.cpp:897
std::map< int, double > volume_uncertainty
Definition phantom.h:96
int global_reg_stop
ending global region index for this phantom
Definition phantom.h:294
void update(EB_Message message, void *data)
Definition phantom.cpp:124
bool can_write_3ddose
this phantom can output 3ddose files
Definition phantom.h:297
EGS_Float getUncorrectedVolume(int ireg)
get corrected volume for a given region
Definition phantom.cpp:302
EGS_BaseGeometry * geometry
the phantom geometry object
Definition phantom.h:284
void getCurrentScore(int ireg, double &sum, double &sum2)
get current tlen score for region
Definition phantom.cpp:332
void output3DDoses(ostream &out, EGS_ScoringArray *score, string type)
write input scoring array to 3ddose file
Definition phantom.cpp:758
vector< int > getRegionsWithCorrections()
return a vector of all regions which were corrected
Definition phantom.cpp:194
double getTlenNorm(int ireg)
Definition phantom.cpp:344
int global_reg_start
starting global region index for this phantom
Definition phantom.h:293
EGS_Float effective_histories
Definition phantom.h:91
void outputTopDoses(int top_n, vector< RegionResult > region_results)
write the top_n doses to console
Definition phantom.cpp:567
EGS_Float getRealRho(int ireg)
return actual density for region
Definition phantom.cpp:285
void resetCounter()
Definition phantom.cpp:1022
void writeVolumeCorrection(ostream &)
write voxel volumes to file
Definition phantom.cpp:922
void setEffectiveHistories(EGS_Float current_case)
set number of effective histories for normalizing scoring arrays
Definition phantom.cpp:253
void enableScatterScoring()
enableInteractionScoring must be called before simulation begins if you want to score dose with inter...
Definition phantom.cpp:222
void setHistory(EGS_I64 current_case)
set current history on scoring arrays for proper statistics
Definition phantom.cpp:234
bool needs_user_geoms
this phantom requires user specified geometries
Definition phantom.h:296
EB_Phantom(EGS_Application *, EGS_BaseGeometry *, set< int > global_regions, int nsource, Publisher *publisher)
EB_Phantom constructor.
Definition phantom.cpp:104
static const string threeddose_geom_types[]
Definition phantom.h:101
int readData(istream *ifile)
Definition phantom.cpp:977
void scoreEdep(int ir, EGS_Float dose)
add energy deposition dose to region ir
Definition phantom.cpp:155
EGS_Float getRealMass(int ireg)
return (corrected) real mass for region
Definition phantom.cpp:291
static const string autovol_phantom_geom_types[]
Definition phantom.h:97
EGS_Application * app
Parent application instance. Required for constructing filenames.
Definition phantom.h:78
EGS_ScoringArray * sscat_score
Tracklenth scored dose from single scattered particles.
Definition phantom.h:82
vector< RegionResult > getRegionResults()
create a vector of RegionResult structs which can then be sorted by dose value. used for output routi...
Definition phantom.cpp:377
EGS_Float total_radiant_e
Definition phantom.h:89
void setCorrectedVolume(int ir, double fraction, double unc=0)
Allow user to tell phantom what the actual volume of a region is.
Definition phantom.cpp:202
void getScoringArrays(vector< EGS_ScoringArray * > &scores, vector< string > &types, vector< string > &descriptions)
get all active scoring arrays, their types and descriptions
Definition phantom.cpp:636
string medIndex(int medium)
get medium index 1-9A-Z
Definition phantom.cpp:823
EGS_ScoringArray * mscat_score
Tracklenth scored dose from multiple scattered particles.
Definition phantom.h:84
static bool canWrite3ddose(const string &geom_type)
function for checking whether a given geometry type can output 3ddose files
Definition phantom.cpp:180
void outputResults(int top_n=20, string output_3ddose="text", string output_egsphant="text", string output_voxinfo="text", string output_volcor="text")
tell phantom to output its results.
Definition phantom.cpp:259
int addState(istream &ifile)
Definition phantom.cpp:996
EGS_ScoringArray * prim_score
Tracklenth scored dose from primary particles.
Definition phantom.h:81
void writeEGSPhant(ostream &)
write actual egsphant data to file for this phantom
Definition phantom.cpp:781
void getEGSdatScoringArrays(vector< EGS_ScoringArray * > &scores)
get all active scoring arrays for writing to egsdat file
Definition phantom.cpp:668
void enableTLenScoring()
enableTLenScoring must be called before simulation begins if you want to score dose with tracklength ...
Definition phantom.cpp:208
void outputDoseStats(EGS_ScoringArray *score, string type)
write some stats about dose arrays
Definition phantom.cpp:408
Publisher * publisher
Definition phantom.h:93
EGS_I64 cur_history
Definition phantom.h:90
EGS_ScoringArray * tlen_score
Tracklength dose scoring array.
Definition phantom.h:79
std::map< int, double > corrected_volumes
Corrected volume in a given region.
Definition phantom.h:95
void outputVolumeCorrection(string format)
intialize and output write voxel volumes to file
Definition phantom.cpp:934
EGS_Float dose_scale
Definition phantom.h:87
void output3DBounds(ostream &out)
write the phantom bounds to 3ddose file
Definition phantom.cpp:735
EGS_Float getCorrectedVolume(int ireg)
get uncorrected volume for a given region
Definition phantom.cpp:314
void setDoseScale(EGS_Float)
set dose scaling factor for output
Definition phantom.cpp:162
static bool needsUserVolumes(const string &geom_type)
function for checking whether a given geometry type requires user specified volumes
Definition phantom.cpp:166
void writeVoxelInfo(ostream &)
write voxel info file
Definition phantom.cpp:865
int outputData(ostream *ofile)
Definition phantom.cpp:958
set< int > global_regions
the set of all global regions contained in this phantom
Definition phantom.h:291
EGS_Float getUncorrectedMass(int ireg)
return uncorrected for region
Definition phantom.cpp:298
int nsources
Definition phantom.h:86
EGS_Float getVolumeUncertainty(int ireg)
get volume unc
Definition phantom.cpp:323
void enableInteractionScoring()
enableInteractionScoring must be called before simulation begins if you want to score dose with inter...
Definition phantom.cpp:215
EGS_Float avgVoxelVol()
return average voxel volume
Definition phantom.cpp:855
EGS_ScoringArray * edep_score
Interaction scored dose scoring array.
Definition phantom.h:80
static bool isMultScat(int latch)
Definition latch.cpp:121
static bool isPrimary(int latch)
Definition latch.cpp:105
static bool isSingleScat(int latch)
Definition latch.cpp:113
void subscribe(Subscriber *s, EB_Message message)
Definition pubsub.h:83
void close()
Definition gzstream.C:154
void open(const char *name, int open_mode=std::ios::out)
Definition gzstream.h:121
handle particles latch bits for egs_brachy
string space2underscore(std::string text)
Definition phantom.cpp:84
bool tlenRegTopResultCompare(const RegionResult &firstElem, const RegionResult &secondElem)
Definition phantom.cpp:73
bool edepRegTopResultCompare(const RegionResult &firstElem, const RegionResult &secondElem)
Definition phantom.cpp:77
Header file for phantom objects.
EB_Message
Definition pubsub.h:54
@ PARTICLE_ESCAPING_SOURCE
Definition pubsub.h:60
@ NEW_HISTORY
Definition pubsub.h:56
double tlen
Definition phantom.cpp:63
double volume_unc
Definition phantom.cpp:61
double volume
Definition phantom.cpp:60
double edep_err
Definition phantom.cpp:67
double tlen_err
Definition phantom.cpp:64
double edep
Definition phantom.cpp:66