Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/management/src/G4GeometryManager.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  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 // 26.07.95, P.Kent - Initial version, including optimisation build
 29 // 12.06.24, J.Apostolakis - Added parallel optimisation in workers
 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 //
 42 #include "G4LogicalVolumeStore.hh"
 43 #include "G4VPhysicalVolume.hh"
 44 #include "G4SmartVoxelHeader.hh"
 45 #include "voxeldefs.hh"
 46 
 47 // Needed for setting the extent for tolerance value
 48 //
 49 #include "G4GeometryTolerance.hh"
 50 #include "G4SolidStore.hh"
 51 #include "G4VSolid.hh"
 52 
 53 // Needed for parallel optimisation
 54 #include "G4AutoLock.hh"
 55 
 56 namespace  // Data structures / mutexes for parallel optimisation
 57 {
 58   // Mutex to obtain a volume to optimise
 59   G4Mutex obtainVolumeMutex = G4MUTEX_INITIALIZER;
 60 
 61   // Mutex to lock saving of voxel statistics
 62   G4Mutex voxelStatsMutex = G4MUTEX_INITIALIZER;
 63 
 64   // Mutex to provide Statistics Results
 65   G4Mutex statResultsMutex = G4MUTEX_INITIALIZER;
 66 
 67   // Mutex to start wall clock (global) timer
 68   G4Mutex wallClockTimerMutex = G4MUTEX_INITIALIZER;
 69 
 70   // Mutex to write debug output
 71   G4Mutex outputDbgMutex = G4MUTEX_INITIALIZER;
 72 }
 73 
 74 // ***************************************************************************
 75 // Static class data
 76 // ***************************************************************************
 77 //
 78 G4ThreadLocal G4GeometryManager* G4GeometryManager::fgInstance = nullptr;
 79 
 80 // Static *global* class data
 81 G4bool G4GeometryManager::fParallelVoxelOptimisationRequested = false;
 82   // Records User choice to use parallel voxel optimisation (or not)
 83 
 84 G4bool  G4GeometryManager::fOptimiseInParallelConfigured = false;
 85   // Configured = requested && available (ie if MT or Threads is used)
 86   // Value calculated during each effort to optimise
 87 
 88 std::vector<G4LogicalVolume*> G4GeometryManager::fVolumesToOptimise;
 89 std::vector<G4LogicalVolume*>::const_iterator G4GeometryManager::fLogVolumeIterator;
 90 
 91 std::vector<G4SmartVoxelStat> G4GeometryManager::fGlobVoxelStats;
 92 // Container for statistics
 93 
 94 // Derived state
 95 G4bool G4GeometryManager::fVerboseParallel = false;
 96 G4bool G4GeometryManager::fParallelVoxelOptimisationUnderway = false;
 97 G4bool G4GeometryManager::fParallelVoxelOptimisationFinished = false;
 98 G4double G4GeometryManager::fSumVoxelTime = 0.0;
 99 
