ATLAS Offline Software
Loading...
Searching...
No Matches
ElectronMaterialMixtureConvolution.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2020-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
12
18
21//
22#include "TrkGeometry/Layer.h"
25
26#include <array>
27namespace {
28
30inline void
31dummyCacheElement(GsfMaterial::Combined& elem)
32{
33 elem.numEntries = 1;
34 elem.deltaPs[0] = 0;
35 elem.parameters[0] = AmgVector(5)::Zero();
36 elem.covariances[0] = AmgSymMatrix(5)::Zero();
37}
38
39// Avoid out-of-line Eigen calls
41inline void
42updateCacheElement(GsfMaterial::Combined& updated,
43 size_t index,
44 const AmgVector(5) & parameters,
45 const AmgSymMatrix(5) * covariance)
46{
47 updated.parameters[index] = parameters;
48 if (covariance) {
49 updated.covariances[index] += *covariance;
50 } else {
51 updated.covariances[index].setZero();
52 }
53}
54
55bool
56updateP(double& qOverP, double deltaP)
57{
58 double p = 1. / std::abs(qOverP);
59 p += deltaP;
60 if (p <= 0.) {
61 return false;
62 }
63 qOverP = qOverP > 0. ? 1. / p : -1. / p;
64 return true;
65}
66
67std::pair<const Trk::MaterialProperties*, double>
68getMaterialProperties(const Trk::TrackParameters* trackParameters,
69 const Trk::Layer& layer)
70{
71
72 const Trk::MaterialProperties* materialProperties(nullptr);
73 double pathCorrection(0.);
74
75 // Check that the material properties have been defined - if not define them
76 // from the layer information
77 materialProperties = materialProperties
78 ? materialProperties
79 : layer.fullUpdateMaterialProperties(*trackParameters);
80 // Bail out if still no material properties can be found
81 if (!materialProperties) {
82 return { nullptr, 0 };
83 }
84 // Define the path correction
85 pathCorrection =
86 pathCorrection > 0.
87 ? pathCorrection
88 : layer.surfaceRepresentation().pathCorrection(
89 trackParameters->position(), trackParameters->momentum());
90
91 // The pathlength ( in mm ) is the path correction * the thickness of the
92 // material
93 const double pathLength = pathCorrection * materialProperties->thickness();
94 return { materialProperties, pathLength };
95}
96
97Trk::MultiComponentState createMergedState(const GSFUtils::MergeArray& merges,
98 std::vector<GsfMaterial::Combined>& caches,
99 const std::vector<std::pair<size_t, size_t>>& indices,
100 const Trk::MultiComponentState& inputState){
101
103 size_t numComponents = indices.size();
104 // Gather the merges we need
105 std::vector<char> isMerged(numComponents, 0);
106 // Merge components "From" to components "To"
107 const int returnedMerges = merges.size();
108 for (int i = 0; i < returnedMerges; ++i) {
109 const int mini = merges[i].To;
110 const int minj = merges[i].From;
111 // Get the first TP
112 const size_t stateIndex = indices[mini].first;
113 const size_t materialIndex = indices[mini].second;
114 // Copy weight and first parameters as they are needed later on
115 // for updating the covariance
116 const AmgVector(5) firstParameters =
117 caches[stateIndex].parameters[materialIndex];
118 const double firstWeight = caches[stateIndex].weights[materialIndex];
119 // Get the second TP
120 const size_t stateIndex2 = indices[minj].first;
121 const size_t materialIndex2 = indices[minj].second;
122 // Set as merged
123 isMerged[minj] = 1;
124 // Update first parameters and weight
125 Trk::MultiComponentStateCombiner::combineParametersWithWeight(
126 caches[stateIndex].parameters[materialIndex],
127 caches[stateIndex].weights[materialIndex],
128 caches[stateIndex2].parameters[materialIndex2],
129 caches[stateIndex2].weights[materialIndex2]);
130 // Update covariance
131 Trk::MultiComponentStateCombiner::combineCovWithWeight(
132 firstParameters, caches[stateIndex].covariances[materialIndex],
133 firstWeight, caches[stateIndex2].parameters[materialIndex2],
134 caches[stateIndex2].covariances[materialIndex2],
135 caches[stateIndex2].weights[materialIndex2]);
136 // Reset 2nd parameters values just for clarity
137 caches[stateIndex2].parameters[materialIndex2].setZero();
138 caches[stateIndex2].covariances[materialIndex2].setZero();
139 }
140
141 // Loop over remaining unmerged components
142 for (size_t i(0); i < numComponents; ++i) {
143 if (isMerged[i]) {
144 continue;
145 }
146 // Build the TP
147 const size_t stateIndex = indices[i].first;
148 const size_t materialIndex = indices[i].second;
149 AmgVector(5)& stateVector =
150 caches[stateIndex].parameters[materialIndex];
151 AmgSymMatrix(5)& measuredCov =
152 caches[stateIndex].covariances[materialIndex];
153
154 std::unique_ptr<Trk::TrackParameters> updatedTrackParameters =
155 inputState[stateIndex]
156 .params->associatedSurface()
157 .createUniqueTrackParameters(
158 stateVector[Trk::loc1], stateVector[Trk::loc2],
159 stateVector[Trk::phi], stateVector[Trk::theta],
160 stateVector[Trk::qOverP], measuredCov);
161
162 const double updatedWeight = caches[stateIndex].weights[materialIndex];
163
164 assemblerCache.multiComponentState.push_back(
165 {std::move(updatedTrackParameters), updatedWeight});
166 assemblerCache.validWeightSum += updatedWeight;
167 }
169 std::move(assemblerCache));
170}
171} // end of anonymous namespace
172
174 const std::string& type,
175 const std::string& name,
176 const IInterface* parent)
177 : AthAlgTool(type, name, parent)
178{
179 declareInterface<IMaterialMixtureConvolution>(this);
180}
181
183
184StatusCode
186{
188 ATH_MSG_FATAL("Requested MaximumNumberOfComponents > "
190 return StatusCode::FAILURE;
191 }
192
193 m_materialEffects = std::make_unique<ElectronCombinedMaterialEffects>(
195 return StatusCode::SUCCESS;
196}
197
198/* ==========================================
199 Update with full material effects
200 ========================================== */
203 std::vector<GsfMaterial::Combined>& caches,
204 const Trk::MultiComponentState& multiComponentState,
205 const Trk::Layer& layer,
206 Trk::PropDirection direction) const
207{
208 const double updateFactor = 1.0;
209 Trk::MultiComponentState updatedMergedState = update(
210 caches, multiComponentState, layer, direction, updateFactor);
211
212 if (updatedMergedState.empty()) {
213 return {};
214 }
216 return updatedMergedState;
217}
218
219/* ==========================================
220 Update with pre-update material effects
221========================================== */
224 std::vector<GsfMaterial::Combined>& caches,
225 const Trk::MultiComponentState& multiComponentState,
226 const Trk::Layer& layer,
227 Trk::PropDirection direction) const
228{
229 const double updateFactor =
230 layer.preUpdateMaterialFactor(*multiComponentState.front().params, direction);
231
232 Trk::MultiComponentState updatedMergedState = update(caches,
233 multiComponentState,
234 layer,
235 direction,
236 updateFactor);
237 if (updatedMergedState.empty()) {
238 return {};
239 }
241 return updatedMergedState;
242}
243
244/* ==========================================
245 Update with post-update material effects
246 ========================================== */
249 std::vector<GsfMaterial::Combined>& caches,
250 const Trk::MultiComponentState& multiComponentState,
251 const Trk::Layer& layer,
252 Trk::PropDirection direction) const
253{
254 const double updateFactor = layer.postUpdateMaterialFactor(
255 *multiComponentState.front().params, direction);
256
257 Trk::MultiComponentState updatedMergedState =
258 update(caches, multiComponentState, layer, direction, updateFactor);
259
260 if (updatedMergedState.empty()) {
261 return {};
262 }
264 return updatedMergedState;
265}
266
269 std::vector<GsfMaterial::Combined>& caches,
270 const Trk::MultiComponentState& inputState,
271 const Trk::Layer& layer,
272 Trk::PropDirection direction,
273 double updateFactor) const
274{
275
276 // Check the multi-component state is populated
277 if (inputState.empty()) {
278 return {};
279 }
280 if (updateFactor < 0.01) {
281 // Bail out as factor is too small to bother about
282 return {};
283 }
284 caches.resize(inputState.size());
285
286 // Fill cache and work out how many final components there should be
287 size_t numComponents(0);
288 for (size_t i(0); i < inputState.size(); ++i) {
289 const AmgSymMatrix(5)* measuredCov = inputState[i].params->covariance();
290 // If the momentum is too dont apply material effects
291 if (inputState[i].params->momentum().mag() <= 250. * Gaudi::Units::MeV) {
292 dummyCacheElement(caches[i]);
293 updateCacheElement(caches[i], 0, inputState[i].params->parameters(), measuredCov);
294 caches[i].weights[0] = inputState[i].weight;
295 numComponents += caches[i].numEntries;
296 continue;
297 }
298 // Get the material effects and store them in the cache
299 std::pair<const Trk::MaterialProperties*, double> matPropPair =
300 getMaterialProperties(inputState[i].params.get(), layer);
301
302 if (!matPropPair.first) {
303 dummyCacheElement(caches[i]);
304 updateCacheElement(caches[i], 0, inputState[i].params->parameters(), measuredCov);
305 caches[i].weights[0] = inputState[i].weight;
306 numComponents += caches[i].numEntries;
307 continue;
308 }
309 // Now we can compute/apply actual material effects
310 // Apply the update factor
311 matPropPair.second *= updateFactor;
312 m_materialEffects->compute(caches[i],
313 inputState[i],
314 *matPropPair.first,
315 matPropPair.second,
316 direction);
317
318 // Apply material effects to input state and store results in cache
319 // We have i material caches , one for each input state.
320 // Each cache has j entries. They correspond to each
321 // Gausian used to describe the Bethe Heitler.
322 for (size_t j(0); j < caches[i].numEntries; ++j) {
323 updateCacheElement(caches[i], j, inputState[i].params->parameters(), measuredCov);
324 // Adjust q/p of the (delta) Parameters
325 // make sure update is good.
326 if (!updateP(caches[i].parameters[j][Trk::qOverP],
327 caches[i].deltaPs[j])) {
328 ATH_MSG_ERROR("Cannot update state vector momentum!!!");
329 return {};
330 }
331 // Store component weight
332 caches[i].weights[j] *= inputState[i].weight;
333 // Ensure weight of component is not too small to save us from potential
334 // FPE's Value. Weights are double so the min of float should
335 // be small enough and should be handled
336 if (caches[i].weights[j] < std::numeric_limits<float>::min()) {
337 caches[i].weights[j] = std::numeric_limits<float>::min();
338 }
339 }
340 numComponents += caches[i].numEntries;
341 } // End of loop filling the cache
342
343 // Fill information for calculating which components to merge
344 // In addition scan all components for covariance matrices.
345 // If one component is missing its error matrix,
346 // component reduction is impossible.
347 //
348 bool componentWithoutMeasurement = false;
349 // keep track of the state component and material effects indices
350 // Effectively here we have M state X N Material effects
351 // MXN components.
352 std::vector<std::pair<size_t, size_t>> indices{};
353 indices.resize(numComponents);
354 GSFUtils::Component1DArray componentsArray(numComponents);
355 size_t k(0);
356 for (size_t i(0); i < inputState.size(); ++i) {
357 for (size_t j(0); j < caches[i].numEntries; ++j) {
358 const AmgSymMatrix(5)* measuredCov = inputState[i].params->covariance();
359 // Fill in infomation
360 const double cov =
361 measuredCov ? caches[i].covariances[j](Trk::qOverP, Trk::qOverP)
362 : -1.;
363 if (!measuredCov) {
364 componentWithoutMeasurement = true;
365 }
366 componentsArray[k].mean = caches[i].parameters[j][Trk::qOverP];
367 componentsArray[k].cov = cov;
368 componentsArray[k].invCov = cov > 0 ? 1. / cov : 1e10;
369 componentsArray[k].weight = caches[i].weights[j];
370 indices[k] = {i, j};
371 ++k;
372 }
373 }
374
375 // fallback if we have a component without measurement
376 if (componentWithoutMeasurement) {
377 auto* result = std::max_element(
378 componentsArray.begin(), componentsArray.end(),
379 [](const auto& a, const auto& b) { return a.weight < b.weight; });
380 auto index = std::distance(componentsArray.begin(), result);
381 const size_t stateIndex = indices[index].first;
382 const size_t materialIndex = indices[index].second;
383
384 AmgVector(5)& updatedStateVector = caches[stateIndex].parameters[materialIndex];
385 const AmgSymMatrix(5)* measuredCov = inputState[stateIndex].params->covariance();
386 std::optional<AmgSymMatrix(5)> updatedCovariance = std::nullopt;
387 if (measuredCov && caches[stateIndex].covariances.size() > materialIndex) {
388 updatedCovariance =
389 AmgSymMatrix(5)(caches[stateIndex].covariances[materialIndex]);
390 }
391 std::unique_ptr<Trk::TrackParameters> updatedTrackParameters =
392 inputState[stateIndex]
393 .params->associatedSurface()
394 .createUniqueTrackParameters(
395 updatedStateVector[Trk::loc1], updatedStateVector[Trk::loc2],
396 updatedStateVector[Trk::phi], updatedStateVector[Trk::theta],
397 updatedStateVector[Trk::qOverP], std::move(updatedCovariance));
398
399 Trk::ComponentParameters dummyCompParams = {
400 std::move(updatedTrackParameters), 1.};
401 Trk::MultiComponentState returnMultiState;
402 returnMultiState.push_back(std::move(dummyCompParams));
403 return returnMultiState;
404 }
405
406 //Create the state to rerurn accounting for any needed merges.
408 if (numComponents > m_maximumNumberOfComponents) {
409 merges = findMerges(std::move(componentsArray), m_maximumNumberOfComponents);
410 }
411 auto mergedState = createMergedState(merges, caches, indices, inputState);
412
413 if (mergedState.size() > m_maximumNumberOfComponents) {
414 ATH_MSG_ERROR("Merging failed, target size: " << m_maximumNumberOfComponents
415 << " final size: "
416 << mergedState.size());
417 }
418 return mergedState;
419}
420
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
Definition of component parameters for use in a mixture of many components. In this regime each track...
Class description for convolution of GSF material mixture.
#define AmgSymMatrix(dim)
#define AmgVector(rows)
Utilities to facilitate the calculation of the KL divergence/distance between components of the mixtu...
static Double_t a
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
virtual ~ElectronMaterialMixtureConvolution()
AlgTool initialise method.
virtual MultiComponentState preUpdate(std::vector< GsfMaterial::Combined > &, const MultiComponentState &, const Layer &, PropDirection direction=anyDirection) const override final
Convolution with post-measurement-update material properties.
virtual MultiComponentState update(std::vector< GsfMaterial::Combined > &, const MultiComponentState &, const Layer &, PropDirection direction=anyDirection) const override final
Convolution with pre-measurement-update material properties.
std::unique_ptr< ElectronCombinedMaterialEffects > m_materialEffects
virtual StatusCode initialize() override final
Convolution with full material properties.
ElectronMaterialMixtureConvolution(const std::string &, const std::string &, const IInterface *)
Destructor.
virtual MultiComponentState postUpdate(std::vector< GsfMaterial::Combined > &, const MultiComponentState &, const Layer &, PropDirection direction=anyDirection) const override final
The particle hypothesis we implement material effects for.
Base Class for a Detector Layer in the Tracking realm.
Definition Layer.h:72
Material with information about thickness of material.
const Amg::Vector3D & momentum() const
Access method for the momentum.
const Amg::Vector3D & position() const
Access method for the position.
#define ATH_FLATTEN
constexpr int8_t maxNumberofStateComponents
The state is described by N Gaussian components The Beth Heitler Material effect are also described b...
AlignedDynArray< Component1D, GSFConstants::alignment > Component1DArray
std::vector< Merge > MergeArray
@ qOverP
perigee
@ layer
Definition HitInfo.h:79
MultiComponentState assembledState(MultiComponentStateAssembler::Cache &&cache)
Method to return the cached state object - it performs a reweighting before returning the object base...
void renormaliseState(MultiComponentState &, double norm=1)
Performing renormalisation of total state weighting to one.
Ensure that the ATLAS eigen extensions are properly loaded.
PropDirection
PropDirection, enum for direction of the propagation.
std::vector< ComponentParameters > MultiComponentState
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ loc2
generic first and second local coordinate
Definition ParamDefs.h:35
@ phi
Definition ParamDefs.h:75
@ loc1
Definition ParamDefs.h:34
std::pair< long int, long int > indices
ParametersBase< TrackParametersDim, Charged > TrackParameters
void Zero(TH1D *hin)
Definition generate.cxx:32
Definition index.py:1
iterator end() noexcept
iterator pointing to the past-the-end element
iterator begin() noexcept
iterator pointing to the first element
Helper struct for combined material effects, multicomponent description.
Definition GsfMaterial.h:49
std::array< AmgVector(5), GSFConstants::maxNumberofMatComponents > parameters
Definition GsfMaterial.h:56
std::array< AmgSymMatrix(5), GSFConstants::maxNumberofMatComponents > covariances
Definition GsfMaterial.h:60