Binning
This binning plugin is a flexible binner for particles and field properties. Users can
Define their own axes
Define their own quantity which is binned
Choose which species are used for particle binning
Choose which fields are used for field binning
Choose how frequently they want the binning to be executed
Choose how many times the binned quantity should be combined over time before dumping
Write custom output to file, for example other quantities related to the simulation which the user is interested in
Execute multiple binnings at the same time
Pass extra parameters as a tuple, if additional information is required by the kernels to do binning.
Define a host-side hook to execute code before binning at each notify step, for example to fill
FieldTmp.
User Input
Users can set up their binning in the binningSetup.param file. After setup, PIConGPU needs to be recompiled.
Attention
Unlike other plugins, the binning plugin doesn’t provide any runtime configuration. To set up binning, users need to define it in the param file and then recompile.
A binner is created using the addParticleBinner() or addFieldBinner() functions, which describe the configuration options available to the user to set up particle and field binning respectively. Multiple binnings can be run at the same time by simply calling addParticleBinner() or addFieldBinner() multiple times with different parameters.
-
class BinningCreator
An object of this class is provided to the user to add their binning setups.
Public Functions
-
template<typename T_BinaryOp = pmacc::math::operation::Add, typename T_Extras = std::tuple<>>
inline auto &addParticleBinner(std::string const &binnerOutputName, auto const &axisTupleObject, auto const &speciesTupleObject, auto const &depositionData, T_Extras const &extraData = {}) Creates a particle binner from user input and adds it to the vector of all binners.
The Particle binner will bin particle quantities to create histograms on a grid defined by axes. The results will be written to openPMD files.
- Template Parameters:
T_BinaryOp – The binary operation to be used for reduction. Optional. Defaults to addition.
- Parameters:
binnerOutputName – filename and dataset name for openPMD output. Must be unique to avoid overwrites during data dumps and undefined behaviour during restarts.
axisTupleObject – tuple holding the axes. Each element in the tuple describes an axis for binning.
speciesTupleObject – tuple holding the species to bin. Each element specifies a species to be included in the binning.
depositionData – functor description of the deposited quantity. Defines which particle property to bin.
extraData – tuple holding extra data to be passed to the binner. Can be used for optional configurations. Optional. Defaults to an empty tuple.
- Returns:
ParticleBinningData& reference to the created ParticleBinningData object. This can be used to further configure the binning setup if needed.
-
template<typename T_BinaryOp = pmacc::math::operation::Add, typename T_Extras = std::tuple<>>
inline auto &addFieldBinner(std::string const &binnerOutputName, auto const &axisTupleObject, auto const &fieldsTupleObject, auto const &depositionData, T_Extras const &extraData = {}) Creates a field binner from user input and adds it to the vector of all binners.
The Field binner will bin field quantities to create histograms on a grid defined by axes. The results will be written to openPMD files.
- Template Parameters:
T_BinaryOp – The binary operation to be used for reduction. Optional. Defaults to addition.
- Parameters:
binnerOutputName – filename and dataset name for openPMD output. Must be unique to avoid overwrites during data dumps and undefined behaviour during restarts.
axisTupleObject – tuple holding the axes. Each element in the tuple describes an axis for binning.
fieldsTupleObject – tuple holding the fields to bin. Each element specifies a field to be included in the binning.
depositionData – functor description of the deposited quantity. Defines which field property to bin.
extraData – tuple holding extra data to be passed to the binner. Can be used for optional configurations. Optional. Defaults to an empty tuple.
- Returns:
FieldBinningData& reference to the created FieldBinningData object. This can be used to further configure the binning setup if needed.
-
template<typename T_BinaryOp = pmacc::math::operation::Add, typename T_Extras = std::tuple<>>
The most important parts of defining a binning are the axes (the axes of the histogram which define the bins) and the deposited quantity (the quantity to be binned). Both of these are described using the “Functor Description”.
Functor Description
The basic building block for the binning plugin is the Functor Description object, and it is used to describe both axes and the deposited quantity. It describes the properties which we find interesting and how we can calculate/get this property from the particle or field. A functor description is created using createFunctorDescription.
-
template<typename QuantityType, typename FunctorType>
inline auto picongpu::plugins::binning::createFunctorDescription(FunctorType functor, std::string const &name, std::array<double, numUnits> units) These functions are the analog of make_tuple etc and exist to avoid specifying the T_Functor, which is automatically deduced with these functions.
Describes the functors, units and names for the axes and the deposited quantity
- Todo:
infer T_Quantity from T_Functor, needs particle type also, different particles may have different return types
- Template Parameters:
QuantityType – The type returned by the functor
FunctorType – Automatically deduced type of the functor
- Parameters:
functor – Functor which access the particle property
name – Name for the functor/axis written out with the openPMD output.
units – The dimensionality of the quantity returned by the functor in the 7D format. Defaults to unitless.
- Returns:
FunctorDescription object
Functor
The functor is run on the GPU and constitutes the means by which the user can execute code on device while binning. This may be used to calculate the deposited quantity or to calculate the axis values.
The functor needs to follow the signature shown below. This provides the user access to the particle or field object and with information about the domain.
The return type is defined by the user.
The first two arguments are always the worker and the domainInfo. For particle binning this is followed by the particle object, and for field binning this is followed by the field object (if any). If extra data is passed, this is also passed as arguments. It is the responsibility of the user to ensure that their functors have an appropriate number and types of arguments to match the provided tuples. A few examples are shown below.
For particles:
auto myParticleFunctor = [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& particle) -> returnType
{
// fn body
return myParameter;
};
For particles with two extra data paramters:
auto myParticleWithExtraDataFunctor = [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& particle, auto const& extraData1, auto const& extraData2) -> returnType
{
// fn body
return myParameter;
};
For one field:
auto myFieldFunctor = [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& field) -> returnType
{
// fn body
return myParameter;
};
For two fields with one extra data parameter:
auto myFieldWithExtraDataFunctor = [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& field1, auto const& field2, auto const& extraData) -> returnType
{
// fn body
return myParameter;
};
Note
Fields and extra data may be tuples with multiple elements which are unpacked and passed to the user-defined functors. Therefore, it is the responsibility of the user to ensure that their functors have an appropriate number of arguments to match the provided tuples.
Dimensionality and units
Users can specify the units of their functor output using a 7 dimensional array. Each element of the array corresponds to an SI base unit, and the value stored in that index is the exponent of the unit.
The dimensional base quantities are defined in SIBaseUnits_t following the international system of quantities (ISQ).
If no units are given, the quantity is assumed to be dimensionless.
// Define units
std::array<double, numUnits> momentumDimension{};
momentumDimension[SIBaseUnits::length] = 1.0;
momentumDimension[SIBaseUnits::mass] = 1.0;
momentumDimension[SIBaseUnits::time] = -1.0;
Domain Info
The binning plugin passes the domain info object to functors as a parameter to give the user access to some useful PIConGPU quantities inside the functor. Importantly, it enables the user to find the location of the particle or field in the simulation domain.
The DomainInfo class contains:
-
class DomainInfoBase
Provides knowledge of the simulation domain to the user.
Names and concept are described at https://github.com/ComputationalRadiationPhysics/picongpu/wiki/PIConGPU-domain-definitions
Subclassed by picongpu::plugins::binning::DomainInfo< BinningType::Field >, picongpu::plugins::binning::DomainInfo< BinningType::Particle >
Public Functions
The global and local offsets can be understood by looking at the PIConGPU domain definitions.
Particle binning
For particle binning, we can use the getParticlePosition function.
-
template<DomainOrigin T_Origin, PositionPrecision T_Precision = PositionPrecision::CELL, PositionUnits T_Units = PositionUnits::CELL>
requires concepts::ValidPositionRequest<T_Origin, T_Precision>
ALPAKA_FN_ACC auto picongpu::plugins::binning::getParticlePosition(DomainInfo<BinningType::Particle> const &domainInfo, auto const &particle) Returns the particle position as a pmacc vector.
By default, returns the cell index of the cell the particle is in. Returns a fractional cell index representing the in-cell position if PositionPrecision::SubCell is passed. Returns in SI units if PositionUnits::SI is specified.
Warning
Converting the particle positions to SI might be dangerous, especially with respect to the total origin, as floating point numbers lose precision as the distance from the origin increases.
Warning
The comoving frame defined by DomainOrigin::MOVING_WINDOW is only supported with SUBCELL precision.
- Template Parameters:
T_Origin – The origin reference for the position.
T_Precision – The precision of the position (CELL index or SUBCELL position).
T_Units – The units of the position (SI, PIC or CELL).
- Parameters:
domainInfo – The domain information.
particle – The particle whose position is to be determined.
- Returns:
The particle position as a pmacc vector.
The particle position has the default precision set to cell precision, and the position is returned in cell units by default.
The return type of getParticlePosition depends on the combination of precision and units:
CELLprecision +CELLunits ->pmacc::DataSpace<simDim>(integer cell index vector).SUB_CELLprecision +CELLunits -> floating-point vector in number of cellsAny other combination with
SIorPICunits ->pmacc::math::Vector<floating_T, simDim>(floating-point vector), where thefloating_Ttype is an appropriately precise floating point type based on internal representations.
To request a different origin, precision, or unit system, use the template parameters as described below.
Field binning
For field binning, DomainInfo additionally stores the localCellIndex within the supercell.
We can use the getCellIndex function to return the current cell index being binned relative to the selected origin.
getCellIndex<DomainOrigin, PositionUnits>()
To obtain the exact position of a field inside the cell, relative to the cell index, use the FieldPosition trait.
Domain origin
The reference origin for returned positions is selected with DomainOrigin:
The available origins are:
-
enum class picongpu::plugins::binning::DomainOrigin
Values:
-
enumerator TOTAL
Absolute origin of the simulation.
This includes regions that are no longer part of the current global volume because they have moved out of the sliding window.
-
enumerator GLOBAL
Origin of the current sliding window, i.e.
the currently simulated domain across all GPUs, excluding guard cells.
-
enumerator LOCAL
Origin of the local domain on the current GPU, excluding guard cells.
-
enumerator MOVING_WINDOW
Origin relative to the origin of the sliding window.
This origin only starts moving with the sliding window and is not discretized to the cell grid
-
enumerator LOCAL_WITH_GUARDS
Origin of the local domain on the current GPU, including guard cells.
This setting is in particular used to access field data for the current cell with getCellIndex.
-
enumerator TOTAL
Position precision
For particle positions, the precision is selected with PositionPrecision.
The available precisions are:
-
enum class picongpu::plugins::binning::PositionPrecision
Values:
-
enumerator CELL
Returns the particle position at cell precision, i.e.
as a cell index.
-
enumerator SUB_CELL
Returns the particle position with sub-cell precision, i.e.
as the cell index plus the particle position inside the cell in the range [0,1). The result is therefore a floating-point position in units of cells.
-
enumerator CELL
Position units
The output units for positions are selected with PositionUnits.
The available units are:
-
enum class picongpu::plugins::binning::PositionUnits
Output unit type for particle position.
Values:
-
enumerator SI
Returns the position in SI units.
Note
Converting the particle positions to SI might be dangerous, especially with respect to the total origin, as floating point numbers lose precision as the distance from the origin increases.
-
enumerator PIC
Returns the position in PIC units.
Note
Converting the particle positions to PIC might be dangerous, especially with respect to the total origin, as floating point numbers lose precision as the distance from the origin increases.
-
enumerator CELL
Returns the position as the number of cells.
Integral value if PositionPrecision is Cell and floating point if PositionPrecision is SubCell.
-
enumerator SI
Note
Using positions in SI/PIC units introduces floating point numerical errors and may be especially problematic when using the TOTAL origin with moving window, because floating-point precision decreases as the distance from the origin increases.
Axis
Axis is a combination of a functor description and an axis splitting. These are brought together by createAxis functions, depending on what kind of an axis you want. The name used in the functor description is used as the name of the axis for openPMD.
Attention
The return type of the functor as specified in the functor description is required to be the same as the type of the range (min, max).
- Currently implemented axis types
Linear Axis
Log Axis
-
template<typename T_Attribute, typename T_AttrFunctor>
class LinearAxis Linear axis with contiguous fixed sized bins.
Axis splitting is defined with min, max and n_bins. Bin size = (max-min)/n_bins. Bins are closed open [) intervals [min, min + size), [min + size, min + 2*size) ,…, [max-size, max). Allocates 2 extra bins, for under and overflow. These are bin index 0 and (n_bins+2)-1 T_Attribute is a Numeric/Arithmetic type
-
template<typename T_Attribute, typename T_AttrFunctor>
class LogAxis Log axis with logarithmically sized bins.
Similar to geompace from numpy Axis splitting is defined with min, max and n_bins. A bin with edges a and b is a closed-open interval [a,b) If overflow bins are enabled, allocates 2 extra bins, for under and overflow. These are bin index 0 and (n_bins+2)-1 WARNING: For integral valued axes, the LogAxis always suffers from floating point erros, and can for certain axis splittings and value combinations under or over estimate the bin index
Attention
The log axis suffers from floating point precision errors, so for certain combinations of min, max, nBins and values to be binned the bin index calculated might be off by one. If using an integral log axis, be careful of what you are doing.
Binning can be done over an arbitrary number of axes, by creating a tuple of all the axes. Limited by memory depending on number of bins in each axis.
Axis Splitting
Defines the axis range and how it is split into bins. Bins are defined as closed open intervals from the lower edge to the upper edge. In the future, this plugin will support other ways to split the domain, eg. using the binWidth or by auto-selecting the parameters.
-
template<typename T_Data>
class AxisSplitting Holds the axis range in SI units and information on how this range is split into bins.
Range
-
template<typename T_Data>
class Range Holds the range in SI Units in axis space over which the binning will be done.
Note
Axes are passed to addParticleBinner or addFieldBinner grouped in a tuple. This is just a collection of axis objects and is of arbitrary size.
Users can make a tuple for axes by using the createTuple() function and passing in the axis objects as arguments.
Species
PIConGPU species which should be used in particle binning. Species can be instances of a species type or a particle species name as a PMACC_CSTRING. For example,
auto electronsObj = PMACC_CSTRING("e"){};
Optionally, users can specify a filter to be used with the species. This is a predicate functor, i.e. it is a functor with a signature as described above and which returns a boolean. If the filter returns true it means the particle is included in the binning.
They can then create a FilteredSpecies object which contains the species and the filter.
/**
* Optionally pass a particle filter to be used for this
* Simply a particle predicate functor
*/
auto allParticles
= [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& particle) -> bool
{ return true; };
auto filteredElectrons = FilteredSpecies{electronsObj, allParticles};
Note
Species are passed to addParticleBinner in the form of a tuple. This is just a collection of Species and FilteredSpecies objects (the tuple can be a mixture of both) and is of arbitrary size.
Users can make a species tuple by using the createSpeciesTuple() function and passing in the objects as arguments.
Fields
PIConGPU fields which should be used in field binning.
Fields must be instances of the FieldInfo type.
-
template<typename Field>
struct FieldInfo Struct to hold information about a field.
The constructor takes a id which describes how to get the field from the DataConnector
- Template Parameters:
Field – The type of the field
For example,
// Bring the fields together in a tuple
auto fieldsTuple
= createTuple(FieldInfo<FieldB>(FieldB::getName()), FieldInfo<FieldTmp>(FieldTmp::getUniqueId(0)));
Fields are passed to addFieldBinner in the form of a tuple. This is just a collection of FieldInfo and is of arbitrary size.
Users can make a fields tuple by using the createTuple() function and passing in the objects as arguments.
The functors receive the fields in the form of the field data box, which is the field data on the current GPU including the guard cells, in the order they are passed in the tuple.
auto getFieldX
= [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, auto const& field1, auto const& field2)
-> float_X { return field1(domainInfo.template getCellIndex<DomainOrigin::LOCAL_WITH_GUARDS>()).x(); };
Note
It is possible to have an empty tuple for fields when doing field binning, in which case the functor will be called with no fields. This may be useful if you are passing in extra data and want field traversal over it.
Note
It is possible to have field information available while doing particle binning as well. Users can simply pass in FieldInfo objects in the extra data tuple, and the functor will be called with the field information as well.
Deposited Quantity
Quantity to be deposited is simply a functor description. The signature of quantity functors is
auto myQuantityFunctor = [] ALPAKA_FN_ACC(auto const& worker, auto const& domainInfo, ...) -> returnType
This option makes it evident that the binning is more than just about creating histograms. While histograms are a common use case, the binning plugin allows for using various binary operations to combine various quantities within bins and not just noting the frequencies of occurrences. This means that users can define custom quantities to be combined in each bin, such as charge, energy, momentum, or any other property of interest. The flexibility of the functor description enables users to specify exactly what and how they want to combine data in the bins. For example, you might want to combine the total charge of particles within each bin, or the average kinetic energy of particles in a specific region. The deposited quantity functor provides the mechanism to calculate and return these values, which are then combined in the corresponding bins during the simulation. By default the deposited quantity is added for each bin, but this is configurable by the user by setting a binary operation for the reduce.
Extra Data
Users can pass extra data to the functors if additional information is required by the kernels to do binning.
Extra data is passed to binningCreator’s addParticleBinner or addFieldBinner functions in the form of a tuple. This tuple is just a collection of objects and is of arbitrary size.
Users can make a tuple of the extra data by using the createTuple() function and passing in the objects as arguments.
Note
Ensure that the functors have an appropriate number of arguments to match the provided tuples. The extra data may be tuples with multiple elements which are unpacked and passed to the user-defined functors.
Host-side hook
Users can define a host-side hook to execute code before binning starts for a notify step.
This hook is set using the setHostSideHook method of the BinningCreator class and takes a callable (lambda/functor/std::function) which returns void.
[]() -> void{
// host-side code to be executed before binning
}
For example, this can be used to pre-process data on the host side, such as filling temporary fields before they are used in field binning.
Notify period
Set the periodicity of the output. Follows the period syntax defined here. Notifies every time step by default.
Dump Period
Defines the number of notify steps to reduce over. Note that this is not a reduction over actual PIC iterations, but over the notify periods. For example a value of 10 means that after every 10 notifies, an reduced file will be written out. If PIConGPU exits before executing 10 notifies, then there will be no output. The plugin dumps on every notify if this is set to either 0 or 1. This is the default behaviour.
Binning Particles Leaving the Simulation Volume
-
enum picongpu::plugins::binning::ParticleRegion
Bin particles in enabled region.
All regions must be represented by a unique bit
Values:
By default, only particles within the simulation volume are binned by the particle binner. However, users can modify this behavior to include or exclusively bin particles that are leaving the global simulation volume.
This can be configured using the enableRegion and disableRegion options with the regions defined by the ParticleRegion enum.
Attention
Users must carefully configure the notify period when using the binning plugin for leaving particles. The plugin bins particles leaving the global simulation volume at every timestep (except 0) after particles are pushed, regardless of the notify period. If the plugin is not notified at every timestep, this can cause discrepancies between the binning process and histogram dumps, which follow the notify period. Additionally, the binning plugin is first notified at timestep 0, allowing users to bin initial conditions. However, leaving particles are first binned at timestep 1, after the initial particle push. Therefore, users should consider setting the notify period’s start at timestep 1, depending on their specific needs.
writeOpenPMDFunctor
Users can also write out custom output to file, for example other quantities related to the simulation which the user is interested in.
This is a lambda with the following signature, and is set using the setWriteOpenPMDFunctor method.
[=](::openPMD::Series& series, ::openPMD::Iteration& iteration, ::openPMD::Mesh& mesh) -> void
Note
Make sure to capture by copy only, as the objects defined in the param file are not kept alive
OpenPMD Backend Configuration
Users can set a JSON configuration string for the OpenPMD backend using the setOpenPMDBackendConfig setter method.
Configuration Options Summary
The following table summarizes the configuration options available for the binner object returned by addParticleBinner() or addFieldBinner(). These methods allow for fine-tuning the behavior of the binning plugin.
Option |
Description |
Default Value |
|---|---|---|
|
Sets the periodicity of the output. Follows the period syntax defined here. |
|
|
Defines the number of notify steps to reduce over before dumping. A value of 0 or 1 means dump on every notify. |
|
|
Sets a host-side hook (lambda/functor) to execute code before binning at each notify step. |
An empty lambda |
|
Configures binning for particles inside ( |
|
|
Sets a functor to write custom data to the openPMD output file. |
An empty functor. |
|
Sets a backend-specific JSON configuration string for the openPMD output (used when writing). |
|
|
Sets the file extension for the openPMD output. |
Default OpenPMD extension (e.g., |
|
Sets the infix for file names in openPMD output. The default uses |
|
OpenPMD Output
The binning outputs are stored in HDF5 files in simOutput/binningOpenPMD/ directory.
The files are named as <binnerOutputName>_<timestep>.h5.
The OpenPMD mesh is call “Binning”.
The outputs in written in SI units.
The output histogram has 2 bins more in each dimension than the user-defined nBins in that dimension, to deal with under and overflow.
The number of bin edges written out for an axis is one more than the user-defined nBins. These represent the bins in [min,max]. Since there are actually nBins + 2 bins, two edges are not written out.
These are the first and last edge, corresponding to the overflow bins, and they have the value of -inf and + inf.
Attribute |
Hierarchy |
Description |
|---|---|---|
|
Mesh |
Scaling factor for the deposited quantity to convert to SI |
|
Mesh |
The edges of the bins of an axis in SI units |
|
Mesh |
The units of an axis |
|
Iteration |
Reduction operations per dump period |
Reduction
The binning plugin provides flexible options for reducing data within bins. This is useful when a dump period is set. Instead of simply adding values in a bin, users can utilize other PMacc operations as well. This includes operations such as addition, minimum, maximum, bitwise AND, bitwise OR, and bitwise XOR. This flexibility enables users to tailor the reduction process to their specific needs. For instance, you might want to track the minimum or maximum value of a certain property within each bin. A list of available PMacc operations can be found under /include/pmacc/math/operation. Only operations defining the getMPI_Op function and the AlpakaAtomicOp and NeutralElement traits are supported.
Note
The operation is only viable if it has a corresponding MPI reduction operation, a neutral element, and a cooresponding alpaka operation. Only binary atomics on arithmetic types are supported. In particular CAS operations are not supported.
The reduce operation is passed as a template parameter to the createBinner functions. For example, to use the maximum operation for reduction, you would pass the corresponding PMacc operation as a template parameter:
binningCreator
.addParticleBinner<pmacc::math::operation::Max>(
"particleBinning",
createTuple(ax_timeStep, ax_py),
speciesTuple,
getCounts)
.setDumpPeriod(2000);
Similarly, you can use other PMacc operations such as pmacc::math::operation::Min, pmacc::math::operation::Add, pmacc::math::operation::BitwiseAnd, pmacc::math::operation::BitwiseOr, pmacc::math::operation::BitwiseXor, etc.
Example binning plugin usage: Laser Wakefield electron spectrometer
The LWFA example contains a sample binning plugin setup to calculate an in-situ electron spectrometer. The kinetic energy of the electrons \(E = (\gamma - 1) m_o c^2\) is plotted on axis 1 and the direction of the electrons \(\theta = \mathrm{atan}(p_x/p_y)\) is plotted on axis 2. The charge \(Q\) moving in the bin direction \(\theta\) at the bin energy \(E\) is calculated for each bin. Such spectrometers are a common tool in plasma based electron acceleration experiments [Kurz2018].
Note
Please note that if you specify the SI units of an axis, e.g. via energyDimension in the LWFA example,
PIConGPU automatically converts to the internal unit system, but then also expects SI-compliant values for the axis range
(in the case of energy Joules).
To read the electron spectrometer data in python, one could load and plot it like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import openpmd_api as io
import scipy.constants as const
# access openPMD series of eSpec
series = io.Series("./LWFA/simOutput/binningOpenPMD/eSpec_%T.h5", access=io.Access_Type.read_only)
last_iter = list(series.iterations)[-1]
it = series.iterations[last_iter]
espec_h = it.meshes['Binning'][io.Mesh_Record_Component.SCALAR]
# load data
espec = espec_h[:,:]
series.flush()
# convert to SI units and make positve (electrons have a negative charge)
espec *= espec_h.get_attribute('unitSI') * -1
# get axes (they are already in the correct SI unit)
E_bins = espec_h.get_attribute('Energy_bin_edges')
theta_bins = espec_h.get_attribute('pointingXY_bin_edges')
# convert C/J/rad -> C/MeV/mrad
convert_C_per_Joule_per_rad_to_pC_per_MeV_per_mrad = 1./1e-12 * const.elementary_charge/1e6 * 1/1e3
# plot
plt.pcolormesh(np.array(E_bins) / const.elementary_charge / 1e6,
np.array(theta_bins) / 0.001,
espec[1:-1, 1:-1] * convert_C_per_Joule_per_rad_to_pC_per_MeV_per_mrad,
norm=LogNorm(), cmap=plt.cm.inferno)
cb = plt.colorbar()
plt.xlabel(r"$E \, \mathrm{[MeV]}$", fontsize=18)
plt.xticks(fontsize=14)
plt.ylabel(r"$\theta \, \mathrm{[mrad]}$", fontsize=18)
plt.yticks(fontsize=14)
cb.set_label(r"$\frac{\mathrm{d}^2 Q}{\mathrm{d} E \mathrm{d}\theta} \, \mathrm{[pC/MeV/mrad]}$", fontsize=20)
for i in cb.ax.get_yticklabels():
i.set_fontsize(14)
plt.tight_layout()
plt.show()
References
T. Kurz, J.P. Couperus, J.M. Krämer, et al. Calibration and cross-laboratory implementation of scintillating screens for electron bunch charge determination, Review of Scientific Instruments (2018), https://doi.org/10.1063/1.5041755
More examples of binning plugin usage
Further examples of the binning plugin can be found in the atomic physics example at share/picongpu/examples/AtomicPhysics/include/picongpu/param/binningSetup.param and the share/picongpu/tests/compile2 test.