Commit 02a3fde8 authored by Heinrich Schindler's avatar Heinrich Schindler
Browse files

Merge branch 'bfield' into 'master'

Magnetic fields in AvalancheMC and AvalancheMicroscopic

See merge request !249
parents a11bf017 89a2f0b1
Pipeline #3160187 passed with stage
in 6 minutes and 4 seconds
......@@ -225,9 +225,10 @@ etched into the foil.
\subsubsection{Field map}
As a first step, we need to calculate the electric field in the GEM.
Currently this calculation has to be performed using an external field
solver like Ansys \cite{ANSYS} or Elmer \cite{Elmer}. In this example,
we use Ansys, but the steps for importing and using an Elmer field map
are very similar.
solver like Ansys \cite{ANSYS}, Elmer \cite{Elmer}, or Comsol \cite{Comsol}.
In this example,
we use Ansys, but the steps for importing field maps from other
programs are very similar.
In the following we assume that the output files resulting from the
Ansys run are located in the current working directory.
......
......@@ -15,7 +15,7 @@
\vspace{2cm}
\large
Version 2021.4
Version 2021.5
\vspace{2cm}
\large
......@@ -23,7 +23,7 @@
\vfill
September 2021
October 2021
}
\end{titlepage}
......@@ -255,7 +255,7 @@ is integrated in a stochastic manner:
a step of length \(\Delta{s} = v_{\text{d}}\Delta{t}\)
in the direction of the
drift velocity \(\mathbf{v}_{\text{d}}\)
at the local field is calculated (with either the
at the local electric and magnetic field is calculated (with either the
time step \(\Delta{t}\) or the distance \(\Delta{s}\)
being specified by the user);
\item
......@@ -284,7 +284,7 @@ with a mean equal to a multiple of the ``collision time''
\begin{equation*}
\tau = \frac{m v_{d}}{q E}.
\end{equation*}
The third method is activated by default.
The third method is activated by default.
Instead of making simple straight-line steps (using the drift velocity
vector at the starting point of a step), the end point of a step
......@@ -330,8 +330,8 @@ bool DriftIon(const double x0, const double y0, const double z0, const double t0
\end{description}
The trajectory can be retrieved using
\begin{lstlisting}
unsigned int GetNumberOfDriftLinePoints() const;
void GetDriftLinePoint(const int i, double& x, double& y, double& z, double& t);
size_t GetNumberOfDriftLinePoints() const;
void GetDriftLinePoint(const size_t i, double& x, double& y, double& z, double& t);
\end{lstlisting}
The calculation of an avalanche initiated by an electron,
......@@ -353,8 +353,8 @@ avalanche should be simulated.
The starting and endpoints of electrons in the avalanche can be
retrieved using
\begin{lstlisting}
unsigned int GetNumberOfElectronEndpoints() const;
void GetElectronEndpoint(const unsigned int i,
size_t GetNumberOfElectronEndpoints() const;
void GetElectronEndpoint(const size_t i,
double& x0, double& y0, double& z0, double& t0,
double& x1, double& y1, double& z1, double& t1, int& status) const;
\end{lstlisting}
......@@ -366,14 +366,6 @@ void GetElectronEndpoint(const unsigned int i,
\end{description}
Analogous functions are available for holes and ions.
The functions
\begin{lstlisting}
void EnableMagneticField();
\end{lstlisting}
instructs the class to consider not only the electric but also the magnetic field
in the evaluation of the transport parameters.
By default, magnetic fields are not taken into account.
For debugging purposes, attachment and diffusion can be switched off using
\begin{lstlisting}
void DisableAttachment();
......@@ -471,8 +463,8 @@ void GetAvalancheSize(int& ne, int& ni);
Information about the ``history'' of each avalanche electron can be
retrieved by
\begin{lstlisting}
unsigned int GetNumberOfElectronEndpoints() const;
void GetElectronEndpoint(const unsigned int i,
size_t GetNumberOfElectronEndpoints() const;
void GetElectronEndpoint(const size_t i,
double& x0, double& y0, double& z0, double& t0, double& e0,
double& x1, double& y1, double& z1, double& t1, double& e1,
int& status);
......@@ -524,12 +516,14 @@ aval.EnableElectronEnergyHistogramming(&hEnergy);
After each collision,
the histogram is filled with the current electron energy.
The effect of magnetic fields can be included
in the stepping algorithm using the function
If the sensor has a non-zero magnetic field, \texttt{AvalancheMicroscopic}
will by default use a more complicated stepping algorithm which takes
the effect of the $B$ field on the electron trajectory into account.
In order to explicitly switch using magnetic fields on or off
one can use the function
\begin{lstlisting}
void EnableMagneticField();
void EnableMagneticField(const bool on);
\end{lstlisting}
By default, magnetic fields are not taken into account in the calculation.
Using
\begin{lstlisting}
......
......@@ -53,6 +53,13 @@
key = "Gmsh",
}
% Comsol
@misc{Comsol,
title = "{COMSOL Multiphysics}",
note = "\url{https://www.comsol.com}",
key = "Comsol",
}
% TCAD
@misc{Synopsys,
title = "{Synopsys Sentaurus Device}",
......
......@@ -75,9 +75,6 @@ class AvalancheMC {
/// Retrieve the drift velocity from the component.
void EnableVelocityMap(const bool on = true) { m_useVelocityMap = on; }
/// Enable use of magnetic field in stepping algorithm.
void EnableMagneticField(const bool on = true) { m_useBfield = on; }
/** Set a maximum avalanche size (ignore further multiplication
once this size has been reached). */
void EnableAvalancheSizeLimit(const unsigned int size) { m_sizeCut = size; }
......@@ -115,25 +112,21 @@ class AvalancheMC {
}
/// Return the number of points along the last simulated drift line.
unsigned int GetNumberOfDriftLinePoints() const { return m_drift.size(); }
size_t GetNumberOfDriftLinePoints() const { return m_drift.size(); }
/// Return the coordinates and time of a point along the last drift line.
void GetDriftLinePoint(const unsigned int i, double& x, double& y, double& z,
void GetDriftLinePoint(const size_t i, double& x, double& y, double& z,
double& t) const;
/** Return the number of electron trajectories in the last
* simulated avalanche (including captured electrons). */
unsigned int GetNumberOfElectronEndpoints() const {
size_t GetNumberOfElectronEndpoints() const {
return m_endpointsElectrons.size();
}
/** Return the number of hole trajectories in the last
* simulated avalanche (including captured holes). */
unsigned int GetNumberOfHoleEndpoints() const {
return m_endpointsHoles.size();
}
size_t GetNumberOfHoleEndpoints() const { return m_endpointsHoles.size(); }
/// Return the number of ion trajectories.
unsigned int GetNumberOfIonEndpoints() const {
return m_endpointsIons.size();
}
size_t GetNumberOfIonEndpoints() const { return m_endpointsIons.size(); }
/** Return the coordinates and time of start and end point of a given
* electron drift line.
......@@ -142,13 +135,13 @@ class AvalancheMC {
* \param x1,y1,z1,t1 coordinates and time of the end point
* \param status status code (see GarfieldConstants.hh)
*/
void GetElectronEndpoint(const unsigned int i, double& x0, double& y0,
void GetElectronEndpoint(const size_t i, double& x0, double& y0,
double& z0, double& t0, double& x1, double& y1,
double& z1, double& t1, int& status) const;
void GetHoleEndpoint(const unsigned int i, double& x0, double& y0, double& z0,
void GetHoleEndpoint(const size_t i, double& x0, double& y0, double& z0,
double& t0, double& x1, double& y1, double& z1,
double& t1, int& status) const;
void GetIonEndpoint(const unsigned int i, double& x0, double& y0, double& z0,
void GetIonEndpoint(const size_t i, double& x0, double& y0, double& z0,
double& t0, double& x1, double& y1, double& z1,
double& t1, int& status) const;
......@@ -252,7 +245,6 @@ class AvalancheMC {
bool m_doRKF = false;
bool m_useDiffusion = true;
bool m_useAttachment = true;
bool m_useBfield = false;
/// Scaling factor for electron signals.
double m_scaleE = 1.;
/// Scaling factor for hole signals.
......
......@@ -114,8 +114,11 @@ class AvalancheMicroscopic {
/// Retrieve the currently set size limit.
int GetAvalancheSizeLimit() const { return m_sizeCut; }
/// Enable magnetic field in stepping algorithm (default: off).
void EnableMagneticField(const bool on = true) { m_useBfield = on; }
/// Switch on/off using the magnetic field in the stepping algorithm.
void EnableMagneticField(const bool on = true) {
m_useBfieldAuto = false;
m_useBfield = on;
}
/// Set number of collisions to be skipped for plotting
void SetCollisionSteps(const unsigned int n) { m_nCollSkip = n; }
......@@ -138,7 +141,7 @@ class AvalancheMicroscopic {
/** Return the number of electron trajectories in the last
* simulated avalanche (including captured electrons). */
unsigned int GetNumberOfElectronEndpoints() const {
size_t GetNumberOfElectronEndpoints() const {
return m_endpointsElectrons.size();
}
/** Return the coordinates and time of start and end point of a given
......@@ -149,35 +152,32 @@ class AvalancheMicroscopic {
* \param e0,e1 initial and final energy
* \param status status code (see GarfieldConstants.hh)
*/
void GetElectronEndpoint(const unsigned int i, double& x0, double& y0,
void GetElectronEndpoint(const size_t i, double& x0, double& y0,
double& z0, double& t0, double& e0, double& x1,
double& y1, double& z1, double& t1, double& e1,
int& status) const;
void GetElectronEndpoint(const unsigned int i, double& x0, double& y0,
void GetElectronEndpoint(const size_t i, double& x0, double& y0,
double& z0, double& t0, double& e0, double& x1,
double& y1, double& z1, double& t1, double& e1,
double& dx1, double& dy1, double& dz1,
int& status) const;
unsigned int GetNumberOfElectronDriftLinePoints(
const unsigned int i = 0) const;
unsigned int GetNumberOfHoleDriftLinePoints(const unsigned int i = 0) const;
size_t GetNumberOfElectronDriftLinePoints(const size_t i = 0) const;
size_t GetNumberOfHoleDriftLinePoints(const size_t i = 0) const;
void GetElectronDriftLinePoint(double& x, double& y, double& z, double& t,
const int ip,
const unsigned int iel = 0) const;
void GetHoleDriftLinePoint(double& x, double& y, double& z, double& t,
const int ip, const unsigned int iel = 0) const;
unsigned int GetNumberOfHoleEndpoints() const {
return m_endpointsHoles.size();
}
void GetHoleEndpoint(const unsigned int i, double& x0, double& y0, double& z0,
size_t GetNumberOfHoleEndpoints() const { return m_endpointsHoles.size(); }
void GetHoleEndpoint(const size_t i, double& x0, double& y0, double& z0,
double& t0, double& e0, double& x1, double& y1,
double& z1, double& t1, double& e1, int& status) const;
unsigned int GetNumberOfPhotons() const { return m_photons.size(); }
size_t GetNumberOfPhotons() const { return m_photons.size(); }
// Status codes:
// -2: photon absorbed by gas molecule
void GetPhoton(const unsigned int i, double& e, double& x0, double& y0,
void GetPhoton(const size_t i, double& e, double& x0, double& y0,
double& z0, double& t0, double& x1, double& y1, double& z1,
double& t1, int& status) const;
......@@ -294,6 +294,7 @@ class AvalancheMicroscopic {
bool m_usePhotons = false;
bool m_useBandStructure = true;
bool m_useNullCollisionSteps = false;
bool m_useBfieldAuto = true;
bool m_useBfield = false;
// Transport cuts
......
......@@ -287,6 +287,9 @@ class Component {
/// Switch off debugging messages.
void DisableDebugging() { m_debug = false; }
/// Does the component have a non-zero magnetic field?
virtual bool HasMagneticField() const;
/// Does the component have maps of the Townsend coefficient?
virtual bool HasTownsendMap() const { return false; }
/// Does the component have attachment maps?
......
......@@ -46,6 +46,8 @@ class ComponentGrid : public Component {
bool GetElementaryCell(double& xmin, double& ymin, double& zmin,
double& xmax, double& ymax, double& zmax) override;
bool HasMagneticField() const override;
/** Define the grid.
* \param nx,ny,nz number of nodes along \f$x, y, z\f$.
* \param xmin,xmax range along \f$x\f$.
......
......@@ -39,6 +39,8 @@ class ComponentUser : public Component {
bool GetBoundingBox(double& xmin, double& ymin, double& zmin,
double& xmax, double& ymax, double& zmax) override;
bool HasMagneticField() const override;
/// Set the function to be called for calculating the electric field.
void SetElectricField(
std::function<void(const double, const double, const double,
......
......@@ -34,6 +34,8 @@ class ComponentVoxel : public Component {
void MagneticField(const double x, const double y, const double z, double& bx,
double& by, double& bz, int& status) override;
bool HasMagneticField() const override;
/// Interpolate between field values at the element centres.
void EnableInterpolation(const bool on = true) { m_interpolate = on; }
......
......@@ -31,6 +31,8 @@ class Sensor {
void EnableComponent(const unsigned int i, const bool on);
/// Activate/deactivate use of the magnetic field of a given component.
void EnableMagneticField(const unsigned int i, const bool on);
/// Does the sensor have a non-zero magnetic field?
bool HasMagneticField() const;
/// Add an electrode.
void AddElectrode(Component* comp, const std::string& label);
......
......@@ -130,7 +130,7 @@ void AvalancheMC::SetTimeWindow(const double t0, const double t1) {
m_hasTimeWindow = true;
}
void AvalancheMC::GetDriftLinePoint(const unsigned int i, double& x, double& y,
void AvalancheMC::GetDriftLinePoint(const size_t i, double& x, double& y,
double& z, double& t) const {
if (i >= m_drift.size()) {
std::cerr << m_className << "::GetDriftLinePoint: Index out of range.\n";
......@@ -143,7 +143,7 @@ void AvalancheMC::GetDriftLinePoint(const unsigned int i, double& x, double& y,
t = m_drift[i].t;
}
void AvalancheMC::GetHoleEndpoint(const unsigned int i, double& x0, double& y0,
void AvalancheMC::GetHoleEndpoint(const size_t i, double& x0, double& y0,
double& z0, double& t0, double& x1,
double& y1, double& z1, double& t1,
int& status) const {
......@@ -163,7 +163,7 @@ void AvalancheMC::GetHoleEndpoint(const unsigned int i, double& x0, double& y0,
status = m_endpointsHoles[i].status;
}
void AvalancheMC::GetIonEndpoint(const unsigned int i, double& x0, double& y0,
void AvalancheMC::GetIonEndpoint(const size_t i, double& x0, double& y0,
double& z0, double& t0, double& x1, double& y1,
double& z1, double& t1, int& status) const {
if (i >= m_endpointsIons.size()) {
......@@ -182,7 +182,7 @@ void AvalancheMC::GetIonEndpoint(const unsigned int i, double& x0, double& y0,
status = m_endpointsIons[i].status;
}
void AvalancheMC::GetElectronEndpoint(const unsigned int i, double& x0,
void AvalancheMC::GetElectronEndpoint(const size_t i, double& x0,
double& y0, double& z0, double& t0,
double& x1, double& y1, double& z1,
double& t1, int& status) const {
......@@ -675,18 +675,16 @@ int AvalancheMC::GetField(const std::array<double, 3>& x,
Medium*& medium) const {
e.fill(0.);
b.fill(0.);
// Get the electric field.
int status = 0;
// Get the magnetic field.
m_sensor->MagneticField(x[0], x[1], x[2], b[0], b[1], b[2], status);
// Get the electric field.
m_sensor->ElectricField(x[0], x[1], x[2], e[0], e[1], e[2], medium, status);
// Make sure the point is inside a drift medium.
if (status != 0 || !medium) return StatusLeftDriftMedium;
// Make sure the point is inside the drift area.
if (!m_sensor->IsInArea(x[0], x[1], x[2])) return StatusLeftDriftArea;
// Get the magnetic field, if requested.
if (m_useBfield) {
m_sensor->MagneticField(x[0], x[1], x[2], b[0], b[1], b[2], status);
}
return 0;
}
......
......@@ -227,12 +227,10 @@ void AvalancheMicroscopic::SetTimeWindow(const double t0, const double t1) {
m_hasTimeWindow = true;
}
void AvalancheMicroscopic::GetElectronEndpoint(const unsigned int i, double& x0,
double& y0, double& z0,
double& t0, double& e0,
double& x1, double& y1,
double& z1, double& t1,
double& e1, int& status) const {
void AvalancheMicroscopic::GetElectronEndpoint(const size_t i,
double& x0, double& y0, double& z0, double& t0, double& e0,
double& x1, double& y1, double& z1, double& t1, double& e1,
int& status) const {
if (i >= m_endpointsElectrons.size()) {
std::cerr << m_className << "::GetElectronEndpoint: Index out of range.\n";
x0 = y0 = z0 = t0 = e0 = 0.;
......@@ -254,9 +252,9 @@ void AvalancheMicroscopic::GetElectronEndpoint(const unsigned int i, double& x0,
status = m_endpointsElectrons[i].status;
}
void AvalancheMicroscopic::GetElectronEndpoint(
const unsigned int i, double& x0, double& y0, double& z0, double& t0,
double& e0, double& x1, double& y1, double& z1, double& t1, double& e1,
void AvalancheMicroscopic::GetElectronEndpoint(const size_t i,
double& x0, double& y0, double& z0, double& t0, double& e0,
double& x1, double& y1, double& z1, double& t1, double& e1,
double& dx1, double& dy1, double& dz1, int& status) const {
if (i >= m_endpointsElectrons.size()) {
std::cerr << m_className << "::GetElectronEndpoint: Index out of range.\n";
......@@ -283,11 +281,10 @@ void AvalancheMicroscopic::GetElectronEndpoint(
status = m_endpointsElectrons[i].status;
}
void AvalancheMicroscopic::GetHoleEndpoint(const unsigned int i, double& x0,
double& y0, double& z0, double& t0,
double& e0, double& x1, double& y1,
double& z1, double& t1, double& e1,
int& status) const {
void AvalancheMicroscopic::GetHoleEndpoint(const size_t i,
double& x0, double& y0, double& z0, double& t0, double& e0,
double& x1, double& y1, double& z1, double& t1, double& e1,
int& status) const {
if (i >= m_endpointsHoles.size()) {
std::cerr << m_className << "::GetHoleEndpoint: Index out of range.\n";
x0 = y0 = z0 = t0 = e0 = 0.;
......@@ -309,11 +306,11 @@ void AvalancheMicroscopic::GetHoleEndpoint(const unsigned int i, double& x0,
status = m_endpointsHoles[i].status;
}
unsigned int AvalancheMicroscopic::GetNumberOfElectronDriftLinePoints(
const unsigned int i) const {
size_t AvalancheMicroscopic::GetNumberOfElectronDriftLinePoints(
const size_t i) const {
if (i >= m_endpointsElectrons.size()) {
std::cerr << m_className << "::GetNumberOfElectronDriftLinePoints:\n";
std::cerr << " Endpoint index (" << i << ") out of range.\n";
std::cerr << m_className << "::GetNumberOfElectronDriftLinePoints: "
<< "Index out of range.\n";
return 0;
}
......@@ -322,11 +319,11 @@ unsigned int AvalancheMicroscopic::GetNumberOfElectronDriftLinePoints(
return m_endpointsElectrons[i].driftLine.size() + 2;
}
unsigned int AvalancheMicroscopic::GetNumberOfHoleDriftLinePoints(
const unsigned int i) const {
size_t AvalancheMicroscopic::GetNumberOfHoleDriftLinePoints(
const size_t i) const {
if (i >= m_endpointsHoles.size()) {
std::cerr << m_className << "::GetNumberOfHoleDriftLinePoints:\n";
std::cerr << " Endpoint index (" << i << ") out of range.\n";
std::cerr << m_className << "::GetNumberOfHoleDriftLinePoints: "
<< "Index out of range.\n";
return 0;
}
......@@ -400,11 +397,9 @@ void AvalancheMicroscopic::GetHoleDriftLinePoint(double& x, double& y,
t = m_endpointsHoles[ih].driftLine[ip - 1].t;
}
void AvalancheMicroscopic::GetPhoton(const unsigned int i, double& e,
double& x0, double& y0, double& z0,
double& t0, double& x1, double& y1,
double& z1, double& t1,
int& status) const {
void AvalancheMicroscopic::GetPhoton(const size_t i, double& e,
double& x0, double& y0, double& z0, double& t0,
double& x1, double& y1, double& z1, double& t1, int& status) const {
if (i >= m_photons.size()) {
std::cerr << m_className << "::GetPhoton: Index out of range.\n";
return;
......@@ -508,6 +503,10 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
return false;
}
// Do we need to consider the magnetic field?
const bool useBfield = m_useBfieldAuto ? m_sensor->HasMagneticField() :
m_useBfield;
// Loop over the initial set of electrons/holes.
for (auto& p : stack) {
// Make sure that the starting point is inside a medium.
......@@ -641,7 +640,7 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
// Ratio of transverse electric field component and magnetic field.
double ezovb = 0.;
std::array<std::array<double, 3>, 3> rot;
if (m_useBfield) {
if (useBfield) {
m_sensor->MagneticField(x, y, z, bx, by, bz, status);
const double scale = hole ? Tesla2Internal : -Tesla2Internal;
bx *= scale;
......@@ -720,7 +719,7 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
double a1 = 0., a2 = 0.;
// Initial velocity.
double vx = 0., vy = 0., vz = 0.;
if (m_useBfield) {
if (useBfield) {
// Calculate the velocity vector in the local frame.
const double vmag = c1 * sqrt(en);
ToLocal(rot, vmag * kx, vmag * ky, vmag * kz, vx, vy, vz);
......@@ -761,7 +760,7 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
const double r = RndmUniformPos();
dt += -log(r) * fInv;
// Calculate the energy after the proposed step.
if (m_useBfield) {
if (useBfield) {
en1 = en + (a1 + a2 * dt) * dt;
if (omega > Small) {
cphi = cos(omega * dt);
......@@ -814,7 +813,7 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
// and the proposed new position.
double kx1 = 0., ky1 = 0., kz1 = 0.;
double dx = 0., dy = 0., dz = 0.;
if (m_useBfield) {
if (useBfield) {
// Calculate the new velocity.
double vx1 = vx + 2. * c2 * ex * dt;
double vy1 = vy * cphi + vz * sphi + ezovb;
......@@ -954,7 +953,7 @@ bool AvalancheMicroscopic::TransportElectrons(std::vector<Electron>& stack,
t = t1;
// If switched on, get the magnetic field at the new location.
if (m_useBfield) {
if (useBfield) {
m_sensor->MagneticField(x, y, z, bx, by, bz, status);
const double scale = hole ? Tesla2Internal : -Tesla2Internal;
bx *= scale;
......
......@@ -3,6 +3,7 @@
#include "Garfield/Component.hh"
#include "Garfield/FundamentalConstants.hh"
#include "Garfield/GarfieldConstants.hh"
#include "Garfield/Numerics.hh"
namespace Garfield {
......@@ -132,6 +133,11 @@ bool Component::CrossedPlane(
return false;
}
bool Component::HasMagneticField() const {
return fabs(m_b0[0]) > Small || fabs(m_b0[1]) > Small ||
fabs(m_b0[2]) > Small;
}
double Component::IntegrateFluxCircle(const double xc, const double yc,
const double r, const unsigned int nI) {
// FLDIN2, FCHK3
......
......@@ -175,6 +175,10 @@ void ComponentGrid::MagneticField(const double x, const double y,
}
}
bool ComponentGrid::HasMagneticField() const {
return m_bfields.empty() ? Component::HasMagneticField() : true;
}
Medium* ComponentGrid::GetMedium(const double x, const double y,
const double z) {
// Make sure the field map has been loaded.
......
......@@ -70,8 +70,7 @@ void ComponentUser::MagneticField(const double x, const double y,
const double z, double& bx, double& by,
double& bz, int& status) {
if (!m_bfield) {
bx = by = bz = 0.;
status = -10;
Component::MagneticField(x, y, z, bx, by, bz, status);
return;
}
m_bfield(x, y, z, bx, by, bz);
......@@ -118,6 +117,10 @@ bool ComponentUser::GetBoundingBox(
return true;
}
bool ComponentUser::HasMagneticField() const {
return m_bfield ? true : Component::HasMagneticField();
}
void ComponentUser::SetElectricField(
std::function<void(const double, const double, const double,
double&, double&, double&)> f) {
......
......@@ -144,6 +144,10 @@ void ComponentVoxel::MagneticField(const double x, const double y,
}
}
bool ComponentVoxel::HasMagneticField() const {
return m_hasBfield ? true : Component::HasMagneticField();
}
Medium* ComponentVoxel::GetMedium(const double x, const double y,
const double z) {
// Make sure the field map has been loaded.
......
......@@ -355,6 +355,15 @@ void Sensor::EnableMagneticField(const unsigned int i, const bool on) {
std::get<2>(m_components[i]) = on;
}
bool Sensor::HasMagneticField() const {
for (const auto& cmp : m_components) {
if (!std::get<1>(cmp) || !std::get<2>(cmp)) continue;