ATLAS Offline Software
Loading...
Searching...
No Matches
ElectronCombinedMaterialEffects.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
10
12//
18//
19#include <cmath>
20#include <exception>
21#include <fstream>
22#include <sstream>
23
24namespace {
25/* brief Multiple Scattering description*/
26void
27scattering(GsfMaterial::Scattering& cache,
28 const Trk::ComponentParameters& componentParameters,
29 const Trk::MaterialProperties& materialProperties,
30 double pathLength)
31{
32 // Reset the cache
33 cache.reset();
34
35 // Request track parameters from component parameters
36 const Trk::TrackParameters* trackParameters = componentParameters.params.get();
37 const AmgSymMatrix(5)* measuredTrackCov = trackParameters->covariance();
38
39 if (!measuredTrackCov) {
40 return;
41 }
42
43 const Amg::Vector3D& globalMomentum = trackParameters->momentum();
44 const double p = globalMomentum.mag();
45 double pathcorrection = 1.;
46 if (materialProperties.thickness() != 0) {
47 pathcorrection = pathLength / materialProperties.thickness();
48 }
49 const double t = pathcorrection * materialProperties.thicknessInX0();
50 constexpr double m = Trk::ParticleMasses::mass[Trk::electron];
51 const double E = std::sqrt(p * p + m * m);
52 const double beta = p / E;
53 const double sigma = Trk::MaterialInteraction::sigmaMS(t, p, beta);
54 const double angularVariation = sigma * sigma;
55
56 const double sinTheta = std::sin(trackParameters->parameters()[Trk::theta]);
57 cache.deltaThetaCov = angularVariation;
58 cache.deltaPhiCov = angularVariation / (sinTheta * sinTheta);
59}
60
64constexpr double s_singleGaussianRange = 0.0001;
65constexpr double s_lowerRange = 0.002;
66constexpr double s_xOverRange = 0.10;
67constexpr double s_upperRange = 0.20;
68constexpr double s_componentMeanCut = 0.0;
69//
71using polyArray = std::array<BH::Polynomial, GSFConstants::maxNumberofMatComponents>;
75template<size_t N>
76inline constexpr double
77hornerEvaluate(const std::array<double, N>& a, const double& x)
78{
79 constexpr size_t order = N - 1;
80 if constexpr (order == 0) {
81 return a[0];
82 } else if constexpr (order == 1) {
83 return a[0] * x + a[1];
84 } else if constexpr (order == 2) {
85 return (a[0] * x + a[1]) * x + a[2];
86 } else if constexpr (order == 3) {
87 return ((a[0] * x + a[1]) * x + a[2]) * x + a[3];
88 } else if constexpr (order == 4) {
89 return (((a[0] * x + a[1]) * x + a[2]) * x + a[3]) * x + a[4];
90 } else if constexpr (order == 5) {
91 return ((((a[0] * x + a[1]) * x + a[2]) * x + a[3]) * x + a[4]) * x + a[5];
92 } else { // start from the last one we have
93 double result =
94 ((((a[0] * x + a[1]) * x + a[2]) * x + a[3]) * x + a[4]) * x + a[5];
95 for (size_t i = 6; i < N; ++i) {
96 result = result * x + a[i];
97 }
98 return result;
99 }
100}
101// Logistic function - needed for transformation of weight and mean
102inline double
103logisticFunction(const double x)
104{
105 return 1. / (1. + std::exp(-x));
106}
107
108// Correct weights of components
109void
110correctWeights(BH::MixtureParameters& mixture, const int numberOfComponents)
111{
112 if (numberOfComponents < 1) {
113 return;
114 }
115 // Obtain the sum of weights
116 double weightSum(0.);
117 for (int i = 0; i < numberOfComponents; ++i) {
118 weightSum += mixture[i].weight;
119 }
120 const double norm = 1. / weightSum;
121 // Rescale so that total weighting is 1
122 for (int i = 0; i < numberOfComponents; ++i) {
123 mixture[i].weight *= norm;
124 }
125}
126
127BH::MixtureParameters
128getTransformedMixtureParameters(
129 const std::array<BH::Polynomial, GSFConstants::maxNumberofMatComponents>& polynomialWeights,
130 const std::array<BH::Polynomial, GSFConstants::maxNumberofMatComponents>& polynomialMeans,
131 const std::array<BH::Polynomial, GSFConstants::maxNumberofMatComponents>& polynomialVariances,
132 const double pathlengthInX0,
133 const int numberOfComponents)
134{
135 BH::MixtureParameters mixture{};
136 //loop over actual components
137 for (int i = 0; i < numberOfComponents; ++i) {
138 const double updatedWeight = hornerEvaluate(polynomialWeights[i], pathlengthInX0);
139 const double updatedMean = hornerEvaluate(polynomialMeans[i], pathlengthInX0);
140 const double updatedVariance = hornerEvaluate(polynomialVariances[i], pathlengthInX0);
141 mixture[i] = { logisticFunction(updatedWeight),
142 logisticFunction(updatedMean),
143 std::exp(updatedVariance) };
144 }
145 return mixture;
146}
147
152// for sanity checks of read in parameters
153inline constexpr bool inRange(int var, int lo, int hi) {
154 return ((var <= hi) and (var >= lo));
155}
156// Helper to read in polynomials
157BH::Polynomial readPolynomial(std::ifstream& fin) {
158 BH::Polynomial poly{};
159 for (size_t i = 0; i < GSFConstants::polynomialCoefficients; ++i) {
160 if (!fin) {
161 throw std::logic_error("Reached end of stream but still expecting data.");
162 }
163 fin >> poly[i];
164 }
165 return poly;
166}
167
168// Helper struct to read in info from file
169struct readPolys {
170 polyArray weights{};
171 polyArray means{};
172 polyArray variances{};
173 int numberOfComponents{};
174};
175
176// Helper to read in info from file
177readPolys fillFromFile(const std::string& fileName) {
178
179 readPolys result;
180 // open file
181 const char* filename = fileName.c_str();
182 std::ifstream fin(filename);
183 if (fin.bad()) {
184 std::ostringstream ss;
185 ss << "Error opening file: " << fileName;
186 throw std::logic_error(ss.str());
187 }
188
189 // read in the 1st line
190 fin >> result.numberOfComponents;
191 int orderPolynomial = 0;
192 fin >> orderPolynomial;
193 if (not inRange(result.numberOfComponents, 0,
195 std::ostringstream ss;
196 ss << "numberOfComponents Parameter out of range 0- "
198 << result.numberOfComponents;
199 throw std::logic_error(ss.str());
200 }
201 if (orderPolynomial != (GSFConstants::polynomialCoefficients - 1)) {
202 std::ostringstream ss;
203 ss << "orderPolynomial order != "
205 throw std::logic_error(ss.str());
206 }
207
208 // read in the polynomials
209 int componentIndex = 0;
210 for (; componentIndex < result.numberOfComponents; ++componentIndex) {
211 result.weights[componentIndex] = readPolynomial(fin);
212 result.means[componentIndex] = readPolynomial(fin);
213 result.variances[componentIndex] = readPolynomial(fin);
214 }
215 return result;
216}
217
218} // end of anonymous namespace
219
220// ElectronCombinedMaterialEffects methods
222 const std::string& parameterisationFileName,
223 const std::string& parameterisationFileNameHighX0) {
224 // Read the std polynomials
225 {
226 const std::string resolvedFileName =
227 PathResolver::find_file(parameterisationFileName, "DATAPATH");
228 if (resolvedFileName.empty()) {
229 std::ostringstream ss;
230 ss << "Parameterisation file : " << parameterisationFileName
231 << " not found";
232 throw std::logic_error(ss.str());
233 }
234
235 readPolys readin = fillFromFile(resolvedFileName);
236 m_BHnumberOfComponents = readin.numberOfComponents;
237 m_BHpolynomialWeights = readin.weights;
238 m_BHpolynomialMeans = readin.means;
239 m_BHpolynomialVariances = readin.variances;
240 }
241 // Read the high X0 polynomials
242 {
243 const std::string resolvedFileName =
244 PathResolver::find_file(parameterisationFileNameHighX0, "DATAPATH");
245 if (resolvedFileName.empty()) {
246 std::ostringstream ss;
247 ss << "Parameterisation file : " << parameterisationFileNameHighX0
248 << " not found";
249 throw std::logic_error(ss.str());
250 }
251 readPolys readin = fillFromFile(resolvedFileName);
252 m_BHnumberOfComponentsHighX0 = readin.numberOfComponents;
253 m_BHpolynomialWeightsHighX0 = readin.weights;
254 m_BHpolynomialMeansHighX0 = readin.means;
255 m_BHpolynomialVariancesHighX0 = readin.variances;
256 }
258 std::ostringstream ss;
259 ss << " numberOfComponentsHighX0 != numberOfComponents";
260 throw std::logic_error(ss.str());
261 }
262}
263
264void
267 const Trk::ComponentParameters& componentParameters,
268 const Trk::MaterialProperties& materialProperties,
269 double pathLength,
270 Trk::PropDirection direction) const
271{
272 const AmgSymMatrix(5)* measuredCov = componentParameters.params->covariance();
273 /*
274 * 1. Retrieve multiple scattering corrections
275 */
276 GsfMaterial::Scattering cache_multipleScatter;
277 scattering(cache_multipleScatter, componentParameters, materialProperties, pathLength);
278 /*
279 * 2. Retrieve energy loss corrections
280 * This for electrons comes from the Bethe-Heitler
281 */
282 GsfMaterial::EnergyLoss cache_energyLoss;
283 this->BetheHeitler(cache_energyLoss,
284 componentParameters,
285 materialProperties,
286 pathLength,
287 direction);
288 // Protect if there are no new energy loss
289 // components.
290 if (cache_energyLoss.numElements == 0) {
291 cache_energyLoss.elements[0] = { 1, 0, 0 };
292 cache_energyLoss.numElements = 1;
293 }
294 /*
295 * 3. Combine the multiple scattering with each of the energy loss components
296 */
297 cache.numEntries = 0; //cahce to be filled
298 for (int i = 0; i < cache_energyLoss.numElements; ++i) {
299 cache.weights[i] = cache_energyLoss.elements[i].weight;
300 cache.deltaPs[i] = cache_energyLoss.elements[i].deltaP;
301 if (measuredCov) {
302 // Create the covariance
303 const double covPhi = cache_multipleScatter.deltaPhiCov;
304 const double covTheta = cache_multipleScatter.deltaThetaCov;
305 const double covQoverP = cache_energyLoss.elements[i].deltaQOvePCov;
306 //Here set the "delta" for the covariance
307 //due to material effects
308 cache.covariances[i] << 0, 0, 0, 0, 0, // 5
309 0, 0, 0, 0, 0, // 10
310 0, 0, covPhi, 0, 0, // 15
311 0, 0, 0, covTheta, 0, // 20
312 0, 0, 0, 0, covQoverP;
313 } else {
314 cache.covariances[i].setZero();
315 }
316 ++cache.numEntries;
317 } // end for loop over energy loss components
318}
319
320/*
321 * Use polynomials to get the BetheHeitler
322 * energy loss
323 */
324void
327 const Trk::ComponentParameters& componentParameters,
328 const Trk::MaterialProperties& materialProperties,
329 double pathLength,
330 Trk::PropDirection direction) const
331{
332 cache.numElements = 0;
333
334 const Trk::TrackParameters* trackParameters = componentParameters.params.get();
335 const Amg::Vector3D& globalMomentum = trackParameters->momentum();
336
337 const double radiationLength = materialProperties.x0();
338 const double momentum = globalMomentum.mag();
339 double pathlengthInX0 = pathLength / radiationLength;
340
341 if (pathlengthInX0 < s_singleGaussianRange) {
342 cache.elements[0] = { 1., 0., 0. };
343 cache.numElements = 1;
344 return;
345 }
346
347 // If the amount of material is between 0.0001 and 0.01 in x0 return the gaussian
348 // approximation to the Bethe-Heitler distribution
349 if (pathlengthInX0 < s_lowerRange) {
350 const double meanZ = std::exp(-1. * pathlengthInX0);
351 const double sign = (direction == Trk::oppositeMomentum) ? 1. : -1.;
352 const double varZ =
353 std::exp(-1. * pathlengthInX0 * std::log(3.) / std::log(2.)) -
354 std::exp(-2. * pathlengthInX0);
355 double deltaP(0.);
356 double varQoverP(0.);
357 if (direction == Trk::alongMomentum) {
358 deltaP = sign * momentum * (1. - meanZ);
359 varQoverP = 1. / (meanZ * meanZ * momentum * momentum) * varZ;
360 } else {
361 deltaP = sign * momentum * (1. / meanZ - 1.);
362 varQoverP = varZ / (momentum * momentum);
363 }
364 cache.elements[0] = { 1., deltaP, varQoverP };
365 cache.numElements = 1;
366 return;
367 }
368 //clip to upper range
369 if (pathlengthInX0 > s_upperRange) {
370 pathlengthInX0 = s_upperRange;
371 }
372
373 // Get proper mixture parameters
374 MixtureParameters mixture;
375 if (pathlengthInX0 > s_xOverRange) {
376 mixture = getTransformedMixtureParameters(m_BHpolynomialWeightsHighX0,
379 pathlengthInX0,
381 } else {
382 mixture = getTransformedMixtureParameters(m_BHpolynomialWeights,
385 pathlengthInX0,
387 }
388 // Correct the mixture
389 correctWeights(mixture, m_BHnumberOfComponents);
390 int componentIndex = 0;
391 double weightToBeRemoved(0.);
392 int componentWithHighestMean(0);
393
394 for (; componentIndex < m_BHnumberOfComponents; ++componentIndex) {
395 if (mixture[componentIndex].mean > mixture[componentWithHighestMean].mean) {
396 componentWithHighestMean = componentIndex;
397 }
398 if (mixture[componentIndex].mean >= s_componentMeanCut) {
399 continue;
400 }
401 weightToBeRemoved += mixture[componentIndex].weight;
402 }
403 // Fill the cache to be returned
404 componentIndex = 0;
405 for (; componentIndex < m_BHnumberOfComponents; ++componentIndex) {
406 double varianceInverseMomentum = 0;
407
408 // This is not mathematically correct but it does stabilize the GSF
409 if (mixture[componentIndex].mean < s_componentMeanCut) {
410 continue;
411 }
412
413 double weight = mixture[componentIndex].weight;
414 if (componentIndex == componentWithHighestMean) {
415 weight += weightToBeRemoved;
416 }
417
418 double deltaP(0.);
419 if (direction == alongMomentum) { // For forward propagation
420 deltaP = momentum * (mixture[componentIndex].mean - 1.);
421 const double f = 1. / (momentum * mixture[componentIndex].mean);
422 varianceInverseMomentum = f * f * mixture[componentIndex].variance;
423 } else { // For backwards propagation
424 deltaP = momentum * (1. / mixture[componentIndex].mean - 1.);
425 varianceInverseMomentum = mixture[componentIndex].variance / (momentum * momentum);
426 }
427
428 // set in the cache and increase the elements
429 cache.elements[cache.numElements] = { weight,
430 deltaP,
431 varianceInverseMomentum };
432 ++cache.numElements;
433 } // end for loop over all components
434}
435
#define AmgSymMatrix(dim)
static Double_t a
static Double_t ss
bool inRange(const double *boundaries, const double value, const double tolerance=0.02)
int sign(int a)
#define x
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialMeansHighX0
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialVariances
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialVariancesHighX0
void BetheHeitler(GsfMaterial::EnergyLoss &cache, const ComponentParameters &componentParameters, const MaterialProperties &materialProperties, double pathLenght, PropDirection direction=anyDirection) const
void compute(GsfMaterial::Combined &, const Trk::ComponentParameters &, const Trk::MaterialProperties &, double, Trk::PropDirection=anyDirection) const
ElectronCombinedMaterialEffects(const std::string &parameterisationFileName, const std::string &parameterisationFileNameHighX0)
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialWeightsHighX0
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialWeights
std::array< Polynomial, GSFConstants::maxNumberofMatComponents > m_BHpolynomialMeans
std::array< ComponentValues, GSFConstants::maxNumberofMatComponents > MixtureParameters
Material with information about thickness of material.
float thicknessInX0() const
Return the radiationlength fraction.
float x0() const
Return the radiation length.
float thickness() const
Return the thickness in mm.
const Amg::Vector3D & momentum() const
Access method for the momentum.
void mean(std::vector< double > &bins, std::vector< double > &values, const std::vector< std::string > &files, const std::string &histname, const std::string &tplotname, const std::string &label="")
Eigen::Matrix< double, 3, 1 > Vector3D
constexpr int8_t polynomialCoefficients
Number of coefficients for the polynomials, parametrizing the mean,variace, weights of the Gaussian c...
constexpr int8_t maxNumberofMatComponents
Maximum number of Gaussian components for the material effects description.
constexpr double mass[PARTICLEHYPOTHESES]
the array of masses
PropDirection
PropDirection, enum for direction of the propagation.
@ oppositeMomentum
@ alongMomentum
@ theta
Definition ParamDefs.h:66
ParametersBase< TrackParametersDim, Charged > TrackParameters
order
Configure Herwig7.
Helper struct for combined material effects, multicomponent description.
Definition GsfMaterial.h:49
std::array< double, GSFConstants::maxNumberofMatComponents > deltaPs
Definition GsfMaterial.h:53
std::array< double, GSFConstants::maxNumberofMatComponents > weights
Definition GsfMaterial.h:51
std::array< AmgSymMatrix(5), GSFConstants::maxNumberofMatComponents > covariances
Definition GsfMaterial.h:60
Helper struct for energy loss effects, multicomponent description.
Definition GsfMaterial.h:23
std::array< element, GSFConstants::maxNumberofMatComponents > elements
Definition GsfMaterial.h:30
Helper struct for multiple scattering effects single component description.
Definition GsfMaterial.h:37
std::unique_ptr< Trk::TrackParameters > params
static double sigmaMS(double dInX0, double p, double beta)
multiple scattering as function of dInX0