How To Guides
To simplify the scripting, especially when repetitive, there is the possibility to write recipe for raypyng, to perform simulations, and automatize some tasks.
Write your own Recipe
Recipe Template
This the template to use to write a recipe.
At the beginning of the file import
SimulationRecipe from raypyng
and define a the Simulation class as an empty dummy.
This will ensure that you have access to all the methods of the
Simulation class.
A recipe should containe at least the __init__()
method and three more methods: params(),
and simulation_name(),
and they must have as an argument the simulate class.
Compose the simulation parameters in the params method:
The simulation parameter must return a list of dictionaries,
where the keys of the dictionaries are parameters of on abject
present in the beamline, instances of ParamElement class.
The items of the dictionary must be the values that the parameter should
assume for the simulations.
Compose the simulation parameters in the params() method:
The params() method must return a list of dictionaries.
The keys of the dictionaries are parameters of on abject
present in the beamline, instances of ParamElement class.
The items of the dictionary must be the values that the parameter should
assume for the simulations.
Compose the export parameters in the exports() method:
The The exports() method must return a list of dictionaries,
method must return a list of dictionaries.
The keys of the dictionaries are parameters of on abject
present in the beamline, instances of ParamElement class.
The items of the dictionary is the name of the file that you want to export
(print the output of Simulation.possible_exports and
possible_exports_without_analysis.
Define the name to give to the simulation folder
in simulation_name()
from raypyng.recipes import SimulationRecipe
class Simulate: pass
class MyRecipe(SimulationRecipe):
def __init__(self):
pass
def params(self,sim:Simulate):
params = []
return params
def exports(self,sim:Simulate):
exports = []
return exports
def simulation_name(self,sim:Simulate):
self.sim_folder = ...
return self.sim_folder
How To Write a Recipe
An example of how to write a recipe that automatically exports a file for each element present in the beamline:
from raypyng.recipes import SimulationRecipe
class ExportEachElement(SimulationRecipe):
"""At one defined energy, export a file for each optical element."""
def __init__(self, energy: float, /, nrays: int = None, sim_folder: str = None):
"""
Args:
energy (float): the energy to simulate, in eV
nrays (int): number of rays for the source
sim_folder (str, optional): the name of the simulation folder.
If None, the rml filename will be used. Defaults to None.
"""
if not isinstance(energy, (int, float)):
raise TypeError(
'The energy must be an int or float, while it is a', type(energy)
)
self.energy = energy
self.nrays = nrays
self.sim_folder = sim_folder
def params(self, sim):
params = []
# find the source and scan it over the user-defined energy range
found_source = False
for oe in sim.rml.beamline.children():
if hasattr(oe, "photonEnergy"):
self.source = oe
found_source = True
break
if not found_source:
raise AttributeError('I did not find the source')
params.append({self.source.photonEnergy: self.energy})
# set reflectivity to 100% on every element that supports it
for oe in sim.rml.beamline.children():
for par in oe:
try:
params.append({par.reflectivityType: 0})
except Exception:
pass
# all done, return the resulting params
return params
def exports(self, sim):
# export RawRaysOutgoing for every element in the beamline
exports = []
for oe in sim.rml.beamline.children():
exports.append({oe: 'RawRaysOutgoing'})
return exports
def simulation_name(self, sim):
if self.sim_folder is None:
return 'ExportEachElement'
return self.sim_folder
if __name__ == "__main__":
from raypyng import Simulate
rml_file = 'rml_file.rml'
sim = Simulate(rml_file, hide=True)
sim.analyze = False
myRecipe = ExportEachElement(energy=1000, nrays=10000, sim_folder='MyRecipeTest')
sim.run(myRecipe, multiprocessing="auto", force=True)
How to work with Undulator File
The WaveHelper class helps to inspect a WAVE simulation folder and provides a simple way to extract the absolute path of the simulation files to feed to the Undulator File. In this example we use the WAVE folder provided in the example folder at this link. Inside the folder there are WAVE simulation files for the first, third and fifth harmonic, and the Undulator is called U49
import numpy as np
from raypyng.wave_helper import WaveHelper
WH = WaveHelper(wave_folder_path='WAVE', harmonic=3, undulator='U49')
WH.report_available_energies(verbose=True)
This produces the following output:
I found the following harmonics: dict_keys([1, 3, 5])
the energy points for each harmonic are equally spaced
Harmonic number 1, available energies:
start 80
stop 570
step 10
Harmonic number 3, available energies:
start 240
stop 1710
step 30
Harmonic number 5, available energies:
start 400
stop 2850
step 50
We can now extract the file location for all the energies or a subset of the energies available for the first harmonic of the undulator:
energies = np.arange(80,570,10)
energy_files = WH.convert_energies_to_file_list(1,energies)
energy_files contains the absolute path to the WAVE simulation file for each energy. This can be used to change the energy of an Undulator by calling the parameter undulatorFile.