Geant4 11.4.0
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4GeometryManager.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26// Class G4GeometryManager implementation
27//
28// Author: Paul Kent (CERN), 26.07.1995 - Initial version
29// John Apostolakis (CERN), 12.06.2024 - Added parallel optimisation
30// --------------------------------------------------------------------
31
32#include <iomanip>
33
34#include "G4ios.hh"
35#include "G4Timer.hh"
36#include "G4GeometryManager.hh"
37#include "G4SystemOfUnits.hh"
38#include "G4Threading.hh"
39
40// Needed for building optimisations
41//
43#include "G4VPhysicalVolume.hh"
44#include "G4SmartVoxelHeader.hh"
45#include "voxeldefs.hh"
46
47// Needed for setting the extent for tolerance value
48//
50#include "G4SolidStore.hh"
51#include "G4VSolid.hh"
52
53// Needed for parallel optimisation
54#include "G4AutoLock.hh"
56
57namespace
58{
59 // Mutex to obtain a volume to optimise
60 G4Mutex createHelperMutex = G4MUTEX_INITIALIZER;
61}
62
63// ***************************************************************************
64// Static class data
65// ***************************************************************************
66//
67G4ThreadLocal G4GeometryManager* G4GeometryManager::fgInstance = nullptr;
68
69G4VoxelisationHelper* G4GeometryManager::fParallelVoxeliser = nullptr;
70 // Only one instance created by the master thread's G4GeometryManager
71 // Expected Future: data member (when only one instance)
72
73// Static *global* class data
74G4bool G4GeometryManager::fParallelVoxelOptimisationRequested = true;
75 // Records User choice to use parallel voxel optimisation (or not)
76
77G4bool G4GeometryManager::fOptimiseInParallelConfigured = false;
78 // Configured = requested && available (ie if MT or Threads is used)
79 // Value calculated during each effort to optimise
80
81// ***************************************************************************
82// Contructor
83// ***************************************************************************
84//
85G4GeometryManager::G4GeometryManager()
86{
87 fIsClosed = false;
88
89 // Ensure that the helper objects are created
90 // even if there is no master thread
91 // Note: no longer needed once G4GeometryManager is a canonical Singleton
92 G4AutoLock lock(createHelperMutex);
93 if( fParallelVoxeliser == nullptr )
94 {
95 fParallelVoxeliser= new G4VoxelisationHelper();
96 }
97}
98
99// ***************************************************************************
100// Destructor
101// ***************************************************************************
102//
104{
105 fgInstance = nullptr;
106 fIsClosed = false;
107
108 // Only the master thread can delete the helper objects
109 // - a different mechanism is needed for setups with no master
110 // TODO: See if shared_ptr could help here?
112 {
113 if( fParallelVoxeliser != nullptr )
114 {
115 delete fParallelVoxeliser;
116 fParallelVoxeliser = nullptr;
117 }
118 }
119}
120
121// ***************************************************************************
122// Closes geometry - performs sanity checks and optionally builds optimisation
123// for placed volumes (always built for replicas & parameterised).
124// NOTE: Currently no sanity checks are performed.
125// Applies to just a specific subtree if a physical volume is specified.
126// ***************************************************************************
127//
129 G4VPhysicalVolume* pVolume)
130{
131 if (!fIsClosed && G4Threading::IsMasterThread())
132 {
133 G4bool workDone= false;
134 if (pVolume != nullptr)
135 {
136 workDone= BuildOptimisations(pOptimise, pVolume);
137 }
138 else
139 {
140 workDone= BuildOptimisations(pOptimise, verbose);
141 }
142 fIsClosed = workDone; // Sequential will be done; parallel ongoing
143 }
144 return true;
145}
146
147// ***************************************************************************
148// Inform whether closing of geometry has finished
149// ***************************************************************************
150
152{
153 if( fOptimiseInParallelConfigured )
154 {
155 fIsClosed= fParallelVoxeliser->IsParallelOptimisationFinished();
156 }
157 return fIsClosed;
158}
159
160// ***************************************************************************
161// Opens the geometry and removes optimisations (optionally, related to just
162// the specified logical-volume).
163// Applies to just a specific subtree if a physical volume is specified.
164// ***************************************************************************
165//
167{
168 if (fIsClosed && G4Threading::IsMasterThread())
169 {
170 if (pVolume != nullptr)
171 {
172 DeleteOptimisations(pVolume);
173 }
174 else
175 {
176 DeleteOptimisations();
177 }
178 fIsClosed = false;
179 // fGeometryCloseRequested= false;
180 }
181}
182
183// ***************************************************************************
184// Returns the instance of the singleton.
185// Creates it in case it's called for the first time.
186// ***************************************************************************
187//
189{
190 if (fgInstance == nullptr)
191 {
192 fgInstance = new G4GeometryManager;
193 }
194 return fgInstance;
195}
196
197// ***************************************************************************
198// Returns the instance of the singleton.
199// ***************************************************************************
200//
202{
203 return fgInstance;
204}
205
206// ***************************************************************************
207// Simplest user method to request parallel optimisation.
208// ***************************************************************************
209//
214
215// ***************************************************************************
216// Respond whether parallel optimisation is done
217// ***************************************************************************
218//
220{
221 return fParallelVoxeliser->IsParallelOptimisationFinished();
222}
223
224
225// ***************************************************************************
226// Respond whether parallel optimisation is configured
227// ***************************************************************************
228//
230{
231 return fOptimiseInParallelConfigured;
232}
233
234// ***************************************************************************
235// Creates optimisation info. Builds all voxels if allOpts=true
236// otherwise it builds voxels only for replicated volumes.
237// ***************************************************************************
238// Returns whether optimisation is finished
239//
240G4bool G4GeometryManager::BuildOptimisations(G4bool allOpts, G4bool verbose)
241{
242 G4bool finishedOptimisation = false;
243
244 fOptimiseInParallelConfigured = fParallelVoxelOptimisationRequested
246
247 if( fOptimiseInParallelConfigured )
248 {
249 fParallelVoxeliser->PrepareParallelOptimisation(allOpts, verbose);
250 }
251 else
252 {
253 BuildOptimisationsSequential(allOpts, verbose);
254 finishedOptimisation= true;
255 fIsClosed= true;
256 }
257
258 return finishedOptimisation;
259}
260
261// ***************************************************************************
262// Creates optimisation info. Builds all voxels if allOpts=true
263// otherwise it builds voxels only for replicated volumes.
264//
265// This is the original sequential implementation of this method; was called
266// - at first initialisation to create voxels,
267// - at re-initialisation if the geometry has changed.
268// ***************************************************************************
269//
270void G4GeometryManager::BuildOptimisationsSequential(G4bool allOpts,
271 G4bool verbose)
272{
273 G4Timer timer;
274 G4Timer allTimer;
275 std::vector<G4SmartVoxelStat> stats;
276
277 if (verbose) { allTimer.Start(); }
278
279 G4LogicalVolumeStore* Store = G4LogicalVolumeStore::GetInstance();
280 G4LogicalVolume* volume;
281 G4SmartVoxelHeader* head;
282
283#ifdef G4GEOMETRY_VOXELDEBUG
284 G4cout << G4endl
285 << "*** G4GeometryManager::BuildOptimisationsSequential() called on tid "
286 << G4Threading::G4GetThreadId() << " all-opts= " << allOpts << G4endl;
287#endif
288
289 for (auto & n : *Store)
290 {
291 if (verbose) { timer.Start(); }
292 volume=n;
293 // For safety, check if there are any existing voxels and
294 // delete before replacement
295 //
296 head = volume->GetVoxelHeader();
297 delete head;
298 volume->SetVoxelHeader(nullptr);
299 if ( ( (volume->IsToOptimise())
300 && (volume->GetNoDaughters()>=kMinVoxelVolumesLevel1&&allOpts) )
301 || ( (volume->GetNoDaughters()==1)
302 && (volume->GetDaughter(0)->IsReplicated())
303 && (volume->GetDaughter(0)->GetRegularStructureId()!=1) ) )
304 {
305#ifdef G4GEOMETRY_VOXELDEBUG
306 G4cout << "** G4GeometryManager::BuildOptimisationsSequential()"
307 << " Examining logical volume name = '" << volume->GetName()
308 << "' #daughters= " << volume->GetNoDaughters() << G4endl;
309#endif
310 head = new G4SmartVoxelHeader(volume);
311
312 if (head != nullptr)
313 {
314 volume->SetVoxelHeader(head);
315 }
316 else
317 {
318 std::ostringstream message;
319 message << "VoxelHeader allocation error." << G4endl
320 << "Allocation of new VoxelHeader" << G4endl
321 << " for volume '" << volume->GetName() << "' failed.";
322 G4Exception("G4GeometryManager::BuildOptimisations()", "GeomMgt0003",
323 FatalException, message);
324 }
325 if (verbose)
326 {
327 timer.Stop();
328 stats.emplace_back( volume, head,
329 timer.GetSystemElapsed(),
330 timer.GetUserElapsed() );
331 }
332 }
333 else
334 {
335 // Don't create voxels for this node
336#ifdef G4GEOMETRY_VOXELDEBUG
337 auto numDaughters = volume->GetNoDaughters();
338 G4cout << "- Skipping logical volume with " << numDaughters
339 << " daughters and name = '" << volume->GetName() << "' " << G4endl;
340 if( numDaughters > 1 )
341 {
342 G4cout << "[Placement]";
343 }
344 else
345 {
346 if( numDaughters == 1 )
347 {
348 G4cout << ( volume->GetDaughter(0)->IsReplicated() ? "[Replicated]"
349 : "[Placement]" );
350 }
351 }
352 G4cout << G4endl;
353#endif
354 }
355 }
356 if (verbose)
357 {
358 allTimer.Stop();
359
361 + allTimer.GetUserElapsed() );
362 }
363}
364
365// ***************************************************************************
366// Method which user calls to ask for parallel optimisation (or turn it off).
367// ***************************************************************************
368//
370{
371 fParallelVoxelOptimisationRequested = flag;
372 if( flag )
373 {
374 fParallelVoxeliser->SetVerbosity(verbose);
375 }
376}
377
378// ***************************************************************************
379// Method for a thread/task to contribute dynamically to Optimisation
380// ***************************************************************************
381//
383{
384 fParallelVoxeliser->UndertakeOptimisation();
385}
386
387
388// ***************************************************************************
389// Creates Optimisation info for the specified volumes subtree.
390// ***************************************************************************
391// Returns whether all work is done
392//
393G4bool G4GeometryManager::BuildOptimisations(G4bool allOpts,
394 G4VPhysicalVolume* pVolume)
395{
396 if (pVolume == nullptr) { return false; }
397
398 // Retrieve the mother logical volume, if not NULL,
399 // otherwise apply global optimisation for the world volume
400 //
401 G4LogicalVolume* tVolume = pVolume->GetMotherLogical();
402 if (tVolume == nullptr)
403 {
404 G4bool done=BuildOptimisations(allOpts, false);
405 return done;
406 }
407
408 G4SmartVoxelHeader* head = tVolume->GetVoxelHeader();
409 delete head;
410 tVolume->SetVoxelHeader(nullptr);
411 if ( ( (tVolume->IsToOptimise())
412 && (tVolume->GetNoDaughters()>=kMinVoxelVolumesLevel1&&allOpts) )
413 || ( (tVolume->GetNoDaughters()==1)
414 && (tVolume->GetDaughter(0)->IsReplicated()) ) )
415 {
416 head = new G4SmartVoxelHeader(tVolume);
417 if (head != nullptr)
418 {
419 tVolume->SetVoxelHeader(head);
420 }
421 else
422 {
423 std::ostringstream message;
424 message << "VoxelHeader allocation error." << G4endl
425 << "Allocation of new VoxelHeader" << G4endl
426 << " for volume " << tVolume->GetName() << " failed.";
427 G4Exception("G4GeometryManager::BuildOptimisations()", "GeomMgt0003",
428 FatalException, message);
429 }
430 }
431 else
432 {
433 // Don't create voxels for this node
434#ifdef G4GEOMETRY_VOXELDEBUG
435 G4cout << "** G4GeometryManager::BuildOptimisations()" << G4endl
436 << " Skipping logical volume name = " << tVolume->GetName()
437 << G4endl;
438#endif
439 }
440
441 // Scan recursively the associated logical volume tree
442 //
443 tVolume = pVolume->GetLogicalVolume();
444 if (tVolume->GetNoDaughters() != 0)
445 {
446 BuildOptimisations(allOpts, tVolume->GetDaughter(0));
447 }
448
449 return true; // Work is all done -- no parallelism currently
450}
451
452// ***************************************************************************
453// Removes all optimisation info.
454// Loops over all logical volumes, deleting non-null voxels pointers.
455// ***************************************************************************
456//
457void G4GeometryManager::DeleteOptimisations()
458{
459 G4LogicalVolume* tVolume = nullptr;
460 G4LogicalVolumeStore* Store = G4LogicalVolumeStore::GetInstance();
461 for (auto & n : *Store)
462 {
463 tVolume=n;
464 delete tVolume->GetVoxelHeader();
465 tVolume->SetVoxelHeader(nullptr);
466 }
467}
468
469// ***************************************************************************
470// Removes optimisation info for the specified subtree.
471// Scans recursively all daughter volumes, deleting non-null voxels pointers.
472// ***************************************************************************
473//
474void G4GeometryManager::DeleteOptimisations(G4VPhysicalVolume* pVolume)
475{
476 if (pVolume == nullptr) { return; }
477
478 // Retrieve the mother logical volume, if not NULL,
479 // otherwise global deletion to world volume.
480 //
481 G4LogicalVolume* tVolume = pVolume->GetMotherLogical();
482 if (tVolume == nullptr) { return DeleteOptimisations(); }
483 delete tVolume->GetVoxelHeader();
484 tVolume->SetVoxelHeader(nullptr);
485
486 // Scan recursively the associated logical volume tree
487 //
488 tVolume = pVolume->GetLogicalVolume();
489 if (tVolume->GetNoDaughters() != 0)
490 {
491 DeleteOptimisations(tVolume->GetDaughter(0));
492 }
493}
494
495// ***************************************************************************
496// Sets the maximum extent of the world volume. The operation is allowed only
497// if NO solids have been created already.
498// ***************************************************************************
499//
501{
502 if (!G4SolidStore::GetInstance()->empty())
503 {
504 // Sanity check to assure that extent is fixed BEFORE creating
505 // any geometry object (solids in this case)
506 //
507 G4Exception("G4GeometryManager::SetMaximumExtent()",
508 "GeomMgt0003", FatalException,
509 "Extent can be set only BEFORE creating any geometry object!");
510 }
512}
G4TemplateAutoLock< G4Mutex > G4AutoLock
@ FatalException
void G4Exception(const char *originOfException, const char *exceptionCode, G4ExceptionSeverity severity, const char *description)
G4bool IsReplicated() const override
#define G4MUTEX_INITIALIZER
std::mutex G4Mutex
double G4double
Definition G4Types.hh:83
bool G4bool
Definition G4Types.hh:86
#define G4endl
Definition G4ios.hh:67
G4GLOB_DLL std::ostream G4cout
G4GeometryManager is a singleton class responsible for high level geometrical functions,...
static G4GeometryManager * GetInstance()
G4bool CloseGeometry(G4bool pOptimise=true, G4bool verbose=false, G4VPhysicalVolume *vol=nullptr)
void SetWorldMaximumExtent(G4double worldExtent)
void OptimiseInParallel(G4bool val=true)
void RequestParallelOptimisation(G4bool val=true, G4bool verbose=true)
G4bool IsParallelOptimisationConfigured()
void OpenGeometry(G4VPhysicalVolume *vol=nullptr)
static G4GeometryManager * GetInstanceIfExist()
G4bool IsParallelOptimisationFinished()
void SetSurfaceTolerance(G4double worldExtent)
static G4GeometryTolerance * GetInstance()
static G4LogicalVolumeStore * GetInstance()
void SetVoxelHeader(G4SmartVoxelHeader *pVoxel)
std::size_t GetNoDaughters() const
G4VPhysicalVolume * GetDaughter(const std::size_t i) const
G4bool IsToOptimise() const
const G4String & GetName() const
G4SmartVoxelHeader * GetVoxelHeader() const
static G4SolidStore * GetInstance()
void Stop()
G4double GetSystemElapsed() const
Definition G4Timer.cc:124
G4double GetUserElapsed() const
Definition G4Timer.cc:135
void Start()
G4VPhysicalVolume is an abstract base class for the representation of a positioned volume....
G4LogicalVolume * GetMotherLogical() const
virtual G4bool IsReplicated() const =0
G4LogicalVolume * GetLogicalVolume() const
virtual G4int GetRegularStructureId() const =0
G4VoxelisationHelper is a helper class to undertake voxelisation in parallel, aiding and off-loading ...
static void ReportVoxelStats(std::vector< G4SmartVoxelStat > &stats, G4double totalCpuTime, std::ostream &os=G4cout)
void PrepareParallelOptimisation(G4bool allOpts, G4bool verbose)
G4bool IsMultithreadedApplication()
G4bool IsMasterThread()
G4int G4GetThreadId()
#define G4ThreadLocal
Definition tls.hh:77
const G4int kMinVoxelVolumesLevel1
Definition voxeldefs.hh:39