|
30 | 30 | #include "CommonUtils/NameConf.h" |
31 | 31 | #include "DetectorsBase/Aligner.h" |
32 | 32 |
|
| 33 | +#ifdef O2_WITH_VECGEOM |
| 34 | +#include "TGeo2VecGeom/RootGeoManager.h" |
| 35 | +#include <VecGeom/management/GeoManager.h> |
| 36 | +#include <VecGeom/management/ABBoxManager.h> |
| 37 | +#include <VecGeom/management/BVHManager.h> |
| 38 | +#include <VecGeom/navigation/GlobalLocator.h> |
| 39 | +#include <VecGeom/navigation/NavigationState.h> |
| 40 | +#include <VecGeom/navigation/NewSimpleNavigator.h> |
| 41 | +#include <VecGeom/navigation/BVHNavigator.h> |
| 42 | +#include <VecGeom/navigation/SimpleLevelLocator.h> |
| 43 | +#include <VecGeom/navigation/BVHLevelLocator.h> |
| 44 | +#include <VecGeom/navigation/VNavigator.h> |
| 45 | +#include <VecGeom/volumes/LogicalVolume.h> |
| 46 | +#include <mutex> |
| 47 | +#endif |
| 48 | + |
33 | 49 | using namespace o2::detectors; |
34 | 50 | using namespace o2::base; |
35 | 51 |
|
@@ -536,3 +552,129 @@ void GeometryManager::loadGeometry(std::string_view simPrefix, bool applyMisalig |
536 | 552 | applyMisalignent(applyMisalignment); |
537 | 553 | } |
538 | 554 | } |
| 555 | + |
| 556 | +#ifdef O2_WITH_VECGEOM |
| 557 | + |
| 558 | +namespace |
| 559 | +{ |
| 560 | +/// Converts the currently loaded TGeo geometry to VecGeom and sets up navigators, once per |
| 561 | +/// process, the first time the VecGeom backend is requested. Not part of loadGeometry(), |
| 562 | +/// which every job calls regardless of whether it ever uses the VecGeom backend. |
| 563 | +void ensureVecGeomWorldBuilt() |
| 564 | +{ |
| 565 | + static std::once_flag onceFlag; |
| 566 | + std::call_once(onceFlag, []() { |
| 567 | + if (!gGeoManager) { |
| 568 | + LOG(fatal) << "Cannot build VecGeom geometry: no TGeo geometry loaded (call GeometryManager::loadGeometry() first)"; |
| 569 | + } |
| 570 | + // Translate geometry and material pointers, then build acceleration structures. |
| 571 | + tgeo2vecgeom::RootGeoManager::Instance().SetMaterialConversionHook([](TGeoMaterial const* m) { return (void*)m; }); |
| 572 | + tgeo2vecgeom::RootGeoManager::Instance().SetFlattenAssemblies(true); |
| 573 | + tgeo2vecgeom::RootGeoManager::Instance().LoadRootGeometry(); |
| 574 | + |
| 575 | + // Acceleration structures must be built before the navigators/locators reference them. |
| 576 | + vecgeom::ABBoxManager::Instance().InitABBoxesForCompleteGeometry(); |
| 577 | + // Builds a BVH per logical volume from the ABBoxes computed above. |
| 578 | + vecgeom::BVHManager::Init(); |
| 579 | + |
| 580 | + // For each logical volume, set both a navigator (used for ComputeStep) and a matched |
| 581 | + // level locator (used for point relocation after a boundary crossing via GlobalLocator); |
| 582 | + // volumes with very few daughters are cheaper to brute-force than to accelerate. |
| 583 | + for (auto& lvol : vecgeom::GeoManager::Instance().GetLogicalVolumesMap()) { |
| 584 | + auto* vol = lvol.second; |
| 585 | + if (vol->GetDaughtersp()->size() <= 2) { |
| 586 | + vol->SetNavigator(vecgeom::NewSimpleNavigator<>::Instance()); |
| 587 | + vol->SetLevelLocator(vecgeom::SimpleLevelLocator::GetInstance()); |
| 588 | + } else { |
| 589 | + vol->SetNavigator(vecgeom::BVHNavigator<>::Instance()); |
| 590 | + vol->SetLevelLocator(vecgeom::BVHLevelLocator::GetInstance()); |
| 591 | + } |
| 592 | + } |
| 593 | + }); |
| 594 | +} |
| 595 | +} // namespace |
| 596 | + |
| 597 | +//_____________________________________________________________________________________ |
| 598 | +o2::base::MatBudget GeometryManager::vecGeomMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1) |
| 599 | +{ |
| 600 | + // Mean material budget between "0" and "1" via VecGeom's BVH-accelerated ray/boundary |
| 601 | + // intersection, instead of TGeo. |
| 602 | + ensureVecGeomWorldBuilt(); |
| 603 | + |
| 604 | + using Vector3D = vecgeom::Vector3D<vecgeom::Precision>; |
| 605 | + |
| 606 | + double length, start[3] = {x0, y0, z0}; |
| 607 | + double dir[3] = {x1 - x0, y1 - y0, z1 - z0}; |
| 608 | + if ((length = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]) < TGeoShape::Tolerance() * TGeoShape::Tolerance()) { |
| 609 | + return o2::base::MatBudget(); // return empty struct |
| 610 | + } |
| 611 | + length = std::sqrt(length); |
| 612 | + double invlen = 1. / length; |
| 613 | + for (int i = 3; i--;) { |
| 614 | + dir[i] *= invlen; |
| 615 | + } |
| 616 | + |
| 617 | + thread_local static vecgeom::NavigationState* newnavstate = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); |
| 618 | + thread_local static vecgeom::NavigationState* currnavstate = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); |
| 619 | + thread_local static vecgeom::NavigationState* startCache = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); |
| 620 | + thread_local static bool startCacheValid = false; |
| 621 | + |
| 622 | + Vector3D currPoint(x0, y0, z0); |
| 623 | + Vector3D dirr(dir[0], dir[1], dir[2]); |
| 624 | + constexpr double kPush = 1.E-6; // mimick the nudging of TGeo's FindNextBoundaryAndStep |
| 625 | + auto world = vecgeom::GeoManager::Instance().GetWorld(); |
| 626 | + o2::base::MatBudget budTot, budStep; |
| 627 | + budStep.length = length; |
| 628 | + |
| 629 | + // Locate the starting volume, reusing the path from the previous call when still valid. |
| 630 | + if (startCacheValid && !startCache->IsOutside()) { |
| 631 | + startCache->CopyTo(currnavstate); |
| 632 | + vecgeom::Transformation3D m; |
| 633 | + currnavstate->TopMatrix(m); |
| 634 | + vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(currPoint), *currnavstate); |
| 635 | + } else { |
| 636 | + currnavstate->Clear(); |
| 637 | + vecgeom::GlobalLocator::LocateGlobalPoint(world, currPoint, *currnavstate, true); |
| 638 | + } |
| 639 | + if (currnavstate->IsOutside() || currnavstate->Top() == nullptr) { |
| 640 | + LOG(error) << "start point out of geometry: " << x0 << ':' << y0 << ':' << z0; |
| 641 | + startCacheValid = false; |
| 642 | + return o2::base::MatBudget(); |
| 643 | + } |
| 644 | + currnavstate->CopyTo(startCache); |
| 645 | + startCacheValid = true; |
| 646 | + |
| 647 | + double stepTot = 0.; |
| 648 | + double remainingDist = length; |
| 649 | + Int_t nzero = 0; |
| 650 | + while (remainingDist > 1.E-10) { |
| 651 | + auto* lvol = currnavstate->Top()->GetLogicalVolume(); |
| 652 | + accountMaterial(static_cast<TGeoMaterial*>(lvol->GetMaterialPtr()), budStep); |
| 653 | + vecgeom::VNavigator const* navigator = lvol->GetNavigator(); |
| 654 | + double step = static_cast<double>(navigator->ComputeStepAndPropagatedState(currPoint, dirr, remainingDist, *currnavstate, *newnavstate)); |
| 655 | + if (step < 2.E-10) { |
| 656 | + nzero++; |
| 657 | + } else { |
| 658 | + nzero = 0; |
| 659 | + } |
| 660 | + if (nzero > 3) { |
| 661 | + // This means navigation has problems on one boundary |
| 662 | + LOG(warning) << "Cannot cross boundary at (" << currPoint[0] << ',' << currPoint[1] << ',' << currPoint[2] << ')'; |
| 663 | + budTot.meanRho /= stepTot; |
| 664 | + budTot.length = stepTot; |
| 665 | + return o2::base::MatBudget(budTot); |
| 666 | + } |
| 667 | + |
| 668 | + remainingDist -= step; |
| 669 | + stepTot += step; |
| 670 | + budTot.meanRho += step * budStep.meanRho; |
| 671 | + budTot.meanX2X0 += step / budStep.meanX2X0; |
| 672 | + currPoint = currPoint + (step + kPush) * dirr; |
| 673 | + std::swap(currnavstate, newnavstate); |
| 674 | + } |
| 675 | + budTot.meanRho /= stepTot; |
| 676 | + budTot.length = stepTot; |
| 677 | + return o2::base::MatBudget(budTot); |
| 678 | +} |
| 679 | + |
| 680 | +#endif // O2_WITH_VECGEOM |
0 commit comments