CMInject Developer Guide¶
This developer’s guide gives a short overview of the steps required to develop CMInject and the potential starting points for adding changes. For an overview of CMInject itself, please refer to Welcome to CMInject’s documentation!.
Installation¶
To develop for CMInject, you should first install it as described in CMInject User guide, making
sure to use python3 setup.py develop instead of python3 setup.py install. This ensures
you don’t have to reinstall after every code change you make to see an effect when importing or
running your changed code.
Warning
When making changes to code that is compiled, like Cython code, you still need to rerun the
python3 setup.py develop to see an effect.
Tests¶
Tests are located in the tests/ subdirectory. They can be executed
via the command pytest. You may need to install pytest first,
as it is not an explicit dependency of CMInject:
pip install pytest
You should then be able to just run pytest from your console. Make
sure you are in the top-level directory of the CMInject code folder
when executing pytest, so that pytest can find the test files.
Starting points¶
You can find the full documentation of all modules in the APIdoc.
New concrete object definitions¶
Almost all of CMInject is written in terms of abstract base classes (ABCs). When defining a new kind of physical object like a Field, Device, or Particle, please derive a subclass of the corresponding ABC. For a list of all base classes, see cminject.base module.
For every ABC, there is documentation of concrete subclasses that you can refer to, to get a better idea of what a specific kind of class does and how it can be implemented.
New setup definitions¶
After you implement all required abstract methods defined by the ABC, you can instantiate your new subclass and use it. The next typical thing to define is a new kind of experimental setup, based on the Setup subclass (see cminject.setups package) and a combination of existing or newly implemented subclasses of Particle, Field, etc.
Below, we’ve included verbatim the definition of cminject.setups.example.ExampleSetup,
which is fairly simple. It defines a setup with a very small set of exposed parameters (-f,
--rho and -pos), consisting of only one Source, one Device, and three instances of
Detector.
This code is heavily annotated and should serve as a starting point for new setup definitions. You can define a new setup by copying over this code to a differently named class. Then you can, for example,
expose more parameters or remove existing ones(defined in
get_parser(), used inconstruct_experiment())add more devices (see
cminject.devices)add more sources (see
cminject.sources)add instances of new object definitions that you’ve defined yourself. This can, e.g., be a new kind of
Device. Consider the relevant base classes and their abstract interfaces, which you must implement in your subclasses.
#!/usr/bin/env python3
# -*- coding: utf-8; fill-column: 100; truncate-lines: t -*-
#
# This file is part of CMInject
#
# Copyright (C) 2018,2020 CFEL Controlled Molecule Imaging group
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# If you use this program for scientific work, you should correctly reference it; see the LICENSE.md file for details.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
"""
This file defines a simple example setup. It is, at its core, a reduced version of
:class:`cminject.definitions.setups.OneFlowFieldSetup`, and should only be used as an example to derive own setups from.
See the Developer Guide in the Sphinx documentation for additional explanations.
"""
# We need argparse for parsing arguments from the command line
import argparse
# Import the most basic definitions for defining a Setup which returns an Experiment
# and has arguments parsed by a SetupArgumentParser
from cminject.experiment import Experiment
from cminject.base import Setup
from cminject.utils.args import SetupArgumentParser, distribution_description
# Import some concrete class implementations to be able to define a simple setup:
from cminject.utils.distributions import constant, GaussianDistribution # Required types of distributions
from cminject.sources import VariableDistributionSource # Generates particles from property distributions
from cminject.detectors import SimpleZDetector # A detector which is positioned at some Z position
# A device for fluid flow based on an HDF5 file, together with FlowType, an enumeration of the possible models to use
from cminject.devices.fluid_flow import FluidFlowDevice, FlowType
class ExampleSetup(Setup):
"""
A simple setup which will simulate a single flow field with particles starting from a fixed position and velocity
distribution and radius. The only parameters available are the flow field filename and the density (density) of the
particle material. The density is there just to show how additional arguments can be added.
"""
@staticmethod
def construct_experiment(main_args: argparse.Namespace, args: argparse.Namespace) -> Experiment:
dt = 1e-5
experiment = Experiment(number_of_dimensions=2, time_step=dt, time_interval=(0, 1))
experiment.add_source(VariableDistributionSource(
number_of_particles=main_args.nof_particles,
position=args.pos,
velocity=[GaussianDistribution(mu=0.0, sigma=1e-3), GaussianDistribution(mu=2, sigma=0.5)],
radius=constant(5e-9), density=constant(args.rho)
))
experiment.add_device(FluidFlowDevice(filename=args.filename, flow_type=FlowType.STOKES, brownian_motion=True))
for z in [0.0, 0.01, 0.02]:
experiment.add_detector(SimpleZDetector(z))
return experiment
@staticmethod
def get_parser() -> SetupArgumentParser:
parser = SetupArgumentParser()
parser.add_argument('-f', '--filename', help='The filename of the flow field (HDF5).', type=str)
parser.add_argument('--rho', help='The density of the particle material [kg/m^3].', type=float, default=1050.0)
parser.add_argument('--pos', help='The position distributions in x/z space [m]', type=distribution_description,
nargs=2, default=[GaussianDistribution(0, 1e-3), constant(0.0)])
return parser
Performance considerations¶
A few points regarding runtime performance within this framework are discussed below.
Micro-optimizations¶
What is typically frowned upon and called an unnecessary micro-optimization can make or break the performance of your simulation in a numerical simulation framework. Whether a method to calculate a force takes 1us or 2us becomes highly relevant to the overall runtime of the program when this method is called 10^8 times during the course of a simulation.
To achieve decent performance, you can time primitive calls and compare your possible implementations. An overview of various useful profiling tools to help you with this task is given here.
Default simulation time step¶
Make sure to define an appropriate default time step for your Setup subclasses. Different physical problem warrant different amounts of precision. While choosing a time step that is too large will create numerical errors in user’s simulation results, choosing one that is too small can really impact performance negatively.
Particle instance size¶
Make sure to not create Particle subclasses whose instances will have a large memory footprint, unless absolutely necessary. Since the program is parallelized, Particle instances must be serialized and deserialized between the main process and worker processes every time the simulation of each single Particle starts and ends. Doing so requires I/O, which blocks your thread so useful work can not be done during this (de)serialization step.
Contact¶
Please direct any developer-centric questions to the authors, Simon Welker and Muhamed Amin.