The magnetic field component of ACTS provides functionality to describe arbitrary magnetic field configurations in an experiment. It is implemented in a generic way and can be extended to connect to an experiment specific upstream source of field data.
Algorithms which need magnetic field information (e.g.
{class}Acts::AtlasStepper
, {class}Acts::EigenStepper
) accept the magnetic
field as an explicit argument.
All magnetic field implementations inherit and implement the interface {class}Acts::MagneticFieldProvider
:
It provides a generic interface over different implementations. To speed up magnetic field lookup, each implementation can have a cache object. The cache object can for example be used to store a local interpolation domain, to speed up nearby field lookups. The client is expected to pass into lookup calls:
:::{doxygentypedef} Acts::MagneticFieldProvider::Cache :::
The implementation is then free to use and update this cache instance as
needed. Before a client can issue field lookup calls, it needs to obtain an
initialized instance of this cache object. This can be achieved generically for
all implementations by using {func}Acts::MagneticFieldProvider::makeCache
.
This function accepts an instance of {type}Acts::MagneticFieldContext
, see
for details.
The main lookup method of {class}Acts::MagneticFieldProvider
is
:::{doxygenfunction} Acts::MagneticFieldProvider::getField :outline: :::
Aside from the lookup position as a global position vector, it accepts an
instance of the opaque cache object mentioned before. The return value is a
{class}Acts::Result
object. It either contains the field value at the
requested location, or an {enum}Acts::MagneticFieldError
in case of a lookup
failure, like an out-of-bounds lookup position.
Below is an example of how a client can interact with an instance of
{class}Acts::MagneticFieldProvider
.
// in event context
auto fieldContext = getExperimentFieldContext();
const Acts::MagneticFieldProvider& fieldProvider = getFieldProvider();
auto cache = fieldProvider.makeCache(fieldContext);
auto lookupResult = fieldProvider.getField(Acts::Vector3{10, 10, 10}, cache);
if(!lookupResult.ok()) {
throw std::runtime_error{"Field lookup failure"};
}
Acts::Vector3 fieldValue = *lookupResult;
:::{doxygentypedef} Acts::MagneticFieldContext :outline: :::
The magnetic field context is an opaque type which contains experiment specific
event context information. This can be used to supply event dependent data to
the magnetic field instance, in case it is needed to provide correct field
values. The library itself does not make any assumptions on the content of this
context type (it is implemented using std::any
), but passes a reference
through the call-chain to the field implementation. An experiment specific
field implementation is then expected to performa cast to the concrete type,
and use the contents.
An example use case of the context could be to look up conditions data / records for the value of the magnetic field at the time of the event.
There are a number of field provider implemenations found in core which serve different purposes.
The simplest implementation is a constant field, which returns the same field
values at every queried location. It is implemented in the
{class}Acts::ConstantBField
class.
:::{doxygenclass} Acts::ConstantBField :members: ConstantBField :::
As seen above, the class is constructed from a three-dimensional field vector,
which is returned unmodified to every call to
{func}Acts::ConstantBField::getField
.
For more complex magnetic field implementations
{class}Acts::InterpolatedMagneticField
can be used. The idea here is to calculate
an interpolated value of the magnetic field from a grid of known field values.
In 3D, this means the interpolation is done from the 8 cornerpoints of a field
cell. The field cell can be retrieved for any given position. Since during
typical access patterns, e.g. the propagation, subsequent steps are relatively
likely to not cross the field cell boundary, the field cell can be cached.
:::{figure} ../figures/bfield/field_cell.svg :width: 300 :align: center Illustration of the field cell concept. Subsequent steps are clustered in the same field cell. The field cell only needs to be refetched when the propagation crosses into the next grid region. :::
{class}Acts::InterpolatedMagneticField
extends the
{class}Acts::MagneticFieldProvider
interface to add a number of additional
methods:
:::{doxygenclass} Acts::InterpolatedMagneticField :::
This intermediate interface is again implemented by
{class}Acts::InterpolatedBFieldMap
, which is a template class that depends on
an instance of {class}Acts::detail::Grid
. Varying configurations are possible,
like a 2D field map that exploits
:::{doxygenclass} Acts::InterpolatedBFieldMap :members: false :::
The class constructor accepts a single configuration struct that contains the grid instance, a scale factor and optional conversion function for the lookup positions and the returned field values.
:::{doxygenstruct} Acts::InterpolatedBFieldMap::Config :::
Internally, {class}Acts::InterpolatedBFieldMap
uses a field interpolation
cell to speed up lookups. This cell contains the interpolation points so the
grid does not have to be consulted for each lookup. Explicit methods to create
such a field cell are provided, but field cell creation is automatically
handled by {func}Acts::InterpolatedBFieldMap::makeCache
, opaque to the
client.
Helpers to construct an interpolated field map from text and ROOT file inputs are provided:
:::{doxygenfunction} Acts::fieldMapRZ :::
:::{doxygenfunction} Acts::fieldMapXYZ :::
:::{warning}
The analytical solenoid field is slow. See {func}Acts::solenoidFieldMap
to speed it up.
:::
ACTS also provides a field provider that calculates the field vectors analytically for a solenoid field.
:::{figure} ../figures/bfield/quiver.png :width: 600 :align: center Picture of a solenoid field in rz, with arrows indicating the direction of the field, and their size denoting the strength. The field is almost homogeneous in the center. :::
The implementation has configurable solenoid parameters:
:::{doxygenstruct} Acts::SolenoidBField::Config :::
:::{note} A configuration of
SolenoidBField::Config cfg;
cfg.length = 5.8_m;
cfg.radius = (2.56 + 2.46) * 0.5 * 0.5_m;
cfg.nCoils = 1154;
cfg.bMagCenter = 2_T;
SolenoidBField bField(cfg);
roughly corresponds to the solenoid wrapping the Inner Detector in ATLAS. :::
The calculation uses two special functions:
-
$E_1(k^2)$ is the complete elliptic integral of the 1st kind -
$E_2(k^2)$ is the complete elliptic integral of the 2nd kind
Using these, you can evaluate the two components
In the implementation the factor of Acts::SolenoidBField::Config::bMagCenter
.
As the evaluation of Acts::InterpolatedBFieldMap
easily outperforms
{class}Acts::SolenoidBField
. A helper is provided that builds a map from the
analytical implementation and is much faster to lookup:
:::{doxygenfunction} Acts::solenoidFieldMap :::
:::{doxygenclass} Acts::MagneticFieldProvider :::