From 76423904bdea4defa4184dc035839f4ebf8d3854 Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Tue, 18 Nov 2025 18:50:44 +0100 Subject: [PATCH 01/13] add necessary maps to retrieve assemblies and nested volumes information from gdml --- inc/TRestGeant4GeometryInfo.h | 11 ++- src/TRestGeant4GeometryInfo.cxx | 128 +++++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 12 deletions(-) diff --git a/inc/TRestGeant4GeometryInfo.h b/inc/TRestGeant4GeometryInfo.h index 56b617a..d0d53c9 100644 --- a/inc/TRestGeant4GeometryInfo.h +++ b/inc/TRestGeant4GeometryInfo.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -12,10 +13,11 @@ class G4VPhysicalVolume; class TRestGeant4GeometryInfo { - ClassDef(TRestGeant4GeometryInfo, 3); + ClassDef(TRestGeant4GeometryInfo, 4); private: bool fIsAssembly = false; + TString fPathSeparator = "_"; std::map fVolumeNameMap = {}; std::map fVolumeNameReverseMap = {}; @@ -38,6 +40,10 @@ class TRestGeant4GeometryInfo { * only makes sense when using assembly */ + std::map fGeant4AssemblyImprintToGdmlNameMap; + std::map> fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap; + std::map fGeant4AssemblyImprintToAssemblyLogicalNameMap; + std::map fPhysicalToLogicalVolumeMap; std::map > fLogicalToPhysicalMap; // many physical volumes can point to one single logical @@ -86,7 +92,8 @@ class TRestGeant4GeometryInfo { } inline bool IsAssembly() const { return fIsAssembly; } - + inline TString GetPathSeparator() const { return fPathSeparator; } + void SetPathSeparator(const TString& separator) { fPathSeparator = separator; } void InsertVolumeName(Int_t id, const TString& volumeName); TString GetVolumeFromID(Int_t id) const; diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 48dfca9..0b8e54d 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -3,6 +3,7 @@ // #include "TRestGeant4GeometryInfo.h" +#include "TRestStringHelper.h" #include #include @@ -36,7 +37,8 @@ TString GetNodeAttribute(TXMLEngine xml, XMLNodePointer_t node, const TString& a } void AddVolumesRecursively(vector* physicalNames, vector* logicalNames, const vector& children, map& nameTable, - map>& childrenTable, const TString& name = "") { + map>& childrenTable, const TString& name = "", + const TString& separator = "_") { if (children.empty()) { physicalNames->push_back(name); const auto logicalVolumeName = nameTable[name]; @@ -44,10 +46,10 @@ void AddVolumesRecursively(vector* physicalNames, vector* logi return; } for (const auto& childName : children) { - const auto newName = name + "_" + childName; + const auto newName = name + separator + childName; nameTable[newName] = nameTable[childName]; // needed to retrieve logical volume name AddVolumesRecursively(physicalNames, logicalNames, childrenTable[nameTable[childName]], nameTable, - childrenTable, newName); + childrenTable, newName, separator); } } } // namespace myXml @@ -65,23 +67,108 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { << " not found" << endl; exit(1); } + + // auxiliar maps + map gdmlToGeant4AssemblyNameMap; // to record the ordering of the assemblies definition, e.g. "assemblyName" -> "av_1" + size_t assemblyCounter = 0; + map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly + /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother volume where you are placing the assembly. + This daughter PV are named following the format: "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, + its own daughter PV are named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". + To follow this logic, we need to add this "av_WWW_impr_XXX+1"-> nestedAssemblyPVname to the fGeant4AssemblyImprintToGdmlNameMap too. + To do this we use the auxiliar map 'fixNestedAssembliesMap' to store which nested assemblies where found inside each assembly and their order. + + Also, the daughter physical volumes defined after this nested assembly + gets the imprint number XXX+1 instead of the XXX of its mother assembly imprint (which I think is a bug in Geant4). + To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all the daughter PV + from normal LV before all the nested assembly PV. + */ + map> fixNestedAssembliesMap; // parent assembly -> child PV of assemblies. Used to add later the bad imprints "av_WWW_impr_XXX+1" + map nameTable; map> childrenTable; XMLNodePointer_t mainNode = xml.DocGetRootElement(xmldoc); XMLNodePointer_t structure = myXml::FindChildByName(xml, mainNode, "structure"); XMLNodePointer_t child = xml.GetChild(structure); - while (child) { - TString name = xml.GetNodeName(child); + RESTDebug << "Searching for assemblies..." << RESTendl; + while (child) { // loop over the direct children of structure (logical volumes and assemblies) + TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" + if (!name.EqualTo("assembly")) { + child = xml.GetNext(child); + continue; + } + TString assemblyName = myXml::GetNodeAttribute(xml, child, "name"); + gdmlToGeant4AssemblyNameMap[assemblyName] = "av_" + to_string(++assemblyCounter); // first assembly is av_1 + gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; + RESTDebug << "Assembly found: " << assemblyName << " → " << gdmlToGeant4AssemblyNameMap[assemblyName] + << RESTendl; + + // blablabla + bool hasNestedAssemblies = false; + std::map childrenGeant4toGdmlMap; + size_t childrenPVCounter = 0; + auto physicalVolumeNode = xml.GetChild(child); // children of a volume or assembly are physical volumes + while (physicalVolumeNode) { + auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); + auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + while (volumeRefNode) { + TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + if (volumeRefNodeName.EqualTo("volumeref")) { + TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + TString geant4Name = refName + "_pv_" + to_string(childrenPVCounter++); // first pv is logicalVolumeName_pv_0 + childrenGeant4toGdmlMap[geant4Name] = physicalVolumeName; + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + hasNestedAssemblies = true; // to warn later that pv from assemblies must be defined last + fixNestedAssembliesMap[assemblyName].push_back(physicalVolumeName); + } else { + if (hasNestedAssemblies) { + RESTError << "TRestGeant4GeometryInfo::PopulateFromGdml - Assembly '" + << assemblyName << "' contains physical volumes from normal " + << "(i.e. non-assembly) logical volumes defined after physical volumes " + << "from assemblies. Due to a Geant4 bug you cannot do this. " + << "Please define the physical volumes from the assemblies last." << RESTendl; + } + } + + } + volumeRefNode = xml.GetNext(volumeRefNode); + } + physicalVolumeNode = xml.GetNext(physicalVolumeNode); + } + fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap[assemblyName] = childrenGeant4toGdmlMap; + child = xml.GetNext(child); + } + + child = xml.GetChild(structure); + while (child) { // loop over the direct children of structure (logical volumes and assemblies) + TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" TString volumeName = myXml::GetNodeAttribute(xml, child, "name"); - auto physicalVolumeNode = xml.GetChild(child); + auto physicalVolumeNode = xml.GetChild(child); // children of a volume or assembly are physical volumes childrenTable[volumeName] = {}; while (physicalVolumeNode) { auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = xml.GetChild(physicalVolumeNode); + auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation while (volumeRefNode) { - TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); + TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); + TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; + size_t imprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; // first imprint is impr_1 + TString imprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(imprintCounter); + fGeant4AssemblyImprintToGdmlNameMap[imprint] = physicalVolumeName; + + if (fixNestedAssembliesMap.count(refName) > 0) { + // fix nested assemblies + for (const auto& childPV : fixNestedAssembliesMap[refName]) { + size_t nestedImprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; + TString nestedImprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(nestedImprintCounter); + fGeant4AssemblyImprintToGdmlNameMap[nestedImprint] = childPV; + } + } + } nameTable[physicalVolumeName] = refName; childrenTable[volumeName].push_back(physicalVolumeName); } @@ -106,7 +193,7 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { for (const auto& topName : childrenTable[worldVolumeName]) { auto children = childrenTable[nameTable[topName]]; myXml::AddVolumesRecursively(&fGdmlNewPhysicalNames, &fGdmlLogicalNames, children, nameTable, - childrenTable, topName); + childrenTable, topName, fPathSeparator); } xml.FreeDoc(xmldoc); @@ -128,6 +215,27 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { << endl; exit(1); } + + /* + //print fGeant4AssemblyImprintToGdmlNameMap + cout << "GDML Assembly Imprint to GDML Name Map:" << endl; + for (const auto& kv : fGeant4AssemblyImprintToGdmlNameMap) { + cout << "\t" << kv.first << " → " << kv.second << endl; + } + + cout << "GDML assembly children Geant4 to GDML Physical Name Map:" << endl; + for (const auto& kv : fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap) { + cout << "\tAssembly: " << kv.first << endl; + for (const auto& childKv : kv.second) { + cout << "\t\t" << childKv.first << " → " << childKv.second << endl; + } + } + + cout << "Assembly imprint to Assembly Logical Name Map:" << endl; + for (const auto& kv : fGeant4AssemblyImprintToAssemblyLogicalNameMap) { + cout << "\t" << kv.first << " → " << kv.second << endl; + } + */ } TString TRestGeant4GeometryInfo::GetAlternativeNameFromGeant4PhysicalName( From 91755b014e0da5bc381297584e732dcddd2289c1 Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Tue, 18 Nov 2025 18:51:27 +0100 Subject: [PATCH 02/13] add TRestGeant4GeometryInfo::GetAlternativePathFromGeant4Path --- inc/TRestGeant4GeometryInfo.h | 1 + src/TRestGeant4GeometryInfo.cxx | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/inc/TRestGeant4GeometryInfo.h b/inc/TRestGeant4GeometryInfo.h index d0d53c9..01b4b8b 100644 --- a/inc/TRestGeant4GeometryInfo.h +++ b/inc/TRestGeant4GeometryInfo.h @@ -55,6 +55,7 @@ class TRestGeant4GeometryInfo { void PopulateFromGdml(const TString&); + TString GetAlternativePathFromGeant4Path(const TString&) const; TString GetAlternativeNameFromGeant4PhysicalName(const TString&) const; TString GetGeant4PhysicalNameFromAlternativeName(const TString&) const; diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 0b8e54d..792322f 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -238,6 +238,35 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { */ } +////////////////////////////////////////////////////////////////////////// +/// \brief Converts a Geant4 volume path to a GDML volume path. The path is +/// the concatenation of the names of the nested volumes from the world to the desired volume. +/// This method is meant mainly meant to handle the conversion of assembly imprints names to the +/// GDML assembly names used in the GDML file. +/// \param geant4Path The Geant4 volume path. +/// \return The corresponding GDML volume path using the PV names defined in the GDML file. +TString TRestGeant4GeometryInfo::GetAlternativePathFromGeant4Path(const TString& geant4Path) const { + std::string convertedPath = geant4Path.Data(); + // convert the Geant4 assembly imprint convention to GDML name + // e.g. av_1_impr_2_childLV_pv_1 → assemblyName/childLV_pv_1 + for (const auto& [gvName, gdmlName] : fGeant4AssemblyImprintToGdmlNameMap) { + convertedPath = Replace(convertedPath, (gvName+"_").Data(), (gdmlName+fPathSeparator).Data()); + } + // convert the children names inside assemblies to physical volume names in GDML + // e.g. assemblyName/childLV_pv_1 → assemblyName/childPVname + for (const auto& [gvImprint, assemblyLogicalName] : fGeant4AssemblyImprintToAssemblyLogicalNameMap) { + auto gvImprintPosition = convertedPath.find(gvImprint.Data()); + if (gvImprintPosition != std::string::npos) { + for (const auto& [childGeant4Name, childGdmlName] : fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap.at(assemblyLogicalName)) { + TString toReplace = gvImprint + fPathSeparator + childGeant4Name; + TString replaceBy = gvImprint + fPathSeparator + childGdmlName; + convertedPath = Replace(convertedPath, toReplace.Data(), replaceBy.Data()); + } + } + } + return TString(convertedPath); +} + TString TRestGeant4GeometryInfo::GetAlternativeNameFromGeant4PhysicalName( const TString& geant4PhysicalName) const { if (fGeant4PhysicalNameToNewPhysicalNameMap.count(geant4PhysicalName) > 0) { From 17518e91e7528f3b7e3f6812868b46fd8dc5ab72 Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Tue, 18 Nov 2025 18:52:15 +0100 Subject: [PATCH 03/13] fix trailing whitespace --- src/TRestGeant4GeometryInfo.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 792322f..3b7d2e8 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -74,13 +74,13 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother volume where you are placing the assembly. This daughter PV are named following the format: "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, - its own daughter PV are named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". + its own daughter PV are named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". To follow this logic, we need to add this "av_WWW_impr_XXX+1"-> nestedAssemblyPVname to the fGeant4AssemblyImprintToGdmlNameMap too. To do this we use the auxiliar map 'fixNestedAssembliesMap' to store which nested assemblies where found inside each assembly and their order. - + Also, the daughter physical volumes defined after this nested assembly gets the imprint number XXX+1 instead of the XXX of its mother assembly imprint (which I think is a bug in Geant4). - To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all the daughter PV + To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all the daughter PV from normal LV before all the nested assembly PV. */ map> fixNestedAssembliesMap; // parent assembly -> child PV of assemblies. Used to add later the bad imprints "av_WWW_impr_XXX+1" @@ -102,7 +102,7 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; RESTDebug << "Assembly found: " << assemblyName << " → " << gdmlToGeant4AssemblyNameMap[assemblyName] << RESTendl; - + // blablabla bool hasNestedAssemblies = false; std::map childrenGeant4toGdmlMap; @@ -126,11 +126,10 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { RESTError << "TRestGeant4GeometryInfo::PopulateFromGdml - Assembly '" << assemblyName << "' contains physical volumes from normal " << "(i.e. non-assembly) logical volumes defined after physical volumes " - << "from assemblies. Due to a Geant4 bug you cannot do this. " + << "from assemblies. Due to a Geant4 bug you cannot do this. " << "Please define the physical volumes from the assemblies last." << RESTendl; } } - } volumeRefNode = xml.GetNext(volumeRefNode); } @@ -159,7 +158,7 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { size_t imprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; // first imprint is impr_1 TString imprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(imprintCounter); fGeant4AssemblyImprintToGdmlNameMap[imprint] = physicalVolumeName; - + if (fixNestedAssembliesMap.count(refName) > 0) { // fix nested assemblies for (const auto& childPV : fixNestedAssembliesMap[refName]) { From 710fb1ee9f1871923f711f237916704e4d0255dd Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Tue, 18 Nov 2025 18:53:13 +0100 Subject: [PATCH 04/13] add and fix debugging messages --- src/TRestGeant4Metadata.cxx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/TRestGeant4Metadata.cxx b/src/TRestGeant4Metadata.cxx index 55ba277..a623e8b 100644 --- a/src/TRestGeant4Metadata.cxx +++ b/src/TRestGeant4Metadata.cxx @@ -1387,6 +1387,8 @@ void TRestGeant4Metadata::ReadDetector() { vector physicalVolumes; if (!fGeant4GeometryInfo.IsValidGdmlName(name)) { if (fGeant4GeometryInfo.IsValidLogicalVolume(name)) { + RESTDebug << "Volume name '" << name << "' is a valid logical volume. " + << "Inserting all physical volumes from it." << RESTendl; for (const auto& physical : fGeant4GeometryInfo.GetAllPhysicalVolumesFromLogical(name)) { physicalVolumes.emplace_back( fGeant4GeometryInfo.GetAlternativeNameFromGeant4PhysicalName(physical)); @@ -1394,22 +1396,31 @@ void TRestGeant4Metadata::ReadDetector() { } // does not match a explicit (gdml) physical name or a logical name, perhaps its a regular exp if (physicalVolumes.empty()) { + RESTDebug << "Volume name '" << name << "' is not a valid logical volume. " + << "Trying to match as regular expression for physical volumes." << RESTendl; for (const auto& physical : fGeant4GeometryInfo.GetAllPhysicalVolumesMatchingExpression(name)) { + RESTExtreme << "Volume name '" << name << "' matches physical volume '" + << physical << "'" << RESTendl; physicalVolumes.emplace_back( fGeant4GeometryInfo.GetAlternativeNameFromGeant4PhysicalName(physical)); } } if (physicalVolumes.empty()) { + RESTDebug << "Volume name '" << name << "' is not a valid logical volume neither physical volume regex. " + << "Trying to match as regular expression for logical volumes." << RESTendl; for (const auto& logical : fGeant4GeometryInfo.GetAllLogicalVolumesMatchingExpression(name)) { for (const auto& physical : fGeant4GeometryInfo.GetAllPhysicalVolumesFromLogical(logical)) { + RESTExtreme << "Volume name '" << name << "' matches logical volume '" + << logical << "' with physical volume '" << physical << "'" << RESTendl; physicalVolumes.emplace_back( fGeant4GeometryInfo.GetAlternativeNameFromGeant4PhysicalName(physical)); } } } } else { + RESTDebug << "Volume name '" << name << "' is a valid physical volume." << RESTendl; physicalVolumes.push_back(name); } @@ -1450,7 +1461,7 @@ void TRestGeant4Metadata::ReadDetector() { for (const auto& physical : physicalVolumes) { RESTInfo << "Adding " << (isSensitive ? "sensitive" : "active") << " volume from RML: '" - << physical << (chance != 1 ? " with change: " + to_string(chance) : " ") << RESTendl; + << physical << "'" << (chance != 1 ? " with chance: " + to_string(chance) : " ") << RESTendl; SetActiveVolume(physical, chance, maxStep); if (isSensitive) { InsertSensitiveVolume(physical); From 62a4516ff5ddb06f40798763b1f7b910869eaa4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:54:36 +0000 Subject: [PATCH 05/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- inc/TRestGeant4GeometryInfo.h | 4 +- src/TRestGeant4GeometryInfo.cxx | 103 +++++++++++++++++++------------- src/TRestGeant4Metadata.cxx | 14 +++-- 3 files changed, 73 insertions(+), 48 deletions(-) diff --git a/inc/TRestGeant4GeometryInfo.h b/inc/TRestGeant4GeometryInfo.h index 01b4b8b..f20da1b 100644 --- a/inc/TRestGeant4GeometryInfo.h +++ b/inc/TRestGeant4GeometryInfo.h @@ -2,9 +2,9 @@ #ifndef REST_TRESTGEANT4GEOMETRYINFO_H #define REST_TRESTGEANT4GEOMETRYINFO_H +#include #include #include -#include #include #include @@ -45,7 +45,7 @@ class TRestGeant4GeometryInfo { std::map fGeant4AssemblyImprintToAssemblyLogicalNameMap; std::map fPhysicalToLogicalVolumeMap; - std::map > fLogicalToPhysicalMap; + std::map> fLogicalToPhysicalMap; // many physical volumes can point to one single logical std::map fLogicalToMaterialMap; std::map fPhysicalToPositionInWorldMap; diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 3b7d2e8..fb259f5 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -3,13 +3,14 @@ // #include "TRestGeant4GeometryInfo.h" -#include "TRestStringHelper.h" #include #include #include +#include "TRestStringHelper.h" + using namespace std; namespace myXml { @@ -69,21 +70,26 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { } // auxiliar maps - map gdmlToGeant4AssemblyNameMap; // to record the ordering of the assemblies definition, e.g. "assemblyName" -> "av_1" + map gdmlToGeant4AssemblyNameMap; // to record the ordering of the assemblies + // definition, e.g. "assemblyName" -> "av_1" size_t assemblyCounter = 0; - map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly - /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother volume where you are placing the assembly. - This daughter PV are named following the format: "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, - its own daughter PV are named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". - To follow this logic, we need to add this "av_WWW_impr_XXX+1"-> nestedAssemblyPVname to the fGeant4AssemblyImprintToGdmlNameMap too. - To do this we use the auxiliar map 'fixNestedAssembliesMap' to store which nested assemblies where found inside each assembly and their order. + map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly + /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother + volume where you are placing the assembly. This daughter PV are named following the format: + "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are + named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". To follow this logic, we need to add this + "av_WWW_impr_XXX+1"-> nestedAssemblyPVname to the fGeant4AssemblyImprintToGdmlNameMap too. To do this we + use the auxiliar map 'fixNestedAssembliesMap' to store which nested assemblies where found inside each + assembly and their order. Also, the daughter physical volumes defined after this nested assembly - gets the imprint number XXX+1 instead of the XXX of its mother assembly imprint (which I think is a bug in Geant4). - To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all the daughter PV - from normal LV before all the nested assembly PV. + gets the imprint number XXX+1 instead of the XXX of its mother assembly imprint (which I think is a bug in + Geant4). To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all + the daughter PV from normal LV before all the nested assembly PV. */ - map> fixNestedAssembliesMap; // parent assembly -> child PV of assemblies. Used to add later the bad imprints "av_WWW_impr_XXX+1" + map> + fixNestedAssembliesMap; // parent assembly -> child PV of assemblies. Used to add later the bad + // imprints "av_WWW_impr_XXX+1" map nameTable; map> childrenTable; @@ -91,43 +97,52 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { XMLNodePointer_t structure = myXml::FindChildByName(xml, mainNode, "structure"); XMLNodePointer_t child = xml.GetChild(structure); RESTDebug << "Searching for assemblies..." << RESTendl; - while (child) { // loop over the direct children of structure (logical volumes and assemblies) - TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" + while (child) { // loop over the direct children of structure (logical volumes and assemblies) + TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" if (!name.EqualTo("assembly")) { child = xml.GetNext(child); continue; } TString assemblyName = myXml::GetNodeAttribute(xml, child, "name"); - gdmlToGeant4AssemblyNameMap[assemblyName] = "av_" + to_string(++assemblyCounter); // first assembly is av_1 + gdmlToGeant4AssemblyNameMap[assemblyName] = + "av_" + to_string(++assemblyCounter); // first assembly is av_1 gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; RESTDebug << "Assembly found: " << assemblyName << " → " << gdmlToGeant4AssemblyNameMap[assemblyName] - << RESTendl; + << RESTendl; // blablabla bool hasNestedAssemblies = false; std::map childrenGeant4toGdmlMap; size_t childrenPVCounter = 0; - auto physicalVolumeNode = xml.GetChild(child); // children of a volume or assembly are physical volumes + auto physicalVolumeNode = + xml.GetChild(child); // children of a volume or assembly are physical volumes while (physicalVolumeNode) { auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + auto volumeRefNode = + xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation while (volumeRefNode) { - TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + TString volumeRefNodeName = + xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name - TString geant4Name = refName + "_pv_" + to_string(childrenPVCounter++); // first pv is logicalVolumeName_pv_0 + TString refName = + myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + TString geant4Name = + refName + "_pv_" + + to_string(childrenPVCounter++); // first pv is logicalVolumeName_pv_0 childrenGeant4toGdmlMap[geant4Name] = physicalVolumeName; - if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { // it's an assembly - hasNestedAssemblies = true; // to warn later that pv from assemblies must be defined last + hasNestedAssemblies = + true; // to warn later that pv from assemblies must be defined last fixNestedAssembliesMap[assemblyName].push_back(physicalVolumeName); - } else { + } else { if (hasNestedAssemblies) { RESTError << "TRestGeant4GeometryInfo::PopulateFromGdml - Assembly '" - << assemblyName << "' contains physical volumes from normal " - << "(i.e. non-assembly) logical volumes defined after physical volumes " - << "from assemblies. Due to a Geant4 bug you cannot do this. " - << "Please define the physical volumes from the assemblies last." << RESTendl; + << assemblyName << "' contains physical volumes from normal " + << "(i.e. non-assembly) logical volumes defined after physical volumes " + << "from assemblies. Due to a Geant4 bug you cannot do this. " + << "Please define the physical volumes from the assemblies last." + << RESTendl; } } } @@ -140,30 +155,37 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { } child = xml.GetChild(structure); - while (child) { // loop over the direct children of structure (logical volumes and assemblies) - TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" + while (child) { // loop over the direct children of structure (logical volumes and assemblies) + TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" TString volumeName = myXml::GetNodeAttribute(xml, child, "name"); - auto physicalVolumeNode = xml.GetChild(child); // children of a volume or assembly are physical volumes + auto physicalVolumeNode = + xml.GetChild(child); // children of a volume or assembly are physical volumes childrenTable[volumeName] = {}; while (physicalVolumeNode) { auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + auto volumeRefNode = + xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation while (volumeRefNode) { - TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + TString volumeRefNodeName = + xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + TString refName = + myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { // it's an assembly fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; - size_t imprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; // first imprint is impr_1 - TString imprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(imprintCounter); + size_t imprintCounter = + ++gdmlAssemblyNameToImprintCounterMap[refName]; // first imprint is impr_1 + TString imprint = + gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(imprintCounter); fGeant4AssemblyImprintToGdmlNameMap[imprint] = physicalVolumeName; if (fixNestedAssembliesMap.count(refName) > 0) { // fix nested assemblies for (const auto& childPV : fixNestedAssembliesMap[refName]) { size_t nestedImprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; - TString nestedImprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(nestedImprintCounter); + TString nestedImprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + + to_string(nestedImprintCounter); fGeant4AssemblyImprintToGdmlNameMap[nestedImprint] = childPV; } } @@ -249,20 +271,21 @@ TString TRestGeant4GeometryInfo::GetAlternativePathFromGeant4Path(const TString& // convert the Geant4 assembly imprint convention to GDML name // e.g. av_1_impr_2_childLV_pv_1 → assemblyName/childLV_pv_1 for (const auto& [gvName, gdmlName] : fGeant4AssemblyImprintToGdmlNameMap) { - convertedPath = Replace(convertedPath, (gvName+"_").Data(), (gdmlName+fPathSeparator).Data()); + convertedPath = Replace(convertedPath, (gvName + "_").Data(), (gdmlName + fPathSeparator).Data()); } // convert the children names inside assemblies to physical volume names in GDML // e.g. assemblyName/childLV_pv_1 → assemblyName/childPVname for (const auto& [gvImprint, assemblyLogicalName] : fGeant4AssemblyImprintToAssemblyLogicalNameMap) { auto gvImprintPosition = convertedPath.find(gvImprint.Data()); if (gvImprintPosition != std::string::npos) { - for (const auto& [childGeant4Name, childGdmlName] : fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap.at(assemblyLogicalName)) { + for (const auto& [childGeant4Name, childGdmlName] : + fGdmlAssemblyTChildrenGeant4ToGdmlPhysicalNameMap.at(assemblyLogicalName)) { TString toReplace = gvImprint + fPathSeparator + childGeant4Name; TString replaceBy = gvImprint + fPathSeparator + childGdmlName; convertedPath = Replace(convertedPath, toReplace.Data(), replaceBy.Data()); } } - } + } return TString(convertedPath); } diff --git a/src/TRestGeant4Metadata.cxx b/src/TRestGeant4Metadata.cxx index a623e8b..6e2bc5c 100644 --- a/src/TRestGeant4Metadata.cxx +++ b/src/TRestGeant4Metadata.cxx @@ -1400,20 +1400,21 @@ void TRestGeant4Metadata::ReadDetector() { << "Trying to match as regular expression for physical volumes." << RESTendl; for (const auto& physical : fGeant4GeometryInfo.GetAllPhysicalVolumesMatchingExpression(name)) { - RESTExtreme << "Volume name '" << name << "' matches physical volume '" - << physical << "'" << RESTendl; + RESTExtreme << "Volume name '" << name << "' matches physical volume '" << physical << "'" + << RESTendl; physicalVolumes.emplace_back( fGeant4GeometryInfo.GetAlternativeNameFromGeant4PhysicalName(physical)); } } if (physicalVolumes.empty()) { - RESTDebug << "Volume name '" << name << "' is not a valid logical volume neither physical volume regex. " + RESTDebug << "Volume name '" << name + << "' is not a valid logical volume neither physical volume regex. " << "Trying to match as regular expression for logical volumes." << RESTendl; for (const auto& logical : fGeant4GeometryInfo.GetAllLogicalVolumesMatchingExpression(name)) { for (const auto& physical : fGeant4GeometryInfo.GetAllPhysicalVolumesFromLogical(logical)) { - RESTExtreme << "Volume name '" << name << "' matches logical volume '" - << logical << "' with physical volume '" << physical << "'" << RESTendl; + RESTExtreme << "Volume name '" << name << "' matches logical volume '" << logical + << "' with physical volume '" << physical << "'" << RESTendl; physicalVolumes.emplace_back( fGeant4GeometryInfo.GetAlternativeNameFromGeant4PhysicalName(physical)); } @@ -1461,7 +1462,8 @@ void TRestGeant4Metadata::ReadDetector() { for (const auto& physical : physicalVolumes) { RESTInfo << "Adding " << (isSensitive ? "sensitive" : "active") << " volume from RML: '" - << physical << "'" << (chance != 1 ? " with chance: " + to_string(chance) : " ") << RESTendl; + << physical << "'" << (chance != 1 ? " with chance: " + to_string(chance) : " ") + << RESTendl; SetActiveVolume(physical, chance, maxStep); if (isSensitive) { InsertSensitiveVolume(physical); From 6046e4a9fc1f5a99be9b9fb659997740986f5a0a Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Mon, 24 Nov 2025 17:44:32 +0100 Subject: [PATCH 06/13] add function myXml::GetChildByAttributeValue --- src/TRestGeant4GeometryInfo.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index fb259f5..f1d12ea 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -36,6 +36,18 @@ TString GetNodeAttribute(TXMLEngine xml, XMLNodePointer_t node, const TString& a } return {}; } +XMLNodePointer_t GetChildByAttributeValue(TXMLEngine xml, XMLNodePointer_t node, + const TString& attributeName, const TString& attributeValue) { + XMLNodePointer_t child = xml.GetChild(node); + while (child) { + TString childAttributeValue = GetNodeAttribute(xml, child, attributeName); + if (childAttributeValue.EqualTo(attributeValue)) { + return child; + } + child = xml.GetNext(child); + } + return nullptr; +} void AddVolumesRecursively(vector* physicalNames, vector* logicalNames, const vector& children, map& nameTable, map>& childrenTable, const TString& name = "", From 70cd131de771f798b0ff0ca7754fda2f99288e4d Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Mon, 24 Nov 2025 17:45:27 +0100 Subject: [PATCH 07/13] fix imprint parsing of nested assemblies --- src/TRestGeant4GeometryInfo.cxx | 138 +++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 48 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index f1d12ea..02c0496 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -80,34 +80,18 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { << " not found" << endl; exit(1); } + XMLNodePointer_t mainNode = xml.DocGetRootElement(xmldoc); + XMLNodePointer_t structure = myXml::FindChildByName(xml, mainNode, "structure"); + XMLNodePointer_t child = xml.GetChild(structure); + - // auxiliar maps - map gdmlToGeant4AssemblyNameMap; // to record the ordering of the assemblies - // definition, e.g. "assemblyName" -> "av_1" - size_t assemblyCounter = 0; - map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother volume where you are placing the assembly. This daughter PV are named following the format: - "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are - named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". To follow this logic, we need to add this - "av_WWW_impr_XXX+1"-> nestedAssemblyPVname to the fGeant4AssemblyImprintToGdmlNameMap too. To do this we - use the auxiliar map 'fixNestedAssembliesMap' to store which nested assemblies where found inside each - assembly and their order. - - Also, the daughter physical volumes defined after this nested assembly - gets the imprint number XXX+1 instead of the XXX of its mother assembly imprint (which I think is a bug in - Geant4). To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, you must define all - the daughter PV from normal LV before all the nested assembly PV. + "av_WWW_impr_XXX_YYY_ZZZ". This first loop over the gdml structure is to get the map "assemblyName" -> "av_WWW" */ - map> - fixNestedAssembliesMap; // parent assembly -> child PV of assemblies. Used to add later the bad - // imprints "av_WWW_impr_XXX+1" - - map nameTable; - map> childrenTable; - XMLNodePointer_t mainNode = xml.DocGetRootElement(xmldoc); - XMLNodePointer_t structure = myXml::FindChildByName(xml, mainNode, "structure"); - XMLNodePointer_t child = xml.GetChild(structure); + size_t assemblyCounter = 0; // track the WWW + map gdmlToGeant4AssemblyNameMap; // e.g. "assemblyName" -> "av_1" + map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly RESTDebug << "Searching for assemblies..." << RESTendl; while (child) { // loop over the direct children of structure (logical volumes and assemblies) TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" @@ -118,11 +102,14 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { TString assemblyName = myXml::GetNodeAttribute(xml, child, "name"); gdmlToGeant4AssemblyNameMap[assemblyName] = "av_" + to_string(++assemblyCounter); // first assembly is av_1 - gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; + gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; // initialize with the assembly found RESTDebug << "Assembly found: " << assemblyName << " → " << gdmlToGeant4AssemblyNameMap[assemblyName] << RESTendl; - // blablabla + /* The daughter physical volumes defined after a nested assembly gets the imprint number XXX+1 + instead of the XXX of its mother assembly imprint (which I think is a bug in Geant4). + To avoid having several "av_WWW_impr_XXX+n" pointing to different GDML names, + you must define all the daughter PV from normal LV before all the nested assembly PV.*/ bool hasNestedAssemblies = false; std::map childrenGeant4toGdmlMap; size_t childrenPVCounter = 0; @@ -144,9 +131,7 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { childrenGeant4toGdmlMap[geant4Name] = physicalVolumeName; if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { // it's an assembly - hasNestedAssemblies = - true; // to warn later that pv from assemblies must be defined last - fixNestedAssembliesMap[assemblyName].push_back(physicalVolumeName); + hasNestedAssemblies = true; } else { if (hasNestedAssemblies) { RESTError << "TRestGeant4GeometryInfo::PopulateFromGdml - Assembly '" @@ -166,6 +151,70 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { child = xml.GetNext(child); } + /*Recursive function to obtain the prefix 'av_WWW_impr_XXX' of the daughters of the assemblies imprints. + When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother + volume where you are placing the assembly. This daughter PV are named following the format: + "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are + named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain + to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the highest + assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children imprints and + each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ + std:function ProcessNestedAssembliesRecursively = + [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { + auto physicalVolumeNode = xml.GetChild(parentNode); + //godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested chain + while(physicalVolumeNode) { + auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); + auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + while(volumeRefNode) { + TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + if (volumeRefNodeName.EqualTo("volumeref")) { + TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + TString newGodFatherAssemblyName = godFatherAssemblyName; + if (newGodFatherAssemblyName.IsNull()) { + //start assembly children chain with this assembly as godFather + newGodFatherAssemblyName = refName; + } + size_t imprintCounter = ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; + TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + "_impr_" + + to_string(imprintCounter); + TString path = pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; + fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; + // Continue the assembly children chain with its correspondant godFatherAssembly and path + auto assemblyNode = myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); + } else { + // its a regular logical volume + // Regular children resets the godFatherAssembly and path + auto assemblyNode = myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, "", ""); + } + } + volumeRefNode = xml.GetNext(volumeRefNode); + } + physicalVolumeNode = xml.GetNext(physicalVolumeNode); + } + }; + + // We loop a second time over the gdml structure to get the imprint of each assembly + // into fGeant4AssemblyImprintToGdmlNameMap: e.g. "av_2_impr_5" -> "shielding/vessel" + auto worldNode = myXml::GetChildByAttributeValue(xml, structure, "name", "world"); + if (!worldNode) { + worldNode = myXml::GetChildByAttributeValue(xml, structure, "name", "World"); + if (!worldNode) { + cout << "Could not find world volume in GDML, please name it either 'World' or 'world'" << endl; + exit(1); + } + } + TString godFatherAssemblyName = ""; // the highest assembly volume in the nested chain + TString pathSoFar = ""; // the path for the nested assemblies + ProcessNestedAssembliesRecursively(worldNode, godFatherAssemblyName, pathSoFar); + + // We loop a third time over gdml structure to get the physical and logical volumes names + map nameTable; + map> childrenTable; child = xml.GetChild(structure); while (child) { // loop over the direct children of structure (logical volumes and assemblies) TString name = xml.GetNodeName(child); // name of the type of children: "volume" or "assembly" @@ -183,25 +232,6 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { if (volumeRefNodeName.EqualTo("volumeref")) { TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name - if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { - // it's an assembly - fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; - size_t imprintCounter = - ++gdmlAssemblyNameToImprintCounterMap[refName]; // first imprint is impr_1 - TString imprint = - gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + to_string(imprintCounter); - fGeant4AssemblyImprintToGdmlNameMap[imprint] = physicalVolumeName; - - if (fixNestedAssembliesMap.count(refName) > 0) { - // fix nested assemblies - for (const auto& childPV : fixNestedAssembliesMap[refName]) { - size_t nestedImprintCounter = ++gdmlAssemblyNameToImprintCounterMap[refName]; - TString nestedImprint = gdmlToGeant4AssemblyNameMap[refName] + "_impr_" + - to_string(nestedImprintCounter); - fGeant4AssemblyImprintToGdmlNameMap[nestedImprint] = childPV; - } - } - } nameTable[physicalVolumeName] = refName; childrenTable[volumeName].push_back(physicalVolumeName); } @@ -250,6 +280,18 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { } /* + // print gdmlToGeant4AssemblyNameMap + cout << "GDML to Geant4 Assembly Name Map:" << endl; + for (const auto& kv : gdmlToGeant4AssemblyNameMap) { + cout << "\t" << kv.first << " → " << kv.second << endl; + } + + //print gdmlAssemblyNameToImprintCounterMap + cout << "GDML Assembly Name to Imprint Counter Map:" << endl; + for (const auto& kv : gdmlAssemblyNameToImprintCounterMap) { + cout << "\t" << kv.first << " → " << kv.second << endl; + } + //print fGeant4AssemblyImprintToGdmlNameMap cout << "GDML Assembly Imprint to GDML Name Map:" << endl; for (const auto& kv : fGeant4AssemblyImprintToGdmlNameMap) { From 4f4ca90101c9123d2bfcfea55518d65a75ca1970 Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Mon, 24 Nov 2025 17:46:45 +0100 Subject: [PATCH 08/13] add RML configuration parameter to set the TRestGeant4GeometryInfo::fPathSeparator --- src/TRestGeant4Metadata.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TRestGeant4Metadata.cxx b/src/TRestGeant4Metadata.cxx index 6e2bc5c..be76d09 100644 --- a/src/TRestGeant4Metadata.cxx +++ b/src/TRestGeant4Metadata.cxx @@ -829,6 +829,8 @@ void TRestGeant4Metadata::InitFromConfigFile() { } fGeometryPath = GetParameter("geometryPath", ""); + auto volPathSeparator = GetParameter("volPathSeparator", "_"); // for TRestGeant4GeometryInfo::fPathSeparator + fGeant4GeometryInfo.SetPathSeparator(volPathSeparator); fStoreHadronicTargetInfo = StringToBool(GetParameter("storeHadronicTargetInfo", "false")); From 182df27594f38af7ce27c79520494ee9b027f8fd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:47:08 +0000 Subject: [PATCH 09/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/TRestGeant4GeometryInfo.cxx | 114 +++++++++++++++++--------------- src/TRestGeant4Metadata.cxx | 3 +- 2 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 02c0496..07a426b 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -36,8 +36,8 @@ TString GetNodeAttribute(TXMLEngine xml, XMLNodePointer_t node, const TString& a } return {}; } -XMLNodePointer_t GetChildByAttributeValue(TXMLEngine xml, XMLNodePointer_t node, - const TString& attributeName, const TString& attributeValue) { +XMLNodePointer_t GetChildByAttributeValue(TXMLEngine xml, XMLNodePointer_t node, const TString& attributeName, + const TString& attributeValue) { XMLNodePointer_t child = xml.GetChild(node); while (child) { TString childAttributeValue = GetNodeAttribute(xml, child, attributeName); @@ -84,13 +84,13 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { XMLNodePointer_t structure = myXml::FindChildByName(xml, mainNode, "structure"); XMLNodePointer_t child = xml.GetChild(structure); - /* When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother volume where you are placing the assembly. This daughter PV are named following the format: - "av_WWW_impr_XXX_YYY_ZZZ". This first loop over the gdml structure is to get the map "assemblyName" -> "av_WWW" + "av_WWW_impr_XXX_YYY_ZZZ". This first loop over the gdml structure is to get the map "assemblyName" -> + "av_WWW" */ - size_t assemblyCounter = 0; // track the WWW - map gdmlToGeant4AssemblyNameMap; // e.g. "assemblyName" -> "av_1" + size_t assemblyCounter = 0; // track the WWW + map gdmlToGeant4AssemblyNameMap; // e.g. "assemblyName" -> "av_1" map gdmlAssemblyNameToImprintCounterMap; // to track the number of imprints per assembly RESTDebug << "Searching for assemblies..." << RESTendl; while (child) { // loop over the direct children of structure (logical volumes and assemblies) @@ -101,8 +101,8 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { } TString assemblyName = myXml::GetNodeAttribute(xml, child, "name"); gdmlToGeant4AssemblyNameMap[assemblyName] = - "av_" + to_string(++assemblyCounter); // first assembly is av_1 - gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; // initialize with the assembly found + "av_" + to_string(++assemblyCounter); // first assembly is av_1 + gdmlAssemblyNameToImprintCounterMap[assemblyName] = 0; // initialize with the assembly found RESTDebug << "Assembly found: " << assemblyName << " → " << gdmlToGeant4AssemblyNameMap[assemblyName] << RESTendl; @@ -151,54 +151,64 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { child = xml.GetNext(child); } - /*Recursive function to obtain the prefix 'av_WWW_impr_XXX' of the daughters of the assemblies imprints. - When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother - volume where you are placing the assembly. This daughter PV are named following the format: - "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are - named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain - to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the highest - assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children imprints and - each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ - std:function ProcessNestedAssembliesRecursively = +/*Recursive function to obtain the prefix 'av_WWW_impr_XXX' of the daughters of the assemblies imprints. +When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother +volume where you are placing the assembly. This daughter PV are named following the format: +"av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are +named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain +to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the +highest assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children +imprints and each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ +std: + function ProcessNestedAssembliesRecursively = [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { - auto physicalVolumeNode = xml.GetChild(parentNode); - //godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested chain - while(physicalVolumeNode) { - auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation - while(volumeRefNode) { - TString volumeRefNodeName = xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" - if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name - if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { - // it's an assembly - TString newGodFatherAssemblyName = godFatherAssemblyName; - if (newGodFatherAssemblyName.IsNull()) { - //start assembly children chain with this assembly as godFather - newGodFatherAssemblyName = refName; + auto physicalVolumeNode = xml.GetChild(parentNode); + // godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested + // chain + while (physicalVolumeNode) { + auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); + auto volumeRefNode = + xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + while (volumeRefNode) { + TString volumeRefNodeName = + xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + if (volumeRefNodeName.EqualTo("volumeref")) { + TString refName = + myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + TString newGodFatherAssemblyName = godFatherAssemblyName; + if (newGodFatherAssemblyName.IsNull()) { + // start assembly children chain with this assembly as godFather + newGodFatherAssemblyName = refName; + } + size_t imprintCounter = + ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; + TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + + "_impr_" + to_string(imprintCounter); + TString path = + pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; + fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; + // Continue the assembly children chain with its correspondant godFatherAssembly + // and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); + } else { + // its a regular logical volume + // Regular children resets the godFatherAssembly and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, "", ""); } - size_t imprintCounter = ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; - TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + "_impr_" + - to_string(imprintCounter); - TString path = pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; - fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; - // Continue the assembly children chain with its correspondant godFatherAssembly and path - auto assemblyNode = myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); - } else { - // its a regular logical volume - // Regular children resets the godFatherAssembly and path - auto assemblyNode = myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, "", ""); } + volumeRefNode = xml.GetNext(volumeRefNode); } - volumeRefNode = xml.GetNext(volumeRefNode); + physicalVolumeNode = xml.GetNext(physicalVolumeNode); } - physicalVolumeNode = xml.GetNext(physicalVolumeNode); - } - }; + }; - // We loop a second time over the gdml structure to get the imprint of each assembly + // We loop a second time over the gdml structure to get the imprint of each assembly // into fGeant4AssemblyImprintToGdmlNameMap: e.g. "av_2_impr_5" -> "shielding/vessel" auto worldNode = myXml::GetChildByAttributeValue(xml, structure, "name", "world"); if (!worldNode) { @@ -208,8 +218,8 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { exit(1); } } - TString godFatherAssemblyName = ""; // the highest assembly volume in the nested chain - TString pathSoFar = ""; // the path for the nested assemblies + TString godFatherAssemblyName = ""; // the highest assembly volume in the nested chain + TString pathSoFar = ""; // the path for the nested assemblies ProcessNestedAssembliesRecursively(worldNode, godFatherAssemblyName, pathSoFar); // We loop a third time over gdml structure to get the physical and logical volumes names diff --git a/src/TRestGeant4Metadata.cxx b/src/TRestGeant4Metadata.cxx index be76d09..04eb90a 100644 --- a/src/TRestGeant4Metadata.cxx +++ b/src/TRestGeant4Metadata.cxx @@ -829,7 +829,8 @@ void TRestGeant4Metadata::InitFromConfigFile() { } fGeometryPath = GetParameter("geometryPath", ""); - auto volPathSeparator = GetParameter("volPathSeparator", "_"); // for TRestGeant4GeometryInfo::fPathSeparator + auto volPathSeparator = + GetParameter("volPathSeparator", "_"); // for TRestGeant4GeometryInfo::fPathSeparator fGeant4GeometryInfo.SetPathSeparator(volPathSeparator); fStoreHadronicTargetInfo = StringToBool(GetParameter("storeHadronicTargetInfo", "false")); From 71a4a966cb99746e233296dcd2951f132a11ae6d Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Mon, 24 Nov 2025 17:59:59 +0100 Subject: [PATCH 10/13] fix std::function typo and indentation --- src/TRestGeant4GeometryInfo.cxx | 103 ++++++++++++++++---------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 07a426b..a70f611 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -151,62 +151,61 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { child = xml.GetNext(child); } -/*Recursive function to obtain the prefix 'av_WWW_impr_XXX' of the daughters of the assemblies imprints. -When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother -volume where you are placing the assembly. This daughter PV are named following the format: -"av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are -named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain -to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the -highest assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children -imprints and each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ -std: - function ProcessNestedAssembliesRecursively = - [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { - auto physicalVolumeNode = xml.GetChild(parentNode); - // godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested - // chain - while (physicalVolumeNode) { - auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = - xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation - while (volumeRefNode) { - TString volumeRefNodeName = - xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" - if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = - myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name - if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { - // it's an assembly - TString newGodFatherAssemblyName = godFatherAssemblyName; - if (newGodFatherAssemblyName.IsNull()) { - // start assembly children chain with this assembly as godFather - newGodFatherAssemblyName = refName; - } - size_t imprintCounter = - ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; - TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + - "_impr_" + to_string(imprintCounter); - TString path = - pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; - fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; - // Continue the assembly children chain with its correspondant godFatherAssembly - // and path - auto assemblyNode = - myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); - } else { - // its a regular logical volume - // Regular children resets the godFatherAssembly and path - auto assemblyNode = - myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, "", ""); + /*Recursive function to obtain the prefix 'av_WWW_impr_XXX' of the daughters of the assemblies imprints. + When a PV is placed from an assembly, its daughter physical volumes are imprinted into the mother + volume where you are placing the assembly. This daughter PV are named following the format: + "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are + named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain + to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the + highest assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children + imprints and each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ + std::function ProcessNestedAssembliesRecursively = + [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { + auto physicalVolumeNode = xml.GetChild(parentNode); + // godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested + // chain + while (physicalVolumeNode) { + auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); + auto volumeRefNode = + xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + while (volumeRefNode) { + TString volumeRefNodeName = + xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + if (volumeRefNodeName.EqualTo("volumeref")) { + TString refName = + myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + TString newGodFatherAssemblyName = godFatherAssemblyName; + if (newGodFatherAssemblyName.IsNull()) { + // start assembly children chain with this assembly as godFather + newGodFatherAssemblyName = refName; } + size_t imprintCounter = + ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; + TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + + "_impr_" + to_string(imprintCounter); + TString path = + pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; + fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; + // Continue the assembly children chain with its correspondant godFatherAssembly + // and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); + } else { + // its a regular logical volume + // Regular children resets the godFatherAssembly and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, "", ""); } - volumeRefNode = xml.GetNext(volumeRefNode); } - physicalVolumeNode = xml.GetNext(physicalVolumeNode); + volumeRefNode = xml.GetNext(volumeRefNode); } - }; + physicalVolumeNode = xml.GetNext(physicalVolumeNode); + } + }; // We loop a second time over the gdml structure to get the imprint of each assembly // into fGeant4AssemblyImprintToGdmlNameMap: e.g. "av_2_impr_5" -> "shielding/vessel" From 36182222de6e600e0a3c84da88ef67293347a1f2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 17:00:10 +0000 Subject: [PATCH 11/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/TRestGeant4GeometryInfo.cxx | 89 +++++++++++++++++---------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index a70f611..15ce484 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -157,55 +157,56 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { "av_WWW_impr_XXX_YYY_ZZZ". But, if one of those daughter PV is itself an assembly, its own daughter PV are named (wrongly in my opinion) "av_WWW_impr_XXX+1_yyy_zzz". This behavior is propagated down the chain to the final child assembly which does not have any daughter assembly. So, the godFatherAssembly is the - highest assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly children - imprints and each of this assembly children adds +1 to the imprint number of the godFatherAssembly.*/ + highest assembly which begins this chain and its "av_WWW" is used for all its consecutive assembly + children imprints and each of this assembly children adds +1 to the imprint number of the + godFatherAssembly.*/ std::function ProcessNestedAssembliesRecursively = - [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { - auto physicalVolumeNode = xml.GetChild(parentNode); - // godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested - // chain - while (physicalVolumeNode) { - auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); - auto volumeRefNode = - xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation - while (volumeRefNode) { - TString volumeRefNodeName = - xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" - if (volumeRefNodeName.EqualTo("volumeref")) { - TString refName = - myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name - if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { - // it's an assembly - TString newGodFatherAssemblyName = godFatherAssemblyName; - if (newGodFatherAssemblyName.IsNull()) { - // start assembly children chain with this assembly as godFather - newGodFatherAssemblyName = refName; + [&](const XMLNodePointer_t parentNode, const TString godFatherAssemblyName, const TString pathSoFar) { + auto physicalVolumeNode = xml.GetChild(parentNode); + // godFatherAssemblyName = parentNode logical name // the highest assembly volume in the nested + // chain + while (physicalVolumeNode) { + auto physicalVolumeName = myXml::GetNodeAttribute(xml, physicalVolumeNode, "name"); + auto volumeRefNode = + xml.GetChild(physicalVolumeNode); // this are volumeref, position and rotation + while (volumeRefNode) { + TString volumeRefNodeName = + xml.GetNodeName(volumeRefNode); // "volumeref", "position" or "rotation" + if (volumeRefNodeName.EqualTo("volumeref")) { + TString refName = + myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + // it's an assembly + TString newGodFatherAssemblyName = godFatherAssemblyName; + if (newGodFatherAssemblyName.IsNull()) { + // start assembly children chain with this assembly as godFather + newGodFatherAssemblyName = refName; + } + size_t imprintCounter = + ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; + TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + + "_impr_" + to_string(imprintCounter); + TString path = + pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; + fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; + // Continue the assembly children chain with its correspondant godFatherAssembly + // and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); + } else { + // its a regular logical volume + // Regular children resets the godFatherAssembly and path + auto assemblyNode = + myXml::GetChildByAttributeValue(xml, structure, "name", refName); + ProcessNestedAssembliesRecursively(assemblyNode, "", ""); } - size_t imprintCounter = - ++gdmlAssemblyNameToImprintCounterMap[newGodFatherAssemblyName]; - TString imprint = gdmlToGeant4AssemblyNameMap[newGodFatherAssemblyName] + - "_impr_" + to_string(imprintCounter); - TString path = - pathSoFar + (pathSoFar.IsNull() ? "" : fPathSeparator) + physicalVolumeName; - fGeant4AssemblyImprintToGdmlNameMap[imprint] = path; - // Continue the assembly children chain with its correspondant godFatherAssembly - // and path - auto assemblyNode = - myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, newGodFatherAssemblyName, path); - } else { - // its a regular logical volume - // Regular children resets the godFatherAssembly and path - auto assemblyNode = - myXml::GetChildByAttributeValue(xml, structure, "name", refName); - ProcessNestedAssembliesRecursively(assemblyNode, "", ""); } + volumeRefNode = xml.GetNext(volumeRefNode); } - volumeRefNode = xml.GetNext(volumeRefNode); + physicalVolumeNode = xml.GetNext(physicalVolumeNode); } - physicalVolumeNode = xml.GetNext(physicalVolumeNode); - } - }; + }; // We loop a second time over the gdml structure to get the imprint of each assembly // into fGeant4AssemblyImprintToGdmlNameMap: e.g. "av_2_impr_5" -> "shielding/vessel" From e868d5b592c4294973af10e3eb4d151217f39a31 Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Mon, 24 Nov 2025 22:36:37 +0100 Subject: [PATCH 12/13] add forgotten filling of fGeant4AssemblyImprintToAssemblyLogicalNameMap --- src/TRestGeant4GeometryInfo.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index 15ce484..c0145d8 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -131,6 +131,7 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { childrenGeant4toGdmlMap[geant4Name] = physicalVolumeName; if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { // it's an assembly + fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; hasNestedAssemblies = true; } else { if (hasNestedAssemblies) { From d6f78a6f9debe09afb74c51737fb69744599809e Mon Sep 17 00:00:00 2001 From: Alvaro Ezquerro Date: Fri, 28 Nov 2025 18:58:47 +0100 Subject: [PATCH 13/13] fix filling of fGeant4AssemblyImprintToAssemblyLogicalNameMap --- src/TRestGeant4GeometryInfo.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/TRestGeant4GeometryInfo.cxx b/src/TRestGeant4GeometryInfo.cxx index c0145d8..35c6898 100644 --- a/src/TRestGeant4GeometryInfo.cxx +++ b/src/TRestGeant4GeometryInfo.cxx @@ -130,8 +130,6 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { to_string(childrenPVCounter++); // first pv is logicalVolumeName_pv_0 childrenGeant4toGdmlMap[geant4Name] = physicalVolumeName; if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { - // it's an assembly - fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; hasNestedAssemblies = true; } else { if (hasNestedAssemblies) { @@ -245,6 +243,9 @@ void TRestGeant4GeometryInfo::PopulateFromGdml(const TString& gdmlFilename) { myXml::GetNodeAttribute(xml, volumeRefNode, "ref"); // the logical volume name nameTable[physicalVolumeName] = refName; childrenTable[volumeName].push_back(physicalVolumeName); + if (gdmlToGeant4AssemblyNameMap.count(refName) > 0) { + fGeant4AssemblyImprintToAssemblyLogicalNameMap[physicalVolumeName] = refName; + } } volumeRefNode = xml.GetNext(volumeRefNode); }