Belle II Software  release-06-02-00
Materials.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <geometry/Materials.h>
10 #include <framework/logging/Logger.h>
11 #include <framework/gearbox/GearDir.h>
12 #include <framework/gearbox/Unit.h>
13 #include <geometry/dbobjects/GeoMaterial.h>
14 #include <geometry/dbobjects/GeoOpticalSurface.h>
15 
16 #include <memory>
17 #include <limits>
18 #include <boost/algorithm/string.hpp>
19 
20 #include <G4Material.hh>
21 #include <G4Element.hh>
22 #include <G4NistManager.hh>
23 #include <G4OpticalSurface.hh>
24 #include <G4MaterialPropertiesTable.hh>
25 
26 #include "CLHEP/Units/PhysicalConstants.h"
27 
28 using namespace std;
29 
30 namespace Belle2 {
35  namespace geometry {
36  namespace {
41  template<class T> void addProperties(T& object, const gearbox::Interface& parameters)
42  {
43  // check if we have properties defined for this material
44  if (parameters.getNumberNodes("Property") > 0) {
45  //Apparantly we have properties, so lets add them to the Material
46  for (const GearDir& property : parameters.getNodes("Property")) {
47  string name;
48  try {
49  name = property.getString("@name");
50  } catch (gearbox::PathEmptyError&) {
51  B2ERROR("Property at " << property.getPath() << " does not have a name attribute, ignoring");
52  continue;
53  }
54  const double conversionFactor = Unit::convertValue(1, property.getString("@unit", "GeV"));
55  unsigned int nValues = property.getNumberNodes("value");
56  std::vector<double> energies(nValues, 0);
57  std::vector<double> values(nValues, 0);
58  for (unsigned int i = 0; i < nValues; ++i) {
59  GearDir value(property, "value", i + 1);
60  energies[i] = value.getDouble("@energy") * conversionFactor;
61  values[i] = value.getDouble();
62  }
63  object.addProperty({name, energies, values});
64  }
65  }
66  }
67  }
68 
69  G4MaterialPropertiesTable* Materials::createProperties(const std::vector<GeoMaterialProperty>& props)
70  {
71  if (!props.size()) return nullptr;
72  auto* table = new G4MaterialPropertiesTable();
73  m_PropTables.push_back(table);
74  for (const GeoMaterialProperty& prop : props) {
75  std::vector<double> energies = prop.getEnergies();
76  std::vector<double> values = prop.getValues();
77  const size_t n = values.size();
78  // convert energies to Geant4 Units
79  for (double& energy : energies) energy *= CLHEP::GeV / Unit::GeV;
80  table->AddProperty(prop.getName().c_str(), energies.data(), values.data(), n);
81  }
82  return table;
83  }
84 
85  Materials& Materials::getInstance()
86  {
87  static unique_ptr<Materials> instance(new Materials());
88  return *instance;
89  }
90 
91  Materials::~Materials()
92  {
93  clear();
94  }
95 
96  void Materials::initBuilders()
97  {
98  if (!m_nistMaterialBuilder) {
99  B2DEBUG(50, "Creating new Nist Builder instances");
100  m_nistElementBuilder = new G4NistElementBuilder(0);
101  m_nistMaterialBuilder = new G4NistMaterialBuilder(m_nistElementBuilder, 0);
102  }
103  }
104 
105  G4Material* Materials::findMaterial(const string& name)
106  {
107  G4Material* mat{nullptr};
108  if (m_materialCache.retrieve(name, mat)) {
109  return mat;
110  }
111  initBuilders();
112  mat = m_nistMaterialBuilder->FindOrBuildMaterial(name);
113 
114  //Try different combinations of the Material name to fallback to predefined G4 Elements
115  if (!mat && name.substr(0, 3) != "G4_") {
116  //Mainly for materials from single elements, e.g. G4_Al, G4_Si
117  mat = m_nistMaterialBuilder->FindOrBuildMaterial("G4_" + name);
118  //For predefined materials, e.g. G4_AIR, G4_TEFLON
119  if (!mat) mat = m_nistMaterialBuilder->FindOrBuildMaterial("G4_" + boost::to_upper_copy(name));
120  }
121 
122  //Insert into cache
123  m_materialCache.insert(name, mat);
124  return mat;
125  }
126 
127  G4Material* Materials::getMaterial(const string& name, bool showErrors)
128  {
129  G4Material* mat = findMaterial(name);
130  if (!mat && showErrors) B2ERROR("Material '" << name << "' could not be found");
131  // There is a chance that we want to scale densities of all materials for studies.
132  // Now we have the original so we can look to the scaled one or create it if it's missing
133  if (m_densityScale) {
134  if (m_ignoreScaling.count(name) > 0) return mat;
135  if (name.substr(0, 7) == "scaled:")
136  B2WARNING("Requested an already scaled material ... scaling once more" << LogVar("material", name));
137  std::string scaled = "scaled:" + std::to_string(*m_densityScale) + ":" + name;
138  G4Material* scaledMat = findMaterial(scaled);
139  if (!scaledMat) {
140  scaledMat = new G4Material(scaled, mat->GetDensity() * *m_densityScale, mat,
141  mat->GetState(), mat->GetTemperature(), mat->GetPressure());
142  m_materialCache.insert(scaled, scaledMat);
143  }
144  return scaledMat;
145  }
146  return mat;
147  }
148 
149  G4Element* Materials::getElement(const string& name)
150  {
151  initBuilders();
152  G4Element* elm = m_nistElementBuilder->FindOrBuildElement(name);
153  if (!elm) B2ERROR("Element '" << name << "' could not be found");
154  return elm;
155  }
156 
157  G4Material* Materials::createMaterial(const gearbox::Interface& parameters)
158  {
159  GeoMaterial material = createMaterialConfig(parameters);
160  return createMaterial(material);
161  }
162 
163  G4Material* Materials::createMaterial(const GeoMaterial& parameters)
164  {
165  G4Material* oldmat = getMaterial(parameters.getName(), false);
166  if (oldmat) {
167  //We cannot modify or delete materials, so we just use existing ones
168  B2ERROR("Material with name " << parameters.getName() << " already existing");
169  return oldmat;
170  }
171  B2DEBUG(10, "Creating Material " << parameters.getName());
172  //If density is negative or smaller than epsilon we should calculate the
173  //density from the used materials
174  double density = parameters.getDensity() * CLHEP::g / CLHEP::cm3;
175  if (density < 1e-25) {
176  density = 0;
177  for (const GeoMaterialComponent& component : parameters.getComponents()) {
178  //Check if we can calculate density, only works if we just add materials,
179  //elements do not have density
180  if (component.getIselement()) {
181  B2ERROR("createMaterial " << parameters.getName()
182  << ": Cannot calculate density when adding elements, please provde a density");
183  return nullptr;
184  }
185  G4Material* mat = getMaterial(component.getName());
186  if (!mat) {
187  B2ERROR("createMaterial " << parameters.getName() << ": Material '" << component.getName() << "' not found");
188  return nullptr;
189  }
190  density += mat->GetDensity() * component.getFraction();
191  }
192  }
193 
194  //Finally, create Material and add all components
195  G4Material* mat = new G4Material(parameters.getName(), density, parameters.getComponents().size(),
196  (G4State)parameters.getState(), parameters.getTemperature(), parameters.getPressure() * CLHEP::pascal);
197 
198  for (const GeoMaterialComponent& component : parameters.getComponents()) {
199  if (component.getIselement()) {
200  G4Element* cmp = getElement(component.getName());
201  if (!cmp) {
202  B2ERROR("Cannot create material " << parameters.getName() << ": element " << component.getName() << " not found");
203  return nullptr;
204  }
205  mat->AddElement(cmp, component.getFraction());
206  } else {
207  G4Material* cmp = getMaterial(component.getName());
208  if (!cmp) {
209  B2ERROR("Cannot create material " << parameters.getName() << ": material " << component.getName() << " not found");
210  return nullptr;
211  }
212  mat->AddMaterial(cmp, component.getFraction());
213  }
214  }
215 
216  //add properties
217  mat->SetMaterialPropertiesTable(createProperties(parameters.getProperties()));
218 
219  //Insert into cache and return
220  m_materialCache.insert(parameters.getName(), mat);
221  return mat;
222  }
223 
224  GeoMaterial Materials::createMaterialConfig(const gearbox::Interface& parameters)
225  {
226  //Get straightforward parameters from gearbox
227  string name = parameters.getString("@name");
228  B2DEBUG(10, "Creating Material Config " << name);
229  string stateStr = parameters.getString("state", "undefined");
230  double density = parameters.getDensity("density", 0); // * CLHEP::g / CLHEP::cm3;
231  double temperature = parameters.getDouble("temperature", CLHEP::STP_Temperature);
232  double pressure = parameters.getDouble("pressure", CLHEP::STP_Pressure / CLHEP::pascal);
233 
234  // set the simple ones like they are
235  GeoMaterial material;
236  material.setName(name);
237  material.setPressure(pressure);
238  material.setTemperature(temperature);
239  material.setDensity(density);
240 
241  //Sum up fractions to see if they are normalized
242  double sumFractions(0);
243 
244  // Add all component materials
245  for (const GearDir& component : parameters.getNodes("Components/Material")) {
246  const string componentName = component.getString();
247  double fraction = component.getDouble("@fraction", 1.0);
248  material.addComponent({componentName, false, fraction});
249  sumFractions += fraction;
250  }
251  // Add all component elements
252  for (const GearDir& element : parameters.getNodes("Components/Element")) {
253  const std::string elementName = element.getString();
254  double fraction = element.getDouble("@fraction", 1.0);
255  material.addComponent({elementName, true, fraction});
256  sumFractions += fraction;
257  }
258 
259  //Warn if the fractions are not normalized
260  if (abs(sumFractions - 1) > numeric_limits<double>::epsilon()) {
261  B2WARNING("createMaterial " << name << ": Fractions not normalized, scaling by 1/" << sumFractions);
262  for (GeoMaterialComponent& cmp : material.getComponents()) {
263  cmp.setFraction(cmp.getFraction() / sumFractions);
264  }
265  }
266 
267  //Determine material state
268  boost::to_lower(stateStr);
269  G4State state = kStateUndefined;
270  if (stateStr == "solid") {
271  state = kStateSolid;
272  } else if (stateStr == "liquid") {
273  state = kStateLiquid;
274  } else if (stateStr == "gas") {
275  state = kStateGas;
276  } else if (stateStr != "undefined") {
277  B2WARNING("createMaterial " << name << ": Unknown state '" << stateStr << "', using undefined");
278  }
279  material.setState(state);
280 
281  addProperties(material, parameters);
282  // all done, return material description
283  return material;
284  }
285 
286  G4OpticalSurface* Materials::createOpticalSurface(const gearbox::Interface& parameters)
287  {
288  GeoOpticalSurface surface = createOpticalSurfaceConfig(parameters);
289  return createOpticalSurface(surface);
290  }
291 
292  G4OpticalSurface* Materials::createOpticalSurface(const GeoOpticalSurface& surface)
293  {
294  G4OpticalSurface* optSurf = new G4OpticalSurface(surface.getName(),
295  (G4OpticalSurfaceModel) surface.getModel(),
296  (G4OpticalSurfaceFinish) surface.getFinish(),
297  (G4SurfaceType) surface.getType(),
298  surface.getValue());
299  optSurf->SetMaterialPropertiesTable(createProperties(surface.getProperties()));
300  //m_OptSurfaces.push_back(optSurf);
301  return optSurf;
302  }
303 
304  GeoOpticalSurface Materials::createOpticalSurfaceConfig(const gearbox::Interface& parameters)
305  {
306  string name = parameters.getString("@name", "OpticalSurface");
307  string modelString = parameters.getString("Model", "glisur");
308  string finishString = parameters.getString("Finish", "polished");
309  string typeString = parameters.getString("Type", "dielectric_dielectric");
310  double value = parameters.getDouble("Value", 1.0);
311 
312 #define CHECK_ENUM_VALUE(name,value) if (name##String == #value) { name = value; }
313  G4OpticalSurfaceModel model;
314  boost::to_lower(modelString);
315  CHECK_ENUM_VALUE(model, glisur)
316  else CHECK_ENUM_VALUE(model, unified)
317  else CHECK_ENUM_VALUE(model, LUT)
318  else {
319  B2FATAL("Unknown Optical Surface Model: " << modelString);
320  }
321 
322  G4OpticalSurfaceFinish finish;
323  boost::to_lower(finishString);
324  CHECK_ENUM_VALUE(finish, polished)
325  else CHECK_ENUM_VALUE(finish, polishedfrontpainted)
326  else CHECK_ENUM_VALUE(finish, polishedbackpainted)
327  else CHECK_ENUM_VALUE(finish, ground)
328  else CHECK_ENUM_VALUE(finish, groundfrontpainted)
329  else CHECK_ENUM_VALUE(finish, groundbackpainted)
330  else CHECK_ENUM_VALUE(finish, polishedlumirrorair)
331  else CHECK_ENUM_VALUE(finish, polishedlumirrorglue)
332  else CHECK_ENUM_VALUE(finish, polishedair)
333  else CHECK_ENUM_VALUE(finish, polishedteflonair)
334  else CHECK_ENUM_VALUE(finish, polishedtioair)
335  else CHECK_ENUM_VALUE(finish, polishedtyvekair)
336  else CHECK_ENUM_VALUE(finish, polishedvm2000air)
337  else CHECK_ENUM_VALUE(finish, polishedvm2000glue)
338  else CHECK_ENUM_VALUE(finish, etchedlumirrorair)
339  else CHECK_ENUM_VALUE(finish, etchedlumirrorglue)
340  else CHECK_ENUM_VALUE(finish, etchedair)
341  else CHECK_ENUM_VALUE(finish, etchedteflonair)
342  else CHECK_ENUM_VALUE(finish, etchedtioair)
343  else CHECK_ENUM_VALUE(finish, etchedtyvekair)
344  else CHECK_ENUM_VALUE(finish, etchedvm2000air)
345  else CHECK_ENUM_VALUE(finish, etchedvm2000glue)
346  else CHECK_ENUM_VALUE(finish, groundlumirrorair)
347  else CHECK_ENUM_VALUE(finish, groundlumirrorglue)
348  else CHECK_ENUM_VALUE(finish, groundair)
349  else CHECK_ENUM_VALUE(finish, groundteflonair)
350  else CHECK_ENUM_VALUE(finish, groundtioair)
351  else CHECK_ENUM_VALUE(finish, groundtyvekair)
352  else CHECK_ENUM_VALUE(finish, groundvm2000air)
353  else CHECK_ENUM_VALUE(finish, groundvm2000glue)
354  else {
355  B2FATAL("Unknown Optical Surface Finish: " << finishString);
356  }
357 
358  G4SurfaceType type;
359  boost::to_lower(typeString);
360  CHECK_ENUM_VALUE(type, dielectric_metal)
361  else CHECK_ENUM_VALUE(type, dielectric_dielectric)
362  else CHECK_ENUM_VALUE(type, dielectric_LUT)
363  else CHECK_ENUM_VALUE(type, firsov)
364  else CHECK_ENUM_VALUE(type, x_ray)
365  else {
366  B2FATAL("Unknown Optical Surface Type: " << typeString);
367  }
368 #undef CHECK_ENUM_VALUE
369 
370  GeoOpticalSurface surface(name, model, finish, type, value);
371  addProperties(surface, GearDir(parameters, "Properties"));
372  return surface;
373  }
374 
375  void Materials::clear()
376  {
377  // delete all property tables we have
378  B2DEBUG(50, "Cleaning G4MaterialPropertiesTable");
379  for (G4MaterialPropertiesTable* prop : m_PropTables) delete prop;
380  m_PropTables.clear();
381  // and last but not least: get rid of all materials
382  B2DEBUG(50, "Cleaning G4Materials");
383  G4MaterialTable& materials = *G4Material::GetMaterialTable();
384  for (G4Material* mat : materials) delete mat;
385  materials.clear();
386  B2DEBUG(50, "Cleaning G4Elements");
387  G4ElementTable& elements = *G4Element::GetElementTable();
388  for (G4Element* elm : elements) delete elm;
389  elements.clear();
390  B2DEBUG(50, "Cleaning G4Isotopes");
391  auto& isotopes = const_cast<G4IsotopeTable&>(*G4Isotope::GetIsotopeTable());
392  for (G4Isotope* iso : isotopes) delete iso;
393  isotopes.clear();
394  // delete material and element builder as they keep indices to materials they created :/
395  delete m_nistMaterialBuilder;
396  delete m_nistElementBuilder;
397  m_nistMaterialBuilder = nullptr;
398  m_nistElementBuilder = nullptr;
399  // finally, get rid of the cache, it's invalid now anyway
400  B2DEBUG(50, "Clean up material cache");
401  m_materialCache.clear();
402  }
403 
404  } //geometry namespace
406 } //Belle2 namespace
GearDir is the basic class used for accessing the parameter store.
Definition: GearDir.h:31
Component of a material.
Property of a material.
Class to represent a material informaion in the Database.
Definition: GeoMaterial.h:22
void setPressure(double pressure)
set the pressure of the material (in default framework units
Definition: GeoMaterial.h:35
void setState(int state)
set the state of the material
Definition: GeoMaterial.h:29
void addComponent(const GeoMaterialComponent &component)
add a component to the material.
Definition: GeoMaterial.h:37
std::vector< GeoMaterialComponent > & getComponents()
get all components
Definition: GeoMaterial.h:51
void setName(const std::string &name)
set the name of the material
Definition: GeoMaterial.h:27
void setTemperature(double temperature)
set the temperature of the material (in default framework units
Definition: GeoMaterial.h:33
void setDensity(double density)
set the density of the material (in default framework units
Definition: GeoMaterial.h:31
Represent an optical finish of a surface.
int getFinish() const
get finish of the surface
const std::string & getName() const
get name of the optical surface
double getValue() const
get value for the surface condition
const std::vector< GeoMaterialProperty > & getProperties() const
get all properties
int getType() const
get type of the surface
int getModel() const
get model for the surface
Exception to be thrown in case of an empty result.
Definition: Interface.h:35
Thin wrapper around the Geant4 Material system.
Definition: Materials.h:48
Class to store variables with their name which were sent to the logging service.
Abstract base class for different kinds of events.