From ef7f5fcb88065a9f2c68c4f21920590499268cd9 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Mon, 24 Jan 2022 08:55:20 -0500 Subject: [PATCH 01/18] Add method to access the_epcont->tvstep variable --- HEN_HOUSE/egs++/egs_advanced_application.cpp | 7 +++++++ HEN_HOUSE/egs++/egs_advanced_application.h | 5 +++++ HEN_HOUSE/egs++/egs_application.h | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/HEN_HOUSE/egs++/egs_advanced_application.cpp b/HEN_HOUSE/egs++/egs_advanced_application.cpp index c83928974..4137fb332 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.cpp +++ b/HEN_HOUSE/egs++/egs_advanced_application.cpp @@ -1218,6 +1218,13 @@ void EGS_AdvancedApplication::setRadiativeSplitting(const EGS_Float &nsplit) { the_egsvr->nbr_split = nsplit; } +//************************************************************ +// Utility functions for use with ausgab fluence scoring objects +//************************************************************ +EGS_Float EGS_AdvancedApplication::getTVSTEP() { + return the_epcont->tvstep; +}; + //************************************************************ // Utility function for ausgab phase space scoring objects //************************************************************ diff --git a/HEN_HOUSE/egs++/egs_advanced_application.h b/HEN_HOUSE/egs++/egs_advanced_application.h index b627ef340..1c7070244 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.h +++ b/HEN_HOUSE/egs++/egs_advanced_application.h @@ -217,6 +217,11 @@ class APP_EXPORT EGS_AdvancedApplication : public EGS_Application { //************************************************************ void setLatch(int latch); + //************************************************************ + // Utility functions for fluence scoring objects + //************************************************************ + EGS_Float getTVSTEP(); + /* Needed by some sources */ EGS_Float getRM(); /* Turn ON/OFF radiative splitting */ diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index 9081927c8..6b9bfabdf 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1148,6 +1148,13 @@ class EGS_EXPORT EGS_Application { }; virtual void setRadiativeSplitting(const EGS_Float &nsplit) {}; + //************************************************************ + // Utility functions for use with ausgab fluence scoring objects + //************************************************************ + virtual EGS_Float getTVSTEP() { + return 0.0; + }; + //************************************************************ // Utility function for ausgab phase space scoring objects //************************************************************ From 49ea9dab295696cc4caf1664d7f910e2df1566a2 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 27 Jan 2022 15:12:45 -0500 Subject: [PATCH 02/18] Add a fluence scoring ausgab object - EGS_Interpolator: increase array size when creating an interpolator based on an array of value pairs to have an extra bin to prevent round-off errors. This is also done by PEGS4 when determining fit parameters for each interval. The extra bin uses the same parameters as the previous one, i.e., it extrapolates. - Add auxiliary methods to application classes to get electron step size, energy deposited and app source type. - Fluence scoring: at a plane, in regions for all particles. --- HEN_HOUSE/egs++/Makefile | 3 +- .../egs_fluence_scoring/Makefile | 53 + .../egs_fluence_scoring.cpp | 1086 +++++++++++++++++ .../egs_fluence_scoring/egs_fluence_scoring.h | 623 ++++++++++ HEN_HOUSE/egs++/egs_advanced_application.cpp | 1 + HEN_HOUSE/egs++/egs_advanced_application.h | 5 +- HEN_HOUSE/egs++/egs_application.h | 10 + HEN_HOUSE/egs++/egs_interpolator.cpp | 9 +- 8 files changed, 1787 insertions(+), 3 deletions(-) create mode 100644 HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile create mode 100644 HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp create mode 100644 HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h diff --git a/HEN_HOUSE/egs++/Makefile b/HEN_HOUSE/egs++/Makefile index 4108cabc6..619662264 100644 --- a/HEN_HOUSE/egs++/Makefile +++ b/HEN_HOUSE/egs++/Makefile @@ -66,7 +66,8 @@ shape_libs = egs_circle egs_ellipse egs_extended_shape egs_gaussian_shape \ egs_voxelized_shape egs_spherical_shell egs_conical_shell \ egs_circle_perpendicular -aobject_libs = egs_track_scoring egs_dose_scoring egs_radiative_splitting egs_phsp_scoring +aobject_libs = egs_track_scoring egs_dose_scoring egs_radiative_splitting \ + egs_phsp_scoring egs_fluence_scoring all_libs = $(geometry_libs) $(source_libs) $(shape_libs) $(aobject_libs) lib_objects = $(addprefix $(DSO1), $(addsuffix .$(obje), $(all_libs))) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile new file mode 100644 index 000000000..97a50a517 --- /dev/null +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile @@ -0,0 +1,53 @@ + +############################################################################### +# +# EGSnrc egs++ makefile to build fluence scoring object +# Copyright (C) 2022 National Research Council Canada +# +# This file is part of EGSnrc. +# +# EGSnrc is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with EGSnrc. If not, see . +# +############################################################################### +# +# Author: Ernesto Mainegra-Hing, 2022 +# +# Contributors: +# +############################################################################### + +include $(EGS_CONFIG) +include $(SPEC_DIR)egspp.spec +include $(SPEC_DIR)egspp_$(my_machine).conf + +# For debugging invoke: make DEBUG=-DDEBUG +DEFS = $(DEF1) -DBUILD_FLUENCE_SCORING_DLL $(DEBUG) + +library = egs_fluence_scoring +lib_files = egs_fluence_scoring +my_deps = $(common_ausgab_deps) +extra_dep = $(addprefix $(DSOLIBS), $(my_deps)) + +include $(SPEC_DIR)egspp_libs.spec + +$(make_depend) + +test: + @echo "common_h2: $(common_h2)" + @echo "extra: $(extra)" + @echo "common_ausgab_deps: $(common_ausgab_deps)" + @echo $(ABS_DSO)*$(library)* +clean: + $(REMOVE) $(ABS_DSO)*$(library)* + $(REMOVE) *.o diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp new file mode 100644 index 000000000..fbf5408d1 --- /dev/null +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -0,0 +1,1086 @@ +/* +############################################################################### +# +# EGSnrc egs++ fluence scoring object implementation +# Copyright (C) 2015 National Research Council Canada +# +# This file is part of EGSnrc. +# +# EGSnrc is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with EGSnrc. If not, see . +# +############################################################################### +# +# Author: Ernesto Mainegra-Hing, 2022 +# +# Contributors: +# +############################################################################### + +/*! \file egs_fluence_scoring.cpp + * \brief A fluence scoring ausgab object: implementation + * + */ + +#include +#include + +#include "egs_fluence_scoring.h" +#include "egs_input.h" +#include "egs_functions.h" + +EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : + EGS_AusgabObject(Name,f), hits_field(false), + scoring_charge(photon), particle_name("photon"), + flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), + norm_u(1.0), Nx(1), Ny(1), flu(0), fluT(0), current_ncase(0), + m_primary(0.0), m_tot(0.0) +{ + otype = "EGS_PlanarFluence"; + m_midpoint = EGS_Vector(0,0,5); + m_R = 5; + m_R2 = 25; + field_type = circle; Area = M_PI * m_R2; + m_normal = EGS_Vector(0,0,1); + m_d = m_normal*m_midpoint; +} + +/*! Destructor. */ +EGS_PlanarFluence::~EGS_PlanarFluence(){ + + if( flu ) { + for(int j=0; j name; int the_selection = 0; + name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); + the_selection = inp->getInput("scoring particle", name, 0); + switch(the_selection){ + case 1: + particle_name="electron"; + scoring_charge = electron; + break; + case 2: + particle_name="positron"; + scoring_charge = positron; + break; + default: + particle_name="photon"; + scoring_charge = photon; + } + + EGS_Float flu_Emin, flu_Emax, norma; + int err_n = inp->getInput("number of bins",flu_nbin); + int err_i = inp->getInput("minimum kinetic energy",flu_Emin); + int err_f = inp->getInput("maximum kinetic energy",flu_Emax); + int err_norm = inp->getInput("normalization",norma); + if (!err_norm) norm_u = norma; + + vector scale; + scale.push_back("linear"); scale.push_back("logarithmic"); + flu_s = inp->getInput("scale",scale,0); + if( flu_s == 0 ) { + flu_xmin = flu_Emin; flu_xmax = flu_Emax; + } + else { + flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); + } + flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), one could add extra + bin for E = Emax cases. Alternatively, push those + events into last bin (bias?) during scoring. + Which approach is correct? + ******************************************************/ + //flu_nbin++; + + vector output_spectra; + output_spectra.push_back("no"); + output_spectra.push_back("yes"); + verbose = inp->getInput("verbose",output_spectra,0); + + // Specific plane input + vector tmp_field; + int err2 = inp->getInput("scoring circle",tmp_field); + if( err2 ) { + int err3 = inp->getInput("scoring rectangle",tmp_field); + if( err3 || tmp_field.size() != 4) { + egsWarning( + "\n\n*** Wrong/missing 'scoring rectangle' input " + "setting it to 10 cm X 10 cm field at origin!\n\n"); + m_midpoint = EGS_Vector(); + ax = 10; ay = 10; + field_type = rectangle; Area = ax*ay; + } + else{ + EGS_Float xmin = tmp_field[0],xmax = tmp_field[1], + ymin = tmp_field[2],ymax = tmp_field[3]; + /* scoring plane location in space */ + m_midpoint = EGS_Vector((xmax+xmin)/2.,(ymax+ymin)/2.,0); // plane at origin by default + /* scoring plane normal */ + m_normal = EGS_Vector(0,0,1); // default normal along positive z-axis + /* define unit vectors on right-handed scoring plane */ + ux = EGS_Vector(1,0,0); uy = EGS_Vector(0,1,0); + /* Request a scoring plane transformation for initial position and orientation */ + EGS_AffineTransform *t = EGS_AffineTransform::getTransformation(inp); + if (t){ + t->rotate(m_normal) ; t->transform(m_midpoint); + t->rotate(ux); t->rotate(uy); + delete t; + } + + /* get screen resolution */ + vector screen; + int err4 = inp->getInput("resolution",screen); + if( err4 ) { + Nx=1; Ny=1; + egsWarning( + "\n\n*** Missing/wrong 'resolution' input " + " Scoring in the whole field\n\n"); + + } + else if (screen.size()==1){ Nx = screen[0];Ny = screen[0];} + else if (screen.size()==2){ Nx = screen[0];Ny = screen[1];} + else if (screen.size()> 2){ Nx = screen[0];Ny = screen[1]; + egsWarning( + "\n\n*** Too many 'resolution' inputs\n" + " Using first two entries\n\n"); + + } + + ax = xmax - xmin; ay = ymax-ymin; + vx = ax/Nx; vy = ay/Ny; + field_type = rectangle; Area = vx*vy; + } + } + else if( tmp_field.size() != 4 ) { + egsWarning( + "\n\n*** Wrong/missing 'scoring circle' input " + "setting it to 10 cm diameter field at origin!\n\n"); + m_midpoint = EGS_Vector(); + m_R = 5; + m_R2 = 25; + field_type = circle; Area = M_PI * m_R2; + } + else{ + vector tmp_normal; + int err1 = inp->getInput("scoring plane normal",tmp_normal); + if( err1 || tmp_normal.size() != 3 ) { + egsWarning( + "\n\n*** Wrong/missing 'scoring plane normal' input. " + "Set to null vector\n\n"); + m_normal = EGS_Vector();// set to null vector + } + else{ + m_normal = EGS_Vector(tmp_normal[0],tmp_normal[1], + tmp_normal[2]); + m_normal.normalize(); + } + m_midpoint = EGS_Vector(tmp_field[0],tmp_field[1], + tmp_field[2]); + m_R = tmp_field[3]; + m_R2 = tmp_field[3]*tmp_field[3]; + field_type = circle; Area = M_PI * m_R2; + } + + m_d = m_normal*m_midpoint; +} + +void EGS_PlanarFluence::describeMe(){ + char buf[128]; + sprintf(buf,"\nPlanar %s fluence\n",particle_name.c_str()); + description = buf; + //description = "\nParticle Fluence Scoring\n"; + description += "========================\n"; + description += " - scoring field normal = "; + sprintf(buf,"(%g %g %g)\n",m_normal.x,m_normal.y,m_normal.z); + description += buf; + description += " - scoring field center = "; + sprintf(buf,"(%g %g %g)\n",m_midpoint.x,m_midpoint.y,m_midpoint.z); + description += buf; + if (field_type == circle){ + description += " - scoring field radius = "; + sprintf(buf,"%g cm\n",m_R); + description += buf; + } + else if (field_type == rectangle) { + description += " - scoring field = "; + sprintf(buf,"%g cm X %g cm\n",ax,ay); + description += buf; + description += " - scoring field resolution = "; + sprintf(buf,"%d X %d\n",Nx,Ny); + description += buf; + } + description += " - scoring field distance from origin = "; + sprintf(buf,"%g cm\n",m_d); + description += buf; + + if (flu){ + //if (planar_fluence) + // description += " - scoring planar particle fluence between "; + //else + description += " - scoring planar particle fluence in the "; + EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, + Emax = flu_s ? exp(flu_xmax):flu_xmax; + sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); + description += buf; + if (flu_s) + description += " on a logarithmic scale \n"; + else + description += " on a linear scale \n"; + } + +} + +void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ + EGS_Float up = p.u*m_normal, aup = fabs(up); + /********************************************************** + Prevent large weights from particles very close to plane. + ***********************************************************/ + if( aup < 0.08 ) aup = 0.0871557;// Limit incident angle to 85 degrees + EGS_Float e = p.q ? p.E - app->getRM() : p.E; + if (flu){ + //EGS_Float fup = planar_fluence ? 1.0:aup; + EGS_Float fup = aup; + if( flu_s ) {e = log(e);} // log scale + EGS_Float ae; int je; + /* Score fluence in each voxel */ + if( e > flu_xmin && e <= flu_xmax) { + /* Score total fluence in each voxel */ + fluT->score(ivoxel,p.wt/fup); + /* Score differential fluence in each voxel */ + ae = flu_a*e + flu_b; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), hence push events + with E = Emax into last bin (bias?) during scoring. + Alternatively add extra bin for E = Emax cases. + Which approach is correct? + ******************************************************/ + je = min((int)ae,flu_nbin-1);//je = (int) ae; + /******************************************************/ + if (ivoxel < 0 || ivoxel > Nx*Ny) + egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); + EGS_ScoringArray *aux = flu[ivoxel]; + if (je < 0 || je >= flu_nbin ) + egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); + aux->score(je,p.wt/fup); + } + } +} + +int EGS_PlanarFluence::hitsField(const EGS_Particle& p, EGS_Float* dist){ + if ( field_type==circle ){ + EGS_Float xp = p.x*m_normal, up = p.u*m_normal; + if( (up > 0 && m_d > xp ) || + (up < 0 && m_d < xp ) ){ + EGS_Float t = (m_d - xp)/up; + EGS_Vector x1(p.x + p.u*t - m_midpoint); + if( dist ) *dist = t; + return x1.length2() < m_R*m_R ? 0:-1; + } + return -1; + } + else if ( field_type == rectangle ){ + EGS_Float xp = p.x*m_normal, up = p.u*m_normal; + if( (up > 0 && m_d > xp) || (up < 0 && m_d < xp) ) { + EGS_Float t = (m_d - xp)/up; // distance to plane along u + EGS_Vector x1(p.x + p.u*t - m_midpoint);// vector on scoring plane + EGS_Float xcomp = x1*ux;// x-direction component + EGS_Float ycomp = x1*uy;// y-direction component + xcomp = 2*xcomp + ax; + ycomp = 2*ycomp + ay; + if( xcomp > 0 && xcomp < 2*ax && + ycomp > 0 && ycomp < 2*ay ){ + int i = int(xcomp/(2*vx)), + j = int(ycomp/(2*vy)), + k = i + j*Nx; + if( dist ) *dist = t; + return k; + } + } + return -1; + } + else return -1; +} + +void EGS_PlanarFluence::ouputResults(){ + if (!flu) return; + +EGS_Float src_norm = 1.0, // default to number of histories in this run + Fsrc = app->getFluence();// Fluence or number of primary histories + egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", + current_ncase, Fsrc); + + if (Fsrc) + src_norm = Fsrc/current_ncase;// fluence or primary histories per histories run + + string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; + string src_type = app->sourceType(); + if ( src_type == "EGS_BeamSource" ){ + normLabel = "primary history"; + egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", + src_type.c_str(), src_norm); + } + else if ( src_type == "EGS_CollimatedSource" || + (src_type == "EGS_ParallelBeam" && src_norm != 1)){ + egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", + src_type.c_str(), src_norm); + } + else{ + egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", + src_type.c_str(), src_norm); + + } + + double norm = 1.0/src_norm; //per fluence or particle depending on source + norm /= Area; //per unit area + norm *= flu_a; //per unit bin width + norm *= norm_u; // times user-requested normalization + + if (verbose) + egsInformation("\nNormalization = Ncase/Fsrc/A/bw = %g\n",norm); + + string suffix = "_" + particle_name + ".agr"; + string spe_name = app->constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + //spe_output.open(spe_name.c_str()); + if (!spe_output){ + egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + + egsInformation("\n\n%s fluence scoring\n" + "=================================\n",particle_name.c_str()); + for(int j=0; jcurrentResult(k,fe,dfe); + if( fe > 0 ) dfe = 100*dfe/fe; else dfe = 100; + egsInformation(" total fluence = %10.4le +/- %-7.3lf\%\n", + fe*norm/flu_a,dfe); + if (verbose) egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "---------------------------------------------\n"); + for(int l=0; lcurrentResult(l,fe,dfe); + EGS_Float e = (l+0.5-flu_b)/flu_a; + if( flu_s ) e = exp(e); + spe_output<storeState(data) ) return false; + } + if( !fluT->storeState(data) ) return false; + } + return true; +} + +bool EGS_PlanarFluence::setState(istream &data){ + if( !egsGetI64(data,current_ncase) ) return false; + //data >> m_primary >> m_ray >> m_compt >> m_fluor >> m_multiple; + data >> m_tot >> m_primary; + if (!data.good()) return false; + if( flu ) { + for(int j=0; jsetState(data) ) return false; + } + if( !fluT->setState(data) ) return false; + } + return true; +} + +bool EGS_PlanarFluence::addState(istream &data){ + EGS_I64 tmp_case; if( !egsGetI64(data,tmp_case) ) return false; + current_ncase += tmp_case; + /* individual contributions */ + //EGS_Float tmp_primary, tmp_fluor, tmp_compt, tmp_ray, tmp_multiple; + //data >> tmp_primary >> tmp_ray >> tmp_compt >> tmp_fluor >> tmp_multiple; + EGS_Float tmp_tot, tmp_primary; + data >> tmp_tot >> tmp_primary; + if (!data.good()) return false; + m_primary += tmp_primary; + m_tot += tmp_tot; + //m_primary += tmp_primary;m_ray += tmp_ray;m_compt += tmp_compt; + //m_fluor += tmp_fluor;m_multiple += tmp_multiple; + /* fluence objects */ + if( flu ) { + EGS_ScoringArray tg(flu_nbin); + for(int j=0; j 0 && !f_region.size() ) { + getNumberRegions(f_regionsString, f_region); + getLabelRegions(f_regionsString, f_region); + } + + // Get the number of regions in the geometry. + nreg = app->getnRegions(); + + // Get the number of scoring regions. If too many, reset to nreg + n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg; + + /* Initialize arrays with defaults */ + for (int j=0; j max_reg ) max_reg = f_region[i]; + } + + /* Setup fluence scoring arrays */ + flu = new EGS_ScoringArray* [nreg]; + fluT = new EGS_ScoringArray(nreg); + for (int j = 0; j < nreg; j++){ + if (is_sensitive[j]) + flu[j] = new EGS_ScoringArray(flu_nbin); + } +#ifdef DEBUG + binDist = new EGS_ScoringArray(flu_nbin); +#endif + + /* Initialize data required to score charged particle fluence */ + if ( scoring_charge ){ + EGS_Float flu_Emin = flu_s ? exp(flu_xmin) : flu_xmin, + flu_Emax = flu_s ? exp(flu_xmax) : flu_xmax; + EGS_Float bw = flu_s ? + (log(flu_Emax / flu_Emin))/flu_nbin : + (flu_Emax - flu_Emin) /flu_nbin; + EGS_Float expbw; + /* Pre-calculated values for faster evaluation on log scale */ + if (flu_s){ + expbw = exp(bw); // => (Emax/Emin)^(1/nbin) + r_const = 1/(expbw-1); + DE = new EGS_Float [flu_nbin]; + a_const = new EGS_Float [flu_nbin]; + for ( int i = 0; i < flu_nbin; i++ ){ + DE[i] = flu_Emin*pow(expbw,i)*(expbw-1); + a_const[i] = 1/flu_Emin*pow(1/expbw,i); + } + } + /* Do not score below ECUT - PRM */ + if (flu_Emin < app->getEcut() - app->getRM() ){ + flu_Emin = app->getEcut() - app->getRM() ; + /* Decrease number of bins, preserve bin width */ + flu_nbin = flu_s ? + ceil((log(flu_Emax / flu_Emin))/bw) : + ceil( (flu_Emax - flu_Emin) /bw); + } + + flu_a_i = bw; flu_a = 1.0/bw; + + /* Pre-calculated values for faster 1/stpwr evaluation */ + if (flu_stpwr){ + + int n_media = app->getnMedia(); + + EGS_Float lnE, lnEmin, lnEmax, lnEmid; + + i_dedx = new EGS_Interpolator [n_media];// stp powers + dedx_i = new EGS_Interpolator [n_media];// its inverse + for( int j = 0; j < n_media; j++ ){ + i_dedx[j] = *(app->eDEDX(j)); + EGS_Float Emin = i_dedx[j].getXmin(); + EGS_Float Emax = i_dedx[j].getXmax(); + int n = 1 + i_dedx[j].getIndex(Emax);// getIndex returns lower bin limit? + EGS_Float bwidth = (Emax - Emin)/n; +#ifdef DEBUG + egsInformation("---> stpwr in %s : Emin=%g Emax=%g n=%d bw=%g\n", + app->getMediumName(j), exp(Emin), exp(Emax), n, bwidth); +#endif + int nbins = n + 1; + EGS_Float spwr_i[nbins]; + for (int k = 0; k < nbins; k++ ){ + spwr_i[k] = 1.0 / i_dedx[j].interpolate(Emin+k*bwidth); + } + dedx_i[j].initialize(nbins, Emin, Emax, spwr_i); +#ifdef DEBUG + Emin = dedx_i[j].getXmin(); + Emax = dedx_i[j].getXmax(); + n = 1 + dedx_i[j].getIndex(Emax);// getIndex returns lower bin limit? + bwidth = (Emax - Emin)/n; + egsInformation("---> 1/stpwr in %s : lnEmin=%g lnEmax=%g n=%d bw=%g index(Emax)=%d\n", + app->getMediumName(j), Emin, Emax, n, bwidth, dedx_i[j].getIndexFast(Emax)); + for (int k = 0; k < nbins; k++ ){ + egsInformation(" L(%g MeV) = %g MeV/cm 1/L = %g cm/MeV\n", + exp(Emin+k*bwidth), + i_dedx[j].interpolate(Emin + k*bwidth), + dedx_i[j].interpolate(Emin + k*bwidth)); + } + +#endif + } + + /* Determine stpwr at middle of each fluence scoring bin */ + lnEmin = flu_s ? log(0.5*flu_Emin*(expbw+1)) : 0; + Lmid_i = new EGS_Float [flu_nbin*n_media]; + for( int j = 0; j < n_media; j++ ){ + for ( int i = 0; i < flu_nbin; i++ ){ + lnEmid = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*(i+0.5)); + Lmid_i[i+j*flu_nbin] = 1/i_dedx[j].interpolate(lnEmid); + //egsInformation(" 1/L(%g MeV) = %g cm/MeV\n",exp(lnEmid),Lmid_i[i+j*flu_nbin]); + } + } + } + } + + + describeMe(); +} + +void EGS_VolumetricFluence::getNumberRegions(const string &str, vector ®s) { + app->getNumberRegions(str, regs); +} + +void EGS_VolumetricFluence::getLabelRegions(const string &str, vector ®s) { + app->getLabelRegions(str, regs); +} + +/* + Takes user inputs and sets up simulation parameters not requiring + invoking an application method. This is later done in setApplication. +*/ +void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { + + if( !inp ) { + egsWarning("AO type %s: null input?\n",otype.c_str()); return; + } + + vector name; int the_selection = 0; + name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); + the_selection = inp->getInput("scoring particle", name, 0); + switch(the_selection){ + case 1: + particle_name="electron"; + scoring_charge = electron; + break; + case 2: + particle_name="positron"; + scoring_charge = positron; + break; + default: + particle_name="photon"; + scoring_charge = photon; + } + + EGS_Float flu_Emin, flu_Emax, norma; + int err_n = inp->getInput("number of bins",flu_nbin); + int err_i = inp->getInput("minimum kinetic energy",flu_Emin); + int err_f = inp->getInput("maximum kinetic energy",flu_Emax); + if (err_n) egsFatal("\n**** Missing input: number of bins. Aborting!\n\n"); + if (err_i) egsFatal("\n**** Missing input: minimum kinetic energy. Aborting!\n\n"); + if (err_f) egsFatal("\n**** Missing input: maximum kinetic energy. Aborting!\n\n"); + int err_norm = inp->getInput("normalization",norma); + if (!err_norm) norm_u = norma; + + vector scale; + scale.push_back("linear"); scale.push_back("logarithmic"); + flu_s = inp->getInput("scale",scale,0); + if( flu_s == 0 ) { + flu_xmin = flu_Emin; flu_xmax = flu_Emax; + } + else { + flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); + } + flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; + + vector output_spectra; + output_spectra.push_back("no"); + output_spectra.push_back("yes"); + verbose = inp->getInput("verbose",output_spectra,0); + + /* get region volume[s] in g/cm3 */ + vector v_in; + inp->getInput("volumes",v_in); + + /* get dose regions */ + bool using_all_regions=true; + vector f_start, f_stop; + if (!inp->getInput("regions",f_regionsString) && f_regionsString.length()>0) { + using_all_regions = false; // individual regions + } + else { + int err1 = inp->getInput("start region",f_start); + int err2 = inp->getInput("stop region",f_stop); + if (!err1 && !err2) { + if (f_start.size()==f_stop.size()) { // group of dose regions + for (int i=0; i method; + method.push_back("flurz"); method.push_back("stpwr"); // 3rd order + method.push_back("stpwrO5"); // 5th order + flu_stpwr = eFluType(inp->getInput("method",method,1)); + } +} + +#define REGIONS_ENTRIES 100 +#define REGIONS_PER_LINE 25 +void EGS_VolumetricFluence::describeMe(){ + char buf[128]; + sprintf(buf,"\nVolumetric %s fluence scoring\n",particle_name.c_str()); + description = buf; + description += "=================================\n"; + + description +="\n - scoring regions: "; + // Get the number of regions in the geometry. + int start = 0, stop = 0, k = 0, entries = 0; + /* List up to 100 scoring groups or regions */ + while (k < nreg && entries < REGIONS_ENTRIES){ + if( is_sensitive[k] ){ + start = k; + entries++; + while( is_sensitive[k] && k < nreg) { + k++; + } + stop = k-1; + if ( start < stop ) + sprintf(buf,"[%d - %d]",start,stop); + else if ( k % REGIONS_PER_LINE ) + sprintf(buf," %d",start); + else + sprintf(buf," %d\n ",start); + if (entries == REGIONS_ENTRIES){ + sprintf(buf,"... %d\n",max_reg); + } + description += buf; + } + k++; + } + + for (int l = 0; l < nreg; l++){ + if( is_sensitive[l] ){ + egsInformation(" ---> region # %d volume = %g cm3\n",l,volume[l]); + } + } + + description += "\n\n - scoring in the "; + EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, + Emax = flu_s ? exp(flu_xmax):flu_xmax; + sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); + description += buf; + if (flu_s) + description += " on a logarithmic scale \n"; + else + description += " on a linear scale \n"; + + if (flu_stpwr){ + if (flu_stpwr == stpwr){ + description += " O(eps^3) approach: accounts for change in stpwr\n"; + description += " along the step with eps=edep/Eb\n"; + } + else if (flu_stpwr == stpwrO5){ + description += " O(eps^5) approach: accounts for change in stpwr\n"; + description += " along the step with eps=edep/Eb\n"; + } + } + else + description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n"; + + if (norm_u != 1.0) { + description += " Non-unity user-requested normalization = "; + sprintf(buf,"%g\n",norm_u); + description += buf; + } + +} +void EGS_VolumetricFluence::ouputResults(){ + + + EGS_Float src_norm = 1.0, // default to number of histories in this run + Fsrc = app->getFluence();// Fluence or number of primary histories + egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", + current_ncase, Fsrc); + + if (Fsrc) + src_norm = Fsrc/current_ncase;// fluence or primary histories per histories run + + string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; + string src_type = app->sourceType(); + if ( src_type == "EGS_BeamSource" ){ + normLabel = "primary history"; + egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", + src_type.c_str(), src_norm); + } + else if ( src_type == "EGS_CollimatedSource" || + (src_type == "EGS_ParallelBeam" && src_norm != 1)){ + egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", + src_type.c_str(), src_norm); + } + else{ + egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", + src_type.c_str(), src_norm); + + } +#ifdef DEBUG + EGS_Float fbins, d_fbins; + egsInformation("\nNumber of covered bins distribution\n" + "--------------------------------------\n"); + + int tot_bins = one_bin + multi_bin; + egsInformation("\none_bin = %d [%-7.3lf\%] multi_bin = %d [%-7.3lf\%]\n", + one_bin, 100.0*one_bin/tot_bins, multi_bin,100.0*multi_bin/tot_bins); + + egsInformation("\n # bins freq unc percentage \n"); + for (int i=0; icurrentResult(i,fbins, d_fbins); + if (fbins){ + d_fbins = 100.0*d_fbins/fbins; fbins = current_ncase*fbins; + egsInformation(" %d %11.2f [%-7.3lf\%] %11.2f \%\n", + i+1, fbins, d_fbins, 100.*fbins/tot_bins); + } + } +#endif + + string suffix = "_" + particle_name + ".agr"; + string spe_name = app->constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + if (!spe_output){ + egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + if (src_norm == 1 || normLabel == "primary history") + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + else{ + spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n"; + } + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + egsInformation("\n\n%s fluence scoring\n" + "=================================\n",particle_name.c_str()); + // Get the number of regions in the geometry. + for (int j = 0; j < nreg; j++) { + if ( !is_sensitive[j] ) continue; + EGS_Float norm = 1.0/src_norm;// per particle or fluence + norm *= norm_u; // user-requested normalization + norm /= volume[j]; //per unit volume + //norm *= flu_a; //per unit bin width <- implicit in scoring! + + EGS_Float the_bw = flu_s? 1.0 : flu_a_i;// Implicit for log grid + + if (verbose){ + egsInformation("\nNormalization = Ncase/Fsrc/V = %g\n",norm); + egsInformation("Volume[%d] = %g bw = %g nbins = %d\n",j,volume[j], the_bw, flu_nbin); + } + egsInformation("region # %d : ",j); + + double fe,dfe,fp,dfp; + fluT->currentResult(j,fe,dfe); + if (fe > 0) { + dfe = 100*dfe/fe; + } + else { + dfe = 100; + } + egsInformation(" total fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", + fe*norm*the_bw,dfe); + + spe_output<<"@ s"<currentResult(i,fe,dfe); + EGS_Float e = (i+0.5-flu_b)/flu_a; + if (flu_s) e = exp(e); + spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; + if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", + e, fe*norm, dfe*norm); + } + spe_output << "&\n"; + + } + + + spe_output.close(); + +} + +void EGS_VolumetricFluence::reportResults() { + + egsInformation("\nFluence Scoring (%s)\n",name.c_str()); + egsInformation("======================================================\n"); + + ouputResults(); + +} + +bool EGS_VolumetricFluence::storeState(ostream &data) const { + if( !egsStoreI64(data,current_ncase)) return false; + data << endl; + if (!data.good()) return false; + if( flu ) { + for(int j = 0; j < nreg; j++) { + if ( is_sensitive[j] ){ + if( !flu[j]->storeState(data) ) return false; + } + } + if( !fluT->storeState(data) ) return false; + } + return true; +} + +bool EGS_VolumetricFluence::setState(istream &data){ + if( !egsGetI64(data,current_ncase) ) return false; + if (!data.good()) return false; + if( flu ) { + for(int j=0; jsetState(data) ) return false; + } + } + if( !fluT->setState(data) ) return false; + } + return true; +} + +bool EGS_VolumetricFluence::addState(istream &data){ + EGS_I64 tmp_case; if( !egsGetI64(data,tmp_case) ) return false; + current_ncase += tmp_case; + if (!data.good()) return false; + /* fluence objects */ + if( flu ) { + EGS_ScoringArray tg(flu_nbin); + for(int j = 0; j < nreg; j++) { + if ( is_sensitive[j] ){ + if( !tg.setState(data) ) return false; + (*flu[j]) += tg; + } + } + EGS_ScoringArray tgT(nreg); + if( !tgT.setState(data) ) return false; + (*fluT) += tgT; + } + + return true; +} + + + +extern "C" { + + EGS_FLUENCE_SCORING_EXPORT EGS_AusgabObject* + createAusgabObject(EGS_Input *input, EGS_ObjectFactory *f) { + const static char *func = "createAusgabObject(fluence_scoring)"; + if( !input ) { + egsWarning("%s: null input?\n",func); return 0; + } + + string type; + int error = input->getInput("type",type); + if ( !error && input->compare("planar",type) ) { + EGS_PlanarFluence *result = new EGS_PlanarFluence("", f); + result->setName(input); + result->initScoring(input); + return result; + } + else if ( !error && input->compare("volumetric",type) ){ + EGS_VolumetricFluence *result = new EGS_VolumetricFluence("", f); + result->setName(input); + result->initScoring(input); + return result; + } + else{ + egsFatal("Invalid fluence type input?\n\n\n"); + return 0; + } + } + +} diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h new file mode 100644 index 000000000..6b5e9b049 --- /dev/null +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -0,0 +1,623 @@ +/* +############################################################################### +# +# EGSnrc egs++ fluence scoring object header +# Copyright (C) 2015 National Research Council Canada +# +# This file is part of EGSnrc. +# +# EGSnrc is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with EGSnrc. If not, see . +# +############################################################################### +# +# Author: Ernesto Mainegra-Hing, 2022 +# +# Contributors: +# +############################################################################### + +/*! \file egs_fluence_scoring.h + \brief A fluence scoring object : header + +\ingroup AusgabObjects + + +NEEDS UPDATING ... ! + +This ausgab object can be used to score particle fluence during +run time and to output this information into a file +This ausgab object is specified via +\verbatim +:start ausgab object: + library = egs_fluence_scoring + name = some_name + score photons = yes or no # optional, yes assumed if missing + score electrons = yes or no # optional, yes assumed if missing + score positrons = yes or no # optional, yes assumed if missing + start scoring = event_number # optional, 0 assumed if missing + stop scoring = event_number # optional, 1024 assumed if missing + buffer size = size # optional, 1024 assumed if missing + file name addition = some_string # optional, empty string assumed if missing +:stop ausgab object: +\endverbatim +The output file name is normally constructed from the output file name, +the string specified by file name addition (if present and not empty), +and _wJob in case of parallel runs. The extension given is ptracks. +Using start scoring and stop scoring one can select a +range of histories for which to score the particle track info. One can also +select specific particle type(s). + */ + +#ifndef EGS_FLUENCE_SCORING_ +#define EGS_FLUENCE_SCORING_ + +#include "egs_ausgab_object.h" +#include "egs_transformations.h" +#include "egs_interpolator.h" +#include + +#include +using namespace std; + + +#ifdef WIN32 + +#ifdef BUILD_FLUENCE_SCORING_DLL +#define EGS_FLUENCE_SCORING_EXPORT __declspec(dllexport) +#else +#define EGS_FLUENCE_SCORING_EXPORT __declspec(dllimport) +#endif +#define EGS_FLUENCE_SCORING_LOCAL + +#else + +#ifdef HAVE_VISIBILITY +#define EGS_FLUENCE_SCORING_EXPORT __attribute__ ((visibility ("default"))) +#define EGS_FLUENCE_SCORING_LOCAL __attribute__ ((visibility ("hidden"))) +#else +#define EGS_FLUENCE_SCORING_EXPORT +#define EGS_FLUENCE_SCORING_LOCAL +#endif + +#endif + +/*! Field type */ +enum FieldType { circle=0, rectangle=1 }; + +/*! Particle type */ +enum ParticleType { electron = -1, photon = 0, positron = 1 }; + +/*! Charged particle fluence calculation type */ +enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; + +class EGS_AdvancedApplication; + +class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_AusgabObject { + +public: + /*! Constructors */ + EGS_PlanarFluence(const string &Name="", EGS_ObjectFactory *f = 0); + /*! Destructor. */ + ~EGS_PlanarFluence(); + EGS_Float area(){return Area;}; + //bool needsCall(EGS_Application::AusgabCall iarg) const { return true; }; + bool needsCall(EGS_Application::AusgabCall iarg) const { + if (iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::AfterTransport ){ + return true; + } + else { + return false; + } + }; + + inline int hitsField(const EGS_Particle& p, EGS_Float* dist); + inline void score(const EGS_Particle& p, const int& ivoxel); + void describeMe();//!< Sets fluence scoring object \c description + void initScoring(EGS_Input *inp); + void setApplication(EGS_Application *App); + void ouputResults(); + void reportResults(); + int processEvent(EGS_Application::AusgabCall iarg) { + + int q = app->top_p.q; + + if (q != scoring_charge ) return 0; + + /* Quantify photon contribution to scoring field */ + if( iarg == EGS_Application::BeforeTransport ){ + ixy = hitsField(app->top_p,&distance); + if (ixy >= 0){ + x0 = app->top_p.x; hits_field = true; + } + else{hits_field = false; } + } + + if( iarg == EGS_Application::AfterTransport && hits_field ){ + EGS_Vector xstep = app->top_p.x - x0; + if (xstep.length() >= distance){// crossed scoring field + //if (!app->top_p.latch ) m_primary += app->top_p.wt; + m_tot += app->top_p.wt; + score( app->top_p, ixy ); + } + hits_field = false; + } + + return 0; + + }; + void setCurrentCase(EGS_I64 ncase) { + if( ncase != current_ncase ) { + current_ncase = ncase; + if (flu){ + for (int j = 0; j < Nx*Ny; j++) + flu[j]->setHistory(ncase); + fluT->setHistory(ncase); + } + } + }; + void resetCounter(){ + current_ncase = 0; + if (flu){ + for (int j = 0; j < Nx*Ny; j++) + flu[j]->reset(); + fluT->reset(); + } + } + bool storeState(ostream &data) const; + bool setState(istream &data); + bool addState(istream &data); + +private: + + FieldType field_type; + + ParticleType scoring_charge; + + string particle_name; + + EGS_ScoringArray** flu; + EGS_ScoringArray* fluT; + /* Circular scoring field parameters */ + EGS_Vector m_normal, + m_midpoint, + ux, uy; + EGS_Vector x0; + EGS_Float m_R, m_R2, // scoring field + norm_u; // User normalization + /* Rectangular field parameters */ + EGS_Float ax, ay, vx, vy; + int Nx, Ny, n_sensitive_regs, ixy; + /* Energy grid inputs */ + EGS_Float Area, flu_a, + flu_b, + flu_xmin, + flu_xmax; + int flu_s, + flu_nbin; + + EGS_Float m_d; // distance from origin to center of scoring field + EGS_Float distance; // distance to scoring field along particle's direction + EGS_Float m_primary, + m_tot; +// int fluence_scoring, energy_scoring; + bool hits_field; + EGS_I64 current_ncase; + + bool verbose; + +}; + +class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_AusgabObject { + +public: + /*! Constructors */ + EGS_VolumetricFluence(const string &Name="", EGS_ObjectFactory *f = 0); + + /*! Destructor. */ + ~EGS_VolumetricFluence(); + + bool needsCall(EGS_Application::AusgabCall iarg) const { + if (iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::UserDiscard ){ + return true; + } + else { + return false; + } + }; + + void describeMe();//!< Sets fluence scoring object \c description + + void initScoring(EGS_Input *inp); + + void setApplication(EGS_Application *App); + + void ouputResults(); + + void reportResults(); + + int processEvent(EGS_Application::AusgabCall iarg) { + + int q = app->top_p.q; + if ( q != scoring_charge ) return 0; + + int ir = app->top_p.ir; + if ( ir < 0 || !is_sensitive[ir] ) return 0; + + /* Scoring photon about to be transported in geometry */ + if ( !q && iarg == EGS_Application::BeforeTransport ) { + /* Track-Length scoring (classic) */ + EGS_Float e = app->top_p.E, + wtstep = app->top_p.wt*app->getTVSTEP(); + if (flu_s) { + e = log(e); + } + EGS_Float ae; + int je; + if (e > flu_xmin && e <= flu_xmax) { + ae = flu_a*e + flu_b; + je = min((int)ae,flu_nbin-1); + EGS_ScoringArray *aux = flu[ir]; + aux->score(je,wtstep); + fluT->score(ir,wtstep); + } + return 0; + } + + EGS_Float edep = app->getEdep(); + + /* Scoring charged particle about to be transported in geometry */ + if ( q && edep && + ( iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::UserDiscard ) ){ + /***** Initialization *****/ + EGS_Float Eb = app->top_p.E - app->getRM(), + Ee = Eb - edep, + weight = app->top_p.wt; + EGS_Float xb, xe; + /*********************************/ + if( flu_s ) { + xb = log(Eb); + if( Ee > 0 ) + xe = log(Ee); + else xe = -15; + } + else{ + xb = Eb; xe = Ee; + } + EGS_Float ab, ae; int jb, je; + if( xb > flu_xmin && xe < flu_xmax ) { + /* Fraction of the initial bin covered */ + if( xb < flu_xmax ) { + ab = flu_a*xb + flu_b; jb = (int) ab; + /* Variable bin-width for log scale*/ + if (flu_s){ + ab = (Eb*a_const[jb]-1)*r_const; + } + else{ ab -= jb;}// particle's energy above Emax + } + else { xb = flu_xmax; ab = 1; jb = flu_nbin - 1; } + /* Fraction of the final bin covered */ + if( xe > flu_xmin ) { + ae = flu_a*xe + flu_b; je = (int) ae; + /* Variable bin-width for log scale*/ + if (flu_s){ + ae = (Ee*a_const[je]-1)*r_const; + } + else{ ae -= je; } + } + else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin + +#ifdef DEBUG + //egsInformation("iarg = %d jb = %d je = %d edep = %g MeV weight = %g\n",iarg,jb,je,edep,weight); + if (jb == je) + one_bin++; + else{ + multi_bin++; + //egsInformation("\niarg = %d jb = %d je = %d edep = %g MeV ab = %g ae = %g",iarg,jb,je,edep,ab,ae); + } + binDist->score(jb-je,weight); +#endif + EGS_ScoringArray *aux = flu[ir]; + EGS_ScoringArray *auxT = fluT; + + /************************************************ + * Approach A: + * ----------- + * Uses either an O(3) or O(5) series expansion of the + * integral of the inverse of the stopping power with + * respect to energy. The stopping power is represented + * as a linear interpolation over a log energy grid. It + * accounts for stopping power variation along the particle's + * step within the resolution of the scoring array. This + * is more accurate than the method used in FLURZnrc albeit + * about 10% slower in electron beam cases. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + * + ************************************************/ + if (flu_stpwr){ + int imed = app->getMedium(ir); + // Initial and final energies in same bin + EGS_Float step; + if( jb == je ){ + step = weight*(ab-ae)*getStepPerFraction(imed,xb,xe); + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + } + else { + //EGS_Float flu_a_i = 1/flu_a; + // First bin + Ee = flu_xmin + jb*flu_a_i; Eb=xb; + step = weight*ab*getStepPerFraction(imed,Eb,Ee); + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + // Last bin + Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; + step = weight*(1-ae)*getStepPerFraction(imed,Eb,Ee); + aux->score(je,step); + if (flu_s) + auxT->score(ir,step*DE[je]); + else + auxT->score(ir,step); + // intermediate bins + for(int j=je+1; jscore(j,step); + if (flu_s) + auxT->score(ir,step*DE[j]); + else + auxT->score(ir,step); + } + } + } + /*************************************************** + * ----------------------- + * Approach B (FLURZnrc): + * ---------------------- + * Path length at each energy interval from energy + * deposited edep and total particle step tvstep. It + * assumes stopping power constancy along the particle's + * step. It might introduce artifacts if ESTEPE or the + * scoring bin width are too large. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + **************************************************/ + else{ + EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; + // Initial and final energies in same bin + if( jb == je ){ + step = wtstep*(ab-ae); + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + } + else { + // First bin + step = wtstep*ab; + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + // Last bin + step = wtstep*(1-ae); + aux->score(je,step); + if (flu_s) + auxT->score(ir,step*DE[je]); + else + auxT->score(ir,step); + // intermediate bins + for(int j=je+1; jscore(j,wtstep); + if (flu_s) + auxT->score(ir,wtstep*DE[j]); + else + auxT->score(ir,wtstep); + } + } + } + } + } + + return 0; + }; + + /*! Computes path per energy bin-width traveled by a charged particle when + * slowing down from Eb to Ee. + * + * Computes the path-length traveled while slowing down from energy Eb to energy + * Ee, both energies falling in the same energy bin assuming full coverage. + * The returned value should be multiplied by the actual fraction of the + * energy bin covered by Eb-Ee. + * If using a logarithmic energy interpolation, Eb and Ee are actually the + * logarithms of the initial and final energies. The expression is based on + * linear interpolation in a logarithmic energy grid as used in EGSnrc + * (i.e. dedx = a + b*log(E) ) and a power series expansion of the ExpIntegralEi + * function that is the result of the integration of the inverse of the stopping + * power with respect to energy. + */ + EGS_Float getStepPerFraction( const int & imed, + const EGS_Float & Eb, + const EGS_Float & Ee){ + EGS_Float stpFrac, eps, lnEmid; + if (flu_s){//Using log(E) + eps = 1 - exp(Ee-Eb); + /* 4th order series expansion of log([Eb+Ee]/2) */ + lnEmid = 0.5*(Eb+Ee+0.25*eps*eps*(1+eps*(1+0.875*eps))); + } + else{//Using E + if (flu_stpwr == stpwrO5) + eps = 1 - Ee/Eb; + lnEmid = log(0.5*(Eb+Ee)); + } + + EGS_Float dedxmid_i = dedx_i[imed].interpolate(lnEmid); + // Used in cavity: + // EGS_Float dedxmid_i = 1./i_dedx[imed].interpolate(lnEmid); +#ifdef DEBUG +if (!isfinite(dedxmid_i)){ + if (isnan(dedxmid_i)) + egsInformation("\n Is NaN? dedxmid_i = %g",dedxmid_i); + else + egsInformation("\n Is infinite? dedxmid_i = %g",dedxmid_i); +} +else{ + if (dedxmid_i<0){ + egsInformation("\n Is negative? dedxmid_i = %g Emid = %g lnEmid = %g index = %d", + dedxmid_i,exp(lnEmid),lnEmid, dedx_i[imed].getIndex(lnEmid) ); + } + if (dedxmid_i > 1.E10) + egsInformation("\n Is very large? dedxmid_i = %g Emid = %g lnEmid = %g",dedxmid_i,exp(lnEmid),lnEmid); + +} +#endif + /* O(eps^3) approach */ + if (flu_stpwr == stpwr) return dedxmid_i; + + /* O(eps^5) approach */ + EGS_Float b = i_dedx[imed].get_b(i_dedx[imed].getIndexFast(lnEmid)); + EGS_Float aux = b*dedxmid_i; + aux = aux*(1+2*aux)*pow(eps/(2-eps),2)/6; + //aux = aux*(1+2*aux)*eps*eps/((2-eps)*(2-eps))*0.16666666667; + stpFrac = dedxmid_i*(1+aux); + return stpFrac; + } + + + void setCurrentCase(EGS_I64 ncase) { + if( ncase != current_ncase ) { + current_ncase = ncase; + if (flu){ + for (int j = 0; j < nreg; j++){ + if ( is_sensitive[j] ) + flu[j]->setHistory(ncase); + } + fluT->setHistory(ncase); + } + } +#ifdef DEBUG + binDist->setHistory(ncase); +#endif + + }; + void resetCounter(){ + current_ncase = 0; + if (flu){ + for (int j = 0; j < nreg; j++) + if ( is_sensitive[j] ) + flu[j]->reset(); + fluT->reset(); + } +#ifdef DEBUG + binDist->reset(); +#endif + } + void getNumberRegions(const string &str, vector ®s); + + void getLabelRegions(const string &str, vector ®s); + + bool storeState(ostream &data) const; + + bool setState(istream &data); + + bool addState(istream &data); + +private: + + ParticleType scoring_charge; + + string particle_name; + + EGS_I64 current_ncase; + + /* Fluence Scoring Arrays */ + EGS_ScoringArray** flu; + EGS_ScoringArray* fluT; + + /*******************************************/ + /* Charged particle fluence: Required data */ + /*******************************************/ + EGS_Interpolator* i_dedx; // stopping power for each medium + EGS_Interpolator* dedx_i; // inverse stopping power for each medium + EGS_Float* Lmid_i; // pre-computed inverse of bin midpoint stpwr + eFluType flu_stpwr; // flurz => ave. stpwr = edep/tvstep, + // stpwr => 3rd order in edep/Eb, + // stpwrO5 => 5th order in edep/Eb + /************************************************************** + * Parameters required for calculations on a log-scale + * The main issue here is that the bin width is not constant + *************************************************************/ + EGS_Float r_const; // inverse of (Emax/Emin)**1/flu_nbin - 1 = exp(binwidth)-1 + EGS_Float *a_const; // constant needed to determine bin fractions on log scale + EGS_Float *DE; // bin width of logarithmic scale + /*****************************************************************/ + + vector volume;// volume of each scoring region + int active_region; // Region showing calculation progress + int n_scoring_regions; // number of scoring regions + int nreg; // regions in geometry + int max_reg ; // maximum scoring region number + vector is_sensitive;// flag scoring regions + EGS_Float norm_u; // User normalization + /* Energy grid inputs */ + EGS_Float flu_a, flu_a_i, + flu_b, + flu_xmin, + flu_xmax; + int flu_s, + flu_nbin; + /* Classification variables */ + EGS_Float m_primary, + m_tot; + /* Auxiliary input variables*/ + vector vol_list; // Input list of region volumes + vector f_region; // Input list of scoring regions + string f_regionsString;// Input string of scoring regions or labels + +#ifdef DEBUG + /* Debugging information */ + int one_bin, multi_bin; + EGS_ScoringArray *binDist; +#endif + + bool verbose; + +}; + +#endif diff --git a/HEN_HOUSE/egs++/egs_advanced_application.cpp b/HEN_HOUSE/egs++/egs_advanced_application.cpp index 4137fb332..4d5bce73d 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.cpp +++ b/HEN_HOUSE/egs++/egs_advanced_application.cpp @@ -1221,6 +1221,7 @@ void EGS_AdvancedApplication::setRadiativeSplitting(const EGS_Float &nsplit) { //************************************************************ // Utility functions for use with ausgab fluence scoring objects //************************************************************ + EGS_Float EGS_AdvancedApplication::getTVSTEP() { return the_epcont->tvstep; }; diff --git a/HEN_HOUSE/egs++/egs_advanced_application.h b/HEN_HOUSE/egs++/egs_advanced_application.h index 1c7070244..88f41fa73 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.h +++ b/HEN_HOUSE/egs++/egs_advanced_application.h @@ -221,7 +221,10 @@ class APP_EXPORT EGS_AdvancedApplication : public EGS_Application { // Utility functions for fluence scoring objects //************************************************************ EGS_Float getTVSTEP(); - + EGS_Interpolator* eDEDX( int imed ) { + return &i_ededx[imed]; + }; + /* Needed by some sources */ EGS_Float getRM(); /* Turn ON/OFF radiative splitting */ diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index 6b9bfabdf..edb4c856a 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -43,6 +43,7 @@ #include "egs_base_geometry.h" #include "egs_base_source.h" #include "egs_simple_container.h" +#include "egs_interpolator.h" #include #include @@ -54,6 +55,7 @@ class EGS_RandomGenerator; class EGS_RunControl; class EGS_GeometryHistory; class EGS_AusgabObject; +class EGS_Interpolator; //template class EGS_SimpleContainer; /*! \brief A structure holding the information of one particle @@ -1155,6 +1157,14 @@ class EGS_EXPORT EGS_Application { return 0.0; }; + virtual EGS_Interpolator* eDEDX( int imed ) { + return 0; + }; + + string sourceType() { + return source->getObjectType(); + } + //************************************************************ // Utility function for ausgab phase space scoring objects //************************************************************ diff --git a/HEN_HOUSE/egs++/egs_interpolator.cpp b/HEN_HOUSE/egs++/egs_interpolator.cpp index 27fb14bea..b66680502 100644 --- a/HEN_HOUSE/egs++/egs_interpolator.cpp +++ b/HEN_HOUSE/egs++/egs_interpolator.cpp @@ -87,7 +87,7 @@ void EGS_Interpolator::initialize(int nbin, EGS_Float Xmin, EGS_Float Xmax, clear(); own_data = true; n = nbin - 1; - a = new EGS_Float [n], b = new EGS_Float [n]; + a = new EGS_Float [nbin], b = new EGS_Float [nbin]; xmin = Xmin; xmax = Xmax; fmin = values[0]; @@ -99,6 +99,13 @@ void EGS_Interpolator::initialize(int nbin, EGS_Float Xmin, EGS_Float Xmax, b[j] = (values[j+1]-values[j])*bx; a[j] = values[j] - b[j]*(xmin + dx*j); } + /**************************************************** + Extra subinterval at top of interval, taking care + of round-off errors via extrapolation. + Mimics PEGS4 PWLF QFIT approach. + *****************************************************/ + a[n] = a[n-1]; + b[n] = b[n-1]; } void EGS_Interpolator::initialize(int nbin, EGS_Float Xmin, EGS_Float Xmax, From f507af53879aaf3aa3d6bbd5491525cdf59ecc5e Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 27 Jan 2022 15:59:01 -0500 Subject: [PATCH 03/18] Handle positrons in fluence scoring object Get proper stopping power interpolator depending on charge. --- .../egs_fluence_scoring/egs_fluence_scoring.cpp | 2 +- HEN_HOUSE/egs++/egs_advanced_application.h | 9 +++++++-- HEN_HOUSE/egs++/egs_application.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index fbf5408d1..4930fd3d7 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -619,7 +619,7 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { i_dedx = new EGS_Interpolator [n_media];// stp powers dedx_i = new EGS_Interpolator [n_media];// its inverse for( int j = 0; j < n_media; j++ ){ - i_dedx[j] = *(app->eDEDX(j)); + i_dedx[j] = *( app->getDEDX( j, scoring_charge ) ); EGS_Float Emin = i_dedx[j].getXmin(); EGS_Float Emax = i_dedx[j].getXmax(); int n = 1 + i_dedx[j].getIndex(Emax);// getIndex returns lower bin limit? diff --git a/HEN_HOUSE/egs++/egs_advanced_application.h b/HEN_HOUSE/egs++/egs_advanced_application.h index 88f41fa73..61f6acb0b 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.h +++ b/HEN_HOUSE/egs++/egs_advanced_application.h @@ -221,8 +221,13 @@ class APP_EXPORT EGS_AdvancedApplication : public EGS_Application { // Utility functions for fluence scoring objects //************************************************************ EGS_Float getTVSTEP(); - EGS_Interpolator* eDEDX( int imed ) { - return &i_ededx[imed]; + EGS_Interpolator* getDEDX( const int &imed, const int &iq ) { + if (iq == -1) + return &i_ededx[imed]; + else if (iq == 1) + return &i_pdedx[imed]; + else + return 0; }; /* Needed by some sources */ diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index edb4c856a..0bb49ff0b 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1157,7 +1157,7 @@ class EGS_EXPORT EGS_Application { return 0.0; }; - virtual EGS_Interpolator* eDEDX( int imed ) { + virtual EGS_Interpolator* getDEDX( const int &imed, const int &iq ) { return 0; }; From 0c9755775fa692106673957104eddfe8a349949c Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 27 Jan 2022 16:11:02 -0500 Subject: [PATCH 04/18] Add debugging switch and clean target - Add "clean" target for dose and fluence scoring objects (instead of manually deleting files when building the ausgab object). Need to add to the rest of the ausgab objects! - One can now define DEBUG=-DDEBUG to turn on some debugging messages that I found useful. - Add MYDEF for user defines: use MYDEF=-DDEBUG to activate debugging messages and variables. --- HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile | 4 ++++ HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile b/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile index 677e9fca5..4f3489bc4 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile @@ -46,3 +46,7 @@ $(make_depend) test: @echo "common_h2: $(common_h2)" @echo "extra_dep: $(extra_dep)" + +clean: + $(REMOVE) $(ABS_DSO)*$(library)* + $(REMOVE) *.o diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile index 97a50a517..1e96fd9fd 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/Makefile @@ -31,8 +31,8 @@ include $(EGS_CONFIG) include $(SPEC_DIR)egspp.spec include $(SPEC_DIR)egspp_$(my_machine).conf -# For debugging invoke: make DEBUG=-DDEBUG -DEFS = $(DEF1) -DBUILD_FLUENCE_SCORING_DLL $(DEBUG) +# For extra debugging messages invoke: make MYDEFS=-DDEBUG +DEFS = $(DEF1) -DBUILD_FLUENCE_SCORING_DLL $(MYDEFS) library = egs_fluence_scoring lib_files = egs_fluence_scoring From 937411123126350b66acce3ff0df95ade088db6d Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 3 Feb 2022 08:18:54 -0500 Subject: [PATCH 05/18] Add base class to fluence scoring object Reworked the classes for planar and volumetric fluence scoring to descend from a common base class which contains many commonalities, to avoid code duplication. Should have started that way! --- .../egs_fluence_scoring.cpp | 87 +-- .../egs_fluence_scoring/egs_fluence_scoring.h | 552 ++++++++++-------- 2 files changed, 357 insertions(+), 282 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 4930fd3d7..315b049ab 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -39,12 +39,25 @@ #include "egs_input.h" #include "egs_functions.h" -EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : - EGS_AusgabObject(Name,f), hits_field(false), - scoring_charge(photon), particle_name("photon"), +EGS_FluenceScoring::EGS_FluenceScoring(const string &Name, EGS_ObjectFactory *f) : + EGS_AusgabObject(Name,f), particle_name("photon"), + scoring_charge(photon), source_charge(photon), flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), - norm_u(1.0), Nx(1), Ny(1), flu(0), fluT(0), current_ncase(0), + norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0), + verbose(false), score_primaries(false), m_primary(0.0), m_tot(0.0) +{} + +/*! Destructor. */ +EGS_FluenceScoring::~EGS_FluenceScoring(){ + if(flu) delete [] flu; + if(fluT) delete fluT; + if(flu_p) delete [] flu_p; + if(fluT_p) delete fluT_p; +} + +EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : + EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) { otype = "EGS_PlanarFluence"; m_midpoint = EGS_Vector(0,0,5); @@ -60,8 +73,9 @@ EGS_PlanarFluence::~EGS_PlanarFluence(){ if( flu ) { for(int j=0; j output_spectra; - output_spectra.push_back("no"); - output_spectra.push_back("yes"); - verbose = inp->getInput("verbose",output_spectra,0); + vector choice; + choice.push_back("no"); + choice.push_back("yes"); + verbose = inp->getInput("verbose",choice,0); + score_primaries = inp->getInput("score primaries",choice,0); /* get region volume[s] in g/cm3 */ vector v_in; @@ -946,12 +969,12 @@ void EGS_VolumetricFluence::ouputResults(){ // Get the number of regions in the geometry. for (int j = 0; j < nreg; j++) { if ( !is_sensitive[j] ) continue; - EGS_Float norm = 1.0/src_norm;// per particle or fluence - norm *= norm_u; // user-requested normalization - norm /= volume[j]; //per unit volume - //norm *= flu_a; //per unit bin width <- implicit in scoring! + EGS_Float norm = 1.0/src_norm; // per particle or fluence + norm *= norm_u; // user-requested normalization + norm /= volume[j]; //per volume + norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! - EGS_Float the_bw = flu_s? 1.0 : flu_a_i;// Implicit for log grid + EGS_Float the_bw = flu_s? 1.0 : flu_a_i; // Implicit for log grid if (verbose){ egsInformation("\nNormalization = Ncase/Fsrc/V = %g\n",norm); @@ -1052,8 +1075,6 @@ bool EGS_VolumetricFluence::addState(istream &data){ return true; } - - extern "C" { EGS_FLUENCE_SCORING_EXPORT EGS_AusgabObject* diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index 6b5e9b049..df52c051c 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -42,13 +42,6 @@ This ausgab object is specified via :start ausgab object: library = egs_fluence_scoring name = some_name - score photons = yes or no # optional, yes assumed if missing - score electrons = yes or no # optional, yes assumed if missing - score positrons = yes or no # optional, yes assumed if missing - start scoring = event_number # optional, 0 assumed if missing - stop scoring = event_number # optional, 1024 assumed if missing - buffer size = size # optional, 1024 assumed if missing - file name addition = some_string # optional, empty string assumed if missing :stop ausgab object: \endverbatim The output file name is normally constructed from the output file name, @@ -103,7 +96,45 @@ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; class EGS_AdvancedApplication; -class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_AusgabObject { +class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { + +public: + /*! Constructors */ + EGS_FluenceScoring(const string &Name="", EGS_ObjectFactory *f = 0); + /*! Destructor. */ + ~EGS_FluenceScoring(); + +protected: + + ParticleType scoring_charge;// charge of scored particles + ParticleType source_charge; // charge of source particles + + /* Fluence Scoring Arrays */ + EGS_ScoringArray **flu; // differential fluence: primaries + secondaries + EGS_ScoringArray **flu_p; // differential fluence: primaries only + EGS_ScoringArray *fluT; // Total fluence: primaries + secondaries + EGS_ScoringArray *fluT_p;// Total fluence: primaries only + + EGS_Float norm_u; // User normalization + + /* Energy grid inputs */ + EGS_Float flu_a, flu_a_i, + flu_b, + flu_xmin, + flu_xmax; + int flu_s, + flu_nbin; + /* Classification variables */ + EGS_Float m_primary, + m_tot; + + string particle_name; + EGS_I64 current_ncase; + bool verbose, + score_primaries; +}; + +class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { public: /*! Constructors */ @@ -183,43 +214,26 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_AusgabObject { FieldType field_type; - ParticleType scoring_charge; - - string particle_name; + EGS_Float Area; - EGS_ScoringArray** flu; - EGS_ScoringArray* fluT; /* Circular scoring field parameters */ EGS_Vector m_normal, m_midpoint, ux, uy; EGS_Vector x0; - EGS_Float m_R, m_R2, // scoring field - norm_u; // User normalization + EGS_Float m_R, m_R2, // scoring field + norm_u; // User normalization /* Rectangular field parameters */ EGS_Float ax, ay, vx, vy; int Nx, Ny, n_sensitive_regs, ixy; - /* Energy grid inputs */ - EGS_Float Area, flu_a, - flu_b, - flu_xmin, - flu_xmax; - int flu_s, - flu_nbin; - EGS_Float m_d; // distance from origin to center of scoring field - EGS_Float distance; // distance to scoring field along particle's direction - EGS_Float m_primary, - m_tot; -// int fluence_scoring, energy_scoring; - bool hits_field; - EGS_I64 current_ncase; - - bool verbose; + EGS_Float m_d; // distance from origin to center of scoring field + EGS_Float distance; // distance to scoring field along particle's direction + bool hits_field; }; -class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_AusgabObject { +class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScoring { public: /*! Constructors */ @@ -228,11 +242,29 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_AusgabObject /*! Destructor. */ ~EGS_VolumetricFluence(); + /*************************************************************** + NOTE: Primary particles defined as particles that suffer any + type of interaction, except for the slowing down of + charged particles in a medium. + ****************************************************************/ bool needsCall(EGS_Application::AusgabCall iarg) const { if (iarg == EGS_Application::BeforeTransport || iarg == EGS_Application::UserDiscard ){ return true; } + else if ( score_primaries && + (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh || + iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterBhabha || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest )) + { + return true; + } else { return false; } @@ -250,210 +282,248 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_AusgabObject int processEvent(EGS_Application::AusgabCall iarg) { - int q = app->top_p.q; - if ( q != scoring_charge ) return 0; - - int ir = app->top_p.ir; - if ( ir < 0 || !is_sensitive[ir] ) return 0; - - /* Scoring photon about to be transported in geometry */ - if ( !q && iarg == EGS_Application::BeforeTransport ) { - /* Track-Length scoring (classic) */ - EGS_Float e = app->top_p.E, - wtstep = app->top_p.wt*app->getTVSTEP(); - if (flu_s) { - e = log(e); - } - EGS_Float ae; - int je; - if (e > flu_xmin && e <= flu_xmax) { - ae = flu_a*e + flu_b; - je = min((int)ae,flu_nbin-1); - EGS_ScoringArray *aux = flu[ir]; - aux->score(je,wtstep); - fluT->score(ir,wtstep); + int q = app->top_p.q, + ir = app->top_p.ir; + + if ( q == scoring_charge && ir >= 0 && is_sensitive[ir] ) { + + if ( !q ){// It's a photon + /* Score photon fluence */ + if (iarg == EGS_Application::BeforeTransport ) { + /* Track-Length scoring (classic) */ + EGS_Float e = app->top_p.E, + wtstep = app->top_p.wt*app->getTVSTEP(); + if (flu_s) { + e = log(e); + } + EGS_Float ae; + int je; + if (e > flu_xmin && e <= flu_xmax) { + ae = flu_a*e + flu_b; + je = min((int)ae,flu_nbin-1); + EGS_ScoringArray *aux = flu[ir]; + aux->score(je,wtstep); + fluT->score(ir,wtstep); + } } - return 0; } + else {// It's a charged particle - EGS_Float edep = app->getEdep(); - - /* Scoring charged particle about to be transported in geometry */ - if ( q && edep && - ( iarg == EGS_Application::BeforeTransport || - iarg == EGS_Application::UserDiscard ) ){ - /***** Initialization *****/ - EGS_Float Eb = app->top_p.E - app->getRM(), - Ee = Eb - edep, - weight = app->top_p.wt; - EGS_Float xb, xe; - /*********************************/ - if( flu_s ) { - xb = log(Eb); - if( Ee > 0 ) - xe = log(Ee); - else xe = -15; - } - else{ - xb = Eb; xe = Ee; - } - EGS_Float ab, ae; int jb, je; - if( xb > flu_xmin && xe < flu_xmax ) { - /* Fraction of the initial bin covered */ - if( xb < flu_xmax ) { - ab = flu_a*xb + flu_b; jb = (int) ab; - /* Variable bin-width for log scale*/ - if (flu_s){ - ab = (Eb*a_const[jb]-1)*r_const; - } - else{ ab -= jb;}// particle's energy above Emax - } - else { xb = flu_xmax; ab = 1; jb = flu_nbin - 1; } - /* Fraction of the final bin covered */ - if( xe > flu_xmin ) { - ae = flu_a*xe + flu_b; je = (int) ae; - /* Variable bin-width for log scale*/ - if (flu_s){ - ae = (Ee*a_const[je]-1)*r_const; - } - else{ ae -= je; } - } - else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin - + EGS_Float edep = app->getEdep(); + + /* Score charged particle fluence */ + if ( edep && + ( iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::UserDiscard ) ){ + + /**************************/ + /***** Initialization *****/ + /**************************/ + + EGS_Float Eb = app->top_p.E - app->getRM(), + Ee = Eb - edep, + weight = app->top_p.wt; + + EGS_Float xb, xe; + if( flu_s ) { + xb = log(Eb); + if( Ee > 0 ) + xe = log(Ee); + else xe = -15; + } + else{ + xb = Eb; xe = Ee; + } + + /**********************************************************/ + /* If not out of bounds, proceed with rest of calculation */ + /**********************************************************/ + if( xb > flu_xmin && xe < flu_xmax ) { + EGS_Float ab, ae; int jb, je; + /* Fraction of the initial bin covered */ + if( xb < flu_xmax ) { + ab = flu_a*xb + flu_b; jb = (int) ab; + /* Variable bin-width for log scale*/ + if (flu_s){ + ab = (Eb*a_const[jb]-1)*r_const; + } + else{ ab -= jb;}// particle's energy above Emax + } + else { xb = flu_xmax; ab = 1; jb = flu_nbin - 1; } + /* Fraction of the final bin covered */ + if( xe > flu_xmin ) { + ae = flu_a*xe + flu_b; je = (int) ae; + /* Variable bin-width for log scale*/ + if (flu_s){ + ae = (Ee*a_const[je]-1)*r_const; + } + else{ ae -= je; } + } + else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin + #ifdef DEBUG - //egsInformation("iarg = %d jb = %d je = %d edep = %g MeV weight = %g\n",iarg,jb,je,edep,weight); - if (jb == je) - one_bin++; - else{ - multi_bin++; - //egsInformation("\niarg = %d jb = %d je = %d edep = %g MeV ab = %g ae = %g",iarg,jb,je,edep,ab,ae); - } - binDist->score(jb-je,weight); -#endif - EGS_ScoringArray *aux = flu[ir]; - EGS_ScoringArray *auxT = fluT; - - /************************************************ - * Approach A: - * ----------- - * Uses either an O(3) or O(5) series expansion of the - * integral of the inverse of the stopping power with - * respect to energy. The stopping power is represented - * as a linear interpolation over a log energy grid. It - * accounts for stopping power variation along the particle's - * step within the resolution of the scoring array. This - * is more accurate than the method used in FLURZnrc albeit - * about 10% slower in electron beam cases. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - * - ************************************************/ - if (flu_stpwr){ - int imed = app->getMedium(ir); - // Initial and final energies in same bin - EGS_Float step; - if( jb == je ){ - step = weight*(ab-ae)*getStepPerFraction(imed,xb,xe); - aux->score(jb,step); - if (flu_s) - auxT->score(ir,step*DE[jb]); - else - auxT->score(ir,step); + //egsInformation("iarg = %d jb = %d je = %d edep = %g MeV weight = %g\n",iarg,jb,je,edep,weight); + if (jb == je) + one_bin++; + else{ + multi_bin++; + //egsInformation("\niarg = %d jb = %d je = %d edep = %g MeV ab = %g ae = %g",iarg,jb,je,edep,ab,ae); } - else { - //EGS_Float flu_a_i = 1/flu_a; - // First bin - Ee = flu_xmin + jb*flu_a_i; Eb=xb; - step = weight*ab*getStepPerFraction(imed,Eb,Ee); - aux->score(jb,step); - if (flu_s) + binDist->score(jb-je,weight); +#endif + EGS_ScoringArray *aux = flu[ir]; + EGS_ScoringArray *auxT = fluT; + + /************************************************ + * Approach A: + * ----------- + * Uses either an O(3) or O(5) series expansion of the + * integral of the inverse of the stopping power with + * respect to energy. The stopping power is represented + * as a linear interpolation over a log energy grid. It + * accounts for stopping power variation along the particle's + * step within the resolution of the scoring array. This + * is more accurate than the method used in FLURZnrc albeit + * about 10% slower in electron beam cases. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + * + ************************************************/ + if (flu_stpwr){ + int imed = app->getMedium(ir); + // Initial and final energies in same bin + EGS_Float step; + if( jb == je ){ + step = weight*(ab-ae)*getStepPerFraction(imed,xb,xe); + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + } + else { + //EGS_Float flu_a_i = 1/flu_a; + // First bin + Ee = flu_xmin + jb*flu_a_i; Eb=xb; + step = weight*ab*getStepPerFraction(imed,Eb,Ee); + aux->score(jb,step); + if (flu_s) + auxT->score(ir,step*DE[jb]); + else + auxT->score(ir,step); + // Last bin + Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; + step = weight*(1-ae)*getStepPerFraction(imed,Eb,Ee); + aux->score(je,step); + if (flu_s) + auxT->score(ir,step*DE[je]); + else + auxT->score(ir,step); + // intermediate bins + for(int j=je+1; jscore(j,step); + if (flu_s) + auxT->score(ir,step*DE[j]); + else + auxT->score(ir,step); + } + } + } + /*************************************************** + * ----------------------- + * Approach B (FLURZnrc): + * ---------------------- + * Path length at each energy interval from energy + * deposited edep and total particle step tvstep. It + * assumes stopping power constancy along the particle's + * step. It might introduce artifacts if ESTEPE or the + * scoring bin width are too large. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + **************************************************/ + else{ + EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; + // Initial and final energies in same bin + if( jb == je ){ + step = wtstep*(ab-ae); + aux->score(jb,step); + if (flu_s) auxT->score(ir,step*DE[jb]); - else - auxT->score(ir,step); - // Last bin - Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; - step = weight*(1-ae)*getStepPerFraction(imed,Eb,Ee); - aux->score(je,step); - if (flu_s) - auxT->score(ir,step*DE[je]); - else + else auxT->score(ir,step); - // intermediate bins - for(int j=je+1; jscore(j,step); + } + else { + // First bin + step = wtstep*ab; + aux->score(jb,step); if (flu_s) - auxT->score(ir,step*DE[j]); + auxT->score(ir,step*DE[jb]); else auxT->score(ir,step); - } - } - } - /*************************************************** - * ----------------------- - * Approach B (FLURZnrc): - * ---------------------- - * Path length at each energy interval from energy - * deposited edep and total particle step tvstep. It - * assumes stopping power constancy along the particle's - * step. It might introduce artifacts if ESTEPE or the - * scoring bin width are too large. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - **************************************************/ - else{ - EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; - // Initial and final energies in same bin - if( jb == je ){ - step = wtstep*(ab-ae); - aux->score(jb,step); - if (flu_s) - auxT->score(ir,step*DE[jb]); - else - auxT->score(ir,step); - } - else { - // First bin - step = wtstep*ab; - aux->score(jb,step); - if (flu_s) - auxT->score(ir,step*DE[jb]); - else - auxT->score(ir,step); - // Last bin - step = wtstep*(1-ae); - aux->score(je,step); - if (flu_s) - auxT->score(ir,step*DE[je]); - else - auxT->score(ir,step); - // intermediate bins - for(int j=je+1; jscore(j,wtstep); - if (flu_s) - auxT->score(ir,wtstep*DE[j]); - else - auxT->score(ir,wtstep); - } + // Last bin + step = wtstep*(1-ae); + aux->score(je,step); + if (flu_s) + auxT->score(ir,step*DE[je]); + else + auxT->score(ir,step); + // intermediate bins + for(int j=je+1; jscore(j,wtstep); + if (flu_s) + auxT->score(ir,wtstep*DE[j]); + else + auxT->score(ir,wtstep); + } + } } - } + } } } + } + + /*************************************************************** + NOTE: Secondary particles simply defined as those undergoing any + interaction, except for charged particles slowing down + in a medium. + + BEWARE: Latch set to 1 (bit 0) to flag secondaries in the above sense. + Other applications might use latch for other purposes! + ****************************************************************/ + if ( score_primaries && + (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh || + iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterBhabha || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest )) + { + int np = app->getNp(), npold = app->getNpOld(); + + for(int ip = npold; ip <= np; ip++) { + app->setLatch(ip,1); + } + } + return 0; }; @@ -560,16 +630,6 @@ else{ private: - ParticleType scoring_charge; - - string particle_name; - - EGS_I64 current_ncase; - - /* Fluence Scoring Arrays */ - EGS_ScoringArray** flu; - EGS_ScoringArray* fluT; - /*******************************************/ /* Charged particle fluence: Required data */ /*******************************************/ @@ -588,27 +648,23 @@ else{ EGS_Float *DE; // bin width of logarithmic scale /*****************************************************************/ - vector volume;// volume of each scoring region - int active_region; // Region showing calculation progress + vector volume; // volume of each scoring region + int active_region; // Region showing calculation progress int n_scoring_regions; // number of scoring regions int nreg; // regions in geometry - int max_reg ; // maximum scoring region number + int max_reg ; // maximum scoring region number + /* Regions flags */ vector is_sensitive;// flag scoring regions + vector is_source; // flag source regions such as brems target or radiactive source EGS_Float norm_u; // User normalization /* Energy grid inputs */ - EGS_Float flu_a, flu_a_i, - flu_b, - flu_xmin, - flu_xmax; - int flu_s, - flu_nbin; - /* Classification variables */ - EGS_Float m_primary, - m_tot; + EGS_Float flu_a_i; /* Auxiliary input variables*/ - vector vol_list; // Input list of region volumes - vector f_region; // Input list of scoring regions + vector vol_list; // Input list of region volumes + vector f_region; // Input list of scoring regions + vector s_region; // Input list of source regions string f_regionsString;// Input string of scoring regions or labels + string s_regionsString;// Input string of source regions or labels #ifdef DEBUG /* Debugging information */ @@ -616,8 +672,6 @@ else{ EGS_ScoringArray *binDist; #endif - bool verbose; - }; #endif From 22c62fa9f0756575a0f0d64ecb35367aba34db56 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 3 Feb 2022 08:26:28 -0500 Subject: [PATCH 06/18] Add auxiliary fluence scoring functions Add auxiliary functions to application classes, required by the fluence scoring ausgab object. --- HEN_HOUSE/egs++/egs_advanced_application.cpp | 30 ++++++++++++++++++-- HEN_HOUSE/egs++/egs_advanced_application.h | 13 ++++----- HEN_HOUSE/egs++/egs_application.h | 11 +++++++ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/HEN_HOUSE/egs++/egs_advanced_application.cpp b/HEN_HOUSE/egs++/egs_advanced_application.cpp index 4d5bce73d..e8fd4d03a 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.cpp +++ b/HEN_HOUSE/egs++/egs_advanced_application.cpp @@ -1219,12 +1219,36 @@ void EGS_AdvancedApplication::setRadiativeSplitting(const EGS_Float &nsplit) { } //************************************************************ -// Utility functions for use with ausgab fluence scoring objects +// Utility functions for fluence scoring objects //************************************************************ - EGS_Float EGS_AdvancedApplication::getTVSTEP() { return the_epcont->tvstep; -}; +} + +EGS_Interpolator* EGS_AdvancedApplication::getDEDX( const int &imed, const int &iq ) { + if (iq == -1) + return &i_ededx[imed]; + else if (iq == 1) + return &i_pdedx[imed]; + else + return 0; +} + +void EGS_AdvancedApplication::setLatch( const int &ip, const int &latch ) { + the_stack->latch[ip] = latch; +} + +void EGS_AdvancedApplication::incLatch( const int &ip, const int &increment ){ + the_stack->latch[ip] += increment; +} + +int EGS_AdvancedApplication::getNp() { + return the_stack->np-1;; +} + +int EGS_AdvancedApplication::getNpOld() { + return the_stack->npold-1; +} //************************************************************ // Utility function for ausgab phase space scoring objects diff --git a/HEN_HOUSE/egs++/egs_advanced_application.h b/HEN_HOUSE/egs++/egs_advanced_application.h index 61f6acb0b..a0db92d12 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.h +++ b/HEN_HOUSE/egs++/egs_advanced_application.h @@ -221,14 +221,11 @@ class APP_EXPORT EGS_AdvancedApplication : public EGS_Application { // Utility functions for fluence scoring objects //************************************************************ EGS_Float getTVSTEP(); - EGS_Interpolator* getDEDX( const int &imed, const int &iq ) { - if (iq == -1) - return &i_ededx[imed]; - else if (iq == 1) - return &i_pdedx[imed]; - else - return 0; - }; + EGS_Interpolator* getDEDX( const int &imed, const int &iq ); + void setLatch( const int &ip, const int &latch ); + void incLatch( const int &ip, const int &increment ); + int getNp(); + int getNpOld(); /* Needed by some sources */ EGS_Float getRM(); diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index 0bb49ff0b..419223e84 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1165,6 +1165,17 @@ class EGS_EXPORT EGS_Application { return source->getObjectType(); } + virtual void setLatch( const int &ip, const int &latch ){}; + virtual void incLatch( const int &ip, const int &increment ){}; + + virtual int getNp() { + return 0; + }; + + virtual int getNpOld() { + return 0; + }; + //************************************************************ // Utility function for ausgab phase space scoring objects //************************************************************ From 35da590c236fca817eb3292cac095e565e55a384 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Wed, 9 Feb 2022 16:31:23 -0500 Subject: [PATCH 07/18] Add a method to get the charge of source particles - Add a method to get the charge of a base source, which returns -99 by default, matching the unknown type of the fluence scoring ausgab object. A base source does not know the charge of its particles unless it is later derived from a simple source. For instance a collection of sources, a beam source, or a phase space source will contain multiple type of particles. - Sources derived from the class EGS_SimpleSource return the source particle's charge. - Add a method to get source charge from the application. --- HEN_HOUSE/egs++/egs_application.h | 5 +++++ HEN_HOUSE/egs++/egs_base_source.h | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index 419223e84..c7a14233a 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1165,7 +1165,12 @@ class EGS_EXPORT EGS_Application { return source->getObjectType(); } + int sourceCharge() { + return source->getCharge(); + } + virtual void setLatch( const int &ip, const int &latch ){}; + virtual void incLatch( const int &ip, const int &increment ){}; virtual int getNp() { diff --git a/HEN_HOUSE/egs++/egs_base_source.h b/HEN_HOUSE/egs++/egs_base_source.h index cc3f2c74b..8d87a8ad0 100644 --- a/HEN_HOUSE/egs++/egs_base_source.h +++ b/HEN_HOUSE/egs++/egs_base_source.h @@ -142,6 +142,16 @@ class EGS_EXPORT EGS_BaseSource : public EGS_Object { */ virtual void setSimulationChunk(EGS_I64 nstart, EGS_I64 nrun) { }; + /*! \brief Get the charge of the source. + * + * This virtual function must be re-implemented in derived classes + * that return the source particles' charge. Multi-particle sources + * will return a value of -99. This value corresponds to an unknown + * particle type in the fluence scoring AOs. + */ + virtual int getCharge() const { + return -99; + }; /*! \brief Return the maximum energy of this source. * * This pure virtual function must be implemented in derived classes @@ -640,7 +650,7 @@ class EGS_EXPORT EGS_BaseSimpleSource : public EGS_BaseSource { * * Simply returns the value of the (protected) attribute q. */ - virtual int getCharge() const { + int getCharge() const { return q; }; From 0786b1e1a156a7417c793c362794ce59d2c43e2d Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Tue, 1 Mar 2022 11:48:15 -0500 Subject: [PATCH 08/18] Add method to get the maximum energy of the source Add an application-level method to obtain the maximum energy of the source. This proves useful for ausgab objects that need this information. --- HEN_HOUSE/egs++/egs_application.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index c7a14233a..7be9e0869 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1169,6 +1169,10 @@ class EGS_EXPORT EGS_Application { return source->getCharge(); } + int sourceEmax() { + return source->getEmax(); + } + virtual void setLatch( const int &ip, const int &latch ){}; virtual void incLatch( const int &ip, const int &increment ){}; From da84f5a61a57ec7d41ac9c5d3e3dcb37f7e099eb Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Wed, 27 Jul 2022 16:54:55 -0400 Subject: [PATCH 09/18] Set fluence rectangular field at origin, along +z --- .../egs_fluence_scoring/egs_fluence_scoring.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 315b049ab..6d804a1c1 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -163,7 +163,7 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { EGS_Float xmin = tmp_field[0],xmax = tmp_field[1], ymin = tmp_field[2],ymax = tmp_field[3]; /* scoring plane location in space */ - m_midpoint = EGS_Vector((xmax+xmin)/2.,(ymax+ymin)/2.,0); // plane at origin by default + m_midpoint = EGS_Vector((xmax+xmin)/2.,(ymax+ymin)/2.,0); // at origin by default /* scoring plane normal */ m_normal = EGS_Vector(0,0,1); // default normal along positive z-axis /* define unit vectors on right-handed scoring plane */ @@ -215,8 +215,8 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { if( err1 || tmp_normal.size() != 3 ) { egsWarning( "\n\n*** Wrong/missing 'scoring plane normal' input. " - "Set to null vector\n\n"); - m_normal = EGS_Vector();// set to null vector + "Set along positive z-axis\n\n"); + m_normal = EGS_Vector(0,0,1);// Default along positive z-axis } else{ m_normal = EGS_Vector(tmp_normal[0],tmp_normal[1], From 651c5ac361264e86524260d4c8b9b20723f29e23 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Wed, 9 Feb 2022 16:37:26 -0500 Subject: [PATCH 10/18] Improve fluence scoring logic, add documentation - Add option to define regions from where to score planar fluence to improve efficiency of charged particle calculations. - Move many common tasks to the base class EGS_FluenceScoring. - Fix group scoring region definitions. - Improve logic for defining scoring regions in planar scoring class. - Document classes with Doxygen comments. --- .../egs_fluence_scoring.cpp | 689 ++++++++++++------ .../egs_fluence_scoring/egs_fluence_scoring.h | 546 +++++++++++--- 2 files changed, 909 insertions(+), 326 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 6d804a1c1..e00c30134 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -26,12 +26,14 @@ # Contributors: # ############################################################################### +*/ /*! \file egs_fluence_scoring.cpp * \brief A fluence scoring ausgab object: implementation - * + * \EM */ + #include #include @@ -39,16 +41,23 @@ #include "egs_input.h" #include "egs_functions.h" +#define REGIONS_ENTRIES 100 +#define REGIONS_PER_LINE 25 + EGS_FluenceScoring::EGS_FluenceScoring(const string &Name, EGS_ObjectFactory *f) : EGS_AusgabObject(Name,f), particle_name("photon"), - scoring_charge(photon), source_charge(photon), + scoring_charge(photon), source_charge(unknown), + n_scoring_regions(0), n_source_regions(0), + max_reg(-1), active_region(-1), flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0), verbose(false), score_primaries(false), m_primary(0.0), m_tot(0.0) -{} +{ + otype = "EGS_FluenceScoring"; +} -/*! Destructor. */ +/*! Destructor. */ EGS_FluenceScoring::~EGS_FluenceScoring(){ if(flu) delete [] flu; if(fluT) delete fluT; @@ -56,52 +65,21 @@ EGS_FluenceScoring::~EGS_FluenceScoring(){ if(fluT_p) delete fluT_p; } -EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : - EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) -{ - otype = "EGS_PlanarFluence"; - m_midpoint = EGS_Vector(0,0,5); - m_R = 5; - m_R2 = 25; - field_type = circle; Area = M_PI * m_R2; - m_normal = EGS_Vector(0,0,1); - m_d = m_normal*m_midpoint; -} - -/*! Destructor. */ -EGS_PlanarFluence::~EGS_PlanarFluence(){ - - if( flu ) { - for(int j=0; j name; int the_selection = 0; - name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); - the_selection = inp->getInput("scoring particle", name, 0); + name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); + name.push_back("undefined"); + the_selection = inp->getInput("scoring particle", name, 3); switch(the_selection){ + case 0: + particle_name="photon"; + scoring_charge = photon; + break; case 1: particle_name="electron"; scoring_charge = electron; @@ -111,16 +89,43 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { scoring_charge = positron; break; default: - particle_name="photon"; - scoring_charge = photon; + egsFatal("\n******* ERROR *******\n" + " Undefined scoring charge!\n" + " Aborting!\n" + "************************\n"); + } + + the_selection = inp->getInput("source particle", name, 0); + switch(the_selection){ + case 0: + source_charge = photon; + break; + case 1: + source_charge = electron; + break; + case 2: + source_charge = positron; + break; + default: + source_charge = unknown; } EGS_Float flu_Emin, flu_Emax, norma; int err_n = inp->getInput("number of bins",flu_nbin); int err_i = inp->getInput("minimum kinetic energy",flu_Emin); int err_f = inp->getInput("maximum kinetic energy",flu_Emax); + if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: number of bins.\n" + " Aborting!\n\n"); + if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: minimum kinetic energy.\n" + " Aborting!\n\n"); + if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: maximum kinetic energy.\n" + " Aborting!\n\n"); int err_norm = inp->getInput("normalization",norma); if (!err_norm) norm_u = norma; + else norm_u = 1.0; vector scale; scale.push_back("linear"); scale.push_back("logarithmic"); @@ -141,10 +146,290 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { ******************************************************/ //flu_nbin++; - vector output_spectra; - output_spectra.push_back("no"); - output_spectra.push_back("yes"); - verbose = inp->getInput("verbose",output_spectra,0); + vector choice; + choice.push_back("no"); + choice.push_back("yes"); + verbose = inp->getInput("verbose",choice,0); + score_primaries = inp->getInput("score primaries",choice,0); + + /* + Get source regions where interacting particles + are not subjected to classification + */ + vector s_start, s_stop; + /* + if (!inp->getInput("source regions",s_regionsString) && s_regionsString.length()>0) { + using_all_regions = false; // individual regions + } + else {*/ + /* Failed reading individual regions, try group of regions! */ + if (inp->getInput("source regions",s_regionsString) || s_regionsString.length()<=0) { + int err1 = inp->getInput("start source region",s_start); + int err2 = inp->getInput("stop source region",s_stop); + if (!err1 && !err2) { + if (s_start.size() == s_stop.size()) { // group of dose regions + for (int i=0; i fr) + egsFatal("\nEGS_FluenceScoring::initScoring: \n" + " Decreasing start (%d) / stop %d region in source region group %d\n" + " Aborting!\n\n", ir, fr, i ); + for (int ireg=ir; ireg<=fr; ireg++) { + s_region.push_back(ireg); + } + } + } + else egsFatal( + "\nEGS_FluenceScoring::initScoring: \n" + " Mismatch in number of start (%d) and stop (%d)\n" + " source region groups. Aborting!\n", + s_start.size(),s_stop.size()); + } + } + + /* get scoring regions */ + if (!inp->getInput("scoring regions",f_regionsString) && f_regionsString.length()>0) { + // Individual regions + if ( f_regionsString == "ALL") + score_in_all_regions = true; + else + score_in_all_regions = false; + } + else {// Groups of regions + int err1 = inp->getInput("start region",f_start); + int err2 = inp->getInput("stop region",f_stop); + if (!err1 && !err2) { + if ( f_start.size() == f_stop.size() ){ // group of dose regions + for ( int i=0; i fr ) + egsFatal("\nEGS_FluenceScoring::initScoring: \n" + " Decreasing start (%d) / stop (%d) in region group %d\n" + " Aborting!\n\n", ir, fr, i ); + for (int ireg=ir; ireg<=fr; ireg++) { + f_region.push_back(ireg); + } + } + score_in_all_regions = false; + } + else egsFatal( + "\nEGS_FluenceScoring::initScoring: \n" + " Mismatch in number of start (%d) and stop (%d)\n" + " region groups. Aborting!\n", + f_start.size(),f_stop.size()); + } + } +} + +void EGS_FluenceScoring::getNumberRegions( const string &str, vector ®s ){ + if ( !app ) + egsFatal("EGS_FluenceScoring::getNumberRegions\n" + " Undefined parent application! Aborting!\n"); + + app->getNumberRegions(str, regs); +} + +void EGS_FluenceScoring::getLabelRegions( const string &str, vector ®s ) { + if ( !app ) + egsFatal("EGS_FluenceScoring::getLabelRegions\n" + " Undefined parent application! Aborting!\n"); + + app->getLabelRegions(str, regs); +} + +void EGS_FluenceScoring::setUpRegionFlags() { + + if ( s_regionsString.length() > 0 && !s_region.size() ) { + getNumberRegions(s_regionsString, s_region ); + getLabelRegions( s_regionsString, s_region ); + } + // Get the number of source regions. If too many, reset to nreg. + n_source_regions = s_region.size() < nreg ? s_region.size() : nreg; + + if ( !score_in_all_regions ){ + if ( f_regionsString.length() > 0 && !f_region.size() ){ + getNumberRegions(f_regionsString, f_region); + getLabelRegions(f_regionsString, f_region); + } + // Get the number of scoring regions. If too many, reset to nreg + n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg; + } + else if ( score_in_all_regions ){ + n_scoring_regions = nreg; + for (int j=0; j Warning: Setting n_source_regions = nreg \n" + " effectively supresses classification\n" + " of primaries and secondaries!"); + for (int i = 0; i < n_source_regions; i++) { + is_source[s_region[i]] = true; + } + + for (int i = 0; i < n_scoring_regions; i++) { + is_sensitive[f_region[i]] = true; + if ( f_region[i] > max_reg ) max_reg = f_region[i]; + } +} + +void EGS_FluenceScoring::describeMe(){ + + char buf[128]; + + // Report scoring regions in the geometry. + if ( n_scoring_regions == nreg ) + description += "ALL\n"; + else{ + int start = 0, stop = 0, k = 0, entries = 0; + bool line_ended; + /* List up to 100 scoring groups or regions */ + while (k < nreg && entries < REGIONS_ENTRIES){ + line_ended = false; + if( is_sensitive[k] ){ + start = k; + entries++; + while( is_sensitive[k] && k < nreg) { + k++; + } + stop = k-1; + if ( start < stop ){ + sprintf(buf,"%d-%d",start,stop); + } + else if ( k % REGIONS_PER_LINE ){ + sprintf(buf," %d",start); + } + else{ + sprintf(buf," %d\n",start); + line_ended = true; + } + if (entries == REGIONS_ENTRIES){ + sprintf(buf,"... %d\n",max_reg); + line_ended = true; + } + description += buf; + } + k++; + } + if (!line_ended) description += "\n"; + } + + if ( score_primaries ){ + if ( source_charge == unknown ){ + source_charge = scoring_charge; + description += " - Unknown source charge due to multi-particle source\n"; + description += " and no user input defining source particle type!\n"; + description += " Defaulting to scoring charge: "; + } + else{ + description += " - source charge: "; + } + sprintf(buf,"%d\n",source_charge); + description += buf; + } + + if (flu){ + description += " - scoring in the "; + EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, + Emax = flu_s ? exp(flu_xmax):flu_xmax; + sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); + description += buf; + if (flu_s) + description += " on a logarithmic scale \n"; + else + description += " on a linear scale \n"; + } +} +/********************************************** + * Class EGS_PlanarFluence Implementation * +***********************************************/ + + +EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : + EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) +{ + otype = "EGS_PlanarFluence"; + m_midpoint = EGS_Vector(0,0,5); + m_R = 5; + m_R2 = 25; + field_type = circle; Area = M_PI * m_R2; + m_normal = EGS_Vector(0,0,1); + m_d = m_normal*m_midpoint; +} + +/*! Destructor. */ +EGS_PlanarFluence::~EGS_PlanarFluence(){ + + if( flu ) { + for(int j=0; jsourceCharge(); + + + // Get the number of regions in the geometry. + nreg = app->getnRegions(); + + /* Initialize arrays with defaults */ + for (int j=0; j tmp_field; @@ -235,10 +520,10 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { void EGS_PlanarFluence::describeMe(){ char buf[128]; - sprintf(buf,"\nPlanar %s fluence\n",particle_name.c_str()); + sprintf(buf,"\nPlanar %s fluence scoring\n",particle_name.c_str()); description = buf; - //description = "\nParticle Fluence Scoring\n"; - description += "========================\n"; + description += "================================\n"; + description += " - scoring field normal = "; sprintf(buf,"(%g %g %g)\n",m_normal.x,m_normal.y,m_normal.z); description += buf; @@ -262,21 +547,10 @@ void EGS_PlanarFluence::describeMe(){ sprintf(buf,"%g cm\n",m_d); description += buf; - if (flu){ - //if (planar_fluence) - // description += " - scoring planar particle fluence between "; - //else - description += " - scoring planar particle fluence in the "; - EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, - Emax = flu_s ? exp(flu_xmax):flu_xmax; - sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); - description += buf; - if (flu_s) - description += " on a logarithmic scale \n"; - else - description += " on a linear scale \n"; - } - + description +=" - scoring from region(s): "; + + EGS_FluenceScoring::describeMe(); + } void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ @@ -287,14 +561,15 @@ void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ if( aup < 0.08 ) aup = 0.0871557;// Limit incident angle to 85 degrees EGS_Float e = p.q ? p.E - app->getRM() : p.E; if (flu){ - //EGS_Float fup = planar_fluence ? 1.0:aup; - EGS_Float fup = aup; if( flu_s ) {e = log(e);} // log scale EGS_Float ae; int je; /* Score fluence in each voxel */ if( e > flu_xmin && e <= flu_xmax) { /* Score total fluence in each voxel */ - fluT->score(ivoxel,p.wt/fup); + EGS_Float auxp = p.wt/aup; + fluT->score(ivoxel,auxp); + if (score_primaries && !p.latch) + fluT_p->score(ivoxel,auxp); /* Score differential fluence in each voxel */ ae = flu_a*e + flu_b; /****************************************************** @@ -307,10 +582,12 @@ void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ /******************************************************/ if (ivoxel < 0 || ivoxel > Nx*Ny) egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); - EGS_ScoringArray *aux = flu[ivoxel]; + EGS_ScoringArray *aux = flu[ivoxel]; if (je < 0 || je >= flu_nbin ) egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); - aux->score(je,p.wt/fup); + aux->score(je,auxp); + if (score_primaries && !p.latch) + flu_p[ivoxel]->score(je,auxp); } } } @@ -353,7 +630,7 @@ int EGS_PlanarFluence::hitsField(const EGS_Particle& p, EGS_Float* dist){ void EGS_PlanarFluence::ouputResults(){ if (!flu) return; -EGS_Float src_norm = 1.0, // default to number of histories in this run + EGS_Float src_norm = 1.0, // default to number of histories in this run Fsrc = app->getFluence();// Fluence or number of primary histories egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", current_ncase, Fsrc); @@ -416,20 +693,19 @@ EGS_Float src_norm = 1.0, // default to number of histories in this run spe_output << "@ subtitle font 4\n"; spe_output << "@ subtitle size 1.000000\n"; - + int i_graph = 0; egsInformation("\n\n%s fluence scoring\n" "=================================\n",particle_name.c_str()); for(int j=0; jcurrentResult(k,fe,dfe); if( fe > 0 ) dfe = 100*dfe/fe; else dfe = 100; egsInformation(" total fluence = %10.4le +/- %-7.3lf\%\n", @@ -445,6 +721,30 @@ EGS_Float src_norm = 1.0, // default to number of histories in this run e,fe*norm,dfe*norm); } spe_output << "&\n"; + + if ( score_primaries ){ + fluT_p->currentResult(k,fe,dfe); + if( fe > 0 ) dfe = 100*dfe/fe; else dfe = 100; + egsInformation(" total primary fluence = %10.4le +/- %-7.3lf\%\n", + fe*norm/flu_a,dfe); + if (verbose) egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "---------------------------------------------\n"); + spe_output<<"@ s" << ++i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s" << i_graph <<" legend \""<< + "Voxel # " << k <<" (primary)\"\n"; + spe_output<<"@target G0.S"<< i_graph <<"\n"; + spe_output<<"@type xydy\n"; + for(int l=0; lcurrentResult(l,fe,dfe); + EGS_Float e = (l+0.5-flu_b)/flu_a; + if( flu_s ) e = exp(e); + spe_output<storeState(data) ) return false; } + if( flu_p ) { + for(int j=0; jstoreState(data) ) return false; + } + if( !fluT_p->storeState(data) ) return false; + } return true; } @@ -491,6 +797,12 @@ bool EGS_PlanarFluence::setState(istream &data){ } if( !fluT->setState(data) ) return false; } + if( flu_p ) { + for(int j=0; jsetState(data) ) return false; + } + if( !fluT_p->setState(data) ) return false; + } return true; } @@ -518,17 +830,28 @@ bool EGS_PlanarFluence::addState(istream &data){ if( !tgT.setState(data) ) return false; (*fluT) += tgT; } + if( flu_p ) { + EGS_ScoringArray tg_p(flu_nbin); + for(int j=0; j 0 && !f_region.size() ) { - getNumberRegions(f_regionsString, f_region); - getLabelRegions(f_regionsString, f_region); - } + /*********************************************************** + Defaults to charge of application source. This applies most + of the time, except for bremsstrahlung targets and multiple + particle sources such as radiactive sources. + Can be changed via 'source particle' input. + *************************************************************/ + if ( source_charge == unknown ) + source_charge = (ParticleType)app->sourceCharge(); // Get the number of regions in the geometry. nreg = app->getnRegions(); - // Get the number of scoring regions. If too many, reset to nreg - n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg; - /* Initialize arrays with defaults */ for (int j=0; j max_reg ) max_reg = f_region[i]; } /* Setup fluence scoring arrays */ @@ -690,14 +1015,6 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { describeMe(); } -void EGS_VolumetricFluence::getNumberRegions(const string &str, vector ®s) { - app->getNumberRegions(str, regs); -} - -void EGS_VolumetricFluence::getLabelRegions(const string &str, vector ®s) { - app->getLabelRegions(str, regs); -} - /* Takes user inputs and sets up simulation parameters not requiring invoking an application method. This is later done in setApplication. @@ -708,88 +1025,22 @@ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { egsWarning("AO type %s: null input?\n",otype.c_str()); return; } - vector name; int the_selection = 0; - name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); - the_selection = inp->getInput("scoring particle", name, 0); - switch(the_selection){ - case 1: - particle_name="electron"; - scoring_charge = electron; - break; - case 2: - particle_name="positron"; - scoring_charge = positron; - break; - default: - particle_name="photon"; - scoring_charge = photon; - } + /* Volumetric scoring by default turned off */ + score_in_all_regions = false; - EGS_Float flu_Emin, flu_Emax, norma; - int err_n = inp->getInput("number of bins",flu_nbin); - int err_i = inp->getInput("minimum kinetic energy",flu_Emin); - int err_f = inp->getInput("maximum kinetic energy",flu_Emax); - if (err_n) egsFatal("\n**** Missing input: number of bins. Aborting!\n\n"); - if (err_i) egsFatal("\n**** Missing input: minimum kinetic energy. Aborting!\n\n"); - if (err_f) egsFatal("\n**** Missing input: maximum kinetic energy. Aborting!\n\n"); - int err_norm = inp->getInput("normalization",norma); - if (!err_norm) norm_u = norma; - - vector scale; - scale.push_back("linear"); scale.push_back("logarithmic"); - flu_s = inp->getInput("scale",scale,0); - if( flu_s == 0 ) { - flu_xmin = flu_Emin; flu_xmax = flu_Emax; - } - else { - flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); - } - flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); - flu_b = -flu_xmin*flu_a; - - vector choice; - choice.push_back("no"); - choice.push_back("yes"); - verbose = inp->getInput("verbose",choice,0); - score_primaries = inp->getInput("score primaries",choice,0); + EGS_FluenceScoring::initScoring(inp); /* get region volume[s] in g/cm3 */ vector v_in; inp->getInput("volumes",v_in); - /* get dose regions */ - bool using_all_regions=true; - vector f_start, f_stop; - if (!inp->getInput("regions",f_regionsString) && f_regionsString.length()>0) { - using_all_regions = false; // individual regions - } - else { - int err1 = inp->getInput("start region",f_start); - int err2 = inp->getInput("stop region",f_stop); - if (!err1 && !err2) { - if (f_start.size()==f_stop.size()) { // group of dose regions - for (int i=0; i region # %d volume = %g cm3\n",l,volume[l]); - } - } + description +=" - scoring in region(s): "; - description += "\n\n - scoring in the "; - EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, - Emax = flu_s ? exp(flu_xmax):flu_xmax; - sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); - description += buf; - if (flu_s) - description += " on a logarithmic scale \n"; - else - description += " on a linear scale \n"; + EGS_FluenceScoring::describeMe(); if (flu_stpwr){ if (flu_stpwr == stpwr){ @@ -877,8 +1088,8 @@ void EGS_VolumetricFluence::describeMe(){ else description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n"; - if (norm_u != 1.0) { - description += " Non-unity user-requested normalization = "; + if (norm_u != 1.0D+0) { + description += "\n - Non-unity user-requested normalization = "; sprintf(buf,"%g\n",norm_u); description += buf; } @@ -965,8 +1176,9 @@ void EGS_VolumetricFluence::ouputResults(){ spe_output << "@ subtitle size 1.000000\n"; egsInformation("\n\n%s fluence scoring\n" - "=================================\n",particle_name.c_str()); - // Get the number of regions in the geometry. + "=================================\n\n",particle_name.c_str()); + int i_graph = 0; + // Loop through the number of regions in the geometry. for (int j = 0; j < nreg; j++) { if ( !is_sensitive[j] ) continue; EGS_Float norm = 1.0/src_norm; // per particle or fluence @@ -976,13 +1188,14 @@ void EGS_VolumetricFluence::ouputResults(){ EGS_Float the_bw = flu_s? 1.0 : flu_a_i; // Implicit for log grid + egsInformation("region # %d : ",j); + if (verbose){ - egsInformation("\nNormalization = Ncase/Fsrc/V = %g\n",norm); egsInformation("Volume[%d] = %g bw = %g nbins = %d\n",j,volume[j], the_bw, flu_nbin); + egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); } - egsInformation("region # %d : ",j); - - double fe,dfe,fp,dfp; + + double fe, dfe, fp, dfp; fluT->currentResult(j,fe,dfe); if (fe > 0) { dfe = 100*dfe/fe; @@ -990,12 +1203,25 @@ void EGS_VolumetricFluence::ouputResults(){ else { dfe = 100; } - egsInformation(" total fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", + egsInformation(" Total fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", fe*norm*the_bw,dfe); - spe_output<<"@ s"<currentResult(j,fp,dfp); + if (fp > 0) { + dfp = 100*dfp/fp; + } + else { + dfp = 100; + } + egsInformation(" Primary fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", + fp*norm*the_bw,dfp); + } + + if (verbose) egsInformation("\nTotal differential fluence:\n"); + spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; + spe_output<<"@target G0.S"<< i_graph <<"\n"; spe_output<<"@type xydy\n"; if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" "---------------------------------------------------\n"); @@ -1009,6 +1235,25 @@ void EGS_VolumetricFluence::ouputResults(){ } spe_output << "&\n"; + if ( score_primaries ){ + if (verbose) egsInformation("\nPrimary differential fluence:\n"); + spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; + spe_output<<"@target G0.S"<< i_graph <<"\n"; + spe_output<<"@type xydy\n"; + if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" + "---------------------------------------------------\n"); + for (int i=0; icurrentResult(i,fe,dfe); + EGS_Float e = (i+0.5-flu_b)/flu_a; + if (flu_s) e = exp(e); + spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; + if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", + e, fe*norm, dfe*norm); + } + spe_output << "&\n"; + } + i_graph++; } diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index df52c051c..c9ff1aa5c 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -27,30 +27,52 @@ # ############################################################################### -/*! \file egs_fluence_scoring.h - \brief A fluence scoring object : header - -\ingroup AusgabObjects - +Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at +circular or rectangular scoring fields located anywhere in space for a specific +particle type. Fluence can be scored for multiple particle types by definining +different AOs. An option exists for scoring the fluence of primary particles. +Classification into primary and secondary particles follows the definition used +in FLURZnrc for IPRIMARY = 2 (primaries) and IPRIMARY = 4 (secondaries). -NEEDS UPDATING ... ! +The basic definition of a fluence scoring AO is -This ausgab object can be used to score particle fluence during -run time and to output this information into a file This ausgab object is specified via -\verbatim -:start ausgab object: - library = egs_fluence_scoring - name = some_name -:stop ausgab object: -\endverbatim -The output file name is normally constructed from the output file name, -the string specified by file name addition (if present and not empty), -and _wJob in case of parallel runs. The extension given is ptracks. -Using start scoring and stop scoring one can select a -range of histories for which to score the particle track info. One can also -select specific particle type(s). - */ + + :start ausgab object: + name = a_name + library = egs_fluence_scoring + type = planar # planar or volumetric + scoring particle = electron # or photon, or positron + # Define scoring (volumetric) or contributing (planar) regions + scoring regions = ir1 ir2 ... irn + # Alternatively: + # start region = iri_1, iri_2, ..., iri_n + # stop region = irf_1, irf_2, ..., irf_n + volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number + # of entries as group of regions, assumes groups of + # equal volume regions. If only one entry, assumes + # equal volumes in all regions. Defaults to 1. + # score primaries = yes # or no, optional + # source regions = sr1 sr2 ... srN # <- no classification in these regions + # source particle = photon, electron, or positron #<- brems targets or multi-particle sources + # scoring grid + number of bins = 100 + minimum kinetic energy = 0.001 + maximum kinetic energy = 1.000 + normalization = 1 # user-normalization optional + scale = linear # or logarithmic + # Scoring field for planar scoring + scoring circle = 0 0 5.0 5.0 + scoring plane normal = 0 0 1 + verbose = yes # optional + :stop ausgab object: + +*/ + +/*! \file egs_fluence_scoring.h + * \brief A fluence scoring object : header + * \EM +*/ #ifndef EGS_FLUENCE_SCORING_ #define EGS_FLUENCE_SCORING_ @@ -89,13 +111,48 @@ using namespace std; enum FieldType { circle=0, rectangle=1 }; /*! Particle type */ -enum ParticleType { electron = -1, photon = 0, positron = 1 }; +enum ParticleType { electron = -1, photon = 0, positron = 1, unknown = -99 }; /*! Charged particle fluence calculation type */ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; -class EGS_AdvancedApplication; +/*! \brief Base class for fluence scoring ausgab objects + + \ingroup AusgabObjects + Contains the basic ingredients for fluence scoring such as energy grid, + particle type, scoring regions, and whether to score primary fluence. + Provides the method for defining primary and secondary particles. + +A basic fluence scoring ausgab object is specified via +\verbatim +:start ausgab object: + name = a_name + library = egs_fluence_scoring + type = a_type # planar or volumetric + scoring particle = a_particle # photon, electron, or positron + # Define scoring (volumetric) or contributing (planar) regions + scoring regions = ir1 ir2 ... irn + ## Alternatively: + # start region = iri_1, iri_2, ..., iri_n + # stop region = irf_1, irf_2, ..., irf_n + # score primaries = yes # or no, optional + # source regions = sr1 sr2 ... srN # <- no classification in these regions + # source particle = photon, electron, or positron #<- brems targets or multi-particle sources + ## scoring grid + number of bins = 100 + minimum kinetic energy = 0.001 + maximum kinetic energy = 1.000 + # normalization = 1 # user-normalization optional, defaults to 1 + scale = a_scale # linear or logarithmic + verbose = yes # or no, optional +:stop ausgab object: +\endverbatim + \todo Total kerma scoring + \todo Account for multiple app geometries + \todo Fluence for any particle type? + +*/ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { public: @@ -104,8 +161,78 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { /*! Destructor. */ ~EGS_FluenceScoring(); + void initScoring(EGS_Input *inp); + + void getNumberRegions( const string &str, vector ®s ); + + void getLabelRegions( const string &str, vector ®s ); + + void setUpRegionFlags(); + + void describeMe(); + + void flagSecondaries( const int &iarg, const int &q ){ + int npold = app->getNpOld(), + np = app->getNp(); + if ( scoring_charge ){ + /*************************************************************** + DEFAULT: + FLURZnrc IPRIMARY = 2 (primaries) IPRIMARY = 4 (secondaries) + Secondary charged particles defined as those resulting + from charged particle interactions, atomic relaxations + following EII, brems and annihilation photons. + + OPTIONAL: + FLURZnrc IPRIMARY = 1 (e- from brems as primaries) + Set source_particle to 'photon' in the input file to not flag + brems photons so that when scoring charged partcle fluence in + photon beams, first generation e- are primaries. This is + implicit for photon interactions when scoring charged particle + fluence. But one must explicitly account for that during brems events. + ****************************************************************/ + if ( iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest ){ + /************************************************************************ + Skip block below for a photon beam. First generation e- are primaries. + This will apply to ALL brems events in a photon beam simulation. One + could fine tune it to only brems events in certain regions, for instance + a bremsstrahlung target, by using the is_source flag for those regions. + *************************************************************************/ + if (!(iarg == EGS_Application::AfterBrems && source_charge == photon)){ + for(int ip = npold+1; ip <= np; ip++) { + app->setLatch(ip,1); + } + } + } + else if ( iarg == EGS_Application::AfterBhabha ){ + if (q == -1) app->setLatch(np,1); + else app->setLatch(np-1,1); + } + } + else{ + /*************************************************************** + FLURZnrc IPRIMARY = 3 + Flag scattered photons, secondaries, and relaxation + particles as secondaries + ****************************************************************/ + if ( iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh ){ + for(int ip = npold; ip <= np; ip++) { + app->setLatch(ip,1); + } + } + } + + }; + protected: + EGS_I64 current_ncase; + ParticleType scoring_charge;// charge of scored particles ParticleType source_charge; // charge of source particles @@ -115,7 +242,24 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { EGS_ScoringArray *fluT; // Total fluence: primaries + secondaries EGS_ScoringArray *fluT_p;// Total fluence: primaries only - EGS_Float norm_u; // User normalization + /* Regions flags */ + vector is_sensitive; // flag scoring regions + vector is_source; // Flag regions such as brems target or radiactive source + // Interacting particles not subjected to classification + vector f_start, f_stop; // Markers for group regions input + vector f_region; // Input list of scoring regions + vector s_region; // Input list of source regions + string f_regionsString; // Input string of scoring regions or labels + string s_regionsString; // Input string of source regions or labels + int n_scoring_regions;// number of scoring regions + int n_source_regions; // number of source regions + int nreg; // regions in geometry + int max_reg; // maximum scoring region number + int active_region; // Region showing calculation progress + bool score_in_all_regions; + bool source_in_all_regions; + + EGS_Float norm_u; // User normalization /* Energy grid inputs */ EGS_Float flu_a, flu_a_i, @@ -129,11 +273,62 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { m_tot; string particle_name; - EGS_I64 current_ncase; + /* Auxiliary input variables*/ bool verbose, score_primaries; }; +/*! \brief Ausgab object for scoring fluence at circular or rectangular fields + + \ingroup AusgabObjects + + A linear track-length estimator in the zero thickness limit is used to compute + fluence and fluence-related quantities such as total kerma and cema if the user + requestes it. Differencial and total fluence is estimated for a specific particle + type. If fluence for more than one particle type is desired, multiple AOs are + required. For charged particle fluence scoring this method can be inefficient as + it checks at every single step whether a charged particle is aimed at scoring field. + + To improve the efficieny for charged particles, one has the option to define regions + from where to score. However, users must be careful to select all regions from where + charged particles can cross the scoring field. This option has the added benefit of + allowing to estimate the contribution to the fluence from specific regions in the geometry. + + Scoring field can be either a circular field or a rectangular screen of arbitrary + resolution (pixels). For rectangular fields, fluence is computed at each screen element (pixel) + + To define an EGS_PlanarFluence AO use the syntax below: + \verbatim + :start ausgab object: + name = a_name + library = egs_fluence_scoring + type = volumetric + scoring particle = electron # or photon, or positron + # Scoring field for planar scoring + scoring circle = 0 0 5.0 5.0 + scoring plane normal = 0 0 1 + ## Define contributing regions + # scoring regions = ir1 ir2 ... irn # optional, defaults to ALL + # Alternatively: + # start region = iri_1, iri_2, ..., iri_n + # stop region = irf_1, irf_2, ..., irf_n + # score primaries = yes # or no, optional + # source regions = sr1 sr2 ... srN # <- no classification in these regions + # source particle = photon, electron, or positron #<- brems targets or multi-particle sources + # scoring grid + number of bins = 100 + minimum kinetic energy = 0.001 + maximum kinetic energy = 1.000 + normalization = 1 # user-normalization optional + scale = linear # or logarithmic + verbose = yes # optional + :stop ausgab object: + \endverbatim + + \todo Store results in a 2D binary file for visualization + \todo Add option for (total) kerma scoring + \todo Add option for CEMA scoring ??? +*/ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { public: @@ -142,12 +337,24 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { /*! Destructor. */ ~EGS_PlanarFluence(); EGS_Float area(){return Area;}; - //bool needsCall(EGS_Application::AusgabCall iarg) const { return true; }; bool needsCall(EGS_Application::AusgabCall iarg) const { if (iarg == EGS_Application::BeforeTransport || iarg == EGS_Application::AfterTransport ){ return true; } + else if ( score_primaries && + (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh || + iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterBhabha || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest )) + { + return true; + } else { return false; } @@ -162,29 +369,48 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { void reportResults(); int processEvent(EGS_Application::AusgabCall iarg) { - int q = app->top_p.q; + int q = app->top_p.q, + ir = app->top_p.ir; - if (q != scoring_charge ) return 0; + if ( q == scoring_charge && ir >= 0 && is_sensitive[ir] ) + { - /* Quantify photon contribution to scoring field */ - if( iarg == EGS_Application::BeforeTransport ){ - ixy = hitsField(app->top_p,&distance); - if (ixy >= 0){ - x0 = app->top_p.x; hits_field = true; - } - else{hits_field = false; } + /* Quantify contribution to scoring field */ + if( iarg == EGS_Application::BeforeTransport ){ + ixy = hitsField(app->top_p,&distance); + if (ixy >= 0){ + x0 = app->top_p.x; hits_field = true; + } + else{hits_field = false; } + } + + if( iarg == EGS_Application::AfterTransport && hits_field ){ + EGS_Vector xstep = app->top_p.x - x0; + if (xstep.length() >= distance){// crossed scoring field + //if (!app->top_p.latch ) m_primary += app->top_p.wt; + m_tot += app->top_p.wt; + score( app->top_p, ixy ); + } + hits_field = false; + } } - if( iarg == EGS_Application::AfterTransport && hits_field ){ - EGS_Vector xstep = app->top_p.x - x0; - if (xstep.length() >= distance){// crossed scoring field - //if (!app->top_p.latch ) m_primary += app->top_p.wt; - m_tot += app->top_p.wt; - score( app->top_p, ixy ); - } - hits_field = false; + /******************************************************************** + * Flag secondaries after interactions. Definition of secondaries + * matches FLURZnrc. One could fine tune it by using the is_source + * flag to skip this block in certains regions such as brems targets, + * radioactive sources, etc. + * + * BEWARE: Latch set to 1 (bit 0) to flag secondaries. + * Other applications might use latch for other purposes! + *********************************************************************/ + if ( score_primaries && ir >= 0 && !is_source[ir]){ + + flagSecondaries( iarg, q ); + } + return 0; }; @@ -196,6 +422,11 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { flu[j]->setHistory(ncase); fluT->setHistory(ncase); } + if (flu_p){ + for (int j = 0; j < Nx*Ny; j++) + flu_p[j]->setHistory(ncase); + fluT_p->setHistory(ncase); + } } }; void resetCounter(){ @@ -205,6 +436,11 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { flu[j]->reset(); fluT->reset(); } + if (flu_p){ + for (int j = 0; j < Nx*Ny; j++) + flu_p[j]->reset(); + fluT_p->reset(); + } } bool storeState(ostream &data) const; bool setState(istream &data); @@ -221,8 +457,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { m_midpoint, ux, uy; EGS_Vector x0; - EGS_Float m_R, m_R2, // scoring field - norm_u; // User normalization + EGS_Float m_R, m_R2; // scoring field /* Rectangular field parameters */ EGS_Float ax, ay, vx, vy; int Nx, Ny, n_sensitive_regs, ixy; @@ -233,6 +468,48 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { }; +/*! \brief Ausgab object for scoring fluence in arbitrary geometry regions + + \ingroup AusgabObjects + + A linear track-lenght estimator is used to compute fluence and fluence-related + quantities such as total kerma and cema if the user requestes it. Differencial + and total fluence is estimated for a specific particle type. If fluence for more + than one particle type is desired, multiple AOs are required. + + To define an EGS_VolumetricFluence AO use the syntax below: + \verbatim + :start ausgab object: + name = a_name + library = egs_fluence_scoring + type = volumetric + scoring particle = electron # or photon, or positron + # Define scoring (volumetric) or contributing (planar) regions + scoring regions = ir1 ir2 ... irn + # Alternatively: + # start region = iri_1, iri_2, ..., iri_n + # stop region = irf_1, irf_2, ..., irf_n + volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number + # of entries as group of regions, assumes groups of + # equal volume regions. If only one entry, assumes + # equal volumes in all regions. Defaults to 1. + # score primaries = yes # or no, optional + # source regions = sr1 sr2 ... srN # <- no classification in these regions + # source particle = photon, electron, or positron #<- brems targets or multi-particle sources + # scoring grid + number of bins = 100 + minimum kinetic energy = 0.001 + maximum kinetic energy = 1.000 + normalization = 1 # user-normalization optional + scale = linear # or logarithmic + verbose = yes # optional + :stop ausgab object: + \endverbatim + + \todo Add option for (total) kerma scoring + \todo Add option for CEMA scoring + +*/ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScoring { public: @@ -282,8 +559,9 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori int processEvent(EGS_Application::AusgabCall iarg) { - int q = app->top_p.q, - ir = app->top_p.ir; + int q = app->top_p.q, + ir = app->top_p.ir, + latch = app->top_p.latch; if ( q == scoring_charge && ir >= 0 && is_sensitive[ir] ) { @@ -304,6 +582,10 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori EGS_ScoringArray *aux = flu[ir]; aux->score(je,wtstep); fluT->score(ir,wtstep); + if (score_primaries && !latch){ + flu_p[ir]->score(je,wtstep); + fluT_p->score(ir,wtstep); + } } } } @@ -373,6 +655,12 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori #endif EGS_ScoringArray *aux = flu[ir]; EGS_ScoringArray *auxT = fluT; + EGS_ScoringArray *aux_p, *auxT_p; + if ( score_primaries ){ + aux_p = flu_p[ir]; + auxT_p = fluT_p; + } + bool score_p = score_primaries && !latch; /************************************************ * Approach A: @@ -397,29 +685,52 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if( jb == je ){ step = weight*(ab-ae)*getStepPerFraction(imed,xb,xe); aux->score(jb,step); - if (flu_s) + if (score_p) + aux_p->score(jb,step); + if (flu_s){ auxT->score(ir,step*DE[jb]); - else + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } } else { - //EGS_Float flu_a_i = 1/flu_a; // First bin Ee = flu_xmin + jb*flu_a_i; Eb=xb; step = weight*ab*getStepPerFraction(imed,Eb,Ee); aux->score(jb,step); - if (flu_s) + if (score_p) + aux_p->score(jb,step); + if (flu_s){ auxT->score(ir,step*DE[jb]); - else + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } // Last bin Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; step = weight*(1-ae)*getStepPerFraction(imed,Eb,Ee); aux->score(je,step); - if (flu_s) + if (score_p) + aux_p->score(je,step); + if (flu_s){ auxT->score(ir,step*DE[je]); - else + if (score_p) + auxT_p->score(ir,step*DE[je]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } // intermediate bins for(int j=je+1; jscore(j,step); - if (flu_s) + if (score_p) + aux_p->score(j,step); + if (flu_s){ auxT->score(ir,step*DE[j]); - else + if (score_p) + auxT_p->score(ir,step*DE[j]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } } } } @@ -463,33 +782,65 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if( jb == je ){ step = wtstep*(ab-ae); aux->score(jb,step); - if (flu_s) + if (score_p) + aux_p->score(jb,step); + if (flu_s){ auxT->score(ir,step*DE[jb]); - else + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } } else { // First bin step = wtstep*ab; aux->score(jb,step); - if (flu_s) + if (score_p) + aux_p->score(jb,step); + if (flu_s){ auxT->score(ir,step*DE[jb]); - else + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } // Last bin step = wtstep*(1-ae); aux->score(je,step); - if (flu_s) + if (score_p) + aux_p->score(je,step); + if (flu_s){ auxT->score(ir,step*DE[je]); - else + if (score_p) + auxT_p->score(ir,step*DE[je]); + } + else{ auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } // intermediate bins for(int j=je+1; jscore(j,wtstep); - if (flu_s) + if (score_p) + aux_p->score(j,wtstep); + if (flu_s){ auxT->score(ir,wtstep*DE[j]); - else + if (score_p) + auxT_p->score(ir,wtstep*DE[j]); + } + else{ auxT->score(ir,wtstep); + if (score_p) + auxT_p->score(ir,wtstep); + } } } } @@ -498,34 +849,23 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori } } - /*************************************************************** - NOTE: Secondary particles simply defined as those undergoing any - interaction, except for charged particles slowing down - in a medium. - - BEWARE: Latch set to 1 (bit 0) to flag secondaries in the above sense. - Other applications might use latch for other purposes! - ****************************************************************/ - if ( score_primaries && - (iarg == EGS_Application::AfterPair || - iarg == EGS_Application::AfterCompton || - iarg == EGS_Application::AfterPhoto || - iarg == EGS_Application::AfterRayleigh || - iarg == EGS_Application::AfterBrems || - iarg == EGS_Application::AfterMoller || - iarg == EGS_Application::AfterBhabha || - iarg == EGS_Application::AfterAnnihFlight || - iarg == EGS_Application::AfterAnnihRest )) - { - int np = app->getNp(), npold = app->getNpOld(); - - for(int ip = npold; ip <= np; ip++) { - app->setLatch(ip,1); - } + /******************************************************************** + * Flag secondaries after interactions. Definition of secondaries + * matches FLURZnrc. One could fine tune it by using the is_source + * flag to skip this block in certains regions such as brems targets, + * radioactive sources, etc. + * + * BEWARE: Latch set to 1 (bit 0) to flag secondaries. + * Other applications might use latch for other purposes! + *********************************************************************/ + if ( score_primaries && ir >= 0 && !is_source[ir]){ + + flagSecondaries( iarg, q ); + } + return 0; - return 0; }; /*! Computes path per energy bin-width traveled by a charged particle when @@ -600,6 +940,13 @@ else{ } fluT->setHistory(ncase); } + if (flu_p){ + for (int j = 0; j < nreg; j++){ + if ( is_sensitive[j] ) + flu_p[j]->setHistory(ncase); + } + fluT_p->setHistory(ncase); + } } #ifdef DEBUG binDist->setHistory(ncase); @@ -614,14 +961,17 @@ else{ flu[j]->reset(); fluT->reset(); } + if (flu_p){ + for (int j = 0; j < nreg; j++) + if ( is_sensitive[j] ) + flu_p[j]->reset(); + fluT_p->reset(); + } #ifdef DEBUG binDist->reset(); #endif } - void getNumberRegions(const string &str, vector ®s); - void getLabelRegions(const string &str, vector ®s); - bool storeState(ostream &data) const; bool setState(istream &data); @@ -649,22 +999,10 @@ else{ /*****************************************************************/ vector volume; // volume of each scoring region - int active_region; // Region showing calculation progress - int n_scoring_regions; // number of scoring regions - int nreg; // regions in geometry - int max_reg ; // maximum scoring region number - /* Regions flags */ - vector is_sensitive;// flag scoring regions - vector is_source; // flag source regions such as brems target or radiactive source - EGS_Float norm_u; // User normalization /* Energy grid inputs */ EGS_Float flu_a_i; /* Auxiliary input variables*/ vector vol_list; // Input list of region volumes - vector f_region; // Input list of scoring regions - vector s_region; // Input list of source regions - string f_regionsString;// Input string of scoring regions or labels - string s_regionsString;// Input string of source regions or labels #ifdef DEBUG /* Debugging information */ From a915a0cd691038d31a25ab2393f10c128659e7cd Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Tue, 1 Mar 2022 15:06:38 -0500 Subject: [PATCH 11/18] Improve and add features to fluence scoring object - Add the ability to divide fluence in total and primary fluence. - Add the ability to calculate integral and differential fluence. - Improve output format. - Investigate the effect of scoring integral charged particle fluence using either EDEP (within differential scoring block) or directly TVSTEP: a) Identical for FLURZ-like calculation b) Small, negligible differences using stprO3 and stpwrO5 This allows extraction of the integral fluence scoring from the blocks for differential fluence. When one is only interested in the integral fluence, the calculation is up to 10% faster using a TVSTEP-based approach. Current implementation allows switching between EDEP-based and TVSTEP-based integral fluence calculation via the pre-processor define USETVSTEP. To use TVSTEP-based integral dose calculation use: make MYDEFS=-DUSETVSTEP Next iteration will only use the TVSTEP-based approach for integral fluence as both approaches result in negligible differences. - Add mechanism to track the distribution of number of energy bins covered when spreading EDEP. This is activated via the define variable DEBUG: to use it, build the fluence scoring ausgab object using: make MYDEFS=-DDEBUG One can also trace the difference between the step derived from spreading EDEP over the energy grid and TVSTEP via DEBUG. --- .../egs_fluence_scoring.cpp | 899 +++++++++++------- .../egs_fluence_scoring/egs_fluence_scoring.h | 737 ++++++++------ 2 files changed, 1007 insertions(+), 629 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index e00c30134..5df67d3f5 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -51,10 +51,12 @@ EGS_FluenceScoring::EGS_FluenceScoring(const string &Name, EGS_ObjectFactory *f) max_reg(-1), active_region(-1), flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0), - verbose(false), score_primaries(false), + verbose(false), score_primaries(false), score_spe(false), m_primary(0.0), m_tot(0.0) { otype = "EGS_FluenceScoring"; + flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; } /*! Destructor. */ @@ -95,7 +97,7 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { "************************\n"); } - the_selection = inp->getInput("source particle", name, 0); + the_selection = inp->getInput("source particle", name, 3); switch(the_selection){ case 0: source_charge = photon; @@ -110,47 +112,50 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { source_charge = unknown; } - EGS_Float flu_Emin, flu_Emax, norma; - int err_n = inp->getInput("number of bins",flu_nbin); - int err_i = inp->getInput("minimum kinetic energy",flu_Emin); - int err_f = inp->getInput("maximum kinetic energy",flu_Emax); - if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: number of bins.\n" - " Aborting!\n\n"); - if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: minimum kinetic energy.\n" - " Aborting!\n\n"); - if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: maximum kinetic energy.\n" - " Aborting!\n\n"); - int err_norm = inp->getInput("normalization",norma); - if (!err_norm) norm_u = norma; - else norm_u = 1.0; - - vector scale; - scale.push_back("linear"); scale.push_back("logarithmic"); - flu_s = inp->getInput("scale",scale,0); - if( flu_s == 0 ) { - flu_xmin = flu_Emin; flu_xmax = flu_Emax; - } - else { - flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); - } - flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); - flu_b = -flu_xmin*flu_a; - /****************************************************** - Algorithm assigns E in [Ei,Ei+1), one could add extra - bin for E = Emax cases. Alternatively, push those - events into last bin (bias?) during scoring. - Which approach is correct? - ******************************************************/ - //flu_nbin++; - vector choice; choice.push_back("no"); choice.push_back("yes"); - verbose = inp->getInput("verbose",choice,0); + verbose = inp->getInput("verbose", choice,0); score_primaries = inp->getInput("score primaries",choice,0); + score_spe = inp->getInput( "score spectrum", choice,0); + + if ( score_spe ){ + EGS_Float flu_Emin, flu_Emax, norma; + int err_n = inp->getInput("number of bins",flu_nbin); + int err_i = inp->getInput("minimum kinetic energy",flu_Emin); + int err_f = inp->getInput("maximum kinetic energy",flu_Emax); + if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: number of bins.\n" + " Aborting!\n\n"); + if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: minimum kinetic energy.\n" + " Aborting!\n\n"); + if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: maximum kinetic energy.\n" + " Aborting!\n\n"); + int err_norm = inp->getInput("normalization",norma); + if (!err_norm) norm_u = norma; + else norm_u = 1.0; + + vector scale; + scale.push_back("linear"); scale.push_back("logarithmic"); + flu_s = inp->getInput("scale",scale,0); + if( flu_s == 0 ) { + flu_xmin = flu_Emin; flu_xmax = flu_Emax; + } + else { + flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); + } + flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), one could add extra + bin for E = Emax cases. Alternatively, push those + events into last bin (bias?) during scoring. + Which approach is correct? + ******************************************************/ + //flu_nbin++; + } /* Get source regions where interacting particles @@ -330,7 +335,7 @@ void EGS_FluenceScoring::describeMe(){ description += buf; } - if (flu){ + if (score_spe){ description += " - scoring in the "; EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, Emax = flu_s ? exp(flu_xmax):flu_xmax; @@ -404,17 +409,22 @@ void EGS_PlanarFluence::setApplication(EGS_Application *App) { setUpRegionFlags(); /* Setup fluence scoring arrays, Nx*Ny = 1 for the circle */ - flu = new EGS_ScoringArray* [Nx*Ny]; fluT = new EGS_ScoringArray(Nx*Ny); - for (int j = 0; j < Nx*Ny; j++) - flu[j] = new EGS_ScoringArray(flu_nbin); + if ( score_spe ){ + flu = new EGS_ScoringArray* [Nx*Ny]; + for (int j = 0; j < Nx*Ny; j++) { + flu[j] = new EGS_ScoringArray(flu_nbin); + } + } if ( score_primaries ){ - flu_p = new EGS_ScoringArray* [Nx*Ny]; fluT_p = new EGS_ScoringArray(Nx*Ny); - for (int j = 0; j < Nx*Ny; j++){ - flu_p[j] = new EGS_ScoringArray(flu_nbin); - } + if ( score_spe ){ + flu_p = new EGS_ScoringArray* [Nx*Ny]; + for (int j = 0; j < Nx*Ny; j++){ + flu_p[j] = new EGS_ScoringArray(flu_nbin); + } + } } describeMe(); @@ -560,35 +570,33 @@ void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ ***********************************************************/ if( aup < 0.08 ) aup = 0.0871557;// Limit incident angle to 85 degrees EGS_Float e = p.q ? p.E - app->getRM() : p.E; - if (flu){ - if( flu_s ) {e = log(e);} // log scale - EGS_Float ae; int je; - /* Score fluence in each voxel */ - if( e > flu_xmin && e <= flu_xmax) { - /* Score total fluence in each voxel */ - EGS_Float auxp = p.wt/aup; - fluT->score(ivoxel,auxp); - if (score_primaries && !p.latch) - fluT_p->score(ivoxel,auxp); - /* Score differential fluence in each voxel */ - ae = flu_a*e + flu_b; - /****************************************************** - Algorithm assigns E in [Ei,Ei+1), hence push events - with E = Emax into last bin (bias?) during scoring. - Alternatively add extra bin for E = Emax cases. - Which approach is correct? - ******************************************************/ - je = min((int)ae,flu_nbin-1);//je = (int) ae; - /******************************************************/ - if (ivoxel < 0 || ivoxel > Nx*Ny) - egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); - EGS_ScoringArray *aux = flu[ivoxel]; - if (je < 0 || je >= flu_nbin ) - egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); - aux->score(je,auxp); - if (score_primaries && !p.latch) - flu_p[ivoxel]->score(je,auxp); - } + if( flu_s ) {e = log(e);} // log scale + EGS_Float ae; int je; + EGS_Float auxp = p.wt/aup; + /* Score total fluence in corresponding pixel */ + fluT->score(ivoxel,auxp); + if (score_primaries && !p.latch) + fluT_p->score(ivoxel,auxp); + /* Score differential fluence in corresponding pixel */ + if( score_spe && e > flu_xmin && e <= flu_xmax ){ + ae = flu_a*e + flu_b; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), hence push events + with E = Emax into last bin (bias?) during scoring. + Alternatively add extra bin for E = Emax cases. + Which approach is correct? + ******************************************************/ + je = min((int)ae,flu_nbin-1);//je = (int) ae; + /******************************************************/ + if (ivoxel < 0 || ivoxel > Nx*Ny) + egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); + EGS_ScoringArray *aux = flu[ivoxel]; + if (je < 0 || je >= flu_nbin ) + egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); + aux->score(je,auxp); + if (score_primaries && !p.latch) { + flu_p[ivoxel]->score(je,auxp); + } } } @@ -627,127 +635,148 @@ int EGS_PlanarFluence::hitsField(const EGS_Particle& p, EGS_Float* dist){ else return -1; } -void EGS_PlanarFluence::ouputResults(){ - if (!flu) return; - - EGS_Float src_norm = 1.0, // default to number of histories in this run - Fsrc = app->getFluence();// Fluence or number of primary histories - egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", - current_ncase, Fsrc); - - if (Fsrc) - src_norm = Fsrc/current_ncase;// fluence or primary histories per histories run - - string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; - string src_type = app->sourceType(); - if ( src_type == "EGS_BeamSource" ){ - normLabel = "primary history"; - egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", - src_type.c_str(), src_norm); - } - else if ( src_type == "EGS_CollimatedSource" || - (src_type == "EGS_ParallelBeam" && src_norm != 1)){ - egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", - src_type.c_str(), src_norm); - } - else{ - egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", - src_type.c_str(), src_norm); +void EGS_PlanarFluence::ouputPlanarFluence( EGS_ScoringArray *fT, const double &norma ){ + double fe,dfe,dfer; + int count = 0; + int ix_digits = getDigits(Nx); + int iy_digits = getDigits(Ny); + int xy_digits = getDigits(Nx*Ny); + + egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n", + iy_digits,"iy",ix_digits,"ix",&count); + for(int j=0; jcurrentResult(k,fe,dfe); + if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; + egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); + } + } +} - } +void EGS_PlanarFluence::ouputResults(){ - double norm = 1.0/src_norm; //per fluence or particle depending on source - norm /= Area; //per unit area - norm *= flu_a; //per unit bin width - norm *= norm_u; // times user-requested normalization + EGS_Float src_norm = 1.0, // default to number of histories in this run + Fsrc = app->getFluence();// Fluence or number of primary histories + egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", + current_ncase, Fsrc); - if (verbose) - egsInformation("\nNormalization = Ncase/Fsrc/A/bw = %g\n",norm); - - string suffix = "_" + particle_name + ".agr"; - string spe_name = app->constructIOFileName(suffix.c_str(),true); - ofstream spe_output(spe_name.c_str(),ios::out); - //spe_output.open(spe_name.c_str()); - if (!spe_output){ - egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str()); - exit(1); - } - - spe_output << "# " << particle_name.c_str() << " fluence \n"; - spe_output << "# \n"; - spe_output << "@ legend 0.2, 0.8\n"; - spe_output << "@ legend box linestyle 0\n"; - spe_output << "@ legend font 4\n"; - spe_output << "@ xaxis label \"energy / MeV\"\n"; - spe_output << "@ xaxis label char size 1.560000\n"; - spe_output << "@ xaxis label font 4\n"; - spe_output << "@ xaxis ticklabel font 4\n"; - spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; - spe_output << "@ yaxis label char size 1.560000\n"; - spe_output << "@ yaxis label font 4\n"; - spe_output << "@ yaxis ticklabel font 4\n"; - spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; - spe_output << "@ title font 4\n"; - spe_output << "@ title size 1.500000\n"; - spe_output << "@ subtitle \"for each scoring region\"\n"; - spe_output << "@ subtitle font 4\n"; - spe_output << "@ subtitle size 1.000000\n"; - - int i_graph = 0; - egsInformation("\n\n%s fluence scoring\n" - "=================================\n",particle_name.c_str()); - for(int j=0; jcurrentResult(k,fe,dfe); - if( fe > 0 ) dfe = 100*dfe/fe; else dfe = 100; - egsInformation(" total fluence = %10.4le +/- %-7.3lf\%\n", - fe*norm/flu_a,dfe); - if (verbose) egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "---------------------------------------------\n"); - for(int l=0; lcurrentResult(l,fe,dfe); - EGS_Float e = (l+0.5-flu_b)/flu_a; - if( flu_s ) e = exp(e); - spe_output<currentResult(k,fe,dfe); - if( fe > 0 ) dfe = 100*dfe/fe; else dfe = 100; - egsInformation(" total primary fluence = %10.4le +/- %-7.3lf\%\n", - fe*norm/flu_a,dfe); - if (verbose) egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "---------------------------------------------\n"); - spe_output<<"@ s" << ++i_graph <<" errorbar linestyle 0\n"; - spe_output<<"@ s" << i_graph <<" legend \""<< - "Voxel # " << k <<" (primary)\"\n"; - spe_output<<"@target G0.S"<< i_graph <<"\n"; - spe_output<<"@type xydy\n"; - for(int l=0; lcurrentResult(l,fe,dfe); - EGS_Float e = (l+0.5-flu_b)/flu_a; - if( flu_s ) e = exp(e); - spe_output<sourceType(); + if ( src_type == "EGS_BeamSource" ){ + normLabel = "primary history"; + egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", + src_type.c_str(), src_norm); + } + else if ( src_type == "EGS_CollimatedSource" || + (src_type == "EGS_ParallelBeam" && src_norm != 1)){ + egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", + src_type.c_str(), src_norm); + } + else{ + egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", + src_type.c_str(), src_norm); + + } + + + double norm = 1.0/src_norm; //per fluence or particle depending on source + norm /= Area; //per unit area + norm *= norm_u; // times user-requested normalization + + //egsInformation(" Normalization = %g\n",norm); + + egsInformation("\n\n %s fluence\n" + " ==============\n", particle_name.c_str()); + ouputPlanarFluence( fluT, norm ); + + if ( score_primaries ){ + egsInformation("\n\n primary fluence\n" + " ==============\n"); + ouputPlanarFluence( fluT_p, norm ); + } + + if ( score_spe ){ + norm *= flu_a;//per unit bin width + string suffix = "_" + particle_name + ".agr"; + string spe_name = app->constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + if (!spe_output){ + egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + int i_graph = 0; + double fe,dfe; + for(int j=0; jcurrentResult(l,fe,dfe); + EGS_Float e = (l+0.5-flu_b)/flu_a; + if( flu_s ) e = exp(e); + spe_output<currentResult(l,fe,dfe); + EGS_Float e = (l+0.5-flu_b)/flu_a; + if( flu_s ) e = exp(e); + spe_output<storeState(data) ) return false; + + if( score_spe ) { for(int j=0; jstoreState(data) ) return false; } - if( !fluT->storeState(data) ) return false; } - if( flu_p ) { - for(int j=0; jstoreState(data) ) return false; - } - if( !fluT_p->storeState(data) ) return false; + + if ( score_primaries ){ + if( !fluT_p->storeState(data) ) return false; + if( score_spe ) { + for(int j=0; jstoreState(data) ) return false; + } + } } + return true; } @@ -791,17 +826,25 @@ bool EGS_PlanarFluence::setState(istream &data){ //data >> m_primary >> m_ray >> m_compt >> m_fluor >> m_multiple; data >> m_tot >> m_primary; if (!data.good()) return false; - if( flu ) { + + if( !fluT->setState(data) ) return false; + + if( score_spe ) { for(int j=0; jsetState(data) ) return false; } - if( !fluT->setState(data) ) return false; } - if( flu_p ) { - for(int j=0; jsetState(data) ) return false; - } - if( !fluT_p->setState(data) ) return false; + + if ( score_primaries ){ + + if( !fluT_p->setState(data) ) return false; + + if( score_spe ) { + for(int j=0; jsetState(data) ) return false; + } + } + } return true; } @@ -820,25 +863,32 @@ bool EGS_PlanarFluence::addState(istream &data){ //m_primary += tmp_primary;m_ray += tmp_ray;m_compt += tmp_compt; //m_fluor += tmp_fluor;m_multiple += tmp_multiple; /* fluence objects */ - if( flu ) { + + EGS_ScoringArray tgT(Nx*Ny); + if( !tgT.setState(data) ) return false; + (*fluT) += tgT; + + if( score_spe ) { EGS_ScoringArray tg(flu_nbin); for(int j=0; jgetDEDX( j, scoring_charge ) ); EGS_Float Emin = i_dedx[j].getXmin(); EGS_Float Emax = i_dedx[j].getXmax(); @@ -994,7 +1056,6 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { i_dedx[j].interpolate(Emin + k*bwidth), dedx_i[j].interpolate(Emin + k*bwidth)); } - #endif } @@ -1002,16 +1063,49 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { lnEmin = flu_s ? log(0.5*flu_Emin*(expbw+1)) : 0; Lmid_i = new EGS_Float [flu_nbin*n_media]; for( int j = 0; j < n_media; j++ ){ +#ifdef DEBUG + EGS_Float med_max_step = 0, logEmax, logEmin; +#endif for ( int i = 0; i < flu_nbin; i++ ){ lnEmid = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*(i+0.5)); Lmid_i[i+j*flu_nbin] = 1/i_dedx[j].interpolate(lnEmid); //egsInformation(" 1/L(%g MeV) = %g cm/MeV\n",exp(lnEmid),Lmid_i[i+j*flu_nbin]); +#ifdef DEBUG + // Set max_step to maximum range + if ( i > 0){ + logEmin = flu_s ? lnEmin + (i-1)*bw : log(flu_Emin+bw*(i-1)); + logEmax = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*i); + if (i_dedx[j].interpolate(logEmax) < i_dedx[j].interpolate(logEmin)) + med_max_step += 1.02*(exp(logEmax) - exp(logEmin))/i_dedx[j].interpolate(logEmax); + else + med_max_step += 1.02*(exp(logEmax) - exp(logEmin))*i_dedx[j].interpolate(logEmin); + } +#endif + } +#ifdef DEBUG + if ( med_max_step > max_step ){ + max_step = med_max_step; + imed_max_range = j; } +#endif } } } - +#ifdef DEBUG + EGS_Float RCSDA = max_step; + //max_step /= 4.0; // Set to a quarter of the CSDA range + //if ( max_step > 1.0 ) max_step = 1.0; + max_step = 1.0;// reset to 1 cm + step_a = n_step_bins/max_step; + step_b = 0; + relStepDiff = new EGS_ScoringArray(n_step_bins); + stepDist = new EGS_ScoringArray(n_step_bins); + eCases = 0; + egsInformation("\n===> RCSDA(%s) = %g cm for Emax = %g MeV, max_step = %g cm bin width = %g cm\n", + app->getMediumName(imed_max_range), RCSDA, + flu_s ? exp(flu_Emax) : flu_Emax, max_step, 1.0/step_a); +#endif describeMe(); } @@ -1062,7 +1156,9 @@ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { method.push_back("flurz"); method.push_back("stpwr"); // 3rd order method.push_back("stpwrO5"); // 5th order flu_stpwr = eFluType(inp->getInput("method",method,1)); + } + } void EGS_VolumetricFluence::describeMe(){ @@ -1078,11 +1174,11 @@ void EGS_VolumetricFluence::describeMe(){ if (flu_stpwr){ if (flu_stpwr == stpwr){ description += " O(eps^3) approach: accounts for change in stpwr\n"; - description += " along the step with eps=edep/Eb\n"; + description += " along the step with eps=edep/Emid\n"; } else if (flu_stpwr == stpwrO5){ description += " O(eps^5) approach: accounts for change in stpwr\n"; - description += " along the step with eps=edep/Eb\n"; + description += " along the step with eps=edep/Emid\n"; } } else @@ -1095,6 +1191,28 @@ void EGS_VolumetricFluence::describeMe(){ } } + +void EGS_VolumetricFluence::ouputVolumetricFluence( EGS_ScoringArray *fT, const double &norma ){ + double fe,dfe,dfer; + double norm = norma; +#ifndef USETVSTEP + norm *= flu_s? 1.0 : flu_a_i; // Implicit for log grid +#endif + int count = 0; + int ir_digits = getDigits(nreg); + + egsInformation("\n region# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n"); + for( int k=0; kcurrentResult(k,fe,dfe); + if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; + egsInformation(" %10.4le +/- %10.4le [%-7.3lf%]\n",fe*norm,dfe*norm,dfer); + } +} + void EGS_VolumetricFluence::ouputResults(){ @@ -1133,118 +1251,127 @@ void EGS_VolumetricFluence::ouputResults(){ one_bin, 100.0*one_bin/tot_bins, multi_bin,100.0*multi_bin/tot_bins); egsInformation("\n # bins freq unc percentage \n"); + int bdigits = getDigits(flu_nbin); for (int i=0; icurrentResult(i,fbins, d_fbins); if (fbins){ d_fbins = 100.0*d_fbins/fbins; fbins = current_ncase*fbins; - egsInformation(" %d %11.2f [%-7.3lf\%] %11.2f \%\n", - i+1, fbins, d_fbins, 100.*fbins/tot_bins); + egsInformation(" %*d %11.2f [%-7.3lf\%] %11.2f \%\n", + bdigits, i+1, fbins, d_fbins, 100.*fbins/tot_bins); } } + + if ( scoring_charge ){ + egsInformation("\nDistribution of ratio between computed and taken steps\n" + "------------------------------------------------------\n" + " (Omitted bins with differences less than 0.01\%)\n"); + /* + //egsInformation(" Relative step difference: %8.4f%% +/- %8.4f%% [%8.4lf%%]\n", + //egsInformation(" Relative step ratio: %10.4le +/- %10.4le [%8.4lf%%]\n", + */ + + EGS_Float step_diff, step_diff_std, step_diff_err, stepMid; + EGS_Float f_scores, f_scores_std; + int idigits = getDigits(eCases); + egsInformation("\n step / cm # scores ratio rel unc\n" + "---------------------------------------------------------\n"); + for (int i=0; i < n_step_bins; i++) { + stepDist->currentResult( i, f_scores, f_scores_std ); + if ( f_scores > 0 ){ + stepMid = (i + 0.5 - step_b)/step_a; + relStepDiff->currentResult( i, step_diff, step_diff_std ); + if( step_diff > 0 ) + step_diff_err = 100*step_diff_std/step_diff; + else + step_diff_err = 100; + if ( abs(step_diff/f_scores - 1.0) > 0.0001D+0 ) + egsInformation(" %10.4le %*d %10.4le [%8.4lf\%]\n", + stepMid, idigits, int(f_scores*current_ncase), step_diff/f_scores, step_diff_err); + } + } + + } + #endif - string suffix = "_" + particle_name + ".agr"; - string spe_name = app->constructIOFileName(suffix.c_str(),true); - ofstream spe_output(spe_name.c_str(),ios::out); - if (!spe_output){ - egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str()); - exit(1); + EGS_Float norm = 1.0/src_norm; // per particle or fluence + norm *= norm_u; // user-requested normalization + + egsInformation("\n\n %s fluence\n" + " =====================\n", particle_name.c_str()); + ouputVolumetricFluence( fluT, norm ); + + if ( score_primaries ){ + egsInformation("\n\n primary fluence\n" + " ==============\n"); + ouputVolumetricFluence( fluT_p, norm ); } - spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n"; - spe_output << "# \n"; - spe_output << "@ legend 0.2, 0.8\n"; - spe_output << "@ legend box linestyle 0\n"; - spe_output << "@ legend font 4\n"; - spe_output << "@ xaxis label \"energy / MeV\"\n"; - spe_output << "@ xaxis label char size 1.560000\n"; - spe_output << "@ xaxis label font 4\n"; - spe_output << "@ xaxis ticklabel font 4\n"; - if (src_norm == 1 || normLabel == "primary history") - spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; - else{ - spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n"; + if (verbose){ + egsInformation("\nbw = %g nbins = %d\n", flu_a_i, flu_nbin); } - spe_output << "@ yaxis label char size 1.560000\n"; - spe_output << "@ yaxis label font 4\n"; - spe_output << "@ yaxis ticklabel font 4\n"; - spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; - spe_output << "@ title font 4\n"; - spe_output << "@ title size 1.500000\n"; - spe_output << "@ subtitle \"for each scoring region\"\n"; - spe_output << "@ subtitle font 4\n"; - spe_output << "@ subtitle size 1.000000\n"; - - egsInformation("\n\n%s fluence scoring\n" - "=================================\n\n",particle_name.c_str()); - int i_graph = 0; - // Loop through the number of regions in the geometry. - for (int j = 0; j < nreg; j++) { - if ( !is_sensitive[j] ) continue; - EGS_Float norm = 1.0/src_norm; // per particle or fluence - norm *= norm_u; // user-requested normalization - norm /= volume[j]; //per volume - norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! - - EGS_Float the_bw = flu_s? 1.0 : flu_a_i; // Implicit for log grid - - egsInformation("region # %d : ",j); - - if (verbose){ - egsInformation("Volume[%d] = %g bw = %g nbins = %d\n",j,volume[j], the_bw, flu_nbin); - egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); - } - - double fe, dfe, fp, dfp; - fluT->currentResult(j,fe,dfe); - if (fe > 0) { - dfe = 100*dfe/fe; - } - else { - dfe = 100; - } - egsInformation(" Total fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", - fe*norm*the_bw,dfe); - if ( score_primaries ){ - fluT_p->currentResult(j,fp,dfp); - if (fp > 0) { - dfp = 100*dfp/fp; - } - else { - dfp = 100; + if ( score_spe ){ + string suffix = "_" + particle_name + ".agr"; + string spe_name = app->constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + if (!spe_output){ + egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + if (src_norm == 1 || normLabel == "primary history") + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + else{ + spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n"; + } + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + egsInformation("\n\n%s fluence scoring\n" + "=================================\n\n",particle_name.c_str()); + int i_graph = 0; + double fe, dfe; + norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! + // Loop through the number of regions in the geometry. + for (int j = 0; j < nreg; j++) { + + if ( !is_sensitive[j] ) continue; + + norm /= volume[j]; //per volume + + egsInformation("region # %d : ",j); + + if (verbose){ + egsInformation("Volume[%d] = %g", j, volume[j]); + egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); } - egsInformation(" Primary fluence [cm-2] = %10.4le +/- %-7.3lf\%\n", - fp*norm*the_bw,dfp); - } - - if (verbose) egsInformation("\nTotal differential fluence:\n"); - spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; - spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; - spe_output<<"@target G0.S"<< i_graph <<"\n"; - spe_output<<"@type xydy\n"; - if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" - "---------------------------------------------------\n"); - for (int i=0; icurrentResult(i,fe,dfe); - EGS_Float e = (i+0.5-flu_b)/flu_a; - if (flu_s) e = exp(e); - spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; - if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", - e, fe*norm, dfe*norm); - } - spe_output << "&\n"; - - if ( score_primaries ){ - if (verbose) egsInformation("\nPrimary differential fluence:\n"); - spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; - spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; + + if (verbose) egsInformation("\nTotal differential fluence:\n"); + spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; spe_output<<"@target G0.S"<< i_graph <<"\n"; spe_output<<"@type xydy\n"; if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" "---------------------------------------------------\n"); for (int i=0; icurrentResult(i,fe,dfe); + flu[j]->currentResult(i,fe,dfe); EGS_Float e = (i+0.5-flu_b)/flu_a; if (flu_s) e = exp(e); spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; @@ -1252,13 +1379,32 @@ void EGS_VolumetricFluence::ouputResults(){ e, fe*norm, dfe*norm); } spe_output << "&\n"; - } - i_graph++; - } - + + if ( score_primaries ){ + if (verbose) egsInformation("\nPrimary differential fluence:\n"); + spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; + spe_output<<"@target G0.S"<< i_graph <<"\n"; + spe_output<<"@type xydy\n"; + if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" + "---------------------------------------------------\n"); + for (int i=0; icurrentResult(i,fe,dfe); + EGS_Float e = (i+0.5-flu_b)/flu_a; + if (flu_s) e = exp(e); + spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; + if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", + e, fe*norm, dfe*norm); + } + spe_output << "&\n"; + } + i_graph++; + } - spe_output.close(); + spe_output.close(); + } + } void EGS_VolumetricFluence::reportResults() { @@ -1273,38 +1419,95 @@ void EGS_VolumetricFluence::reportResults() { bool EGS_VolumetricFluence::storeState(ostream &data) const { if( !egsStoreI64(data,current_ncase)) return false; data << endl; + if (!data.good()) return false; - if( flu ) { + +#ifdef DEBUG + if ( scoring_charge ){ + if( !relStepDiff->storeState(data) ) return false; + if( !stepDist->storeState(data) ) return false; + } +#endif + + if( !fluT->storeState(data) ) return false; + if( score_spe ) { for(int j = 0; j < nreg; j++) { if ( is_sensitive[j] ){ if( !flu[j]->storeState(data) ) return false; } } - if( !fluT->storeState(data) ) return false; } + + if ( score_primaries ){ + if( !fluT_p->storeState(data) ) return false; + if( score_spe ) { + for(int j=0; j < nreg; j++) { + if ( is_sensitive[j] ){ + if( !flu_p[j]->storeState(data) ) return false; + } + } + } + } + return true; } bool EGS_VolumetricFluence::setState(istream &data){ + if( !egsGetI64(data,current_ncase) ) return false; + if (!data.good()) return false; - if( flu ) { + +#ifdef DEBUG + if ( scoring_charge ){ + if( !relStepDiff->setState(data) ) return false; + if( !stepDist->setState(data) ) return false; + } +#endif + + if( !fluT->setState(data) ) return false; + if( score_spe ) { for(int j=0; jsetState(data) ) return false; } } - if( !fluT->setState(data) ) return false; } + + if ( score_primaries ){ + if( !fluT_p->setState(data) ) return false; + if( score_spe ) { + for(int j=0; j < nreg; j++) { + if ( is_sensitive[j] ){ + if( !flu_p[j]->setState(data) ) return false; + } + } + } + } + return true; } bool EGS_VolumetricFluence::addState(istream &data){ EGS_I64 tmp_case; if( !egsGetI64(data,tmp_case) ) return false; current_ncase += tmp_case; + if (!data.good()) return false; + +#ifdef DEBUG + if ( scoring_charge ){ + EGS_ScoringArray tmpRelStepDiff(1); + if( !tmpRelStepDiff.setState(data) ) return false; + (*relStepDiff) += tmpRelStepDiff; + } +#endif /* fluence objects */ - if( flu ) { + + EGS_ScoringArray tgT(nreg); + if( !tgT.setState(data) ) return false; + (*fluT) += tgT; + + if( score_spe ) { EGS_ScoringArray tg(flu_nbin); for(int j = 0; j < nreg; j++) { if ( is_sensitive[j] ){ @@ -1312,9 +1515,21 @@ bool EGS_VolumetricFluence::addState(istream &data){ (*flu[j]) += tg; } } - EGS_ScoringArray tgT(nreg); - if( !tgT.setState(data) ) return false; - (*fluT) += tgT; + } + + if ( score_primaries ){ + EGS_ScoringArray tgT_p(nreg); + if( !tgT_p.setState(data) ) return false; + (*fluT_p) += tgT_p; + if( score_spe ) { + EGS_ScoringArray tg_p(flu_nbin); + for(int j=0; j < nreg; j++) { + if ( is_sensitive[j] ){ + if( !tg_p.setState(data) ) return false; + (*flu_p[j]) += tg_p; + } + } + } } return true; diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index c9ff1aa5c..6dcecbee2 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -133,13 +133,15 @@ A basic fluence scoring ausgab object is specified via scoring particle = a_particle # photon, electron, or positron # Define scoring (volumetric) or contributing (planar) regions scoring regions = ir1 ir2 ... irn - ## Alternatively: + ### Alternatively: # start region = iri_1, iri_2, ..., iri_n # stop region = irf_1, irf_2, ..., irf_n + ### # score primaries = yes # or no, optional + ### # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- brems targets or multi-particle sources - ## scoring grid + # source particle = photon, electron, or positron #<- multi-particle sources or brems targets + ### scoring grid number of bins = 100 minimum kinetic energy = 0.001 maximum kinetic energy = 1.000 @@ -171,6 +173,14 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { void describeMe(); + int getDigits(int i) { + int imax = 10; + while (i>=imax) { + imax*=10; + } + return (int)log10((float)imax); + }; + void flagSecondaries( const int &iarg, const int &q ){ int npold = app->getNpOld(), np = app->getNp(); @@ -275,6 +285,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { string particle_name; /* Auxiliary input variables*/ bool verbose, + score_spe, score_primaries; }; @@ -307,15 +318,16 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { # Scoring field for planar scoring scoring circle = 0 0 5.0 5.0 scoring plane normal = 0 0 1 - ## Define contributing regions + ### Define contributing regions # scoring regions = ir1 ir2 ... irn # optional, defaults to ALL - # Alternatively: + ### Alternatively: # start region = iri_1, iri_2, ..., iri_n # stop region = irf_1, irf_2, ..., irf_n + ### # score primaries = yes # or no, optional # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- brems targets or multi-particle sources - # scoring grid + # source particle = photon, electron, or positron #<- multi-particle sources or brems targets + ### scoring grid number of bins = 100 minimum kinetic energy = 0.001 maximum kinetic energy = 1.000 @@ -365,6 +377,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { void describeMe();//!< Sets fluence scoring object \c description void initScoring(EGS_Input *inp); void setApplication(EGS_Application *App); + void ouputPlanarFluence( EGS_ScoringArray *fT, const double &norma ); void ouputResults(); void reportResults(); int processEvent(EGS_Application::AusgabCall iarg) { @@ -414,32 +427,41 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { return 0; }; + void setCurrentCase(EGS_I64 ncase) { if( ncase != current_ncase ) { current_ncase = ncase; - if (flu){ + + fluT->setHistory(ncase); + + if (score_spe){ for (int j = 0; j < Nx*Ny; j++) flu[j]->setHistory(ncase); - fluT->setHistory(ncase); } - if (flu_p){ - for (int j = 0; j < Nx*Ny; j++) - flu_p[j]->setHistory(ncase); + + if ( score_primaries ){ fluT_p->setHistory(ncase); + if (score_spe){ + for (int j = 0; j < Nx*Ny; j++) + flu_p[j]->setHistory(ncase); + } } } }; + void resetCounter(){ current_ncase = 0; + fluT->reset(); if (flu){ for (int j = 0; j < Nx*Ny; j++) flu[j]->reset(); - fluT->reset(); } - if (flu_p){ - for (int j = 0; j < Nx*Ny; j++) - flu_p[j]->reset(); - fluT_p->reset(); + if ( score_primaries ){ + fluT_p->reset(); + if ( score_spe ){ + for (int j = 0; j < Nx*Ny; j++) + flu_p[j]->reset(); + } } } bool storeState(ostream &data) const; @@ -484,18 +506,19 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { library = egs_fluence_scoring type = volumetric scoring particle = electron # or photon, or positron - # Define scoring (volumetric) or contributing (planar) regions + # Define individual scoring regions scoring regions = ir1 ir2 ... irn - # Alternatively: + ### Alternatively use groups of regions: # start region = iri_1, iri_2, ..., iri_n # stop region = irf_1, irf_2, ..., irf_n + ### volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number # of entries as group of regions, assumes groups of # equal volume regions. If only one entry, assumes # equal volumes in all regions. Defaults to 1. # score primaries = yes # or no, optional # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- brems targets or multi-particle sources + # source particle = photon, electron, or positron #<- multi-particle sources or brems targets # scoring grid number of bins = 100 minimum kinetic energy = 0.001 @@ -553,6 +576,8 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori void setApplication(EGS_Application *App); + void ouputVolumetricFluence( EGS_ScoringArray *fT, const double &norma ); + void ouputResults(); void reportResults(); @@ -568,24 +593,30 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if ( !q ){// It's a photon /* Score photon fluence */ if (iarg == EGS_Application::BeforeTransport ) { - /* Track-Length scoring (classic) */ - EGS_Float e = app->top_p.E, - wtstep = app->top_p.wt*app->getTVSTEP(); - if (flu_s) { - e = log(e); + /* Linear track-Length scoring */ + EGS_Float wtstep = app->top_p.wt*app->getTVSTEP(); + /* Score total fluence */ + fluT->score(ir,wtstep); + if (score_primaries && !latch){ + fluT_p->score(ir,wtstep); } - EGS_Float ae; - int je; - if (e > flu_xmin && e <= flu_xmax) { - ae = flu_a*e + flu_b; - je = min((int)ae,flu_nbin-1); - EGS_ScoringArray *aux = flu[ir]; - aux->score(je,wtstep); - fluT->score(ir,wtstep); - if (score_primaries && !latch){ - flu_p[ir]->score(je,wtstep); - fluT_p->score(ir,wtstep); - } + /* Score differential fluence */ + if ( score_spe ) { + EGS_Float e = app->top_p.E; + if (flu_s) { + e = log(e); + } + EGS_Float ae; int je; + /* Score differential fluence */ + if ( e > flu_xmin && e <= flu_xmax ) { + ae = flu_a*e + flu_b; + je = min((int)ae,flu_nbin-1); + EGS_ScoringArray *aux = flu[ir]; + aux->score(je,wtstep); + if (score_primaries && !latch){ + flu_p[ir]->score(je,wtstep); + } + } } } } @@ -601,250 +632,364 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori /**************************/ /***** Initialization *****/ /**************************/ - - EGS_Float Eb = app->top_p.E - app->getRM(), - Ee = Eb - edep, - weight = app->top_p.wt; - - EGS_Float xb, xe; - if( flu_s ) { - xb = log(Eb); - if( Ee > 0 ) - xe = log(Ee); - else xe = -15; - } - else{ - xb = Eb; xe = Ee; + EGS_Float weight = app->top_p.wt; + bool score_p = score_primaries && !latch; + /* Integral fluence scoring arrays */ + EGS_ScoringArray *auxT = fluT, *auxT_p; + if ( score_p ){ + auxT_p = fluT_p; } - /**********************************************************/ - /* If not out of bounds, proceed with rest of calculation */ - /**********************************************************/ - if( xb > flu_xmin && xe < flu_xmax ) { - EGS_Float ab, ae; int jb, je; - /* Fraction of the initial bin covered */ - if( xb < flu_xmax ) { - ab = flu_a*xb + flu_b; jb = (int) ab; - /* Variable bin-width for log scale*/ - if (flu_s){ - ab = (Eb*a_const[jb]-1)*r_const; - } - else{ ab -= jb;}// particle's energy above Emax - } - else { xb = flu_xmax; ab = 1; jb = flu_nbin - 1; } - /* Fraction of the final bin covered */ - if( xe > flu_xmin ) { - ae = flu_a*xe + flu_b; je = (int) ae; - /* Variable bin-width for log scale*/ - if (flu_s){ - ae = (Ee*a_const[je]-1)*r_const; - } - else{ ae -= je; } +#ifdef USETVSTEP + EGS_Float a_step = weight*app->getTVSTEP(); + auxT->score(ir,a_step); + if (score_p) + auxT_p->score(ir,a_step); + if ( score_spe ){ +#endif + + EGS_ScoringArray *aux, *aux_p; +#ifndef USETVSTEP + if ( score_spe ){ +#endif + aux = flu[ir]; + if ( score_p ){ + aux_p = flu_p[ir]; + } +#ifndef USETVSTEP } - else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin - -#ifdef DEBUG - //egsInformation("iarg = %d jb = %d je = %d edep = %g MeV weight = %g\n",iarg,jb,je,edep,weight); - if (jb == je) - one_bin++; - else{ - multi_bin++; - //egsInformation("\niarg = %d jb = %d je = %d edep = %g MeV ab = %g ae = %g",iarg,jb,je,edep,ab,ae); +#endif + + EGS_Float Eb = app->top_p.E - app->getRM(), + Ee = Eb - edep; + + EGS_Float xb, xe; + if( flu_s ) { + xb = log(Eb); + if( Ee > 0 ) + xe = log(Ee); + else xe = -15; } - binDist->score(jb-je,weight); -#endif - EGS_ScoringArray *aux = flu[ir]; - EGS_ScoringArray *auxT = fluT; - EGS_ScoringArray *aux_p, *auxT_p; - if ( score_primaries ){ - aux_p = flu_p[ir]; - auxT_p = fluT_p; + else{ + xb = Eb; xe = Ee; } - bool score_p = score_primaries && !latch; - - /************************************************ - * Approach A: - * ----------- - * Uses either an O(3) or O(5) series expansion of the - * integral of the inverse of the stopping power with - * respect to energy. The stopping power is represented - * as a linear interpolation over a log energy grid. It - * accounts for stopping power variation along the particle's - * step within the resolution of the scoring array. This - * is more accurate than the method used in FLURZnrc albeit - * about 10% slower in electron beam cases. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - * - ************************************************/ - if (flu_stpwr){ - int imed = app->getMedium(ir); - // Initial and final energies in same bin - EGS_Float step; - if( jb == je ){ - step = weight*(ab-ae)*getStepPerFraction(imed,xb,xe); - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } - } - else { - // First bin - Ee = flu_xmin + jb*flu_a_i; Eb=xb; - step = weight*ab*getStepPerFraction(imed,Eb,Ee); - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } - // Last bin - Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; - step = weight*(1-ae)*getStepPerFraction(imed,Eb,Ee); - aux->score(je,step); - if (score_p) - aux_p->score(je,step); - if (flu_s){ - auxT->score(ir,step*DE[je]); - if (score_p) - auxT_p->score(ir,step*DE[je]); + /**********************************************************/ + /* If not out of bounds, proceed with rest of calculation */ + /**********************************************************/ + if( xb > flu_xmin && xe < flu_xmax ) { + EGS_Float ab, ae; int jb, je; + /* Fraction of the initial bin covered */ + if( xb < flu_xmax ) { + ab = flu_a*xb + flu_b; jb = (int) ab; + /* Variable bin-width for log scale*/ + if (flu_s){ + ab = (Eb*a_const[jb]-1)*r_const; + } + else{ ab -= jb;} + } + else { // particle's energy above Emax + xb = flu_xmax; ab = 1; jb = flu_nbin - 1; + } + /* Fraction of the final bin covered */ + if( xe > flu_xmin ) { + ae = flu_a*xe + flu_b; je = (int) ae; + /* Variable bin-width for log scale*/ + if (flu_s){ + ae = (Ee*a_const[je]-1)*r_const; + } + else{ ae -= je; } + } + else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin + +#ifdef DEBUG + if (jb == je) + one_bin++; + else{ + multi_bin++; + } + + binDist->score(jb-je,weight); + + EGS_Float totStep = 0, the_step = app->getTVSTEP(); +#endif + + /************************************************ + * Approach A: + * ----------- + * Uses either an O(3) or O(5) series expansion of the + * integral of the inverse of the stopping power with + * respect to energy. The stopping power is represented + * as a linear interpolation over a log energy grid. It + * accounts for stopping power variation along the particle's + * step within the resolution of the scoring array. This + * is more accurate than the method used in FLURZnrc albeit + * about 10% slower in electron beam cases. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + * + ************************************************/ + if ( flu_stpwr ){ + int imed = app->getMedium(ir); + // Initial and final energies in same bin + EGS_Float step; + if( jb == je ){ + step = weight*(ab-ae)*getStepPerEnergyLoss(imed,xb,xe); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(jb,step); + if (score_p) + aux_p->score(jb,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[jb]); + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); + else { + + // First bin + + Ee = flu_xmin + jb*flu_a_i; Eb=xb; + step = weight*ab*getStepPerEnergyLoss(imed,Eb,Ee); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(jb,step); + if (score_p) + aux_p->score(jb,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[jb]); + if (score_p) + auxT_p->score(ir,step*DE[jb]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + + // Last bin + + Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; + step = weight*(1-ae)*getStepPerEnergyLoss(imed,Eb,Ee); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(je,step); + if (score_p) + aux_p->score(je,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[je]); + if (score_p) + auxT_p->score(ir,step*DE[je]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + + // intermediate bins + + for(int j=je+1; jscore(j,step); + if (score_p) + aux_p->score(j,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[j]); + if (score_p) + auxT_p->score(ir,step*DE[j]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + } } - // intermediate bins - for(int j=je+1; jscore(j,step); - if (score_p) - aux_p->score(j,step); - if (flu_s){ - auxT->score(ir,step*DE[j]); - if (score_p) - auxT_p->score(ir,step*DE[j]); - } - else{ - auxT->score(ir,step); + } + /*************************************************** + * ----------------------- + * Approach B (FLURZnrc): + * ---------------------- + * Path length at each energy interval from energy + * deposited edep and total particle step tvstep. It + * assumes stopping power constancy along the particle's + * step. It might introduce artifacts if ESTEPE or the + * scoring bin width are too large. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + **************************************************/ + else{ + EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; + // Initial and final energies in same bin + if( jb == je ){ + step = wtstep*(ab-ae); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(jb,step); if (score_p) - auxT_p->score(ir,step); - } - } - } - } - /*************************************************** - * ----------------------- - * Approach B (FLURZnrc): - * ---------------------- - * Path length at each energy interval from energy - * deposited edep and total particle step tvstep. It - * assumes stopping power constancy along the particle's - * step. It might introduce artifacts if ESTEPE or the - * scoring bin width are too large. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - **************************************************/ - else{ - EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; - // Initial and final energies in same bin - if( jb == je ){ - step = wtstep*(ab-ae); - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } - } - else { - // First bin - step = wtstep*ab; - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } - // Last bin - step = wtstep*(1-ae); - aux->score(je,step); - if (score_p) - aux_p->score(je,step); - if (flu_s){ - auxT->score(ir,step*DE[je]); - if (score_p) - auxT_p->score(ir,step*DE[je]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } - // intermediate bins - for(int j=je+1; jscore(j,wtstep); + aux_p->score(jb,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[jb]); if (score_p) - aux_p->score(j,wtstep); + auxT_p->score(ir,step*DE[jb]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + } + else { + + // First bin + + step = wtstep*ab; +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(jb,step); + if (score_p) + aux_p->score(jb,step); + } +#ifndef USETVSTEP + /* Integral fluence */ if (flu_s){ - auxT->score(ir,wtstep*DE[j]); + auxT->score(ir,step*DE[jb]); if (score_p) - auxT_p->score(ir,wtstep*DE[j]); + auxT_p->score(ir,step*DE[jb]); } else{ - auxT->score(ir,wtstep); + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + + // Last bin + + step = wtstep*(1-ae); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if ( score_spe ){ + aux->score(je,step); + if (score_p) + aux_p->score(je,step); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,step*DE[je]); if (score_p) - auxT_p->score(ir,wtstep); + auxT_p->score(ir,step*DE[je]); + } + else{ + auxT->score(ir,step); + if (score_p) + auxT_p->score(ir,step); + } +#endif + // intermediate bins + for(int j=je+1; jscore(j,wtstep); + if (score_p) + aux_p->score(j,wtstep); + } +#ifndef USETVSTEP + /* Integral fluence */ + if (flu_s){ + auxT->score(ir,wtstep*DE[j]); + if (score_p) + auxT_p->score(ir,wtstep*DE[j]); + } + else{ + auxT->score(ir,wtstep); + if (score_p) + auxT_p->score(ir,wtstep); + } +#endif } } - } + } + +#ifdef DEBUG + EGS_Float edep_step = totStep*flu_a_i, diff = edep_step/the_step; + EGS_Float astep = step_a*the_step + step_b; int jstep = (int) astep; + if (jstep < 0 || jstep > n_step_bins) + egsFatal("\n**** EGS_VolumetricFluence::processEvent-> jstep = %d\n is out of bound!\n"); + stepDist->score(jstep,app->top_p.wt); + relStepDiff->score(jstep,diff); eCases++; +#endif } +#ifdef USETVSTEP } +#endif } } } @@ -859,22 +1004,20 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori * Other applications might use latch for other purposes! *********************************************************************/ if ( score_primaries && ir >= 0 && !is_source[ir]){ - flagSecondaries( iarg, q ); - } return 0; }; - /*! Computes path per energy bin-width traveled by a charged particle when + /*! Computes path per energy loss traveled by a charged particle when * slowing down from Eb to Ee. * * Computes the path-length traveled while slowing down from energy Eb to energy * Ee, both energies falling in the same energy bin assuming full coverage. - * The returned value should be multiplied by the actual fraction of the - * energy bin covered by Eb-Ee. + * The returned value should be multiplied by the fraction of the + * energy bin covered by Eb-Ee to compute fluence per bin width. * If using a logarithmic energy interpolation, Eb and Ee are actually the * logarithms of the initial and final energies. The expression is based on * linear interpolation in a logarithmic energy grid as used in EGSnrc @@ -882,7 +1025,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori * function that is the result of the integration of the inverse of the stopping * power with respect to energy. */ - EGS_Float getStepPerFraction( const int & imed, + EGS_Float getStepPerEnergyLoss( const int & imed, const EGS_Float & Eb, const EGS_Float & Ee){ EGS_Float stpFrac, eps, lnEmid; @@ -933,42 +1076,55 @@ else{ void setCurrentCase(EGS_I64 ncase) { if( ncase != current_ncase ) { current_ncase = ncase; - if (flu){ + fluT->setHistory(ncase); + if ( score_primaries ) + fluT_p->setHistory(ncase); + if (score_spe){ for (int j = 0; j < nreg; j++){ if ( is_sensitive[j] ) flu[j]->setHistory(ncase); } - fluT->setHistory(ncase); - } - if (flu_p){ - for (int j = 0; j < nreg; j++){ - if ( is_sensitive[j] ) - flu_p[j]->setHistory(ncase); + if ( score_primaries ){ + for (int j = 0; j < nreg; j++){ + if ( is_sensitive[j] ) + flu_p[j]->setHistory(ncase); + } } - fluT_p->setHistory(ncase); } } #ifdef DEBUG binDist->setHistory(ncase); + if ( scoring_charge ){ + stepDist->setHistory(ncase); + relStepDiff->setHistory(ncase); + } #endif }; void resetCounter(){ current_ncase = 0; - if (flu){ - for (int j = 0; j < nreg; j++) + fluT->reset(); + if ( score_primaries ) + fluT_p->reset(); + if ( score_spe ){ + for (int j = 0; j < nreg; j++){ if ( is_sensitive[j] ) flu[j]->reset(); - fluT->reset(); - } - if (flu_p){ - for (int j = 0; j < nreg; j++) - if ( is_sensitive[j] ) - flu_p[j]->reset(); - fluT_p->reset(); + } + + if ( score_primaries ){ + for (int j = 0; j < nreg; j++){ + if ( is_sensitive[j] ) + flu_p[j]->reset(); + } + } } #ifdef DEBUG binDist->reset(); + if ( scoring_charge ){ + stepDist->reset(); + relStepDiff->reset(); + } #endif } @@ -998,9 +1154,11 @@ else{ EGS_Float *DE; // bin width of logarithmic scale /*****************************************************************/ + EGS_I64 eCases; + vector volume; // volume of each scoring region /* Energy grid inputs */ - EGS_Float flu_a_i; + //EGS_Float flu_a_i; /* Auxiliary input variables*/ vector vol_list; // Input list of region volumes @@ -1008,6 +1166,11 @@ else{ /* Debugging information */ int one_bin, multi_bin; EGS_ScoringArray *binDist; + EGS_ScoringArray *stepDist; + EGS_ScoringArray *relStepDiff; // Relative difference between tvstep and edep-derived step + EGS_Float max_step; + EGS_Float step_a, step_b; + EGS_I32 n_step_bins; #endif }; From f700470029c98e544c7d2f1632231fef1e893295 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Wed, 23 Mar 2022 11:42:33 -0400 Subject: [PATCH 12/18] Split fluence scoring object input in blocks - Add input blocks to define the energy grid and the scoring options based on the scoring type. - Improve output format. --- .../egs_fluence_scoring.cpp | 184 ++++++++++++------ .../egs_fluence_scoring/egs_fluence_scoring.h | 9 +- 2 files changed, 131 insertions(+), 62 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 5df67d3f5..da559993f 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -120,19 +120,23 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { score_spe = inp->getInput( "score spectrum", choice,0); if ( score_spe ){ - EGS_Float flu_Emin, flu_Emax, norma; - int err_n = inp->getInput("number of bins",flu_nbin); - int err_i = inp->getInput("minimum kinetic energy",flu_Emin); - int err_f = inp->getInput("maximum kinetic energy",flu_Emax); - if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: number of bins.\n" - " Aborting!\n\n"); - if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: minimum kinetic energy.\n" - " Aborting!\n\n"); - if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: maximum kinetic energy.\n" - " Aborting!\n\n"); + EGS_Float flu_Emin, flu_Emax; + EGS_Input *eGrid = inp->takeInputItem("energy grid"); + if( eGrid ) { + int err_n = eGrid->getInput("number of bins",flu_nbin); + int err_i = eGrid->getInput("minimum kinetic energy",flu_Emin); + int err_f = eGrid->getInput("maximum kinetic energy",flu_Emax); + if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: number of bins.\n" + " Aborting!\n\n"); + if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: minimum kinetic energy.\n" + " Aborting!\n\n"); + if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: maximum kinetic energy.\n" + " Aborting!\n\n"); + } + EGS_Float norma; int err_norm = inp->getInput("normalization",norma); if (!err_norm) norm_u = norma; else norm_u = 1.0; @@ -192,8 +196,27 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { } } +} + +void EGS_FluenceScoring::getSensitiveRegions(EGS_Input *inp){ + + string listLabel, startLabel, stopLabel; + + if ( otype == "EGS_PlanarFluence" ){ + listLabel = "contributing regions"; + startLabel = "start contributing region"; + stopLabel = "stop contributing region"; + } + else if (otype == "EGS_VolumetricFluence"){ + listLabel = "scoring regions"; + startLabel = "start region"; + stopLabel = "stop region"; + } + else + egsFatal("\n*** Unknown fluence scoriung type! Aborting!\n\n"); + /* get scoring regions */ - if (!inp->getInput("scoring regions",f_regionsString) && f_regionsString.length()>0) { + if (!inp->getInput(listLabel,f_regionsString) && f_regionsString.length()>0) { // Individual regions if ( f_regionsString == "ALL") score_in_all_regions = true; @@ -201,8 +224,8 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { score_in_all_regions = false; } else {// Groups of regions - int err1 = inp->getInput("start region",f_start); - int err2 = inp->getInput("stop region",f_stop); + int err1 = inp->getInput(startLabel,f_start); + int err2 = inp->getInput(stopLabel, f_stop); if (!err1 && !err2) { if ( f_start.size() == f_stop.size() ){ // group of dose regions for ( int i=0; i ®s ){ if ( !app ) egsFatal("EGS_FluenceScoring::getNumberRegions\n" @@ -432,20 +455,30 @@ void EGS_PlanarFluence::setApplication(EGS_Application *App) { void EGS_PlanarFluence::initScoring(EGS_Input *inp) { + EGS_Input *pScoringInput; + if( !inp ) { - egsWarning("AO type %s: null input?\n",otype.c_str()); return; + egsFatal("AO type %s: null input?\n",otype.c_str()); return; + } + else{ + pScoringInput = inp->takeInputItem("planar scoring"); + if( ! pScoringInput ){ + egsFatal("AO type %s: Missing planar scoring input?\n",otype.c_str()); + return; + } } + EGS_FluenceScoring::initScoring(inp);// common inputs + /* Planar scoring by default from all regions */ score_in_all_regions = true; - - EGS_FluenceScoring::initScoring(inp); + EGS_FluenceScoring::getSensitiveRegions(pScoringInput); // Specific plane input vector tmp_field; - int err2 = inp->getInput("scoring circle",tmp_field); + int err2 = pScoringInput->getInput("scoring circle",tmp_field); if( err2 ) { - int err3 = inp->getInput("scoring rectangle",tmp_field); + int err3 = pScoringInput->getInput("scoring rectangle",tmp_field); if( err3 || tmp_field.size() != 4) { egsWarning( "\n\n*** Wrong/missing 'scoring rectangle' input " @@ -473,7 +506,7 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { /* get screen resolution */ vector screen; - int err4 = inp->getInput("resolution",screen); + int err4 = pScoringInput->getInput("resolution",screen); if( err4 ) { Nx=1; Ny=1; egsWarning( @@ -506,7 +539,7 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { } else{ vector tmp_normal; - int err1 = inp->getInput("scoring plane normal",tmp_normal); + int err1 = pScoringInput->getInput("scoring plane normal",tmp_normal); if( err1 || tmp_normal.size() != 3 ) { egsWarning( "\n\n*** Wrong/missing 'scoring plane normal' input. " @@ -642,17 +675,32 @@ void EGS_PlanarFluence::ouputPlanarFluence( EGS_ScoringArray *fT, const double & int iy_digits = getDigits(Ny); int xy_digits = getDigits(Nx*Ny); - egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "-----------------------------------------------------\n", - iy_digits,"iy",ix_digits,"ix",&count); - for(int j=0; jcurrentResult(k,fe,dfe); - if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; - egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); - } + if (field_type == circle){ + egsInformation("\n pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n"); + } + else{ + egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n", + iy_digits,"iy",ix_digits,"ix",&count); + } + if (field_type == circle){ + int k = 0; + egsInformation(" %*d ",xy_digits,k); + fT->currentResult(k,fe,dfe); + if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; + egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); + } + else{ + for(int j=0; jcurrentResult(k,fe,dfe); + if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; + egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); + } + } } } @@ -691,13 +739,14 @@ void EGS_PlanarFluence::ouputResults(){ //egsInformation(" Normalization = %g\n",norm); - egsInformation("\n\n %s fluence\n" - " ==============\n", particle_name.c_str()); + egsInformation( "\n\n Integral fluence\n" + " ================\n\n"); + + egsInformation("\n\n Total %s fluence\n", particle_name.c_str()); ouputPlanarFluence( fluT, norm ); if ( score_primaries ){ - egsInformation("\n\n primary fluence\n" - " ==============\n"); + egsInformation("\n\n Primary fluence\n"); ouputPlanarFluence( fluT_p, norm ); } @@ -731,19 +780,25 @@ void EGS_PlanarFluence::ouputResults(){ spe_output << "@ subtitle font 4\n"; spe_output << "@ subtitle size 1.000000\n"; + egsInformation( "\n\n Differential fluence\n" + " ====================\n\n"); + int i_graph = 0; double fe,dfe; for(int j=0; jcurrentResult(l,fe,dfe); EGS_Float e = (l+0.5-flu_b)/flu_a; @@ -755,8 +810,11 @@ void EGS_PlanarFluence::ouputResults(){ spe_output << "&\n"; if ( score_primaries ){ - if (verbose) egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "---------------------------------------------\n"); + if (verbose) { + egsInformation( "\n\n Primary\n\n"); + egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "---------------------------------------------\n"); + } spe_output<<"@ s" << ++i_graph <<" errorbar linestyle 0\n"; spe_output<<"@ s" << i_graph <<" legend \""<< "Voxel # " << k <<" (primary)\"\n"; @@ -1115,18 +1173,28 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { */ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { + EGS_Input *vScoringInput; + if( !inp ) { - egsWarning("AO type %s: null input?\n",otype.c_str()); return; + egsFatal("AO type %s: null input?\n",otype.c_str()); return; } + else{ + vScoringInput = inp->takeInputItem("volumetric scoring"); + if( ! vScoringInput ){ + egsFatal("AO type %s: Missing volumetric scoring input?\n",otype.c_str()); + return; + } + } + + EGS_FluenceScoring::initScoring(inp);// common inputs /* Volumetric scoring by default turned off */ score_in_all_regions = false; - - EGS_FluenceScoring::initScoring(inp); + EGS_FluenceScoring::getSensitiveRegions(vScoringInput); /* get region volume[s] in g/cm3 */ vector v_in; - inp->getInput("volumes",v_in); + vScoringInput->getInput("volumes",v_in); //================================================ // Check if one volume for each group requested. @@ -1155,7 +1223,7 @@ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { vector method; method.push_back("flurz"); method.push_back("stpwr"); // 3rd order method.push_back("stpwrO5"); // 5th order - flu_stpwr = eFluType(inp->getInput("method",method,1)); + flu_stpwr = eFluType(vScoringInput->getInput("method",method,1)); } @@ -1297,13 +1365,14 @@ void EGS_VolumetricFluence::ouputResults(){ EGS_Float norm = 1.0/src_norm; // per particle or fluence norm *= norm_u; // user-requested normalization - egsInformation("\n\n %s fluence\n" - " =====================\n", particle_name.c_str()); + egsInformation("\n\n Integral fluence output\n" + " =======================\n\n"); + + egsInformation("\n\n Total %s fluence\n", particle_name.c_str()); ouputVolumetricFluence( fluT, norm ); if ( score_primaries ){ - egsInformation("\n\n primary fluence\n" - " ==============\n"); + egsInformation("\n\n Primary fluence\n"); ouputVolumetricFluence( fluT_p, norm ); } @@ -1344,8 +1413,9 @@ void EGS_VolumetricFluence::ouputResults(){ spe_output << "@ subtitle font 4\n"; spe_output << "@ subtitle size 1.000000\n"; - egsInformation("\n\n%s fluence scoring\n" - "=================================\n\n",particle_name.c_str()); + if ( verbose ) + egsInformation("\n\n Differential fluence output\n" + " =============================\n\n"); int i_graph = 0; double fe, dfe; norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! @@ -1363,7 +1433,7 @@ void EGS_VolumetricFluence::ouputResults(){ egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); } - if (verbose) egsInformation("\nTotal differential fluence:\n"); + if (verbose) egsInformation("\nTotal fluence:\n"); spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; spe_output<<"@target G0.S"<< i_graph <<"\n"; @@ -1381,7 +1451,7 @@ void EGS_VolumetricFluence::ouputResults(){ spe_output << "&\n"; if ( score_primaries ){ - if (verbose) egsInformation("\nPrimary differential fluence:\n"); + if (verbose) egsInformation("\nPrimary fluence:\n"); spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; spe_output<<"@target G0.S"<< i_graph <<"\n"; diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index 6dcecbee2..76a299b8d 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -36,8 +36,6 @@ in FLURZnrc for IPRIMARY = 2 (primaries) and IPRIMARY = 4 (secondaries). The basic definition of a fluence scoring AO is -This ausgab object is specified via - :start ausgab object: name = a_name library = egs_fluence_scoring @@ -153,7 +151,6 @@ A basic fluence scoring ausgab object is specified via \todo Total kerma scoring \todo Account for multiple app geometries \todo Fluence for any particle type? - */ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { @@ -165,6 +162,8 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { void initScoring(EGS_Input *inp); + void getSensitiveRegions(EGS_Input *inp); + void getNumberRegions( const string &str, vector ®s ); void getLabelRegions( const string &str, vector ®s ); @@ -194,8 +193,8 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { OPTIONAL: FLURZnrc IPRIMARY = 1 (e- from brems as primaries) - Set source_particle to 'photon' in the input file to not flag - brems photons so that when scoring charged partcle fluence in + Set `source particle` to 'photon' in the input file to not flag + brems photons so that when scoring charged particle fluence in photon beams, first generation e- are primaries. This is implicit for photon interactions when scoring charged particle fluence. But one must explicitly account for that during brems events. From fd63546fc369c0d501d0972f6fadcbb4bdb95a71 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Tue, 26 Jul 2022 16:41:35 -0400 Subject: [PATCH 13/18] Improve fluence scoring object documentation Remove example for base class and add specific blocks for planar an volumetric scoring inputs. --- .../egs_fluence_scoring/egs_fluence_scoring.h | 290 +++++++++--------- 1 file changed, 150 insertions(+), 140 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index 76a299b8d..ddc8233b0 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -26,45 +26,15 @@ # Contributors: # ############################################################################### - -Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at -circular or rectangular scoring fields located anywhere in space for a specific -particle type. Fluence can be scored for multiple particle types by definining -different AOs. An option exists for scoring the fluence of primary particles. -Classification into primary and secondary particles follows the definition used -in FLURZnrc for IPRIMARY = 2 (primaries) and IPRIMARY = 4 (secondaries). - -The basic definition of a fluence scoring AO is - - :start ausgab object: - name = a_name - library = egs_fluence_scoring - type = planar # planar or volumetric - scoring particle = electron # or photon, or positron - # Define scoring (volumetric) or contributing (planar) regions - scoring regions = ir1 ir2 ... irn - # Alternatively: - # start region = iri_1, iri_2, ..., iri_n - # stop region = irf_1, irf_2, ..., irf_n - volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number - # of entries as group of regions, assumes groups of - # equal volume regions. If only one entry, assumes - # equal volumes in all regions. Defaults to 1. - # score primaries = yes # or no, optional - # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- brems targets or multi-particle sources - # scoring grid - number of bins = 100 - minimum kinetic energy = 0.001 - maximum kinetic energy = 1.000 - normalization = 1 # user-normalization optional - scale = linear # or logarithmic - # Scoring field for planar scoring - scoring circle = 0 0 5.0 5.0 - scoring plane normal = 0 0 1 - verbose = yes # optional - :stop ausgab object: - +# +# Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at +# circular or rectangular scoring fields located anywhere in space for a specific +# particle type. Fluence can be scored for multiple particle types by definining +# different AOs. An option exists for scoring the fluence of primary particles. +# Classification into primary and secondary particles follows the definition used +# in FLURZnrc for IPRIMARY = 2 (primaries) and IPRIMARY = 4 (secondaries). +# +############################################################################### */ /*! \file egs_fluence_scoring.h @@ -114,7 +84,7 @@ enum ParticleType { electron = -1, photon = 0, positron = 1, unknown = -99 }; /*! Charged particle fluence calculation type */ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; -/*! \brief Base class for fluence scoring ausgab objects +/*! \brief Base class for fluence scoring. \ingroup AusgabObjects @@ -122,32 +92,6 @@ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; particle type, scoring regions, and whether to score primary fluence. Provides the method for defining primary and secondary particles. -A basic fluence scoring ausgab object is specified via -\verbatim -:start ausgab object: - name = a_name - library = egs_fluence_scoring - type = a_type # planar or volumetric - scoring particle = a_particle # photon, electron, or positron - # Define scoring (volumetric) or contributing (planar) regions - scoring regions = ir1 ir2 ... irn - ### Alternatively: - # start region = iri_1, iri_2, ..., iri_n - # stop region = irf_1, irf_2, ..., irf_n - ### - # score primaries = yes # or no, optional - ### - # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- multi-particle sources or brems targets - ### scoring grid - number of bins = 100 - minimum kinetic energy = 0.001 - maximum kinetic energy = 1.000 - # normalization = 1 # user-normalization optional, defaults to 1 - scale = a_scale # linear or logarithmic - verbose = yes # or no, optional -:stop ausgab object: -\endverbatim \todo Total kerma scoring \todo Account for multiple app geometries \todo Fluence for any particle type? @@ -292,53 +236,80 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { \ingroup AusgabObjects - A linear track-length estimator in the zero thickness limit is used to compute - fluence and fluence-related quantities such as total kerma and cema if the user - requestes it. Differencial and total fluence is estimated for a specific particle - type. If fluence for more than one particle type is desired, multiple AOs are - required. For charged particle fluence scoring this method can be inefficient as - it checks at every single step whether a charged particle is aimed at scoring field. + A linear track-length estimator in the zero-thickness limit is used to + compute fluence for either a circular field or a rectangular screen of + arbitrary resolution (pixels). Rectangular fields are by default at Z = 0 + directed along the positive z-axis. An affine transofrmation can be included + to place a rectangular field anywhere in space. User can request to score primary + fluence as well as differential fluence. If fluence for more than one particle type + is desired, multiple AOs are required. - To improve the efficieny for charged particles, one has the option to define regions + For charged particle fluence scoring this method can be inefficient as + it checks at every single step whether a charged particle is aimed at scoring field. + To improve the efficieny for charged particles, one has the option to define contributing regions from where to score. However, users must be careful to select all regions from where charged particles can cross the scoring field. This option has the added benefit of allowing to estimate the contribution to the fluence from specific regions in the geometry. - Scoring field can be either a circular field or a rectangular screen of arbitrary - resolution (pixels). For rectangular fields, fluence is computed at each screen element (pixel) - To define an EGS_PlanarFluence AO use the syntax below: \verbatim - :start ausgab object: - name = a_name - library = egs_fluence_scoring - type = volumetric - scoring particle = electron # or photon, or positron - # Scoring field for planar scoring - scoring circle = 0 0 5.0 5.0 - scoring plane normal = 0 0 1 - ### Define contributing regions - # scoring regions = ir1 ir2 ... irn # optional, defaults to ALL - ### Alternatively: - # start region = iri_1, iri_2, ..., iri_n - # stop region = irf_1, irf_2, ..., irf_n - ### - # score primaries = yes # or no, optional - # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- multi-particle sources or brems targets - ### scoring grid - number of bins = 100 - minimum kinetic energy = 0.001 - maximum kinetic energy = 1.000 - normalization = 1 # user-normalization optional - scale = linear # or logarithmic - verbose = yes # optional - :stop ausgab object: +:start ausgab object: + name = id-string # Arbitrary identifying string + library = egs_fluence_scoring # Library name + type = planar # Score on circular or square field + scoring particle = photon, or electron, or positron + source particle = photon, or electron, or positron + # Optional. Only required to score primary fluence. + # Defaults to source particles if all the same. + # In the case of multiple particles, + # defaults to scoring particle. Useful for + # bremsstrahlung targets and radioactive sources. + score primaries = yes or no # Defaults to `no`. + score spectrum = yes or no # Defaults to `no`. + verbose = yes or no # Defaults to `no`. + normalization = norm # User-requested normalization. Defaults to 1. + ######### + # If scoring spectrum, define energy grid + # Default: 128 linear energy bins between 1 keV and 1 MeV + ######### + :start energy grid: + number of bins = nbins + minimum kinetic energy = Emin + maximum kinetic energy = Emax + scale = linear or logarithmic # Defaults to `linear`. + :stop energy grid: + ######## + # Define scoring based on type + ######## + :start planar scoring: + # Define contributing regions + contributing regions = ir1 ir2 ... irn + ### Alternatively: + # start contributing region = iri_1, iri_2, ..., iri_n + # stop contributing region = irf_1, irf_2, ..., irf_n + ### + ################################ + # If a circular field desired: + ################################ + scoring circle = x y z R + scoring plane normal = ux uy uz + ######################################################## + # If a rectangular field desired: + # + #scoring rectangle = xmin xmax ymin ymax + ##### + # See documentation for EGS_AffineTransform + ##### + #:start transformation: + # rotation = 2, 3 or 9 floating point numbers + # translation = tx, ty, tz + #:stop transformation: + ########################################################## + :stop planar scoring: +:stop ausgab object: \endverbatim \todo Store results in a 2D binary file for visualization - \todo Add option for (total) kerma scoring - \todo Add option for CEMA scoring ??? */ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { @@ -417,12 +388,9 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { * Other applications might use latch for other purposes! *********************************************************************/ if ( score_primaries && ir >= 0 && !is_source[ir]){ - flagSecondaries( iarg, q ); - } - return 0; }; @@ -493,44 +461,86 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { \ingroup AusgabObjects - A linear track-lenght estimator is used to compute fluence and fluence-related - quantities such as total kerma and cema if the user requestes it. Differencial - and total fluence is estimated for a specific particle type. If fluence for more - than one particle type is desired, multiple AOs are required. + A linear track-length estimator is used to compute fluence in specific + geometrical regions. User can request to score primary fluence as well + as differential fluence. If fluence for more than one particle type is + desired, multiple AOs are required. + + Differential fluence for charged particle is calculated accounting for + continuos energy losses along the path using two methods. One method follows + the FLURZnrc implementation, whereby stopping power is assumed constant along + the step and estimated as the ratio EDEP/TVSTEP. The contributions to each + energy bin of width \f$\Delta E_i\f$ is obtained as the energy fraction + TVSTEP*\f$\Delta E_i\f$/EDEP. + + The second method follows the approach currently used in the EGSnrc + application \ref cavity "cavity", which accounts for stopping power + changes within a scoring energy bin. It uses a series expansion of the + integral of the inverse of the stopping power with respect to energy. + Stopping power is represented as a linear interpolation over a log energy grid. + A technical note is in preparation providing more details about this implementation. To define an EGS_VolumetricFluence AO use the syntax below: \verbatim - :start ausgab object: - name = a_name - library = egs_fluence_scoring - type = volumetric - scoring particle = electron # or photon, or positron - # Define individual scoring regions - scoring regions = ir1 ir2 ... irn - ### Alternatively use groups of regions: - # start region = iri_1, iri_2, ..., iri_n - # stop region = irf_1, irf_2, ..., irf_n - ### - volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number - # of entries as group of regions, assumes groups of - # equal volume regions. If only one entry, assumes - # equal volumes in all regions. Defaults to 1. - # score primaries = yes # or no, optional - # source regions = sr1 sr2 ... srN # <- no classification in these regions - # source particle = photon, electron, or positron #<- multi-particle sources or brems targets - # scoring grid - number of bins = 100 - minimum kinetic energy = 0.001 - maximum kinetic energy = 1.000 - normalization = 1 # user-normalization optional - scale = linear # or logarithmic - verbose = yes # optional - :stop ausgab object: +:start ausgab object: + name = id-string # Arbitrary identifying string + library = egs_fluence_scoring # Library name + type = volumetric # Score in a volume + scoring particle = photon, or electron, or positron + source particle = photon, or electron, or positron + # Optional. Only required to score primary fluence. + # Defaults to source particles if all the same. + # In the case of multiple particles, + # defaults to scoring particle. Useful for + # bremsstrahlung targets and radioactive sources. + score primaries = yes or no # Defaults to `no`. + score spectrum = yes or no # Defaults to `no`. + verbose = yes or no # Defaults to `no`. + normalization = norm # User-requested normalization. Defaults to 1. + # If scoring spectrum, define energy grid + # Default: 128 linear energy bins between 1 keV and 1 MeV + :start energy grid: + number of bins = nbins + minimum kinetic energy = Emin + maximum kinetic energy = Emax + scale = linear or logarithmic # Defaults to `linear`. + :stop energy grid: + :start volumetric scoring: + scoring regions = ir1 ir2 ... irn + ### Alternatively: + #start region = iri_1, iri_2, ..., iri_n + #stop region = irf_1, irf_2, ..., irf_n + ### + volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number + # of entries as group of regions, assumes groups of + # equal volume regions. If only one entry, assumes + # equal volumes in all regions. Defaults to 1. + method = flurz or stpwr or stpwrO5 # For charged particle scoring. + # + # flurz => FLURZnrc algorithm + # + # Path length at each energy interval from energy + # deposited EDEP and total particle step TVSTEP. + # Assumes stopping power constancy along the particle's + # step. It might introduce artifacts if ESTEPE or the + # scoring bin width are too large. + # + # stpwr => Accounts for stopping power variation + # along the particle's step. More accurate + # than method used in FLURZnrc albeit about + # about 10% slower in electron beam cases. + # + # Uses an O(3) series expansion of the integral of the + # inverse of the stopping power with respect to energy. + # Stopping power is represented as a linear interpolation + # over a log energy grid. + # + # stpwrO5 => Uses an O(5) series expansion. Slightly slower. + # + # Defaults to `stpwr`. + :stop volumetric scoring: +:stop ausgab object: \endverbatim - - \todo Add option for (total) kerma scoring - \todo Add option for CEMA scoring - */ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScoring { From 5c2757a738c02d3aee711af0406a42aa4fdc6a76 Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Thu, 28 Jul 2022 17:11:10 -0400 Subject: [PATCH 14/18] Score integral electron fluence using TVSTEP - Remove initial approach to score charged particle integral fluence from each contribution to the differential fluence. Now is uses TVSTEP and computes integral fluence in one go. - Remove checks for pre-processor directive USETVSTEP as it not needed anymore. - Polish documentation. --- .../egs_fluence_scoring.cpp | 3 - .../egs_fluence_scoring/egs_fluence_scoring.h | 153 +++--------------- 2 files changed, 20 insertions(+), 136 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index da559993f..76eea9338 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -1263,9 +1263,6 @@ void EGS_VolumetricFluence::describeMe(){ void EGS_VolumetricFluence::ouputVolumetricFluence( EGS_ScoringArray *fT, const double &norma ){ double fe,dfe,dfer; double norm = norma; -#ifndef USETVSTEP - norm *= flu_s? 1.0 : flu_a_i; // Implicit for log grid -#endif int count = 0; int ir_digits = getDigits(nreg); diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index ddc8233b0..d73bd5f20 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -28,7 +28,7 @@ ############################################################################### # # Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at -# circular or rectangular scoring fields located anywhere in space for a specific +# circular or rectangular scoring fields located anywhere in space for a specific # particle type. Fluence can be scored for multiple particle types by definining # different AOs. An option exists for scoring the fluence of primary particles. # Classification into primary and secondary particles follows the definition used @@ -92,7 +92,6 @@ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; particle type, scoring regions, and whether to score primary fluence. Provides the method for defining primary and secondary particles. - \todo Total kerma scoring \todo Account for multiple app geometries \todo Fluence for any particle type? */ @@ -466,20 +465,17 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { as differential fluence. If fluence for more than one particle type is desired, multiple AOs are required. - Differential fluence for charged particle is calculated accounting for - continuos energy losses along the path using two methods. One method follows - the FLURZnrc implementation, whereby stopping power is assumed constant along - the step and estimated as the ratio EDEP/TVSTEP. The contributions to each - energy bin of width \f$\Delta E_i\f$ is obtained as the energy fraction - TVSTEP*\f$\Delta E_i\f$/EDEP. - - The second method follows the approach currently used in the EGSnrc - application \ref cavity "cavity", which accounts for stopping power - changes within a scoring energy bin. It uses a series expansion of the - integral of the inverse of the stopping power with respect to energy. - Stopping power is represented as a linear interpolation over a log energy grid. - A technical note is in preparation providing more details about this implementation. - + Differential fluence for charged particles is calculated accounting for + continuos energy losses along the path. As these particles slow down in medium, + their contribution to fluence will spread over several energy bins. Two methods + to account for this are provided. One method follows the FLURZnrc implementation, + whereby stopping power is assumed constant along the step and estimated as the + ratio of the deposited energy EDEP to the total step length TVSTEP. The second + method follows the approach used in the EGSnrc application \ref cavity "cavity", + which accounts for stopping power changes within a scoring energy bin. It uses a + series expansion of the integral of the inverse of the stopping power with respect + to energy. Implementation details are provided in an upcoming publication. + To define an EGS_VolumetricFluence AO use the syntax below: \verbatim :start ausgab object: @@ -649,25 +645,22 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori auxT_p = fluT_p; } -#ifdef USETVSTEP + /***************************************/ + /* Score integral fluence using TVSTEP */ + /***************************************/ EGS_Float a_step = weight*app->getTVSTEP(); auxT->score(ir,a_step); if (score_p) auxT_p->score(ir,a_step); + /***************************************/ + if ( score_spe ){ -#endif EGS_ScoringArray *aux, *aux_p; -#ifndef USETVSTEP - if ( score_spe ){ -#endif - aux = flu[ir]; - if ( score_p ){ - aux_p = flu_p[ir]; - } -#ifndef USETVSTEP + aux = flu[ir]; + if ( score_p ){ + aux_p = flu_p[ir]; } -#endif EGS_Float Eb = app->top_p.E - app->getRM(), Ee = Eb - edep; @@ -753,19 +746,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(jb,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif } else { @@ -782,19 +762,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(jb,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif // Last bin @@ -809,19 +776,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(je,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[je]); - if (score_p) - auxT_p->score(ir,step*DE[je]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif // intermediate bins @@ -849,19 +803,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(j,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[j]); - if (score_p) - auxT_p->score(ir,step*DE[j]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif } } } @@ -892,19 +833,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(jb,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif } else { @@ -920,19 +848,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(jb,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[jb]); - if (score_p) - auxT_p->score(ir,step*DE[jb]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif // Last bin @@ -946,19 +861,6 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori if (score_p) aux_p->score(je,step); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,step*DE[je]); - if (score_p) - auxT_p->score(ir,step*DE[je]); - } - else{ - auxT->score(ir,step); - if (score_p) - auxT_p->score(ir,step); - } -#endif // intermediate bins for(int j=je+1; jscore(j,wtstep); } -#ifndef USETVSTEP - /* Integral fluence */ - if (flu_s){ - auxT->score(ir,wtstep*DE[j]); - if (score_p) - auxT_p->score(ir,wtstep*DE[j]); - } - else{ - auxT->score(ir,wtstep); - if (score_p) - auxT_p->score(ir,wtstep); - } -#endif } } } @@ -996,9 +885,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori relStepDiff->score(jstep,diff); eCases++; #endif } -#ifdef USETVSTEP } -#endif } } } From 1951f85bd0b03495f1656bb6d987eec39652ee7a Mon Sep 17 00:00:00 2001 From: Ernesto Mainegra Date: Wed, 27 Jul 2022 16:58:44 -0400 Subject: [PATCH 15/18] Link in documentation for fluence scoring object --- HEN_HOUSE/doc/src/pirs898-egs++/Doxyfile | 1 + HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy | 9 ++++++++- HEN_HOUSE/doc/src/pirs898-egs++/main.doxy | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/HEN_HOUSE/doc/src/pirs898-egs++/Doxyfile b/HEN_HOUSE/doc/src/pirs898-egs++/Doxyfile index 2b444a653..54de8568b 100644 --- a/HEN_HOUSE/doc/src/pirs898-egs++/Doxyfile +++ b/HEN_HOUSE/doc/src/pirs898-egs++/Doxyfile @@ -738,6 +738,7 @@ INPUT = main.doxy \ common.doxy \ geometry.doxy \ sources.doxy \ + aobjects.doxy \ cavity.doxy \ egs_cbct.doxy \ egs_chamber.doxy \ diff --git a/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy b/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy index 347167e1d..1311d1ff1 100644 --- a/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy +++ b/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy @@ -44,4 +44,11 @@ \section ausgab_objects_general General discussion -An ausgab object is an object that blah blah blah. +An ausgab object allows accessing the simulation at specific points in the simulation +to extract information (scoring) or modify aspects of the simulation, for instance to increase +calculation efficiency by means of variance reduction techinques. +The enumerated type EGS_Application#AusgabCall links certain events with an integer-valued constant, which is +passed to the \c ausgab scoring routine of the EGS_Application class and all defined ausgab objects +if the event flag is set to \c true. + +*/ \ No newline at end of file diff --git a/HEN_HOUSE/doc/src/pirs898-egs++/main.doxy b/HEN_HOUSE/doc/src/pirs898-egs++/main.doxy index f74fcb501..bf916cb40 100644 --- a/HEN_HOUSE/doc/src/pirs898-egs++/main.doxy +++ b/HEN_HOUSE/doc/src/pirs898-egs++/main.doxy @@ -82,6 +82,9 @@ \ref Sources "Particle sources" + + \ref AusgabObjects "Ausgab objects" + \ref egspp_main "Main library" From bd45bd78d32a713eadad07431edaa5243c208580 Mon Sep 17 00:00:00 2001 From: Frederic Tessier Date: Tue, 18 Oct 2022 14:09:56 -0400 Subject: [PATCH 16/18] Apply standard EGSnrc code formatting with astyle --- HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy | 8 +- .../ausgab_objects/egs_dose_scoring/Makefile | 2 +- .../egs_fluence_scoring.cpp | 2376 +++++++++-------- .../egs_fluence_scoring/egs_fluence_scoring.h | 1598 +++++------ HEN_HOUSE/egs++/egs_advanced_application.cpp | 29 +- HEN_HOUSE/egs++/egs_advanced_application.h | 8 +- HEN_HOUSE/egs++/egs_application.h | 10 +- HEN_HOUSE/egs++/egs_base_source.h | 2 +- HEN_HOUSE/egs++/egs_interpolator.cpp | 4 +- 9 files changed, 2155 insertions(+), 1882 deletions(-) diff --git a/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy b/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy index 1311d1ff1..25961e389 100644 --- a/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy +++ b/HEN_HOUSE/doc/src/pirs898-egs++/aobjects.doxy @@ -44,11 +44,11 @@ \section ausgab_objects_general General discussion -An ausgab object allows accessing the simulation at specific points in the simulation +An ausgab object allows accessing the simulation at specific points in the simulation to extract information (scoring) or modify aspects of the simulation, for instance to increase calculation efficiency by means of variance reduction techinques. -The enumerated type EGS_Application#AusgabCall links certain events with an integer-valued constant, which is -passed to the \c ausgab scoring routine of the EGS_Application class and all defined ausgab objects +The enumerated type EGS_Application#AusgabCall links certain events with an integer-valued constant, which is +passed to the \c ausgab scoring routine of the EGS_Application class and all defined ausgab objects if the event flag is set to \c true. -*/ \ No newline at end of file +*/ diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile b/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile index 4f3489bc4..f1491e212 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_dose_scoring/Makefile @@ -49,4 +49,4 @@ test: clean: $(REMOVE) $(ABS_DSO)*$(library)* - $(REMOVE) *.o + $(REMOVE) *.o diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 76eea9338..7eafd22ee 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -23,7 +23,7 @@ # # Author: Ernesto Mainegra-Hing, 2022 # -# Contributors: +# Contributors: # ############################################################################### */ @@ -45,125 +45,145 @@ #define REGIONS_PER_LINE 25 EGS_FluenceScoring::EGS_FluenceScoring(const string &Name, EGS_ObjectFactory *f) : - EGS_AusgabObject(Name,f), particle_name("photon"), - scoring_charge(photon), source_charge(unknown), - n_scoring_regions(0), n_source_regions(0), + EGS_AusgabObject(Name,f), particle_name("photon"), + scoring_charge(photon), source_charge(unknown), + n_scoring_regions(0), n_source_regions(0), max_reg(-1), active_region(-1), - flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), - norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0), + flu_s(0), flu_nbin(128), flu_xmin(0.001), flu_xmax(1.0), + norm_u(1.0), flu(0), fluT(0), flu_p(0), fluT_p(0), current_ncase(0), verbose(false), score_primaries(false), score_spe(false), - m_primary(0.0), m_tot(0.0) -{ - otype = "EGS_FluenceScoring"; - flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); - flu_b = -flu_xmin*flu_a; + m_primary(0.0), m_tot(0.0) { + otype = "EGS_FluenceScoring"; + flu_a = flu_nbin; + flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; } /*! Destructor. */ -EGS_FluenceScoring::~EGS_FluenceScoring(){ - if(flu) delete [] flu; - if(fluT) delete fluT; - if(flu_p) delete [] flu_p; - if(fluT_p) delete fluT_p; +EGS_FluenceScoring::~EGS_FluenceScoring() { + if (flu) { + delete [] flu; + } + if (fluT) { + delete fluT; + } + if (flu_p) { + delete [] flu_p; + } + if (fluT_p) { + delete fluT_p; + } } void EGS_FluenceScoring::initScoring(EGS_Input *inp) { - if( !inp ) { - egsWarning("AO type %s: null input?\n",otype.c_str()); return; + if (!inp) { + egsWarning("AO type %s: null input?\n",otype.c_str()); + return; } - vector name; int the_selection = 0; - name.push_back("photon"); name.push_back("electron"); name.push_back("positron"); + vector name; + int the_selection = 0; + name.push_back("photon"); + name.push_back("electron"); + name.push_back("positron"); name.push_back("undefined"); the_selection = inp->getInput("scoring particle", name, 3); - switch(the_selection){ - case 0: - particle_name="photon"; - scoring_charge = photon; - break; - case 1: - particle_name="electron"; - scoring_charge = electron; - break; - case 2: - particle_name="positron"; - scoring_charge = positron; - break; - default: - egsFatal("\n******* ERROR *******\n" - " Undefined scoring charge!\n" - " Aborting!\n" - "************************\n"); - } + switch (the_selection) { + case 0: + particle_name="photon"; + scoring_charge = photon; + break; + case 1: + particle_name="electron"; + scoring_charge = electron; + break; + case 2: + particle_name="positron"; + scoring_charge = positron; + break; + default: + egsFatal("\n******* ERROR *******\n" + " Undefined scoring charge!\n" + " Aborting!\n" + "************************\n"); + } the_selection = inp->getInput("source particle", name, 3); - switch(the_selection){ - case 0: - source_charge = photon; - break; - case 1: - source_charge = electron; - break; - case 2: - source_charge = positron; - break; - default: - source_charge = unknown; - } - - vector choice; + switch (the_selection) { + case 0: + source_charge = photon; + break; + case 1: + source_charge = electron; + break; + case 2: + source_charge = positron; + break; + default: + source_charge = unknown; + } + + vector choice; choice.push_back("no"); choice.push_back("yes"); verbose = inp->getInput("verbose", choice,0); score_primaries = inp->getInput("score primaries",choice,0); - score_spe = inp->getInput( "score spectrum", choice,0); - - if ( score_spe ){ - EGS_Float flu_Emin, flu_Emax; - EGS_Input *eGrid = inp->takeInputItem("energy grid"); - if( eGrid ) { - int err_n = eGrid->getInput("number of bins",flu_nbin); - int err_i = eGrid->getInput("minimum kinetic energy",flu_Emin); - int err_f = eGrid->getInput("maximum kinetic energy",flu_Emax); - if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: number of bins.\n" - " Aborting!\n\n"); - if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: minimum kinetic energy.\n" - " Aborting!\n\n"); - if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" - " Missing input: maximum kinetic energy.\n" - " Aborting!\n\n"); - } - EGS_Float norma; - int err_norm = inp->getInput("normalization",norma); - if (!err_norm) norm_u = norma; - else norm_u = 1.0; - - vector scale; - scale.push_back("linear"); scale.push_back("logarithmic"); - flu_s = inp->getInput("scale",scale,0); - if( flu_s == 0 ) { - flu_xmin = flu_Emin; flu_xmax = flu_Emax; - } - else { - flu_xmin = log(flu_Emin); flu_xmax = log(flu_Emax); - } - flu_a = flu_nbin; flu_a /= (flu_xmax - flu_xmin); - flu_b = -flu_xmin*flu_a; - /****************************************************** - Algorithm assigns E in [Ei,Ei+1), one could add extra - bin for E = Emax cases. Alternatively, push those - events into last bin (bias?) during scoring. - Which approach is correct? - ******************************************************/ - //flu_nbin++; + score_spe = inp->getInput("score spectrum", choice,0); + + if (score_spe) { + EGS_Float flu_Emin, flu_Emax; + EGS_Input *eGrid = inp->takeInputItem("energy grid"); + if (eGrid) { + int err_n = eGrid->getInput("number of bins",flu_nbin); + int err_i = eGrid->getInput("minimum kinetic energy",flu_Emin); + int err_f = eGrid->getInput("maximum kinetic energy",flu_Emax); + if (err_n) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: number of bins.\n" + " Aborting!\n\n"); + if (err_i) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: minimum kinetic energy.\n" + " Aborting!\n\n"); + if (err_f) egsFatal("\n**** EGS_FluenceScoring::initScoring" + " Missing input: maximum kinetic energy.\n" + " Aborting!\n\n"); + } + EGS_Float norma; + int err_norm = inp->getInput("normalization",norma); + if (!err_norm) { + norm_u = norma; + } + else { + norm_u = 1.0; + } + + vector scale; + scale.push_back("linear"); + scale.push_back("logarithmic"); + flu_s = inp->getInput("scale",scale,0); + if (flu_s == 0) { + flu_xmin = flu_Emin; + flu_xmax = flu_Emax; + } + else { + flu_xmin = log(flu_Emin); + flu_xmax = log(flu_Emax); + } + flu_a = flu_nbin; + flu_a /= (flu_xmax - flu_xmin); + flu_b = -flu_xmin*flu_a; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), one could add extra + bin for E = Emax cases. Alternatively, push those + events into last bin (bias?) during scoring. + Which approach is correct? + ******************************************************/ + //flu_nbin++; } - /* + /* Get source regions where interacting particles - are not subjected to classification + are not subjected to classification */ vector s_start, s_stop; /* @@ -179,10 +199,10 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { if (s_start.size() == s_stop.size()) { // group of dose regions for (int i=0; i fr) - egsFatal("\nEGS_FluenceScoring::initScoring: \n" - " Decreasing start (%d) / stop %d region in source region group %d\n" - " Aborting!\n\n", ir, fr, i ); + if (ir > fr) + egsFatal("\nEGS_FluenceScoring::initScoring: \n" + " Decreasing start (%d) / stop %d region in source region group %d\n" + " Aborting!\n\n", ir, fr, i); for (int ireg=ir; ireg<=fr; ireg++) { s_region.push_back(ireg); } @@ -198,42 +218,45 @@ void EGS_FluenceScoring::initScoring(EGS_Input *inp) { } -void EGS_FluenceScoring::getSensitiveRegions(EGS_Input *inp){ +void EGS_FluenceScoring::getSensitiveRegions(EGS_Input *inp) { string listLabel, startLabel, stopLabel; - if ( otype == "EGS_PlanarFluence" ){ - listLabel = "contributing regions"; - startLabel = "start contributing region"; - stopLabel = "stop contributing region"; + if (otype == "EGS_PlanarFluence") { + listLabel = "contributing regions"; + startLabel = "start contributing region"; + stopLabel = "stop contributing region"; + } + else if (otype == "EGS_VolumetricFluence") { + listLabel = "scoring regions"; + startLabel = "start region"; + stopLabel = "stop region"; } - else if (otype == "EGS_VolumetricFluence"){ - listLabel = "scoring regions"; - startLabel = "start region"; - stopLabel = "stop region"; + else { + egsFatal("\n*** Unknown fluence scoriung type! Aborting!\n\n"); } - else - egsFatal("\n*** Unknown fluence scoriung type! Aborting!\n\n"); /* get scoring regions */ if (!inp->getInput(listLabel,f_regionsString) && f_regionsString.length()>0) { // Individual regions - if ( f_regionsString == "ALL") - score_in_all_regions = true; - else - score_in_all_regions = false; + if (f_regionsString == "ALL") { + score_in_all_regions = true; + } + else { + score_in_all_regions = false; + } } else {// Groups of regions int err1 = inp->getInput(startLabel,f_start); int err2 = inp->getInput(stopLabel, f_stop); if (!err1 && !err2) { - if ( f_start.size() == f_stop.size() ){ // group of dose regions - for ( int i=0; i fr ) - egsFatal("\nEGS_FluenceScoring::initScoring: \n" - " Decreasing start (%d) / stop (%d) in region group %d\n" - " Aborting!\n\n", ir, fr, i ); + if (ir > fr) + egsFatal("\nEGS_FluenceScoring::initScoring: \n" + " Decreasing start (%d) / stop (%d) in region group %d\n" + " Aborting!\n\n", ir, fr, i); for (int ireg=ir; ireg<=fr; ireg++) { f_region.push_back(ireg); } @@ -249,181 +272,197 @@ void EGS_FluenceScoring::getSensitiveRegions(EGS_Input *inp){ } } -void EGS_FluenceScoring::getNumberRegions( const string &str, vector ®s ){ - if ( !app ) - egsFatal("EGS_FluenceScoring::getNumberRegions\n" - " Undefined parent application! Aborting!\n"); +void EGS_FluenceScoring::getNumberRegions(const string &str, vector ®s) { + if (!app) + egsFatal("EGS_FluenceScoring::getNumberRegions\n" + " Undefined parent application! Aborting!\n"); app->getNumberRegions(str, regs); } -void EGS_FluenceScoring::getLabelRegions( const string &str, vector ®s ) { - if ( !app ) - egsFatal("EGS_FluenceScoring::getLabelRegions\n" - " Undefined parent application! Aborting!\n"); +void EGS_FluenceScoring::getLabelRegions(const string &str, vector ®s) { + if (!app) + egsFatal("EGS_FluenceScoring::getLabelRegions\n" + " Undefined parent application! Aborting!\n"); app->getLabelRegions(str, regs); } void EGS_FluenceScoring::setUpRegionFlags() { - if ( s_regionsString.length() > 0 && !s_region.size() ) { - getNumberRegions(s_regionsString, s_region ); - getLabelRegions( s_regionsString, s_region ); + if (s_regionsString.length() > 0 && !s_region.size()) { + getNumberRegions(s_regionsString, s_region); + getLabelRegions(s_regionsString, s_region); } // Get the number of source regions. If too many, reset to nreg. n_source_regions = s_region.size() < nreg ? s_region.size() : nreg; - if ( !score_in_all_regions ){ - if ( f_regionsString.length() > 0 && !f_region.size() ){ - getNumberRegions(f_regionsString, f_region); - getLabelRegions(f_regionsString, f_region); - } - // Get the number of scoring regions. If too many, reset to nreg - n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg; + if (!score_in_all_regions) { + if (f_regionsString.length() > 0 && !f_region.size()) { + getNumberRegions(f_regionsString, f_region); + getLabelRegions(f_regionsString, f_region); + } + // Get the number of scoring regions. If too many, reset to nreg + n_scoring_regions = f_region.size() < nreg ? f_region.size() : nreg; } - else if ( score_in_all_regions ){ + else if (score_in_all_regions) { n_scoring_regions = nreg; for (int j=0; j Warning: Setting n_source_regions = nreg \n" - " effectively supresses classification\n" - " of primaries and secondaries!"); + if (n_source_regions == nreg && score_primaries) + egsWarning("=> Warning: Setting n_source_regions = nreg \n" + " effectively supresses classification\n" + " of primaries and secondaries!"); for (int i = 0; i < n_source_regions; i++) { is_source[s_region[i]] = true; } for (int i = 0; i < n_scoring_regions; i++) { is_sensitive[f_region[i]] = true; - if ( f_region[i] > max_reg ) max_reg = f_region[i]; + if (f_region[i] > max_reg) { + max_reg = f_region[i]; + } } } -void EGS_FluenceScoring::describeMe(){ +void EGS_FluenceScoring::describeMe() { char buf[128]; // Report scoring regions in the geometry. - if ( n_scoring_regions == nreg ) - description += "ALL\n"; - else{ - int start = 0, stop = 0, k = 0, entries = 0; - bool line_ended; - /* List up to 100 scoring groups or regions */ - while (k < nreg && entries < REGIONS_ENTRIES){ - line_ended = false; - if( is_sensitive[k] ){ - start = k; - entries++; - while( is_sensitive[k] && k < nreg) { - k++; - } - stop = k-1; - if ( start < stop ){ - sprintf(buf,"%d-%d",start,stop); - } - else if ( k % REGIONS_PER_LINE ){ - sprintf(buf," %d",start); - } - else{ - sprintf(buf," %d\n",start); - line_ended = true; - } - if (entries == REGIONS_ENTRIES){ - sprintf(buf,"... %d\n",max_reg); - line_ended = true; - } - description += buf; - } - k++; - } - if (!line_ended) description += "\n"; - } - - if ( score_primaries ){ - if ( source_charge == unknown ){ - source_charge = scoring_charge; - description += " - Unknown source charge due to multi-particle source\n"; - description += " and no user input defining source particle type!\n"; - description += " Defaulting to scoring charge: "; - } - else{ - description += " - source charge: "; - } - sprintf(buf,"%d\n",source_charge); - description += buf; - } - - if (score_spe){ - description += " - scoring in the "; - EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, - Emax = flu_s ? exp(flu_xmax):flu_xmax; - sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); - description += buf; - if (flu_s) - description += " on a logarithmic scale \n"; - else - description += " on a linear scale \n"; + if (n_scoring_regions == nreg) { + description += "ALL\n"; + } + else { + int start = 0, stop = 0, k = 0, entries = 0; + bool line_ended; + /* List up to 100 scoring groups or regions */ + while (k < nreg && entries < REGIONS_ENTRIES) { + line_ended = false; + if (is_sensitive[k]) { + start = k; + entries++; + while (is_sensitive[k] && k < nreg) { + k++; + } + stop = k-1; + if (start < stop) { + sprintf(buf,"%d-%d",start,stop); + } + else if (k % REGIONS_PER_LINE) { + sprintf(buf," %d",start); + } + else { + sprintf(buf," %d\n",start); + line_ended = true; + } + if (entries == REGIONS_ENTRIES) { + sprintf(buf,"... %d\n",max_reg); + line_ended = true; + } + description += buf; + } + k++; + } + if (!line_ended) { + description += "\n"; + } + } + + if (score_primaries) { + if (source_charge == unknown) { + source_charge = scoring_charge; + description += " - Unknown source charge due to multi-particle source\n"; + description += " and no user input defining source particle type!\n"; + description += " Defaulting to scoring charge: "; + } + else { + description += " - source charge: "; + } + sprintf(buf,"%d\n",source_charge); + description += buf; + } + + if (score_spe) { + description += " - scoring in the "; + EGS_Float Emin = flu_s ? exp(flu_xmin):flu_xmin, + Emax = flu_s ? exp(flu_xmax):flu_xmax; + sprintf(buf,"%g MeV and %g MeV energy range",Emin,Emax); + description += buf; + if (flu_s) { + description += " on a logarithmic scale \n"; + } + else { + description += " on a linear scale \n"; + } } } -/********************************************** +/********************************************** * Class EGS_PlanarFluence Implementation * ***********************************************/ EGS_PlanarFluence::EGS_PlanarFluence(const string &Name, EGS_ObjectFactory *f) : - EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) -{ + EGS_FluenceScoring(Name,f), hits_field(false), Nx(1), Ny(1) { otype = "EGS_PlanarFluence"; m_midpoint = EGS_Vector(0,0,5); m_R = 5; m_R2 = 25; - field_type = circle; Area = M_PI * m_R2; + field_type = circle; + Area = M_PI * m_R2; m_normal = EGS_Vector(0,0,1); m_d = m_normal*m_midpoint; } /*! Destructor. */ -EGS_PlanarFluence::~EGS_PlanarFluence(){ - - if( flu ) { - for(int j=0; jsourceCharge(); + if (source_charge == unknown) { + source_charge = (ParticleType)app->sourceCharge(); + } - // Get the number of regions in the geometry. + // Get the number of regions in the geometry. nreg = app->getnRegions(); /* Initialize arrays with defaults */ for (int j=0; jtakeInputItem("planar scoring"); - if( ! pScoringInput ){ - egsFatal("AO type %s: Missing planar scoring input?\n",otype.c_str()); - return; - } + else { + pScoringInput = inp->takeInputItem("planar scoring"); + if (! pScoringInput) { + egsFatal("AO type %s: Missing planar scoring input?\n",otype.c_str()); + return; + } } EGS_FluenceScoring::initScoring(inp);// common inputs @@ -477,17 +517,19 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { // Specific plane input vector tmp_field; int err2 = pScoringInput->getInput("scoring circle",tmp_field); - if( err2 ) { + if (err2) { int err3 = pScoringInput->getInput("scoring rectangle",tmp_field); - if( err3 || tmp_field.size() != 4) { + if (err3 || tmp_field.size() != 4) { egsWarning( - "\n\n*** Wrong/missing 'scoring rectangle' input " - "setting it to 10 cm X 10 cm field at origin!\n\n"); + "\n\n*** Wrong/missing 'scoring rectangle' input " + "setting it to 10 cm X 10 cm field at origin!\n\n"); m_midpoint = EGS_Vector(); - ax = 10; ay = 10; - field_type = rectangle; Area = ax*ay; + ax = 10; + ay = 10; + field_type = rectangle; + Area = ax*ay; } - else{ + else { EGS_Float xmin = tmp_field[0],xmax = tmp_field[1], ymin = tmp_field[2],ymax = tmp_field[3]; /* scoring plane location in space */ @@ -495,99 +537,116 @@ void EGS_PlanarFluence::initScoring(EGS_Input *inp) { /* scoring plane normal */ m_normal = EGS_Vector(0,0,1); // default normal along positive z-axis /* define unit vectors on right-handed scoring plane */ - ux = EGS_Vector(1,0,0); uy = EGS_Vector(0,1,0); + ux = EGS_Vector(1,0,0); + uy = EGS_Vector(0,1,0); /* Request a scoring plane transformation for initial position and orientation */ EGS_AffineTransform *t = EGS_AffineTransform::getTransformation(inp); - if (t){ - t->rotate(m_normal) ; t->transform(m_midpoint); - t->rotate(ux); t->rotate(uy); + if (t) { + t->rotate(m_normal) ; + t->transform(m_midpoint); + t->rotate(ux); + t->rotate(uy); delete t; } - + /* get screen resolution */ vector screen; int err4 = pScoringInput->getInput("resolution",screen); - if( err4 ) { - Nx=1; Ny=1; - egsWarning( - "\n\n*** Missing/wrong 'resolution' input " - " Scoring in the whole field\n\n"); - + if (err4) { + Nx=1; + Ny=1; + egsWarning( + "\n\n*** Missing/wrong 'resolution' input " + " Scoring in the whole field\n\n"); + } - else if (screen.size()==1){ Nx = screen[0];Ny = screen[0];} - else if (screen.size()==2){ Nx = screen[0];Ny = screen[1];} - else if (screen.size()> 2){ Nx = screen[0];Ny = screen[1]; - egsWarning( - "\n\n*** Too many 'resolution' inputs\n" - " Using first two entries\n\n"); + else if (screen.size()==1) { + Nx = screen[0]; + Ny = screen[0]; + } + else if (screen.size()==2) { + Nx = screen[0]; + Ny = screen[1]; + } + else if (screen.size()> 2) { + Nx = screen[0]; + Ny = screen[1]; + egsWarning( + "\n\n*** Too many 'resolution' inputs\n" + " Using first two entries\n\n"); } - ax = xmax - xmin; ay = ymax-ymin; - vx = ax/Nx; vy = ay/Ny; - field_type = rectangle; Area = vx*vy; + ax = xmax - xmin; + ay = ymax-ymin; + vx = ax/Nx; + vy = ay/Ny; + field_type = rectangle; + Area = vx*vy; } } - else if( tmp_field.size() != 4 ) { + else if (tmp_field.size() != 4) { egsWarning( - "\n\n*** Wrong/missing 'scoring circle' input " - "setting it to 10 cm diameter field at origin!\n\n"); + "\n\n*** Wrong/missing 'scoring circle' input " + "setting it to 10 cm diameter field at origin!\n\n"); m_midpoint = EGS_Vector(); m_R = 5; m_R2 = 25; - field_type = circle; Area = M_PI * m_R2; + field_type = circle; + Area = M_PI * m_R2; } - else{ + else { vector tmp_normal; int err1 = pScoringInput->getInput("scoring plane normal",tmp_normal); - if( err1 || tmp_normal.size() != 3 ) { - egsWarning( - "\n\n*** Wrong/missing 'scoring plane normal' input. " - "Set along positive z-axis\n\n"); - m_normal = EGS_Vector(0,0,1);// Default along positive z-axis + if (err1 || tmp_normal.size() != 3) { + egsWarning( + "\n\n*** Wrong/missing 'scoring plane normal' input. " + "Set along positive z-axis\n\n"); + m_normal = EGS_Vector(0,0,1);// Default along positive z-axis } - else{ - m_normal = EGS_Vector(tmp_normal[0],tmp_normal[1], - tmp_normal[2]); - m_normal.normalize(); + else { + m_normal = EGS_Vector(tmp_normal[0],tmp_normal[1], + tmp_normal[2]); + m_normal.normalize(); } m_midpoint = EGS_Vector(tmp_field[0],tmp_field[1], tmp_field[2]); - m_R = tmp_field[3]; + m_R = tmp_field[3]; m_R2 = tmp_field[3]*tmp_field[3]; - field_type = circle; Area = M_PI * m_R2; + field_type = circle; + Area = M_PI * m_R2; } m_d = m_normal*m_midpoint; } -void EGS_PlanarFluence::describeMe(){ +void EGS_PlanarFluence::describeMe() { char buf[128]; sprintf(buf,"\nPlanar %s fluence scoring\n",particle_name.c_str()); description = buf; description += "================================\n"; description += " - scoring field normal = "; - sprintf(buf,"(%g %g %g)\n",m_normal.x,m_normal.y,m_normal.z); + sprintf(buf,"(%g %g %g)\n",m_normal.x,m_normal.y,m_normal.z); description += buf; description += " - scoring field center = "; - sprintf(buf,"(%g %g %g)\n",m_midpoint.x,m_midpoint.y,m_midpoint.z); + sprintf(buf,"(%g %g %g)\n",m_midpoint.x,m_midpoint.y,m_midpoint.z); description += buf; - if (field_type == circle){ - description += " - scoring field radius = "; - sprintf(buf,"%g cm\n",m_R); - description += buf; + if (field_type == circle) { + description += " - scoring field radius = "; + sprintf(buf,"%g cm\n",m_R); + description += buf; } else if (field_type == rectangle) { - description += " - scoring field = "; - sprintf(buf,"%g cm X %g cm\n",ax,ay); - description += buf; - description += " - scoring field resolution = "; - sprintf(buf,"%d X %d\n",Nx,Ny); - description += buf; + description += " - scoring field = "; + sprintf(buf,"%g cm X %g cm\n",ax,ay); + description += buf; + description += " - scoring field resolution = "; + sprintf(buf,"%d X %d\n",Nx,Ny); + description += buf; } description += " - scoring field distance from origin = "; - sprintf(buf,"%g cm\n",m_d); + sprintf(buf,"%g cm\n",m_d); description += buf; description +=" - scoring from region(s): "; @@ -596,246 +655,277 @@ void EGS_PlanarFluence::describeMe(){ } -void EGS_PlanarFluence::score(const EGS_Particle& p, const int& ivoxel){ - EGS_Float up = p.u*m_normal, aup = fabs(up); - /********************************************************** - Prevent large weights from particles very close to plane. - ***********************************************************/ - if( aup < 0.08 ) aup = 0.0871557;// Limit incident angle to 85 degrees - EGS_Float e = p.q ? p.E - app->getRM() : p.E; - if( flu_s ) {e = log(e);} // log scale - EGS_Float ae; int je; - EGS_Float auxp = p.wt/aup; - /* Score total fluence in corresponding pixel */ - fluT->score(ivoxel,auxp); - if (score_primaries && !p.latch) +void EGS_PlanarFluence::score(const EGS_Particle &p, const int &ivoxel) { + EGS_Float up = p.u*m_normal, aup = fabs(up); + /********************************************************** + Prevent large weights from particles very close to plane. + ***********************************************************/ + if (aup < 0.08) { + aup = 0.0871557; // Limit incident angle to 85 degrees + } + EGS_Float e = p.q ? p.E - app->getRM() : p.E; + if (flu_s) { + e = log(e); // log scale + } + EGS_Float ae; + int je; + EGS_Float auxp = p.wt/aup; + /* Score total fluence in corresponding pixel */ + fluT->score(ivoxel,auxp); + if (score_primaries && !p.latch) { fluT_p->score(ivoxel,auxp); - /* Score differential fluence in corresponding pixel */ - if( score_spe && e > flu_xmin && e <= flu_xmax ){ - ae = flu_a*e + flu_b; - /****************************************************** - Algorithm assigns E in [Ei,Ei+1), hence push events - with E = Emax into last bin (bias?) during scoring. - Alternatively add extra bin for E = Emax cases. - Which approach is correct? - ******************************************************/ - je = min((int)ae,flu_nbin-1);//je = (int) ae; - /******************************************************/ - if (ivoxel < 0 || ivoxel > Nx*Ny) - egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); - EGS_ScoringArray *aux = flu[ivoxel]; - if (je < 0 || je >= flu_nbin ) - egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); - aux->score(je,auxp); - if (score_primaries && !p.latch) { - flu_p[ivoxel]->score(je,auxp); - } - } + } + /* Score differential fluence in corresponding pixel */ + if (score_spe && e > flu_xmin && e <= flu_xmax) { + ae = flu_a*e + flu_b; + /****************************************************** + Algorithm assigns E in [Ei,Ei+1), hence push events + with E = Emax into last bin (bias?) during scoring. + Alternatively add extra bin for E = Emax cases. + Which approach is correct? + ******************************************************/ + je = min((int)ae,flu_nbin-1);//je = (int) ae; + /******************************************************/ + if (ivoxel < 0 || ivoxel > Nx*Ny) { + egsFatal("\n-> Scoring out of bounds, ivoxel = %d\n",ivoxel); + } + EGS_ScoringArray *aux = flu[ivoxel]; + if (je < 0 || je >= flu_nbin) { + egsFatal("\n-> Scoring out of bounds, ibin = %d ae = %g E = %g MeV\n",je,ae,e); + } + aux->score(je,auxp); + if (score_primaries && !p.latch) { + flu_p[ivoxel]->score(je,auxp); + } + } } -int EGS_PlanarFluence::hitsField(const EGS_Particle& p, EGS_Float* dist){ - if ( field_type==circle ){ - EGS_Float xp = p.x*m_normal, up = p.u*m_normal; - if( (up > 0 && m_d > xp ) || - (up < 0 && m_d < xp ) ){ - EGS_Float t = (m_d - xp)/up; - EGS_Vector x1(p.x + p.u*t - m_midpoint); - if( dist ) *dist = t; - return x1.length2() < m_R*m_R ? 0:-1; - } - return -1; - } - else if ( field_type == rectangle ){ - EGS_Float xp = p.x*m_normal, up = p.u*m_normal; - if( (up > 0 && m_d > xp) || (up < 0 && m_d < xp) ) { - EGS_Float t = (m_d - xp)/up; // distance to plane along u - EGS_Vector x1(p.x + p.u*t - m_midpoint);// vector on scoring plane - EGS_Float xcomp = x1*ux;// x-direction component - EGS_Float ycomp = x1*uy;// y-direction component - xcomp = 2*xcomp + ax; - ycomp = 2*ycomp + ay; - if( xcomp > 0 && xcomp < 2*ax && - ycomp > 0 && ycomp < 2*ay ){ - int i = int(xcomp/(2*vx)), - j = int(ycomp/(2*vy)), - k = i + j*Nx; - if( dist ) *dist = t; - return k; - } - } - return -1; - } - else return -1; +int EGS_PlanarFluence::hitsField(const EGS_Particle &p, EGS_Float *dist) { + if (field_type==circle) { + EGS_Float xp = p.x*m_normal, up = p.u*m_normal; + if ((up > 0 && m_d > xp) || + (up < 0 && m_d < xp)) { + EGS_Float t = (m_d - xp)/up; + EGS_Vector x1(p.x + p.u*t - m_midpoint); + if (dist) { + *dist = t; + } + return x1.length2() < m_R*m_R ? 0:-1; + } + return -1; + } + else if (field_type == rectangle) { + EGS_Float xp = p.x*m_normal, up = p.u*m_normal; + if ((up > 0 && m_d > xp) || (up < 0 && m_d < xp)) { + EGS_Float t = (m_d - xp)/up; // distance to plane along u + EGS_Vector x1(p.x + p.u*t - m_midpoint);// vector on scoring plane + EGS_Float xcomp = x1*ux;// x-direction component + EGS_Float ycomp = x1*uy;// y-direction component + xcomp = 2*xcomp + ax; + ycomp = 2*ycomp + ay; + if (xcomp > 0 && xcomp < 2*ax && + ycomp > 0 && ycomp < 2*ay) { + int i = int(xcomp/(2*vx)), + j = int(ycomp/(2*vy)), + k = i + j*Nx; + if (dist) { + *dist = t; + } + return k; + } + } + return -1; + } + else { + return -1; + } } -void EGS_PlanarFluence::ouputPlanarFluence( EGS_ScoringArray *fT, const double &norma ){ +void EGS_PlanarFluence::ouputPlanarFluence(EGS_ScoringArray *fT, const double &norma) { double fe,dfe,dfer; int count = 0; int ix_digits = getDigits(Nx); int iy_digits = getDigits(Ny); int xy_digits = getDigits(Nx*Ny); - if (field_type == circle){ - egsInformation("\n pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "-----------------------------------------------------\n"); - } - else{ - egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "-----------------------------------------------------\n", - iy_digits,"iy",ix_digits,"ix",&count); - } - if (field_type == circle){ - int k = 0; - egsInformation(" %*d ",xy_digits,k); - fT->currentResult(k,fe,dfe); - if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; - egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); - } - else{ - for(int j=0; jcurrentResult(k,fe,dfe); - if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; - egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); - } - } + if (field_type == circle) { + egsInformation("\n pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n"); + } + else { + egsInformation("\n %*s %*s pixel# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "-----------------------------------------------------\n", + iy_digits,"iy",ix_digits,"ix",&count); + } + if (field_type == circle) { + int k = 0; + egsInformation(" %*d ",xy_digits,k); + fT->currentResult(k,fe,dfe); + if (fe > 0) { + dfer = 100*dfe/fe; + } + else { + dfer = 100; + } + egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); + } + else { + for (int j=0; jcurrentResult(k,fe,dfe); + if (fe > 0) { + dfer = 100*dfe/fe; + } + else { + dfer = 100; + } + egsInformation(" %10.4le +/- %10.4le [%-7.3lf\%]\n",fe*norma,dfe*norma,dfer); + } + } } } -void EGS_PlanarFluence::ouputResults(){ +void EGS_PlanarFluence::ouputResults() { EGS_Float src_norm = 1.0, // default to number of histories in this run Fsrc = app->getFluence();// Fluence or number of primary histories egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", - current_ncase, Fsrc); - - if (Fsrc) - src_norm = Fsrc/current_ncase;// fluence or primary histories per histories run - + current_ncase, Fsrc); + + if (Fsrc) { + src_norm = Fsrc/current_ncase; // fluence or primary histories per histories run + } + string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; string src_type = app->sourceType(); - if ( src_type == "EGS_BeamSource" ){ + if (src_type == "EGS_BeamSource") { normLabel = "primary history"; - egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", - src_type.c_str(), src_norm); + egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", + src_type.c_str(), src_norm); } - else if ( src_type == "EGS_CollimatedSource" || - (src_type == "EGS_ParallelBeam" && src_norm != 1)){ - egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", - src_type.c_str(), src_norm); + else if (src_type == "EGS_CollimatedSource" || + (src_type == "EGS_ParallelBeam" && src_norm != 1)) { + egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", + src_type.c_str(), src_norm); } - else{ - egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", - src_type.c_str(), src_norm); - + else { + egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", + src_type.c_str(), src_norm); + } - - + + double norm = 1.0/src_norm; //per fluence or particle depending on source - norm /= Area; //per unit area - norm *= norm_u; // times user-requested normalization - + norm /= Area; //per unit area + norm *= norm_u; // times user-requested normalization + //egsInformation(" Normalization = %g\n",norm); - - egsInformation( "\n\n Integral fluence\n" - " ================\n\n"); + + egsInformation("\n\n Integral fluence\n" + " ================\n\n"); egsInformation("\n\n Total %s fluence\n", particle_name.c_str()); - ouputPlanarFluence( fluT, norm ); - - if ( score_primaries ){ - egsInformation("\n\n Primary fluence\n"); - ouputPlanarFluence( fluT_p, norm ); - } - - if ( score_spe ){ - norm *= flu_a;//per unit bin width - string suffix = "_" + particle_name + ".agr"; - string spe_name = app->constructIOFileName(suffix.c_str(),true); - ofstream spe_output(spe_name.c_str(),ios::out); - if (!spe_output){ - egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str()); - exit(1); - } - - spe_output << "# " << particle_name.c_str() << " fluence \n"; - spe_output << "# \n"; - spe_output << "@ legend 0.2, 0.8\n"; - spe_output << "@ legend box linestyle 0\n"; - spe_output << "@ legend font 4\n"; - spe_output << "@ xaxis label \"energy / MeV\"\n"; - spe_output << "@ xaxis label char size 1.560000\n"; - spe_output << "@ xaxis label font 4\n"; - spe_output << "@ xaxis ticklabel font 4\n"; - spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; - spe_output << "@ yaxis label char size 1.560000\n"; - spe_output << "@ yaxis label font 4\n"; - spe_output << "@ yaxis ticklabel font 4\n"; - spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; - spe_output << "@ title font 4\n"; - spe_output << "@ title size 1.500000\n"; - spe_output << "@ subtitle \"for each scoring region\"\n"; - spe_output << "@ subtitle font 4\n"; - spe_output << "@ subtitle size 1.000000\n"; - - egsInformation( "\n\n Differential fluence\n" - " ====================\n\n"); - - int i_graph = 0; - double fe,dfe; - for(int j=0; jcurrentResult(l,fe,dfe); - EGS_Float e = (l+0.5-flu_b)/flu_a; - if( flu_s ) e = exp(e); - spe_output<constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + if (!spe_output) { + egsFatal("\n EGS_PlanarFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + egsInformation("\n\n Differential fluence\n" + " ====================\n\n"); + + int i_graph = 0; + double fe,dfe; + for (int j=0; jcurrentResult(l,fe,dfe); + if (verbose) { + egsInformation("\n\n Total \n\n"); + egsInformation("\n Emid/MeV Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" + "---------------------------------------------\n"); + } + for (int l=0; lcurrentResult(l,fe,dfe); EGS_Float e = (l+0.5-flu_b)/flu_a; - if( flu_s ) e = exp(e); - spe_output<currentResult(l,fe,dfe); + EGS_Float e = (l+0.5-flu_b)/flu_a; + if (flu_s) { + e = exp(e); + } + spe_output<storeState(data) ) return false; - - if( score_spe ) { - for(int j=0; jstoreState(data) ) return false; - } - } - - if ( score_primaries ){ - if( !fluT_p->storeState(data) ) return false; - if( score_spe ) { - for(int j=0; jstoreState(data) ) return false; - } - } - } - - return true; + if (!egsStoreI64(data,current_ncase)) { + return false; + } + data << endl; + //data << m_primary << " " << m_ray << " " << m_compt << " " << m_fluor << " " << m_multiple; + data << m_tot << " " << m_primary; + data << endl; + if (!data.good()) { + return false; + } + + if (!fluT->storeState(data)) { + return false; + } + + if (score_spe) { + for (int j=0; jstoreState(data)) { + return false; + } + } + } + + if (score_primaries) { + if (!fluT_p->storeState(data)) { + return false; + } + if (score_spe) { + for (int j=0; jstoreState(data)) { + return false; + } + } + } + } + + return true; } -bool EGS_PlanarFluence::setState(istream &data){ - if( !egsGetI64(data,current_ncase) ) return false; - //data >> m_primary >> m_ray >> m_compt >> m_fluor >> m_multiple; - data >> m_tot >> m_primary; - if (!data.good()) return false; +bool EGS_PlanarFluence::setState(istream &data) { + if (!egsGetI64(data,current_ncase)) { + return false; + } + //data >> m_primary >> m_ray >> m_compt >> m_fluor >> m_multiple; + data >> m_tot >> m_primary; + if (!data.good()) { + return false; + } - if( !fluT->setState(data) ) return false; + if (!fluT->setState(data)) { + return false; + } + + if (score_spe) { + for (int j=0; jsetState(data)) { + return false; + } + } + } - if( score_spe ) { - for(int j=0; jsetState(data) ) return false; - } - } + if (score_primaries) { - if ( score_primaries ){ + if (!fluT_p->setState(data)) { + return false; + } - if( !fluT_p->setState(data) ) return false; - - if( score_spe ) { - for(int j=0; jsetState(data) ) return false; - } - } + if (score_spe) { + for (int j=0; jsetState(data)) { + return false; + } + } + } - } - return true; + } + return true; } -bool EGS_PlanarFluence::addState(istream &data){ - EGS_I64 tmp_case; if( !egsGetI64(data,tmp_case) ) return false; - current_ncase += tmp_case; - /* individual contributions */ - //EGS_Float tmp_primary, tmp_fluor, tmp_compt, tmp_ray, tmp_multiple; - //data >> tmp_primary >> tmp_ray >> tmp_compt >> tmp_fluor >> tmp_multiple; - EGS_Float tmp_tot, tmp_primary; - data >> tmp_tot >> tmp_primary; - if (!data.good()) return false; - m_primary += tmp_primary; - m_tot += tmp_tot; - //m_primary += tmp_primary;m_ray += tmp_ray;m_compt += tmp_compt; - //m_fluor += tmp_fluor;m_multiple += tmp_multiple; - /* fluence objects */ - - EGS_ScoringArray tgT(Nx*Ny); - if( !tgT.setState(data) ) return false; - (*fluT) += tgT; - - if( score_spe ) { - EGS_ScoringArray tg(flu_nbin); - for(int j=0; j> tmp_primary >> tmp_ray >> tmp_compt >> tmp_fluor >> tmp_multiple; + EGS_Float tmp_tot, tmp_primary; + data >> tmp_tot >> tmp_primary; + if (!data.good()) { + return false; + } + m_primary += tmp_primary; + m_tot += tmp_tot; + //m_primary += tmp_primary;m_ray += tmp_ray;m_compt += tmp_compt; + //m_fluor += tmp_fluor;m_multiple += tmp_multiple; + /* fluence objects */ + + EGS_ScoringArray tgT(Nx*Ny); + if (!tgT.setState(data)) { + return false; + } + (*fluT) += tgT; + + if (score_spe) { + EGS_ScoringArray tg(flu_nbin); + for (int j=0; jsourceCharge(); + if (source_charge == unknown) { + source_charge = (ParticleType)app->sourceCharge(); + } // Get the number of regions in the geometry. nreg = app->getnRegions(); @@ -1011,146 +1147,151 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { is_source.push_back(false); volume.push_back(vol_list[0]);// set to either 1.0 or first volume entered } - + /* Flag scoring and source regions*/ setUpRegionFlags(); /* Update arrays with user inputs */ for (int i = 0; i < n_scoring_regions; i++) { - if ( i < vol_list.size() ) { + if (i < vol_list.size()) { volume[f_region[i]] = vol_list[i]; } } /* Setup fluence scoring arrays */ fluT = new EGS_ScoringArray(nreg); - if ( score_spe ){ - flu = new EGS_ScoringArray* [nreg]; - for (int j = 0; j < nreg; j++){ - if (is_sensitive[j]) - flu[j] = new EGS_ScoringArray(flu_nbin); - } - } - if ( score_primaries ){ - fluT_p = new EGS_ScoringArray(nreg); - if ( score_spe ){ - flu_p = new EGS_ScoringArray* [nreg]; - for (int j = 0; j < nreg; j++){ - if (is_sensitive[j]) - flu_p[j] = new EGS_ScoringArray(flu_nbin); - } - } - } -#ifdef DEBUG + if (score_spe) { + flu = new EGS_ScoringArray* [nreg]; + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu[j] = new EGS_ScoringArray(flu_nbin); + } + } + } + if (score_primaries) { + fluT_p = new EGS_ScoringArray(nreg); + if (score_spe) { + flu_p = new EGS_ScoringArray* [nreg]; + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu_p[j] = new EGS_ScoringArray(flu_nbin); + } + } + } + } +#ifdef DEBUG binDist = new EGS_ScoringArray(flu_nbin); int imed_max_range; #endif - EGS_Float flu_Emin = flu_s ? exp(flu_xmin) : flu_xmin, + EGS_Float flu_Emin = flu_s ? exp(flu_xmin) : flu_xmin, flu_Emax = flu_s ? exp(flu_xmax) : flu_xmax; EGS_Float bw = flu_s ?(log(flu_Emax / flu_Emin))/flu_nbin : - (flu_Emax - flu_Emin) /flu_nbin; - flu_a_i = bw; flu_a = 1.0/bw; - + (flu_Emax - flu_Emin) /flu_nbin; + flu_a_i = bw; + flu_a = 1.0/bw; + /* Initialize data required to score charged particle fluence */ - if ( scoring_charge ){ - EGS_Float expbw; - /* Pre-calculated values for faster evaluation on log scale */ - if (flu_s){ - expbw = exp(bw); // => (Emax/Emin)^(1/nbin) - r_const = 1/(expbw-1); - DE = new EGS_Float [flu_nbin]; - a_const = new EGS_Float [flu_nbin]; - for ( int i = 0; i < flu_nbin; i++ ){ - DE[i] = flu_Emin*pow(expbw,i)*(expbw-1); - a_const[i] = 1/flu_Emin*pow(1/expbw,i); - } - } - /* Do not score below ECUT - PRM */ - if (flu_Emin < app->getEcut() - app->getRM() ){ - flu_Emin = app->getEcut() - app->getRM() ; - /* Decrease number of bins, preserve bin width */ - flu_nbin = flu_s ? - ceil((log(flu_Emax / flu_Emin))/bw) : - ceil( (flu_Emax - flu_Emin) /bw); - } - - /* Pre-calculated values for faster 1/stpwr evaluation */ - if (flu_stpwr){ - - int n_media = app->getnMedia(); - - EGS_Float lnE, lnEmin, lnEmax, lnEmid; - - i_dedx = new EGS_Interpolator [n_media];// stp powers - dedx_i = new EGS_Interpolator [n_media];// its inverse - for( int j = 0; j < n_media; j++ ){ - /* Gets charged partcle stopping powers based on the scoring charge */ - i_dedx[j] = *( app->getDEDX( j, scoring_charge ) ); - EGS_Float Emin = i_dedx[j].getXmin(); - EGS_Float Emax = i_dedx[j].getXmax(); - int n = 1 + i_dedx[j].getIndex(Emax);// getIndex returns lower bin limit? - EGS_Float bwidth = (Emax - Emin)/n; -#ifdef DEBUG - egsInformation("---> stpwr in %s : Emin=%g Emax=%g n=%d bw=%g\n", - app->getMediumName(j), exp(Emin), exp(Emax), n, bwidth); -#endif - int nbins = n + 1; - EGS_Float spwr_i[nbins]; - for (int k = 0; k < nbins; k++ ){ - spwr_i[k] = 1.0 / i_dedx[j].interpolate(Emin+k*bwidth); - } - dedx_i[j].initialize(nbins, Emin, Emax, spwr_i); -#ifdef DEBUG - Emin = dedx_i[j].getXmin(); - Emax = dedx_i[j].getXmax(); - n = 1 + dedx_i[j].getIndex(Emax);// getIndex returns lower bin limit? - bwidth = (Emax - Emin)/n; - egsInformation("---> 1/stpwr in %s : lnEmin=%g lnEmax=%g n=%d bw=%g index(Emax)=%d\n", - app->getMediumName(j), Emin, Emax, n, bwidth, dedx_i[j].getIndexFast(Emax)); - for (int k = 0; k < nbins; k++ ){ - egsInformation(" L(%g MeV) = %g MeV/cm 1/L = %g cm/MeV\n", - exp(Emin+k*bwidth), - i_dedx[j].interpolate(Emin + k*bwidth), - dedx_i[j].interpolate(Emin + k*bwidth)); - } -#endif - } - - /* Determine stpwr at middle of each fluence scoring bin */ - lnEmin = flu_s ? log(0.5*flu_Emin*(expbw+1)) : 0; - Lmid_i = new EGS_Float [flu_nbin*n_media]; - for( int j = 0; j < n_media; j++ ){ -#ifdef DEBUG - EGS_Float med_max_step = 0, logEmax, logEmin; -#endif - for ( int i = 0; i < flu_nbin; i++ ){ - lnEmid = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*(i+0.5)); - Lmid_i[i+j*flu_nbin] = 1/i_dedx[j].interpolate(lnEmid); - //egsInformation(" 1/L(%g MeV) = %g cm/MeV\n",exp(lnEmid),Lmid_i[i+j*flu_nbin]); -#ifdef DEBUG - // Set max_step to maximum range - if ( i > 0){ - logEmin = flu_s ? lnEmin + (i-1)*bw : log(flu_Emin+bw*(i-1)); - logEmax = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*i); - if (i_dedx[j].interpolate(logEmax) < i_dedx[j].interpolate(logEmin)) - med_max_step += 1.02*(exp(logEmax) - exp(logEmin))/i_dedx[j].interpolate(logEmax); - else - med_max_step += 1.02*(exp(logEmax) - exp(logEmin))*i_dedx[j].interpolate(logEmin); + if (scoring_charge) { + EGS_Float expbw; + /* Pre-calculated values for faster evaluation on log scale */ + if (flu_s) { + expbw = exp(bw); // => (Emax/Emin)^(1/nbin) + r_const = 1/(expbw-1); + DE = new EGS_Float [flu_nbin]; + a_const = new EGS_Float [flu_nbin]; + for (int i = 0; i < flu_nbin; i++) { + DE[i] = flu_Emin*pow(expbw,i)*(expbw-1); + a_const[i] = 1/flu_Emin*pow(1/expbw,i); + } + } + /* Do not score below ECUT - PRM */ + if (flu_Emin < app->getEcut() - app->getRM()) { + flu_Emin = app->getEcut() - app->getRM() ; + /* Decrease number of bins, preserve bin width */ + flu_nbin = flu_s ? + ceil((log(flu_Emax / flu_Emin))/bw) : + ceil((flu_Emax - flu_Emin) /bw); + } + + /* Pre-calculated values for faster 1/stpwr evaluation */ + if (flu_stpwr) { + + int n_media = app->getnMedia(); + + EGS_Float lnE, lnEmin, lnEmax, lnEmid; + + i_dedx = new EGS_Interpolator [n_media];// stp powers + dedx_i = new EGS_Interpolator [n_media];// its inverse + for (int j = 0; j < n_media; j++) { + /* Gets charged partcle stopping powers based on the scoring charge */ + i_dedx[j] = *(app->getDEDX(j, scoring_charge)); + EGS_Float Emin = i_dedx[j].getXmin(); + EGS_Float Emax = i_dedx[j].getXmax(); + int n = 1 + i_dedx[j].getIndex(Emax);// getIndex returns lower bin limit? + EGS_Float bwidth = (Emax - Emin)/n; +#ifdef DEBUG + egsInformation("---> stpwr in %s : Emin=%g Emax=%g n=%d bw=%g\n", + app->getMediumName(j), exp(Emin), exp(Emax), n, bwidth); +#endif + int nbins = n + 1; + EGS_Float spwr_i[nbins]; + for (int k = 0; k < nbins; k++) { + spwr_i[k] = 1.0 / i_dedx[j].interpolate(Emin+k*bwidth); + } + dedx_i[j].initialize(nbins, Emin, Emax, spwr_i); +#ifdef DEBUG + Emin = dedx_i[j].getXmin(); + Emax = dedx_i[j].getXmax(); + n = 1 + dedx_i[j].getIndex(Emax);// getIndex returns lower bin limit? + bwidth = (Emax - Emin)/n; + egsInformation("---> 1/stpwr in %s : lnEmin=%g lnEmax=%g n=%d bw=%g index(Emax)=%d\n", + app->getMediumName(j), Emin, Emax, n, bwidth, dedx_i[j].getIndexFast(Emax)); + for (int k = 0; k < nbins; k++) { + egsInformation(" L(%g MeV) = %g MeV/cm 1/L = %g cm/MeV\n", + exp(Emin+k*bwidth), + i_dedx[j].interpolate(Emin + k*bwidth), + dedx_i[j].interpolate(Emin + k*bwidth)); } -#endif +#endif } -#ifdef DEBUG - if ( med_max_step > max_step ){ - max_step = med_max_step; - imed_max_range = j; + + /* Determine stpwr at middle of each fluence scoring bin */ + lnEmin = flu_s ? log(0.5*flu_Emin*(expbw+1)) : 0; + Lmid_i = new EGS_Float [flu_nbin*n_media]; + for (int j = 0; j < n_media; j++) { +#ifdef DEBUG + EGS_Float med_max_step = 0, logEmax, logEmin; +#endif + for (int i = 0; i < flu_nbin; i++) { + lnEmid = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*(i+0.5)); + Lmid_i[i+j*flu_nbin] = 1/i_dedx[j].interpolate(lnEmid); + //egsInformation(" 1/L(%g MeV) = %g cm/MeV\n",exp(lnEmid),Lmid_i[i+j*flu_nbin]); +#ifdef DEBUG + // Set max_step to maximum range + if (i > 0) { + logEmin = flu_s ? lnEmin + (i-1)*bw : log(flu_Emin+bw*(i-1)); + logEmax = flu_s ? lnEmin + i*bw : log(flu_Emin+bw*i); + if (i_dedx[j].interpolate(logEmax) < i_dedx[j].interpolate(logEmin)) { + med_max_step += 1.02*(exp(logEmax) - exp(logEmin))/i_dedx[j].interpolate(logEmax); + } + else { + med_max_step += 1.02*(exp(logEmax) - exp(logEmin))*i_dedx[j].interpolate(logEmin); + } + } +#endif + } +#ifdef DEBUG + if (med_max_step > max_step) { + max_step = med_max_step; + imed_max_range = j; + } +#endif } -#endif - } - } + } } -#ifdef DEBUG +#ifdef DEBUG EGS_Float RCSDA = max_step; //max_step /= 4.0; // Set to a quarter of the CSDA range //if ( max_step > 1.0 ) max_step = 1.0; @@ -1161,13 +1302,13 @@ void EGS_VolumetricFluence::setApplication(EGS_Application *App) { stepDist = new EGS_ScoringArray(n_step_bins); eCases = 0; egsInformation("\n===> RCSDA(%s) = %g cm for Emax = %g MeV, max_step = %g cm bin width = %g cm\n", - app->getMediumName(imed_max_range), RCSDA, - flu_s ? exp(flu_Emax) : flu_Emax, max_step, 1.0/step_a); + app->getMediumName(imed_max_range), RCSDA, + flu_s ? exp(flu_Emax) : flu_Emax, max_step, 1.0/step_a); #endif describeMe(); } -/* +/* Takes user inputs and sets up simulation parameters not requiring invoking an application method. This is later done in setApplication. */ @@ -1175,15 +1316,16 @@ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { EGS_Input *vScoringInput; - if( !inp ) { - egsFatal("AO type %s: null input?\n",otype.c_str()); return; + if (!inp) { + egsFatal("AO type %s: null input?\n",otype.c_str()); + return; } - else{ - vScoringInput = inp->takeInputItem("volumetric scoring"); - if( ! vScoringInput ){ - egsFatal("AO type %s: Missing volumetric scoring input?\n",otype.c_str()); - return; - } + else { + vScoringInput = inp->takeInputItem("volumetric scoring"); + if (! vScoringInput) { + egsFatal("AO type %s: Missing volumetric scoring input?\n",otype.c_str()); + return; + } } EGS_FluenceScoring::initScoring(inp);// common inputs @@ -1211,25 +1353,26 @@ void EGS_VolumetricFluence::initScoring(EGS_Input *inp) { } } } - else if ( v_in.size() ) { + else if (v_in.size()) { vol_list = v_in; } - else{ + else { vol_list.push_back(1.0); } /* Initialize data required to score charged particle fluence */ - if ( scoring_charge ){ - vector method; - method.push_back("flurz"); method.push_back("stpwr"); // 3rd order - method.push_back("stpwrO5"); // 5th order - flu_stpwr = eFluType(vScoringInput->getInput("method",method,1)); + if (scoring_charge) { + vector method; + method.push_back("flurz"); + method.push_back("stpwr"); // 3rd order + method.push_back("stpwrO5"); // 5th order + flu_stpwr = eFluType(vScoringInput->getInput("method",method,1)); } } -void EGS_VolumetricFluence::describeMe(){ +void EGS_VolumetricFluence::describeMe() { char buf[128]; sprintf(buf,"\nVolumetric %s fluence scoring\n",particle_name.c_str()); description = buf; @@ -1239,18 +1382,19 @@ void EGS_VolumetricFluence::describeMe(){ EGS_FluenceScoring::describeMe(); - if (flu_stpwr){ - if (flu_stpwr == stpwr){ - description += " O(eps^3) approach: accounts for change in stpwr\n"; - description += " along the step with eps=edep/Emid\n"; - } - else if (flu_stpwr == stpwrO5){ - description += " O(eps^5) approach: accounts for change in stpwr\n"; - description += " along the step with eps=edep/Emid\n"; - } + if (flu_stpwr) { + if (flu_stpwr == stpwr) { + description += " O(eps^3) approach: accounts for change in stpwr\n"; + description += " along the step with eps=edep/Emid\n"; + } + else if (flu_stpwr == stpwrO5) { + description += " O(eps^5) approach: accounts for change in stpwr\n"; + description += " along the step with eps=edep/Emid\n"; + } + } + else { + description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n"; } - else - description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n"; if (norm_u != 1.0D+0) { description += "\n - Non-unity user-requested normalization = "; @@ -1260,217 +1404,239 @@ void EGS_VolumetricFluence::describeMe(){ } -void EGS_VolumetricFluence::ouputVolumetricFluence( EGS_ScoringArray *fT, const double &norma ){ +void EGS_VolumetricFluence::ouputVolumetricFluence(EGS_ScoringArray *fT, const double &norma) { double fe,dfe,dfer; double norm = norma; int count = 0; int ir_digits = getDigits(nreg); egsInformation("\n region# Flu/(MeV*cm2) DFlu/(MeV*cm2)\n" - "-----------------------------------------------------\n"); - for( int k=0; kcurrentResult(k,fe,dfe); - if( fe > 0 ) dfer = 100*dfe/fe; else dfer = 100; - egsInformation(" %10.4le +/- %10.4le [%-7.3lf%]\n",fe*norm,dfe*norm,dfer); + "-----------------------------------------------------\n"); + for (int k=0; kcurrentResult(k,fe,dfe); + if (fe > 0) { + dfer = 100*dfe/fe; + } + else { + dfer = 100; + } + egsInformation(" %10.4le +/- %10.4le [%-7.3lf%]\n",fe*norm,dfe*norm,dfer); } } -void EGS_VolumetricFluence::ouputResults(){ - - - EGS_Float src_norm = 1.0, // default to number of histories in this run - Fsrc = app->getFluence();// Fluence or number of primary histories - egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", - current_ncase, Fsrc); - - if (Fsrc) - src_norm = Fsrc/current_ncase;// fluence or primary histories per histories run - - string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; - string src_type = app->sourceType(); - if ( src_type == "EGS_BeamSource" ){ - normLabel = "primary history"; - egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", - src_type.c_str(), src_norm); - } - else if ( src_type == "EGS_CollimatedSource" || - (src_type == "EGS_ParallelBeam" && src_norm != 1)){ - egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", - src_type.c_str(), src_norm); - } - else{ +void EGS_VolumetricFluence::ouputResults() { + + + EGS_Float src_norm = 1.0, // default to number of histories in this run + Fsrc = app->getFluence();// Fluence or number of primary histories + egsInformation("\n\n last case = %lld source particles or fluence = %g\n\n", + current_ncase, Fsrc); + + if (Fsrc) { + src_norm = Fsrc/current_ncase; // fluence or primary histories per histories run + } + + string normLabel = src_norm == 1 ? "history" : "MeV-1 cm-2"; + string src_type = app->sourceType(); + if (src_type == "EGS_BeamSource") { + normLabel = "primary history"; + egsInformation("\n\n %s normalization = %g (primary histories per particle)\n\n", + src_type.c_str(), src_norm); + } + else if (src_type == "EGS_CollimatedSource" || + (src_type == "EGS_ParallelBeam" && src_norm != 1)) { + egsInformation("\n\n %s normalization = %g (fluence per particle)\n\n", + src_type.c_str(), src_norm); + } + else { egsInformation("\n\n %s normalization = %g (histories per particle)\n\n", - src_type.c_str(), src_norm); + src_type.c_str(), src_norm); - } + } #ifdef DEBUG - EGS_Float fbins, d_fbins; - egsInformation("\nNumber of covered bins distribution\n" - "--------------------------------------\n"); - - int tot_bins = one_bin + multi_bin; - egsInformation("\none_bin = %d [%-7.3lf\%] multi_bin = %d [%-7.3lf\%]\n", - one_bin, 100.0*one_bin/tot_bins, multi_bin,100.0*multi_bin/tot_bins); - - egsInformation("\n # bins freq unc percentage \n"); - int bdigits = getDigits(flu_nbin); - for (int i=0; icurrentResult(i,fbins, d_fbins); - if (fbins){ - d_fbins = 100.0*d_fbins/fbins; fbins = current_ncase*fbins; - egsInformation(" %*d %11.2f [%-7.3lf\%] %11.2f \%\n", - bdigits, i+1, fbins, d_fbins, 100.*fbins/tot_bins); - } - } - - if ( scoring_charge ){ - egsInformation("\nDistribution of ratio between computed and taken steps\n" - "------------------------------------------------------\n" - " (Omitted bins with differences less than 0.01\%)\n"); - /* - //egsInformation(" Relative step difference: %8.4f%% +/- %8.4f%% [%8.4lf%%]\n", - //egsInformation(" Relative step ratio: %10.4le +/- %10.4le [%8.4lf%%]\n", - */ - - EGS_Float step_diff, step_diff_std, step_diff_err, stepMid; - EGS_Float f_scores, f_scores_std; - int idigits = getDigits(eCases); - egsInformation("\n step / cm # scores ratio rel unc\n" - "---------------------------------------------------------\n"); - for (int i=0; i < n_step_bins; i++) { - stepDist->currentResult( i, f_scores, f_scores_std ); - if ( f_scores > 0 ){ - stepMid = (i + 0.5 - step_b)/step_a; - relStepDiff->currentResult( i, step_diff, step_diff_std ); - if( step_diff > 0 ) - step_diff_err = 100*step_diff_std/step_diff; - else - step_diff_err = 100; - if ( abs(step_diff/f_scores - 1.0) > 0.0001D+0 ) - egsInformation(" %10.4le %*d %10.4le [%8.4lf\%]\n", - stepMid, idigits, int(f_scores*current_ncase), step_diff/f_scores, step_diff_err); - } - } - - } + EGS_Float fbins, d_fbins; + egsInformation("\nNumber of covered bins distribution\n" + "--------------------------------------\n"); + + int tot_bins = one_bin + multi_bin; + egsInformation("\none_bin = %d [%-7.3lf\%] multi_bin = %d [%-7.3lf\%]\n", + one_bin, 100.0*one_bin/tot_bins, multi_bin,100.0*multi_bin/tot_bins); + + egsInformation("\n # bins freq unc percentage \n"); + int bdigits = getDigits(flu_nbin); + for (int i=0; icurrentResult(i,fbins, d_fbins); + if (fbins) { + d_fbins = 100.0*d_fbins/fbins; + fbins = current_ncase*fbins; + egsInformation(" %*d %11.2f [%-7.3lf\%] %11.2f \%\n", + bdigits, i+1, fbins, d_fbins, 100.*fbins/tot_bins); + } + } + + if (scoring_charge) { + egsInformation("\nDistribution of ratio between computed and taken steps\n" + "------------------------------------------------------\n" + " (Omitted bins with differences less than 0.01\%)\n"); + /* + //egsInformation(" Relative step difference: %8.4f%% +/- %8.4f%% [%8.4lf%%]\n", + //egsInformation(" Relative step ratio: %10.4le +/- %10.4le [%8.4lf%%]\n", + */ + + EGS_Float step_diff, step_diff_std, step_diff_err, stepMid; + EGS_Float f_scores, f_scores_std; + int idigits = getDigits(eCases); + egsInformation("\n step / cm # scores ratio rel unc\n" + "---------------------------------------------------------\n"); + for (int i=0; i < n_step_bins; i++) { + stepDist->currentResult(i, f_scores, f_scores_std); + if (f_scores > 0) { + stepMid = (i + 0.5 - step_b)/step_a; + relStepDiff->currentResult(i, step_diff, step_diff_std); + if (step_diff > 0) { + step_diff_err = 100*step_diff_std/step_diff; + } + else { + step_diff_err = 100; + } + if (abs(step_diff/f_scores - 1.0) > 0.0001D+0) + egsInformation(" %10.4le %*d %10.4le [%8.4lf\%]\n", + stepMid, idigits, int(f_scores*current_ncase), step_diff/f_scores, step_diff_err); + } + } + + } #endif - EGS_Float norm = 1.0/src_norm; // per particle or fluence - norm *= norm_u; // user-requested normalization - - egsInformation("\n\n Integral fluence output\n" - " =======================\n\n"); - - egsInformation("\n\n Total %s fluence\n", particle_name.c_str()); - ouputVolumetricFluence( fluT, norm ); - - if ( score_primaries ){ - egsInformation("\n\n Primary fluence\n"); - ouputVolumetricFluence( fluT_p, norm ); - } - - if (verbose){ - egsInformation("\nbw = %g nbins = %d\n", flu_a_i, flu_nbin); - } - - if ( score_spe ){ - string suffix = "_" + particle_name + ".agr"; - string spe_name = app->constructIOFileName(suffix.c_str(),true); - ofstream spe_output(spe_name.c_str(),ios::out); - if (!spe_output){ - egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str()); - exit(1); - } - - spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n"; - spe_output << "# \n"; - spe_output << "@ legend 0.2, 0.8\n"; - spe_output << "@ legend box linestyle 0\n"; - spe_output << "@ legend font 4\n"; - spe_output << "@ xaxis label \"energy / MeV\"\n"; - spe_output << "@ xaxis label char size 1.560000\n"; - spe_output << "@ xaxis label font 4\n"; - spe_output << "@ xaxis ticklabel font 4\n"; - if (src_norm == 1 || normLabel == "primary history") - spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; - else{ - spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n"; - } - spe_output << "@ yaxis label char size 1.560000\n"; - spe_output << "@ yaxis label font 4\n"; - spe_output << "@ yaxis ticklabel font 4\n"; - spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; - spe_output << "@ title font 4\n"; - spe_output << "@ title size 1.500000\n"; - spe_output << "@ subtitle \"for each scoring region\"\n"; - spe_output << "@ subtitle font 4\n"; - spe_output << "@ subtitle size 1.000000\n"; - - if ( verbose ) - egsInformation("\n\n Differential fluence output\n" + EGS_Float norm = 1.0/src_norm; // per particle or fluence + norm *= norm_u; // user-requested normalization + + egsInformation("\n\n Integral fluence output\n" + " =======================\n\n"); + + egsInformation("\n\n Total %s fluence\n", particle_name.c_str()); + ouputVolumetricFluence(fluT, norm); + + if (score_primaries) { + egsInformation("\n\n Primary fluence\n"); + ouputVolumetricFluence(fluT_p, norm); + } + + if (verbose) { + egsInformation("\nbw = %g nbins = %d\n", flu_a_i, flu_nbin); + } + + if (score_spe) { + string suffix = "_" + particle_name + ".agr"; + string spe_name = app->constructIOFileName(suffix.c_str(),true); + ofstream spe_output(spe_name.c_str(),ios::out); + if (!spe_output) { + egsFatal("\n EGS_VolumetricFluence: Error: Failed to open file %s\n",spe_name.c_str()); + exit(1); + } + + spe_output << "# Volumetric " << particle_name.c_str() << " fluence \n"; + spe_output << "# \n"; + spe_output << "@ legend 0.2, 0.8\n"; + spe_output << "@ legend box linestyle 0\n"; + spe_output << "@ legend font 4\n"; + spe_output << "@ xaxis label \"energy / MeV\"\n"; + spe_output << "@ xaxis label char size 1.560000\n"; + spe_output << "@ xaxis label font 4\n"; + spe_output << "@ xaxis ticklabel font 4\n"; + if (src_norm == 1 || normLabel == "primary history") { + spe_output << "@ yaxis label \"fluence / MeV\\S-1\\Ncm\\S-2\"\n"; + } + else { + spe_output << "@ yaxis label \"fluence / MeV\\S-1\"\n"; + } + spe_output << "@ yaxis label char size 1.560000\n"; + spe_output << "@ yaxis label font 4\n"; + spe_output << "@ yaxis ticklabel font 4\n"; + spe_output << "@ title \""<< particle_name.c_str() << " fluence" <<"\"\n"; + spe_output << "@ title font 4\n"; + spe_output << "@ title size 1.500000\n"; + spe_output << "@ subtitle \"for each scoring region\"\n"; + spe_output << "@ subtitle font 4\n"; + spe_output << "@ subtitle size 1.000000\n"; + + if (verbose) + egsInformation("\n\n Differential fluence output\n" " =============================\n\n"); - int i_graph = 0; - double fe, dfe; - norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! - // Loop through the number of regions in the geometry. - for (int j = 0; j < nreg; j++) { - - if ( !is_sensitive[j] ) continue; - - norm /= volume[j]; //per volume - - egsInformation("region # %d : ",j); - - if (verbose){ - egsInformation("Volume[%d] = %g", j, volume[j]); - egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); - } - - if (verbose) egsInformation("\nTotal fluence:\n"); - spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; - spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; - spe_output<<"@target G0.S"<< i_graph <<"\n"; - spe_output<<"@type xydy\n"; - if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" - "---------------------------------------------------\n"); - for (int i=0; icurrentResult(i,fe,dfe); - EGS_Float e = (i+0.5-flu_b)/flu_a; - if (flu_s) e = exp(e); - spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; - if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", - e, fe*norm, dfe*norm); - } - spe_output << "&\n"; - - if ( score_primaries ){ - if (verbose) egsInformation("\nPrimary fluence:\n"); - spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; - spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; + int i_graph = 0; + double fe, dfe; + norm *= scoring_charge ? 1 : flu_a;//per bin width <- implicit for charged particles! + // Loop through the number of regions in the geometry. + for (int j = 0; j < nreg; j++) { + + if (!is_sensitive[j]) { + continue; + } + + norm /= volume[j]; //per volume + + egsInformation("region # %d : ",j); + + if (verbose) { + egsInformation("Volume[%d] = %g", j, volume[j]); + egsInformation(" Normalization = Ncase/Fsrc/V = %g\n",norm); + } + + if (verbose) { + egsInformation("\nTotal fluence:\n"); + } + spe_output<<"@ s"<< i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "total (ir # " << j <<")\"\n"; spe_output<<"@target G0.S"<< i_graph <<"\n"; spe_output<<"@type xydy\n"; if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" - "---------------------------------------------------\n"); + "---------------------------------------------------\n"); for (int i=0; icurrentResult(i,fe,dfe); + flu[j]->currentResult(i,fe,dfe); EGS_Float e = (i+0.5-flu_b)/flu_a; - if (flu_s) e = exp(e); + if (flu_s) { + e = exp(e); + } spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", - e, fe*norm, dfe*norm); + e, fe*norm, dfe*norm); } spe_output << "&\n"; - } - i_graph++; - } - spe_output.close(); - - } + if (score_primaries) { + if (verbose) { + egsInformation("\nPrimary fluence:\n"); + } + spe_output<<"@ s"<< ++i_graph <<" errorbar linestyle 0\n"; + spe_output<<"@ s"<< i_graph <<" legend \""<< "primary (ir # " << j <<")\"\n"; + spe_output<<"@target G0.S"<< i_graph <<"\n"; + spe_output<<"@type xydy\n"; + if (verbose) egsInformation("\n Emid/MeV Flu/(MeV-1*cm-2) DFlu/(MeV-1*cm-2)\n" + "---------------------------------------------------\n"); + for (int i=0; icurrentResult(i,fe,dfe); + EGS_Float e = (i+0.5-flu_b)/flu_a; + if (flu_s) { + e = exp(e); + } + spe_output << e <<" "<< fe *norm<<" "<< dfe *norm<< "\n"; + if (verbose) egsInformation("%11.6f %14.6e %14.6e\n", + e, fe*norm, dfe*norm); + } + spe_output << "&\n"; + } + i_graph++; + } + + spe_output.close(); + + } } @@ -1480,155 +1646,203 @@ void EGS_VolumetricFluence::reportResults() { egsInformation("======================================================\n"); ouputResults(); - + } bool EGS_VolumetricFluence::storeState(ostream &data) const { - if( !egsStoreI64(data,current_ncase)) return false; - data << endl; + if (!egsStoreI64(data,current_ncase)) { + return false; + } + data << endl; - if (!data.good()) return false; + if (!data.good()) { + return false; + } #ifdef DEBUG - if ( scoring_charge ){ - if( !relStepDiff->storeState(data) ) return false; - if( !stepDist->storeState(data) ) return false; - } + if (scoring_charge) { + if (!relStepDiff->storeState(data)) { + return false; + } + if (!stepDist->storeState(data)) { + return false; + } + } #endif - if( !fluT->storeState(data) ) return false; - if( score_spe ) { - for(int j = 0; j < nreg; j++) { - if ( is_sensitive[j] ){ - if( !flu[j]->storeState(data) ) return false; - } - } - } - - if ( score_primaries ){ - if( !fluT_p->storeState(data) ) return false; - if( score_spe ) { - for(int j=0; j < nreg; j++) { - if ( is_sensitive[j] ){ - if( !flu_p[j]->storeState(data) ) return false; + if (!fluT->storeState(data)) { + return false; + } + if (score_spe) { + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + if (!flu[j]->storeState(data)) { + return false; + } } - } - } - } + } + } + + if (score_primaries) { + if (!fluT_p->storeState(data)) { + return false; + } + if (score_spe) { + for (int j=0; j < nreg; j++) { + if (is_sensitive[j]) { + if (!flu_p[j]->storeState(data)) { + return false; + } + } + } + } + } - return true; + return true; } -bool EGS_VolumetricFluence::setState(istream &data){ - - if( !egsGetI64(data,current_ncase) ) return false; +bool EGS_VolumetricFluence::setState(istream &data) { - if (!data.good()) return false; + if (!egsGetI64(data,current_ncase)) { + return false; + } + + if (!data.good()) { + return false; + } #ifdef DEBUG - if ( scoring_charge ){ - if( !relStepDiff->setState(data) ) return false; - if( !stepDist->setState(data) ) return false; - } + if (scoring_charge) { + if (!relStepDiff->setState(data)) { + return false; + } + if (!stepDist->setState(data)) { + return false; + } + } #endif - if( !fluT->setState(data) ) return false; - if( score_spe ) { - for(int j=0; jsetState(data) ) return false; - } - } - } - - if ( score_primaries ){ - if( !fluT_p->setState(data) ) return false; - if( score_spe ) { - for(int j=0; j < nreg; j++) { - if ( is_sensitive[j] ){ - if( !flu_p[j]->setState(data) ) return false; + if (!fluT->setState(data)) { + return false; + } + if (score_spe) { + for (int j=0; jsetState(data)) { + return false; + } } - } - } - } + } + } - return true; + if (score_primaries) { + if (!fluT_p->setState(data)) { + return false; + } + if (score_spe) { + for (int j=0; j < nreg; j++) { + if (is_sensitive[j]) { + if (!flu_p[j]->setState(data)) { + return false; + } + } + } + } + } + + return true; } -bool EGS_VolumetricFluence::addState(istream &data){ - EGS_I64 tmp_case; if( !egsGetI64(data,tmp_case) ) return false; - current_ncase += tmp_case; +bool EGS_VolumetricFluence::addState(istream &data) { + EGS_I64 tmp_case; + if (!egsGetI64(data,tmp_case)) { + return false; + } + current_ncase += tmp_case; - if (!data.good()) return false; + if (!data.good()) { + return false; + } #ifdef DEBUG - if ( scoring_charge ){ - EGS_ScoringArray tmpRelStepDiff(1); - if( !tmpRelStepDiff.setState(data) ) return false; - (*relStepDiff) += tmpRelStepDiff; - } + if (scoring_charge) { + EGS_ScoringArray tmpRelStepDiff(1); + if (!tmpRelStepDiff.setState(data)) { + return false; + } + (*relStepDiff) += tmpRelStepDiff; + } #endif - /* fluence objects */ - - EGS_ScoringArray tgT(nreg); - if( !tgT.setState(data) ) return false; - (*fluT) += tgT; - - if( score_spe ) { - EGS_ScoringArray tg(flu_nbin); - for(int j = 0; j < nreg; j++) { - if ( is_sensitive[j] ){ - if( !tg.setState(data) ) return false; - (*flu[j]) += tg; - } - } - } - - if ( score_primaries ){ - EGS_ScoringArray tgT_p(nreg); - if( !tgT_p.setState(data) ) return false; - (*fluT_p) += tgT_p; - if( score_spe ) { - EGS_ScoringArray tg_p(flu_nbin); - for(int j=0; j < nreg; j++) { - if ( is_sensitive[j] ){ - if( !tg_p.setState(data) ) return false; - (*flu_p[j]) += tg_p; - } - } - } - } - - return true; + /* fluence objects */ + + EGS_ScoringArray tgT(nreg); + if (!tgT.setState(data)) { + return false; + } + (*fluT) += tgT; + + if (score_spe) { + EGS_ScoringArray tg(flu_nbin); + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + if (!tg.setState(data)) { + return false; + } + (*flu[j]) += tg; + } + } + } + + if (score_primaries) { + EGS_ScoringArray tgT_p(nreg); + if (!tgT_p.setState(data)) { + return false; + } + (*fluT_p) += tgT_p; + if (score_spe) { + EGS_ScoringArray tg_p(flu_nbin); + for (int j=0; j < nreg; j++) { + if (is_sensitive[j]) { + if (!tg_p.setState(data)) { + return false; + } + (*flu_p[j]) += tg_p; + } + } + } + } + + return true; } extern "C" { - EGS_FLUENCE_SCORING_EXPORT EGS_AusgabObject* - createAusgabObject(EGS_Input *input, EGS_ObjectFactory *f) { - const static char *func = "createAusgabObject(fluence_scoring)"; - if( !input ) { - egsWarning("%s: null input?\n",func); return 0; - } - - string type; - int error = input->getInput("type",type); - if ( !error && input->compare("planar",type) ) { - EGS_PlanarFluence *result = new EGS_PlanarFluence("", f); - result->setName(input); - result->initScoring(input); - return result; - } - else if ( !error && input->compare("volumetric",type) ){ - EGS_VolumetricFluence *result = new EGS_VolumetricFluence("", f); - result->setName(input); - result->initScoring(input); - return result; - } - else{ - egsFatal("Invalid fluence type input?\n\n\n"); - return 0; - } - } + EGS_FLUENCE_SCORING_EXPORT EGS_AusgabObject * + createAusgabObject(EGS_Input *input, EGS_ObjectFactory *f) { + const static char *func = "createAusgabObject(fluence_scoring)"; + if (!input) { + egsWarning("%s: null input?\n",func); + return 0; + } + + string type; + int error = input->getInput("type",type); + if (!error && input->compare("planar",type)) { + EGS_PlanarFluence *result = new EGS_PlanarFluence("", f); + result->setName(input); + result->initScoring(input); + return result; + } + else if (!error && input->compare("volumetric",type)) { + EGS_VolumetricFluence *result = new EGS_VolumetricFluence("", f); + result->setName(input); + result->initScoring(input); + return result; + } + else { + egsFatal("Invalid fluence type input?\n\n\n"); + return 0; + } + } } diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index d73bd5f20..08c256cad 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -23,22 +23,22 @@ # # Author: Ernesto Mainegra-Hing, 2022 # -# Contributors: +# Contributors: # ############################################################################### -# -# Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at -# circular or rectangular scoring fields located anywhere in space for a specific -# particle type. Fluence can be scored for multiple particle types by definining -# different AOs. An option exists for scoring the fluence of primary particles. -# Classification into primary and secondary particles follows the definition used +# +# Ausgab objects (AOs) for fluence scoring in arbitrary geometrical regions or at +# circular or rectangular scoring fields located anywhere in space for a specific +# particle type. Fluence can be scored for multiple particle types by definining +# different AOs. An option exists for scoring the fluence of primary particles. +# Classification into primary and secondary particles follows the definition used # in FLURZnrc for IPRIMARY = 2 (primaries) and IPRIMARY = 4 (secondaries). # -############################################################################### +############################################################################### */ /*! \file egs_fluence_scoring.h - * \brief A fluence scoring object : header + * \brief A fluence scoring object : header * \EM */ @@ -56,22 +56,22 @@ using namespace std; #ifdef WIN32 -#ifdef BUILD_FLUENCE_SCORING_DLL -#define EGS_FLUENCE_SCORING_EXPORT __declspec(dllexport) -#else -#define EGS_FLUENCE_SCORING_EXPORT __declspec(dllimport) -#endif -#define EGS_FLUENCE_SCORING_LOCAL + #ifdef BUILD_FLUENCE_SCORING_DLL + #define EGS_FLUENCE_SCORING_EXPORT __declspec(dllexport) + #else + #define EGS_FLUENCE_SCORING_EXPORT __declspec(dllimport) + #endif + #define EGS_FLUENCE_SCORING_LOCAL #else -#ifdef HAVE_VISIBILITY -#define EGS_FLUENCE_SCORING_EXPORT __attribute__ ((visibility ("default"))) -#define EGS_FLUENCE_SCORING_LOCAL __attribute__ ((visibility ("hidden"))) -#else -#define EGS_FLUENCE_SCORING_EXPORT -#define EGS_FLUENCE_SCORING_LOCAL -#endif + #ifdef HAVE_VISIBILITY + #define EGS_FLUENCE_SCORING_EXPORT __attribute__ ((visibility ("default"))) + #define EGS_FLUENCE_SCORING_LOCAL __attribute__ ((visibility ("hidden"))) + #else + #define EGS_FLUENCE_SCORING_EXPORT + #define EGS_FLUENCE_SCORING_LOCAL + #endif #endif @@ -84,12 +84,12 @@ enum ParticleType { electron = -1, photon = 0, positron = 1, unknown = -99 }; /*! Charged particle fluence calculation type */ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; -/*! \brief Base class for fluence scoring. +/*! \brief Base class for fluence scoring. \ingroup AusgabObjects Contains the basic ingredients for fluence scoring such as energy grid, - particle type, scoring regions, and whether to score primary fluence. + particle type, scoring regions, and whether to score primary fluence. Provides the method for defining primary and secondary particles. \todo Account for multiple app geometries @@ -98,156 +98,160 @@ enum eFluType { flurz=0, stpwr=1, stpwrO5=2 }; class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { public: - /*! Constructors */ - EGS_FluenceScoring(const string &Name="", EGS_ObjectFactory *f = 0); - /*! Destructor. */ - ~EGS_FluenceScoring(); + /*! Constructors */ + EGS_FluenceScoring(const string &Name="", EGS_ObjectFactory *f = 0); + /*! Destructor. */ + ~EGS_FluenceScoring(); - void initScoring(EGS_Input *inp); + void initScoring(EGS_Input *inp); - void getSensitiveRegions(EGS_Input *inp); + void getSensitiveRegions(EGS_Input *inp); - void getNumberRegions( const string &str, vector ®s ); + void getNumberRegions(const string &str, vector ®s); - void getLabelRegions( const string &str, vector ®s ); + void getLabelRegions(const string &str, vector ®s); - void setUpRegionFlags(); + void setUpRegionFlags(); - void describeMe(); + void describeMe(); - int getDigits(int i) { + int getDigits(int i) { int imax = 10; while (i>=imax) { imax*=10; } return (int)log10((float)imax); - }; - - void flagSecondaries( const int &iarg, const int &q ){ - int npold = app->getNpOld(), - np = app->getNp(); - if ( scoring_charge ){ - /*************************************************************** - DEFAULT: - FLURZnrc IPRIMARY = 2 (primaries) IPRIMARY = 4 (secondaries) - Secondary charged particles defined as those resulting - from charged particle interactions, atomic relaxations - following EII, brems and annihilation photons. - - OPTIONAL: - FLURZnrc IPRIMARY = 1 (e- from brems as primaries) - Set `source particle` to 'photon' in the input file to not flag - brems photons so that when scoring charged particle fluence in - photon beams, first generation e- are primaries. This is - implicit for photon interactions when scoring charged particle - fluence. But one must explicitly account for that during brems events. - ****************************************************************/ - if ( iarg == EGS_Application::AfterBrems || - iarg == EGS_Application::AfterMoller || - iarg == EGS_Application::AfterAnnihFlight || - iarg == EGS_Application::AfterAnnihRest ){ - /************************************************************************ - Skip block below for a photon beam. First generation e- are primaries. - This will apply to ALL brems events in a photon beam simulation. One - could fine tune it to only brems events in certain regions, for instance - a bremsstrahlung target, by using the is_source flag for those regions. - *************************************************************************/ - if (!(iarg == EGS_Application::AfterBrems && source_charge == photon)){ - for(int ip = npold+1; ip <= np; ip++) { + }; + + void flagSecondaries(const int &iarg, const int &q) { + int npold = app->getNpOld(), + np = app->getNp(); + if (scoring_charge) { + /*************************************************************** + DEFAULT: + FLURZnrc IPRIMARY = 2 (primaries) IPRIMARY = 4 (secondaries) + Secondary charged particles defined as those resulting + from charged particle interactions, atomic relaxations + following EII, brems and annihilation photons. + + OPTIONAL: + FLURZnrc IPRIMARY = 1 (e- from brems as primaries) + Set `source particle` to 'photon' in the input file to not flag + brems photons so that when scoring charged particle fluence in + photon beams, first generation e- are primaries. This is + implicit for photon interactions when scoring charged particle + fluence. But one must explicitly account for that during brems events. + ****************************************************************/ + if (iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest) { + /************************************************************************ + Skip block below for a photon beam. First generation e- are primaries. + This will apply to ALL brems events in a photon beam simulation. One + could fine tune it to only brems events in certain regions, for instance + a bremsstrahlung target, by using the is_source flag for those regions. + *************************************************************************/ + if (!(iarg == EGS_Application::AfterBrems && source_charge == photon)) { + for (int ip = npold+1; ip <= np; ip++) { + app->setLatch(ip,1); + } + } + } + else if (iarg == EGS_Application::AfterBhabha) { + if (q == -1) { + app->setLatch(np,1); + } + else { + app->setLatch(np-1,1); + } + } + } + else { + /*************************************************************** + FLURZnrc IPRIMARY = 3 + Flag scattered photons, secondaries, and relaxation + particles as secondaries + ****************************************************************/ + if (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh) { + for (int ip = npold; ip <= np; ip++) { app->setLatch(ip,1); - } - } - } - else if ( iarg == EGS_Application::AfterBhabha ){ - if (q == -1) app->setLatch(np,1); - else app->setLatch(np-1,1); - } - } - else{ - /*************************************************************** - FLURZnrc IPRIMARY = 3 - Flag scattered photons, secondaries, and relaxation - particles as secondaries - ****************************************************************/ - if ( iarg == EGS_Application::AfterPair || - iarg == EGS_Application::AfterCompton || - iarg == EGS_Application::AfterPhoto || - iarg == EGS_Application::AfterRayleigh ){ - for(int ip = npold; ip <= np; ip++) { - app->setLatch(ip,1); - } - } - } - - }; - -protected: - - EGS_I64 current_ncase; - - ParticleType scoring_charge;// charge of scored particles - ParticleType source_charge; // charge of source particles - - /* Fluence Scoring Arrays */ - EGS_ScoringArray **flu; // differential fluence: primaries + secondaries - EGS_ScoringArray **flu_p; // differential fluence: primaries only - EGS_ScoringArray *fluT; // Total fluence: primaries + secondaries - EGS_ScoringArray *fluT_p;// Total fluence: primaries only - - /* Regions flags */ - vector is_sensitive; // flag scoring regions - vector is_source; // Flag regions such as brems target or radiactive source - // Interacting particles not subjected to classification - vector f_start, f_stop; // Markers for group regions input - vector f_region; // Input list of scoring regions - vector s_region; // Input list of source regions - string f_regionsString; // Input string of scoring regions or labels - string s_regionsString; // Input string of source regions or labels - int n_scoring_regions;// number of scoring regions - int n_source_regions; // number of source regions - int nreg; // regions in geometry - int max_reg; // maximum scoring region number - int active_region; // Region showing calculation progress - bool score_in_all_regions; - bool source_in_all_regions; - - EGS_Float norm_u; // User normalization - - /* Energy grid inputs */ - EGS_Float flu_a, flu_a_i, - flu_b, - flu_xmin, - flu_xmax; - int flu_s, - flu_nbin; - /* Classification variables */ - EGS_Float m_primary, - m_tot; - - string particle_name; - /* Auxiliary input variables*/ - bool verbose, - score_spe, - score_primaries; + } + } + } + + }; + +protected: + + EGS_I64 current_ncase; + + ParticleType scoring_charge;// charge of scored particles + ParticleType source_charge; // charge of source particles + + /* Fluence Scoring Arrays */ + EGS_ScoringArray **flu; // differential fluence: primaries + secondaries + EGS_ScoringArray **flu_p; // differential fluence: primaries only + EGS_ScoringArray *fluT; // Total fluence: primaries + secondaries + EGS_ScoringArray *fluT_p;// Total fluence: primaries only + + /* Regions flags */ + vector is_sensitive; // flag scoring regions + vector is_source; // Flag regions such as brems target or radiactive source + // Interacting particles not subjected to classification + vector f_start, f_stop; // Markers for group regions input + vector f_region; // Input list of scoring regions + vector s_region; // Input list of source regions + string f_regionsString; // Input string of scoring regions or labels + string s_regionsString; // Input string of source regions or labels + int n_scoring_regions;// number of scoring regions + int n_source_regions; // number of source regions + int nreg; // regions in geometry + int max_reg; // maximum scoring region number + int active_region; // Region showing calculation progress + bool score_in_all_regions; + bool source_in_all_regions; + + EGS_Float norm_u; // User normalization + + /* Energy grid inputs */ + EGS_Float flu_a, flu_a_i, + flu_b, + flu_xmin, + flu_xmax; + int flu_s, + flu_nbin; + /* Classification variables */ + EGS_Float m_primary, + m_tot; + + string particle_name; + /* Auxiliary input variables*/ + bool verbose, + score_spe, + score_primaries; }; /*! \brief Ausgab object for scoring fluence at circular or rectangular fields \ingroup AusgabObjects - A linear track-length estimator in the zero-thickness limit is used to - compute fluence for either a circular field or a rectangular screen of - arbitrary resolution (pixels). Rectangular fields are by default at Z = 0 + A linear track-length estimator in the zero-thickness limit is used to + compute fluence for either a circular field or a rectangular screen of + arbitrary resolution (pixels). Rectangular fields are by default at Z = 0 directed along the positive z-axis. An affine transofrmation can be included - to place a rectangular field anywhere in space. User can request to score primary - fluence as well as differential fluence. If fluence for more than one particle type - is desired, multiple AOs are required. - - For charged particle fluence scoring this method can be inefficient as + to place a rectangular field anywhere in space. User can request to score primary + fluence as well as differential fluence. If fluence for more than one particle type + is desired, multiple AOs are required. + + For charged particle fluence scoring this method can be inefficient as it checks at every single step whether a charged particle is aimed at scoring field. - To improve the efficieny for charged particles, one has the option to define contributing regions + To improve the efficieny for charged particles, one has the option to define contributing regions from where to score. However, users must be careful to select all regions from where - charged particles can cross the scoring field. This option has the added benefit of + charged particles can cross the scoring field. This option has the added benefit of allowing to estimate the contribution to the fluence from specific regions in the geometry. To define an EGS_PlanarFluence AO use the syntax below: @@ -257,10 +261,10 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { library = egs_fluence_scoring # Library name type = planar # Score on circular or square field scoring particle = photon, or electron, or positron - source particle = photon, or electron, or positron + source particle = photon, or electron, or positron # Optional. Only required to score primary fluence. # Defaults to source particles if all the same. - # In the case of multiple particles, + # In the case of multiple particles, # defaults to scoring particle. Useful for # bremsstrahlung targets and radioactive sources. score primaries = yes or no # Defaults to `no`. @@ -313,146 +317,153 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { public: - /*! Constructors */ - EGS_PlanarFluence(const string &Name="", EGS_ObjectFactory *f = 0); - /*! Destructor. */ - ~EGS_PlanarFluence(); - EGS_Float area(){return Area;}; - bool needsCall(EGS_Application::AusgabCall iarg) const { - if (iarg == EGS_Application::BeforeTransport || - iarg == EGS_Application::AfterTransport ){ - return true; - } - else if ( score_primaries && - (iarg == EGS_Application::AfterPair || - iarg == EGS_Application::AfterCompton || - iarg == EGS_Application::AfterPhoto || - iarg == EGS_Application::AfterRayleigh || - iarg == EGS_Application::AfterBrems || - iarg == EGS_Application::AfterMoller || - iarg == EGS_Application::AfterBhabha || - iarg == EGS_Application::AfterAnnihFlight || - iarg == EGS_Application::AfterAnnihRest )) - { - return true; - } - else { - return false; - } - }; - - inline int hitsField(const EGS_Particle& p, EGS_Float* dist); - inline void score(const EGS_Particle& p, const int& ivoxel); - void describeMe();//!< Sets fluence scoring object \c description - void initScoring(EGS_Input *inp); - void setApplication(EGS_Application *App); - void ouputPlanarFluence( EGS_ScoringArray *fT, const double &norma ); - void ouputResults(); - void reportResults(); - int processEvent(EGS_Application::AusgabCall iarg) { - - int q = app->top_p.q, - ir = app->top_p.ir; - - if ( q == scoring_charge && ir >= 0 && is_sensitive[ir] ) - { - - /* Quantify contribution to scoring field */ - if( iarg == EGS_Application::BeforeTransport ){ - ixy = hitsField(app->top_p,&distance); - if (ixy >= 0){ - x0 = app->top_p.x; hits_field = true; - } - else{hits_field = false; } - } - - if( iarg == EGS_Application::AfterTransport && hits_field ){ - EGS_Vector xstep = app->top_p.x - x0; - if (xstep.length() >= distance){// crossed scoring field - //if (!app->top_p.latch ) m_primary += app->top_p.wt; - m_tot += app->top_p.wt; - score( app->top_p, ixy ); - } - hits_field = false; - } - } - - /******************************************************************** - * Flag secondaries after interactions. Definition of secondaries - * matches FLURZnrc. One could fine tune it by using the is_source - * flag to skip this block in certains regions such as brems targets, - * radioactive sources, etc. - * - * BEWARE: Latch set to 1 (bit 0) to flag secondaries. - * Other applications might use latch for other purposes! - *********************************************************************/ - if ( score_primaries && ir >= 0 && !is_source[ir]){ - flagSecondaries( iarg, q ); - } - - return 0; - - }; - - void setCurrentCase(EGS_I64 ncase) { - if( ncase != current_ncase ) { + /*! Constructors */ + EGS_PlanarFluence(const string &Name="", EGS_ObjectFactory *f = 0); + /*! Destructor. */ + ~EGS_PlanarFluence(); + EGS_Float area() { + return Area; + }; + bool needsCall(EGS_Application::AusgabCall iarg) const { + if (iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::AfterTransport) { + return true; + } + else if (score_primaries && + (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh || + iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterBhabha || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest)) { + return true; + } + else { + return false; + } + }; + + inline int hitsField(const EGS_Particle &p, EGS_Float *dist); + inline void score(const EGS_Particle &p, const int &ivoxel); + void describeMe();//!< Sets fluence scoring object \c description + void initScoring(EGS_Input *inp); + void setApplication(EGS_Application *App); + void ouputPlanarFluence(EGS_ScoringArray *fT, const double &norma); + void ouputResults(); + void reportResults(); + int processEvent(EGS_Application::AusgabCall iarg) { + + int q = app->top_p.q, + ir = app->top_p.ir; + + if (q == scoring_charge && ir >= 0 && is_sensitive[ir]) { + + /* Quantify contribution to scoring field */ + if (iarg == EGS_Application::BeforeTransport) { + ixy = hitsField(app->top_p,&distance); + if (ixy >= 0) { + x0 = app->top_p.x; + hits_field = true; + } + else { + hits_field = false; + } + } + + if (iarg == EGS_Application::AfterTransport && hits_field) { + EGS_Vector xstep = app->top_p.x - x0; + if (xstep.length() >= distance) { // crossed scoring field + //if (!app->top_p.latch ) m_primary += app->top_p.wt; + m_tot += app->top_p.wt; + score(app->top_p, ixy); + } + hits_field = false; + } + } + + /******************************************************************** + * Flag secondaries after interactions. Definition of secondaries + * matches FLURZnrc. One could fine tune it by using the is_source + * flag to skip this block in certains regions such as brems targets, + * radioactive sources, etc. + * + * BEWARE: Latch set to 1 (bit 0) to flag secondaries. + * Other applications might use latch for other purposes! + *********************************************************************/ + if (score_primaries && ir >= 0 && !is_source[ir]) { + flagSecondaries(iarg, q); + } + + return 0; + + }; + + void setCurrentCase(EGS_I64 ncase) { + if (ncase != current_ncase) { current_ncase = ncase; fluT->setHistory(ncase); - if (score_spe){ - for (int j = 0; j < Nx*Ny; j++) - flu[j]->setHistory(ncase); + if (score_spe) { + for (int j = 0; j < Nx*Ny; j++) { + flu[j]->setHistory(ncase); + } } - if ( score_primaries ){ - fluT_p->setHistory(ncase); - if (score_spe){ - for (int j = 0; j < Nx*Ny; j++) - flu_p[j]->setHistory(ncase); - } + if (score_primaries) { + fluT_p->setHistory(ncase); + if (score_spe) { + for (int j = 0; j < Nx*Ny; j++) { + flu_p[j]->setHistory(ncase); + } + } + } + } + }; + + void resetCounter() { + current_ncase = 0; + fluT->reset(); + if (flu) { + for (int j = 0; j < Nx*Ny; j++) { + flu[j]->reset(); } } - }; - - void resetCounter(){ - current_ncase = 0; - fluT->reset(); - if (flu){ - for (int j = 0; j < Nx*Ny; j++) - flu[j]->reset(); - } - if ( score_primaries ){ - fluT_p->reset(); - if ( score_spe ){ - for (int j = 0; j < Nx*Ny; j++) - flu_p[j]->reset(); - } - } - } - bool storeState(ostream &data) const; - bool setState(istream &data); - bool addState(istream &data); - -private: - - FieldType field_type; - - EGS_Float Area; - - /* Circular scoring field parameters */ - EGS_Vector m_normal, - m_midpoint, - ux, uy; - EGS_Vector x0; - EGS_Float m_R, m_R2; // scoring field - /* Rectangular field parameters */ - EGS_Float ax, ay, vx, vy; - int Nx, Ny, n_sensitive_regs, ixy; - - EGS_Float m_d; // distance from origin to center of scoring field - EGS_Float distance; // distance to scoring field along particle's direction - bool hits_field; + if (score_primaries) { + fluT_p->reset(); + if (score_spe) { + for (int j = 0; j < Nx*Ny; j++) { + flu_p[j]->reset(); + } + } + } + } + bool storeState(ostream &data) const; + bool setState(istream &data); + bool addState(istream &data); + +private: + + FieldType field_type; + + EGS_Float Area; + + /* Circular scoring field parameters */ + EGS_Vector m_normal, + m_midpoint, + ux, uy; + EGS_Vector x0; + EGS_Float m_R, m_R2; // scoring field + /* Rectangular field parameters */ + EGS_Float ax, ay, vx, vy; + int Nx, Ny, n_sensitive_regs, ixy; + + EGS_Float m_d; // distance from origin to center of scoring field + EGS_Float distance; // distance to scoring field along particle's direction + bool hits_field; }; @@ -460,22 +471,22 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { \ingroup AusgabObjects - A linear track-length estimator is used to compute fluence in specific - geometrical regions. User can request to score primary fluence as well - as differential fluence. If fluence for more than one particle type is + A linear track-length estimator is used to compute fluence in specific + geometrical regions. User can request to score primary fluence as well + as differential fluence. If fluence for more than one particle type is desired, multiple AOs are required. - Differential fluence for charged particles is calculated accounting for + Differential fluence for charged particles is calculated accounting for continuos energy losses along the path. As these particles slow down in medium, their contribution to fluence will spread over several energy bins. Two methods - to account for this are provided. One method follows the FLURZnrc implementation, - whereby stopping power is assumed constant along the step and estimated as the - ratio of the deposited energy EDEP to the total step length TVSTEP. The second - method follows the approach used in the EGSnrc application \ref cavity "cavity", - which accounts for stopping power changes within a scoring energy bin. It uses a - series expansion of the integral of the inverse of the stopping power with respect - to energy. Implementation details are provided in an upcoming publication. - + to account for this are provided. One method follows the FLURZnrc implementation, + whereby stopping power is assumed constant along the step and estimated as the + ratio of the deposited energy EDEP to the total step length TVSTEP. The second + method follows the approach used in the EGSnrc application \ref cavity "cavity", + which accounts for stopping power changes within a scoring energy bin. It uses a + series expansion of the integral of the inverse of the stopping power with respect + to energy. Implementation details are provided in an upcoming publication. + To define an EGS_VolumetricFluence AO use the syntax below: \verbatim :start ausgab object: @@ -483,10 +494,10 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { library = egs_fluence_scoring # Library name type = volumetric # Score in a volume scoring particle = photon, or electron, or positron - source particle = photon, or electron, or positron + source particle = photon, or electron, or positron # Optional. Only required to score primary fluence. # Defaults to source particles if all the same. - # In the case of multiple particles, + # In the case of multiple particles, # defaults to scoring particle. Useful for # bremsstrahlung targets and radioactive sources. score primaries = yes or no # Defaults to `no`. @@ -508,31 +519,31 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { #stop region = irf_1, irf_2, ..., irf_n ### volumes = V1, V2, ..., VN # Enter as many as scoring regions. If same number - # of entries as group of regions, assumes groups of - # equal volume regions. If only one entry, assumes + # of entries as group of regions, assumes groups of + # equal volume regions. If only one entry, assumes # equal volumes in all regions. Defaults to 1. method = flurz or stpwr or stpwrO5 # For charged particle scoring. - # + # # flurz => FLURZnrc algorithm - # + # # Path length at each energy interval from energy - # deposited EDEP and total particle step TVSTEP. + # deposited EDEP and total particle step TVSTEP. # Assumes stopping power constancy along the particle's # step. It might introduce artifacts if ESTEPE or the # scoring bin width are too large. - # - # stpwr => Accounts for stopping power variation - # along the particle's step. More accurate + # + # stpwr => Accounts for stopping power variation + # along the particle's step. More accurate # than method used in FLURZnrc albeit about # about 10% slower in electron beam cases. # # Uses an O(3) series expansion of the integral of the - # inverse of the stopping power with respect to energy. - # Stopping power is represented as a linear interpolation + # inverse of the stopping power with respect to energy. + # Stopping power is represented as a linear interpolation # over a log energy grid. - # + # # stpwrO5 => Uses an O(5) series expansion. Slightly slower. - # + # # Defaults to `stpwr`. :stop volumetric scoring: :stop ausgab object: @@ -541,371 +552,404 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScoring { public: - /*! Constructors */ - EGS_VolumetricFluence(const string &Name="", EGS_ObjectFactory *f = 0); - - /*! Destructor. */ - ~EGS_VolumetricFluence(); - - /*************************************************************** - NOTE: Primary particles defined as particles that suffer any - type of interaction, except for the slowing down of - charged particles in a medium. - ****************************************************************/ - bool needsCall(EGS_Application::AusgabCall iarg) const { - if (iarg == EGS_Application::BeforeTransport || - iarg == EGS_Application::UserDiscard ){ - return true; - } - else if ( score_primaries && - (iarg == EGS_Application::AfterPair || - iarg == EGS_Application::AfterCompton || - iarg == EGS_Application::AfterPhoto || - iarg == EGS_Application::AfterRayleigh || - iarg == EGS_Application::AfterBrems || - iarg == EGS_Application::AfterMoller || - iarg == EGS_Application::AfterBhabha || - iarg == EGS_Application::AfterAnnihFlight || - iarg == EGS_Application::AfterAnnihRest )) - { - return true; - } - else { - return false; - } - }; - - void describeMe();//!< Sets fluence scoring object \c description - - void initScoring(EGS_Input *inp); - - void setApplication(EGS_Application *App); - - void ouputVolumetricFluence( EGS_ScoringArray *fT, const double &norma ); - - void ouputResults(); - - void reportResults(); - - int processEvent(EGS_Application::AusgabCall iarg) { - - int q = app->top_p.q, - ir = app->top_p.ir, - latch = app->top_p.latch; - - if ( q == scoring_charge && ir >= 0 && is_sensitive[ir] ) { - - if ( !q ){// It's a photon - /* Score photon fluence */ - if (iarg == EGS_Application::BeforeTransport ) { - /* Linear track-Length scoring */ - EGS_Float wtstep = app->top_p.wt*app->getTVSTEP(); - /* Score total fluence */ - fluT->score(ir,wtstep); - if (score_primaries && !latch){ - fluT_p->score(ir,wtstep); - } - /* Score differential fluence */ - if ( score_spe ) { - EGS_Float e = app->top_p.E; - if (flu_s) { - e = log(e); - } - EGS_Float ae; int je; - /* Score differential fluence */ - if ( e > flu_xmin && e <= flu_xmax ) { - ae = flu_a*e + flu_b; - je = min((int)ae,flu_nbin-1); - EGS_ScoringArray *aux = flu[ir]; - aux->score(je,wtstep); - if (score_primaries && !latch){ - flu_p[ir]->score(je,wtstep); + /*! Constructors */ + EGS_VolumetricFluence(const string &Name="", EGS_ObjectFactory *f = 0); + + /*! Destructor. */ + ~EGS_VolumetricFluence(); + + /*************************************************************** + NOTE: Primary particles defined as particles that suffer any + type of interaction, except for the slowing down of + charged particles in a medium. + ****************************************************************/ + bool needsCall(EGS_Application::AusgabCall iarg) const { + if (iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::UserDiscard) { + return true; + } + else if (score_primaries && + (iarg == EGS_Application::AfterPair || + iarg == EGS_Application::AfterCompton || + iarg == EGS_Application::AfterPhoto || + iarg == EGS_Application::AfterRayleigh || + iarg == EGS_Application::AfterBrems || + iarg == EGS_Application::AfterMoller || + iarg == EGS_Application::AfterBhabha || + iarg == EGS_Application::AfterAnnihFlight || + iarg == EGS_Application::AfterAnnihRest)) { + return true; + } + else { + return false; + } + }; + + void describeMe();//!< Sets fluence scoring object \c description + + void initScoring(EGS_Input *inp); + + void setApplication(EGS_Application *App); + + void ouputVolumetricFluence(EGS_ScoringArray *fT, const double &norma); + + void ouputResults(); + + void reportResults(); + + int processEvent(EGS_Application::AusgabCall iarg) { + + int q = app->top_p.q, + ir = app->top_p.ir, + latch = app->top_p.latch; + + if (q == scoring_charge && ir >= 0 && is_sensitive[ir]) { + + if (!q) { // It's a photon + /* Score photon fluence */ + if (iarg == EGS_Application::BeforeTransport) { + /* Linear track-Length scoring */ + EGS_Float wtstep = app->top_p.wt*app->getTVSTEP(); + /* Score total fluence */ + fluT->score(ir,wtstep); + if (score_primaries && !latch) { + fluT_p->score(ir,wtstep); + } + /* Score differential fluence */ + if (score_spe) { + EGS_Float e = app->top_p.E; + if (flu_s) { + e = log(e); + } + EGS_Float ae; + int je; + /* Score differential fluence */ + if (e > flu_xmin && e <= flu_xmax) { + ae = flu_a*e + flu_b; + je = min((int)ae,flu_nbin-1); + EGS_ScoringArray *aux = flu[ir]; + aux->score(je,wtstep); + if (score_primaries && !latch) { + flu_p[ir]->score(je,wtstep); + } + } } } - } - } - } - else {// It's a charged particle - - EGS_Float edep = app->getEdep(); - - /* Score charged particle fluence */ - if ( edep && - ( iarg == EGS_Application::BeforeTransport || - iarg == EGS_Application::UserDiscard ) ){ - - /**************************/ - /***** Initialization *****/ - /**************************/ - EGS_Float weight = app->top_p.wt; - bool score_p = score_primaries && !latch; - /* Integral fluence scoring arrays */ - EGS_ScoringArray *auxT = fluT, *auxT_p; - if ( score_p ){ - auxT_p = fluT_p; - } - - /***************************************/ - /* Score integral fluence using TVSTEP */ - /***************************************/ - EGS_Float a_step = weight*app->getTVSTEP(); - auxT->score(ir,a_step); - if (score_p) - auxT_p->score(ir,a_step); - /***************************************/ - - if ( score_spe ){ - - EGS_ScoringArray *aux, *aux_p; - aux = flu[ir]; - if ( score_p ){ - aux_p = flu_p[ir]; - } - - EGS_Float Eb = app->top_p.E - app->getRM(), - Ee = Eb - edep; - - EGS_Float xb, xe; - if( flu_s ) { - xb = log(Eb); - if( Ee > 0 ) - xe = log(Ee); - else xe = -15; - } - else{ - xb = Eb; xe = Ee; - } - /**********************************************************/ - /* If not out of bounds, proceed with rest of calculation */ - /**********************************************************/ - if( xb > flu_xmin && xe < flu_xmax ) { - EGS_Float ab, ae; int jb, je; - /* Fraction of the initial bin covered */ - if( xb < flu_xmax ) { - ab = flu_a*xb + flu_b; jb = (int) ab; - /* Variable bin-width for log scale*/ - if (flu_s){ - ab = (Eb*a_const[jb]-1)*r_const; - } - else{ ab -= jb;} - } - else { // particle's energy above Emax - xb = flu_xmax; ab = 1; jb = flu_nbin - 1; - } - /* Fraction of the final bin covered */ - if( xe > flu_xmin ) { - ae = flu_a*xe + flu_b; je = (int) ae; - /* Variable bin-width for log scale*/ - if (flu_s){ - ae = (Ee*a_const[je]-1)*r_const; - } - else{ ae -= je; } - } - else { xe = flu_xmin; ae = 0; je = 0; }// extends below Emin - -#ifdef DEBUG - if (jb == je) - one_bin++; - else{ - multi_bin++; - } - - binDist->score(jb-je,weight); - - EGS_Float totStep = 0, the_step = app->getTVSTEP(); -#endif - - /************************************************ - * Approach A: - * ----------- - * Uses either an O(3) or O(5) series expansion of the - * integral of the inverse of the stopping power with - * respect to energy. The stopping power is represented - * as a linear interpolation over a log energy grid. It - * accounts for stopping power variation along the particle's - * step within the resolution of the scoring array. This - * is more accurate than the method used in FLURZnrc albeit - * about 10% slower in electron beam cases. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - * - ************************************************/ - if ( flu_stpwr ){ - int imed = app->getMedium(ir); - // Initial and final energies in same bin - EGS_Float step; - if( jb == je ){ - step = weight*(ab-ae)*getStepPerEnergyLoss(imed,xb,xe); -#ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); + } + else {// It's a charged particle + + EGS_Float edep = app->getEdep(); + + /* Score charged particle fluence */ + if (edep && + (iarg == EGS_Application::BeforeTransport || + iarg == EGS_Application::UserDiscard)) { + + /**************************/ + /***** Initialization *****/ + /**************************/ + EGS_Float weight = app->top_p.wt; + bool score_p = score_primaries && !latch; + /* Integral fluence scoring arrays */ + EGS_ScoringArray *auxT = fluT, *auxT_p; + if (score_p) { + auxT_p = fluT_p; + } + + /***************************************/ + /* Score integral fluence using TVSTEP */ + /***************************************/ + EGS_Float a_step = weight*app->getTVSTEP(); + auxT->score(ir,a_step); + if (score_p) { + auxT_p->score(ir,a_step); + } + /***************************************/ + + if (score_spe) { + + EGS_ScoringArray *aux, *aux_p; + aux = flu[ir]; + if (score_p) { + aux_p = flu_p[ir]; + } + + EGS_Float Eb = app->top_p.E - app->getRM(), + Ee = Eb - edep; + + EGS_Float xb, xe; + if (flu_s) { + xb = log(Eb); + if (Ee > 0) { + xe = log(Ee); + } + else { + xe = -15; } } else { - - // First bin - - Ee = flu_xmin + jb*flu_a_i; Eb=xb; - step = weight*ab*getStepPerEnergyLoss(imed,Eb,Ee); -#ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); + xb = Eb; + xe = Ee; + } + /**********************************************************/ + /* If not out of bounds, proceed with rest of calculation */ + /**********************************************************/ + if (xb > flu_xmin && xe < flu_xmax) { + EGS_Float ab, ae; + int jb, je; + /* Fraction of the initial bin covered */ + if (xb < flu_xmax) { + ab = flu_a*xb + flu_b; + jb = (int) ab; + /* Variable bin-width for log scale*/ + if (flu_s) { + ab = (Eb*a_const[jb]-1)*r_const; + } + else { + ab -= jb; + } } - - // Last bin - - Ee = xe; Eb = flu_xmin+(je+1)*flu_a_i; - step = weight*(1-ae)*getStepPerEnergyLoss(imed,Eb,Ee); -#ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(je,step); - if (score_p) - aux_p->score(je,step); + else { // particle's energy above Emax + xb = flu_xmax; + ab = 1; + jb = flu_nbin - 1; + } + /* Fraction of the final bin covered */ + if (xe > flu_xmin) { + ae = flu_a*xe + flu_b; + je = (int) ae; + /* Variable bin-width for log scale*/ + if (flu_s) { + ae = (Ee*a_const[je]-1)*r_const; + } + else { + ae -= je; + } } - - // intermediate bins - - for(int j=je+1; jscore(j,step); - if (score_p) - aux_p->score(j,step); - } + if (jb == je) { + one_bin++; } - } - } - /*************************************************** - * ----------------------- - * Approach B (FLURZnrc): - * ---------------------- - * Path length at each energy interval from energy - * deposited edep and total particle step tvstep. It - * assumes stopping power constancy along the particle's - * step. It might introduce artifacts if ESTEPE or the - * scoring bin width are too large. - * - * BEWARE: For this approach to work, no range rejection - * ------ nor Russian Roulette should be used. - **************************************************/ - else{ - EGS_Float step, wtstep = weight*app->getTVSTEP()/edep; - // Initial and final energies in same bin - if( jb == je ){ - step = wtstep*(ab-ae); + else { + multi_bin++; + } + + binDist->score(jb-je,weight); + + EGS_Float totStep = 0, the_step = app->getTVSTEP(); +#endif + + /************************************************ + * Approach A: + * ----------- + * Uses either an O(3) or O(5) series expansion of the + * integral of the inverse of the stopping power with + * respect to energy. The stopping power is represented + * as a linear interpolation over a log energy grid. It + * accounts for stopping power variation along the particle's + * step within the resolution of the scoring array. This + * is more accurate than the method used in FLURZnrc albeit + * about 10% slower in electron beam cases. + * + * BEWARE: For this approach to work, no range rejection + * ------ nor Russian Roulette should be used. + * + ************************************************/ + if (flu_stpwr) { + int imed = app->getMedium(ir); + // Initial and final energies in same bin + EGS_Float step; + if (jb == je) { + step = weight*(ab-ae)*getStepPerEnergyLoss(imed,xb,xe); #ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - } - } - else { - - // First bin - - step = wtstep*ab; + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(jb,step); + if (score_p) { + aux_p->score(jb,step); + } + } + } + else { + + // First bin + + Ee = flu_xmin + jb*flu_a_i; + Eb=xb; + step = weight*ab*getStepPerEnergyLoss(imed,Eb,Ee); #ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(jb,step); - if (score_p) - aux_p->score(jb,step); - } - - // Last bin - - step = wtstep*(1-ae); + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(jb,step); + if (score_p) { + aux_p->score(jb,step); + } + } + + // Last bin + + Ee = xe; + Eb = flu_xmin+(je+1)*flu_a_i; + step = weight*(1-ae)*getStepPerEnergyLoss(imed,Eb,Ee); #ifdef DEBUG - totStep += step; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(je,step); - if (score_p) - aux_p->score(je,step); + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(je,step); + if (score_p) { + aux_p->score(je,step); + } + } + + // intermediate bins + + for (int j=je+1; jscore(j,step); + if (score_p) { + aux_p->score(j,step); + } + } + } + } } - // intermediate bins - for(int j=je+1; jgetTVSTEP()/edep; + // Initial and final energies in same bin + if (jb == je) { + step = wtstep*(ab-ae); #ifdef DEBUG - totStep += wtstep; -#endif - /* Differential fluence */ - if ( score_spe ){ - aux->score(j,wtstep); - if (score_p) - aux_p->score(j,wtstep); + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(jb,step); + if (score_p) { + aux_p->score(jb,step); + } + } + } + else { + + // First bin + + step = wtstep*ab; +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(jb,step); + if (score_p) { + aux_p->score(jb,step); + } + } + + // Last bin + + step = wtstep*(1-ae); +#ifdef DEBUG + totStep += step; +#endif + /* Differential fluence */ + if (score_spe) { + aux->score(je,step); + if (score_p) { + aux_p->score(je,step); + } + } + // intermediate bins + for (int j=je+1; jscore(j,wtstep); + if (score_p) { + aux_p->score(j,wtstep); + } + } + } } } - } - } - + #ifdef DEBUG - EGS_Float edep_step = totStep*flu_a_i, diff = edep_step/the_step; - EGS_Float astep = step_a*the_step + step_b; int jstep = (int) astep; - if (jstep < 0 || jstep > n_step_bins) - egsFatal("\n**** EGS_VolumetricFluence::processEvent-> jstep = %d\n is out of bound!\n"); - stepDist->score(jstep,app->top_p.wt); - relStepDiff->score(jstep,diff); eCases++; -#endif - } - } - } - } - } + EGS_Float edep_step = totStep*flu_a_i, diff = edep_step/the_step; + EGS_Float astep = step_a*the_step + step_b; + int jstep = (int) astep; + if (jstep < 0 || jstep > n_step_bins) { + egsFatal("\n**** EGS_VolumetricFluence::processEvent-> jstep = %d\n is out of bound!\n"); + } + stepDist->score(jstep,app->top_p.wt); + relStepDiff->score(jstep,diff); + eCases++; +#endif + } + } + } + } + } - /******************************************************************** - * Flag secondaries after interactions. Definition of secondaries - * matches FLURZnrc. One could fine tune it by using the is_source - * flag to skip this block in certains regions such as brems targets, - * radioactive sources, etc. - * - * BEWARE: Latch set to 1 (bit 0) to flag secondaries. - * Other applications might use latch for other purposes! - *********************************************************************/ - if ( score_primaries && ir >= 0 && !is_source[ir]){ - flagSecondaries( iarg, q ); - } + /******************************************************************** + * Flag secondaries after interactions. Definition of secondaries + * matches FLURZnrc. One could fine tune it by using the is_source + * flag to skip this block in certains regions such as brems targets, + * radioactive sources, etc. + * + * BEWARE: Latch set to 1 (bit 0) to flag secondaries. + * Other applications might use latch for other purposes! + *********************************************************************/ + if (score_primaries && ir >= 0 && !is_source[ir]) { + flagSecondaries(iarg, q); + } - return 0; + return 0; - }; + }; /*! Computes path per energy loss traveled by a charged particle when * slowing down from Eb to Ee. @@ -921,44 +965,50 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_VolumetricFluence : public EGS_FluenceScori * function that is the result of the integration of the inverse of the stopping * power with respect to energy. */ - EGS_Float getStepPerEnergyLoss( const int & imed, - const EGS_Float & Eb, - const EGS_Float & Ee){ + EGS_Float getStepPerEnergyLoss(const int &imed, + const EGS_Float &Eb, + const EGS_Float &Ee) { EGS_Float stpFrac, eps, lnEmid; - if (flu_s){//Using log(E) - eps = 1 - exp(Ee-Eb); - /* 4th order series expansion of log([Eb+Ee]/2) */ - lnEmid = 0.5*(Eb+Ee+0.25*eps*eps*(1+eps*(1+0.875*eps))); + if (flu_s) { //Using log(E) + eps = 1 - exp(Ee-Eb); + /* 4th order series expansion of log([Eb+Ee]/2) */ + lnEmid = 0.5*(Eb+Ee+0.25*eps*eps*(1+eps*(1+0.875*eps))); } - else{//Using E - if (flu_stpwr == stpwrO5) - eps = 1 - Ee/Eb; - lnEmid = log(0.5*(Eb+Ee)); + else { //Using E + if (flu_stpwr == stpwrO5) { + eps = 1 - Ee/Eb; + } + lnEmid = log(0.5*(Eb+Ee)); } EGS_Float dedxmid_i = dedx_i[imed].interpolate(lnEmid); // Used in cavity: // EGS_Float dedxmid_i = 1./i_dedx[imed].interpolate(lnEmid); #ifdef DEBUG -if (!isfinite(dedxmid_i)){ - if (isnan(dedxmid_i)) - egsInformation("\n Is NaN? dedxmid_i = %g",dedxmid_i); - else - egsInformation("\n Is infinite? dedxmid_i = %g",dedxmid_i); -} -else{ - if (dedxmid_i<0){ - egsInformation("\n Is negative? dedxmid_i = %g Emid = %g lnEmid = %g index = %d", - dedxmid_i,exp(lnEmid),lnEmid, dedx_i[imed].getIndex(lnEmid) ); - } - if (dedxmid_i > 1.E10) - egsInformation("\n Is very large? dedxmid_i = %g Emid = %g lnEmid = %g",dedxmid_i,exp(lnEmid),lnEmid); - -} -#endif + if (!isfinite(dedxmid_i)) { + if (isnan(dedxmid_i)) { + egsInformation("\n Is NaN? dedxmid_i = %g",dedxmid_i); + } + else { + egsInformation("\n Is infinite? dedxmid_i = %g",dedxmid_i); + } + } + else { + if (dedxmid_i<0) { + egsInformation("\n Is negative? dedxmid_i = %g Emid = %g lnEmid = %g index = %d", + dedxmid_i,exp(lnEmid),lnEmid, dedx_i[imed].getIndex(lnEmid)); + } + if (dedxmid_i > 1.E10) { + egsInformation("\n Is very large? dedxmid_i = %g Emid = %g lnEmid = %g",dedxmid_i,exp(lnEmid),lnEmid); + } + + } +#endif /* O(eps^3) approach */ - if (flu_stpwr == stpwr) return dedxmid_i; - + if (flu_stpwr == stpwr) { + return dedxmid_i; + } + /* O(eps^5) approach */ EGS_Float b = i_dedx[imed].get_b(i_dedx[imed].getIndexFast(lnEmid)); EGS_Float aux = b*dedxmid_i; @@ -969,104 +1019,110 @@ else{ } - void setCurrentCase(EGS_I64 ncase) { - if( ncase != current_ncase ) { + void setCurrentCase(EGS_I64 ncase) { + if (ncase != current_ncase) { current_ncase = ncase; fluT->setHistory(ncase); - if ( score_primaries ) - fluT_p->setHistory(ncase); - if (score_spe){ - for (int j = 0; j < nreg; j++){ - if ( is_sensitive[j] ) - flu[j]->setHistory(ncase); - } - if ( score_primaries ){ - for (int j = 0; j < nreg; j++){ - if ( is_sensitive[j] ) - flu_p[j]->setHistory(ncase); - } - } + if (score_primaries) { + fluT_p->setHistory(ncase); + } + if (score_spe) { + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu[j]->setHistory(ncase); + } + } + if (score_primaries) { + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu_p[j]->setHistory(ncase); + } + } + } } } #ifdef DEBUG binDist->setHistory(ncase); - if ( scoring_charge ){ - stepDist->setHistory(ncase); - relStepDiff->setHistory(ncase); + if (scoring_charge) { + stepDist->setHistory(ncase); + relStepDiff->setHistory(ncase); } -#endif - - }; - void resetCounter(){ - current_ncase = 0; - fluT->reset(); - if ( score_primaries ) - fluT_p->reset(); - if ( score_spe ){ - for (int j = 0; j < nreg; j++){ - if ( is_sensitive[j] ) - flu[j]->reset(); +#endif + + }; + void resetCounter() { + current_ncase = 0; + fluT->reset(); + if (score_primaries) { + fluT_p->reset(); } - - if ( score_primaries ){ - for (int j = 0; j < nreg; j++){ - if ( is_sensitive[j] ) - flu_p[j]->reset(); - } + if (score_spe) { + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu[j]->reset(); + } + } + + if (score_primaries) { + for (int j = 0; j < nreg; j++) { + if (is_sensitive[j]) { + flu_p[j]->reset(); + } + } + } } - } #ifdef DEBUG - binDist->reset(); - if ( scoring_charge ){ - stepDist->reset(); - relStepDiff->reset(); - } + binDist->reset(); + if (scoring_charge) { + stepDist->reset(); + relStepDiff->reset(); + } #endif - } - - bool storeState(ostream &data) const; - - bool setState(istream &data); - - bool addState(istream &data); - -private: - - /*******************************************/ - /* Charged particle fluence: Required data */ - /*******************************************/ - EGS_Interpolator* i_dedx; // stopping power for each medium - EGS_Interpolator* dedx_i; // inverse stopping power for each medium - EGS_Float* Lmid_i; // pre-computed inverse of bin midpoint stpwr - eFluType flu_stpwr; // flurz => ave. stpwr = edep/tvstep, - // stpwr => 3rd order in edep/Eb, - // stpwrO5 => 5th order in edep/Eb - /************************************************************** - * Parameters required for calculations on a log-scale - * The main issue here is that the bin width is not constant - *************************************************************/ - EGS_Float r_const; // inverse of (Emax/Emin)**1/flu_nbin - 1 = exp(binwidth)-1 - EGS_Float *a_const; // constant needed to determine bin fractions on log scale - EGS_Float *DE; // bin width of logarithmic scale - /*****************************************************************/ - - EGS_I64 eCases; - - vector volume; // volume of each scoring region - /* Energy grid inputs */ - //EGS_Float flu_a_i; - /* Auxiliary input variables*/ - vector vol_list; // Input list of region volumes + } + + bool storeState(ostream &data) const; + + bool setState(istream &data); + + bool addState(istream &data); + +private: + + /*******************************************/ + /* Charged particle fluence: Required data */ + /*******************************************/ + EGS_Interpolator *i_dedx; // stopping power for each medium + EGS_Interpolator *dedx_i; // inverse stopping power for each medium + EGS_Float *Lmid_i; // pre-computed inverse of bin midpoint stpwr + eFluType flu_stpwr; // flurz => ave. stpwr = edep/tvstep, + // stpwr => 3rd order in edep/Eb, + // stpwrO5 => 5th order in edep/Eb + /************************************************************** + * Parameters required for calculations on a log-scale + * The main issue here is that the bin width is not constant + *************************************************************/ + EGS_Float r_const; // inverse of (Emax/Emin)**1/flu_nbin - 1 = exp(binwidth)-1 + EGS_Float *a_const; // constant needed to determine bin fractions on log scale + EGS_Float *DE; // bin width of logarithmic scale + /*****************************************************************/ + + EGS_I64 eCases; + + vector volume; // volume of each scoring region + /* Energy grid inputs */ + //EGS_Float flu_a_i; + /* Auxiliary input variables*/ + vector vol_list; // Input list of region volumes #ifdef DEBUG - /* Debugging information */ - int one_bin, multi_bin; - EGS_ScoringArray *binDist; - EGS_ScoringArray *stepDist; - EGS_ScoringArray *relStepDiff; // Relative difference between tvstep and edep-derived step - EGS_Float max_step; - EGS_Float step_a, step_b; - EGS_I32 n_step_bins; + /* Debugging information */ + int one_bin, multi_bin; + EGS_ScoringArray *binDist; + EGS_ScoringArray *stepDist; + EGS_ScoringArray *relStepDiff; // Relative difference between tvstep and edep-derived step + EGS_Float max_step; + EGS_Float step_a, step_b; + EGS_I32 n_step_bins; #endif }; diff --git a/HEN_HOUSE/egs++/egs_advanced_application.cpp b/HEN_HOUSE/egs++/egs_advanced_application.cpp index e8fd4d03a..5b55abf4f 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.cpp +++ b/HEN_HOUSE/egs++/egs_advanced_application.cpp @@ -1225,29 +1225,32 @@ EGS_Float EGS_AdvancedApplication::getTVSTEP() { return the_epcont->tvstep; } -EGS_Interpolator* EGS_AdvancedApplication::getDEDX( const int &imed, const int &iq ) { - if (iq == -1) - return &i_ededx[imed]; - else if (iq == 1) - return &i_pdedx[imed]; - else - return 0; +EGS_Interpolator *EGS_AdvancedApplication::getDEDX(const int &imed, const int &iq) { + if (iq == -1) { + return &i_ededx[imed]; + } + else if (iq == 1) { + return &i_pdedx[imed]; + } + else { + return 0; + } } -void EGS_AdvancedApplication::setLatch( const int &ip, const int &latch ) { - the_stack->latch[ip] = latch; +void EGS_AdvancedApplication::setLatch(const int &ip, const int &latch) { + the_stack->latch[ip] = latch; } -void EGS_AdvancedApplication::incLatch( const int &ip, const int &increment ){ - the_stack->latch[ip] += increment; +void EGS_AdvancedApplication::incLatch(const int &ip, const int &increment) { + the_stack->latch[ip] += increment; } int EGS_AdvancedApplication::getNp() { - return the_stack->np-1;; + return the_stack->np-1;; } int EGS_AdvancedApplication::getNpOld() { - return the_stack->npold-1; + return the_stack->npold-1; } //************************************************************ diff --git a/HEN_HOUSE/egs++/egs_advanced_application.h b/HEN_HOUSE/egs++/egs_advanced_application.h index a0db92d12..b77fc6093 100644 --- a/HEN_HOUSE/egs++/egs_advanced_application.h +++ b/HEN_HOUSE/egs++/egs_advanced_application.h @@ -221,12 +221,12 @@ class APP_EXPORT EGS_AdvancedApplication : public EGS_Application { // Utility functions for fluence scoring objects //************************************************************ EGS_Float getTVSTEP(); - EGS_Interpolator* getDEDX( const int &imed, const int &iq ); - void setLatch( const int &ip, const int &latch ); - void incLatch( const int &ip, const int &increment ); + EGS_Interpolator *getDEDX(const int &imed, const int &iq); + void setLatch(const int &ip, const int &latch); + void incLatch(const int &ip, const int &increment); int getNp(); int getNpOld(); - + /* Needed by some sources */ EGS_Float getRM(); /* Turn ON/OFF radiative splitting */ diff --git a/HEN_HOUSE/egs++/egs_application.h b/HEN_HOUSE/egs++/egs_application.h index 7be9e0869..c7842f38b 100644 --- a/HEN_HOUSE/egs++/egs_application.h +++ b/HEN_HOUSE/egs++/egs_application.h @@ -1157,7 +1157,7 @@ class EGS_EXPORT EGS_Application { return 0.0; }; - virtual EGS_Interpolator* getDEDX( const int &imed, const int &iq ) { + virtual EGS_Interpolator *getDEDX(const int &imed, const int &iq) { return 0; }; @@ -1173,16 +1173,16 @@ class EGS_EXPORT EGS_Application { return source->getEmax(); } - virtual void setLatch( const int &ip, const int &latch ){}; + virtual void setLatch(const int &ip, const int &latch) {}; - virtual void incLatch( const int &ip, const int &increment ){}; + virtual void incLatch(const int &ip, const int &increment) {}; virtual int getNp() { - return 0; + return 0; }; virtual int getNpOld() { - return 0; + return 0; }; //************************************************************ diff --git a/HEN_HOUSE/egs++/egs_base_source.h b/HEN_HOUSE/egs++/egs_base_source.h index 8d87a8ad0..392eed3d8 100644 --- a/HEN_HOUSE/egs++/egs_base_source.h +++ b/HEN_HOUSE/egs++/egs_base_source.h @@ -146,7 +146,7 @@ class EGS_EXPORT EGS_BaseSource : public EGS_Object { * * This virtual function must be re-implemented in derived classes * that return the source particles' charge. Multi-particle sources - * will return a value of -99. This value corresponds to an unknown + * will return a value of -99. This value corresponds to an unknown * particle type in the fluence scoring AOs. */ virtual int getCharge() const { diff --git a/HEN_HOUSE/egs++/egs_interpolator.cpp b/HEN_HOUSE/egs++/egs_interpolator.cpp index b66680502..d2ed9979f 100644 --- a/HEN_HOUSE/egs++/egs_interpolator.cpp +++ b/HEN_HOUSE/egs++/egs_interpolator.cpp @@ -99,8 +99,8 @@ void EGS_Interpolator::initialize(int nbin, EGS_Float Xmin, EGS_Float Xmax, b[j] = (values[j+1]-values[j])*bx; a[j] = values[j] - b[j]*(xmin + dx*j); } - /**************************************************** - Extra subinterval at top of interval, taking care + /**************************************************** + Extra subinterval at top of interval, taking care of round-off errors via extrapolation. Mimics PEGS4 PWLF QFIT approach. *****************************************************/ From 2d80093cb97fcd5b25168e15aa8a136a46150e17 Mon Sep 17 00:00:00 2001 From: Frederic Tessier Date: Tue, 10 Jan 2023 14:33:56 -0500 Subject: [PATCH 17/18] Remove double precision suffix notation for clang --- .../ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp index 7eafd22ee..2b5c67262 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.cpp @@ -1396,7 +1396,7 @@ void EGS_VolumetricFluence::describeMe() { description += " Fluence calculated a-la-FLURZ using Lave=EDEP/TVSTEP.\n"; } - if (norm_u != 1.0D+0) { + if (norm_u != 1.0) { description += "\n - Non-unity user-requested normalization = "; sprintf(buf,"%g\n",norm_u); description += buf; From 14fa7246f6a0cbfccdae91fc96fe1dbcd15ea4ee Mon Sep 17 00:00:00 2001 From: Frederic Tessier Date: Mon, 23 Jan 2023 10:08:43 -0500 Subject: [PATCH 18/18] Fix a few typos in the documentation text --- .../egs_fluence_scoring/egs_fluence_scoring.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h index 08c256cad..9bf2be808 100644 --- a/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h +++ b/HEN_HOUSE/egs++/ausgab_objects/egs_fluence_scoring/egs_fluence_scoring.h @@ -242,14 +242,14 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_FluenceScoring : public EGS_AusgabObject { A linear track-length estimator in the zero-thickness limit is used to compute fluence for either a circular field or a rectangular screen of arbitrary resolution (pixels). Rectangular fields are by default at Z = 0 - directed along the positive z-axis. An affine transofrmation can be included + directed along the positive z-axis. An affine transformation can be included to place a rectangular field anywhere in space. User can request to score primary fluence as well as differential fluence. If fluence for more than one particle type is desired, multiple AOs are required. For charged particle fluence scoring this method can be inefficient as it checks at every single step whether a charged particle is aimed at scoring field. - To improve the efficieny for charged particles, one has the option to define contributing regions + To improve the efficiency for charged particles, one has the option to define contributing regions from where to score. However, users must be careful to select all regions from where charged particles can cross the scoring field. This option has the added benefit of allowing to estimate the contribution to the fluence from specific regions in the geometry. @@ -477,7 +477,7 @@ class EGS_FLUENCE_SCORING_EXPORT EGS_PlanarFluence : public EGS_FluenceScoring { desired, multiple AOs are required. Differential fluence for charged particles is calculated accounting for - continuos energy losses along the path. As these particles slow down in medium, + continuous energy losses along the path. As these particles slow down in medium, their contribution to fluence will spread over several energy bins. Two methods to account for this are provided. One method follows the FLURZnrc implementation, whereby stopping power is assumed constant along the step and estimated as the