Adding new models#
Users and developers can easily add any kind of new or already existing model to Pyxel, thanks to the model plug-in mechanism developed for this purpose.
Existing models: Models
Model function#
A model function is a function that takes in the Detector
object as one of the arguments
and edits the data stored in it. To add it to Pyxel, you have to copy the script containing your function,
let’s say my_script.py
, into the corresponding model group folder in Pyxel.
For example if our function edits the photon array, the script my_script.py
should go into pyxel/models/photon_collection
.
A model function that multiplies the photon array with the input argument would look like this:
from pyxel.detectors import Detector
def my_model_function(detector: Detector, arg: int = 0) -> None:
"""This is my model that will multiply pixel array with the argument.
Parameters
----------
detector
arg
"""
detector.photon.array = detector.photon.array * arg
return None
Editing the YAML file#
To use the new model function in a Pyxel pipeline you need to add your model function to the YAML config file, providing the input arguments for the function.
# YAML config file with a new model in photon_collection
pipeline:
photon_collection:
- name: some_other_model
func: pyxel.models.photon_collection.some_other_model
enabled: true
arguments:
wavelength: 650
NA: 0.9
#######################################################################
- name: my_model #
func: pyxel.models.photon_collection.my_script.my_model_function #
enabled: true #
arguments: #
arg: 124 #
#######################################################################
Tip
If we import my_model_function
in the pyxel/models/photon_collection/__init__.py
,
then the path to the model is shorter: func: pyxel.models.photon_collection.my_model_function
.
Model wrapper#
If your model is a Python class, package or it is implemented in a programming language other than Python (C/C++, Fortran, Java), then it is necessary to create a wrapper model function, which calls and handles the code (class, package or non-Python code).
Creating a new model with a Pyxel command#
It is possible to create a new model from an already prepared template with the built in command like so:
pyxel create-model photon_collection/new_model
or
python -m pyxel create-model photon_collection/new_model
This will create a new python script new_model.py
with a template model function
in folder pyxel/models/photon_collection
. All you have to do is edit your model function
and the docstring and then copy the YAML
configuration section from the docstring into the desired configuration file.
Don’t forget to import your model function in the __init__.py
file of the appropriate model group for faster access.
Best Practices#
Write models as pairs of pure and impure functions#
If a model is changing one of the data structures stored in the Detector
object,
then it is advised to write the model as a pair of functions:
one as an impure function that changes the state of the Detector
object;
the other as a pure function that does the actual calculations without changing the state of the input arguments.
More info on pure and impure functions: https://en.wikipedia.org/wiki/Pure_function, https://alvinalexander.com/scala/fp-book/benefits-of-pure-functions/.
So instead of this:
# impure function
def my_model(detector: Detector, arg: int) -> None:
input_array = detector.pixel.array
# do computations with array
output_array = arg * input_array
detector.pixel.array = output_array
Do this:
# pure function
def compute_model_effect(input_array: numpy.ndarray, arg: int) -> np.ndarray:
# do computations with array
output_array = arg * input_array
return output_array
# impure function
def my_model(detector: Detector, arg: int) -> None:
input_array = detector.pixel.array # type: np.ndarray
output_array = compute_model_effect(input_array=input_array, arg=arg)
detector.pixel.array = output_array
This way the model effect and the function compute_model_effect
are much easier to test,
also it simplifies the use of package numba
for speeding up code.
Using the numpy.random module in models#
If a model uses functions from numpy.random
module,
avoid resetting the global seed with numpy.random.seed()
inside the model,
instead use the “with” statement function set_random_seed
from pyxel.util
and provide an optional argument seed
.
The function set_random_seed
will use this seed to temporary change the state of the random generator,
or keep the same state (use the outer scope seed) if no specific seed is provided.
Example:
from pyxel.util import set_random_seed
def my_model(detector, user_arg, seed=None):
input_array = detector.pixel.array
with set_random_seed(seed):
# compute_model_effect uses functions from numpy.random module
output_array = compute_model_effect(input_array=input_array, arg=arg)
detector.pixel.array = output_array