Geant4 Cross Reference |
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