0% found this document useful (0 votes)
19 views51 pages

PenRed Implementation Guide

Uploaded by

Renzo Ocampo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views51 pages

PenRed Implementation Guide

Uploaded by

Renzo Ocampo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

PenRed: Implementation Manual

V. Giménez-Alventosa1 , V. Giménez Gómez2 , and S. Oliver3


1
Departament de Fı́sica Atòmica Molecular i Nuclear, Universitat de València , Dr.
Moliner, 50, 46100, Burjassot, València, Spain, [email protected]
2
Departament de Fı́sica Teòrica and IFIC, Universitat de València-CSIC , Dr.
Moliner, 50, 46100, Burjassot, València, Spain,
3
Instituto de Seguridad Industrial, Radiofı́sica y Medioambiental (ISIRYM),
Universitat Politècnica de València, Camı́ de Vera s/n, 46022, València, Spain,
[email protected]

September 15, 2023

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).

1.1 Document structure


The present document is structured as follows. First, how particle states are defined in
PenRed is explained in the section 2. Secondly, the section 3 describes the data structure
defined in the framework and its usage. This one is used to provide the necessary information
to configure each component. Once the basic components are described, the section 5
contains how to create new geometry modules. The section 6 explains how to implement
and include tallies to extract information from the simulation. Then, the modules used to

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,

• E: Stores the particle energy in eV.

• X, Y, Z: Stores the particle position vector (X, Y, Z) respectively, in cm.

• U, V, W: Stores the particle normalized direction vector (U, V, W ).

• 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.

• MAT: Stores the material 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.

• LAGE: Enables/disables the particle time recording.

• PAGE: Stores the particle life time in seconds.

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.

3 Internal data format


To be able to provide a common interface for the input data of all components, regardless
the amount of required data, PenRed uses a set of classes to store multiple variables of
multiple types in a single instance. These classes can be found in the file,

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.

3.1 Input text format


The users can specify all the required data to configure a components via a text file where
each line contains a key and a value separate by white spaces, just like the code 1 shows for
the configuration of a cylindrical dose distribution tally.
1 t a l l i e s / c y l D o s e D i s t r i b / type ”CYLINDRICAL DOSE DISTRIB”
2 t a l l i e s / c y l D o s e D i s t r i b / p r i n t −xyz t r u e
3 t a l l i e s / c y l D o s e D i s t r i b / rmin 0 . 0
4 t a l l i e s / c y l D o s e D i s t r i b /rmax 3 0 . 0
5 t a l l i e s / c y l D o s e D i s t r i b / n b i n s r 60
6 t a l l i e s / c y l D o s e D i s t r i b / zmin 0
7 t a l l i e s / c y l D o s e D i s t r i b /zmax 3 0 . 0
8 t a l l i e s / c y l D o s e D i s t r i b / n b i n s z 60
Code 1: Internal data example

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.2 Reading data


To read information from a pen parserSection instance, the read method must be used.
This one is overloaded for the allowed types, i.e., characters, integers, doubles, bools,
pen parserData, strings, pen parserArray and pen parserSection. These functions are
shown in the code 3.
1
2 // Read f u n c t i o n s

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

Notice that it is possible to read a subsection stored in a pen parserSection instance,


as the last function in the code 3 suggests. A subsection is interpreted as a “cut” in a key.
For example, if we have the following structure in a pen parserSection instance,

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

In the previous example, the variable talliesSection is a pen parserSection instance.

3.3 Auxiliary functions


In this section some auxiliary functions to handle the PenRed internal data format classes
are described.

• 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/

contains some isolated examples.

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.

5.1 Generic assumptions


To create any new geometry module, this assumptions must be satisfied,

• 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.

• Material index 0 is reserved to void regions.

• Detector assignation is optional. If a body does not belong to any detector, the
detector index must be set to 0.

• An interface is defined as the change of material or detector index between bodies.


Thus, if two bodies in contact shares the same material index, there is no interface
between both, unless they belong to different detectors.

• 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).

5.2 Mandatory functions


