The components listed approximately from lower to higher level - later files are less fundamental.
-
penelope.py
def run_penelope(...)
: Bypasses the penelope GUI and calls the pyPENELOPE API directly to run penelope in the CWD
-
lumerical.py
def run_detector_test(...)
: Calls the Lumerical python API to build a simulation volume around a generation rate matrix and run a charge transport simulation.
-
trajectory.py
class Trajectory
: Contains the description of a "trajectory" object, used to store pyPENELOPE trajectories in memory. Its members are the properties of the trajectory, as well as a list containing the trajectory's events.
-
traj_parser.py
def parse_traj(filename, trim=True)
: Used to parsepe-trajectories.dat
files from pyPENLOPE into a list ofTrajectory
objects. Has the option to trim extraneous datapoints based on IBODY value.def separate_collisions(trajectories)
: Used to separate a list of trajectory objects by primary particle, resulting in a list of "showers" that each were caused by a single primary.
-
process_impact.py
def preprocess_data(trajectories)
: Scans the trajectories for minimum and maximum values to later be used to adjust the simulation volumedef deltaX(event0, event1)
: Compute the distance between two pointsdef process_impact(trajectories, N_grid)
: Compute charge generation due to a shower of trajectories, with a given bin side length
-
charge_density.py
def compute_charge(trajectory, volumes=None)
: Compute the charge density resulting from a trajectory, using a simple equation to approximate charge diffusion. Has the ability to output a list of charge clouds (volumes), storing spatial information along with the radius and density of each cloud, for more detailed processing.
-
plot_dat.py
andplot_traj.py
: Used to plotpe-trajectories.dat
files and Lumerical generation rate.mat
files, respectively, using matplotlib. -
generation_rate.py
def process_data(...)
: processes ape-trajectories.dat
file into charge generation matrices, to be fed to Lumerical.
-
pipeline.py
- Notes:
- "Parameters" describe inputs to the simulations themselves, such as materials, particle types and energies, etc. "Options" describe inputs describing pipeline behavior, such as which simulations to run, whether to save intermediate files, etc.
- If
pipeline.py
is run directly, the constants at the beginning of the file are used to define the parameters and option of the run.
def build_parameters(...)
/def build_options(...)
: Takes single named arguments representing pipeline parameters and options, respectively, and packages them into single dictionaries.def build_options(...)
: Takes single named run options and packages them into a single dictionary.def default_parameters()
/def default_options()
: Create dictionaries for parameters and options, respectively, containing their default values.def get_file_number(filename)
: Extract the suffix number from a .mat filename that ends in a number followed by.mat
, e.g.file_42.mat
. File extension could easily be parameterized if this function could be useful elsewhere.def find_file_number_in_list(number, file_list)
: Finds the index of a file with a given number in an ordered list. Necessary because numbered files won't be sorted alphabetically, so resuming execution requires a more specialized search.def configure_and_run_lumerical(lumerical_targets, params, options, scripts=None)
: Takes a list of charge generation files and runs lumerical to simulate charge transport, running the specified lumerical scripts on the simulation resultsdef run_pipeline(params=DEFAULT_PARAMS, options=DEFAULT_OPTIONS, scripts=None)
: Run a series of simulations in sequence. Penelope is run first, followed by either the lightweight in-house code, or Lumerical.
- Notes:
First, the pyPENELOPE root directory must be in the pythonpath, so that
pyPENELOPE can be imported into python scripts. This is the folder that
contains the folder penelopetools
, and is probably called pyPENELOPE-0.2.10.
Export its location using its absolute path like this:
z-adams@Z-ThinkPad-T450s:~$ export PYTHONPATH="${PYTHONPATH}:
/abs/path/to/pyPENELOPE-0.2.10/"
This can either be done manually each time you enter a new console, or it can be permanently added to path by putting that same command inside your .bashrc file in your user directory.
If it's your first time running pyPENELOPE, you'll need to create a
pypenelope.cfg file under ~/.pypenelope/pypenelope.cfg
on Linux, or
the appropriate location on other systems (remember that since the folder
begins with . it's a hidden folder and can only be seen with ls -a
). I don't
remember whether installing penelope creates this folder automatically, or
whether it has to be done manually.
The file should contain the following lines, with the paths changed to the appropriate locations of the executables and data files:
[Shower2010]
programname = penshower
programbasepath = /<path>/fortran/penshower
[Penepma2011]
programname = penepma
programbasepath = /<path>/fortran/penepma
[PENELOPE2011]
basenameatomicelectronshell = atomicelectronshell.txt
basenamecompositiondata = pdcompos.p08
basenamerelaxationdata = pdrelax.p11
basenamegroundstateconfigurationsdata = pdatconf.p08
pathname = /<path>/fortran/penelope/pendbase/pdfiles
[Material2011mod]
inputpath = /<path>/fortran/penelope/pendbase
programbasepath = /<path>/fortran/penelope/pendbase
programname = material
[pyPENELOPE GUI]
opendirectory = /<desired default folder>
saveresultsdirectory = /<desired default folder>
savefiguresdirectory = /<desired default folder>
simulationdirectory = /<desired default folder>
maximumdisplayedshowers = 100
With Penelope's path exported to the system path, the program should now be
callable from python, regardless of the location it's called from. Any script
can then call the run_penelope()
function after importing it from penelope.py.
When run without parameters, it will simulate 10 350keV electrons incident on an
infinite CdTe substrate. Open penelope.py and look inside to see how the energy
parameters, materials, and geometry data are formatted if you want to pass them
as paramters to run_penelope()
. Briefly, they are python dictionaries or
arrays of dictionaries that encode the values necessary to represent the various
attributes of the simulation inputs. They are detailed in comments with examples
provided around lines 70 and 130.
When penelope.py is called, it will do its work in the current working directory (cwd). By default, this is where the script was called from.
The easiest way to do this is to have the SUpipeline directory at the same level as the data directory, e.g.
../
|- SUpipeline/
| |- penelope.py, etc.
|
|- test1/
| |-
And then with the current working directory set to test1, the pipeline can be run directly from the SUpipeline folder, but will place the resulting files in the test1 folder.
For example:
z-adams@Z-ThinkPad-T450s:~/Desktop/SU/test1/$ python ../SUpipeline/penelope.py
# OR:
z-adams@Z-ThinkPad-T450s:~/Desktop/SU/test1/$ python script_that_calls_penelope.py
The current working directory is ~/Desktop/SU/test1/
both in the 2nd case where
a script that imports penelope.py is in the current directory, and in the first
case, since even though penelope.py lives in ~/Desktop/SU/SUpipeline/
, it's
being called from our cwd.
Note that a python script can also change the cwd using os.chdir('path')
For Lumerical to be run using lumerical.py, it must be configured to be run
using the Python API. If Lumerical indicates that python integration is
available (an automation license is required), the lumapi.py
script can then
be imported manually via the imp
module. At the top of lumerical.py, a
conditional statement loads the script based on the operating system. Make sure
the path points to the correct location.
The arguments of run_detector_test()
are as follows:
charge_data_filename
: The filepath to the .mat file containing the generation rate matrix, to be read by Lumerical's CHARGE solverworking_dir
: The working directory to use when running lumerical. Files produced by Lumerical will be saved here.output_filename
: The name of the.ldev
output file produced by Lumericalmaterial
: The name of the material to be used as the detector. This must be one of the materials available within Lumerical, and the name must match exactly.mesh_options
: A dictionary storing the min and max mesh length scales, just as they would be normally entered into Lumerical, in the form, e.g.{'min': 1e-6, 'max': 1e-5}
.results
: Dictionary describing changes to the "results" field, to specify which values you want Lumerical to compute in the results. For instance, if I don't want to compute free and space charge, I can pass{'free_charge': False, 'space_charge': False}
and both will be disabled in the final output. The way they allow this to be controlled via the python API is abhorrent and I'm sorry if it doesn't work.scripts
: An optional list of lumerical scripts to be run post-simulationpause
: Set to "True" if you want Lumerical to pause after the simulation. The script will freeze and allow you to interact with Lumerical and inspect the simulation results manually, until a key is pressed at the terminal running the python script
Pipeline.py uses lumerical.py and penelope.py as interfaces to pipeline the running of the simulation process, beginning with primary particles and material specifications and ending with charge transport and eventually optical modulation calculations. It defines a set of arguments that describe how the pipeline should behave, which are separated into two categories:
Options describe meta-propeties of the pipeline run:
USE_PENELOPE_FILE
: Whether or not to use an existingpe-trajectories.dat
file rather than producing a new one using pyPENELOPE (useful for when the running system doesn't have PENELOPE installed)RUN_LUMERICAL
: Whether or not to run the lumerical script on the resultsWRITE_CHARGE_MAT
: Whether or not to compute a dense voxel matrix of charge generation data, required by LumericalRUN_MANUAL_CHARGE
: Whether or not to run our in-house charge simulation scriptsRUN_FROM_SIM
: Start at the nth trajectory (0 mean start at the beginning)RUN_TO_SIM
: -1 to run to the last trajectory, else stop at the nthPURGE_LARGE_DATA
: Whether or not to delete large intermediate simulation files after runningRUN_NTH_SIM
: If not less than zero, runs only the Nth trajectory sim
Parameters describe the inputs of the physical simulations
NUM_PARTICLES
: Number of primary particles (trajectories) to simulateINCLUDE_SECONDARIES
: Whether or not to simulate secondary particle trajectoriesBEAM_ENERGY
: Energy of primary particlesPARTICLE
: Which primary particle to use (1: electron, 2: photon, 3: positron)PEN_MATERIALS
: Material properties for penelope simulation (see penelope.py for details on how to create this)GEOMETRY
: Geometric configuration of penelope simulation (see penelope.py)LUM_MAT
: Material to use for Lumerical simulation. This will be the same material passed to penelope inPEN_MATERIALS
, but must use the exact name that Lumerical uses for the material in its GUI material pickerLUM_MESH
: The min and max lengths allowed in the Lumerical mesh (see section on lumerical.py)LUM_RESULTS
: Configure results output (see section on lumerical.py)
At the top of pipeline.py, these options are all exposed as constants. They will be used only if pipeline.py is run directly, rather than called by another file.
Several functions are available that streamline the definition of the arguments:
build_parameters()
and build_options()
take the parameters and options as
named arguments, and pack them into a single dictionary, which can then be
passed to run_pipeline()
when the pipeline is being run.
To make things even easier, default_parameters()
and default_options()
automatically fill out dictionaries containing the default params and options.
These dicts can then be manually changed if desired. This makes it simple to,
for instance, use all the default settings, but run more particles. Rather than
entering every single parameter, one can simply do:
params = default_parameters()
params['NUM_PARTICLES'] = 100
and the pipeline will run with all the default options apart from that single change.
The ability to run the pipeline using self-contained packages of parameters allows it to be called programmatically. "Batch"-like scripts are what I call scripts that generate pipeline configurations. All that is required to run many instances of the pipeline is to write a function that produces a list of sets of parameters/options. A simple example is as follows:
def energy_sweep():
BEAM_ENERGIES = [250e3, 300e3, 350e3, 400e3]
configurations = []
options = default_options()
for E in BEAM_ENERGIES:
params = default_parameters()
params['BEAM_ENERGY'] = E
configurations.append(
{'name': "{0:d}_eV".format(int(E)),
'parameters': params,
'options': options
})
return configurations
The run_batch()
function takes this function as an argument as well as
the option to use multiprocessing, and simply runs the pipeline as many times
as the config function specifies configurations.