100 G4int G4GeometryManager::fNumberThreadsReporting = 0;
101 unsigned int G4GeometryManager::fTotalNumberVolumesOptimised = 0U;
102 
103 // For Wall clock
104 G4Timer* G4GeometryManager::fWallClockTimer = nullptr;
105 G4bool G4GeometryManager::fWallClockStarted = false;
106 
107 // ***************************************************************************
108 // Destructor
109 // ***************************************************************************
110 //
111 G4GeometryManager::~G4GeometryManager()
112 {
113   fgInstance = nullptr;
114   fIsClosed = false;
115   
116   if( fWallClockTimer && G4Threading::IsMasterThread() )
117   {
118     delete fWallClockTimer;
119     fWallClockTimer= nullptr;
120   }
121 }
122 
123 // ***************************************************************************
124 // Closes geometry - performs sanity checks and optionally builds optimisation
125 // for placed volumes (always built for replicas & parameterised).
126 // NOTE: Currently no sanity checks are performed.
127 // Applies to just a specific subtree if a physical volume is specified.
128 // ***************************************************************************
129 //
130 G4bool G4GeometryManager::CloseGeometry(G4bool pOptimise, G4bool verbose,
131                                         G4VPhysicalVolume* pVolume)
132 {
133   if (!fIsClosed && G4Threading::IsMasterThread())
134   {
135     if (pVolume != nullptr)
136     {
137       BuildOptimisations(pOptimise, pVolume);
138     }
139     else
140     {
141       BuildOptimisations(pOptimise, verbose);
142     }
143     fIsClosed = true;
144   }
145   return true;
146 }
147 
148 // ***************************************************************************
149 // Opens the geometry and removes optimisations (optionally, related to just
150 // the specified logical-volume).
151 // Applies to just a specific subtree if a physical volume is specified.
152 // ***************************************************************************
153 //
154 void G4GeometryManager::OpenGeometry(G4VPhysicalVolume* pVolume)
155 {
156   if (fIsClosed && G4Threading::IsMasterThread())
157   {
158     if (pVolume != nullptr)
159     {
160       DeleteOptimisations(pVolume);
161     }
162     else
163     {
164       DeleteOptimisations();
165     }
166     fIsClosed = false;
167   }
168 }
169 
170 // ***************************************************************************
171 // Returns the instance of the singleton.
172 // Creates it in case it's called for the first time.
173 // ***************************************************************************
174 //
175 G4GeometryManager* G4GeometryManager::GetInstance()
176 {
177   if (fgInstance == nullptr)
178   {
179     fgInstance = new G4GeometryManager;
180     
181     if( (fWallClockTimer == nullptr) && G4Threading::IsMasterThread() )
182     {
183       fWallClockTimer = new G4Timer;
184     }
185   }
186   return fgInstance;
187 }
188 
189 // ***************************************************************************
190 // Returns the instance of the singleton.
191 // ***************************************************************************
192 //
193 G4GeometryManager* G4GeometryManager::GetInstanceIfExist()
194 {
195   return fgInstance;
196 }
197 
198 // ***************************************************************************
199 // Simplest user method to request parallel optimisation.
200 // ***************************************************************************
201 //
202 void G4GeometryManager::OptimiseInParallel( G4bool val )
203 {
204   RequestParallelOptimisation(val);
205 }
206 
207 // ***************************************************************************
208 // Report about Voxel(isation) of a logical volume.
209 // ***************************************************************************
210 //
211 void
212 G4GeometryManager::ReportVoxelInfo(G4LogicalVolume* logVolume, std::ostream& os)
213 {
214   G4SmartVoxelHeader* head = logVolume->GetVoxelHeader();
215   if( head != nullptr )
216   {
217     os << "** Created optimisations for logical-volume '" 
218        << std::setw(50) << logVolume->GetName() << "'" << G4endl
219        << "- Result VoxelInfo - START: " << " ptr= " << head << G4endl
220        << *head
221        << "- Result VoxelInfo -   END. " << G4endl;
222   }
223   else
224   {
225     os << "** No optimisation for log-vol " << logVolume->GetName() << G4endl;
226   }
227   os << "*** Report Voxel Info: END " <<  G4endl;
228 }
229 
230 // ***************************************************************************
231 // Creates optimisation info. Builds all voxels if allOpts=true
232 // otherwise it builds voxels only for replicated volumes.
233 // ***************************************************************************
234 // Returns whether optimisation is finished
235 //
236 G4bool G4GeometryManager::BuildOptimisations(G4bool allOpts, G4bool verbose)
237 {
238   G4bool finishedOptimisation = false;
239   
240   fOptimiseInParallelConfigured = fParallelVoxelOptimisationRequested
241                                && G4Threading::IsMultithreadedApplication();
242 
243   static unsigned int NumCallsBuildOptimisations = 0; // WORKAROUND - TODO fix
244   if( fOptimiseInParallelConfigured && (NumCallsBuildOptimisations==0) )
245   {
246     PrepareParallelOptimisation(allOpts, verbose);
247     ++NumCallsBuildOptimisations;
248   }
249   else
250   {
251     BuildOptimisationsSequential(allOpts, verbose);
252     finishedOptimisation= true;
253   }
254 
255   return finishedOptimisation;
256 }
257 
258 // ***************************************************************************
259 // Creates optimisation info. Builds all voxels if allOpts=true
260 // otherwise it builds voxels only for replicated volumes.
261 //
262 // This is the original sequential implementation of this method; was called
263 // - at first initialisation to create voxels,
264 // - at re-initialisation if the geometry has changed.
265 // ***************************************************************************
266 //
267 void G4GeometryManager::BuildOptimisationsSequential(G4bool allOpts,
268                                                      G4bool verbose)
269 {
270   G4Timer timer;
271   G4Timer allTimer;
272   std::vector<G4SmartVoxelStat> stats;
273   
274   if (verbose)  { allTimer.Start(); }
275   
276   G4LogicalVolumeStore* Store = G4LogicalVolumeStore::GetInstance();
277   G4LogicalVolume* volume;
278   G4SmartVoxelHeader* head;
279 
280 #ifdef G4GEOMETRY_VOXELDEBUG
281   G4cout << G4endl
282      << "*** G4GeometryManager::BuildOptimisationsSequential() called on tid "
283      << G4Threading::G4GetThreadId() << " all-opts= " << allOpts << G4endl;
284 #endif
285   
286   for (auto & n : *Store)
287   {
288     if (verbose) timer.Start();
289     volume=n;
290     // For safety, check if there are any existing voxels and
291     // delete before replacement
292     //
293     head = volume->GetVoxelHeader();
294     delete head;
295     volume->SetVoxelHeader(nullptr);
296     if (    ( (volume->IsToOptimise())
297              && (volume->GetNoDaughters()>=kMinVoxelVolumesLevel1&&allOpts) )
298         || ( (volume->GetNoDaughters()==1)
299             && (volume->GetDaughter(0)->IsReplicated())
300             && (volume->GetDaughter(0)->GetRegularStructureId()!=1) ) )
301     {
302 #ifdef G4GEOMETRY_VOXELDEBUG
303     G4cout << "** G4GeometryManager::BuildOptimisationsSequential()"
304            << "   Examining logical volume name = '" << volume->GetName()
305            << "'  #daughters= " << volume->GetNoDaughters()  << G4endl;
306 #endif
307       head = new G4SmartVoxelHeader(volume);
308       
309       if (head != nullptr)
310       {
311         volume->SetVoxelHeader(head);
312       }
313       else
314       {
315         std::ostringstream message;
316         message << "VoxelHeader allocation error." << G4endl
317                 << "Allocation of new VoxelHeader" << G4endl
318                 << "        for volume '" << volume->GetName() << "' failed.";
319         G4Exception("G4GeometryManager::BuildOptimisations()", "GeomMgt0003",
320                     FatalException, message);
321       }
322       if (verbose)
323       {
324         timer.Stop();
325         stats.emplace_back( volume, head,
326                            timer.GetSystemElapsed(),
327                            timer.GetUserElapsed() );
328       }
329     }
330     else
331     {
332       // Don't create voxels for this node
333 #ifdef G4GEOMETRY_VOXELDEBUG
334       auto numDaughters = volume->GetNoDaughters();
335       G4cout << "- Skipping logical volume with " << numDaughters
336              << " daughters and name = '" << volume->GetName() << "' " << G4endl;
337       if( numDaughters > 1 )
338       {
339         G4cout << "[Placement]";
340       }
341       else
342       {
343         if( numDaughters == 1 )
344         {
345           G4cout << ( volume->GetDaughter(0)->IsReplicated() ? "[Replicated]"
346                                                              : "[Placement]" );
347         }
348       }
349       G4cout << G4endl;
350 #endif
351     }
352   }
353   if (verbose)
354   {
355     allTimer.Stop();
356     
357     ReportVoxelStats( stats, allTimer.GetSystemElapsed()
358                      + allTimer.GetUserElapsed() );
359   }
360 }
361 
362 // ***************************************************************************
363 // Creates a list of logical volumes which will be optimised
364 //    if allOpts=true it lists all voxels
365 //    otherwise       it lists only the voxels of replicated volumes.
366 // This list will be used subsequently to build their voxels.
367 //
368 // Note: this method is NOT thread safe!
369 //    It expects to be called only once in each (re)initalisation
370 //    i.e. either by master thread or a selected thread.
371 // ***************************************************************************
372 //
373 void
374 G4GeometryManager::CreateListOfVolumesToOptimise(G4bool allOpts, G4bool verbose)
375 {
376   // Prepare the work - must be called only in one thread !!
377   
378   G4LogicalVolumeStore* Store = G4LogicalVolumeStore::GetInstance();
379 
380   if( !fVolumesToOptimise.empty() )
381   {
382     ResetListOfVolumesToOptimise();
383   }
384   
385   for (auto & n : *Store)
386   {
387     G4LogicalVolume* volume=n;
388     
389     if (    ( (volume->IsToOptimise())
390              && (volume->GetNoDaughters()>=kMinVoxelVolumesLevel1&&allOpts) )
391         || ( (volume->GetNoDaughters()==1)
392             && (volume->GetDaughter(0)->IsReplicated())
393             && (volume->GetDaughter(0)->GetRegularStructureId()!=1) ) )
394     {
395       fVolumesToOptimise.push_back(volume);
396 
397       // For safety, must check (later) if there are any existing voxels and
398       //     delete before replacement:
399       // All 'clients' of this code must do the following:
400       //   delete volume->GetVoxelHeader();
401       //   volume->SetVoxelHeader(nullptr);
402       
403 #ifdef G4GEOMETRY_VOXELDEBUG
404       G4cout << "- Booking  logical volume with " << volume->GetNoDaughters()
405       << " daughters and name = '" << volume->GetName() << "' "
406       << " -- for optimisation (ie voxels will be built for it). " << G4endl;
407 #endif
408     }
409     else
410     {
411 #ifdef G4GEOMETRY_VOXELDEBUG
412       G4cout << "- Skipping logical volume with " << volume->GetNoDaughters()
413       << " daughters and name = '" << volume->GetName() << "' " << G4endl;
414 #endif
415     }
416   }
417   
418   if(verbose)
419     G4cout << "** G4GeometryManager::PrepareOptimisationWork: "
420            << "  Number of volumes for voxelisation = "
421            << fVolumesToOptimise.size() << G4endl;
422   
423   fLogVolumeIterator = fVolumesToOptimise.cbegin();
424 }
425 
426 // ***************************************************************************
427 // Obtain a logical volume from the list of volumes to optimise
428 // Must be thread-safe: its role is to be called in parallel by threads/tasks!
429 // Critical method for parallel optimisation - must be correct and fast.
430 // ***************************************************************************
431 //
432 G4LogicalVolume* G4GeometryManager::ObtainVolumeToOptimise()
433 {
434   G4LogicalVolume* logVolume = nullptr;
435 
436   G4AutoLock lock(obtainVolumeMutex);
437     
438   if( fLogVolumeIterator != fVolumesToOptimise.cend() )
439   {
440     logVolume = *fLogVolumeIterator;
441     ++fLogVolumeIterator;
442   }
443   return logVolume;
444 }
445 
446 // ***************************************************************************
447 // Thread-safe method to clear the list of volumes to Optimise.
448 // ***************************************************************************
449 //
450 void G4GeometryManager::ResetListOfVolumesToOptimise()
451 {
452   G4AutoLock lock(obtainVolumeMutex);
453 
454   std::vector<G4LogicalVolume*>().swap(fVolumesToOptimise);
455   // Swapping with an empty vector in order to empty it
456   // without calling destructors of logical volumes.
457   // Must not call clear: i.e. fVolumesToOptimise.clear();
458 
459   assert(fVolumesToOptimise.empty());
460   fLogVolumeIterator = fVolumesToOptimise.cbegin();
461   
462   fGlobVoxelStats.clear();
463   // Reset also the statistics of volumes -- to avoid double recording.
464 }
465 
466 // ***************************************************************************
467 // Method which user calls to ask for parallel optimisation (or turn it off).
468 // ***************************************************************************
469 //
470 void G4GeometryManager::RequestParallelOptimisation(G4bool flag, G4bool verbose)
471 {
472   fParallelVoxelOptimisationRequested = flag;
473   if( flag )
474   {
475     ConfigureParallelOptimisation(verbose);
476   }
477 }
478 
479 // ***************************************************************************
480 // Setup up state to enable parallel optimisation by workers.
481 // ***************************************************************************
482 //
483 void G4GeometryManager::ConfigureParallelOptimisation(G4bool verbose)
484 {
485   if(verbose)
486   {
487     G4cout << "** G4GeometryManager::ConfigureParallelOptimisation() called. "
488     << " LEAVING all the work (of voxel optimisation) to the threads/tasks !"
489     << G4endl;
490   }
491   fParallelVoxelOptimisationRequested = true;
492   fParallelVoxelOptimisationUnderway = false;
493   fParallelVoxelOptimisationFinished = false;
494   
495   // Keep values of options / verbosity for use in threads
496   fVerboseParallel = verbose;
497   
498   // New effort -- reset the total time -- and number of threads reporting
499   fSumVoxelTime = 0.0;
500   fNumberThreadsReporting = 0;
501   fTotalNumberVolumesOptimised = 0;   // Number of volumes done
502   
503   fWallClockStarted = false;  // Will need to restart it!
504 }
505 
506 // ***************************************************************************
507 // Build voxel optimisation in parallel -- prepare the work for threads/tasks
508 // ***************************************************************************
509 //
510 void
511 G4GeometryManager::PrepareParallelOptimisation(G4bool allOpts, G4bool verbose)
512 {
513   if( verbose )
514   {
515     G4cout << "** G4GeometryManager::PrepareParallelOptimisation() called."
516            << G4endl;
517   }
518   CreateListOfVolumesToOptimise(allOpts, verbose);
519   ConfigureParallelOptimisation(verbose);
520 }
521 
522 // ***************************************************************************
523 // Method for a thread/task to contribute dynamically to Optimisation
524 // ***************************************************************************
525 //
526 void G4GeometryManager::UndertakeOptimisation()
527 {
528   G4bool verbose = fVerboseParallel;
529   G4LogicalVolume* logVolume = nullptr;
530 
531   fParallelVoxelOptimisationUnderway  = true;
532   
533   // Start timer - if not already done
534   if( ( !fWallClockStarted ) && verbose )
535   {
536     G4AutoLock startTimeLock(wallClockTimerMutex);
537     if( !fWallClockStarted )
538     {
539       fWallClockTimer->Start();
540       fWallClockStarted= true;
541     }
542   }
543 
544   G4Timer fetimer;
545   unsigned int numVolumesOptimised = 0;
546   
547   while( (logVolume = ObtainVolumeToOptimise()) != nullptr )
548   {
549     if (verbose) fetimer.Start();
550 
551     G4SmartVoxelHeader* head = logVolume->GetVoxelHeader();
552     delete head;
553     logVolume->SetVoxelHeader(nullptr);
554 
555     head = new G4SmartVoxelHeader(logVolume);
556     //     *********************************
557     logVolume->SetVoxelHeader(head);
558     
559     if (head != nullptr)
560     {
561       ++numVolumesOptimised;
562     }
563     else
564     {
565       G4ExceptionDescription message;
566       message << "VoxelHeader allocation error." << G4endl
567               << "Allocation of new VoxelHeader" << G4endl
568               << "for logical volume " << logVolume->GetName() << " failed.";
569       G4Exception("G4GeometryManager::BuildOptimisationsParallel()",
570                   "GeomMgt0003", FatalException, message);
571     }
572 
573     if(verbose)
574     {
575       fetimer.Stop();
576       auto feRealElapsed = fetimer.GetRealElapsed();
577       // Must use 'real' elapsed time -- cannot trust user/system time
578       // (it accounts for all threads)
579       
580       G4AutoLock lock(voxelStatsMutex);
581       fGlobVoxelStats.emplace_back( logVolume, head,
582                           0.0,             // Cannot estimate system time
583                           feRealElapsed ); // Use real time instead of user time
584       fSumVoxelTime += feRealElapsed;
585     }
586   }
587 
588   G4bool allDone = false;
589   G4int myCount= -1;
590 
591   myCount = ReportWorkerIsDoneOptimising(numVolumesOptimised);
592   allDone = IsParallelOptimisationFinished();
593 
594   if( allDone && (myCount == G4Threading::GetNumberOfRunningWorkerThreads()) )
595   {
596     G4int badVolumes = CheckOptimisation(); // Check all voxels are created!
597     if( badVolumes > 0 )
598     {
599       G4ExceptionDescription errmsg;
600       errmsg <<" Expected that all voxelisation work is done, "
601              << "but found that voxels headers are missing in "
602              << badVolumes << " volumes.";
603       G4Exception("G4GeometryManager::UndertakeOptimisation()",
604                   "GeomMng002", FatalException, errmsg);
605     }
606     
607     // Create report
608 
609     if( verbose )
610     {
611       fWallClockTimer->Stop();
612 
613       std::ostream& report_stream = std::cout; // G4cout; does not work!
614       report_stream << G4endl
615         << "G4GeometryManager::UndertakeOptimisation"
616         << " -- Timing for Voxel Optimisation" << G4endl;
617       report_stream << "  - Elapsed time (real) = " << std::setprecision(4)
618         << fWallClockTimer->GetRealElapsed() << "s (wall clock)"
619         << ", user " << fWallClockTimer->GetUserElapsed() << "s"
620         << ", system " << fWallClockTimer->GetSystemElapsed() << "s."
621         << G4endl;
622       report_stream << "  - Sum voxel time (real) = " << fSumVoxelTime
623                     << "s.";
624       report_stream << std::setprecision(6) << G4endl << G4endl;
625 
626       ReportVoxelStats( fGlobVoxelStats, fSumVoxelTime, report_stream );
627       report_stream.flush();
628     }
629   }
630   else
631   {
632     WaitForVoxelisationFinish(false);
633   }
634 }
635 
636 // ***************************************************************************
637 // Ensure that all the work of voxelisation is done.
638 // Can be called in GeometryManager methods or externally.
639 // ***************************************************************************
640 //
641 void G4GeometryManager::WaitForVoxelisationFinish(G4bool verbose)
642 {
643   // Must wait until all workers are done ...
644   using namespace std::chrono_literals;
645   unsigned int trials = 0;
646   auto tid = G4Threading::G4GetThreadId();
647   
648   std::ostream& out_stream = std::cout; // G4cout; does not work!
649   while( ! IsParallelOptimisationFinished() )
650   {
651     // Each thread must wait until all are done ...
652     std::this_thread::sleep_for(250ms);
653     ++trials;
654   }
655   
656   if( verbose )
657   {
658     G4AutoLock lock(outputDbgMutex);
659     out_stream << G4endl
660                << "** UndertakeOptimisation done on tid= " << tid
661                <<  " after waiting for " << trials << " trials." << G4endl;
662     out_stream.flush();
663   }
664 }
665 
666 // ***************************************************************************
667 // Ensure that all logical volumes in list have a voxel-header.
668 // ***************************************************************************
669 //
670 G4int G4GeometryManager::CheckOptimisation()
671 {
672   unsigned int numErrors = 0;
673   for ( const auto& logical : fVolumesToOptimise )
674   {
675     if( logical->GetVoxelHeader() == nullptr ) { ++numErrors; }
676   }
677   return numErrors;
678 }
679 
680 // ***************************************************************************
681 // Report that current thread/task is done optimising.
682 // A thread call this method to reports that is is done (finished), and how
683 // many volumes it optimised. The method:
684 //   - increments the count of workers that have finished, and return it;
685 //   - keeps count of number of volumes optimised;
686 //   - if all works is done (ie all workers have reported) it will result
687 //     in the 'Finished' state.
688 // ***************************************************************************
689 //
690 G4int
691 G4GeometryManager::ReportWorkerIsDoneOptimising(unsigned int numVolumesOptimised)
692 {
693   // Check that all are done and, if so, signal that optimisation is finished
694   G4int orderReporting;
695   
696   G4AutoLock lock(statResultsMutex);
697   orderReporting = ++fNumberThreadsReporting;
698   fTotalNumberVolumesOptimised += numVolumesOptimised;
699   
700   if (fNumberThreadsReporting == G4Threading::GetNumberOfRunningWorkerThreads())
701   {
702     InformOptimisationIsFinished(fVerboseParallel);
703   }
704   
705   return orderReporting;
706 }
707 
708 // ***************************************************************************
709 // Inform that all work for parallel optimisation is finished.
710 // ***************************************************************************
711 //
712 void G4GeometryManager::InformOptimisationIsFinished(G4bool verbose)
713 {
714   if(verbose)   // G4cout does not work!
715   {
716     std::cout << "** G4GeometryManager: All voxel optimisation work is completed!"
717               << G4endl;
718     std::cout << "   Total number of volumes optimised = "
719               << fTotalNumberVolumesOptimised 
720               << " of " << fVolumesToOptimise.size() << " expected\n";
721     std::cout << "   Number of workers reporting       = "
722               << fNumberThreadsReporting
723               << " of " << G4Threading::GetNumberOfRunningWorkerThreads()
724               << " expected\n";
725   }
726   assert ( fTotalNumberVolumesOptimised == fVolumesToOptimise.size() );
727 
728   fParallelVoxelOptimisationFinished  = true;
729   // fParallelVoxelOptimisationRequested = false; // Maintain request for next one!
730   fParallelVoxelOptimisationUnderway  = false; // It's no longer underway!
731 }
732 
733 // ***************************************************************************
734 // Creates Optimisation info for the specified volumes subtree.
735 // ***************************************************************************
736 //
737 void G4GeometryManager::BuildOptimisations(G4bool allOpts,
738                                            G4VPhysicalVolume* pVolume)
739 {
740   if (pVolume == nullptr) { return; }
741 
742   // Retrieve the mother logical volume, if not NULL,
743   // otherwise apply global optimisation for the world volume
744   //
745   G4LogicalVolume* tVolume = pVolume->GetMotherLogical();
746   if (tVolume == nullptr)
747   {
748     BuildOptimisations(allOpts, false);
749     return;
750   }
751 
752   G4SmartVoxelHeader* head = tVolume->GetVoxelHeader();
753   delete head;
754   tVolume->SetVoxelHeader(nullptr);
755   if (    ( (tVolume->IsToOptimise())
756          && (tVolume->GetNoDaughters()>=kMinVoxelVolumesLevel1&&allOpts) )
757        || ( (tVolume->GetNoDaughters()==1)
758          && (tVolume->GetDaughter(0)->IsReplicated()) ) ) 
759   {
760     head = new G4SmartVoxelHeader(tVolume);
761     if (head != nullptr)
762     {
763       tVolume->SetVoxelHeader(head);
764     }
765     else
766     {
767       std::ostringstream message;
768       message << "VoxelHeader allocation error." << G4endl
769               << "Allocation of new VoxelHeader" << G4endl
770               << "        for volume " << tVolume->GetName() << " failed.";
771       G4Exception("G4GeometryManager::BuildOptimisations()", "GeomMgt0003",
772                   FatalException, message);
773     }
774   }
775   else
776   {
777     // Don't create voxels for this node
778 #ifdef G4GEOMETRY_VOXELDEBUG
779     G4cout << "** G4GeometryManager::BuildOptimisations()" << G4endl
780            << "     Skipping logical volume name = " << tVolume->GetName()
781            << G4endl;
782 #endif
783   }
784 
785   // Scan recursively the associated logical volume tree
786   //
787   tVolume = pVolume->GetLogicalVolume();
788   if (tVolume->GetNoDaughters() != 0)
789   {
790     BuildOptimisations(allOpts, tVolume->GetDaughter(0));
791   }
792 }
793 
794 // ***************************************************************************
795 // Removes all optimisation info.
796 // Loops over all logical volumes, deleting non-null voxels pointers.
797 // ***************************************************************************
798 //
799 void G4GeometryManager::DeleteOptimisations()
800 {
801   G4LogicalVolume* tVolume = nullptr;
802   G4LogicalVolumeStore* Store = G4LogicalVolumeStore::GetInstance();
803   for (auto & n : *Store)
804   {
805     tVolume=n;
806     delete tVolume->GetVoxelHeader();
807     tVolume->SetVoxelHeader(nullptr);
808   }
809 }
810 
811 // ***************************************************************************
812 // Removes optimisation info for the specified subtree.
813 // Scans recursively all daughter volumes, deleting non-null voxels pointers.
814 // ***************************************************************************
815 //
816 void G4GeometryManager::DeleteOptimisations(G4VPhysicalVolume* pVolume)
817 {
818   if (pVolume == nullptr) { return; }
819 
820   // Retrieve the mother logical volume, if not NULL,
821   // otherwise global deletion to world volume.
822   //
823   G4LogicalVolume* tVolume = pVolume->GetMotherLogical();
824   if (tVolume == nullptr) { return DeleteOptimisations(); }
825   delete tVolume->GetVoxelHeader();
826   tVolume->SetVoxelHeader(nullptr);
827 
828   // Scan recursively the associated logical volume tree
829   //
830   tVolume = pVolume->GetLogicalVolume();
831   if (tVolume->GetNoDaughters() != 0)
832   {
833     DeleteOptimisations(tVolume->GetDaughter(0));
834   }
835 }
836 
837 // ***************************************************************************
838 // Sets the maximum extent of the world volume. The operation is allowed only
839 // if NO solids have been created already.
840 // ***************************************************************************
841 //
842 void G4GeometryManager::SetWorldMaximumExtent(G4double extent)
843 {
844   if (!G4SolidStore::GetInstance()->empty())
845   {
846      // Sanity check to assure that extent is fixed BEFORE creating
847      // any geometry object (solids in this case)
848      //
849      G4Exception("G4GeometryManager::SetMaximumExtent()",
850                  "GeomMgt0003", FatalException,
851                  "Extent can be set only BEFORE creating any geometry object!");
852   }
853   G4GeometryTolerance::GetInstance()->SetSurfaceTolerance(extent);
854 }
855 
856 // ***************************************************************************
857 // Reports statistics on voxel optimisation when closing geometry.
858 // ***************************************************************************
859 //
860 void
861 G4GeometryManager::ReportVoxelStats( std::vector<G4SmartVoxelStat> & stats,
862                                      G4double totalCpuTime,
863                                      std::ostream &os )
864 {
865   os << "--------------------------------------------------------------------------------"
866      << G4endl;
867   os << "G4GeometryManager::ReportVoxelStats -- Voxel Statistics"
868          << G4endl << G4endl;
869  
870   //
871   // Get total memory use
872   //
873   G4int i, nStat = (G4int)stats.size();
874   G4long totalMemory = 0;
875  
876   for( i=0; i<nStat; ++i )  { totalMemory += stats[i].GetMemoryUse(); }
877  
878   os << "    Total memory consumed for geometry optimisation:   "
879          << totalMemory/1024 << " kByte" << G4endl;
880   os << "    Total CPU time elapsed for geometry optimisation: " 
881          << std::setprecision(4) << totalCpuTime << " seconds"
882          << std::setprecision(6) << G4endl;
883  
884   //
885   // First list: sort by total CPU time
886   //
887   std::sort( stats.begin(), stats.end(),
888     [](const G4SmartVoxelStat& a, const G4SmartVoxelStat& b)
889   {
890     return a.GetTotalTime() > b.GetTotalTime();
891   } );
892          
893   const G4int maxPrint = 20;
894   G4int nPrint = std::min ( nStat, maxPrint );
895 
896   if (nPrint != 0)
897   {
898     os << "\n    Voxelisation: top CPU users:" << G4endl;
899     os << "    Percent   Total CPU    System CPU       Memory  Volume\n"
900        << "    -------   ----------   ----------     --------  ----------"
901        << G4endl;
902   }
903 
904   for(i=0; i<nPrint; ++i)
905   {
906     G4double total = stats[i].GetTotalTime();
907     G4double system = stats[i].GetSysTime();
908     G4double perc = 0.0;
909 
910     if (system < 0) { system = 0.0; }
911     if ((total < 0) || (totalCpuTime < perMillion))
912       { total = 0; }
913     else
914       { perc = total*100/totalCpuTime; }
915 
916     os << std::setprecision(2) 
917            << std::setiosflags(std::ios::fixed|std::ios::right)
918            << std::setw(11) << perc
919            << std::setw(13) << total
920            << std::setw(13) << system
921            << std::setw(13) << (stats[i].GetMemoryUse()+512)/1024
922            << "k " << std::setiosflags(std::ios::left)
923            << stats[i].GetVolume()->GetName()
924            << std::resetiosflags(std::ios::floatfield|std::ios::adjustfield)
925            << std::setprecision(6)
926            << G4endl;
927   }
928  
929   //
930   // Second list: sort by memory use
931   //
932   std::sort( stats.begin(), stats.end(),
933     [](const G4SmartVoxelStat& a, const G4SmartVoxelStat& b)
934   {
935     return a.GetMemoryUse() > b.GetMemoryUse();
936   } );
937  
938   if (nPrint != 0)
939   {
940     os << "\n    Voxelisation: top memory users:" << G4endl;
941     os << "    Percent     Memory      Heads    Nodes   Pointers    Total CPU    Volume\n"
942        << "    -------   --------     ------   ------   --------   ----------    ----------"
943        << G4endl;
944   }
945 
946   for(i=0; i<nPrint; ++i)
947   {
948     G4long memory = stats[i].GetMemoryUse();
949     G4double totTime = stats[i].GetTotalTime();
950     if (totTime < 0) { totTime = 0.0; }
951 
952     os << std::setprecision(2) 
953        << std::setiosflags(std::ios::fixed|std::ios::right)
954        << std::setw(11) << G4double(memory*100)/G4double(totalMemory)
955        << std::setw(11) << memory/1024 << "k "
956        << std::setw( 9) << stats[i].GetNumberHeads()
957        << std::setw( 9) << stats[i].GetNumberNodes()
958        << std::setw(11) << stats[i].GetNumberPointers()
959        << std::setw(13) << totTime << "    "
960        << std::setiosflags(std::ios::left)
961        << stats[i].GetVolume()->GetName()
962        << std::resetiosflags(std::ios::floatfield|std::ios::adjustfield)
963        << std::setprecision(6)
964        << G4endl;
965   }
966   os << "--------------------------------------------------------------------------------"
967      << G4endl << G4endl;
968 }
969 
970 // ***************************************************************************
971 // Check whether parallel optimisation was requested -- static (class) data.
972 // ***************************************************************************
973 //
974 G4bool G4GeometryManager::IsParallelOptimisationConfigured()
975 {
976   return fOptimiseInParallelConfigured;
977 }
978 
979 // ***************************************************************************
980 // Report whether parallel optimisation is done -- static (class) data.
981 // ***************************************************************************
982 //
983 G4bool G4GeometryManager::IsParallelOptimisationFinished()
984 {
985   return fParallelVoxelOptimisationFinished;
986 }
987