The abc geometry and abc mesh classes defines most of the pure virtual methods inherited
from the wrapper geometry class for body and mesh based geometries respectively. How-
ever, some methods remains to be implemented by the geometry developer in both cases.
These methods are listed following,

• 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.

• getBodyName (code 14): The counterpart of getIBody must be provided also,


i.e. a function to retrieve the body name corresponding to the specified input index
(ibody).
1
2 v i r t u a l s t d : : s t r i n g getBodyName ( c o n s t u n s i g n e d ibody ) c o n s t = 0 ;
Code 14: Declaration of the pure virtual method getBodyName from the wrapper geometry
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

and is shown in the code 20.


1
2 // Read m a t e r i a l d e n s i t i e s s e c t i o n
3 p e n p a r s e r S e c t i o n matSec ;
4 s t d : : v e c t o r <s t d : : s t r i n g > matNames ;
5 i f ( c o n f i g . r e a d S u b s e c t i o n ( ” m a t e r i a l s ” , matSec ) != INTDATA SUCCESS) {
6 i f ( verbose > 0) {
7 p r i n t f ( ” pen dicomGeo : c o n f i g u r e : No m a t e r i a l i n f o r m a t i o n p r o v i d e d \n”
);
8 }
9 configStatus = 4;
10 return 4;
11 }
12

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.

5.4 Body based


Body based geometries inherits from the template abstract class abc geometry, located in
the file,

src/geometry/includes/geometry classes.hh

Geometries derived from this class must inherit as,

public abc geometry<bodyType>

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/

5.4.1 Body type


The body type can be defined by the developer to adapt it to the geometry. However,
is advisable to define the body type as a derived class of pen baseBody (code 25), which
defines the minimum interface to be compatible with all predefined methods belonging the
abc geometry class.
1
2 s t r u c t pen baseBody {
3
4 u n s i g n e d i n t MATER;
5 u n s i g n e d i n t KDET;
6 d o u b l e DSMAX;
7 d o u b l e localEABS [ c o n s t a n t s : : nParTypes ] ;
8

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,

• MATER: Material index associated to the body.

• KDET: Detector index associated to the body.

• 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,

EABS = max(localEABS, matEABS) (1)

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.

5.4.3 Including files


To use a developed body based geometry in the PenRed environment, in addition to the
geometry register steps described in the section 5.3, the developer must append a include
of the corresponding header and source file names in the files,

src/geometry/objects/includes/pen object geos.hh

and

src/geometry/objects/source/pen object geos.cpp

respectively.

5.5 Mesh based


Mesh based geometries inherits from the template abstract class abc mesh, located in the
file,

src/geometry/includes/geometry classes.hh

Geometries derived from this class must inherit as,

public abc mesh<meshElement>

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/

5.5.1 Mesh element type


The mesh element type can be defined by the developer to fit the mesh geometry re-
quirements. However, it should derive from the provided base type pen baseMesh, which
definition is included in the code 27. This base type provides the minimum interface to be
compatible with all predefined methods belonging the abc mesh class.
1
2 s t r u c t pen baseMesh {
3
4 u n s i g n e d i n t MATER;
5
6 pen baseMesh ( ) : MATER( 0 ) {}
7 };
Code 27: Definition of pen quadBody structure.

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.

5.5.2 Inherited variables


The abc mesh class provides some variables that must be used to ensure a proper operation
of the developed geometry. These varaibles are defined following,

• 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.

The mesh array must be filled during the geometry configuration.

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.

5.5.3 Including files


To use a developed mesh based geometry in the PenRed environment, in addition to the
geometry register steps described in the section 5.3, the developer must append a include
of the corresponding header and source file names in the files,

src/geometry/meshes/includes/pen mesh geos.hh

and

src/geometry/meshes/source/pen mesh geos.cpp

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

which source can be found in,

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,

public pen genericTally<pen particleState>

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.

6.1 Extracting simulation data


The implementation of tallies, and other components, is done via callbacks defined as virtual
functions. These callbacks will be called during the simulation at specific points. Thus, to
create a new tally, the developer must know when these callbacks are called an which
information is received in each one. Before describing the callbacks, it is advisable to talk
about some arguments that are common for most of these functions. These are listed in the
code 29,
1 const unsigned long long n h i st
2 const u n s i g n e d kdet
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
Code 29: Common variables for tally functions.

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

Code 30: Dummy logger tally beginSim function.

• 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

Code 31: Dummy logger tally endSim function.

• 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

Code 32: Dummy logger tally move2geo function.

• 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

Code 33: Dummy logger tally sampledPart function.

• 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

Code 34: Dummy logger tally endHist function.

• tally beginPart: Called when a particle simulation begins. As explained in the


tally move2geo function, a particle simulation begins only if the particle reaches a
non void region of the geometry system. Therefore, if the particle is sampled in a void
region and remains in a void region after trying to move it forward, this function will
not be triggered.
1 v o i d pen tallyDummyLog : : t a l l y b e g i n 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 /∗ 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 ) {

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

Code 35: Dummy logger tally beginPart function.

• tally endPart: Called when a particle simulation ends. Unlike the


tally beginPart callback, this one will be triggered also when a particle is sampled
in a void region and not reaches any non void region of the geometry system.
1 v o i d pen tallyDummyLog : : t a l l y e n 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 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 // This f u n c t i o n i s c a l l e d when t h e
5 // s i m u l a t i o n o f a p a r t i c l e ends
6
7 p r i n t f ( ”A %s from h i s t o y %l l u ends i t s s i m u l a t i o n . \ n” ,
8 p a r t i c l e N a m e ( kpar ) , n h i s t ) ;
9 }
10

