EGS Brachy
An egs++ user code for rapid brachytherapy calculations
Loading...
Searching...
No Matches
/home/runner/work/egs_brachy/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
44
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 covered_threshold = 99.9;
241 }
242
243 if (covered_threshold > 1.){
244 covered_threshold /= 100;
245 }
246
247}
248
250 return bounds->getRandomPoint(rng);
251}
252
254
255 EGS_Input *source_cor_inp = input->takeInputItem("source volume correction");
256 source_opts = new Options(source_cor_inp);
257 if (source_cor_inp) {
258 delete source_cor_inp;
259 }
260
261
262 EGS_Input *gen_cor_inp = input->takeInputItem("extra volume correction");
263 gen_opts = new Options(gen_cor_inp);
264 if (gen_cor_inp) {
265 delete gen_cor_inp;
266 }
267
268 EGS_Input *file_cor_inp = input->takeInputItem("volume correction from file");
269
270 if (file_cor_inp) {
271 EGS_Input *ij;
272 int input_num =1;
273 while ((ij = file_cor_inp->takeInputItem("phantom file"))) {
274
275 string phantom, filename;
276 vector<string> phant_file;
277 ij->getInput("phantom file", phant_file);
278 if (phant_file.size() != 2) {
279 egsWarning("VolumeCorrector::setupOptions ignoring 'phantom file' input number %d\n", input_num);
280 } else {
281 phantom_files[phant_file[0]] = phant_file[1];
282 }
283
284 input_num += 1;
285 delete ij;
286
287 }
288
289 delete file_cor_inp;
290 }
291
292}
293
295
296 HitCounterT hit_counter;
297
298 for (EGS_I64 i=0; i < gen_opts->npoints; i++) {
299
300 EGS_Vector point = gen_opts->getRandomPoint();
301 int global_reg = base_geom->isWhere(point);
302
303 for (size_t pidx=0; pidx < phantoms.size(); pidx++) {
304 bool phant_is_covered = !phantoms[pidx]->globalRegIsInPhant(global_reg);
305 if (!phant_is_covered) {
306 continue;
307 }
308
309 int phant_reg = phantoms[pidx]->geometry->isWhere(point);
310
311 PhantRegT phant_and_reg(pidx, phant_reg);
312 hit_counter[phant_and_reg] += 1;
313 }
314
315 }
316
317 applyVolumeCorrections(gen_opts, hit_counter);
318
319 return 0;
320
321}
322// source specific MC volume calculation
323//
324// Works by generating random points within the sources boundary shape and
325// then, if the point is within the source, transforming each point to
326// all the sources locations and checking which phantom region the point lands in.
327// The volume of a region occupied by a source then is the bounding shape volume
328// times the fraction of points landing in the source. The volume of the region
329// then is the uncorrected volume minus the volume occupied by source
330// Vcor(reg) = V(reg) - V_source(reg)
331// = V(reg) - V_b * n_points_in_source(reg)/n_total
332//
333// where V_b is volume of bounding shape
335
336 EGS_Vector point;
337
338 HitCounterT hit_counter;
339 EGS_I64 n_in_source = 0;
340
341 for (EGS_I64 i=0; i < source_opts->npoints; i++) {
342
343 point = source_opts->getRandomPoint();
344 base_transform->transform(point);
345
346 int ir = base_geom->isWhere(point);
347 if (!ginfo->isSource(ir)) {
348 continue;
349 }
350 n_in_source += 1;
351 base_transform_inv->transform(point);
352
353 for (size_t sidx = 0; sidx < transforms.size(); sidx++) {
354
355 EGS_Vector transformed(point);
356 transforms[sidx]->transform(transformed);
357
358 for (size_t phant= 0; phant<phantoms.size(); phant++) {
359 int reg = phantoms[phant]->geometry->isWhere(transformed);
360 bool in_phant = reg >= 0;
361
362 if (in_phant) {
363 PhantRegT phant_reg(phant, reg);
364 hit_counter[phant_reg] += 1;
365 }
366
367 }
368 }
369 }
370
372
373 return source_opts->bounds_volume*(double)n_in_source/(int)source_opts->npoints;
374
375}
376
378
379 bool zero_dose = opts->mode == ZERO_DOSE;
380 double bounds_vol = opts->bounds_volume;
381 double npoints = opts->npoints;
382
383 for (HitCounterT::iterator hi = hit_counter.begin(); hi != hit_counter.end(); hi++) {
384
385 int phant_idx = hi->first.first;
386 int phant_reg = hi->first.second;
387 if (phant_reg < 0) {
388 continue;
389 }
390 int hits = hi->second;
391 double reg_vol = phantoms[phant_idx]->getUncorrectedVolume(phant_reg);
392 double corrected_vol = reg_vol - bounds_vol*double(hits)/npoints;
393 double unc;
394
395 /* consider a voxel coverd if corrected vol is 0.1% (default) or less of nominal volume */
396 bool voxel_covered = corrected_vol / reg_vol <= (1. - opts->covered_threshold);
397 if (!(voxel_covered || zero_dose)){
398 unc = bounds_vol/corrected_vol*sqrt(hits*(npoints-hits)/(npoints*npoints*npoints));
399 } else {
400 corrected_vol = 0;
401 unc = 0;
402 }
403 phantoms[phant_idx]->setCorrectedVolume(phant_reg, corrected_vol, unc);
404 }
405
406}
407
408void readVolumes(istream &vfile, vector<RegVolume> &reg_volumes) {
409 size_t nrecords;
410 vfile >> nrecords;
411 for (size_t rec = 0; rec < nrecords; rec++) {
412 int reg;
413 EGS_Float vol, vol_unc;
414 vfile >> reg >> vol >> vol_unc;
415 RegVolume rvol = {reg, vol, vol_unc};
416 reg_volumes.push_back(rvol);
417 }
418}
419
420int loadVolumes(string fname, vector<RegVolume> &reg_volumes) {
421 ifstream vfile(fname.c_str());
422 if (!vfile.is_open()) {
423 return 1;
424 }
425
426 if (isGZip(vfile)) {
427 igzstream gzf(fname.c_str());
428 readVolumes(gzf, reg_volumes);
429 gzf.close();
430 } else {
431 readVolumes(vfile, reg_volumes);
432 }
433 vfile.close();
434
435 return 0;
436
437}
438
440
441 map<string, int> nreg_read_for_phant;
442
443 for (size_t phant_idx=0; phant_idx < phantoms.size(); phant_idx++) {
444
445 string phant_name = phantoms[phant_idx]->geometry->getName();
446
447 if (phantom_files.count(phant_name)) {
448
449 string fname = phantom_files[phant_name];
450
451 vector<RegVolume> volumes;
452 int error = loadVolumes(fname, volumes);
453 if (error) {
454 egsFatal(
455 "VolumeCorrector::loadFileVolumeCorrections: failed to read "
456 "volumes for '%s' from file '%s'\n", phant_name.c_str(), fname.c_str()
457 );
458 }
459
460 nreg_read_for_phant[phant_name] = volumes.size();
461
462 for (size_t rv=0; rv < volumes.size(); rv++) {
463 RegVolume reg_vol = volumes[rv];
464 phantoms[phant_idx]->setCorrectedVolume(reg_vol.ir, reg_vol.vol, reg_vol.unc);
465 }
466 }
467
468 }
469 return nreg_read_for_phant;
470}
471}
472
EGS_RandomGenerator wrapper for Sobol library.
Definition egs_sobol.h:71
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:323
vector< EGS_AffineTransform * > transforms
Definition eb_volcor.h:326
void applyVolumeCorrections(Options *options, HitCounterT hit_counter)
EGS_AffineTransform * base_transform_inv
Definition eb_volcor.h:328
double correctPhantomVolumesForSources()
vector< EB_Phantom * > phantoms
Definition eb_volcor.h:322
EGS_AffineTransform * base_transform
Definition eb_volcor.h:327
map< string, string > phantom_files
Definition eb_volcor.h:321
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