Belle II Software development
GeometryManager.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 <framework/logging/Logger.h>
10#include <framework/gearbox/GearDir.h>
11#include <framework/database/IntervalOfValidity.h>
12#include <geometry/GeometryManager.h>
13#include <geometry/Materials.h>
14#include <geometry/CreatorManager.h>
15#include <geometry/CreatorBase.h>
16#include <geometry/utilities.h>
17#include <geometry/dbobjects/GeoConfiguration.h>
18#include <geometry/bfieldmap/BFieldMap.h>
19#include <geometry/bfieldmap/BFieldFrameworkInterface.h>
20#include <framework/dbobjects/MagneticField.h>
21#include <framework/database/DBStore.h>
22
23#include "G4Box.hh"
24#include "G4ThreeVector.hh"
25#include "G4LogicalVolume.hh"
26#include "G4PVPlacement.hh"
27
28#include "G4RunManager.hh"
29#include "G4GeometryManager.hh"
30#include "G4PhysicalVolumeStore.hh"
31#include "G4LogicalVolumeStore.hh"
32#include "G4SolidStore.hh"
33#include "G4RegionStore.hh"
34#include "G4SurfaceProperty.hh"
35#include "G4LogicalBorderSurface.hh"
36#include "G4LogicalSkinSurface.hh"
37#include "G4VisAttributes.hh"
38#include "G4VoxelLimits.hh"
39
40//VGM stuff
41#include "Geant4GM/volumes/Factory.h"
42#include "RootGM/volumes/Factory.h"
43#include "VGM/volumes/IPlacement.h"
44#include "TGeoManager.h"
45
46#include <memory>
47
48using namespace std;
49
50namespace Belle2 {
55 using namespace gearbox;
56
57 namespace {
67 double getTopMinSize(const std::string& name, EAxis axis, G4LogicalVolume* volume, double current)
68 {
69 G4VoxelLimits dummyLimits;
70 double extent{0};
71 for (size_t i = 0; i < volume->GetNoDaughters(); ++i) {
72 G4VPhysicalVolume* daughter = volume->GetDaughter(i);
73 G4AffineTransform trans(daughter->GetRotation(), daughter->GetTranslation());
74 double vmin{0}, vmax{0};
75 daughter->GetLogicalVolume()->GetSolid()->CalculateExtent((EAxis) axis, dummyLimits, trans, vmin, vmax);
76 extent = std::max({extent, std::fabs(vmin), std::fabs(vmax)});
77 }
78 B2DEBUG(100, "Current global volume size in " << name << ": " << std::fixed
79 << std::setprecision(std::numeric_limits<double>::max_digits10)
80 << current << ", needed: " << extent);
81 if (current < extent && (extent - current) > Unit::um) {
82 if (current > 0) {
83 B2WARNING("Global volume not large enough in " << name << " direction, enlarging from "
84 << current << " mm to " << extent << " mm");
85 } else {
86 B2DEBUG(10, "Setting global volume size to " << name << "= +-" << extent << " mm");
87 }
88 return extent;
89 }
90 return current;
91 }
92 }
93
94 namespace geometry {
95
97 {
98 static unique_ptr<GeometryManager> instance(new GeometryManager());
99 return *instance;
100 }
101
103 {
104 B2DEBUG(50, "Cleaning up Geometry");
105 for (G4VisAttributes* visAttr : m_VisAttributes) delete visAttr;
106 m_VisAttributes.clear();
107 for (CreatorBase* creator : m_creators) delete creator;
108 m_creators.clear();
109 m_topVolume = nullptr;
110 //Clean up existing Geometry
111 G4GeometryManager::GetInstance()->OpenGeometry();
112 G4PhysicalVolumeStore::Clean();
113 G4LogicalVolumeStore::Clean();
114 G4SolidStore::Clean();
115 G4LogicalBorderSurface::CleanSurfaceTable();
116 G4LogicalSkinSurface::CleanSurfaceTable();
117 G4SurfaceProperty::CleanSurfacePropertyTable();
118 //And finally clean up materials
120 }
121
123 {
124 std::string detectorName;
125 try {
126 detectorName = detectorDir.getString("Name");
127 B2DEBUG(50, "Creating geometry for detector: " << detectorName);
128 } catch (gearbox::PathEmptyError& e) {
129 B2FATAL("Could not read detector name, make sure gearbox is connected and "
130 << detectorDir.getPath() << " points to the geometry description");
131 }
132
133 const double globalWidth = detectorDir.getLength("Global/width", 0);
134 const double globalHeight = detectorDir.getLength("Global/height", 0);
135 const double globalLength = detectorDir.getLength("Global/length", 0);
136 const std::string globalMaterial = detectorDir.getString("Global/material", "Air");
137
138 GeoConfiguration config(detectorName, globalWidth, globalHeight, globalLength, globalMaterial);
139
140 // Add materials
141 Materials& materials = Materials::getInstance();
142 for (const GearDir& matlist : detectorDir.getNodes("Materials")) {
143 for (const GearDir& mat : matlist.getNodes("Material")) {
144 GeoMaterial material = materials.createMaterialConfig(mat);
145 config.addMaterial(material);
146 }
147 }
148
149 std::set<std::string> componentNames = m_components;
150 std::set<std::string> excludedNames = m_excluded;
151 std::set<std::string> additionalNames = m_additional;
152 if (!m_components.empty() && !m_additional.empty()) {
153 B2WARNING("Additional components are ignored when a list of components is provided.");
154 }
155
156 //Now create all subcomponents
157 for (const GearDir& component : detectorDir.getNodes("DetectorComponent")) {
158 string name;
159 string creatorName;
160 try {
161 name = component.getString("@name");
162 creatorName = component.getString("Creator");
163 } catch (gearbox::PathEmptyError& e) {
164 B2ERROR("Could not find required element Name or Creator for " << component.getPath());
165 continue;
166 }
167 const bool isDefault = component.getBool("@isDefault", true);
168 //Remove this name from the list of selected, excluded or additional components. At
169 //the end there should be nothing left in these lists
170 componentNames.erase(name);
171 excludedNames.erase(name);
172 additionalNames.erase(name);
173 if (!m_components.empty() && m_components.count(name) == 0) {
174 B2DEBUG(50, "DetectorComponent " << name << " not in list of components, skipping");
175 continue;
176 }
177 if (m_components.empty() && !isDefault) {
178 if (m_additional.count(name) == 0) {
179 B2DEBUG(50, "DectorComponent " << name << " is not enabled by default, skipping");
180 continue;
181 } else {
182 B2DEBUG(50, "DectorComponent " << name << " is enabled in addition to the default components");
183 }
184 }
185 if (!m_excluded.empty() && m_excluded.count(name) > 0) {
186 B2DEBUG(10, "DetectorComponent " << name << " in list of excluded components, skipping");
187 continue;
188 }
189
190 string libraryName = component.getString("Creator/@library", "");
191 if (!iov.empty()) {
192 CreatorBase* creator = CreatorManager::getCreator(creatorName, libraryName);
193 if (creator) {
194 creator->createPayloads(GearDir(component, "Content"), iov);
195 } else {
196 B2ERROR("Could not load creator " << creatorName << " from " << libraryName);
197 }
198 }
199 config.addComponent({name, creatorName, libraryName});
200 }
201
202 //If there are still names left in the componentNames, excludedNames or
203 //additionalNames there is probably an typo in the respective component
204 //list. Throw an error for each name left using a small lambda function
205 auto checkRemaining = [](const std::string & type, const std::set<std::string>& remainingNames) {
206 for (const std::string& name : remainingNames) {
207 B2ERROR("Geometry '" << name << "' is specified in list of "
208 << type << " but could not be found");
209 }
210 };
211
212 checkRemaining("components", componentNames);
213 checkRemaining("excluded components", excludedNames);
214 checkRemaining("additional components", additionalNames);
215
216 return config;
217 }
218
220 {
222 createGeometry(config, type, false);
223 }
224
226 {
227 // remove the old geometry
228 clear();
229
230 // if we don't use the DB make sure the magnetic field is properly set
231 // by adding it as a fake database payload
233 if (!useDB) {
234 auto* fieldmap = new MagneticField();
235 fieldmap->addComponent(new BFieldFrameworkInterface());
236 DBStore::Instance().addConstantOverride("MagneticField", fieldmap, false);
237 }
238
239 //Let Geant4 know that we "modified" the geometry
240 G4RunManager* runManager = G4RunManager::GetRunManager();
241 if (runManager) runManager->ReinitializeGeometry(true, true);
242
243 // create new geometry
244 B2DEBUG(10, "Creating geometry for detector: " << config.getName());
245
246 // create Materials
247 Materials& materials = Materials::getInstance();
248 for (const GeoMaterial& mat : config.getMaterials()) {
249 materials.createMaterial(mat);
250 }
251
252 //Now set Top volume. Be aware that Geant4 uses "half size" so the size
253 //will be in each direction even though the member name suggests a total size
254 G4Material* top_mat = Materials::get(config.getGlobalMaterial());
255 double xHalfLength = config.getGlobalWidth() / Unit::cm * CLHEP::cm;
256 double yHalfLength = config.getGlobalHeight() / Unit::cm * CLHEP::cm;
257 double zHalfLength = config.getGlobalLength() / Unit::cm * CLHEP::cm;
258 //Geant4 doesn't like negative dimensions so lets make sure it gets at
259 //least a positive value. We'll rezize the box anyway if the length is
260 //zero so this is not important now
261 G4Box* top_box = new G4Box("Top", xHalfLength > 0 ? xHalfLength : 1,
262 yHalfLength > 0 ? yHalfLength : 1,
263 zHalfLength > 0 ? zHalfLength : 1);
264 G4LogicalVolume* top_log = new G4LogicalVolume(top_box, top_mat, "Top", nullptr, nullptr, nullptr);
265 setVisibility(*top_log, false);
266 m_topVolume = new G4PVPlacement(nullptr, G4ThreeVector(), top_log, "Top", nullptr, false, 0);
267 B2DEBUG(10, "Created top volume with x= +-" << config.getGlobalWidth() << " cm, y= +-"
268 << config.getGlobalHeight() << " cm, z= +-" << config.getGlobalLength() << " cm");
269
270
271 auto getDensityScale = [this](const std::string & name) {
272 std::optional<double> scale;
273 if (auto it = m_densityScaling.find(name); it != m_densityScaling.end()) {
274 scale = it->second;
275 }
276 return scale;
277 };
278 auto globalScale = getDensityScale("*");
279
280 for (const GeoComponent& component : config.getComponents()) {
281 CreatorBase* creator = CreatorManager::getCreator(component.getCreator(), component.getLibrary());
282 if (creator) {
283 // Do we want to scale the density for this or all components?
284 auto componentScale = getDensityScale(component.getName());
285 if (componentScale or globalScale) {
286 double scale = globalScale ? *globalScale : 1.0;
287 if (componentScale) scale *= *componentScale;
289 }
290 // remember how many we had to print the difference
291 int oldSolids = G4SolidStore::GetInstance()->size();
292 int oldLogical = G4LogicalVolumeStore::GetInstance()->size();
293 int oldPhysical = G4PhysicalVolumeStore::GetInstance()->size();
294 try {
295 if (!useDB) throw CreatorBase::DBNotImplemented();
296 creator->createFromDB(component.getName(), *top_log, type);
297 B2DEBUG(50, "called creator " << component.getCreator() << " to create component " << component.getName() << " from DB");
298
299 } catch (CreatorBase::DBNotImplemented& e) {
300 GearDir parameters = Gearbox::getInstance().getDetectorComponent(component.getName());
301 creator->create(parameters, *top_log, type);
302 B2DEBUG(50, "called creator " << component.getCreator() << " to create component " << component.getName() << " from Gearbox");
303 }
304 int newSolids = G4SolidStore::GetInstance()->size() - oldSolids;
305 int newLogical = G4LogicalVolumeStore::GetInstance()->size() - oldLogical;
306 int newPhysical = G4PhysicalVolumeStore::GetInstance()->size() - oldPhysical;
307 B2DEBUG(50, "DetectorComponent " << component.getName() << " created " << newSolids
308 << " solids, " << newLogical << " logical volumes and "
309 << newPhysical << " physical volumes");
310 m_creators.push_back(creator);
311 //Done creating things, please reset density scaling
313 if (m_assignRegions) {
314 //Automatically assign a region with the creator name to all volumes
315 G4Region* region {nullptr};
316 //We loop over all children of the top volume and check whether they
317 //have the correct region assigned
318 for (size_t i = 0; i < top_log->GetNoDaughters(); ++i) {
319 G4LogicalVolume* vol = top_log->GetDaughter(i)->GetLogicalVolume();
320 //We only assign a region if there is not already one
321 if (!vol->GetRegion()) {
322 //Ok, new region, create or get one if not already done and assign it
323 if (!region) region = G4RegionStore::GetInstance()->FindOrCreateRegion(component.getName());
324 vol->SetRegion(region);
325 //And propagate the region to all child volumes
326 region->AddRootLogicalVolume(vol);
327 }
328 }
329 }
330 }
331 }
332
333 int newSolids = G4SolidStore::GetInstance()->size();
334 int newLogical = G4LogicalVolumeStore::GetInstance()->size();
335 int newPhysical = G4PhysicalVolumeStore::GetInstance()->size();
336 B2DEBUG(50, "Created a total of " << newSolids << " solids, " << newLogical
337 << " logical volumes and " << newPhysical << " physical volumes");
338
339 //Enlarge top volume to always encompass all volumes
340 top_box->SetXHalfLength(getTopMinSize("x", kXAxis, top_log, xHalfLength));
341 top_box->SetYHalfLength(getTopMinSize("y", kYAxis, top_log, yHalfLength));
342 top_box->SetZHalfLength(getTopMinSize("z", kZAxis, top_log, zHalfLength));
343
344 B2DEBUG(50, "Optimizing geometry and creating lookup tables ...");
345 G4GeometryManager::GetInstance()->CloseGeometry(true, LogSystem::Instance().isLevelEnabled(LogConfig::c_Debug, 200, PACKAGENAME()));
346 }
347
349 {
350 if (!m_topVolume) {
351 B2ERROR("No Geometry found, please create a geometry before converting it to ROOT::TGeo");
352 return;
353 }
354
355 Geant4GM::Factory g4Factory;
356 if (LogSystem::Instance().isLevelEnabled(LogConfig::c_Debug, 200, PACKAGENAME())) {
357 g4Factory.SetDebug(1);
358 }
359 g4Factory.Import(m_topVolume);
360 RootGM::Factory rtFactory;
361 rtFactory.SetIgnore(1);
362 if (LogSystem::Instance().isLevelEnabled(LogConfig::c_Debug, 200, PACKAGENAME())) {
363 rtFactory.SetDebug(1);
364 }
365 g4Factory.Export(&rtFactory);
366 gGeoManager->CloseGeometry();
367 delete g4Factory.Top();
368 delete rtFactory.Top();
369 }
370
372 {
373 m_VisAttributes.push_back(new G4VisAttributes());
374 return m_VisAttributes.back();
375 }
376 }
378} //Belle2 namespace
Simple BFieldComponent to just wrap the existing BFieldMap with the new BFieldManager.
void clear()
Clear the existing components.
Definition: BFieldMap.cc:33
static BFieldMap & Instance()
Static method to get a reference to the BFieldMap instance.
Definition: BFieldMap.cc:15
GearDir is the basic class used for accessing the parameter store.
Definition: GearDir.h:31
virtual std::string getString(const std::string &path="") const noexcept(false) override
Get the parameter path as a string.
Definition: GearDir.h:69
Describe one component of the Geometry.
Definition: GeoComponent.h:19
configuration of the geometry
Class to represent a material informaion in the Database.
Definition: GeoMaterial.h:22
A class that describes the interval of experiments/runs for which an object in the database is valid.
@ c_Debug
Debug: for code development.
Definition: LogConfig.h:26
static LogSystem & Instance()
Static method to get a reference to the LogSystem instance.
Definition: LogSystem.cc:31
Magnetic field map.
Definition: MagneticField.h:32
static const double um
[micrometers]
Definition: Unit.h:71
static const double cm
Standard units with the value = 1.
Definition: Unit.h:47
std::string getPath() const
Return path of the current interface.
Definition: Interface.h:70
double getLength(const std::string &path="") const noexcept(false)
Get the parameter path as a double converted to the standard length unit.
Definition: Interface.h:259
std::vector< GearDir > getNodes(const std::string &path="") const
Get vector of GearDirs which point to all the nodes the given path evaluates to.
Definition: Interface.cc:21
Pure virtual base class for all geometry creators.
Definition: CreatorBase.h:28
virtual void createFromDB(const std::string &name, G4LogicalVolume &topVolume, GeometryTypes type)
Function to create the geometry from the Database.
Definition: CreatorBase.cc:17
virtual void create(const GearDir &content, G4LogicalVolume &topVolume, GeometryTypes type)=0
Function to actually create the geometry, has to be overridden by derived classes.
virtual void createPayloads(const GearDir &content, const IntervalOfValidity &iov)
Function to create the geometry database.
Definition: CreatorBase.cc:24
static CreatorBase * getCreator(const std::string &name, const std::string &library="")
Return a new instance of a creator with the given name.
Class to manage the creation and conversion of the geometry.
G4VisAttributes * newVisAttributes()
Create an anonymous G4VisAttributes for an existing G4LogicalVolume.
G4VPhysicalVolume * m_topVolume
Pointer to the top volume of the native geometry description.
bool m_assignRegions
Bool to indicate whether we automatically assign a region with the creator name to all volumes create...
std::vector< G4VisAttributes * > m_VisAttributes
List of visualization attributes.
GeoConfiguration createGeometryConfig(const GearDir &detectorDir, const IntervalOfValidity &iov)
Create Geometry configuration object.
std::set< std::string > m_components
List of names of components to be created, all other components will be ignored.
std::set< std::string > m_additional
List of additional components to be added to the default set of components.
void createGeometry(const GearDir &params, GeometryTypes type=FullGeometry)
Create Geometry.
std::set< std::string > m_excluded
List of names of components to be excluded from creation.
std::vector< CreatorBase * > m_creators
List of all creators, to be freed when geometry is destucted.
void clear()
Delete the existing Geant4 Geometry.
static GeometryManager & getInstance()
Return a reference to the instance.
GeometryManager()
Default constructor declared private since class is a Singleton.
void createTGeoRepresentation()
Create a TGeo representation of the native geometry description.
std::map< std::string, double > m_densityScaling
Scaling factors for densities.
Thin wrapper around the Geant4 Material system.
Definition: Materials.h:48
static G4Material * get(const std::string &name)
Find given material.
Definition: Materials.h:63
GeoMaterial createMaterialConfig(const gearbox::Interface &parameters)
Create Material from XML description.
Definition: Materials.cc:233
void resetDensityScale()
Disable density scaling.
Definition: Materials.h:142
void setDensityScale(double scale)
Set the density scale to the given value.
Definition: Materials.h:136
static Materials & getInstance()
Get a reference to the singleton instance.
Definition: Materials.cc:85
G4Material * createMaterial(const gearbox::Interface &parameters)
Create a material from the parameters specified by parameters.
Definition: Materials.cc:158
void clear()
Clear all existing materials.
Definition: Materials.cc:384
static Gearbox & getInstance()
Return reference to the Gearbox instance.
Definition: Gearbox.cc:81
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:28
GearDir getDetectorComponent(const std::string &component)
Return GearDir representing a given DetectorComponent.
Definition: Gearbox.cc:314
void addConstantOverride(const std::string &name, TObject *obj, bool oneRun=false)
Add constant override payload.
Definition: DBStore.cc:204
void setVisibility(G4LogicalVolume &volume, bool visible)
Helper function to quickly set the visibility of a given volume.
Definition: utilities.cc:108
GeometryTypes
Flag indiciating the type of geometry to be used.
Abstract base class for different kinds of events.
STL namespace.