diff --git a/src/SHiPGeometry.cpp b/src/SHiPGeometry.cpp index 053d909..7ff5d1a 100644 --- a/src/SHiPGeometry.cpp +++ b/src/SHiPGeometry.cpp @@ -16,6 +16,7 @@ #include "UpstreamTagger/UpstreamTaggerFactory.h" #include +#include #include #include #include @@ -42,7 +43,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { // Note: These are relative to the cave origin constexpr double cm = GeoModelKernelUnits::cm; GeoTrf::Transform3D targetTrf = GeoTrf::Translate3D(0.0, -14.45 * cm, 43.25 * cm); - world->add(new GeoNameTag("target_vacuum_box")); + world->add(new GeoNameTag("/SHiP/target")); + world->add(new GeoIdentifierTag(1)); world->add(new GeoTransform(targetTrf)); world->add(target); @@ -51,7 +53,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { MuonShieldFactory muonShieldFactory(materials); GeoPhysVol* muonShield = muonShieldFactory.build(); GeoTrf::Transform3D muonShieldTrf = GeoTrf::Translate3D(0.0, 0.0, 16763.3); - world->add(new GeoNameTag("MuonShieldArea")); + world->add(new GeoNameTag("/SHiP/muon_shield")); + world->add(new GeoIdentifierTag(2)); world->add(new GeoTransform(muonShieldTrf)); world->add(muonShield); @@ -61,7 +64,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { UpstreamTaggerFactory upstreamTaggerFactory(materials); GeoVPhysVol* upstreamTagger = upstreamTaggerFactory.build(&ubtManager); GeoTrf::Transform3D upstreamTaggerTrf = GeoTrf::Translate3D(0.0, 0.0, 32.72 * 1000.0); - world->add(new GeoNameTag("Upstream_Tagger")); + world->add(new GeoNameTag("/SHiP/upstream_tagger")); + world->add(new GeoIdentifierTag(3)); world->add(new GeoTransform(upstreamTaggerTrf)); world->add(upstreamTagger); @@ -71,7 +75,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { GeoPhysVol* decayVolume = decayVolumeFactory.build(); GeoTrf::Transform3D decayVolumeTrf = GeoTrf::Translate3D(0.0, 0.0, 58.12 * 1000.0); // Convert m to mm - world->add(new GeoNameTag("DecayVolume")); + world->add(new GeoNameTag("/SHiP/decay_volume")); + world->add(new GeoIdentifierTag(4)); world->add(new GeoTransform(decayVolumeTrf)); world->add(decayVolume); @@ -84,7 +89,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { constexpr double trackersCentreZ = (84.07 + 95.07) / 2.0 * 1000.0; // Average of station 1 and 4 centres GeoTrf::Transform3D trackersTrf = GeoTrf::Translate3D(0.0, 0.0, trackersCentreZ); - world->add(new GeoNameTag("TrackersContainer")); + world->add(new GeoNameTag("/SHiP/trackers")); + world->add(new GeoIdentifierTag(5)); world->add(new GeoTransform(trackersTrf)); world->add(trackers); @@ -94,7 +100,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { GeoPhysVol* magnet = magnetFactory.build(); GeoTrf::Transform3D magnetTrf = GeoTrf::Translate3D(0.0, 0.0, 89.57 * 1000.0); // Convert m to mm - world->add(new GeoNameTag("Magnet")); + world->add(new GeoNameTag("/SHiP/magnet")); + world->add(new GeoIdentifierTag(6)); world->add(new GeoTransform(magnetTrf)); world->add(magnet); @@ -104,7 +111,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { GeoPhysVol* timingDetector = timingDetectorFactory.build(); GeoTrf::Transform3D timingDetectorTrf = GeoTrf::Translate3D(0.0, 0.0, 95.902 * 1000.0); // Convert m to mm - world->add(new GeoNameTag("TimingDetector")); + world->add(new GeoNameTag("/SHiP/timing_detector")); + world->add(new GeoIdentifierTag(7)); world->add(new GeoTransform(timingDetectorTrf)); world->add(timingDetector); @@ -114,7 +122,8 @@ GeoPhysVol* SHiPGeometryBuilder::build() { GeoPhysVol* calorimeter = calorimeterFactory.build(); GeoTrf::Transform3D calorimeterTrf = GeoTrf::Translate3D(0.0, 0.0, 98.32 * 1000.0); // Convert m to mm - world->add(new GeoNameTag("CalorimeterContainer")); + world->add(new GeoNameTag("/SHiP/calorimeter")); + world->add(new GeoIdentifierTag(8)); world->add(new GeoTransform(calorimeterTrf)); world->add(calorimeter); diff --git a/subsystems/Calorimeter/src/CalorimeterFactory.cpp b/subsystems/Calorimeter/src/CalorimeterFactory.cpp index 9ffeddb..ef3c9d3 100644 --- a/subsystems/Calorimeter/src/CalorimeterFactory.cpp +++ b/subsystems/Calorimeter/src/CalorimeterFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -21,42 +22,45 @@ GeoPhysVol* CalorimeterFactory::build() { // Create container volume that spans all calorimeter components auto* containerBox = new GeoBox(s_containerHalfX, s_containerHalfY, s_containerHalfZ); - auto* containerLog = new GeoLogVol("CalorimeterContainer", containerBox, air); + auto* containerLog = new GeoLogVol("/SHiP/calorimeter", containerBox, air); auto* containerPhys = new GeoPhysVol(containerLog); // Create and place ECAL front auto* ecalFrontBox = new GeoBox(s_ecalFrontHalfX, s_ecalFrontHalfY, s_ecalFrontHalfZ); - auto* ecalFrontLog = new GeoLogVol("ECAL_front", ecalFrontBox, air); + auto* ecalFrontLog = new GeoLogVol("/SHiP/calorimeter/ecal_front", ecalFrontBox, air); auto* ecalFrontPhys = new GeoPhysVol(ecalFrontLog); double ecalFrontRelativeZ = s_ecalFrontZ - s_containerCentreZ; GeoTrf::Transform3D ecalFrontTrf = GeoTrf::Translate3D(0.0, 0.0, ecalFrontRelativeZ); - containerPhys->add(new GeoNameTag("ECAL_front")); + containerPhys->add(new GeoNameTag("/SHiP/calorimeter/ecal_front")); + containerPhys->add(new GeoIdentifierTag(0)); containerPhys->add(new GeoTransform(ecalFrontTrf)); containerPhys->add(ecalFrontPhys); // Create and place ECAL back auto* ecalBackBox = new GeoBox(s_ecalBackHalfX, s_ecalBackHalfY, s_ecalBackHalfZ); - auto* ecalBackLog = new GeoLogVol("ECAL_back", ecalBackBox, air); + auto* ecalBackLog = new GeoLogVol("/SHiP/calorimeter/ecal_back", ecalBackBox, air); auto* ecalBackPhys = new GeoPhysVol(ecalBackLog); double ecalBackRelativeZ = s_ecalBackZ - s_containerCentreZ; GeoTrf::Transform3D ecalBackTrf = GeoTrf::Translate3D(0.0, 0.0, ecalBackRelativeZ); - containerPhys->add(new GeoNameTag("ECAL_back")); + containerPhys->add(new GeoNameTag("/SHiP/calorimeter/ecal_back")); + containerPhys->add(new GeoIdentifierTag(1)); containerPhys->add(new GeoTransform(ecalBackTrf)); containerPhys->add(ecalBackPhys); // Create and place HCAL auto* hcalBox = new GeoBox(s_hcalHalfX, s_hcalHalfY, s_hcalHalfZ); - auto* hcalLog = new GeoLogVol("HCAL", hcalBox, air); + auto* hcalLog = new GeoLogVol("/SHiP/calorimeter/hcal", hcalBox, air); auto* hcalPhys = new GeoPhysVol(hcalLog); double hcalRelativeZ = s_hcalZ - s_containerCentreZ; GeoTrf::Transform3D hcalTrf = GeoTrf::Translate3D(0.0, 0.0, hcalRelativeZ); - containerPhys->add(new GeoNameTag("HCAL")); + containerPhys->add(new GeoNameTag("/SHiP/calorimeter/hcal")); + containerPhys->add(new GeoIdentifierTag(2)); containerPhys->add(new GeoTransform(hcalTrf)); containerPhys->add(hcalPhys); diff --git a/subsystems/Cavern/src/CavernFactory.cpp b/subsystems/Cavern/src/CavernFactory.cpp index fd488ae..47acc95 100644 --- a/subsystems/Cavern/src/CavernFactory.cpp +++ b/subsystems/Cavern/src/CavernFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -66,12 +67,13 @@ GeoPhysVol* CavernFactory::build() { const GeoShape* cavernShape = &(shape4->subtract((*targetPit) << targetPitTrf)); // Create cavern logical and physical volumes - auto* cavernLog = new GeoLogVol("Cavern", cavernShape, concrete); + auto* cavernLog = new GeoLogVol("/SHiP/cavern", cavernShape, concrete); m_cavern = new GeoPhysVol(cavernLog); // Place cavern in world with name tag GeoTrf::Transform3D cavernTrf = GeoTrf::Translate3D(0.0, 0.0, s_cavernPosZ); - m_world->add(new GeoNameTag("Cavern")); + m_world->add(new GeoNameTag("/SHiP/cavern")); + m_world->add(new GeoIdentifierTag(0)); m_world->add(new GeoTransform(cavernTrf)); m_world->add(m_cavern); diff --git a/subsystems/DecayVolume/src/DecayVolumeFactory.cpp b/subsystems/DecayVolume/src/DecayVolumeFactory.cpp index 7ad71e8..ae96a30 100644 --- a/subsystems/DecayVolume/src/DecayVolumeFactory.cpp +++ b/subsystems/DecayVolume/src/DecayVolumeFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -22,16 +23,17 @@ GeoPhysVol* DecayVolumeFactory::build() { // Outer aluminium vessel auto* outerBox = new GeoBox(s_halfX, s_halfY, s_halfZ); - auto* outerLog = new GeoLogVol("DecayVolume", outerBox, aluminium); + auto* outerLog = new GeoLogVol("/SHiP/decay_volume", outerBox, aluminium); auto* outerPhys = new GeoPhysVol(outerLog); // Inner helium atmosphere auto* innerBox = new GeoBox(s_innerHalfX, s_innerHalfY, s_innerHalfZ); - auto* innerLog = new GeoLogVol("DecayVacuum", innerBox, helium); + auto* innerLog = new GeoLogVol("/SHiP/decay_volume/vacuum", innerBox, helium); auto* innerPhys = new GeoPhysVol(innerLog); // Place inner at centre of outer - outerPhys->add(new GeoNameTag("DecayVacuum")); + outerPhys->add(new GeoNameTag("/SHiP/decay_volume/vacuum")); + outerPhys->add(new GeoIdentifierTag(0)); outerPhys->add(new GeoTransform(GeoTrf::TranslateZ3D(0.0))); outerPhys->add(innerPhys); diff --git a/subsystems/Magnet/src/MagnetFactory.cpp b/subsystems/Magnet/src/MagnetFactory.cpp index 5dae356..7a2cd6c 100644 --- a/subsystems/Magnet/src/MagnetFactory.cpp +++ b/subsystems/Magnet/src/MagnetFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -22,57 +23,66 @@ GeoPhysVol* MagnetFactory::build() { // Create container auto* containerBox = new GeoBox(s_containerHalfX, s_containerHalfY, s_containerHalfZ); - auto* containerLog = new GeoLogVol("SHiPMagnet", containerBox, air); + auto* containerLog = new GeoLogVol("/SHiP/magnet", containerBox, air); auto* containerPhys = new GeoPhysVol(containerLog); // Create and place yoke auto* yoke = createYoke(); - containerPhys->add(new GeoNameTag("magyoke")); + containerPhys->add(new GeoNameTag("/SHiP/magnet/yoke")); + containerPhys->add(new GeoIdentifierTag(0)); containerPhys->add(new GeoTransform(GeoTrf::Transform3D::Identity())); containerPhys->add(yoke); // Create and place coils - auto* coil1 = createCoil("MCoil1"); - containerPhys->add(new GeoNameTag("MCoil1")); + auto* coil1 = createCoil("/SHiP/magnet/coil_1"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/coil_1")); + containerPhys->add(new GeoIdentifierTag(1)); containerPhys->add(new GeoTransform(GeoTrf::Translate3D(s_coilXOffset, s_coilYOffset, 0.0))); containerPhys->add(coil1); - auto* coil2 = createCoil("MCoil2"); - containerPhys->add(new GeoNameTag("MCoil2")); + auto* coil2 = createCoil("/SHiP/magnet/coil_2"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/coil_2")); + containerPhys->add(new GeoIdentifierTag(2)); containerPhys->add(new GeoTransform(GeoTrf::Translate3D(-s_coilXOffset, s_coilYOffset, 0.0))); containerPhys->add(coil2); - auto* coil3 = createCoil("MCoil3"); - containerPhys->add(new GeoNameTag("MCoil3")); + auto* coil3 = createCoil("/SHiP/magnet/coil_3"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/coil_3")); + containerPhys->add(new GeoIdentifierTag(3)); containerPhys->add(new GeoTransform(GeoTrf::Translate3D(s_coilXOffset, -s_coilYOffset, 0.0))); containerPhys->add(coil3); - auto* coil4 = createCoil("MCoil4"); - containerPhys->add(new GeoNameTag("MCoil4")); + auto* coil4 = createCoil("/SHiP/magnet/coil_4"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/coil_4")); + containerPhys->add(new GeoIdentifierTag(4)); containerPhys->add(new GeoTransform(GeoTrf::Translate3D(-s_coilXOffset, -s_coilYOffset, 0.0))); containerPhys->add(coil4); // Create and place vertical connectors - auto* cv1 = createVerticalConnector("CV_1"); - containerPhys->add(new GeoNameTag("CV_1")); + auto* cv1 = createVerticalConnector("/SHiP/magnet/connector_1"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/connector_1")); + containerPhys->add(new GeoIdentifierTag(5)); containerPhys->add( new GeoTransform(GeoTrf::Translate3D(s_connectorXOffset, 0.0, -s_connectorZOffset))); containerPhys->add(cv1); - auto* cv2 = createVerticalConnector("CV_2"); - containerPhys->add(new GeoNameTag("CV_2")); + auto* cv2 = createVerticalConnector("/SHiP/magnet/connector_2"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/connector_2")); + containerPhys->add(new GeoIdentifierTag(6)); containerPhys->add( new GeoTransform(GeoTrf::Translate3D(-s_connectorXOffset, 0.0, -s_connectorZOffset))); containerPhys->add(cv2); - auto* cv3 = createVerticalConnector("CV_3"); - containerPhys->add(new GeoNameTag("CV_3")); + auto* cv3 = createVerticalConnector("/SHiP/magnet/connector_3"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/connector_3")); + containerPhys->add(new GeoIdentifierTag(7)); containerPhys->add( new GeoTransform(GeoTrf::Translate3D(s_connectorXOffset, 0.0, s_connectorZOffset))); containerPhys->add(cv3); - auto* cv4 = createVerticalConnector("CV_4"); - containerPhys->add(new GeoNameTag("CV_4")); + auto* cv4 = createVerticalConnector("/SHiP/magnet/connector_4"); + containerPhys->add(new GeoNameTag("/SHiP/magnet/connector_4")); + containerPhys->add(new GeoIdentifierTag(8)); containerPhys->add( new GeoTransform(GeoTrf::Translate3D(-s_connectorXOffset, 0.0, s_connectorZOffset))); containerPhys->add(cv4); @@ -90,7 +100,7 @@ GeoPhysVol* MagnetFactory::createYoke() { // Subtract to create yoke shape const GeoShape* yokeShape = &(outerBox->subtract(*innerBox)); - auto* yokeLog = new GeoLogVol("magyoke", yokeShape, iron); + auto* yokeLog = new GeoLogVol("/SHiP/magnet/yoke", yokeShape, iron); return new GeoPhysVol(yokeLog); } diff --git a/subsystems/MuonShield/src/MuonShieldFactory.cpp b/subsystems/MuonShield/src/MuonShieldFactory.cpp index ade1599..52c3202 100644 --- a/subsystems/MuonShield/src/MuonShieldFactory.cpp +++ b/subsystems/MuonShield/src/MuonShieldFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -24,105 +25,105 @@ namespace SHiPGeometry { // --------------------------------------------------------------------------- const MuonShieldFactory::StationData MuonShieldFactory::k_stations[6] = { // ── MagnAbsorb (GDML z = 319.5 cm, dz = 115.5 cm) ────────────────── - {"MagnAbsorb", + {"magn_absorb", -13568.3, 1020.0, 1691.0, 1155.0, { - {250.0, 1690.0, 1155.0, 250.0, -1.0, "MiddleMagL"}, - {250.0, 1690.0, 1155.0, -250.0, 1.0, "MiddleMagR"}, - {250.0, 1690.0, 1155.0, 770.0, 0.0, "MagRetL"}, - {250.0, 1690.0, 1155.0, -770.0, 0.0, "MagRetR"}, - {510.0, 250.0, 1155.0, 510.0, 1440.0, "MagTopLeft"}, - {510.0, 250.0, 1155.0, -510.0, 1440.0, "MagTopRight"}, - {510.0, 250.0, 1155.0, 510.0, -1440.0, "MagBotLeft"}, - {510.0, 250.0, 1155.0, -510.0, -1440.0, "MagBotRight"}, + {250.0, 1690.0, 1155.0, 250.0, -1.0, "middle_mag_l"}, + {250.0, 1690.0, 1155.0, -250.0, 1.0, "middle_mag_r"}, + {250.0, 1690.0, 1155.0, 770.0, 0.0, "mag_ret_l"}, + {250.0, 1690.0, 1155.0, -770.0, 0.0, "mag_ret_r"}, + {510.0, 250.0, 1155.0, 510.0, 1440.0, "mag_top_left"}, + {510.0, 250.0, 1155.0, -510.0, 1440.0, "mag_top_right"}, + {510.0, 250.0, 1155.0, 510.0, -1440.0, "mag_bot_left"}, + {510.0, 250.0, 1155.0, -510.0, -1440.0, "mag_bot_right"}, }}, // ── Magn1 (GDML z = 950 cm, dz = 495 cm) ──────────────────────────── - {"Magn1", + {"magn_1", -7263.3, 1697.0, 1230.0, 4950.0, { - {399.6, 1228.2, 4950.0, 399.6, 0.0, "MiddleMagL"}, - {399.6, 1228.2, 4950.0, -399.6, 0.0, "MiddleMagR"}, - {487.7, 1229.2, 4950.0, 1208.7, 0.0, "MagRetL"}, - {487.7, 1229.2, 4950.0, -1208.7, 0.0, "MagRetR"}, - {848.2, 479.6, 4950.0, 848.2, 749.6, "MagTopLeft"}, - {848.2, 479.6, 4950.0, -848.2, 749.6, "MagTopRight"}, - {848.2, 479.6, 4950.0, 848.2, -749.6, "MagBotLeft"}, - {848.2, 479.6, 4950.0, -848.2, -749.6, "MagBotRight"}, + {399.6, 1228.2, 4950.0, 399.6, 0.0, "middle_mag_l"}, + {399.6, 1228.2, 4950.0, -399.6, 0.0, "middle_mag_r"}, + {487.7, 1229.2, 4950.0, 1208.7, 0.0, "mag_ret_l"}, + {487.7, 1229.2, 4950.0, -1208.7, 0.0, "mag_ret_r"}, + {848.2, 479.6, 4950.0, 848.2, 749.6, "mag_top_left"}, + {848.2, 479.6, 4950.0, -848.2, 749.6, "mag_top_right"}, + {848.2, 479.6, 4950.0, 848.2, -749.6, "mag_bot_left"}, + {848.2, 479.6, 4950.0, -848.2, -749.6, "mag_bot_right"}, }}, // ── Magn2 (GDML z = 1735.48 cm, dz = 280.48 cm) ───────────────────── - {"Magn2", + {"magn_2", 591.5, 1736.0, 1056.0, 2804.8, { - {265.6, 1054.6, 2804.8, 265.6, 0.0, "MiddleMagL"}, - {265.6, 1054.6, 2804.8, -265.6, 0.0, "MiddleMagR"}, - {594.7, 1055.6, 2804.8, 1140.3, 0.0, "MagRetL"}, - {594.7, 1055.6, 2804.8, -1140.3, 0.0, "MagRetR"}, - {867.5, 312.8, 2804.8, 867.5, 742.8, "MagTopLeft"}, - {867.5, 312.8, 2804.8, -867.5, 742.8, "MagTopRight"}, - {867.5, 312.8, 2804.8, 867.5, -742.8, "MagBotLeft"}, - {867.5, 312.8, 2804.8, -867.5, -742.8, "MagBotRight"}, + {265.6, 1054.6, 2804.8, 265.6, 0.0, "middle_mag_l"}, + {265.6, 1054.6, 2804.8, -265.6, 0.0, "middle_mag_r"}, + {594.7, 1055.6, 2804.8, 1140.3, 0.0, "mag_ret_l"}, + {594.7, 1055.6, 2804.8, -1140.3, 0.0, "mag_ret_r"}, + {867.5, 312.8, 2804.8, 867.5, 742.8, "mag_top_left"}, + {867.5, 312.8, 2804.8, -867.5, 742.8, "mag_top_right"}, + {867.5, 312.8, 2804.8, 867.5, -742.8, "mag_bot_left"}, + {867.5, 312.8, 2804.8, -867.5, -742.8, "mag_bot_right"}, }}, // ── Magn3 (GDML z = 2258.49 cm, dz = 232.53 cm) ───────────────────── - {"Magn3", + {"magn_3", 5821.6, 1781.0, 597.0, 2325.3, { - {18.4, 595.8, 2325.3, 23.4, 0.0, "MiddleMagL"}, - {18.4, 595.8, 2325.3, -23.4, 0.0, "MiddleMagR"}, - {849.3, 596.8, 2325.3, 931.6, 0.0, "MagRetL"}, - {849.3, 596.8, 2325.3, -931.6, 0.0, "MagRetR"}, - {888.0, 18.4, 2325.3, 893.0, 578.4, "MagTopLeft"}, - {888.0, 18.4, 2325.3, -893.0, 578.4, "MagTopRight"}, - {888.0, 18.4, 2325.3, 893.0, -578.4, "MagBotLeft"}, - {888.0, 18.4, 2325.3, -893.0, -578.4, "MagBotRight"}, + {18.4, 595.8, 2325.3, 23.4, 0.0, "middle_mag_l"}, + {18.4, 595.8, 2325.3, -23.4, 0.0, "middle_mag_r"}, + {849.3, 596.8, 2325.3, 931.6, 0.0, "mag_ret_l"}, + {849.3, 596.8, 2325.3, -931.6, 0.0, "mag_ret_r"}, + {888.0, 18.4, 2325.3, 893.0, 578.4, "mag_top_left"}, + {888.0, 18.4, 2325.3, -893.0, 578.4, "mag_top_right"}, + {888.0, 18.4, 2325.3, 893.0, -578.4, "mag_bot_left"}, + {888.0, 18.4, 2325.3, -893.0, -578.4, "mag_bot_right"}, }}, // ── Magn4 (GDML z = 2586.02 cm, dz = 85 cm) ───────────────────────── - {"Magn4", + {"magn_4", 9096.9, 1797.0, 1332.0, 850.0, { - {535.6, 1330.2, 850.0, 535.6, 0.0, "MiddleMagL"}, - {535.6, 1330.2, 850.0, -535.6, 0.0, "MiddleMagR"}, - {713.0, 1331.2, 850.0, 1083.0, 0.0, "MagRetL"}, - {713.0, 1331.2, 850.0, -1083.0, 0.0, "MagRetR"}, - {898.0, 385.6, 850.0, 898.0, 945.6, "MagTopLeft"}, - {898.0, 385.6, 850.0, -898.0, 945.6, "MagTopRight"}, - {898.0, 385.6, 850.0, 898.0, -945.6, "MagBotLeft"}, - {898.0, 385.6, 850.0, -898.0, -945.6, "MagBotRight"}, + {535.6, 1330.2, 850.0, 535.6, 0.0, "middle_mag_l"}, + {535.6, 1330.2, 850.0, -535.6, 0.0, "middle_mag_r"}, + {713.0, 1331.2, 850.0, 1083.0, 0.0, "mag_ret_l"}, + {713.0, 1331.2, 850.0, -1083.0, 0.0, "mag_ret_r"}, + {898.0, 385.6, 850.0, 898.0, 945.6, "mag_top_left"}, + {898.0, 385.6, 850.0, -898.0, 945.6, "mag_top_right"}, + {898.0, 385.6, 850.0, 898.0, -945.6, "mag_bot_left"}, + {898.0, 385.6, 850.0, -898.0, -945.6, "mag_bot_right"}, }}, // ── Magn5 (GDML z = 2914.84 cm, dz = 233.82 cm) ───────────────────── - {"Magn5", + {"magn_5", 12385.1, 1808.0, 960.0, 2338.2, { - {200.0, 959.0, 2338.2, 200.0, 0.0, "MiddleMagL"}, - {200.0, 959.0, 2338.2, -200.0, 0.0, "MiddleMagR"}, - {728.9, 960.0, 2338.2, 1079.2, 0.0, "MagRetL"}, - {728.9, 960.0, 2338.2, -1079.2, 0.0, "MagRetR"}, - {904.0, 200.0, 2338.2, 904.0, 760.0, "MagTopLeft"}, - {904.0, 200.0, 2338.2, -904.0, 760.0, "MagTopRight"}, - {904.0, 200.0, 2338.2, 904.0, -760.0, "MagBotLeft"}, - {904.0, 200.0, 2338.2, -904.0, -760.0, "MagBotRight"}, + {200.0, 959.0, 2338.2, 200.0, 0.0, "middle_mag_l"}, + {200.0, 959.0, 2338.2, -200.0, 0.0, "middle_mag_r"}, + {728.9, 960.0, 2338.2, 1079.2, 0.0, "mag_ret_l"}, + {728.9, 960.0, 2338.2, -1079.2, 0.0, "mag_ret_r"}, + {904.0, 200.0, 2338.2, 904.0, 760.0, "mag_top_left"}, + {904.0, 200.0, 2338.2, -904.0, 760.0, "mag_top_right"}, + {904.0, 200.0, 2338.2, 904.0, -760.0, "mag_bot_left"}, + {904.0, 200.0, 2338.2, -904.0, -760.0, "mag_bot_right"}, }}, }; @@ -137,19 +138,20 @@ GeoPhysVol* MuonShieldFactory::buildStation(const StationData& station) { // Air container that spans all 8 pieces of this station auto* stationBox = new GeoBox(station.containerHalfX, station.containerHalfY, station.containerHalfZ); - std::string containerName = std::string(station.name) + "_container"; + std::string containerName = "/SHiP/muon_shield/" + std::string(station.name); auto* stationLog = new GeoLogVol(containerName, stationBox, air); auto* stationPhys = new GeoPhysVol(stationLog); // Place 8 Iron bounding-box approximations for (const PieceData& piece : station.pieces) { auto* pieceBox = new GeoBox(piece.halfX, piece.halfY, piece.halfZ); - std::string pieceName = std::string(station.name) + "_" + piece.name; + std::string pieceName = "/SHiP/muon_shield/" + std::string(station.name) + "/" + piece.name; auto* pieceLog = new GeoLogVol(pieceName, pieceBox, iron); auto* piecePhys = new GeoPhysVol(pieceLog); GeoTrf::Transform3D trf = GeoTrf::Translate3D(piece.centX, piece.centY, 0.0); stationPhys->add(new GeoNameTag(pieceName)); + stationPhys->add(new GeoIdentifierTag(static_cast(&piece - &station.pieces[0]))); stationPhys->add(new GeoTransform(trf)); stationPhys->add(piecePhys); } @@ -162,15 +164,16 @@ GeoPhysVol* MuonShieldFactory::build() { // Overall MuonShieldArea container (Air) auto* areaBox = new GeoBox(s_areaHalfX, s_areaHalfY, s_areaHalfZ); - auto* areaLog = new GeoLogVol("MuonShieldArea", areaBox, air); + auto* areaLog = new GeoLogVol("/SHiP/muon_shield", areaBox, air); auto* areaPhys = new GeoPhysVol(areaLog); // Build and place 6 stations for (const StationData& station : k_stations) { GeoPhysVol* stationPhys = buildStation(station); - std::string containerName = std::string(station.name) + "_container"; + std::string stationName = "/SHiP/muon_shield/" + std::string(station.name); GeoTrf::Transform3D trf = GeoTrf::Translate3D(0.0, 0.0, station.stationZ); - areaPhys->add(new GeoNameTag(containerName)); + areaPhys->add(new GeoNameTag(stationName)); + areaPhys->add(new GeoIdentifierTag(static_cast(&station - &k_stations[0]))); areaPhys->add(new GeoTransform(trf)); areaPhys->add(stationPhys); } diff --git a/subsystems/Target/src/TargetFactory.cpp b/subsystems/Target/src/TargetFactory.cpp index 0d02170..38c515c 100644 --- a/subsystems/Target/src/TargetFactory.cpp +++ b/subsystems/Target/src/TargetFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -27,34 +28,38 @@ GeoPhysVol* TargetFactory::build() { // Create target_vacuum_box (main container) auto* vacuumBox = new GeoBox(s_vacuumBoxHalfX, s_vacuumBoxHalfY, s_vacuumBoxHalfZ); - auto* vacuumBoxLog = new GeoLogVol("target_vacuum_box", vacuumBox, vacuum); + auto* vacuumBoxLog = new GeoLogVol("/SHiP/target", vacuumBox, vacuum); auto* vacuumBoxPhys = new GeoPhysVol(vacuumBoxLog); // Create and place proximity shielding auto* proximityShielding = createProximityShielding(); GeoTrf::Transform3D proxTrf = GeoTrf::Translate3D(0.0, s_proxPosY, 0.0); - vacuumBoxPhys->add(new GeoNameTag("proximity_shielding")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/proximity_shielding")); + vacuumBoxPhys->add(new GeoIdentifierTag(0)); vacuumBoxPhys->add(new GeoTransform(proxTrf)); vacuumBoxPhys->add(proximityShielding); // Create and place top shielding auto* topShielding = createTopShielding(); GeoTrf::Transform3D topTrf = GeoTrf::Translate3D(0.0, s_topShieldPosY, 0.0); - vacuumBoxPhys->add(new GeoNameTag("top_shielding")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/top_shielding")); + vacuumBoxPhys->add(new GeoIdentifierTag(1)); vacuumBoxPhys->add(new GeoTransform(topTrf)); vacuumBoxPhys->add(topShielding); // Create and place bottom shielding auto* bottomShielding = createBottomShielding(); GeoTrf::Transform3D bottomTrf = GeoTrf::Translate3D(0.0, s_bottomShieldPosY, 0.0); - vacuumBoxPhys->add(new GeoNameTag("bottom_shielding")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/bottom_shielding")); + vacuumBoxPhys->add(new GeoIdentifierTag(2)); vacuumBoxPhys->add(new GeoTransform(bottomTrf)); vacuumBoxPhys->add(bottomShielding); // Create and place shielding pedestal auto* shieldingPedestal = createShieldingPedestal(); GeoTrf::Transform3D pedestalTrf = GeoTrf::Translate3D(0.0, s_pedestalPosY, s_pedestalPosZ); - vacuumBoxPhys->add(new GeoNameTag("shielding_pedestal")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/shielding_pedestal")); + vacuumBoxPhys->add(new GeoIdentifierTag(3)); vacuumBoxPhys->add(new GeoTransform(pedestalTrf)); vacuumBoxPhys->add(shieldingPedestal); @@ -67,7 +72,8 @@ GeoPhysVol* TargetFactory::build() { auto* targetVessel = createTargetVessel(); GeoTrf::Transform3D vesselTrf = GeoTrf::Translate3D(0.0, s_targetAreaPosY, s_targetAreaPosZ + s_vesselPosZ); - vacuumBoxPhys->add(new GeoNameTag("TargetVessel")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/vessel")); + vacuumBoxPhys->add(new GeoIdentifierTag(4)); vacuumBoxPhys->add(new GeoTransform(vesselTrf)); vacuumBoxPhys->add(targetVessel); @@ -76,7 +82,8 @@ GeoPhysVol* TargetFactory::build() { auto* targetVesselFront = createTargetVesselFront(); GeoTrf::Transform3D vesselFrontTrf = GeoTrf::Translate3D(0.0, s_targetAreaPosY, s_targetAreaPosZ + s_vesselFrontPosZ); - vacuumBoxPhys->add(new GeoNameTag("TargetVesselFront")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/vessel_front")); + vacuumBoxPhys->add(new GeoIdentifierTag(5)); vacuumBoxPhys->add(new GeoTransform(vesselFrontTrf)); vacuumBoxPhys->add(targetVesselFront); @@ -85,7 +92,8 @@ GeoPhysVol* TargetFactory::build() { auto* targetVesselBack = createTargetVesselBack(); GeoTrf::Transform3D vesselBackTrf = GeoTrf::Translate3D(0.0, s_targetAreaPosY, s_targetAreaPosZ + s_vesselBackPosZ); - vacuumBoxPhys->add(new GeoNameTag("TargetVesselBack")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/vessel_back")); + vacuumBoxPhys->add(new GeoIdentifierTag(6)); vacuumBoxPhys->add(new GeoTransform(vesselBackTrf)); vacuumBoxPhys->add(targetVesselBack); @@ -94,7 +102,8 @@ GeoPhysVol* TargetFactory::build() { auto* heVolume = createHeVolume(); GeoTrf::Transform3D heVolumeTrf = GeoTrf::Translate3D(0.0, s_targetAreaPosY, s_targetAreaPosZ + s_heVolumePosZ); - vacuumBoxPhys->add(new GeoNameTag("HeVolume")); + vacuumBoxPhys->add(new GeoNameTag("/SHiP/target/he_volume")); + vacuumBoxPhys->add(new GeoIdentifierTag(7)); vacuumBoxPhys->add(new GeoTransform(heVolumeTrf)); vacuumBoxPhys->add(heVolume); @@ -119,7 +128,7 @@ GeoPhysVol* TargetFactory::createProximityShielding() { GeoTrf::Transform3D holeTrf = GeoTrf::Translate3D(0.0, s_proxHoleOffsetY, s_proxHoleOffsetZ); const GeoShape* proxShape = &(shape1->subtract((*hole) << holeTrf)); - auto* proxLog = new GeoLogVol("proximity_shielding", proxShape, copper); + auto* proxLog = new GeoLogVol("/SHiP/target/proximity_shielding", proxShape, copper); return new GeoPhysVol(proxLog); } @@ -127,7 +136,7 @@ GeoPhysVol* TargetFactory::createTopShielding() { const GeoMaterial* copper = m_materials.requireMaterial("Copper"); auto* topBox = new GeoBox(s_topShieldHalfX, s_topShieldHalfY, s_topShieldHalfZ); - auto* topLog = new GeoLogVol("top_shielding", topBox, copper); + auto* topLog = new GeoLogVol("/SHiP/target/top_shielding", topBox, copper); return new GeoPhysVol(topLog); } @@ -135,7 +144,7 @@ GeoPhysVol* TargetFactory::createBottomShielding() { const GeoMaterial* copper = m_materials.requireMaterial("Copper"); auto* bottomBox = new GeoBox(s_bottomShieldHalfX, s_bottomShieldHalfY, s_bottomShieldHalfZ); - auto* bottomLog = new GeoLogVol("bottom_shielding", bottomBox, copper); + auto* bottomLog = new GeoLogVol("/SHiP/target/bottom_shielding", bottomBox, copper); return new GeoPhysVol(bottomLog); } @@ -143,7 +152,7 @@ GeoPhysVol* TargetFactory::createShieldingPedestal() { const GeoMaterial* iron = m_materials.requireMaterial("Iron"); auto* pedestalBox = new GeoBox(s_pedestalHalfX, s_pedestalHalfY, s_pedestalHalfZ); - auto* pedestalLog = new GeoLogVol("shielding_pedestal", pedestalBox, iron); + auto* pedestalLog = new GeoLogVol("/SHiP/target/shielding_pedestal", pedestalBox, iron); return new GeoPhysVol(pedestalLog); } @@ -151,7 +160,7 @@ GeoPhysVol* TargetFactory::createTargetVessel() { const GeoMaterial* inconel718 = m_materials.requireMaterial("Inconel718"); auto* vesselTube = new GeoTube(s_vesselRmin, s_vesselRmax, s_vesselHalfZ); - auto* vesselLog = new GeoLogVol("TargetVessel", vesselTube, inconel718); + auto* vesselLog = new GeoLogVol("/SHiP/target/vessel", vesselTube, inconel718); return new GeoPhysVol(vesselLog); } @@ -159,7 +168,7 @@ GeoPhysVol* TargetFactory::createTargetVesselFront() { const GeoMaterial* inconel718 = m_materials.requireMaterial("Inconel718"); auto* frontDisk = new GeoTube(0.0, s_vesselCapRadius, s_vesselCapHalfZ); - auto* frontLog = new GeoLogVol("TargetVesselFront", frontDisk, inconel718); + auto* frontLog = new GeoLogVol("/SHiP/target/vessel_front", frontDisk, inconel718); return new GeoPhysVol(frontLog); } @@ -167,7 +176,7 @@ GeoPhysVol* TargetFactory::createTargetVesselBack() { const GeoMaterial* inconel718 = m_materials.requireMaterial("Inconel718"); auto* backDisk = new GeoTube(0.0, s_vesselCapRadius, s_vesselCapHalfZ); - auto* backLog = new GeoLogVol("TargetVesselBack", backDisk, inconel718); + auto* backLog = new GeoLogVol("/SHiP/target/vessel_back", backDisk, inconel718); return new GeoPhysVol(backLog); } @@ -178,7 +187,7 @@ GeoPhysVol* TargetFactory::createTargetEnclosure() { auto* cutoutBox = new GeoBox(s_enclosureCutoutHalfX, s_enclosureCutoutHalfY, s_enclosureHalfZ); const GeoShape* enclosureShape = &(outerTube->subtract(*cutoutBox)); - auto* enclosureLog = new GeoLogVol("target_enclosure", enclosureShape, steel316L); + auto* enclosureLog = new GeoLogVol("/SHiP/target/enclosure", enclosureShape, steel316L); return new GeoPhysVol(enclosureLog); } @@ -189,12 +198,13 @@ GeoPhysVol* TargetFactory::createHeVolume() { // Create HeVolume container auto* heVolumeTube = new GeoTube(0.0, s_heVolumeRadius, s_heVolumeHalfZ); - auto* heVolumeLog = new GeoLogVol("HeVolume", heVolumeTube, pressurisedHe90); + auto* heVolumeLog = new GeoLogVol("/SHiP/target/he_volume", heVolumeTube, pressurisedHe90); auto* heVolumePhys = new GeoPhysVol(heVolumeLog); // Create and place target enclosure auto* targetEnclosure = createTargetEnclosure(); - heVolumePhys->add(new GeoNameTag("target_enclosure")); + heVolumePhys->add(new GeoNameTag("/SHiP/target/enclosure")); + heVolumePhys->add(new GeoIdentifierTag(0)); heVolumePhys->add(new GeoTransform(GeoTrf::Transform3D::Identity())); heVolumePhys->add(targetEnclosure); @@ -202,24 +212,26 @@ GeoPhysVol* TargetFactory::createHeVolume() { for (int i = 0; i < s_numSlabs; ++i) { // Create cladding tube (Tantalum) auto* claddingTube = new GeoTube(0.0, s_claddingRadius, s_claddingHalfZ[i]); - std::string claddingName = "CladdedTarget_" + std::to_string(i + 1); + std::string claddingName = "/SHiP/target/cladding_" + std::to_string(i + 1); auto* claddingLog = new GeoLogVol(claddingName, claddingTube, tantalum); auto* claddingPhys = new GeoPhysVol(claddingLog); // Create core tube (Tungsten) auto* coreTube = new GeoTube(0.0, s_coreRadius, s_coreHalfZ[i]); - std::string coreName = "TargetCore_" + std::to_string(i + 1); + std::string coreName = "/SHiP/target/core_" + std::to_string(i + 1); auto* coreLog = new GeoLogVol(coreName, coreTube, tungsten); auto* corePhys = new GeoPhysVol(coreLog); // Place core inside cladding (centered) claddingPhys->add(new GeoNameTag(coreName)); + claddingPhys->add(new GeoIdentifierTag(i)); claddingPhys->add(new GeoTransform(GeoTrf::Transform3D::Identity())); claddingPhys->add(corePhys); // Place cladding in HeVolume GeoTrf::Transform3D slabTrf = GeoTrf::Translate3D(0.0, 0.0, s_slabPosZ[i]); heVolumePhys->add(new GeoNameTag(claddingName)); + heVolumePhys->add(new GeoIdentifierTag(i + 1)); heVolumePhys->add(new GeoTransform(slabTrf)); heVolumePhys->add(claddingPhys); } diff --git a/subsystems/TimingDetector/src/TimingDetectorFactory.cpp b/subsystems/TimingDetector/src/TimingDetectorFactory.cpp index 203e2c7..7997ee5 100644 --- a/subsystems/TimingDetector/src/TimingDetectorFactory.cpp +++ b/subsystems/TimingDetector/src/TimingDetectorFactory.cpp @@ -19,7 +19,7 @@ GeoPhysVol* TimingDetectorFactory::build() { auto* air = m_materials.requireMaterial("Air"); auto* containerBox = new GeoBox(s_containerHalfX, s_containerHalfY, s_containerHalfZ); - auto* containerLog = new GeoLogVol("Timing_Detector", containerBox, air); + auto* containerLog = new GeoLogVol("/SHiP/timing_detector", containerBox, air); auto* containerPhys = new GeoPhysVol(containerLog); SHiPTimingDetInterface iface; diff --git a/subsystems/Trackers/src/TrackersFactory.cpp b/subsystems/Trackers/src/TrackersFactory.cpp index f388b28..6171857 100644 --- a/subsystems/Trackers/src/TrackersFactory.cpp +++ b/subsystems/Trackers/src/TrackersFactory.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -23,7 +24,7 @@ GeoPhysVol* TrackersFactory::build() { // Create container volume that spans all 4 stations auto* containerBox = new GeoBox(s_halfX, s_halfY, s_containerHalfZ); - auto* containerLog = new GeoLogVol("TrackersContainer", containerBox, air); + auto* containerLog = new GeoLogVol("/SHiP/trackers", containerBox, air); auto* containerPhys = new GeoPhysVol(containerLog); // Create and place individual stations @@ -31,7 +32,7 @@ GeoPhysVol* TrackersFactory::build() { for (int i = 0; i < 4; ++i) { auto* stationBox = new GeoBox(s_halfX, s_halfY, s_halfZ); - std::string stationName = "TrackerStation_" + std::to_string(i + 1); + std::string stationName = "/SHiP/trackers/station_" + std::to_string(i + 1); auto* stationLog = new GeoLogVol(stationName, stationBox, air); auto* stationPhys = new GeoPhysVol(stationLog); @@ -40,6 +41,7 @@ GeoPhysVol* TrackersFactory::build() { GeoTrf::Transform3D stationTrf = GeoTrf::Translate3D(0.0, 0.0, relativeZ); containerPhys->add(new GeoNameTag(stationName)); + containerPhys->add(new GeoIdentifierTag(i)); containerPhys->add(new GeoTransform(stationTrf)); containerPhys->add(stationPhys); } diff --git a/subsystems/Trackers/test_trackers.cpp b/subsystems/Trackers/test_trackers.cpp index 7646b19..f0d1c11 100644 --- a/subsystems/Trackers/test_trackers.cpp +++ b/subsystems/Trackers/test_trackers.cpp @@ -30,7 +30,7 @@ TEST_CASE("TrackersWithinEnvelope", "[trackers]") { SHiPGeometry::TrackersFactory factory(materials); GeoPhysVol* tc = factory.build(); REQUIRE(tc != nullptr); - const GeoVPhysVol* st1 = findChild(tc, "TrackerStation_1"); + const GeoVPhysVol* st1 = findChild(tc, "/SHiP/trackers/station_1"); INFO("TrackerStation_1 not found"); REQUIRE(st1 != nullptr); auto* box = dynamic_cast(st1->getLogVol()->getShape()); diff --git a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp index 5390036..11d04a8 100644 --- a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp +++ b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp @@ -18,7 +18,7 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) { const GeoMaterial* scint = m_materials.requireMaterial("Scintillator"); auto* box = new GeoBox(s_halfX, s_halfY, s_halfZ); - auto* log = new GeoLogVol("Upstream_Tagger", box, scint); + auto* log = new GeoLogVol("/SHiP/upstream_tagger", box, scint); auto* fpv = new GeoFullPhysVol(log); if (manager) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d4038d3..0140bf6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,13 @@ target_link_libraries( ) catch_discover_tests(test_materials) +add_executable(test_consistency test_consistency.cpp) +target_link_libraries( + test_consistency + PRIVATE SHiPGeometry Catch2::Catch2WithMain +) +catch_discover_tests(test_consistency) + # Integration test: build geometry into SQLite then run overlap check add_test( NAME OverlapCheck diff --git a/tests/test_consistency.cpp b/tests/test_consistency.cpp new file mode 100644 index 0000000..ff1d270 --- /dev/null +++ b/tests/test_consistency.cpp @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) CERN for the benefit of the SHiP Collaboration + +#include "SHiPGeometry/SHiPGeometry.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using SHiPGeometry::SHiPGeometryBuilder; + +namespace { + +struct SubsystemInfo { + std::string name; + double centreZ; // mm, from world origin + double halfZ; // mm +}; + +// Extract Z translation from a Transform3D +double extractZ(const GeoTrf::Transform3D& trf) { + return trf.translation().z(); +} + +// Get the Z half-length of a volume, assuming GeoBox shape +double getHalfZ(const GeoVPhysVol* vol) { + const auto* box = dynamic_cast(vol->getLogVol()->getShape()); + if (box) { + return box->getZHalfLength(); + } + // For non-box shapes (e.g. GeoFullPhysVol with GeoBox), try the tube + const auto* tube = dynamic_cast(vol->getLogVol()->getShape()); + if (tube) { + return tube->getZHalfLength(); + } + return 0.0; +} + +// Collect info about all children of the cavern volume (skipping cavern itself) +std::vector collectSubsystems(GeoPhysVol* world) { + // The world's first child is the Cavern rock; the subsystems are children + // of the world placed after the Cavern. Find the cavern first. + std::vector subsystems; + + for (unsigned int i = 0; i < world->getNChildVols(); ++i) { + PVConstLink child = world->getChildVol(i); + std::string name = child->getLogVol()->getName(); + GeoTrf::Transform3D trf = world->getXToChildVol(i); + double zCentre = extractZ(trf); + double halfZ = getHalfZ(&*child); + + // Skip the cavern rock — it's infrastructure, not a subsystem + if (name == "/SHiP/cavern") { + continue; + } + + subsystems.push_back({name, zCentre, halfZ}); + } + + return subsystems; +} + +} // namespace + +TEST_CASE("ConsistencyTest.ExpectedSubsystemCount", "[consistency]") { + SHiPGeometryBuilder builder; + GeoPhysVol* world = builder.build(); + REQUIRE(world != nullptr); + + auto subsystems = collectSubsystems(world); + // 8 subsystems: target, muon_shield, upstream_tagger, decay_volume, + // trackers, magnet, timing_detector, calorimeter + CHECK(subsystems.size() == 8u); // NOLINT(readability/check) +} + +TEST_CASE("ConsistencyTest.SubsystemsGenerallyInZOrder", "[consistency]") { + SHiPGeometryBuilder builder; + GeoPhysVol* world = builder.build(); + REQUIRE(world != nullptr); + + auto subsystems = collectSubsystems(world); + REQUIRE(subsystems.size() >= 2); + + // Verify monotonically increasing Z for placement order, allowing + // co-located subsystems (trackers/magnet overlap is intentional). + // Use a weaker check: each subsystem's centre should be >= previous - tolerance + for (size_t i = 1; i < subsystems.size(); ++i) { + INFO("Subsystem " << subsystems[i].name << " at Z=" << subsystems[i].centreZ + << " should not be before " << subsystems[i - 1].name + << " at Z=" << subsystems[i - 1].centreZ); + CHECK(subsystems[i].centreZ >= subsystems[i - 1].centreZ - 1.0); + } +} + +TEST_CASE("ConsistencyTest.NoUnexpectedZOverlaps", "[consistency]") { + SHiPGeometryBuilder builder; + GeoPhysVol* world = builder.build(); + REQUIRE(world != nullptr); + + auto subsystems = collectSubsystems(world); + REQUIRE(subsystems.size() >= 2); + + // The trackers container intentionally spans across the magnet + // (stations 1-2 before, stations 3-4 after), so that pair is allowed to overlap. + auto isAllowedOverlap = [](const std::string& a, const std::string& b) { + return (a == "/SHiP/trackers" && b == "/SHiP/magnet") || + (a == "/SHiP/magnet" && b == "/SHiP/trackers"); + }; + + // Sort by Z centre + std::sort(subsystems.begin(), subsystems.end(), + [](const SubsystemInfo& a, const SubsystemInfo& b) { return a.centreZ < b.centreZ; }); + + for (size_t i = 1; i < subsystems.size(); ++i) { + if (isAllowedOverlap(subsystems[i - 1].name, subsystems[i].name)) { + continue; + } + + double previousEnd = subsystems[i - 1].centreZ + subsystems[i - 1].halfZ; + double currStart = subsystems[i].centreZ - subsystems[i].halfZ; + + INFO("Checking " << subsystems[i - 1].name << " (ends at Z=" << previousEnd << ") vs " + << subsystems[i].name << " (starts at Z=" << currStart << ")"); + CHECK(previousEnd <= currStart + 1.0); // 1 mm tolerance for numerical precision + } +} + +TEST_CASE("ConsistencyTest.PositionsSanity", "[consistency]") { + SHiPGeometryBuilder builder; + GeoPhysVol* world = builder.build(); + REQUIRE(world != nullptr); + + auto subsystems = collectSubsystems(world); + + // Expected centres derived from subsystem_envelopes.csv (z_start + z_end) / 2, in mm + // These are approximate — the actual placements may differ slightly from the + // CSV midpoints due to coordinate system offsets. + struct Expected { + std::string name; + double approxZ; // mm + double tolerance; // mm + }; + + // Only check subsystems whose positions are straightforward to derive + std::vector expected = { + {"/SHiP/upstream_tagger", 32720.0, 500.0}, + {"/SHiP/decay_volume", 58120.0, 500.0}, + {"/SHiP/magnet", 89570.0, 500.0}, + {"/SHiP/calorimeter", 98320.0, 500.0}, + }; + + for (const auto& exp : expected) { + auto it = std::find_if(subsystems.begin(), subsystems.end(), + [&](const SubsystemInfo& s) { return s.name == exp.name; }); + + INFO("Looking for subsystem " << exp.name); + REQUIRE(it != subsystems.end()); + INFO(exp.name << " at Z=" << it->centreZ << ", expected ~" << exp.approxZ); + CHECK(std::abs(it->centreZ - exp.approxZ) < exp.tolerance); + } +}