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/eb_volcor.cpp
Go to the documentation of this file.
1/*
2################################################################################
3#
4# egs_brachy eb_volcor.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
45#include <eb_volcor.h>
46#include <iostream>
47#include <fstream>
48#include "gzstream.h"
49
50namespace ebvolcor {
51
54bool isGZip(istream &vfile) {
55 return (vfile.get() == 0x1f && vfile.get() == 0x8b);
56}
57
77EGS_Float getShapeVolume(EGS_Input *shape_inp) {
78
79 if (!shape_inp) {
80 return -1;
81 }
82
83 EGS_Float volume;
84 int err = shape_inp->getInput("shape volume", volume);
85 if (!err) {
86 return volume;
87 }
88
89 string shape_type;
90 shape_inp->getInput("type", shape_type);
91
92 if (shape_type == "cylinder") {
93 EGS_Float radius, height;
94 shape_inp->getInput("radius", radius);
95 shape_inp->getInput("height", height);
96 return M_PI*radius*radius*height;
97 } else if (shape_type == "sphere") {
98 EGS_Float radius, height;
99 shape_inp->getInput("radius", radius);
100 shape_inp->getInput("height", height);
101 return 4./3.*M_PI*radius*radius*radius;
102 } else if (shape_type == "spherical shell" || shape_type == "egsSphericalShell") {
103 EGS_Float ri, ro;
104 int hemi;
105 int err = shape_inp->getInput("inner radius", ri);
106 if (err) {
107 egsFatal("getShapeVolume :: missing 'inner radius' input for shape\n");
108 }
109
110 err = shape_inp->getInput("outer radius", ro);
111 if (err) {
112 egsFatal("getShapeVolume :: missing 'outer radius' input for shape\n");
113 }
114
115 err = shape_inp->getInput("hemisphere", hemi);
116 if (err) {
117 hemi = 0;
118 }
119 EGS_Float vol = 4./3.*M_PI*(ro*ro*ro - ri*ri*ri);
120 if (hemi !=0) {
121 return vol/2;
122 }
123
124 return vol;
125
126 } else if (shape_type == "box") {
127 vector<EGS_Float> box_size;
128 shape_inp->getInput("box size", box_size);
129 if (box_size.size() == 3) {
130 return box_size[0]*box_size[1]*box_size[2];
131 } else {
132 return box_size[0]*box_size[0]*box_size[0];
133 }
134 }
135 if (shape_type == "") {
136 egsWarning("Either include a `type` or `shape volume` input key.");
137 } else {
138 egsWarning(
139 "The volume (in cm^3) for shape type '%s' must be specified using a `shape volume` input key.",
140 shape_type.c_str()
141 );
142 }
143
144 return -1;
145
146}
147
150 vector<string> choices;
151 choices.push_back("none");
152 choices.push_back("zero dose");
153 choices.push_back("correct");
154 if (!input){
156 }else{
157 mode = (VolCorMode)input->getInput("correction type", choices, (int)NO_CORRECTION);
158 }
159}
160
163
164 EGS_Input *shape_inp = input->takeInputItem("shape");
165 if (!shape_inp) {
166 egsWarning("VolCor::Options::setBoundsShape - no `shape` input found.\n");
167 return 1;
168 }
169
170 bounds_volume = getShapeVolume(shape_inp);
171 if (bounds_volume < 0) {
172 egsWarning("VolCor::Options::setBoundsShape - unable to calculate volume.\n");
173 delete shape_inp;
174 return 1;
175 }
176
177 bounds = EGS_BaseShape::createShape(shape_inp);
178 if (!bounds) {
179 egsWarning("VolCor::Options::setBoundsShape - `shape` input not valid.\n");
180 delete shape_inp;
181 return 1;
182 }
183
184 sobolAllowed = bounds->getObjectType() == "box";
185
186 delete shape_inp;
187 return 0;
188
189}
190
193
194 int err = input->getInput("density of random points (cm^-3)", density);
195 if (err) {
196 egsWarning("The volume correction 'density of random points (cm^-3)' input was not found\n");
198 }
199
200 npoints = floor(max(1., density*bounds_volume));
201}
202
205
206 EGS_Input *rng_input = input->getInputItem("rng definition");
207
208 if (rng_input) {
209 string type;
210 int err = rng_input->getInput("type", type);
211 if (!err && rng_input->compare(type, "sobol")) {
212 if (!sobolAllowed) {
213 egsWarning(
214 "Sobol QRNG are not allowed for non rectilinear shapes. "
215 "Using default Ranmar instead.\n"
216 );
217 } else {
218 rng = new EGS_Sobol(rng_input);
219 }
220 } else {
221 rng = EGS_RandomGenerator::createRNG(rng_input);
222 }
223 }
224
225 if (!rng) {
226 if (sobolAllowed) {
227 rng = new EGS_Sobol();
228 } else {
229 rng = EGS_RandomGenerator::defaultRNG();
230 }
231 }
232
233}
234
237
238 int err = input->getInput("total coverage threshold %", covered_threshold);
239 if (err) {
240 egsWarning("The input 'total coverage threshold %%' input was not found. Using 99.9%\n");
241 covered_threshold = 99.9;
242 }
243
244 if (covered_threshold > 1.){
245 covered_threshold /= 100;
246 }
247
248}
249
251 return bounds->getRandomPoint(rng);
252}
253
255
256 EGS_Input *source_cor_inp = input->takeInputItem("source volume correction");
257 source_opts = new Options(source_cor_inp);
258 if (source_cor_inp) {
259 delete source_cor_inp;
260 }
261
262
263 EGS_Input *gen_cor_inp = input->takeInputItem("extra volume correction");
264 gen_opts = new Options(gen_cor_inp);
265 if (gen_cor_inp) {
266 delete gen_cor_inp;
267 }
268
269 EGS_Input *file_cor_inp = input->takeInputItem("volume correction from file");
270
271 if (file_cor_inp) {
272 EGS_Input *ij;
273 int input_num =1;
274 while ((ij = file_cor_inp->takeInputItem("phantom file"))) {
275
276 string phantom, filename;
277 vector<string> phant_file;
278 ij->getInput("phantom file", phant_file);
279 if (phant_file.size() != 2) {
280 egsWarning("VolumeCorrector::setupOptions ignoring 'phantom file' input number %d\n", input_num);
281 } else {
282 phantom_files[phant_file[0]] = phant_file[1];
283 }
284
285 input_num += 1;
286 delete ij;
287
288 }
289
290 delete file_cor_inp;
291 }
292
293}
294
296
297 HitCounterT hit_counter;
298
299 for (EGS_I64 i=0; i < gen_opts->npoints; i++) {
300
301 EGS_Vector point = gen_opts->getRandomPoint();
302 int global_reg = base_geom->isWhere(point);
303
304 for (size_t pidx=0; pidx < phantoms.size(); pidx++) {
305 bool phant_is_covered = !phantoms[pidx]->globalRegIsInPhant(global_reg);
306 if (!phant_is_covered) {
307 continue;
308 }
309
310 int phant_reg = phantoms[pidx]->geometry->isWhere(point);
311
312 PhantRegT phant_and_reg(pidx, phant_reg);
313 hit_counter[phant_and_reg] += 1;
314 }
315
316 }
317
318 applyVolumeCorrections(gen_opts, hit_counter);
319
320 return 0;
321
322}
323// source specific MC volume calculation
324//
325// Works by generating random points within the sources boundary shape and
326// then, if the point is within the source, transforming each point to
327// all the sources locations and checking which phantom region the point lands in.
328// The volume of a region occupied by a source then is the bounding shape volume
329// times the fraction of points landing in the source. The volume of the region
330// then is the uncorrected volume minus the volume occupied by source
331// Vcor(reg) = V(reg) - V_source(reg)
332// = V(reg) - V_b * n_points_in_source(reg)/n_total
333//
334// where V_b is volume of bounding shape
336
337 EGS_Vector point;
338
339 HitCounterT hit_counter;
340 EGS_I64 n_in_source = 0;
341
342 for (EGS_I64 i=0; i < source_opts->npoints; i++) {
343
344 point = source_opts->getRandomPoint();
345 base_transform->transform(point);
346
347 int ir = base_geom->isWhere(point);
348 if (!ginfo->isSource(ir)) {
349 continue;
350 }
351 n_in_source += 1;
352 base_transform_inv->transform(point);
353
354 for (size_t sidx = 0; sidx < transforms.size(); sidx++) {
355
356 EGS_Vector transformed(point);
357 transforms[sidx]->transform(transformed);
358
359 for (size_t phant= 0; phant<phantoms.size(); phant++) {
360 int reg = phantoms[phant]->geometry->isWhere(transformed);
361 bool in_phant = reg >= 0;
362
363 if (in_phant) {
364 PhantRegT phant_reg(phant, reg);
365 hit_counter[phant_reg] += 1;
366 }
367
368 }
369 }
370 }
371
373
374 return source_opts->bounds_volume*(double)n_in_source/(int)source_opts->npoints;
375
376}
377
379
380 bool zero_dose = opts->mode == ZERO_DOSE;
381 double bounds_vol = opts->bounds_volume;
382 double npoints = opts->npoints;
383
384 for (HitCounterT::iterator hi = hit_counter.begin(); hi != hit_counter.end(); hi++) {
385
386 int phant_idx = hi->first.first;
387 int phant_reg = hi->first.second;
388 if (phant_reg < 0) {
389 continue;
390 }
391 int hits = hi->second;
392 double reg_vol = phantoms[phant_idx]->getUncorrectedVolume(phant_reg);
393 double corrected_vol = reg_vol - bounds_vol*double(hits)/npoints;
394 double unc;
395
396 /* consider a voxel coverd if corrected vol is 0.1% (default) or less of nominal volume */
397 bool voxel_covered = corrected_vol / reg_vol <= (1. - opts->covered_threshold);
398 if (!(voxel_covered || zero_dose)){
399 unc = bounds_vol/corrected_vol*sqrt(hits*(npoints-hits)/(npoints*npoints*npoints));
400 } else {
401 corrected_vol = 0;
402 unc = 0;
403 }
404 phantoms[phant_idx]->setCorrectedVolume(phant_reg, corrected_vol, unc);
405 }
406
407}
408
409void readVolumes(istream &vfile, vector<RegVolume> &reg_volumes) {
410 size_t nrecords;
411 vfile >> nrecords;
412 for (size_t rec = 0; rec < nrecords; rec++) {
413 int reg;
414 EGS_Float vol, vol_unc;
415 vfile >> reg >> vol >> vol_unc;
416 RegVolume rvol = {reg, vol, vol_unc};
417 reg_volumes.push_back(rvol);
418 }
419}
420
421int loadVolumes(string fname, vector<RegVolume> &reg_volumes) {
422 ifstream vfile(fname.c_str());
423 if (!vfile.is_open()) {
424 return 1;
425 }
426
427 if (isGZip(vfile)) {
428 igzstream gzf(fname.c_str());
429 readVolumes(gzf, reg_volumes);
430 gzf.close();
431 } else {
432 readVolumes(vfile, reg_volumes);
433 }
434 vfile.close();
435
436 return 0;
437
438}
439
441
442 map<string, int> nreg_read_for_phant;
443
444 for (size_t phant_idx=0; phant_idx < phantoms.size(); phant_idx++) {
445
446 string phant_name = phantoms[phant_idx]->geometry->getName();
447
448 if (phantom_files.count(phant_name)) {
449
450 string fname = phantom_files[phant_name];
451
452 vector<RegVolume> volumes;
453 int error = loadVolumes(fname, volumes);
454 if (error) {
455 egsFatal(
456 "VolumeCorrector::loadFileVolumeCorrections: failed to read "
457 "volumes for '%s' from file '%s'\n", phant_name.c_str(), fname.c_str()
458 );
459 }
460
461 nreg_read_for_phant[phant_name] = volumes.size();
462
463 for (size_t rv=0; rv < volumes.size(); rv++) {
464 RegVolume reg_vol = volumes[rv];
465 phantoms[phant_idx]->setCorrectedVolume(reg_vol.ir, reg_vol.vol, reg_vol.unc);
466 }
467 }
468
469 }
470 return nreg_read_for_phant;
471}
472}
473
EGS_RandomGenerator wrapper for Sobol library.
Definition egs_sobol.h:71
bool isSource(int ir)
Definition ginfo.cpp:503
Volume correction initialization helper class.
Definition eb_volcor.h:145
EGS_Float density
Definition eb_volcor.h:202
static const unsigned long DEFAULT_RAND_POINT_DENSITY
Definition eb_volcor.h:147
EGS_Float covered_threshold
Definition eb_volcor.h:205
EGS_BaseShape * bounds
Definition eb_volcor.h:152
EGS_Input * input
Definition eb_volcor.h:149
int setBoundsShape()
create bounding shape from the shape input and calculate its volume
EGS_Float bounds_volume
Definition eb_volcor.h:201
void setCoveredThreshold()
EGS_Vector getRandomPoint()
VolCorMode mode
Definition eb_volcor.h:204
void setMode()
read mode from input
EGS_Float npoints
Definition eb_volcor.h:203
EGS_RandomGenerator * rng
Definition eb_volcor.h:156
map< string, int > loadFileVolumeCorrections()
EGS_BaseGeometry * base_geom
Definition eb_volcor.h:318
vector< EGS_AffineTransform * > transforms
Definition eb_volcor.h:321
void applyVolumeCorrections(Options *options, HitCounterT hit_counter)
EGS_AffineTransform * base_transform_inv
Definition eb_volcor.h:323
double correctPhantomVolumesForSources()
vector< EB_Phantom * > phantoms
Definition eb_volcor.h:317
EGS_AffineTransform * base_transform
Definition eb_volcor.h:322
map< string, string > phantom_files
Definition eb_volcor.h:316
void close()
Definition gzstream.C:154
Volume correction routines for egs_brachy.
pair< int, int > PhantRegT
PhantRegT is a pair of the form (PhantomNumber, PhantomRegion) e.g. a pair of (2, 12) would represent...
Definition eb_volcor.h:86
std::map< PhantRegT, EGS_I64 > HitCounterT
HitCounterT is used for counting how many random points land in a given phantoms region.
Definition eb_volcor.h:97
@ NO_CORRECTION
Definition eb_volcor.h:78
@ ZERO_DOSE
Definition eb_volcor.h:78
int loadVolumes(string fname, vector< RegVolume > &reg_volumes)
void readVolumes(istream &vfile, vector< RegVolume > &reg_volumes)
EGS_Float getShapeVolume(EGS_Input *shape_inp)
get shape volume from a shape input item
Definition eb_volcor.cpp:77
bool isGZip(istream &vfile)
Definition eb_volcor.cpp:54
RegVolumeT sruct with members (ir=RegionNumber, vol=Volume, unc=Unc)
Definition eb_volcor.h:89