Code 36: Dummy logger tally endPart function.

• 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

Code 37: Dummy logger tally localEdep function.

• 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

Code 38: tally StepData structure.

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

Code 39: Dummy logger tally step function.

• 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

Code 40: Dummy logger tally interfCross function.

• 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

Code 41: Dummy logger tally matChange function.

• 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

Code 42: Dummy logger tally jump function.

• 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

Code 43: Dummy logger tally knock function.

• 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

Code 44: Dummy logger tally lastHist function.

6.2 Callbacks to trigger


Despite the amount of available tally functions, a single tally does not requires to implement
all of them. Only the necessary functions to get the interest data should be implemented.
To specify which functions will be used by a tally, the corresponding flags must be passed
as argument in the interface class pen genericTally constructor. These flags are,

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.

6.3 Mandatory methods


In addition to the optional tally callbacks, all tallies must implement some mandatory pure
virtual methods. As our dummy logger doesn’t extract any information, these function does
nothing in its class. However, several examples can be found in the PenRed built-in tallies.
These methods are the following,

• 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.

• configure: As other components, tallies require a configure initialization function.


This one takes a configuration structure, a verbose level, a geometry pointer and the
material information as arguments, as we can see at the code 49.

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.

6.4 Optional non callback methods


In addition to mandatory methods, there exist some implementable methods to provide
access to some specific information and features. These ones are optional and can be
omitted if are not required in our tally.

• 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.

6.5 Auxiliary functions


Following are already implemented and available helper functions that can be used to de-
velop a tally,
• getThread: Returns the thread ID of this tally instance.
1 i n l i n e u n s i g n e d getThread ( ) c o n s t
Code 52: Tally getThread function.

• 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

REGISTER COMMON TALLY

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.

• Direction: Samples initial particle direction vector (U ,V ,W ).

• Energy: Samples initial particle energy E.

• 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,

public abc spatialSampler

This base class is located in the file,

src/particleGen/includes/pen samplers.hh

and several examples of spatial sampler implementations can be found in,

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.

7.2.1 Including files


To use a developed spatial 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/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,

public abc directionSampler

The base class is located in the file,

src/particleGen/includes/pen samplers.hh

and several examples of direction sampler implementations can be found in,

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.

7.3.1 Including files


To use a developed direction sampler in the PenRed environment, in addition to the sam-
pler register steps described in the section 7.1, the developer must append a include of the
corresponding header and source file names in the files,

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,

public abc energySampler

The base class is located in the file,

src/particleGen/includes/pen samplers.hh

and several examples of energy sampler implementations can be found in,

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.

7.4.1 Including files


