PenRed Implementation Guide
PenRed Implementation Guide
Abstract
The present document is a manual of the PenRed code system which content is fo-
cused on the development of new components and how to include and use them inside
the framework. PenRed is a general-purpose, stand-alone and extensible framework
code based on PENELOPE for parallel Monte Carlo simulations of radiation transport
through matter. It has been implemented in C++ programming language and takes
advantage of modern object-oriented technologies. In addition, PenRed offers, among
other features, the capability to read and process DICOM images to perform simula-
tions. These feature facilitate its usage in medical applications. Our framework has
been successfully tested against the original PENELOPE Fortran code.
1 Introduction
This document will explain step by step how to implement new components in the PenRed
[1] environment. As the intention is to focus exclusively on the development of new compo-
nents, this document does not provide a deep description of the PenRed structure. However,
a deeper description can be found in the PenRed components documentation (in develop-
ment).
V. Giménez-Alventosa https://orcid.org/0000-0003-1646-6094
V. Giménez Gómez https://orcid.org/0000-0003-3855-2567
S. Oliver https://orcid.org/0000-0001-8258-3972
1
generate new particle states to be simulated follows in the section 7. Finally, the section 8
shows how to implement variance reduction (VR) modules.
2 Particle state
The class pen particleState stores the minimal data required to specify the particles
state. The definition of this class can be found in the file,
src/kernel/states/pen baseState.hh
and the variables defined inside the class are described following,
• WGHT: Stores the particle weight, which could be modified, for example, as conse-
quence of variance reduction techniques.
• IBODY: Stores the geometry system body index where the particle is located.
• ILB: Array with 5 components to store particle metadata information, such as the
parent particle type. Further details can be found in the constants and definitions
section in the usage documentation.
Although it is possible to define new particle states, like the used for polarised photons
(pen state gPol), notice that all particle states must derive from the provided basic state
pen particleState. This restriction ensures the compatibility of any developed state with
the PenRed components.
src/kernel/parsers/internalData/pen data.hh
Among these classes, the pen parserSection class is required by almost all the frame-
work components to get the configuration parameters. To store the data, this class use a
basic key/value pair format where the key structure is based on the unix folder system, i.e.
a generic key is a string with the following structure:
/f older1/f older2/.../element
2
On the other hand, the value can be a number, bool (True or False), character, string
or an array of numbers, bools or characters. If an array is specified, this one can contain
multiple elements of different types.
Notice that, to be parsed correctly, a string value must be enclosed with double quotes,
like “text”, as is shown in the previous example. In addition, arrays must be enclosed by [
and ] and the elements separated by comas. An example extracted from the 7 − aba quadric
example follows,
sources/generic/source1/energy/probabilities [50.0,50.0]
where an array is used to specify the emission probabilities. Notice that an array cannot
contain strings, but characters are allowed.
To parse these files, the library includes a function named parseFile (code 2). However,
the parsing step is usually handled by the main program and is not required to configure a
component.
1 i n t p a r s e F i l e ( const char ∗ filename ,
2 p e n p a r s e r S e c t i o n& s e c t i o n ,
3 s t d : : s t r i n g& e r r o r S t r i n g ,
4 l o n g u n s i g n e d& e r r o r L i n e ) ;
Code 2: Parse file function
In the code 2, the filename parameter specify the name of the file to be parsed, the
parameter section will be filled with the parsed data, and the errorString and errorLine
parameters will be filled only if an error is produced during the parsing. The first one
returns a descriptive message of the parsing error and the second one the line where the
error has been found. In addition, to check if the parsing has been done successfully, the
returned value of the parseFile function is expected to be INTDATA SUCCESS, which is
defined in the enumeration pen parserErrors. On error, another value is returned.
3
3 i n t r e a d ( c o n s t c h a r ∗ key , c h a r& data , c o n s t u n s i g n e d i n d e x = 0 ) c o n s t
4
5 i n t r e a d ( c o n s t c h a r ∗ key , i n t& data , c o n s t u n s i g n e d i n d e x = 0 ) c o n s t
6
7 i n t r e a d ( c o n s t c h a r ∗ key , d o u b l e& data , c o n s t u n s i g n e d i n d e x = 0 ) c o n s t
8
9 i n t r e a d ( c o n s t c h a r ∗ key , b o o l& data , c o n s t u n s i g n e d i n d e x = 0 ) c o n s t
10
11 i n t r e a d ( c o n s t c h a r ∗ key , p e n p a r s e r D a t a& data , c o n s t u n s i g n e d i n d e x = 0 )
const
12
13 i n t r e a d ( c o n s t c h a r ∗ key , s t d : : s t r i n g& data , c o n s t u n s i g n e d i n d e x = 0 ) c o n s t
14
15 i n t r e a d ( c o n s t c h a r ∗ key , p e n p a r s e r A r r a y& data , c o n s t u n s i g n e d = 0 ) c o n s t
16
17 i n t r e a d ( c o n s t c h a r ∗ key , p e n p a r s e r S e c t i o n& data , c o n s t u n s i g n e d = 0 ) c o n s t
Code 3: Parse section read functions
Notice that the index parameter is used only to specify the element position when the
value to read belongs to an array which key is specified by the parameter key. The data
parameter is used to store the value of the specified key. All read functions returns a
INTDATA SUCCESS if the read is done successfully or another value otherwise. An error
is returned, for example, if the element specified by the parameter key is not convertible to
the data parameter type.
An example of the read function usage is shown in the code 4. There, the key value
“energy” is read and stored in the variable E.
1 int err ;
2
3 double E;
4 e r r = c o n f i g . r e a d ( ” e n e r g y ” ,E) ;
5 i f ( e r r != INTDATA SUCCESS) {
6 i f ( verbose > 0) {
7 p r i n t f ( ” monoenergetic : c o n f i g u r e : unable to read ’ energy ’ in c o n f i g u r a t i o n
. Real number e x p e c t e d . \ n” ) ;
8 }
9 r e t u r n −1;
10 }
Code 4: Read example
f1/f2/f3/f4/data1 6
f1/f2/f3/data1 2
f1/f2/f3/data2 4.2
f1/f2/data1 [1,2,3]
f1/data1 1.2
f1/data2 "text"
f1/data3 false
and we read the subsection “f1/f2”, the resulting pen parserSection instance will contain,
f3/f4/data1 6
f3/data1 2
4
f3/data2 4.2
data1 [1,2,3]
i.e., all keys which prefix do not match with the specified key will not be copied. By default,
the prefix is removed also. However, the readSubsection function (code 5) can be used for
the same purpose but providing the possibility to conserve the prefix key via the parameter
removeKey.
1 i n t r e a d S u b s e c t i o n ( c o n s t c h a r ∗ key , p e n p a r s e r S e c t i o n& secOut , c o n s t b o o l
removeKey = t r u e )
Code 5: Read subsection function
One usage example of this function can be found in the main program. There, the
function is used to filter the subsections corresponding to different components, such as
geometry, tallies, sources, etc. The code 6 shows an example corresponding to the read of
“tallies” section in the main program.
1 int err = config . readSubsection (” t a l l i e s ” , t a l l i e s S e c t i o n ) ;
2 i f ( e r r != INTDATA SUCCESS) {
3 i f ( verbose > 0) {
4 p r i n t f ( ” c r e a t e T a l l i e s : E r r o r : C o n f i g u r a t i o n ’ t a l l i e s ’ s e c t i o n doesn ’ t
e x i s t . \ n” ) ;
5 }
6 r e t u r n −1;
7 }
Code 6: Read subsection example
• ls: The ls method from the pen parserSection class (code 7) fills a vector of strings
(vect) with all names in the sections top “folder”, i.e. before the first “/”. The names
are not repeated if several keys shares the same prefix.
1 i n t l s ( s t d : : v e c t o r <s t d : : s t r i n g >& v e c t )
Code 7: Method ls from pen parserSection class
3.4 Examples
Several examples of the usage of the internal format classes can be found in the configura-
tion functions of most PenRed components and in the main program. In addition, the tests
folder,
src/tests/internalData/
5
4 Automatic image export
PenRed provides a image exporter library to handle different image formats automatically.
this one can be found in the folder,
src/lib/image
That library provides a single structure named pen imageExporter which handles the
export process via a user provided function, which must return a specific image pixel value
when called. This one must be specified in the constructor. All the available constructors
are shown in the code 8. As can be seen, one constructor is defined for each compatible
returned variable type. Moreover, two different kind of functions can be provided for the
same variable type depending on whether the function returns the associated uncertainty
of the value or not. Both will be discussed in the next section.
1 // ∗∗ C o n s t r u c t o r s f o r images w i t h o u t a s s o c i a t e d u n c e r t a i n t y
2 pen imageExporter ( std : : f u n c t i o n <f l o a t ( u n s i g n e d l o n g long , s i z e t )> f i n ) ;
3 pen imageExporter ( std : : f u n c t i o n <d o u b l e ( u n s i g n e d l o n g long , s i z e t )> f i n ) ;
4 pen imageExporter ( std : : f u n c t i o n <s t d : : i n t 8 t ( u n s i g n e d l o n g long , s i z e t )> f i n
);
5 pen imageExporter ( std : : f u n c t i o n <s t d : : u i n t 8 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
6 pen imageExporter ( std : : f u n c t i o n <s t d : : i n t 1 6 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
7 pen imageExporter ( std : : f u n c t i o n <s t d : : u i n t 1 6 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
8 pen imageExporter ( std : : f u n c t i o n <s t d : : i n t 3 2 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
9 pen imageExporter ( std : : f u n c t i o n <s t d : : u i n t 3 2 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
10 pen imageExporter ( std : : f u n c t i o n <s t d : : i n t 6 4 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
11 pen imageExporter ( std : : f u n c t i o n <s t d : : u i n t 6 4 t ( u n s i g n e d l o n g long , s i z e t )>
fin ) ;
12
13 // ∗∗ C o n s t r u c t o r s f o r images with a s s o c i a t e d u n c e r t a i n t y
14 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <f l o a t ( u n s i g n e d l o n g long , s i z e t , f l o a t &)>
fin ) ;
15 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <d o u b l e ( u n s i g n e d l o n g long , s i z e t , d o u b l e &)>
fin ) ;
16 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : i n t 8 t ( u n s i g n e d l o n g long , s i z e t , s t d
: : i n t 8 t &)> f i n ) ;
17 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : u i n t 8 t ( u n s i g n e d l o n g long , s i z e t , s t d
: : u i n t 8 t &)> f i n ) ;
18 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : i n t 1 6 t ( u n s i g n e d l o n g long , s i z e t , s t d
: : i n t 1 6 t &)> f i n ) ;
19 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : u i n t 1 6 t ( u n s i g n e d l o n g long , s i z e t ,
s t d : : u i n t 1 6 t &)> f i n ) ;
20 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : i n t 3 2 t ( u n s i g n e d l o n g long , s i z e t , s t d
: : i n t 3 2 t &)> f i n ) ;
21 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : u i n t 3 2 t ( u n s i g n e d l o n g long , s i z e t ,
s t d : : u i n t 3 2 t &)> f i n ) ;
22 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : i n t 6 4 t ( u n s i g n e d l o n g long , s i z e t , s t d
: : i n t 6 4 t &)> f i n ) ;
23 p e n i m a g e E x p o r t e r ( s t d : : f u n c t i o n <s t d : : u i n t 6 4 t ( u n s i g n e d l o n g long , s i z e t ,
s t d : : u i n t 6 4 t &)> f i n ) ;
Code 8: Image exporter constructors
6
4.1 User provided functions
First of all, the description of the functions with no associated uncertainties follows, as their
parameters are common to both function types. The arguments passed to these functions
are shown in the example of the code 11, which is used to export the DICOM contour masks
in the DICOM based geometry configuration method.
1 s t d : : f u n c t i o n <s t d : : u i n t 8 t ( u n s i g n e d l o n g long , s i z e t )> f =
2 [= , &mask ] ( u n s i g n e d l o n g long ,
3 s i z e t i ) −> s t d : : u i n t 8 t {
4
5 r e t u r n s t a t i c c a s t <s t d : : u i n t 8 t >(mask [ i ] ) ;
6
7 };
8
9 pen imageExporter exporter ( f ) ;
Code 9: Image exporter constructor example for DICOM contour masks
Although the return type can vary, the arguments are the same. The first one, with type
unsigned long long, is expected to be the number of simulated histories, which is not used
in this case. Secondly, the i parameter specify the pixel to be rendered, in the example, it
corresponds to the position inside the mask vector. Notice also the cast done to the returned
value. As the returned type must match any of the types in the available constructors (code
8), it has been casted from unsigned char to uint8 t.
Once constructed, some values must be set in the pen imageExporter instance to export
the image properly. First of all, a name must be provided to assign a filename to the
exporter image. This is done via the public variable baseName, which type is a std::string.
Then, the number of dimension, number of elements in each dimension and the element size
in each dimension must be specified using the member function setDimensions. Taking also
the DICOM mask exporter example, the number of dimensions in each mask are 3 (x,y,z),
and the number of elements and their size coincide with the number and size of voxels in
the DICOM, as is shown in the code 11. In addition, a origin could be provided using the
method setOrigin.
1
2 unsigned nElements [ 3 ] = {
3 static c a s t <unsigned >(dicom . getNX ( ) ) ,
4 static c a s t <unsigned >(dicom . getNY ( ) ) ,
5 static c a s t <unsigned >(dicom . getNZ ( ) ) } ;
6
7 f l o a t elementSizes [ 3 ] = {
8 s t a t i c c a s t <f l o a t >(dicom . getDX ( ) ) ,
9 s t a t i c c a s t <f l o a t >(dicom . getDY ( ) ) ,
10 s t a t i c c a s t <f l o a t >(dicom . getDZ ( ) ) } ;
11
12 e x p o r t e r . baseName = f i l e n a m e ;
13 e x p o r t e r . s e t D i m e n s i o n s ( 3 , nElements , e l e m e n t S i z e s ) ;
14 exporter . setOrigin ( origin ) ;
15
16 e x p o r t e r . exportImage ( 1 , p e n i m a g e E x p o r t e r : : formatTypes : :MHD) ;
Code 10: Image exporter configuration for DICOM contour masks
Regarding the functions with an associated uncertainties, these ones uses the same
arguments as discussed before and another one. That extra parameter is the associated
uncertainty to the returned value, which type must match with the returned type. For
example, the code
1
7
2 // R e g i s t e r data t o c r e a t e images
3 unsigned elements [ ] = {
4 s t a t i c c a s t <unsigned >(nx ) ,
5 s t a t i c c a s t <unsigned >(ny ) ,
6 s t a t i c c a s t <unsigned >(nz ) } ;
7
8 f l o a t delements [ ] = {
9 s t a t i c c a s t <f l o a t >(dx ) ,
10 s t a t i c c a s t <f l o a t >(dy ) ,
11 s t a t i c c a s t <f l o a t >(dz ) } ;
12
13 // ∗∗ C a l c u l a t e t h e o r i g i n
14 double o r i g i n [ 3 ] ;
15 // Get geometry o f f s e t
16 geometry . g e t O f f s e t ( o r i g i n ) ;
17 // Add t h e mesh o r i g i n
18 o r i g i n [ 0 ] += xmin ;
19 o r i g i n [ 1 ] += ymin ;
20 o r i g i n [ 2 ] += zmin ;
21
22 addImage<double >(” s p a t i a l D o s e D i s t r i b ” , 3 , e l e m e n t s , delements , o r i g i n ,
23 [=]( unsigned long long nhist ,
24 s i z e t i , d o u b l e& sigma ) −> d o u b l e {
25
26 c o n s t d o u b l e d h i s t s = s t a t i c c a s t <double >( n h i s t ) ;
27 c o n s t d o u b l e f a c t = ivoxMass [ i ] ;
28 c o n s t d o u b l e q = edep [ i ] / d h i s t s ;
29 sigma = edep2 [ i ] / d h i s t s − q∗q ;
30 i f ( sigma > 0 . 0 )
31 {
32 sigma = f a c t ∗ s q r t ( sigma / d h i s t s ) ;
33 }
34 else
35 {
36 sigma = 0 . 0 ;
37 }
38
39 r e t u r n q∗ f a c t ;
40 }) ;
Code 11: Image exporter constructor example for DICOM contour masks
5 Geometry
Geometry modules are used to both, locate and move the particles through the geometry
system. In PenRed, all geometry modules must inherit from the wrapper geometry class,
defined in the file,
src/kernel/includes/pen classes.hh
Creating a new geometry module directly from the base class wrapper geometry require
to define many functions whose behaviour is repeated in most cases, depending on the
geometry type. Therefore, PenRed provides two high level classes to develop geometry
modules depending on the method to construct the geometry system. The first case, involves
geometries constructed via objects, where each object is constituted by a single material.
The method to describe these objects is not restricted. For example, the objects can be
defined via quadric surfaces, a triangulated mesh etc. In the second case, the geometries
8
are constructed using a mesh where each element gets an independent index and no objects
are defined explicitly.
In the next sections the procedure to implement each type of geometry will be explained,
but first we will discuss some common information and mandatory methods that must be
defined by the developer regardless the geometry type.
• Any geometry module must be constituted by bodies. These bodies are filled with a
single material, identified by a unsigned integer index. In addition, the bodies can
belong to a detector, which is also identified by a unsigned integer index.
• Detector assignation is optional. If a body does not belong to any detector, the
detector index must be set to 0.
• The particle must be stopped when a interface is reached unless the new material
after the interface cross is void. In that case, the particle must be moved across the
void region until a non void region is reached or the particle escapes the geometry
system.
• A particle escapes from the geometry system when no non void region can be reached.
• When the particle escapes from the geometry system, it is moved an “infinite” dis-
tance, usually (1035 cm).
• getIBody (code 12): This method takes a body name (elementName) as the only
argument. Then, it is intended to return a body index identified by this name. If
the name does not corresponds to any defined body, the number of bodies must be
returned instead.
1
2 v i r t u a l u n s i g n e d getIBody ( c o n s t c h a r ∗ elementName ) c o n s t = 0 ;
Code 12: Declaration of the pure virtual method getIBody from the wrapper geometry
class.
For example, considering the case of quadric geometries (pen quadricGeo), the array
BALIAS belonging to each body is used to identify the body, as is shown in the code
13.
9
1
2 u n s i g n e d p e n q u a d r i c G e o : : getIBody ( c o n s t c h a r ∗ elementName ) c o n s t {
3
4 // C o n s t r u c t c o r r e c t e d a l i a s
5 char auxAlias [ 5 ] ;
6 s p r i n t f ( a u x A l i a s , ” %4.4 s ” , elementName ) ;
7 a u x A l i a s [ 4 ] = ’ \0 ’ ;
8 f o r ( u n s i g n e d j = 0 ; j < g e t E l e m e n t s ( ) ; j ++){
9 // Check i f body a l i a s i s t h e e x p e c t e d one
10 i f ( strcmp ( a u x A l i a s , b o d i e s [ j ] . BALIAS) == 0 ) {
11 return j ;
12 }
13 }
14 return getElements ( ) ;
15 }
Code 13: Definition of the method getIBody from the pen quadricGeo class.
• locate (code 15): The locate method handles the localization of a particle inside the
geometry system. For that purpose, the method takes the particle state as the only
parameter. Then, the locate method must update the body (IBODY) and material
(MAT) indexes of the input state accordingly to its variables. Usually, only the
position (X, Y, Z) is required for this purpose, but all the state is accessible.
1
2 v i r t u a l v o i d l o c a t e ( p e n p a r t i c l e S t a t e& s t a t e ) c o n s t = 0 ;
Code 15: Declaration of the pure virtual method locate from the wrapper geometry class.
In the case the particle is not inside the geometry system, the material index must
be set to void (0) and the body index to a value greater or equal to the number
of bodies. An example is shown in the code 16, where the locate method for the
voxelized geometry class pen voxelGeo is defined.
1
2 v o i d p en v o x e l G eo : : l o c a t e ( p e n p a r t i c l e S t a t e& s t a t e ) c o n s t {
3
4 long i n t ix , iy , i z ;
5
6 i x = s t a t e .X/dx ;
7 i y = s t a t e .Y/dy ;
8 i z = s t a t e . Z/ dz ;
9
10 i f ( i x < 0 | | ( l o n g u n s i g n e d ) i x >= nx | |
11 i y < 0 | | ( l o n g u n s i g n e d ) i y >= ny | |
12 i z < 0 | | ( l o n g u n s i g n e d ) i z >= nz ) {
13 // P a r t i c l e s c a p e s from geometry mesh
14 s t a t e . IBODY = c o n s t a n t s : :MAXMAT;
15 s t a t e .MAT = 0 ;
16 }
17 else{
10
18 // P a r t i c l e i s i n t h e geometry mesh , c a l c u l a t e v o x e l
19 // i n d e x and i t s m a t e r i a l
20 l o n g i n t i n d e x = i z ∗ nxy + i y ∗nx + i x ;
21 s t a t e .MAT = mesh [ i n d e x ] .MATER;
22 s t a t e . IBODY = s t a t e .MAT−1;
23 }
24
25 }
Code 16: Definition of the locate method for the pen voxelGeo class.
As can be seen, locate is defined as a constant method. Thus, is not possible changing
the geometry state during that function call.
• step (code 17): The step method handles the movement of the particle inside the
geometry system. To be able to achieve this purpose, all the assumptions of the
section 5.1 must be satisfied.
1
2 v i r t u a l v o i d s t e p ( p e n p a r t i c l e S t a t e& s t a t e ,
3 d o u b l e DS ,
4 d o u b l e &DSEF,
5 d o u b l e &DSTOT,
6 i n t &NCROSS) c o n s t = 0 ;
Code 17: Declaration of the pure virtual method step from the wrapper geometry class.
The step method takes several parameters, which are listed following,
– state: Provides the particle state to be moved. During the call, the state must
be changed to move the particle accordingly. In addition, the body (IBODY)
and material (MAT) indexes must be updated.
– DS: This input parameter stores the maximum distance to be traveled by the
particle. The travelled distance can be lesser if a interface is crossed. Notice that
the void regions must be skipped even if DS is lesser than the required distance
to cross a void region.
– DSEF: This output parameter must be filled with the traveled distance in the
original material, i.e. in the material index stored in the state before moving it.
Notice that, if the particle is located in a non void region and a interface with a
void region is reached, the distance traveled in the void region after the interface
cross must not be scored. However, if the particle is initially located in a void
region, DSEF must score the traveled distance until the next non void region or
until the particle escapes the geometry system.
– DSTOT: This output parameter must be filled with the total traveled distance,
regardless if a void region has been crossed or not. Therefore, if the particle
does not reached an interface or the material after the interface is not void, the
relation DST OT = DSEF must be accomplished. Instead, if the particle is
moving from a non void region and crosses a interface with a void material, the
DST OT > DSEF must be satisfied, because DSTOT must score both, the
traveled distance in the origin material (DSEF) and the traveled distance in the
void region.
– NCROSS: This output parameter scores the number of crossed interfaces, i.e.
its value must be set to 0 if the particle remains in the original material and
detector, or greater than 0 if an interface is crossed.
11
As the locate case, the step is defined as a constant method. Thus, it is not possible
changing the geometry state during that function call. The code 18 shows the simplest
possible step function, where the whole space is filled by a single material and no
interface can be crossed.
1
2 v o i d pen dummyGeo : : s t e p ( p e n p a r t i c l e S t a t e& s t a t e , d o u b l e DS , d o u b l e &DSEF
, d o u b l e &DSTOT, i n t &NCROSS) c o n s t {
3
4 DSEF = DS ;
5 DSTOT = DS ;
6 NCROSS = 0 ;
7 s t a t e .X += DS∗ s t a t e .U;
8 s t a t e .Y += DS∗ s t a t e .V;
9 s t a t e . Z += DS∗ s t a t e .W;
10 }
Code 18: Declaration of the step method of the pen dummyGeo class.
• configure (code 21): The configuration function takes as first argument a pen parserSection
(section 3) instance where all the information provided by the user to configure the
geometry is included. In addition, the main program includes information about the
configured materials. This information is included in a subsection named “materials”
where, at this moment, the index and density of each configured material is stored.
However this information could be extended in future implementations. The code 19
shows how this information is included.
1 //Append m a t e r i a l i n f o r m a t i o n t o geometry s e c t i o n
2
3 f o r ( u n s i g n e d imat = 0 ; imat < c o n t e x t . getNMats ( ) ; imat++){
4 c h a r key [ 4 0 0 ] ;
5 s p r i n t f ( key , ” m a t e r i a l s /mat%03d/ID” , imat +1) ;
6 g e o m e t r y S e c t i o n . s e t ( key , ( i n t ) imat +1) ;
7 s p r i n t f ( key , ” m a t e r i a l s /mat%03d/ d e n s i t y ” , imat +1) ;
8 g e o m e t r y S e c t i o n . s e t ( key , c o n t e x t . r e a d B a s e M a t e r i a l ( imat ) . readDens ( ) ) ;
9 }
Code 19: Material section included by the main program in the geometry subsection.
An example of how to read the material information can be found in the DICOM
geometry configuration function,
src/geometry/meshes/source/DICOM geo.cpp
12
13 // E x t r a c t m a t e r i a l names
14 matSec . l s ( matNames ) ;
15
16 // I t e r a t e o v e r a l l m a t e r i a l
17 f o r ( u n s i g n e d imat = 0 ; imat < matNames . s i z e ( ) ; imat++){
18
19 d o u b l e auxDens ;
20 i n t auxID ;
21
22 s t d : : s t r i n g i d F i e l d = matNames [ imat ] + s t d : : s t r i n g ( ” /ID” ) ;
23 s t d : : s t r i n g d e n s F i e l d = matNames [ imat ] + s t d : : s t r i n g ( ” / d e n s i t y ” ) ;
24
25 // Read m a t e r i a l ID
26 i f ( matSec . r e a d ( i d F i e l d . c s t r ( ) , auxID ) != INTDATA SUCCESS)
27
28 .
29 .
30 .
31
32 // Read d e n s i t y
33 i f ( matSec . r e a d ( d e n s F i e l d . c s t r ( ) , auxDens ) != INTDATA SUCCESS)
34
35 .
36 .
37 .
38 }
Code 20: Geometry material information read in the DICOM configuration function.
The second argument corresponds to a verbosity level, and must be used to control
the volume of printed information.
1
2 v i r t u a l i n t c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t u n s i g n e d
verbose ) = 0;
Code 21: Declaration of the pure virtual method configure for the wrapper geometry class.
During the configuration, the variable configStatus must be filled with a value that
depends on the configuration result. If the geometry has been configured successfully,
both, the configStatus and the returned value must be set to 0. Instead, a non 0
value on both, the configStatus or the returned value will be interpreted as a failed
configuration.
In addition, depending on the geometry type, some variables must be filled to en-
sure that the geometry module works properly. These variables will be explained in
the sections corresponding to body based (section 5.4) and mesh based (section 5.5)
geometries.
5.3 Registering
To be able to use a developed geometry module in the PenRed environment, regardless the
kind of geometry, the macros DECLARE GEOMETRY and REGISTER GEOMETRY must be used
(code 22).
1
2 DECLARE GEOMETRY( C l a s s )
3 REGISTER GEOMETRY( C l a s s , ID )
Code 22: Provided registration geometry macros.
13
The parameters of these macros corresponds to the class name (parameter Class) and a
text identifier to be able to specify the geometry type via the configuration file (parameter
ID).
The first one, the DECLARE GEOMETRY macro, must be placed inside the class definition,
just after the class name, as is shown in the example of the code 23.
1
2 c l a s s p e n q u a d r i c G e o : p u b l i c abc geometry <pen quadBody>{
3 DECLARE GEOMETRY( p e n q u a d r i c G e o )
4 .
5 .
6 .
7 }
Code 23: Usage of the DECLARE GEOMETRY in the pen quadricGeo geometry definition.
The second macro (REGISTER GEOMETRY), must be placed in the source file, outside the
scope of any function, as is shown in the code 24.
1
2 REGISTER GEOMETRY( pen voxelGeo ,VOXEL)
Code 24: Usage of the REGISTER GEOMETRY in the pen voxelGeo geometry source file.
In addition, the header and source files must be included in a specific file depending on
the geometry type. This step will be explained in the specific geometry type subsection.
src/geometry/includes/geometry classes.hh
where the only template parameter (bodyType) specifies the type of the bodies used to con-
struct the geometry. Some implemented examples can be found in the folder,
src/geometry/objects/
14
9 pen baseBody ( ) : MATER( 0 ) , KDET( 0 ) , DSMAX( 1 . 0 e35 ) {
10 // S e t body e n e r g y c u t o f f s t o ” i n f i n i t e ” by d e f a u l t
11 f o r ( u n s i g n e d i = 0 ; i < c o n s t a n t s : : nParTypes ; i ++){
12 localEABS [ i ] = 1 . 0 e −15;
13 }
14 }
15 };
Code 25: Definition of pen baseBody structure.
The required variables for any body are declared in the pen baseBody class and are
described following,
• DSMAX: Maximum allowed step length for particles with soft energy deposition
(electrons and positrons).
• localEABS: Array to store the local absorption energies for each particle type. Notice
that, in addition, the material absorption energy must be considered to determine the
final absorption energy. The most restrictive energy will be used in each body, i.e., if
matEABS corresponds to the material absorption energy, the final absorption energy
(EABS) will be set as,
An example of extended body can be found in the body type defined for the quadric
geometry implementation, named pen quadBody (code 26) and located in the file,
src/geometry/objects/includes/quadric geo.hh
1
2 s t r u c t pen quadBody : p u b l i c pen baseBody {
3
4 s t a t i c c o n s t u n s i g n e d i n t NXG = 2 5 0 ;
5
6 c h a r BALIAS [ 5 ] ;
7
8 u n s i g n e d i n t KBODY[NXG] ;
9 u n s i g n e d i n t KBOMO;
10
11 unsigned i n t KMOTH;
12 unsigned i n t KDGHT[NXG] ;
13
14 p e n b o d y S u r f s u r f s [NXG] ;
15
16 .
17 .
18 .
19 }
Code 26: Definition of pen quadBody structure.
15
5.4.2 Inherited variables
The abc geometry class provides a few variables that must be used to ensure a proper
operation of the developed geometry. These variables are defined following,
• NB: Is a constant unsigned integer that stores the maximum allowed number of
bodies. This constant takes its value from the pen geoconst namespace, which value
is 5000 by default. If a different number of allowed bodies is required, the developer
must change the value in the pen geoconst namespace. It is located in the file,
src/kernel/includes/pen constants.hh
• NBODYS: Variable unsigned integer that stores the number of bodies in the
current geometry. It is initialized to 0 and must be updated during the geometry
configuration.
• bodies: Array of bodies with dimension NB. The array type corresponds to the
geometry body type, which is determined via the geometry template argument. This
array must be filled with the constructed bodies during the geometry configuration.
The number of defined bodies in the array is assumed to be equal to NBODYS.
and
respectively.
src/geometry/includes/geometry classes.hh
where the template argument meshElement specify the type of the mesh elements used to
construct the geometry.
In mesh based geometries, to be able to use all tallies and sources, each non void material
is considered as a independent body. Thus, the IBODY index can be obtained as,
IBODY = M AT (2)
16
for non void materials. In addition, the body index 0 is reserved for a enclosure that is
supposed to include the whole mesh. This one is created to be able to consider the backscat-
tering effects in the mesh frontiers. Depending on the implementation, the enclosure could
be considered as a detector. This approach forces an interface between the enclosure and
the mesh regardless the mesh element material. Some implemented examples can be found
in the folder,
src/geometry/meshes/
As is shown, the only requirement for a mesh element is a material index (MATER). It
is because, as we discussed, mesh based geometries are supposed to identify each non void
material with a body. Thus, each element is not a body itself, but a body portion. For this
reason, the default mesh element have not a detector index or local energy absorption, as
this variables are intended to be set for each body. Moreover, in meshes, it has no sense to
define a local energy absorption for each body, because it will produce the same effect as
defining the same energy absorption for the whole material.
Therefore, to be able to control the detector index and the maximum distance between
hard interactions in soft energy loss steps, a set of variables are defined in the abc mesh
class and discussed in the next section.
• mesh: Is a pointer which type coincide with the mesh elements type, defined in the
template parameter meshElement. This pointer will store the geometry mesh as an
array of meshElement. To set the array size, the developer must use the method
resizeMesh (code 28), which takes the new mesh dimension as the only parameter.
Take into account that the mesh is completely cleaned when is resized, i.e the stored
elements are destroyed.
1
2 v o i d r e s i z e M e s h ( u n s i g n e d dim ) ;
Code 28: resizeMesh method pen quadBody structure.
17
• meshDim: This variable stores the dimension of the mesh array, i.e. the number of
elements that can store. The value of the meshDim variable must not be changed,
as is handled automatically by the resizeMesh method.
• nElements: Must store the number of mesh elements. The developer must set its
value during the geometry configuration.
• DSMAX: Array with dimension of the maximum number of allowed materials (con-
stants::MAXMAT). This array stores the maximum allowed step length for particles
with soft energy deposition (electrons and positrons) in each body. Notice that a
body is constituted by all elements with the same non void material. Therefore, the
body index can be obtained from the equation 2. This array must be filled during the
geometry configuration.
• KDET: Array with dimension of the maximum number of allowed materials (con-
stants::MAXMAT). Each array element stores the detector index of one body. This
array must be filled during the geometry configuration.
• nBodies: Variable to store the number of bodies in the geometry, i.e. the number
of non void materials plus one for the enclosure. This value must be set during the
geometry configuration.
and
respectively.
6 Tallies
Tallies are used to extract information from the simulation, such as absorbed energy, particle
fluence and spectrums etc. All tallies should be created as derived classes of a common
interface defined in the class,
pen genericTally
src/tallies/includes/
src/tallies/source/
18
Notice that pen genericTally is a template class which takes the particle state type
as unique argument. However, actually, only tallies with the base particle state (class
pen particleState) can be used with the provided main program. Therefore, tallies must
inherit from,
The following sections describe how to implement a custom tally. To exemplify the
procedure, a dummy tally named pen tallyDummyLog has been created and included in the
PenRed package. The corresponding files can be used as template to create new tallies,
src/tallies/generic/includes/tallyDummyLog.hh
src/tallies/generic/source/tallyDummyLog.cpp
In the same folders several examples of different implemented tallies can be found.
where nhist stores the particle history number, kdet the detector where the particle is
located, kpar the corresponding particle type index and, finally, state the actual particle
state. The particle indexes can be found in the Particle indexes subsection in the Con-
stants, parameters and definitions section of the usage documentation. Not all functions
get all these parameters as arguments, furthermore, some ones requires another specific pa-
rameters. Then, following we will describe all the available callbacks and their parameters.
In addition, the code corresponding to the implementation of the tally pen tallyDummyLog
is provided for each callback.
Important!
The function tally beginHist has been replaced by tally sampledPart to provide
a better support for specific sources. This change applies since version 1.3.1.
• tally beginSim: This function does not get any argument and is called when the
global simulation begins, i.e., is not called on every new history. Moreover, is not
called when a new source simulation begins. However, notice that if the simulation is
resumed via a dump file, this function will be called at the beginning of the resumed
simulation too.
1 v o i d pen tallyDummyLog : : t a l l y b e g i n S i m ( ) {
2 // This f u n c t i o n i s c a l l e d a t t h e
19
3 // b e g i n n i n g o f t h e g l o b a l s i m u l a t i o n
4
5 p r i n t f ( ” G l o b a l s i m u l a t i o n b e g i n s \n” ) ;
6 }
7
• tally endSim: Called when the global simulation ends. Thus, this function is ex-
pected to be called only one time per simulation. This function takes the number of
the last simulated history as the only argument.
1 v o i d pen tallyDummyLog : : t a l l y e n d S i m ( c o n s t u n s i g n e d l o n g l o n g n h i s t ) {
2
3 // This f u n c t i o n i s c a l l e d when t h e s i m u l a t i o n ends ,
4 // ” n h i s t ” t e l l s us t h e l a s t h i s t o r y s i m u l a t e d
5
6 p r i n t f ( ” S i m u l a t i o n ends a t h i s t o r y number %l l u \n” , n h i s t ) ;
7 }
8
• tally move2geo: Called when a particle has been sampled by a source into a void
region, and has been moved forward to check if it arrives to any non void volume
via the step geometry function. Notice that the function is called after moving the
particle, thus if the particle remains in a void material means that it escaped from the
geometry system. Instead, if the particle reaches a non void volume, the simulation
will continue, i.e. the particle simulation is considered as begun. Otherwise, this
particle simulation never began and will be considered as ended. Therefore, this
function is called when the particle simulation has not been started. Notice also that
this function is only called when a particle is sampled in a void region. If the particle
is sampled in a non void region of the geometry system, this function will not be
triggered.
Regarding the two extra variables (dsef and dstot) these provide the corresponding
output values of the step function. As the particle is initially in a void region, dsef
and dstot stores the same value. However, both values are provided to allow future
features.
1 v o i d pen tallyDummyLog : : t a l l y m o v e 2 g e o ( c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/
,
2 c o n s t u n s i g n e d /∗ kdet ∗/ ,
3 c o n s t pen KPAR kpar ,
4 c o n s t p e n p a r t i c l e S t a t e& s t a t e ,
5 const double dsef ,
6 c o n s t d o u b l e /∗ d s t o t ∗/ ) {
7 // This f u n c t i o n i s c a l l e d when a new p a r t i c l e
8 // i s sampled i n t o a v o i d zone and , then , i s
9 //moved t o f i n d a geometry non v o i d zone .
10
11 p r i n t f ( ”A %s has been c r e a t e d i n a v o i d r e g i o n . ” , p a r t i c l e N a m e ( kpar ) ) ;
12 i f ( s t a t e .MAT != 0 ) {
13 p r i n t f ( ” A f t e r moving i t %E cm , has s t r i k e d t h e geometry ”
14 ” body %u which m a t e r i a l i s %u . \ n”
15 , d s e f , s t a t e . IBODY, s t a t e .MAT) ;
16 }
17 else{
18 p r i n t f ( ” There a r e n ’ t any non v o i d r e g i o n on i t s ”
20
19 ” d i r e c t i o n (%E,%E,%E) . \ n” , s t a t e . U, s t a t e . V, s t a t e .W) ;
20 p r i n t f ( ”The ’ t a l l y b e g i n P a r t ’ f u n c t i o n w i l l not be ”
21 ” triggered for this particle , ”
22 ” but t h e ’ t a l l y e n d P a r t ’ . \ n” ) ;
23 }
24 }
25
• tally sampledPart: Called when a new particle is sampled. When this function is
called, the state of the sampled particle is the ones which the sampler function creates,
i.e. if the particle has been created in a void volume (M AT = 0), this function
will be called before trying to move the particle to the next non void volume. So,
tally sampledPart is triggered before than tally move2geo. The parameter dhist
corresponds to the number of skipped histories since the previous sampled particle.
Notice that a specific sampler can create several particles within the same history.
Moreover, a specific sampler can also skip several histories in a single sample call. For
instance, this behaviour is usually found when the phase space file (PSF) sampler is
used.
1 v o i d pen tallyDummyLog : : t a l l y s a m p l e d P a r t ( c o n s t u n s i g n e d l o n g l o n g n h i s t ,
2 c o n s t u n s i g n e d l o n g l o n g /∗ d h i s t ∗/ ,
3 c o n s t u n s i g n e d /∗ kdet ∗/ ,
4 c o n s t pen KPAR /∗ kpar ∗/ ,
5 c o n s t p e n p a r t i c l e S t a t e& /∗ s t a t e ∗/ ) {
6
7 // This f u n c t i o n i s c a l l e d when a new p a r t i c l e i s sampled
8 p r i n t f ( ” S i m u l a t i o n o f sampled p a r t i c l e i n h i s t o r y %l l u b e g i n s \n” ,
nhist ) ;
9 }
10
• tally endHist: Called when a history ends its simulation, i.e. primary particle and
all his secondary generated particles has been simulated.
1 v o i d pen tallyDummyLog : : t a l l y e n d H i s t ( c o n s t u n s i g n e d l o n g l o n g n h i s t ) {
2
3 // This f u n c t i o n i s c a l l e d when a h i s t o r y ends i t s s i m u l a t i o n
4
5 p r i n t f ( ” S i m u l a t i o n o f h i s t o r y %l l u ends \n” , n h i s t ) ;
6
7 }
8
21
5
6 // C a l l e d when t h e s i m u l a t i o n o f some p a r t i c l e b e g g i n s .
7 //At t h i s p o i n t , t h e p a r t i c l e must be i n s i d e t h e
8 // geometry system i n t o a non v o i d r e g i o n .
9
10 p r i n t f ( ”A %s from h i s t o y %l l u b e g i n s i t s s i m u l a t i o n . \ n” ,
11 p a r t i c l e N a m e ( kpar ) , n h i s t ) ;
12 p r i n t f ( ” I s l o c a t e d a t body %u wich m a t e r i a l i s %u . \ n” ,
13 s t a t e . IBODY, s t a t e .MAT) ;
14
15 }
16
• tally localEdep: Called when a particle losses energy locally during its simulation.
It is triggered, for example, on interactions or particle absorption, but does not trigger
on energy losses by soft interactions during a step. Notice that part of the lost energy
can be used to create new particles. So, energy lost is not the energy absorbed by the
material. To measure the absorbed energy properly, the energy of the generated sec-
ondary particles must be considered in the tally beginPart callback. The argument
dE, as its name suggest, stores the amount of deposited energy in eV.
1 v o i d pen tallyDummyLog : : t a l l y localEdep (
2 const u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
3 const pen KPAR kpar ,
4 const p e n p a r t i c l e S t a t e& /∗ s t a t e ∗/ ,
5 const d o u b l e dE) {
6
7 // C a l l e d when t h e p a r t i c l e l o s s e s e n e r g y l o c a l l y d u r i n g i t s
8 // s i m u l a t i o n i . e . t h e e n e r g y l o s s i s not c o n t i n u o u s on a
9 // t r a v e l e d s t e p
10
11 p r i n t f ( ”%s l o s s e s e n e r g y (%E eV ) l o c a l l y \n” ,
12 p a r t i c l e N a m e ( kpar ) ,dE) ;
13 }
14
• tally step: Called after step call during particle simulation. This function will be
called after the update of particle age, i.e. after dpage call. Notice that will not be
triggered when step is used to move a new sampled particle to the geometry when
22
has been created in a void volume (as tally move2geo), because the simulation has
not already started. Therefore, the tally step is triggered only after the particle
simulation begins.
As extra argument, this function receives stepData. Its type corresponds to a structure
named tally StepData which definition can be found in the code 38.
1 struct tally StepData {
2 double d s e f ;
3 double dstot ;
4 d o u b l e softDE ;
5 double softX ;
6 double softY ;
7 double s o f t Z ;
8 u n s i g n e d originIBODY ;
9 u n s i g n e d originMAT ;
10 };
11
First both variables (dsef and dstot) store the corresponding output values of the
step function. Then, softDE stores the energy deposited due the traveled step. That
energy has been deposited at the point (softX,softY,softZ). Finally, originIBODY and
originMAT save the particle IBODY and MAT values, respectively, before the step
call.
1 v o i d pen tallyDummyLog : : t a l l y s t e p ( c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
2 const pen KPAR kpar ,
3 const p e n p a r t i c l e S t a t e& /∗ s t a t e ∗/ ,
4 const t a l l y S t e p D a t a& stepData ) {
5
6 // C a l l e d a f t e r a s t e p c a l l d u r i n g t h e p a r t i c l e
7 // s i m u l a t i o n s i . e . a f t e r ” t a l l y b e g i n P a r t ” has been
8 // c a l l e d f o r c u r r e n t p a r t i c l e
9
10 i f ( stepData . d s t o t > stepData . d s e f )
11 p r i n t f ( ”%s moves %E cm i n t h e o r i g i n m a t e r i a l (%d ) and ”
12 ”%E cm i n v o i d r e g i o n s . \ n” ,
13 p a r t i c l e N a m e ( kpar ) , stepData . d s e f ,
14 stepData . originMAT , stepData . d s t o t −stepData . d s e f ) ;
15 else
16 p r i n t f ( ”%s moves %E cm i n t h e o r i g i n m a t e r i a l (%d ) . \ n” ,
17 p a r t i c l e N a m e ( kpar ) , stepData . d s e f ,
18 stepData . originMAT ) ;
19
20 // Check i f t h e p a r t i c l e l o s s e s e n e r g y d u r i n g t h e s t e p
21 i f ( stepData . softDE > 0 . 0 ) {
22 p r i n t f ( ” During i t s t r a v e l , t h e p a r t i c l e l o s s e s %E eV . \ n” , stepData .
softDE ) ;
23 p r i n t f ( ” C o n s i d e r e t h a t t h i s e n e r g y has been ”
24 ” d e p o s i t e d a t P=(%E,%E,%E) . \ n” ,
25 stepData . softX , stepData . softY , stepData . s o f t Z ) ;
26 }
27 }
28
• tally interfCross: Called when a particle cross an interface during its simulation.
An interface has been crossed when the step method returns a NCROSS with a non
23
zero value. Moving particles sampled in void regions to the geometry will not trigger
this function.
1 v o i d pen tallyDummyLog : : t a l l y i n t e r f C r o s s (
2 c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
3 c o n s t u n s i g n e d /∗ kdet ∗/ ,
4 c o n s t pen KPAR kpar ,
5 c o n s t p e n p a r t i c l e S t a t e& /∗ s t a t e ∗/ ) {
6 // C a l l e d when t h e p a r t i c l e c r o s s e s
7 // an i n t e r f a c e d u r i n g t h e s i m u l a t i o n .
8 p r i n t f ( ”%s c r o s s e d an i n t e r f a c e . \ n” , p a r t i c l e N a m e ( kpar ) ) ;
9
10 }
11
• tally matChange: Called only when the particle change material during the simu-
lation. Moving particles sampled in void regions to the geometry will not trigger this
function.
1 v o i d pen tallyDummyLog : : t a l l y m a t C h a n g e (
2 c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
3 c o n s t pen KPAR kpar ,
4 c o n s t p e n p a r t i c l e S t a t e& s t a t e ,
5 c o n s t u n s i g n e d prevMat ) {
6 // C a l l e d when t h e p a r t i c l e c r o s s e s an i n t e r f a c e and e n t e r s
7 // i n a new m a t e r i a l d u r i n g t h e s i m u l a t i o n
8
9 p r i n t f ( ”%s go from m a t e r i a l %u t o m a t e r i a l %u . \ n” ,
10 p a r t i c l e N a m e ( kpar ) , prevMat , s t a t e .MAT) ;
11
12 }
13
• tally jump: Triggered immediately after each jump call during the particle simula-
tion. The parameter ds stores the distance to travel obtained from the jump method.
1 v o i d pen tallyDummyLog : : t a l l y j u m p ( c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
2 c o n s t pen KPAR kpar ,
3 c o n s t p e n p a r t i c l e S t a t e& s t a t e ,
4 c o n s t d o u b l e ds ) {
5 // C a l l e d a f t e r jump f u n c t i o n d u r i n g t h e p a r t i c l e s i m u l a t i o n
6 p r i n t f ( ”%s w i l l t r y t o t r a v e l %E cm f o l l o w i n g ”
7 ” t h e d i r e c t i o n (%E,%E,%E) . \ n” ,
8 p a r t i c l e N a m e ( kpar ) , ds , s t a t e . U, s t a t e . V, s t a t e .W) ;
9 }
10
• tally knock: Triggered immediately after knock call during particle simulation. The
parameter icol stores the interaction index computed by the knock function. The
correspondence of these indexes can be found in the PenRed usage manual.
1 v o i d pen tallyDummyLog : : t a l l y k n o c k ( c o n s t u n s i g n e d l o n g l o n g /∗ n h i s t ∗/ ,
2 c o n s t pen KPAR kpar ,
3 c o n s t p e n p a r t i c l e S t a t e& /∗ s t a t e ∗/ ,
4 const int i c o l ) {
5 // C a l l e d a f t e r knock f u n c t i o n d u r i n g t h e s i m u l a t i o n
24
6 p r i n t f ( ”%s has i n t e r a c t with t h e m a t e r i a l v i a t h e ”
7 ” i n t e r a c t i o n with i n d e x %d . \ n” ,
8 p a r t i c l e N a m e ( kpar ) , i c o l ) ;
9 }
10
• tally lastHist: Triggered to update the number of previous histories, for example
on source changes or for resumed simulations. Tallies that use information about
last registered history, such as the creation of phase space files, can’t work property
without this information.
1 v o i d pen tallyDummyLog : : t a l l y l a s t H i s t ( c o n s t u n s i g n e d l o n g l o n g l a s t h i s t )
{
2 // C a l l e d when a s o u r c e b e g i n s i t s s i m u l a t i o n .
3 // ” l a s t h i s t ” t e l l s us what i s t h e i n i t i a l h i s t o r y
4 // t o be s i m u l a t e d a t t h i s s o u r c e .
5
6 p r i n t f ( ”New s o u r c e b e g i n s a t h i s t o r y number %l l u \n” ,
7 lasthist ) ;
8
9 }
10
USE BEGINSIM
USE ENDSIM
USE SAMPLEDPART
USE ENDHIST
USE MOVE2GEO
USE BEGINPART
USE ENDPART
USE JUMP
USE STEP
USE INTERFCROSS
USE MATCHANGE
USE KNOCK
USE LOCALEDEP
25
USE LASTHIST
where each flag name specify the corresponding function. To provide an example, the code
45 shows the constructor of the material energy deposition tally (pen EdepMat class). This
one includes the flags of all necessary callbakcs.
1
2 pen EdepMat ( ) : p e n g e n e r i c T a l l y ( USE ELOSS |
3 USE BEGINPART |
4 USE SAMPLEDPART |
5 USE ENDHIST |
6 USE MOVE2GEO)
7 {}
Code 45: Tally flag usage on implemented tallies.
• saveData: Called when data report occurs. This function must store the data of
interest in a file. Gets as argument the number of simulated histories. It is not
required to care about the output file names, as the tally cluster will add automatically
a prefix to all created files regarding its name, thread identifier and MPI process
number. Notice that the saveData function can be called only when a history finishes
its simulation. The signature is shown in the Code 46.
1 v i r t u a l v o i d saveData ( c o n s t u n s i g n e d l o n g l o n g n h i s t ) c o n s t = 0 ;
Code 46: Tally saveData function.
• flush: This function handles, if needed, the calculation of final results. Commonly,
tallies use auxiliary and temporal data buffers to avoid recalculating unchanged bins.
After the flush call, all the measured data is expected to be stored in the corresponding
final buffers. This function is called automatically when the simulated data is needed,
for example before the saveData function. The signature is shown in the Code 47.
1 v i r t u a l void f l u s h ( ) = 0 ;
Code 47: Tally flush function.
• sumTally: This function gets a second tally instance of the same type to sum their
results. The sum must be stored in the tally which calls sumTally, not in the argument
instance. On success, this function returns 0. This function is used, for example, to
sum-up the results of different threads on multi-threading simulations. The signature
is shown in the Code 48.
1 i n t sumTally ( c o n s t t a l l y T y p e& t a l l y ) ;
Code 48: Tally sumTally function. The tallyType type must coincide with the developed
tally class type.
26
1
2 virtual int configure (
3 c o n s t wrapper geometry& geometry ,
4 c o n s t a b c m a t e r i a l ∗ c o n s t m a t e r i a l s [ c o n s t a n t s : :MAXMAT] ,
5 c o n s t p e n p a r s e r S e c t i o n& c o n f i g ,
6 const unsigned verbose ) = 0 ;
Code 49: Tally configuration function.
The config function must parse and save the user specified parameters to configure
the tally. This data must be extracted from the pen parserSection structure, which
description can be found in the section 3. The verbose parameter specify the output
verbose level.
On the other hand, geometry and materials provide all the simulation geometry and
materials information which could be used by the tally. For example, a tally could be
specific for some geometry or use that information to obtain the mass of virtual mesh
elements to calculate the absorbed dose. Notice that the material with index N in the
geometry system corresponds to the position N − 1 in the materials array, because
the void (M AT = 0) is not a material itself. Therefore, the void is not included in
the materials array.
In addition, the configuration function must register the necessary data to store the
tally state using the member variable dump, which is provided by the tally inter-
face. This dump, handles the tally state writing and reading in binary format. Both
processes are automatically handled by the tally cluster, however the user must spec-
ify which variables must be dumped. To specify the variables to save and load the
developer must use the method toDump. An example is shown in the code 50.
1
2 // R e g i s t e r data t o dump
3 dump . toDump ( edptmp , nmat ) ;
4 dump . toDump ( edep , nmat ) ;
5 dump . toDump ( edep2 , nmat ) ;
6 dump . toDump(&nmat , 1 ) ;
Code 50: Tally dump registration for tallyEnergyDepositionMat.
As we can see, only one function is required to register the tally data. The toDump
function will register the data for both purposes, write and read binary dumps. to-
Dump, gets two arguments. The first one, is a pointer to the data to register. The
second one, is the number of elements to register from that pointer. Notice that
toDump is overloaded to accept the basic data types in order to simplify its usage.
However, can’t handle user defined structures.
• sharedConfig: This function gets a second tally instance of the same type to get
values after the configuration or perform any other task. The function is called just
after the configuration function. It is only called by tallies with a thread identifier
greater than 0 getting as argument the corresponding tally with the thread identifier 0.
Therefore, this function exists to be able to perform expensive configuration calculus
in the thread 0 tally and, then, share the results with the other threads, avoiding
27
repeating the calculus and duplication of data. The function must return a value of 0
on success. The signature is shown in the Code 53.
1 i n t s h a r e d C o n f i g ( c o n s t t a l l y T y p e& t a l l y )
Code 51: Tally sharedConfig function. Notice that the tallyType type must coincide with
the developed tally class type.
• readStack: Gets a kpar value as argument and returns the particle stack correspond-
ing to this kpar. Notice that the stack is only available for reading and its state can
not be modified. Notice that stacks are only available in the simulation callbacks and
can not be used in the configuration function.
1 i n l i n e c o n s t a b c p a r t i c l e S t a c k ∗ r e a d S t a c k ( c o n s t pen KPAR kpar ) c o n s t
Code 53: Tally readStack function.
6.6 Register
Implemented tallies must be registered using the
DECLARE TALLY
and
macros (code 54). These macros, takes as arguments the class name of the developed tally
(Class parameter), the particle state type required by the tally (State parameter) and a
unique tally name (ID parameter), which will be used in the configuration file to specify its
type.
1
2 DECLARE TALLY( C l a s s , S t a t e )
3 REGISTER COMMON TALLY( C l a s s , ID )
Code 54: Tally register macros.
The first one must be placed inside the class declaration, just after the class name, as is
shown in the code 55. That code corresponds to the pen tallyDummyLog class definition.
1
2 c l a s s pen tallyDummyLog : p u b l i c p e n g e n e r i c T a l l y <p e n p a r t i c l e S t a t e > {
3 DECLARE TALLY( pen tallyDummyLog , p e n p a r t i c l e S t a t e )
4 .
5 .
6 .
7 }
Code 55: Tally pen tallyDummyLog declaration macro.
28
The second one must be placed in the source file, as is shown in the code 56.
1
2 REGISTER COMMON TALLY( pen tallyDummyLog , DUMMY LOG EXAMPLE)
Code 56: Tally pen tallyDummyLog register macro.
Finally, a include of the header and source file names must be appended to the lists
inside the files
src/tallies/generic/includes/genericTallies.hh
and
src/tallies/generic/source/genericTallies.cpp
respectively.
7 Source samplers
Source samplers are used to sample the particle states to be simulated. The samplers are
classified in five categories, spatial, direction, energy, time and specific. Each one
has its own purpose, which is explained following,
• Spatial: Samples initial particle position X,Y ,Z. The IBODY and M AT identifiers
will be determined using the sampled position, but are not set during the sampling.
• Time: Samples initial particle age (variable P AGE). This is an optional sampler
type.
• Specific: Unlike previous samplers, specific samplers gets the whole particle state
and can modify all its variables. In addition, specific samplers are not restricted to
be used on generic particle states, and can be defined for other states.
Thus, we will consider the Spatial, Direction, Energy and Time samplers as generic
samplers, because these can be used in any particle state.
How to implement a sampler of each type will be described in the following sections. The
main differences between different samplers are the state variables which are calculated by
the sampler. However, all samplers shares a common function that must be implemented
by the developer. This one is the configure virtual method, which signature is sampler
dependent. However, all configure methods must return an integer value. A returned 0
value is interpreted as success, while nonzero as failed.
In addition, the samplers could require geometry information to perform their calculus.
For this reason, when a geometry is set to the samplers, the virtual method updateGeometry
(code 57) is triggered and the sampler can use and store any information provided by the
geometry. This method can be used in all sampler types and must be overwritten by the
developer to use the geometry information, if it is required. Notice that during the configure
execution the geometry is not accessible, thus must be accessed in the updateGeometry
method.
29
1
2 v i r t u a l v o i d updateGeometry ( c o n s t wrapper geometry ∗ geometryIn ) {}
Code 57: Sampler updateGeometry virtual function.
7.1 Register
To register a sampler, the developer must use the provided macros to declare and register
the sampler. On the one hand, the “declare sampler” macro is shown in the code 58, which
gets a single argument corresponding to the sampler class name.
1
2 DECLARE SAMPLER( C l a s s )
Code 58: Sampler declaration macro.
This macro must be used inside the sampler class definition, just after the class name,
as is shown in the code 59 for the spatial box sampler case. The same procedure is used to
declare all samplers, regardless its type.
1
2 c l a s s box spatialSampling : public abc spatialSampler {
3
4 DECLARE SAMPLER( b o x s p a t i a l S a m p l i n g )
5
6 .
7 .
8 .
9 }
Code 59: Sampler declaration macro example.
On the other hand, two “register sampler” macros are provided, which are shown in the
code 60.
1
2 REGISTER SAMPLER( C l a s s , ID )
3 REGISTER SPECIFIC SAMPLER( C l a s s , S t a t e , ID )
Code 60: Sampler registration macros.
Only specific samplers must use the REGISTER SPECIFIC SAMPLER macro,
while the other types must use the REGISTER SAMPLER macro. There, the Class
parameter corresponds to the sampler class name, the specific sampler compatible particle
state class is specified by the State parameter, and the ID parameter provides a unique
identifier for the registered sampler. Both macros must be called in the source file, out
of any function scope. An example of each macro usage is shown in the code 61, where
the box spatialSampling and gammaPolarised specificSampler are used for the generic and
specific sampler registration respectively.
1
2 REGISTER SAMPLER( b o x s p a t i a l S a m p l i n g ,BOX)
3 REGISTER SPECIFIC SAMPLER( g a m m a P o l a r i s e d s p e c i f i c S a m p l e r , p e n s t a t e g P o l ,
GAMMA POL)
Code 61: Sampler register macros.
However, notice that a specific sampler does not require to use a non generic particle
state. In fact, a specific sampler can be used to sample basic particle states. This is the
case of the phase space file sampler, which register macro is shown in the code 62.
30
1
2 REGISTER SPECIFIC SAMPLER( p s f s p e c i f i c S a m p l e r , p e n p a r t i c l e S t a t e , PSF)
Code 62: Sampler register macro with generic particle state.
7.2 Spatial
Custom spatial samplers must inherit from the base class abc spatialSampler as,
src/particleGen/includes/pen samplers.hh
src/particleGen/spatial
The samplers are intended to provide a initial position to be rotated and translated.
Thus, the resulting sampled position (X 0 ), in cm, follows the expression,
X 0 = T + R · X, (3)
where T is a translation, R a rotation matrix, and X the initial sampled position. Both,
T and R are variables provided in the base class abc spatialSampler, corresponding to the
double translation[3];
and
double rotation[9];
variables, respectively. On the one hand, the translation variable can be accessed directly in
derived classes and is intended to be filled in the configure function. On the other hand, the
rotation variable should be filled also during the configure execution, but using the public
method setRotationZYZ (code 63), which takes as arguments three Euler angles to calculate
the final rotation matrix. Notice that the Euler angles axis follows the same schema as the
PENELOPE package, i.e. the rotations are performed using the Z, Y , Z axis. Notice that
the rotation is optional, thus if no rotation matrix constructed using the setRotationZYZ
method, no rotation will be applied to the sampled position.
1
2 v o i d setRotationZYZ ( c o n s t d o u b l e omega , c o n s t d o u b l e t h e t a , c o n s t d o u b l e p h i )
Code 63: Set rotation method.
There are no more common variables to fill in the spatial sampler, remaining the vari-
ables required by the implemented sampler. Turning on functions, the only two required
methods to implement a spatial sampler are the configure and the geoSampling methods,
which signature is shown in the code 64.
1
2 v i r t u a l i n t c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t u n s i g n e d
verbose = 0) = 0 ;
31
3
4 v i r t u a l v o i d geoSampling ( d o u b l e pos [ 3 ] , pe n r an d& random ) c o n s t = 0 ;
Code 64: Spatial sampler mandatory functions.
First, the configure method takes a pen parserSection (section 3) as first argument,
which includes all the data specified by the user to configure the sampler. In addition, the
parameter verbose must be used to control the number of prints. An example is shown in the
code 65, which corresponds to the box spatial sampler. As can be seen in the configuration
function, this one does not use rotations.
1
2 i n t b o x s p a t i a l S a m p l i n g : : c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t
unsigned verbose ) {
3
4 int err ;
5
6 e r r = c o n f i g . r e a d ( ” s i z e /dx” , dx ) ;
7 i f ( e r r != INTDATA SUCCESS) {
8 i f ( verbose > 0) {
9 p r i n t f ( ” b o x S p a t i a l : c o n f i g u r e : u n a b l e t o r e a d ’ s i z e /dx ’ i n c o n f i g u r a t i o n .
Double e x p e c t e d \n” ) ;
10 }
11 r e t u r n −1;
12 }
13
14 e r r = c o n f i g . r e a d ( ” s i z e /dy” , dy ) ;
15 i f ( e r r != INTDATA SUCCESS) {
16 i f ( verbose > 0) {
17 p r i n t f ( ” b o x S p a t i a l : c o n f i g u r e : u n a b l e t o r e a d ’ s i z e /dy ’ i n c o n f i g u r a t i o n .
Double e x p e c t e d \n” ) ;
18 }
19 r e t u r n −1;
20 }
21
22 e r r = c o n f i g . r e a d ( ” s i z e / dz ” , dz ) ;
23 i f ( e r r != INTDATA SUCCESS) {
24 i f ( verbose > 0) {
25 p r i n t f ( ” b o x S p a t i a l : c o n f i g u r e : u n a b l e t o r e a d ’ s i z e / dz ’ i n c o n f i g u r a t i o n .
Double e x p e c t e d \n” ) ;
26 }
27 r e t u r n −1;
28 }
29
30 i f ( dx < 0 . 0 | | dy < 0 . 0 | | dz < 0 . 0 ) {
31 r e t u r n −2;
32 }
33
34 dx05 = dx ∗ 0 . 5 ;
35 dy05 = dy ∗ 0 . 5 ;
36 dz05 = dz ∗ 0 . 5 ;
37
38 e r r = c o n f i g . r e a d ( ” p o s i t i o n /x” , t r a n s l a t i o n [ 0 ] ) ;
39 i f ( e r r != INTDATA SUCCESS) {
40 i f ( verbose > 0) {
41 p r i n t f ( ” b o x S p a t i a l : c o n f i g u r e : u n a b l e t o r e a d ’ p o s i t i o n /x ’ i n
c o n f i g u r a t i o n . Double e x p e c t e d \n” ) ;
42 }
43 r e t u r n −2;
44 }
45
46 e r r = c o n f i g . r e a d ( ” p o s i t i o n /y” , t r a n s l a t i o n [ 1 ] ) ;
32
47 i f ( e r r != INTDATA SUCCESS) {
48 i f ( verbose > 0) {
49 p r i n t f ( ” b o x S p a t i a l : c o n f i g u r e : u n a b l e t o r e a d ’ p o s i t i o n /y ’ i n
c o n f i g u r a t i o n . Double e x p e c t e d \n” ) ;
50 }
51 r e t u r n −2;
52 }
53
54 e r r = c o n f i g . read ( ” p o s i t i o n /z” , t r a n s l a t i o n [ 2 ] ) ;
55 i f ( e r r != INTDATA SUCCESS) {
56 i f ( verbose > 0) {
57 p r i n t f ( ” boxSpatial : c o n f i g u r e : unable to read ’ p o s i t i o n /z ’ in
c o n f i g u r a t i o n . Double e x p e c t e d \n” ) ;
58 }
59 r e t u r n −2;
60 }
61
62 i f ( verbose > 1) {
63 p r i n t f ( ”Box c e n t e r ( x , y , z ) : \ n %12.4E %12.4E %12.4E\n” , t r a n s l a t i o n [ 0 ] ,
translation [1] , translation [2]) ;
64 p r i n t f ( ”Box s i z e ( dx , dy , dz ) : \ n %12.4E %12.4E %12.4E\n” , dx , dy , dz ) ;
65 }
66
67 return 0;
68 }
Code 65: box spatialSampling configure function.
Secondly, the geoSampling method gets an array of doubles with dimension three, which
must be filled with the sampled x, y, z position (vector X from equation 3). Also, a random
number generator is provided via the random parameter. Again, the box spatial sampler is
used as example and the corresponding geoSampling method is shown in the code 66.
1
2 v o i d b o x s p a t i a l S a m p l i n g : : geoSampling ( d o u b l e pos [ 3 ] , p en r an d& random ) c o n s t {
3
4 pos [ 0 ] = dx∗random . rand ( )−dx05 ;
5 pos [ 1 ] = dy∗random . rand ( )−dy05 ;
6 pos [ 2 ] = dz ∗random . rand ( )−dz05 ;
7
8 }
Code 66: Spatial sampler geoSampling function.
Notice that the geoSampling method is defined as constant. Therefore, this method
can’t change the sampler state. This is only allowed in the configure and updateGeometry
calls.
src/particleGen/spatial/includes/spatialSamplers.hh
and
src/particleGen/spatial/source/spatialSamplers.cpp
33
respectively.
7.3 Direction
Direction samplers must inherit from the base class abc directionSampler as,
src/particleGen/includes/pen samplers.hh
src/particleGen/direction
This kind of samplers are intended to provide the initial normalized direction (U, V, W )
of the particle. To achieve this purpose, the direction samplers do not require to fill any vari-
able of the base class, unlike the spatial sampling case. Therefore, a direction sampler only
requires to implement the configure and the sampling function, named directionSampling,
which signatures are shown in the code 67.
1
2 v i r t u a l i n t c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n &, c o n s t u n s i g n e d = 0 ) = 0 ;
3 v i r t u a l v o i d d i r e c t i o n S a m p l i n g ( d o u b l e d i r [ 3 ] , p en r an d& random ) c o n s t = 0 ;
Code 67: Direction sampler mandatory functions.
First, the configure method takes a pen parserSection (section 3) as first argument,
which includes all the data specified by the user to configure the sampler. In addition, the
parameter verbose must be used to control the number of prints. An example is shown in
the code 68, which corresponds to the conic direction sampler.
1
2 i n t c o n e d i r e c t i o n S a m p l i n g : : c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t
unsigned verbose ) {
3
4 int err ;
5 d o u b l e t h e t a , phi , a l p h a ;
6 // S t o r e c o s i n e s ( u , v , w)
7 e r r = c o n f i g . read ( ” theta ” , theta ) ;
8 i f ( e r r != INTDATA SUCCESS) {
9 i f ( verbose > 0) {
10 p r i n t f ( ” coneDirection : c o n f i g u r e : unable to read ’ theta ’ in c o n f i g u r a t i o n .
Real number e x p e c t e d \n” ) ;
11 }
12 r e t u r n −1;
13 }
14 e r r = c o n f i g . read ( ” phi ” , phi ) ;
15 i f ( e r r != INTDATA SUCCESS) {
16 i f ( verbose > 0) {
17 p r i n t f ( ” coneDirection : c o n f i g u r e : unable to read ’ phi ’ in c o n f i g u r a t i o n .
Real number e x p e c t e d \n” ) ;
18 }
19 r e t u r n −1;
20 }
21 e r r = c o n f i g . read ( ” alpha ” , alpha ) ;
22 i f ( e r r != INTDATA SUCCESS) {
23 i f ( verbose > 0) {
34
24 p r i n t f ( ” coneDirection : c o n f i g u r e : unable to read ’ alpha ’ in c o n f i g u r a t i o n .
Real number e x p e c t e d \n” ) ;
25 }
26 r e t u r n −1;
27 }
28
29 i f ( verbose > 1) {
30 p r i n t f ( ” Theta : %12.4E DEG\n” , t h e t a ) ;
31 p r i n t f ( ” Phi : %12.4E DEG\n” , p h i ) ;
32 p r i n t f ( ” Alpha : %12.4E DEG\n” , a l p h a ) ;
33 }
34
35 t h e t a ∗= deg2rad ;
36 phi ∗= deg2rad ;
37 a l p h a ∗= deg2rad ;
38
39 CPCT = cos ( phi ) ∗ cos ( theta ) ;
40 CPST = cos ( phi ) ∗ s i n ( theta ) ;
41 SPCT = s i n ( phi ) ∗ cos ( theta ) ;
42 SPST = s i n ( phi ) ∗ s i n ( theta ) ;
43 SPHI = s i n ( phi ) ;
44 CPHI = cos ( phi ) ;
45 STHE = sin ( theta ) ;
46 CTHE = cos ( theta ) ;
47 CAPER = cos ( alpha ) ;
48
49 return 0;
50
51 }
Code 68: Configure implementation of the conic direction sampler.
Secondly, the directionSampling method gets an array of doubles with dimension three,
which must be filled with the normalized sampled direction (U, V, W ). Also, a random
number generator is provided via the random parameter. The source 69 shows the imple-
mentation of the directionSampling function for the conic sampler.
1
2 v o i d c o n e d i r e c t i o n S a m p l i n g : : d i r e c t i o n S a m p l i n g ( d o u b l e d i r [ 3 ] , pe n r an d& random
) const {
3
4 c o n s t d o u b l e TWOPI = 2 . 0 ∗ c o n s t a n t s : : PI ;
5
6 double UT,VT,WT;
7 double DF;
8 double SUV;
9 double UF, VF,WF;
10
11 // D e f i n e a d i r e c t i o n r e l a t i v e t o t h e z−a x i s
12 WT = CAPER + (1.0 −CAPER) ∗random . rand ( ) ;
13 DF = TWOPI∗random . rand ( ) ;
14 SUV = s q r t (1.0 −WT∗WT) ;
15 UT = SUV∗ c o s (DF) ;
16 VT = SUV∗ s i n (DF) ;
17 // Rotate t o t h e beam a x i s d i r e c t i o n
18 UF = CPCT∗UT−SPHI∗VT+CPST∗WT;
19 VF = SPCT∗UT+CPHI∗VT+SPST∗WT;
20 WF =−STHE∗UT+CTHE∗WT;
21 // Ensure n o r m a l i s a t i o n
22 d o u b l e DXY = UF∗UF+VF∗VF;
23 d o u b l e DXYZ = DXY+WF∗WF;
24 i f ( f a b s (DXYZ−1.0) > 1 . 0 e −14){
25 d o u b l e FNORM = 1 . 0 / s q r t (DXYZ) ;
35
26 d i r [ 0 ] = FNORM∗UF;
27 d i r [ 1 ] = FNORM∗VF;
28 d i r [ 2 ] = FNORM∗WF;
29 }
30 else{
31 d i r [ 0 ] = UF;
32 d i r [ 1 ] = VF;
33 d i r [ 2 ] = WF;
34 }
35 }
Code 69: Conic direction sampler directionSampling implementation.
Notice that the directionSampling method is defined as constant. Therefore, this method
can’t change the sampler state. This is only allowed in the configure and updateGeometry
calls.
src/particleGen/direction/includes/directionSamplers.hh
and
src/particleGen/direction/source/directionSamplers.cpp
respectively.
7.4 Energy
Custom energy samplers must inherit from the base class abc energySampler as,
src/particleGen/includes/pen samplers.hh
src/particleGen/energy
Energy samplers are used to obtain the initial energy (E) of the particle. As direction
samplers, energy samplers have not to fill any base class member variable, but implement
two functions, the configure and the sampling method named energySampling. Both func-
tion declarations are shown in the code 70.
1
2 v i r t u a l i n t c o n f i g u r e ( d o u b l e& Emax , c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t
unsigned verbose = 0) = 0 ;
3 v i r t u a l v o i d e n e r g y S a m p l i n g ( d o u b l e& Energy , pe n r an d& random ) c o n s t = 0 ;
Code 70: Energy sampler mandatory functions.
36
The configure method takes the pen parserSection (parameter config) to obtain the
configuration data specified by the user and the verbose parameter to control the print
verbosity, just as the previous samplers. However, it takes also an extra parameter named
Emax. This one is an output parameter and must be filled with the maximum energy that
the sampler can return in any call of the energySampling method. It is important to fill this
value correctly, as the maximum energy of all the sources involved in the simulation will be
used to construct the required energetic grids to perform the simulations. An example of
configure function is shown in the code 71, which corresponds to the monoenergetic energy
sampler.
1
2 i n t m o n o e n e r g e t i c : : c o n f i g u r e ( d o u b l e& Emax , c o n s t p e n p a r s e r S e c t i o n& c o n f i g ,
const unsigned verbose ) {
3
4 int err ;
5
6 e r r = c o n f i g . r e a d ( ” e n e r g y ” ,E) ;
7 i f ( e r r != INTDATA SUCCESS) {
8 i f ( verbose > 0) {
9 p r i n t f ( ” monoenergetic : c o n f i g u r e : unable to read ’ energy ’ in c o n f i g u r a t i o n
. Real number e x p e c t e d . \ n” ) ;
10 }
11 r e t u r n −1;
12 }
13
14 i f ( verbose > 1)
15 p r i n t f ( ” Energy : %12.4E\n” ,E) ;
16
17 Emax = E ;
18
19 return 0;
20 }
Code 71: Monoenergetic sampler configuration function.
Then, the energySampling method gets two parameters. The first one, named Energy,
must be filled by with the sampled energy in eV. In addition, takes a instance of the
pen random class (parameter random) to be able to randomize the energy sampling. The
code 72 shows the implementation of the energySampling method for the monoenergetic
sampler. As it is a mono-energetic source, the random generator is not needed.
1
2 v o i d m o n o e n e r g e t i c : : e n e r g y S a m p l i n g ( d o u b l e& energy , p e n r an d& /∗ random ∗/ ) c o n s t
{
3 // S e t e n e r g y
4 energy = E;
5 }
Code 72: Monoenergetic sampler energySampling implementation.
Notice that the energySampling method is defined as constant. Therefore, this method
can’t change the sampler state. This is only allowed in the configure and updateGeometry
calls.
37
src/particleGen/energy/includes/energySamplers.hh
and
src/particleGen/energy/source/energySamplers.cpp
respectively.
7.5 Time
Custom time samplers must inherit from the base class abc timeSampler as,
src/particleGen/includes/pen samplers.hh
src/particleGen/time
Time samplers are optional in the simulation, and are used to sample a initial particle
time in seconds. If no time sampler is specified, the particles time will be set to 0 during
the state sample. Also, notice that using a time sampler does not enables the particles time
of flight calculations. This one must be enabled explicitly in the configuration file. For
example, in the PenRed main program, this must be specified in the configuration file as,
sources/generic/source1/record-time true
where source1 is the source name and can change depending on the simulation. Regarding
the implementation, time samplers have not any base class member variables to fill, re-
maining only two required functions to implement. As other cases, these functions are the
configure and the sampling method, named timeSampling. Both declarations are shown in
the code 73.
1
2 v i r t u a l i n t c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t u n s i g n e d v e r b o s e
= 0) = 0 ;
3 v i r t u a l v o i d timeSampling ( d o u b l e& time , pe n r an d& random ) c o n s t = 0 ;
Code 73: Time sampler mandatory functions.
One more time, the configure method takes a pen parserSection as first argument,
which includes all the data specified by the user to configure the sampler. In addition, the
parameter verbose must be used to control the number of prints. An example is shown in
the code 74, which corresponds to the decay time sampler.
1
2 i n t d e c a y t i m e S a m p l i n g : : c o n f i g u r e ( c o n s t p e n p a r s e r S e c t i o n& c o n f i g , c o n s t
unsigned verbose ) {
3
4 int err ;
5 // S t o r e a c t i v i t y and h a l f l i f e
6 double h a l f L i f e ;
38
7 e r r = c o n f i g . read ( ” h a l f L i f e ” , h a l f L i f e ) ;
8 i f ( e r r != INTDATA SUCCESS) {
9 i f ( verbose > 0) {
10 p r i n t f ( ” decayTime : c o n f i g u r e : u n a b l e t o r e a d ’ h a l f L i f e ’ i n c o n f i g u r a t i o n .
Double e x p e c t e d \n” ) ;
11 }
12 r e t u r n −1;
13 }
14
15 i f ( h a l f L i f e <= 0 . 0 )
16 r e t u r n −2;
17
18 // Get time window
19 d o u b l e time0 ;
20 d o u b l e time1 ;
21 e r r = c o n f i g . r e a d ( ” time / time0 ” , time0 ) ;
22 i f ( e r r != INTDATA SUCCESS) {
23 i f ( verbose > 0) {
24 p r i n t f ( ” decayTime : c o n f i g u r e : u n a b l e t o r e a d ’ time / time0 ’ i n c o n f i g u r a t i o n
. Double e x p e c t e d \n” ) ;
25 }
26 r e t u r n −3;
27 }
28
29 e r r = c o n f i g . r e a d ( ” time / time1 ” , time1 ) ;
30 i f ( e r r != INTDATA SUCCESS) {
31 i f ( verbose > 0) {
32 p r i n t f ( ” decayTime : c o n f i g u r e : u n a b l e t o r e a d ’ time / time1 ’ i n c o n f i g u r a t i o n
. Double e x p e c t e d \n” ) ;
33 }
34 r e t u r n −3;
35 }
36
37 i f ( time0 < 0 . 0 | | time1 < 0 . 0 | | time1 < time0 ) {
38 i f ( verbose > 0) {
39 p r i n t f ( ” decayTime : c o n f i g u r e : N e g a t i v e time i n t e r v a l s a r e not a l l o w e d . \ n”
);
40 }
41 r e t u r n −4;
42 }
43
44 tau = h a l f L i f e /LOG2 ;
45
46 // C a l c u l a t e r a n g e o f randoms
47 rand0 = 1.0 − exp(− time0 / tau ) ;
48 drand = 1.0 − exp(− time1 / tau ) ;
49
50 i f ( drand >= 1 . 0 )
51 drand = 1.0 −1.0 e −16;
52
53 drand −= rand0 ;
54
55 i f ( verbose > 1) {
56 p r i n t f ( ”T1/2 ( s ) : %12.4E\n” , h a l f L i f e ) ;
57 p r i n t f ( ” tau ( s ) : %12.4E\n” , tau ) ;
58 }
59
60 return 0;
61 }
Code 74: Decay time sampler configuration function.
Regarding the sampling, the first parameter of the timeSampling method, named time,
39
must be filled with the sampled time, in seconds. In addition, an instance of the class
pen rand is provided (parameter random) to be able to sample the time randomly. An
example is shown in the code 75.
1
2 v o i d d e c a y t i m e S a m p l i n g : : timeSampling ( d o u b l e& time , pe n r an d& random ) c o n s t {
3
4 d o u b l e rand = rand0+random . rand ( ) ∗ drand ;
5 time = −tau ∗ l o g (1.0 − rand ) ;
6 }
Code 75: Decay time sampler timeSampling function.
Notice that the timeSampling method is defined as constant. Therefore, this method
can’t change the sampler state. This is only allowed in the configure and updateGeometry
calls.
src/particleGen/time/includes/timeSamplers.hh
and
src/particleGen/time/source/timeSamplers.cpp
respectively.
7.6 Specific
Custom specific samplers derive from the base class abc specificSampler, which is a
template class that takes a particle state as unique argument. Therefore, derived samplers
must inherit as,
where the particleState specify the particle state class. The base class is located in the file,
src/particleGen/includes/pen samplers.hh
src/particleGen/specific
Specific samplers are not like previous ones, but are a more versatile and complex kind
of samplers. The first difference is, as we have seen, that specific samplers can only handle
a specific particle state, specified by the template parameter particleState. However,
derived types of particleState can also be used by the sampler. Therefore, if the generic
particle state type (pen particleState) is specified as the template argument, the sampler
will be compatible with any particle type. This approach has been used to implement the
phase space file sampler, which class declaration is shown in the code 76.
40
1
2 c l a s s p s f s p e c i f i c S a m p l e r : p u b l i c a b c s p e c i f i c S a m p l e r <p e n p a r t i c l e S t a t e >{
3 DECLARE SAMPLER( p s f s p e c i f i c S a m p l e r )
4 .
5 .
6 .
7 }
Code 76: Phase space file sampler class declaration.
41
1
2 a b c s p e c i f i c S a m p l e r ( usedSamp u s e d S a m p l i n g s I n ) :
3 usedSamplings ( usedSamplingsIn ) ,
4 pSpatial ( nullptr ) ,
5 pDirection ( nullptr ) ,
6 pEnergy ( n u l l p t r ) ,
7 pTime ( n u l l p t r )
8 {}
Code 79: Specific sampler base class constructor.
1
2 enum usedSamp {
3 USE SPATIAL = 1 << 0,
4 USE DIRECTION = 1 << 1,
5 USE ENERGY = 1 << 2,
6 USE TIME = 1 << 3,
7 USE GENERIC = 1 << 4,
8 USE NONE = 1 << 5
9 };
Code 80: Generic sampling usage flags for specific samplers.
Notice that the flags can be combined using the | operand, like
(USE SPATIAL | USE DIRECTION)
• USE NONE: Indicates that no generic sampler is required for this specific sampler.
Notice that if the USE GENERIC flag is not set, the usage of the specified generic
samplers must be handled by the specific sampler, inside the sample method. However, the
configuration will be handled automatically. Therefore, using the flags USE SPATIAL,
USE DIRECTION and USE ENERGY does not provide the same result as using the
flag USE GENERIC. In the last case, the generic samplers will be handled automatically
by the framework, and the specific sampler function will receive the generic state part of
the state parameter already sampled.
Also, take into account that the flags are used to specify which generic samplers are
mandatory. Therefore, if some mandatory generic sampler has not provided by the user
in the configuration file, the configuration will return an error and the execution will stop.
However, if a non mandatory sampler is provided by the user, it could be still used by the
specific sampler, depending on its implementation. Nevertheless, a warning will be printed
to inform that the corresponding generic sampler could be ignored.
42
Following we will discuss two examples of specific samplers. The first one, corresponding
to the code 81, shows the specific sampler for polarised photons. This one delegates all the
generic sampling step, indicated via the USE GENERIC flag.
1
2 c l a s s gammaPolarised specificSampler : public abc specificSampler <
p e n s t a t e g P o l >{
3 DECLARE SAMPLER( g a m m a P o l a r i s e d s p e c i f i c S a m p l e r )
4 private :
5
6 d o u b l e SP10 , SP20 , SP30 ;
7 i n t IPOL0 ;
8
9 public :
10
11 g a m m a P o l a r i s e d s p e c i f i c S a m p l e r ( ) : a b c s p e c i f i c S a m p l e r <p e n s t a t e g P o l >(
USE GENERIC) ,
12 SP10 ( 0 . 0 ) ,
13 SP20 ( 0 . 0 ) ,
14 SP30 ( 0 . 0 ) ,
15 IPOL0 ( 0 )
16 {}
Code 81: Gamma polarised photons specific sampler class header.
Thus, its sample function does not require to handle the generic state part. As is shown
in the code 82, the sample method only sets the polarisation related variables, which are
specific of the state pen state gPol class.
1
2 v o i d g a m m a P o l a r i s e d s p e c i f i c S a m p l e r : : sample ( p e n s t a t e g P o l& s t a t e ,
3 pen KPAR& /∗ genKpar ∗/ ,
4 u n s i g n e d l o n g l o n g& /∗ d h i s t ∗/ ,
5 p en r an d& /∗ random ∗/ ) {
6
7 state . IPOL = IPOL0 ;
8 state . SP1 = SP10 ;
9 state . SP2 = SP20 ;
10 state . SP3 = SP30 ;
11 }
Code 82: Gamma polarised photons sample function.
The next example handles completely the sample of the generic state part, and corre-
sponds to the phase space file specific sampler. Thus, no generic sampler is required, and
this is specified using the USE NONE flag (code 83).
1
2 psf specificSampler () :
3 a b c s p e c i f i c S a m p l e r <p e n p a r t i c l e S t a t e >(USE NONE) ,
4 pSF ( n u l l p t r ) ,
5 nChunks ( 0 ) ,
6 chunksPerPart ( 0 ) ,
7 offsetChunks (0) ,
8 remainingChunks ( 0 ) ,
9 buffer ( nullptr ) ,
10 bufferSize (0) ,
11 NSPLIT ( 1 ) ,
12 WGHTL( 0 . 0 ) ,
13 WGHTU( 1 . 0 ) ,
14 RWGHTL( 1 . 0 e35 ) ,
15 splitted (0) ,
16 requiredSplits (0) ,
43
17 l a s t K p a r (ALWAYS AT END)
Code 83: Phase space file constructor.
Finally, to access the assigned spatial, direction, energy or time samplers, the
abc specificSampler provides a set of methods to read the pointers to these samplers,
which are described in the section 7.6.6.
7.6.3 Configuration
The specific sampler configuration virtual method is defined in the code 84.
1
2 v i r t u a l i n t c o n f i g u r e ( d o u b l e& Emax ,
3 c o n s t p e n p a r s e r S e c t i o n& c o n f i g ,
4 const unsigned nthreads ,
5 const unsigned verbose = 0) = 0 ;
6
The skip method takes as only argument the number of histories to be skipped (pa-
rameter dhists). Notice that this function only must be implemented if is required by the
44
sampler, usually because the state is changed during the sample call. This is the case of the
phase space file sampler (class psf specificSampler), as it reads the particle information
from a file. Instead, the gamma polarised sampler does not implement this function, as its
sampling method does not change the sampler state.
• spatial: Returns a pointer to the assigned spatial sampler. If it has not been assigned
a null pointer is returned instead.
• direction: Returns a pointer to the assigned direction sampler. If it has not been
assigned a null pointer is returned instead.
• energy: Returns a pointer to the assigned energy sampler. If it has not been assigned
a null pointer is returned instead.
• time: Returns a pointer to the assigned time sampler. If it has not been assigned a
null pointer is returned instead.
• geo: Returns a pointer to the assigned geometry. If no geometry has been assigned a
null pointer is returned instead.
• getThread: Returns a numeric identifier for the thread which owns the specific
sampler instance.
Notice that to perform some generic sampling with the provided pointers, all returned
samplers have a public method named sample which must be called instead of the overwrit-
ten method to implement the generic sampler. For example, to use a spatial sampler, the
developer must call the sample method instead of the geoSampling method.
45
7.6.7 Including files
To use a developed specific sampler in the PenRed environment, in addition to the sampler
register steps described in the section 7.1, the developer must append a include of the cor-
responding header and source file names in the files,
src/particleGen/specific/includes/specificSamplers.hh
and
src/particleGen/specific/source/specificSamplers.cpp
respectively.
pen genericVR
src/VR/includes/
src/VR/source/
This class is a template interface which takes as template argument a particle state
type. Like specific sources, a VR module can be compatible with all particle states if the
template argument is set to the basic particle state, i.e. pen particleState. In this case,
the developed VR class must inherit from the basic VR class as,
Otherwise, a different particle state can be specified to limit the VR module usability.
For example, the x-ray splitting module (code 88) is limited to photons with the state
pen state gPol.
1
2 c l a s s p e n V R x r a y s p l i t t i n g : p u b l i c pen genericVR<p e n s t a t e g P o l >
Code 88: pen VRxraysplitting class declaration.
Notice that the implementation of VR modules follows the same pattern regardless the
used particle state. The only difference is where the source files are registered. Therefore,
the same explanation applies for both types of VR modules, generic and specific.
8.1 Register
Implemented VR modules must be registered using the DECLARE VR and REGISTER VR
macros (code 89). These macros, takes as arguments the class name of our implemented
46
module (Class parameter), the particle state type required by the module (stateType param-
eter) and a unique VR module name (ID parameter), which will be used in the configuration
file to specify its type.
1
2 DECLARE VR( C l a s s )
3 REGISTER VR( C l a s s , stateType , ID )
Code 89: VR register macros.
The first macro must be placed inside the class declaration, just after the class name, as
is shown in the code 90, which corresponds to the pen VRRussianRoulette class definition.
1
2 c l a s s pen VRRussianRoulette : p u b l i c pen genericVR<p e n p a r t i c l e S t a t e >{
3 DECLARE VR( pen VRRussianRoulette )
4 .
5 .
6 .
7 }
Code 90: VR declare macro.
The second one must be placed in the source file, as is shown in the code 91.
1
2 REGISTER VR( pen VRRussianRoulette , p e n p a r t i c l e S t a t e , RUSSIAN ROULETTE)
Code 91: VR register macro.
The source file inclusion depends on the specified particle state. Those who use the
generic particle state (pen particleState) are classified as generic VR modules. Instead,
if another particle state is used, the module will be considered as specific.
src/VR/generic/includes/genericVR.hh
and
src/VR/generic/source/genericVR.cpp
respectively.
src/VR/specific/includes/specificVR.hh
and
47
src/VR/specific/source/specificVR.cpp
respectively.
8.2 VR callbacks
Like tallies (section 6), VR modules runs in some specific points during the simulation exe-
cution. These points can be enabled/disabled for each implemented VR module depending
on its needs. Each of these points are handled by a specific callback, which will be described
in this section. Regarding the parameters, all callbacks gets the variables shown in the code
92.
1
2 const unsigned long long nhist ,
3 c o n s t pen KPAR kpar ,
4 s t a t e T y p e& s t a t e ,
5 s t d : : a r r a y <stateType , c o n s t a n t s : : NMS>& s t a c k ,
6 u n s i g n e d& c r e a t e d ,
7 const unsigned a v a i l a b l e ,
8 p e n r an d& random
Code 92: Common VR callbacks parameters.
First, nhist specify the history number. Secondly, kpar specify the particle type to which
the VR technique will be applied. Then, state stores the particle state. Notice that, usually,
this state will change during the call. Therefore, is not declared as a constant parameter.
The state type is specified by the template argument (stateType) of the pen genericVR
class. As many VR techniques clones particles, like techniques based on particle splitting,
the stack parameter provides an storage to save generated particle states. The stack size is
limited to constants::NMS states. However, as the generated particles in the VR module will
be included in the secondary particle stack, which is not accessible, the developer must take
into account the created and available parameters. The first one (created) saves the number
of states stored in the provided stack array, which could be filled previously by another
VR callback. Therefore, when a new state is stored in the stack, the value of created must
be increased, and the next position to save a new state is determined by the value of created,
stack[created] = newState;
++created;
stack[created++] = newState;
Otherwise a previous created state could be overwritten. Finally, the parameter available
stores how many free spaces are available in the secondary particle stack. This one is
constant, as the secondary particle state does not change until all VR callbacks have been
called. So, the remaining space can be calculated as,
48
Finally, the random parameter is a pen rand instance which provides a random number
generator.
All the available callbacks are listed following,
• vr matChange: This function is called when the material where the particle is lo-
cated changes. As extra argument, the prevMat parameter stores the previous material
where the particle was located.
1
2 v o i d vr matChange ( c o n s t u n s i g n e d l o n g l o n g n h i s t ,
3 c o n s t pen KPAR kpar ,
4 c o n s t u n s i g n e d prevMat ,
5 s t a t e T y p e& s t a t e ,
6 s t d : : a r r a y <stateType , c o n s t a n t s : : NMS>& s t a c k ,
7 u n s i g n e d& c r e a t e d ,
8 const unsigned a v a i l a b l e ,
9 p en r an d& random ) c o n s t
10
11
• vr particleStack: This function is called when a particle state is extracted from the
secondary stack. As extra argument, the kdet parameter stores the detector index
where the particle is located.
1
2 void v r p a r t i c l e S t a c k ( const unsigned long long nhist ,
3 c o n s t pen KPAR kpar ,
4 c o n s t u n s i g n e d kdet ,
5 s t a t e T y p e& s t a t e ,
6 s t d : : a r r a y <stateType , c o n s t a n t s : : NMS>& s t a c k ,
7 u n s i g n e d& c r e a t e d ,
8 const unsigned a v a i l a b l e ,
9 p en r an d& random ) c o n s t
10
11
Notice that all callbacks are defined as constant methods, thus changing the module
state is not possible during the callback call.
49
8.3 Callbacks to trigger
Each VR module does not require to implement all the available callbacks. Only the nec-
essary methods should be implemented. To specify which methods will be used by the VR
module, the corresponding flags must be specified in the argument of the interface class
constructor (pen genericVR). These flags are,
VR USE PARTICLESTACK
VR USE MATCHANGE
VR USE INTERFCROSS
For example, the code 96 shows the constructor of the splitting VR module, which only
requires the interface cross callback.
1
2 p e n V R s p l i t t i n g ( ) : pen genericVR (VR USE INTERFCROSS) {
3 .
4 .
5 .
6 }
Code 96: VR splitting module constructor.
Notice that flags can be combined using the binary or operand (|). For example, to
enable the particleStack and matchange flags, the constructor call must be done as shown
in the code 97.
1
2 VR derived class () :
3 pen genericVR (VR USE PARTICLESTACK | VR USE MATCHANGE) {
4 .
5 .
6 .
7 }
Code 97: VR combined flags in module constructor.
References
[1] V. Giménez-Alventosa, V. Giménez Gómez, S. Oliver, Penred: An extensible and
parallel monte-carlo framework for radiation transport based on penelope, Computer
50
Physics Communications 267 (2021) 108065. doi:https://doi.org/10.1016/j.cpc.
2021.108065.
URL https://www.sciencedirect.com/science/article/pii/S0010465521001776
51