ATLAS Offline Software
Loading...
Searching...
No Matches
InDetMaterialManager.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4
8#include "GeoModelKernel/GeoIntrusivePtr.h"
9#include "GeoModelKernel/GeoMaterial.h"
10#include "GeoModelKernel/GeoElement.h"
11#include "GeoModelKernel/Units.h"
12#include "GaudiKernel/SystemOfUnits.h"
17
18#include <iostream>
19#include <iomanip>
20#include <cmath> //for std::abs
21#include <stdexcept>
22#include <format>
23
24//for checking whether a value is a reliable denominator
26
27// Constructor
28InDetMaterialManager::InDetMaterialManager(const std::string& managerName,
29 StoreGateSvc* detStore)
30 : AthMessaging(managerName),
31 m_managerName(managerName),
33{
35}
36
37// Constructor
38InDetMaterialManager::InDetMaterialManager(const std::string& managerName,
39 StoreGateSvc* detStore,
40 const IRDBRecordset_ptr& weightTable,
41 const std::string& space,
42 bool extraFunctionality)
43 : AthMessaging(managerName),
44 m_managerName(managerName),
45 m_extraFunctionality(extraFunctionality)
46{
48
49 if (weightTable) addWeightTable(weightTable, space);
50
51 // For testing we add a few materials.
52 //m_weightMap["sct::CoolingBlock"] = MaterialByWeight(2.418*CLHEP::gram);
53 //m_weightMap["sct::CoolingBlock"] = MaterialByWeight(2*CLHEP::gram);
54 //m_weightMap["sct::BrlHybrid"] = MaterialByWeight("sct::Hybrid", 8*CLHEP::gram);
55 //m_weightMap["sct::FwdHybrid"] = MaterialByWeight("std::Carbon", 7.662*CLHEP::gram);
56}
57
58InDetMaterialManager::InDetMaterialManager(const std::string& managerName, StoreGateSvc* detStore,
59 const IRDBRecordset_ptr& weightTable,
60 const IRDBRecordset_ptr& compositionTable,
61 const std::string& space)
62 : AthMessaging(managerName),
63 m_managerName(managerName),
65{
67
68 if (weightTable) addWeightTable(weightTable, space);
69 if (compositionTable) addCompositionTable(compositionTable, space);
70}
71
72InDetMaterialManager::InDetMaterialManager(const std::string& managerName,
73 InDetDD::AthenaComps* athenaComps)
74 : AthMessaging(managerName),
75 m_managerName(managerName),
77{
79}
80
82
85 return detStore->tryRetrieve<StoredMaterialManager>("MATERIALS");
86}
87
88const GeoElement*
89InDetMaterialManager::getElement(const std::string& elementName) {
91 std::string errorMessage("Null pointer to Stored Material Manager!");
92 ATH_MSG_FATAL(errorMessage);
93 throw std::runtime_error(errorMessage);
94 }
95 return m_materialManager->getElement(elementName);
96}
97
98const GeoMaterial*
99InDetMaterialManager::getMaterial(std::string_view materialName) {
100 return extraScaledMaterial(materialName, getMaterialInternal(materialName));
101}
102
103bool
104InDetMaterialManager::hasMaterial(const std::string& materialName) const {
105 return m_weightMap.find(materialName) != m_weightMap.end();
106}
107
108const GeoMaterial*
109InDetMaterialManager::getMaterialInternal(std::string_view materialName) {
110 // First check local store of materials. If not found then get it from the GeoModel
111 // manager.
112 const GeoMaterial* material = getAdditionalMaterial(materialName);
113
114 if (!material) {
115 if(!m_materialManager) {
116 std::string errorMessage("Null pointer to Stored Material Manager!");
117 ATH_MSG_FATAL(errorMessage);
118 throw std::runtime_error(errorMessage);
119 }
120 // This prints error message if not found.
121 material = m_materialManager->getMaterial(materialName);
122 }
123 return material;
124}
125
126const GeoMaterial*
127InDetMaterialManager::getAdditionalMaterial(std::string_view materialName) const {
128 MaterialStore::const_iterator iter;
129 if ((iter = m_store.find(materialName)) != m_store.end()) {
130 return iter->second;
131 } else {
132 return nullptr;
133 }
134}
135
136const GeoMaterial*
138 const double volumeTot,
139 const double volume1, const std::string& matName1,
140 const double volume2, const std::string& matName2
141 ) {
142 std::vector<std::string> baseMaterials;
143 std::vector<double> fracWeight;
144 baseMaterials.reserve(2);
145 fracWeight.reserve(2);
146
147 ATH_MSG_DEBUG("Composite material : " << volumeTot / Gaudi::Units::cm3 << " = " << volume1 / Gaudi::Units::cm3
148 << " + " << volume2 / Gaudi::Units::cm3);
149 ATH_MSG_DEBUG("Composite material : " << matName1 << " " << matName2);
150
151 double density1, density2;
152
153 MaterialWeightMap::const_iterator iter;
154 if ((iter = m_weightMap.find(matName1)) != m_weightMap.end()) {
155 const GeoMaterial* mat1 = getMaterialForVolume(matName1, volume1);
156 density1 = mat1->getDensity();
157 ATH_MSG_DEBUG("Composite material 1 - weight : " << density1 / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
158 } else {
159 const GeoMaterial* mat1 = getMaterial(matName1);
160 density1 = mat1->getDensity();
161 ATH_MSG_DEBUG("Composite material 1 - standard : " << density1 / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
162 }
163
164 if ((iter = m_weightMap.find(matName2)) != m_weightMap.end()) {
165 const GeoMaterial* mat2 = getMaterialForVolume(matName2, volume2);
166 density2 = mat2->getDensity();
167 ATH_MSG_DEBUG("Composite material 2 - weight : " << density2 / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
168 } else {
169 const GeoMaterial* mat2 = getMaterial(matName2);
170 density2 = mat2->getDensity();
171 ATH_MSG_DEBUG("Composite material 2 - standard : " << density2 / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
172 }
173
174 double weight1 = density1 * volume1;
175 double weight2 = density2 * volume2;
176 double invWeightTot = 1.0 / (weight1 + weight2);
177
178 double density = (weight1 + weight2) / volumeTot;
179
180 double frac1 = weight1 / (weight1 + weight2);
181 double frac2 = weight2 / (weight1 + weight2);
182 double density_2 = 1.0 / (frac1 / density1 + frac2 / density2);
183 double density_3 = (weight1 + weight2) / (volume1 + volume2);
184 ATH_MSG_DEBUG("-> weights : " << weight1 / (GeoModelKernelUnits::gram) << " " << weight2 / (GeoModelKernelUnits::gram));
185 ATH_MSG_DEBUG("-> density : " << density / (GeoModelKernelUnits::gram / Gaudi::Units::cm3) << " " << density_2 /
186 (GeoModelKernelUnits::gram / Gaudi::Units::cm3) << " " << density_3 / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
187
188
189 baseMaterials.push_back(matName1);
190 baseMaterials.push_back(matName2);
191 fracWeight.push_back(weight1 * invWeightTot);
192 fracWeight.push_back(weight2 * invWeightTot);
193
194 return getMaterial(newMatName, baseMaterials, fracWeight, density);
195}
196
197// This creates a new material with specified density.
198
199// If a newName is supplied it creates the new material even if the orginal material
200// has the same density. It however first checks if the material with NewName exists.
201
202// If no new name is supplied then it checks the density of
203// the existing material. If it is consistent it returns the material.
204// If it is different it creates a material with the string "Modified" added to the
205// name.
206
207
208const GeoMaterial*
209InDetMaterialManager::getMaterial(std::string_view origMaterialName,
210 double density,
211 std::string_view newName) {
212 return extraScaledMaterial(origMaterialName, newName,
213 getMaterialInternal(origMaterialName, density, newName));
214}
215
216const GeoMaterial*
217InDetMaterialManager::getMaterialInternal(std::string_view origMaterialName,
218 double density,
219 std::string_view newName) {
220 std::string newName2{newName};
221 bool newNameProvided = !newName2.empty();
222 if (!newNameProvided) {
223 newName2 = std::string{origMaterialName} + "Modified";
224 }
225
226 const GeoMaterial* newMaterial = nullptr;
227
228 // First see if we already have the modified material
229 const GeoMaterial* material = getAdditionalMaterial(newName2);
230 if (material) {
231 if (!compareDensity(material->getDensity(), density)) {
232 ATH_MSG_WARNING("Density is not consistent for material " << newName2
233 << " " << material->getDensity() / (GeoModelKernelUnits::gram / Gaudi::Units::cm3)
234 << " / " << density / (GeoModelKernelUnits::gram / Gaudi::Units::cm3));
235 }
236 newMaterial = material;
237 } else {
238 const GeoMaterial* origMaterial = getMaterialInternal(origMaterialName);
239 newMaterial = origMaterial;
240 if (origMaterial) {
241 // If no new name was provided we check if the density is compatible
242 // and if so we return the original material.
243 if (newNameProvided || !compareDensity(origMaterial->getDensity(), density)) {
244 // create new material
245 GeoMaterial* newMaterialTmp = new GeoMaterial(newName2, density);
246 newMaterialTmp->add(const_cast<GeoMaterial*>(origMaterial), 1.);
247 addMaterial(newMaterialTmp);
248 newMaterial = newMaterialTmp;
249 }
250 }
251 }
252 return newMaterial;
253}
254
255const GeoMaterial*
256InDetMaterialManager::getMaterialScaled(const std::string& origMaterialName,
257 double scaleFactor,
258 const std::string& newName) {
259 return extraScaledMaterial(origMaterialName, newName,
260 getMaterialScaledInternal(origMaterialName, scaleFactor, newName));
261}
262
263const GeoMaterial*
264InDetMaterialManager::getMaterialScaledInternal(const std::string& origMaterialName,
265 double scaleFactor,
266 const std::string& newName) {
267 // Don't allow large scale factors
268 if (scaleFactor > 1000 || scaleFactor < 0.001) {
269 ATH_MSG_ERROR("Scale factor must be between 0.001 and 1000.");
270 return nullptr;
271 }
272
273 const GeoMaterial* origMaterial = getMaterialInternal(origMaterialName);
274
275 // If scalefactor is 1 and no new name is requested
276 // then just return the orginal material
277 if (newName.empty() && scaleFactor == 1.) return origMaterial;
278
279 const GeoMaterial* newMaterial = nullptr;
280
281 if (origMaterial) {
282 double density = origMaterial->getDensity() * scaleFactor;
283 std::string newName2 = newName;
284 if (newName2.empty()) {
285 // Create name using the scale factor.
286 int scaleInt = static_cast<int>(scaleFactor * 10000);
287 int scale1 = scaleInt / 10000;
288 int scale2 = scaleInt % 10000;
289
290 std::ostringstream os;
291 os << origMaterialName << scale1 << "_" << std::setw(4) << std::setfill('0') << scale2;
292 newName2 = os.str();
293 }
294
295 newMaterial = getMaterialInternal(origMaterialName, density, newName2);
296 }
297
298 return newMaterial;
299}
300
301void
302InDetMaterialManager::addMaterial(GeoMaterial* material) {
303 GeoIntrusivePtr<const GeoMaterial> matPtr{material};
304 std::string name(material->getName());
305 if (m_store.find(name) != m_store.end()) {
306 ATH_MSG_WARNING("Ignoring attempt to redefine an existing material: " << name);
307 // Delete the material if it is not already ref counted.
308 //std::cout << m_store[name] << std::endl;
309 } else {
310 material->lock();
311 m_store[name] = std::move(matPtr);
312
313 ATH_MSG_DEBUG("Created new material: " << name << ", " << material->getDensity() /
314 (Gaudi::Units::g / Gaudi::Units::cm3) << " g/cm3");
315 }
316}
317
318bool
319InDetMaterialManager::compareDensity(double d1, double d2) const {
320 if (close_to_zero(d2)){
321 throw (std::runtime_error("InDetMaterialManager:compareDensity: Density is zero"));
322 }
323 return(std::abs(d1 / d2 - 1.) < 1e-5);
324}
325
326void
327InDetMaterialManager::addWeightTable(const IRDBRecordset_ptr& weightTable, const std::string& space) {
328 ATH_MSG_DEBUG("Reading in weight table: " << weightTable->nodeName());
329 for(const auto& rec : *weightTable) {
330 std::string materialName = rec->getString("MATERIAL");
331 if (!space.empty()) {
332 materialName = space + "::" + materialName;
333 }
334 std::string materialBase;
335 try {
336 if (!rec->isFieldNull("BASEMATERIAL")) {
337 materialBase = rec->getString("BASEMATERIAL");
338 }
339 }
340 catch(std::runtime_error&) {
341 ATH_MSG_DEBUG(weightTable->nodeName() << " table has no column named BASEMATERIAL");
342 }
343 double weight = rec->getDouble("WEIGHT") * GeoModelKernelUnits::gram;
344
345 bool linearWeightFlag = false;
347 try {
348 if(!rec->isFieldNull("LINWEIGHTFLAG")) {
349 linearWeightFlag = rec->getInt("LINWEIGHTFLAG");
350 }
351 }
352 catch(std::runtime_error&) {
353 ATH_MSG_DEBUG(weightTable->nodeName() << " table has no column named LINWEIGHTFLAG");
354 }
355 }
356
357 if (m_weightMap.find(materialName) != m_weightMap.end()) {
358 ATH_MSG_WARNING("Material: " << materialName << " already exists in weight table");
359 } else {
360 ATH_MSG_DEBUG("Adding " << materialName
361 << " weight " << weight
362 << " linearWeightFlag " << linearWeightFlag
363 << " raw weight " << rec->getDouble("WEIGHT")
364 << " m_extraFunctionality " << m_extraFunctionality
365 << " to weight table");
366 m_weightMap[materialName] = MaterialByWeight(materialBase, weight, linearWeightFlag);
367 }
368 }
369}
370
371void
372InDetMaterialManager::addWeightMaterial(const std::string& materialName, const std::string& materialBase, double weight,
373 int linearWeightFlag) {
374 // Weight in gr
375 weight = weight * GeoModelKernelUnits::gram;
376
377 if (m_weightMap.find(materialName) != m_weightMap.end()) {
378 ATH_MSG_WARNING("Material: " << materialName << " already exists in weight table");
379 } else {
380 ATH_MSG_DEBUG("Adding " << materialName
381 << " weight " << weight
382 << " linearWeightFlag " << linearWeightFlag
383 << " to weight table");
384 m_weightMap[materialName] = MaterialByWeight(materialBase, weight, linearWeightFlag);
385 }
386}
387
388void
389InDetMaterialManager::addCompositionTable(const IRDBRecordset_ptr& compositionTable, const std::string& space) {
390 ATH_MSG_DEBUG("Reading in composition table: " << compositionTable->nodeName());
391
392 for (const auto& rec : *compositionTable) {
393 std::string materialName = rec->getString("MATERIAL");
394 if (!space.empty()) {
395 materialName = space + "::" + materialName;
396 }
397
398 std::string componentName = rec->getString("COMPONENT");
399 int count = rec->getInt("COUNT");
400 double factor = rec->getDouble("FACTOR");
401 double actualLength = rec->getDouble("ACTUALLENGTH");
402
403 m_matCompositionMap.insert(std::pair<std::string, MaterialComponent>(materialName,
404 MaterialComponent(componentName,
405 count * factor,
406 actualLength)));
407 }
408}
409
410void
412 if (!scalingTable || scalingTable->size()==0) return;
413 ATH_MSG_DEBUG("Reading in extra material scaling table: " << scalingTable->nodeName());
414 for (const auto& rec : *scalingTable) {
415 std::string materialName = rec->getString("MATERIAL");
416 double scalingFactor = rec->getDouble("FACTOR");
417
418 if (scalingFactor >= 0 || scalingFactor == 1) {
419 ATH_MSG_DEBUG("Material " << materialName << " will be scaled by: " << scalingFactor);
420 } else {
421 // -ve or scalefactor = 1 means will not be scaled.
422 ATH_MSG_DEBUG("Material " << materialName << " will be NOT be scaled.");
423 }
424 if (m_scalingMap.find(materialName) != m_scalingMap.end()) {
425 ATH_MSG_WARNING("Overriding material: " << materialName << " which already exists in scaling table");
426 }
427 m_scalingMap[materialName] = scalingFactor;
428 }
429}
430
431const GeoMaterial*
432InDetMaterialManager::getMaterialForVolume(std::string_view materialName, double volume, std::string_view newName) {
433 // Make sure we have a valid volume size.
434 if (volume <= 0) {
435 ATH_MSG_ERROR("Invalid volume : " << volume);
436 return nullptr;
437 }
438
439 // Find if material is in the weight table.
440 // If so we use the information to create a material with the
441 // density calculated from the volume and weight. If a base material
442 // is specified in the weight table, then a new material is made
443 // which is the same as the base material but with the new
444 // density. If no base material is specified then there should be a
445 // material already existing with that name. If the existing material already has the
446 // correct density it is used, otherwise a new material is created
447 // with the string "Modified" added to the material name.
448
449 MaterialWeightMap::const_iterator iter;
450 if (iter = m_weightMap.find(materialName); iter != m_weightMap.end()) {
451 const std::string& materialBase = iter->second.name;
452 double weight = iter->second.weight;
453 double density = weight / volume;
454 if (iter->second.linearWeightFlag) {
455 ATH_MSG_ERROR("Material defined by linear weight cannot be created with getMaterialForVolume method: " <<
456 materialName);
457 }
458
459 ATH_MSG_VERBOSE("Found material in weight table - name, base, weight(g), volume(cm3), density(g/cm3): "
460 << materialName << ", "
461 << materialBase << ", "
462 << weight / GeoModelKernelUnits::gram << ", "
463 << volume / Gaudi::Units::cm3 << ", "
464 << density / (Gaudi::Units::g / Gaudi::Units::cm3));
465
466 if (materialBase.empty()) {
467 return getMaterial(materialName, density, newName);
468 } else {
469 if (newName.empty()) {
470 return getMaterial(materialBase, density, materialName);
471 } else {
472 return getMaterial(materialBase, density, newName);
473 }
474 }
475 } else {
476 // If not in the weight table we just return the material.
477 // This is not an error.
478 ATH_MSG_VERBOSE("Material not in weight table, using standard material: "
479 << materialName
480 << ", volume(cm3) = " << volume / Gaudi::Units::cm3);
481 return getMaterial(materialName);
482 }
483}
484
485const GeoMaterial*
486InDetMaterialManager::getMaterialForVolumeLength(const std::string& materialName, double volume, double length,
487 const std::string& newName) {
488 // In the case there is no material composition table (MaterialCompositionMap) and no linear weights are used this
489 // will
490 // behave the same way as getMaterialForVolume.
491 // If the material is in the MaterialCompositionMap it will build a material using the components
492 // from that table. If any components are defined as a linear weight the length is used to calculate the
493 // weight (ie linear weight * length).
494
495
496 std::string name;
497 if (newName.empty()) {
498 name = materialName;
499 } else {
500 name = newName;
501 }
502
503 // Make sure we have a valid volume size.
504 if (volume <= 0 || length <= 0) {
505 ATH_MSG_ERROR("Invalid volume or length : " << volume << ", " << length);
506 return nullptr;
507 }
508
509 // First look in the predefinded collections
510 std::pair<MaterialCompositionMap::const_iterator, MaterialCompositionMap::const_iterator> iterRange;
511 iterRange = m_matCompositionMap.equal_range(materialName);
512 if (iterRange.first != m_matCompositionMap.end()) {
513 ATH_MSG_VERBOSE("Found material in material composition table:" << materialName);
514
515 std::vector<double> factors;
516 std::vector<std::string> components;
517 for (MaterialCompositionMap::const_iterator iter = iterRange.first; iter != iterRange.second; ++iter) {
518 double factorTmp = iter->second.factor;
519 if (iter->second.actualLength > 0) factorTmp *= iter->second.actualLength / length;
520 factors.push_back(factorTmp);
521 components.push_back(iter->second.name);
522 }
523 return getMaterialForVolumeLength(name, components, factors, volume, length);
524 }
525
526 // Next look in weight table
527 MaterialWeightMap::const_iterator iter;
528 if ((iter = m_weightMap.find(materialName)) != m_weightMap.end()) {
529 const std::string& materialBase = iter->second.name;
530 double weight = iter->second.weight;
531 double density = weight / volume;
532 if (iter->second.linearWeightFlag) weight *= length;
533
534 if (materialBase.empty()) {
535 return getMaterial(materialName, density, newName);
536 } else {
537 return getMaterial(materialBase, density, name);
538 }
539 } else {
540 // Otherwise we just return the material.
541 // This is not an error.
542 ATH_MSG_VERBOSE("Material not in weight table, using standard material: "
543 << materialName
544 << ", volume(cm3) = " << volume / Gaudi::Units::cm3);
545 return getMaterial(materialName);
546 }
547}
548
549const GeoMaterial*
551 const std::string& materialComponent,
552 double factor,
553 double volume,
554 double length) {
555 std::vector<std::string> tmpMaterialComponents(1, materialComponent);
556 std::vector<double> tmpFactors(1, factor);
557 return getMaterialForVolumeLength(name, tmpMaterialComponents, tmpFactors, volume, length);
558}
559
560const GeoMaterial*
562 const std::vector<std::string>& materialComponents,
563 const std::vector<double>& factors,
564 double volume,
565 double length) {
566 // Make sure we have a valid volume size.
567 if (volume <= 0 || length <= 0) {
568 ATH_MSG_ERROR("Invalid volume or length : " << volume << ", " << length);
569 return nullptr;
570 }
571
572 if (!factors.empty() && factors.size() < materialComponents.size()) {
573 ATH_MSG_WARNING("getMaterialForVolumeLength: factor vector size too small. Setting remaining factors to 1.");
574 }
575
576 std::vector<std::string> baseMaterials;
577 std::vector<double> fracWeight;
578 baseMaterials.reserve(materialComponents.size());
579 fracWeight.reserve(materialComponents.size());
580
581 double totWeight = 0;
582 for (unsigned int iComp = 0; iComp < materialComponents.size(); ++iComp) {
583 const std::string& materialName = materialComponents[iComp];
584
585 // First search in MaterialWeightMap
586 MaterialWeightMap::const_iterator iter;
587 if ((iter = m_weightMap.find(materialName)) != m_weightMap.end()) {
588 const std::string& materialBase = iter->second.name;
589 double weight = iter->second.weight;
590
591 if (iComp < factors.size()) {
592 weight *= factors[iComp];
593 }
594 ATH_MSG_DEBUG("Material " << materialName
595 << " found in weight table, weight " << iter->second.weight / GeoModelKernelUnits::gram
596 << " factor " << factors[iComp]
597 << " w*fac*len " << weight * length / GeoModelKernelUnits::gram
598 << " basMat " << materialBase
599 << " linear? " << iter->second.linearWeightFlag);
600
601 if (iter->second.linearWeightFlag) weight *= length;
602 if (materialBase.empty()) {
603 // If no base material then name should refer to an already defined material
604 baseMaterials.push_back(materialName);
605 } else {
606 baseMaterials.push_back(materialBase);
607 }
608 fracWeight.push_back(weight); // Will be normalized later.
609 totWeight += weight;
610 } else {
611 // If not in the weight table we look for a regular material.
612 // I don't think this would normally be intentional so we give a warning message.
613 /*
614 if (msgLvl(MSG::WARNING))
615 msg(MSG::WARNING)
616 << "Component material not in weight table, using standard material: "
617 << materialName << " with weight= "
618 << factors.at(iComp) * length
619 << endmsg;
620 const GeoMaterial * material = getMaterialInternal(materialName);
621 */
622
623 // In this case the factor should correspond to the linear weight
624 double weight = factors.at(iComp) * length * GeoModelKernelUnits::gram;
625
626 // If material not found, will get error message when attempting to make the material. So carry on here.
627 baseMaterials.push_back(materialName);
628 fracWeight.push_back(weight);
629 totWeight += weight;
630 }
631 }
632
633 if (msgLvl(MSG::VERBOSE)) {
634 msg(MSG::VERBOSE) << "Creating material from multiple components: " << name << endmsg;
635 for (unsigned int i = 0; i < materialComponents.size(); ++i) {
636 msg(MSG::VERBOSE) << " Component " << i << ": Name = " << baseMaterials[i]
637 << " Weight(g) = " << fracWeight[i] / Gaudi::Units::g << endmsg;
638 }
639 }
640 if (close_to_zero(totWeight)){
641 ATH_MSG_ERROR("totWeight is zero in InDetMaterialManager::getMaterialForVolumeLength");
642 return nullptr;
643 }
644 for (unsigned int i = 0; i < fracWeight.size(); ++i) {
645 fracWeight[i] /= totWeight;
646 }
647 if (close_to_zero(volume)){
648 ATH_MSG_ERROR("volume is zero in InDetMaterialManager::getMaterialForVolumeLength");
649 return nullptr;
650 }
651 double density = totWeight / volume;
652
653 return getMaterial(name, baseMaterials, fracWeight, density);
654}
655
656
657const GeoMaterial*
658InDetMaterialManager::getMaterial(const std::string& name,
659 const std::vector<std::string>& materialComponents,
660 const std::vector<double>& fracWeight,
661 double density) {
662 return extraScaledMaterial(name, getMaterialInternal(name, materialComponents, fracWeight, density));
663}
664
665const GeoMaterial*
667 const std::vector<std::string>& materialComponents,
668 const std::vector<double>& fracWeight,
669 double density) {
670 const GeoMaterial* newMaterial = nullptr;
671
672 // First see if we already have the material
673 const GeoMaterial* material = getAdditionalMaterial(name);
674
675 if (material) {
676 if (!compareDensity(material->getDensity(), density)) {
677 ATH_MSG_WARNING("Density is not consistent for material " << name);
678 }
679 newMaterial = material;
680 } else {
681 GeoMaterial* newMaterialTmp = new GeoMaterial(name, density);
682 for (unsigned int i = 0; i < materialComponents.size(); i++) {
683 const GeoMaterial* origMaterial = getMaterialInternal(materialComponents[i]);
684 if (origMaterial) {
685 newMaterialTmp->add(const_cast<GeoMaterial*>(origMaterial), fracWeight[i]);
686 } else {
687 ATH_MSG_ERROR("Material component missing " << materialComponents[i]);
688 }
689 }
690 addMaterial(newMaterialTmp);
691 newMaterial = newMaterialTmp;
692 }
693 return newMaterial;
694}
695
696void
698 if (material.numComponents() == 0) {
699 ATH_MSG_ERROR("Material has no components: " << material.name());
700 return;
701 }
702
703 // If total of fractions is greater than 1.1 then assume material is define by ratio of atoms.
704 double totWeight = material.totalFraction();
705 bool byAtomicRatio = false;
706 if (totWeight > 1.1) {
707 byAtomicRatio = true;
708 for (unsigned int i = 0; i < material.numComponents(); i++) {
709 if (material.compName(i).find("::") != std::string::npos) {
710 // If component name has "::" in it then its not an element.
711 ATH_MSG_ERROR("Material, " << material.name() <<
712 ", is assumed to be defined by atomic ratio (due to total fraction > 1)"
713 " but component is not an element: " << material.compName(i));
714 return;
715 }
716 const GeoElement* element = getElement(material.compName(i));
717 if (!element) {
718 ATH_MSG_ERROR("Error making material " << material.name() << ". Element not found: " <<
719 material.compName(i));
720 return;
721 }
722 totWeight += material.fraction(i) * element->getA();
723 }
724 } else {
725 // Check if total fraction is close to 1.
726 if (std::abs(totWeight - 1) > 0.01) {
727 ATH_MSG_WARNING("Total fractional weight does not sum to 1. Will renormalize. Total = " << totWeight);
728 }
729 }
730 // Now build the material
731 GeoIntrusivePtr<GeoMaterial> newMaterial{new GeoMaterial(material.name(), material.density())};
732 ATH_MSG_DEBUG("Creating material: " << material.name() << " with density: "
733 << material.density() / (Gaudi::Units::g / Gaudi::Units::cm3));
734 if (close_to_zero(totWeight)){
735 ATH_MSG_ERROR("totWeight is zero in InDetMaterialManager::createMaterial");
736 return;
737 }
738 for (unsigned int i = 0; i < material.numComponents(); i++) {
739 double fracWeight = material.fraction(i) / totWeight;
740 if (material.compName(i).find("::") == std::string::npos) {
741 const GeoElement* element = getElement(material.compName(i));
742 if (!element) {
743 ATH_MSG_ERROR("Error making material " << material.name() << ". Element not found: " << material.compName(i));
744 // delete the partially created material
745 return;
746 }
747 if (byAtomicRatio) {
748 fracWeight = material.fraction(i) * element->getA() / totWeight;
749 }
750 newMaterial->add(const_cast<GeoElement*>(element), fracWeight);
751 ATH_MSG_DEBUG(" Component: " << material.compName(i) << " " << fracWeight);
752 } else {
753 GeoIntrusivePtr<const GeoMaterial> materialTmp{getMaterialInternal(material.compName(i))};
754 if (!materialTmp) {
755 ATH_MSG_ERROR("Error making material " << material.name() << ". Component not found: " << material.compName(i));
756 // delete the partially created material
757 return;
758 }
759 if (byAtomicRatio) {
760 // Should not happen as already checked that all components were elements.
761 ATH_MSG_ERROR("Unexpected Error");
762 }
763 newMaterial->add(const_cast<GeoMaterial*>(materialTmp.get()), fracWeight);
764 ATH_MSG_DEBUG(" Component: " << material.compName(i) << " " << fracWeight);
765 }
766 }
767 newMaterial->lock();
768 addMaterial(newMaterial);
769}
770
775
777 : m_name(name),
779 m_created(false)
780{}
781
782void
784 m_components.push_back(compName);
785 m_fractions.push_back(fraction);
786}
787
788double
790 double sum = 0;
791
792 for (unsigned int i = 0; i < m_fractions.size(); i++) {
793 sum += m_fractions[i];
794 }
795 return sum;
796}
797
798// We need the original name as the GeoMaterial from the standard
799// material manager has its namespace dropped. We have two versions
800// of extraScaledMaterial. One where two names are provided. In this
801// version if newName is not empty that is used, otherwise
802// materialName is used. The other just has one name and that is the
803// one that is used.
804
805const GeoMaterial*
806InDetMaterialManager::extraScaledMaterial(std::string_view materialName,
807 std::string_view newName,
808 const GeoMaterial* origMaterial) {
809 if (newName.empty()) {
810 return extraScaledMaterial(materialName, origMaterial);
811 } else {
812 return extraScaledMaterial(newName, origMaterial);
813 }
814}
815
816const GeoMaterial*
817InDetMaterialManager::extraScaledMaterial(std::string_view materialName, const GeoMaterial* origMaterial) {
818 if (!origMaterial) throw std::runtime_error(std::format("Invalid material: {}",materialName));
819
820 double scaleFactor = getExtraScaleFactor(materialName);
821 // -1 (or any -ve number) indicates material is not scaled. And if the scale factor
822 // is 1 then there is no need to create a new material.
823 if (scaleFactor < 0 || scaleFactor == 1 || materialName.find("Ether") != std::string::npos) return origMaterial;
824
825 if (scaleFactor == 0) return getMaterialInternal("std::Vacuum");
826
827 std::string newName = std::string{materialName} + "_ExtraScaling";
828
829 // Check if it is already made.
830 const GeoMaterial* newMaterial = getAdditionalMaterial(newName);
831
832 // Already made so we return it.
833 if (newMaterial) return newMaterial;
834
835 // Otherwise we need to make it.
836 double density = origMaterial->getDensity() * scaleFactor;
837
838 // create new material
839 GeoMaterial* newMaterialTmp = new GeoMaterial(newName, density);
840 newMaterialTmp->add(const_cast<GeoMaterial*>(origMaterial), 1.);
841 addMaterial(newMaterialTmp);
842 newMaterial = newMaterialTmp;
843
844 return newMaterial;
845}
846
847double
848InDetMaterialManager::getExtraScaleFactor(std::string_view materialName) {
849 // If name is found in map we return the corresponding scale factor.
850 // The special name "ALL" indicates all materials are scaled.
851 // Individual materials can be excluded from scaling by giving either
852 // a -ve scaling factor or just specifying a scaling factor of 1.
853 // A scaling factor of 0 means the material will be replaced by vacuum.
854
855 ExtraScaleFactorMap::const_iterator iter = m_scalingMap.find(materialName);
856 if (iter != m_scalingMap.end()) {
857 return iter->second;
858 } else {
859 // Check for special names
860 // ALL means everything scaled. Do not scale air or vacuum (unless explicity requested)
861 iter = m_scalingMap.find("ALL");
862 if (iter != m_scalingMap.end() && materialName != "std::Air" && materialName != "std::Vacuum") {
863 return iter->second;
864 }
865 }
866
867 // If not found then return -1 to indicate material is not to be scaled.
868 return -1;
869}
#define endmsg
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double length(const pvec &v)
std::shared_ptr< IRDBRecordset > IRDBRecordset_ptr
Definition of the abstract IRDBRecord interface.
Definition of the abstract IRDBRecordset interface.
bool close_to_zero(T value, T eps=std::numeric_limits< T >::epsilon())
#define scale2
#define scale1
MsgStream & msg() const
The standard message stream.
bool msgLvl(const MSG::Level lvl) const
Test the output level.
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
virtual std::string nodeName() const =0
virtual unsigned int size() const =0
Class to hold various Athena components.
const StoreGateSvc * detStore() const
Class to hold information need to create a material.
void addComponent(const std::string &compName, double fraction)
const std::string & compName(unsigned int i) const
std::vector< std::string > m_components
double fraction(unsigned int i) const
ExtraScaleFactorMap m_scalingMap
const GeoMaterial * extraScaledMaterial(std::string_view materialName, std::string_view newName, const GeoMaterial *origMaterial)
const GeoElement * getElement(const std::string &elementName)
Get element from GeoModel material manager.
const GeoMaterial * getAdditionalMaterial(std::string_view materialName) const
MaterialWeightMap m_weightMap
bool compareDensity(double d1, double d2) const
void addMaterial(GeoMaterial *material)
Add material.
void createMaterial(const MaterialDef &material)
MaterialCompositionMap m_matCompositionMap
const GeoMaterial * getMaterialScaledInternal(const std::string &origMaterialName, double scaleFactor, const std::string &newName="")
void addCompositionTable(const IRDBRecordset_ptr &compositionTable, const std::string &space="")
const GeoMaterial * getCompositeMaterialForVolume(const std::string &newMatName, const double volumeTot, const double volume1, const std::string &matName1, const double volume2, const std::string &matName2)
StoredMaterialManager * retrieveManager(const StoreGateSvc *detStore)
const GeoMaterial * getMaterialScaled(const std::string &origMaterialName, double scaleFactor, const std::string &newName="")
void addScalingTable(const IRDBRecordset_ptr &scalingTable)
double getExtraScaleFactor(std::string_view materialName)
const GeoMaterial * getMaterialForVolume(std::string_view materialName, double volume, std::string_view newName={})
Create and get material with a density calculated to give weight in predefined weight table.
const GeoMaterial * getMaterial(std::string_view materialName)
Get material. First looks for locally defined material and if not found looks in GeoModel material ma...
void addWeightTable(const IRDBRecordset_ptr &weightTable, const std::string &space="")
StoredMaterialManager * m_materialManager
const GeoMaterial * getMaterialInternal(std::string_view materialName)
void addWeightMaterial(const std::string &materialName, const std::string &materialBase, double weight, int linearWeightFlag)
bool hasMaterial(const std::string &materialName) const
InDetMaterialManager(const std::string &managerName, StoreGateSvc *detStore)
const GeoMaterial * getMaterialForVolumeLength(const std::string &materialName, double volume, double length, const std::string &newName="")
The Athena Transient Store API.
T * tryRetrieve() const
Variant of the above which doesn't print a warning message.
This class holds one or more material managers and makes them storeable, under StoreGate.
test if a value is close enough to zero to be an unreliable denominator.
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:148
bool close_to_zero(T value, T eps=std::numeric_limits< T >::epsilon())