To use a developed energy 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,

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,

public abc timeSampler

The base class is located in the file,

src/particleGen/includes/pen samplers.hh

and several examples of time sampler implementations can be found in,

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.

7.5.1 Including files


To use a developed time 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/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,

public abc specificSampler<particleState>

where the particleState specify the particle state class. The base class is located in the file,

src/particleGen/includes/pen samplers.hh

and several examples of specific sampler implementations can be found in,

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.

7.6.1 Sampler function


Another key difference is the sampler function, named sample, which declaration is shown
in the code 77.
1
2 v i r t u a l v o i d sample ( p a r t i c l e S t a t e& 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 ) = 0 ;
Code 77: Specific sampler sample function.
Notice that the sample function is not declared as a constant method. Therefore,
specific samplers are allowed to change their state during sampling calls. However, in
return, the developer must ensure that the sample function is thread save to be able to
use the multi-threading capabilities. To facilitate this procedure, each simulation thread
uses its own specific sampler private instance, which is not shared among other threads.
In addition, just in case a shared resource must be used by several threads, the base class
abc specificSampler provides a method to obtain a thread number identifier. This one is
named getThread and is shown in the code 78.
1
2 i n l i n e u n s i g n e d getThread ( ) c o n s t { r e t u r n n t h r e a d ; }
Code 78: Specific sampler getThread method.
So, the sample function is not as simple as in the previous cases. As is shown in the
code 77, the first argument corresponds to the whole particle state to be filled. The second
argument of type pen KPAR should be filled with the sampled particle type, thus a specific
sampler can produce different particle types. Particle type indexes can be found in the
PenRed user manual. Then, the parameter dhist must be filled with the history increment.
This one is supposed 1 by generic samplers, meaning that each sampled particle corresponds
to a new history. However, in specific samplers this can be changed, sampling several
particles in the same history or skipping more than a single history between consecutive
sample calls. Finally, the random parameter provides a instance of the pen rand class to
be able to generate random numbers.

7.6.2 Sampling generic state


Although the sampler can change the whole state, it can delegate, partially or completely,
the sampling of the generic state part to generic samplers, i.e., the position to spatial
samplers, the direction to direction samplers, the energy to energy samplers and the time
to time samplers.
If the developer want to delegate the sampling of some parts of the generic state to
spatial, direction, energy and time samplers, this must be indicated in the constructor via
the specific sampler base class constructor (code 79). This is done using a set of predefined
flags, which definition is included in the code 80. The procedure will be exemplified later.

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)

The flag descriptions follows,

• USE SPATIAL: A generic spatial sampler must be used.

• USE DIRECTION: A generic direction sampler is required.

• USE ENERGY: Makes the generic energy sampler mandatory.

• USE TIME: Requires the usage of a generic time sampler.

• USE GENERIC: Indicates that a spatial, a direction and a energy generic


samplers are required, but the time sampler is optional. Furthermore, with this flag,
the generic part of the particle state will be sampled automatically as if no specific
sampler was defined. Then, once the generic part has been sampled, the sample
method of the specific sampler will be called, which can overwrite any part of the
previous sampling.

• 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

Code 84: Specific sampler configuration method.

As previous samplers, this configuration method takes a pen parserSection instace,


named config, which provides all the configuration parameters specified by the user. Also,
the verbose parameter determines the verbosity level, i.e. the amount of printed informa-
tion. As energy samplers, the Emax parameter must be filled with the maximum energy
achievable by the sampler. Notice that this parameter is not necessary to be filled if a com-
plete generic sampler is used. To enable this feature, as has been described in the section
7.6.2, the USE GENERIC flag must be used in the constructor. Finally, the nthreads
parameter provides the number of threads to be used in the simulation.
Notice that, when the configuration function is called, all the possible generic samplers
(spatial, direction, energy and time) are already saved in the corresponding pointers. There-
fore, the pointers to these samplers can be accessed, during the configuration call, using
the functions described in the section 7.6.6. However, as has been discussed, the developer
must take into account that the geometry can’t be accessed during the configure method
call. Therefore, no geometry related calculations should be done in the configure function,
as the geo function will return a null pointer. Instead, this calculus must be done in the
updateGeometry method (code 57), which is called when a geometry is assigned to the
samplers.

