Belle II Software  release-08-01-10
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  assert(mat);
141  scaledMat = new G4Material(scaled, mat->GetDensity() * *m_densityScale, mat,
142  mat->GetState(), mat->GetTemperature(), mat->GetPressure());
143  m_materialCache.insert(scaled, scaledMat);
144  }
145  return scaledMat;
146  }
147  return mat;
148  }
149 
150  G4Element* Materials::getElement(const string& name)
151  {
152  initBuilders();
153  G4Element* elm = m_nistElementBuilder->FindOrBuildElement(name);
154  if (!elm) B2ERROR("Element '" << name << "' could not be found");
155  return elm;
156  }
157 
158  G4Material* Materials::createMaterial(const gearbox::Interface& parameters)
159  {
160  GeoMaterial material = createMaterialConfig(parameters);
161  return createMaterial(material);
162  }
163 
164  G4Material* Materials::createMaterial(const GeoMaterial& parameters)
165  {
166  G4Material* oldmat = getMaterial(parameters.getName(), false);
167  if (oldmat) {
168  //We cannot modify or delete materials, so we just use existing ones
169  B2ERROR("Material with name " << parameters.getName() << " already existing");
170  return oldmat;
171  }
172  B2DEBUG(10, "Creating Material " << parameters.getName());
173  //If density is negative or smaller than epsilon we should calculate the
174  //density from the used materials
175  double density = parameters.getDensity() * CLHEP::g / CLHEP::cm3;
176  if (density < 1e-25) {
177  density = 0;
178  for (const GeoMaterialComponent& component : parameters.getComponents()) {
179  //Check if we can calculate density, only works if we just add materials,
180  //elements do not have density
181  if (component.getIselement()) {
182  B2ERROR("createMaterial " << parameters.getName()
183  << ": Cannot calculate density when adding elements, please provde a density");
184  return nullptr;
185  }
186  G4Material* mat = getMaterial(component.getName());
187  if (!mat) {
188  B2ERROR("createMaterial " << parameters.getName() << ": Material '" << component.getName() << "' not found");
189  return nullptr;
190  }
191  density += mat->GetDensity() * component.getFraction();
192  }
193  }
194 
195  //Finally, create Material and add all components
196  G4Material* mat = new G4Material(parameters.getName(), density, parameters.getComponents().size(),
197  (G4State)parameters.getState(), parameters.getTemperature(), parameters.getPressure() * CLHEP::pascal);
198 
199  for (const GeoMaterialComponent& component : parameters.getComponents()) {
200  if (component.getIselement()) {
201  G4Element* cmp = getElement(component.getName());
202  if (!cmp) {
203  B2ERROR("Cannot create material " << parameters.getName() << ": element " << component.getName() << " not found");
204  return nullptr;
205  }
206  mat->AddElement(cmp, component.getFraction());
207  } else {
208  G4Material* cmp = getMaterial(component.getName());
209  if (!cmp) {
210  B2ERROR("Cannot create material " << parameters.getName() << ": material " << component.getName() << " not found");
211 #ifdef __clang_analyzer__
212  continue;
213 #else
214  return nullptr;
215 #endif
216  }
217  mat->AddMaterial(cmp, component.getFraction());
218  }
219  }
220 
221  //add properties
222  mat->SetMaterialPropertiesTable(createProperties(parameters.getProperties()));
223 
224  //Insert into cache and return
225  m_materialCache.insert(parameters.getName(), mat);
226  return mat;
227  }
228 
229  GeoMaterial Materials::createMaterialConfig(const gearbox::Interface& parameters)
230  {
231  //Get straightforward parameters from gearbox
232  string name = parameters.getString("@name");
233  B2DEBUG(10, "Creating Material Config " << name);
234  string stateStr = parameters.getString("state", "undefined");
235  double density = parameters.getDensity("density", 0); // * CLHEP::g / CLHEP::cm3;
236  double temperature = parameters.getDouble("temperature", CLHEP::STP_Temperature);
237  double pressure = parameters.getDouble("pressure", CLHEP::STP_Pressure / CLHEP::pascal);
238 
239  // set the simple ones like they are
240  GeoMaterial material;
241  material.setName(name);
242  material.setPressure(pressure);
243  material.setTemperature(temperature);
244  material.setDensity(density);
245 
246  //Sum up fractions to see if they are normalized
247  double sumFractions(0);
248 
249  // Add all component materials
250  for (const GearDir& component : parameters.getNodes("Components/Material")) {
251  const string componentName = component.getString();
252  double fraction = component.getDouble("@fraction", 1.0);
253  material.addComponent({componentName, false, fraction});
254  sumFractions += fraction;
255  }
256  // Add all component elements
257  for (const GearDir& element : parameters.getNodes("Components/Element")) {
258  const std::string elementName = element.getString();
259  double fraction = element.getDouble("@fraction", 1.0);
260  material.addComponent({elementName, true, fraction});
261  sumFractions += fraction;
262  }
263 
264  //Warn if the fractions are not normalized
265  if (abs(sumFractions - 1) > numeric_limits<double>::epsilon()) {
266  B2WARNING("createMaterial " << name << ": Fractions not normalized, scaling by 1/" << sumFractions);
267  for (GeoMaterialComponent& cmp : material.getComponents()) {
268  cmp.setFraction(cmp.getFraction() / sumFractions);
269  }
270  }
271 
272  //Determine material state
273  boost::to_lower(stateStr);
274  G4State state = kStateUndefined;
275  if (stateStr == "solid") {
276  state = kStateSolid;
277  } else if (stateStr == "liquid") {
278  state = kStateLiquid;
279  } else if (stateStr == "gas") {
280  state = kStateGas;
281  } else if (stateStr != "undefined") {
282  B2WARNING("createMaterial " << name << ": Unknown state '" << stateStr << "', using undefined");
283  }
284  material.setState(state);
285 
286  addProperties(material, parameters);
287  // all done, return material description
288  return material;
289  }
290 
291  G4OpticalSurface* Materials::createOpticalSurface(const gearbox::Interface& parameters)
292  {
293  GeoOpticalSurface surface = createOpticalSurfaceConfig(parameters);
294  return createOpticalSurface(surface);
295  }
296 
297  G4OpticalSurface* Materials::createOpticalSurface(const GeoOpticalSurface& surface)
298  {
299  G4OpticalSurface* optSurf = new G4OpticalSurface(surface.getName(),
300  (G4OpticalSurfaceModel) surface.getModel(),
301  (G4OpticalSurfaceFinish) surface.getFinish(),
302  (G4SurfaceType) surface.getType(),
303  surface.getValue());
304  optSurf->SetMaterialPropertiesTable(createProperties(surface.getProperties()));
305  //m_OptSurfaces.push_back(optSurf);
306  return optSurf;
307  }
308 
309  GeoOpticalSurface Materials::createOpticalSurfaceConfig(const gearbox::Interface& parameters)
310  {
311  string name = parameters.getString("@name", "OpticalSurface");
312  string modelString = parameters.getString("Model", "glisur");
313  string finishString = parameters.getString("Finish", "polished");
314  string typeString = parameters.getString("Type", "dielectric_dielectric");
315  double value = parameters.getDouble("Value", 1.0);
316 
317 #define CHECK_ENUM_VALUE(name,value) if (name##String == #value) { name = value; }
318  G4OpticalSurfaceModel model;
319  boost::to_lower(modelString);
320  CHECK_ENUM_VALUE(model, glisur)
321  else CHECK_ENUM_VALUE(model, unified)
322  else CHECK_ENUM_VALUE(model, LUT)
323  else {
324  B2FATAL("Unknown Optical Surface Model: " << modelString);
325  }
326 
327  G4OpticalSurfaceFinish finish;
328  boost::to_lower(finishString);
329  CHECK_ENUM_VALUE(finish, polished)
330  else CHECK_ENUM_VALUE(finish, polishedfrontpainted)
331  else CHECK_ENUM_VALUE(finish, polishedbackpainted)
332  else CHECK_ENUM_VALUE(finish, ground)
333  else CHECK_ENUM_VALUE(finish, groundfrontpainted)
334  else CHECK_ENUM_VALUE(finish, groundbackpainted)
335  else CHECK_ENUM_VALUE(finish, polishedlumirrorair)
336  else CHECK_ENUM_VALUE(finish, polishedlumirrorglue)
337  else CHECK_ENUM_VALUE(finish, polishedair)
338  else CHECK_ENUM_VALUE(finish, polishedteflonair)
339  else CHECK_ENUM_VALUE(finish, polishedtioair)
340  else CHECK_ENUM_VALUE(finish, polishedtyvekair)
341  else CHECK_ENUM_VALUE(finish, polishedvm2000air)
342  else CHECK_ENUM_VALUE(finish, polishedvm2000glue)
343  else CHECK_ENUM_VALUE(finish, etchedlumirrorair)
344  else CHECK_ENUM_VALUE(finish, etchedlumirrorglue)
345  else CHECK_ENUM_VALUE(finish, etchedair)
346  else CHECK_ENUM_VALUE(finish, etchedteflonair)
347  else CHECK_ENUM_VALUE(finish, etchedtioair)
348  else CHECK_ENUM_VALUE(finish, etchedtyvekair)
349  else CHECK_ENUM_VALUE(finish, etchedvm2000air)
350  else CHECK_ENUM_VALUE(finish, etchedvm2000glue)
351  else CHECK_ENUM_VALUE(finish, groundlumirrorair)
352  else CHECK_ENUM_VALUE(finish, groundlumirrorglue)
353  else CHECK_ENUM_VALUE(finish, groundair)
354  else CHECK_ENUM_VALUE(finish, groundteflonair)
355  else CHECK_ENUM_VALUE(finish, groundtioair)
356  else CHECK_ENUM_VALUE(finish, groundtyvekair)
357  else CHECK_ENUM_VALUE(finish, groundvm2000air)
358  else CHECK_ENUM_VALUE(finish, groundvm2000glue)
359  else {
360  B2FATAL("Unknown Optical Surface Finish: " << finishString);
361  }
362 
363  G4SurfaceType type;
364  boost::to_lower(typeString);
365  CHECK_ENUM_VALUE(type, dielectric_metal)
366  else CHECK_ENUM_VALUE(type, dielectric_dielectric)
367  else CHECK_ENUM_VALUE(type, dielectric_LUT)
368  else CHECK_ENUM_VALUE(type, firsov)
369  else CHECK_ENUM_VALUE(type, x_ray)
370  else {
371  B2FATAL("Unknown Optical Surface Type: " << typeString);
372  }
373 #undef CHECK_ENUM_VALUE
374 
375  GeoOpticalSurface surface(name, model, finish, type, value);
376  addProperties(surface, GearDir(parameters, "Properties"));
377  return surface;
378  }
379 
380  void Materials::clear()
381  {
382  // delete all property tables we have
383  B2DEBUG(50, "Cleaning G4MaterialPropertiesTable");
384  for (G4MaterialPropertiesTable* prop : m_PropTables) delete prop;
385  m_PropTables.clear();
386  // and last but not least: get rid of all materials
387  B2DEBUG(50, "Cleaning G4Materials");
388  G4MaterialTable& materials = *G4Material::GetMaterialTable();
389  for (G4Material* mat : materials) delete mat;
390  materials.clear();
391  B2DEBUG(50, "Cleaning G4Elements");
392  G4ElementTable& elements = *G4Element::GetElementTable();
393  for (G4Element* elm : elements) delete elm;
394  elements.clear();
395  B2DEBUG(50, "Cleaning G4Isotopes");
396  auto& isotopes = const_cast<G4IsotopeTable&>(*G4Isotope::GetIsotopeTable());
397  for (G4Isotope* iso : isotopes) delete iso;
398  isotopes.clear();
399  // delete material and element builder as they keep indices to materials they created :/
400  delete m_nistMaterialBuilder;
401  delete m_nistElementBuilder;
402  m_nistMaterialBuilder = nullptr;
403  m_nistElementBuilder = nullptr;
404  // finally, get rid of the cache, it's invalid now anyway
405  B2DEBUG(50, "Clean up material cache");
406  m_materialCache.clear();
407  }
408 
409  } //geometry namespace
411 } //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.