Check duplicate issues.
Description
In #21409, the functionality of TGeoTessellated::AddFacet was modified to remove the automatic CloseShape(check=true) when the last facet is added.
In #21409 also, an early return was added to CloseShape (if fIsClosed && fBVH) that prevents it from being run multiple times (since fIsClosed is set and BuildBVH called unconditionally).
Finally (and pre-existing), TGDMLParse::Tessellated always ends with CloseShape(false).
These three aspects conspire to prevent the closure of tessellated volumes when entered via TGDMLParse. The CloseShape(false) in TGDMLParse essentially locks the default non-closed value since CheckClosure is not called. Later CloseShape(true) calls have no effect (and worse they just silently return without action).
Reproducer
Macro (see also zipped/attached below):
#include "TGeoManager.h"
#include "TGeoTessellated.h"
void tessellated_closeshape()
{
// Load a minimal GDML containing a closed tetrahedron (4 triangular faces).
// TGDMLParse::Tessellated() calls CloseShape(false) after parsing, leaving
// fClosedBody=false regardless of ROOT version.
TGeoManager::Import("tetrahedron.gdml");
auto *tess = dynamic_cast<TGeoTessellated *>(
gGeoManager->GetListOfShapes()->FindObject("Tetrahedron"));
if (!tess) {
printf("[ERROR] Tetrahedron shape not found in tetrahedron.gdml\n");
return;
}
// Call CloseShape(true) to trigger CheckClosure() on the loaded shape.
// REGRESSION: ROOT v6.40 added `if (fIsClosed && fBVH) return` which blocks
// this call after CloseShape(false) was already invoked by the GDML parser.
tess->CloseShape(true);
bool ok = tess->IsClosedBody();
printf("[%s] CloseShape(true) after GDML load -> IsClosedBody = %d (expected 1)\n",
ok ? "PASS" : "FAIL", ok);
}
Minimal input gdml file (see also zipped/attached below):
<?xml version="1.0"?>
<gdml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd">
<define>
<position name="v0" x="0" y="0" z="0" unit="cm"/>
<position name="v1" x="10" y="0" z="0" unit="cm"/>
<position name="v2" x="5" y="10" z="0" unit="cm"/>
<position name="v3" x="5" y="5" z="10" unit="cm"/>
</define>
<materials>
<material name="Vacuum" Z="1">
<D unit="g/cm3" value="1e-25"/>
<atom unit="g/mole" value="1.008"/>
</material>
</materials>
<solids>
<tessellated name="Tetrahedron">
<triangular vertex1="v0" vertex2="v2" vertex3="v1" type="ABSOLUTE"/>
<triangular vertex1="v0" vertex2="v1" vertex3="v3" type="ABSOLUTE"/>
<triangular vertex1="v1" vertex2="v2" vertex3="v3" type="ABSOLUTE"/>
<triangular vertex1="v0" vertex2="v3" vertex3="v2" type="ABSOLUTE"/>
</tessellated>
</solids>
<structure>
<volume name="World">
<materialref ref="Vacuum"/>
<solidref ref="Tetrahedron"/>
</volume>
</structure>
<setup name="Default" version="1.0">
<world ref="World"/>
</setup>
</gdml>
On ROOT 6.40.00 this produces
$ root -l -q tessellated_closeshape.C
Processing tessellated_closeshape.C...
Info in <TGeoManager::Import>: Reading geometry from file: tetrahedron.gdml
Info in <TGeoManager::TGeoManager>: Geometry GDMLImport, Geometry imported from GDML created
Info in <TGeoManager::SetTopVolume>: Top volume is World. Master volume is World
Info in <TGeoNavigator::BuildCache>: --- Maximum geometry depth set to 100
Info in <TGeoManager::CheckGeometry>: Fixing runtime shapes...
Info in <TGeoManager::CheckGeometry>: ...Nothing to fix
Info in <TGeoManager::CloseGeometry>: Counting nodes...
Info in <TGeoManager::Voxelize>: Voxelizing...
Info in <TGeoManager::CloseGeometry>: Building cache...
Info in <TGeoManager::CountLevels>: max level = 1, max placements = 0
Info in <TGeoManager::CloseGeometry>: 1 nodes/ 1 volume UID's in Geometry imported from GDML
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
[FAIL] CloseShape(true) after GDML load -> IsClosedBody = 0 (expected 1)
On ROOT 6.38.00 this produces:
$ root -l -q tessellated_closeshape.C
Processing tessellated_closeshape.C...
Info in <TGeoManager::Import>: Reading geometry from file: tetrahedron.gdml
Info in <TGeoManager::TGeoManager>: Geometry GDMLImport, Geometry imported from GDML created
Info in <TGeoManager::SetTopVolume>: Top volume is World. Master volume is World
Info in <TGeoNavigator::BuildCache>: --- Maximum geometry depth set to 100
Info in <TGeoManager::CheckGeometry>: Fixing runtime shapes...
Info in <TGeoManager::CheckGeometry>: ...Nothing to fix
Info in <TGeoManager::CloseGeometry>: Counting nodes...
Info in <TGeoManager::Voxelize>: Voxelizing...
Info in <TGeoManager::CloseGeometry>: Building cache...
Info in <TGeoManager::CountLevels>: max level = 1, max placements = 0
Info in <TGeoManager::CloseGeometry>: 1 nodes/ 1 volume UID's in Geometry imported from GDML
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
[PASS] CloseShape(true) after GDML load -> IsClosedBody = 1 (expected 1)
tessellated_closeshape.C.zip
tetrahedron.gdml.zip
ROOT version
-------------------------------------------------------------------------------------------------------
| Welcome to ROOT 6.40.00 https://root.cern |
| (c) 1995-2025, The ROOT Team; conception: R. Brun, F. Rademakers |
| Built for linuxx8664gcc on May 23 2026, 20:08:51 |
| From tags/6-40-00@6-40-00 |
| With Ubuntu clang version 23.0.0 (++20260517084559+5f2bedca745d-1~exp1~20260517204734.778) std202002 |
| Try '.help'/'.?', '.demo', '.license', '.credits', '.quit'/'.q' |
-------------------------------------------------------------------------------------------------------
Installation method
Spack (develop)
Operating system
Linux Ubuntu 26.04
Additional context
A minimal fix would likely be to turn TGDMLParse tessellated solids into closed solids by default, i.e. tsl->CloseShape(true, true, false) (aligning with the main use case), but in a sense that just switches to the different default and may affect people who are using gdml imports otherwise.
Introducing early returns without warning for functions like CloseShape should be avoided.
The more fundamental fix is probably to revisit the PR that introduced this regression and disentangle the early return to avoid closure checks from the attempted avoidance of BVH rebuilds.
Check duplicate issues.
Description
In #21409, the functionality of
TGeoTessellated::AddFacetwas modified to remove the automaticCloseShape(check=true)when the last facet is added.In #21409 also, an early return was added to
CloseShape(iffIsClosed && fBVH) that prevents it from being run multiple times (sincefIsClosedis set andBuildBVHcalled unconditionally).Finally (and pre-existing),
TGDMLParse::Tessellatedalways ends withCloseShape(false).These three aspects conspire to prevent the closure of tessellated volumes when entered via
TGDMLParse. TheCloseShape(false)inTGDMLParseessentially locks the default non-closed value sinceCheckClosureis not called. LaterCloseShape(true)calls have no effect (and worse they just silently return without action).Reproducer
Macro (see also zipped/attached below):
Minimal input gdml file (see also zipped/attached below):
On ROOT 6.40.00 this produces
On ROOT 6.38.00 this produces:
tessellated_closeshape.C.zip
tetrahedron.gdml.zip
ROOT version
Installation method
Spack (develop)
Operating system
Linux Ubuntu 26.04
Additional context
A minimal fix would likely be to turn TGDMLParse tessellated solids into closed solids by default, i.e.
tsl->CloseShape(true, true, false)(aligning with the main use case), but in a sense that just switches to the different default and may affect people who are using gdml imports otherwise.Introducing early returns without warning for functions like CloseShape should be avoided.
The more fundamental fix is probably to revisit the PR that introduced this regression and disentangle the early return to avoid closure checks from the attempted avoidance of BVH rebuilds.