7.6.4 Resume a simulation


A counterpart of allowing to modify the state of the specific sampler during the sampling
method, is that the sampling can depend on the number of previous sample calls. For
example, if the information of the states to sample are read from a file. This fact can be
problematic if the simulation is resumed from a dump file. Considering the same case, if the
simulation is resumed, the file will be read from the beginning, reading the same information
twice.
To handle this situation, the specific sampler base class provides a virtual method named
skip, which declaration is shown in the code 85.
1
2 v i r t u a l v o i d s k i p ( 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 s ∗/ ) {}
Code 85: Skip virtual method of the abc specificSampler class.

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.

7.6.5 Dynamic generic sampling


The specific sampler base class abc specificSampler provides a virtual method named
updateSamplers (code 86). Actually, this method has not usage, but has been included
to allow, in future implementations, to change the generic samplers during the simulation.
Therefore, actually, this method must not be implemented.
1
2 v i r t u a l v o i d updateSamplers ( ) {}
Code 86: Virtual method updateSamplers of the abc specificSampler class. This method
must not be implemented.

7.6.6 Auxiliary functions


The base class abc specificSampler provides some auxiliary methods to be able to access
information to perform the sampling process. The definition of these methods are listed in
the code 87
1
2 inline const abc spatialSampler ∗ s p a t i a l () const { return pSpatial ;}
3 inline const abc directionSampler ∗ d i r e c t i o n () const { return pDirection ;}
4 inline c o n s t a b c e n e r g y S a m p l e r ∗ e n e r g y ( ) c o n s t { r e t u r n pEnergy ; }
5 inline c o n s t a b c t i m e S a m p l e r ∗ time ( ) c o n s t { r e t u r n pTime ; }
6 inline c o n s t wrapper geometry ∗ geo ( ) c o n s t { r e t u r n geometry ; }
7 inline u n s i g n e d getThread ( ) c o n s t { r e t u r n n t h r e a d ; }
Code 87: Auxiliary methods provided in the abc specificSampler class.
The description of each one follows,

• 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.

8 Variance reduction (VR)


Variance reduction techniques (VR) are used to improve the variance reduction of the
magnitudes estimated by the simulation. VR modules inherits from the class,

pen genericVR

which source can be found in,

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,

public pen genericVR<pen particleState>

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.

8.1.1 Generic modules files inclusion


To use a developed generic VR module in the PenRed environment, in addition to the VR
register steps described in the section 8.1, the developer must append a include of the cor-
responding header and source files names in the files,

src/VR/generic/includes/genericVR.hh

and

src/VR/generic/source/genericVR.cpp

respectively.

8.1.2 Specific modules files inclusion


To use a developed specific VR module in the PenRed environment, in addition to the
VR register steps described in the section 8.1, the developer must append a include of the
corresponding header and source files names in the files,

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;

or, more compact,

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,

f reeSpace = available − created (4)


Notice that the created parameter could store a non zero value when the callback is
called, because that parameter is passed to the next VR module callback until all of them
have been called. Therefore, when all callbacks have been executed, the stack parameter
will store all the particles generated by all enabled VR callbacks, and the created parameter
will store the total number of generated states.

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

Code 93: VR material changed callback.

• vr interfCross: This function is called when the particle crosses an interface. As


extra argument, the kdet parameter stores the detector index where the particle is
located.
1
2 void v r i n t e r f C r o s s ( 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

Code 94: VR interface crossed callback.

• 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

Code 95: VR secondary particle extracted from stack callback.

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.

8.4 Mandatory methods


The only mandatory method to implement in any VR module is the configure method
(code 98). This takes as arguments a pen parserSection instance named config which
contains all the necessary information specified by the user to configure the VR module,
the geometry information via the parameter geometry, and a verbose parameter to handle
the print verbosity.
1
2 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 ,
3 c o n s t wrapper geometry& geometry ,
4 const unsigned verbose )
Code 98: VR configure method.

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

You might also like