Skip to content

TGeo: stepping does not resolve a MANY overlap when the overlapping node has an ONLY daughter #23192

Description

@sawenzel

TGeoNavigator's point location and stepping disagree for geometries using AddNodeOverlap()
(the GEANT3 MANY convention).

A minimal reproducer is in many_only_overlap.C, attached below.

The geometry is deliberately simple:

  1. Two sibling volumes overlap. One is added with AddNodeOverlap(), the other is an ONLY
    volume.
  2. The overlapping MANY volume contains an ONLY daughter covering the overlap region.
  3. FindNode() correctly resolves the overlap and returns the ONLY sibling.
  4. FindNextBoundaryAndStep() instead steps through the overlap as if it were air.
  5. Removing the daughter makes point location and stepping agree again.

Thus, stepping and FindNode() produce different material sequences for the same geometry.

The likely cause is that FindNextBoundaryAndStep() tracks the overlap state per current node.
When it descends into the ONLY daughter, the overlap state is cleared, so the overlapping
sibling is no longer considered.

This can affect existing ALICE geometries using MANY placements. In one TOF geometry, stepping
missed several cm of aluminium, resulting in a factor-eight discrepancy in the material integral
along one test line.

Expected behaviour is that stepping and FindNode() resolve the same material sequence, with the
ONLY volume taking priority in the MANY overlap region.

Walkthrough

many_only_overlap.C builds a mother air box with A (aluminium) placed MANY and B (lead)
placed ONLY inside it, and scans a line along z twice — once with FindNode(), resetting the
navigator each query, and once with FindNextBoundaryAndStep().

root -l -b -q many_only_overlap.C

Without a daughter in A, the two agree — air, Al, Pb, Al, air:

=== A placed MANY, no daughter ===
  point location (FindNode, reset each time):
    z [-10.00, -5.00] Air
    z [ -5.00, -3.00] Al
    z [ -3.00,  3.00] Pb
    z [  3.00,  5.00] Al
    z [  5.00, 10.00] Air
  stepping (FindNextBoundaryAndStep):
    z [-10.00, -5.00] Air  (ovl=0)
    z [ -5.00, -3.00] Al   (ovl=1)
    z [ -3.00,  3.00] Pb   (ovl=0)
    z [  3.00,  5.00] Al   (ovl=1)
    z [  5.00, 10.00] Air  (ovl=0)

With an air daughter D inside A covering the region B occupies, point location still finds
the lead over z = -3..3 while stepping crosses 10 cm of air and never sees it:

=== A placed MANY, with air daughter D covering the overlap ===
  point location (FindNode, reset each time):
    z [-10.00, -3.00] Air
    z [ -3.00,  3.00] Pb
    z [  3.00, 10.00] Air
  stepping (FindNextBoundaryAndStep):
    z [-10.00, -5.00] Air  (ovl=0)
    z [ -5.00,  5.00] Air  (ovl=0)
    z [  5.00, 10.00] Air  (ovl=0)

ovl is TGeoManager::IsCurrentOverlapping() at the start of each step.

Setup

Reproduced on ROOT master, v6-25-02-24334-g9ebb0b723c, self-built on EL9 / x86_64, which
reports ROOT Version: 6.41.01. Also seen on the ALICE fork tag v6-36-10-alice3.

many_only_overlap.C
// Minimal reproducer: does TGeo stepping resolve a declared (MANY) overlap when the
// overlap region lies inside a DAUGHTER of the overlapping node?
//   M (air)
//    +- A (Al)  placed MANY, box 5x5x5
//    |   +- D (air) box 2x2x5      <- present only in the "with daughter" case
//    +- B (Pb)  placed ONLY, box 2x2x3, sitting inside A (and inside D when D exists)
#include <cstdio>
static void build(bool withDaughter) {
  new TGeoManager("g", "minimal MANY test");
  auto* matAir = new TGeoMaterial("Air", 14.61, 7.3, 1.2e-3);
  auto* matAl  = new TGeoMaterial("Al", 26.98, 13, 2.7);
  auto* matPb  = new TGeoMaterial("Pb", 207.2, 82, 11.35);
  auto* air = new TGeoMedium("air", 1, matAir);
  auto* al  = new TGeoMedium("al", 2, matAl);
  auto* pb  = new TGeoMedium("pb", 3, matPb);
  TGeoVolume* top = gGeoManager->MakeBox("TOP", air, 50, 50, 50);
  gGeoManager->SetTopVolume(top);
  TGeoVolume* M = gGeoManager->MakeBox("M", air, 20, 20, 20);
  TGeoVolume* A = gGeoManager->MakeBox("A", al, 5, 5, 5);
  TGeoVolume* B = gGeoManager->MakeBox("B", pb, 2, 2, 3);
  if (withDaughter) {
    TGeoVolume* D = gGeoManager->MakeBox("D", air, 2, 2, 5);
    A->AddNode(D, 1);                       // ONLY, fills the region B will overlap
  }
  top->AddNode(M, 1);
  M->AddNodeOverlap(A, 1);                  // MANY
  M->AddNode(B, 1);                         // ONLY
  gGeoManager->CloseGeometry();
}
static void trace(const char* tag) {
  double p[3] = {0., 0., -10.}, d[3] = {0., 0., 1.};
  printf("%s\n", tag);
  printf("  point location (FindNode, reset each time):\n");
  TString prev = ""; double z0 = -10;
  for (int i = 0; i <= 2000; ++i) {
    double z = -10. + i * 0.01;
    gGeoManager->CdTop();
    TGeoNode* n = gGeoManager->FindNode(0., 0., z);
    TString cur = n ? TString(n->GetVolume()->GetMaterial()->GetName()) : TString("OUT");
    if (cur != prev) { if (prev != "") printf("    z [%6.2f,%6.2f] %s\n", z0, z, prev.Data()); prev = cur; z0 = z; }
  }
  printf("    z [%6.2f,%6.2f] %s\n", z0, 10., prev.Data());
  printf("  stepping (FindNextBoundaryAndStep):\n");
  gGeoManager->InitTrack(p, d);
  double travelled = 0.;
  for (int i = 0; i < 30 && travelled < 20. && !gGeoManager->IsOutside(); ++i) {
    TGeoNode* n = gGeoManager->GetCurrentNode();
    const char* mat = n ? n->GetVolume()->GetMaterial()->GetName() : "?";
    int ovl = gGeoManager->IsCurrentOverlapping();
    gGeoManager->FindNextBoundaryAndStep(20. - travelled);
    double st = gGeoManager->GetStep();
    if (st <= 0) break;
    printf("    z [%6.2f,%6.2f] %-4s (ovl=%d)\n", travelled - 10., travelled + st - 10., mat, ovl);
    travelled += st;
  }
}
void many_min() {
  build(false); trace("=== A placed MANY, no daughter ===");
  build(true);  trace("=== A placed MANY, with air daughter D covering the overlap ===");
  printf("MANY_MIN_DONE\n");
}

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions