From 6b25f40dc8448b98e35fe5f064a48bb0393ebecd Mon Sep 17 00:00:00 2001 From: Matei Climescu Date: Wed, 11 Mar 2026 18:40:04 +0100 Subject: [PATCH 1/3] Implemented mixed tube tile geometry --- src/SHiPMaterials.cpp | 27 ++ subsystems/UpstreamTagger/README.md | 120 ++++++-- .../include/UpstreamTagger/SHiPUBTManager.h | 44 ++- .../UpstreamTagger/UpstreamTaggerFactory.h | 52 +++- .../src/UpstreamTaggerFactory.cpp | 262 +++++++++++++++++- .../UpstreamTagger/test_upstreamtagger.cpp | 48 +++- 6 files changed, 496 insertions(+), 57 deletions(-) diff --git a/src/SHiPMaterials.cpp b/src/SHiPMaterials.cpp index 6ddc1e6..5356082 100644 --- a/src/SHiPMaterials.cpp +++ b/src/SHiPMaterials.cpp @@ -179,6 +179,33 @@ void SHiPMaterials::createMaterials() { scintillator->add(m_elements["Hydrogen"], 0.085); scintillator->lock(); m_materials["Scintillator"] = scintillator; + + // Mylar / PET (density 1.39 g/cm³): C10H8O4 → C 62.50%, H 4.20%, O 33.30% + GeoMaterial* mylar = + new GeoMaterial("Mylar", 1.39 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); + mylar->add(m_elements["Carbon"], 0.6250); + mylar->add(m_elements["Hydrogen"], 0.0420); + mylar->add(m_elements["Oxygen"], 0.3330); + mylar->lock(); + m_materials["Mylar"] = mylar; + + // ArCO2 70/30 drift gas (density 1.842e-3 g/cm³): Ar 67.93%, C 8.75%, O 23.32% + GeoMaterial* arco2 = + new GeoMaterial("ArCO2", 1.842e-3 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); + arco2->add(m_elements["Argon"], 0.6793); + arco2->add(m_elements["Carbon"], 0.0875); + arco2->add(m_elements["Oxygen"], 0.2332); + arco2->lock(); + m_materials["ArCO2"] = arco2; + + // Polystyrene (density 1.06 g/cm³): C8H8 → C 92.26%, H 7.74% + GeoMaterial* polystyrene = + new GeoMaterial("Polystyrene", 1.06 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); + polystyrene->add(m_elements["Carbon"], 0.9226); + polystyrene->add(m_elements["Hydrogen"], 0.0774); + polystyrene->lock(); + m_materials["Polystyrene"] = polystyrene; + } } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/README.md b/subsystems/UpstreamTagger/README.md index 185aec3..df4ed47 100644 --- a/subsystems/UpstreamTagger/README.md +++ b/subsystems/UpstreamTagger/README.md @@ -1,36 +1,122 @@ # UpstreamTagger -Upstream veto tagger. +Upstream Background Tagger (UBT) — segmented drift tube and scintillator tile detector. ## Description -The UpstreamTagger subsystem implements a scintillator slab upstream of the decay vessel, used to veto charged particles entering from the target region. Currently modelled as a single monolithic scintillator box. The full implementation requires bar segmentation with SiPM readout. +The UpstreamTagger implements the UBT as a segmented 2×3 m² tracking plane placed at +z = 32720 mm, upstream of the decay vessel. It consists of: -The volume is created as a `GeoFullPhysVol` (rather than `GeoPhysVol`) to allow sensitive detector registration via `SHiPUBTManager`. +- **Drift tubes** — mylar straw tubes (5 mm diameter, 15 µm wall, ArCO2 70/30 fill, 1.2 m long) + arranged in double-staggered layers +- **Scintillator tiles** — 1×1×1 cm³ polystyrene tiles in two 40×40 blocks ## Geometry Tree ``` -Upstream_Tagger (Scintillator, 4400×6400×160 mm) +UBT_Envelope_LV (Air, 2000×3000×16 mm) +├── UBT_Top_Left_LOG (Air, outer top band, left half-plane) +│ ├── UBT_Top_Left_S0_T*_Wall (Mylar, hollow tube annulus) +│ └── UBT_Top_Left_S0_T*_Gas (ArCO2, solid cylinder) [sensitive] +│ ├── UBT_Top_Left_S1_T*_Wall +│ └── UBT_Top_Left_S1_T*_Gas [sensitive] +├── UBT_Top_Right_LOG (Air, outer top band, right half-plane) +│ └── ... +├── UBT_Bottom_Left_LOG (Air, outer bottom band, left half-plane) +│ └── ... +├── UBT_Bottom_Right_LOG (Air, outer bottom band, right half-plane) +│ └── ... +├── UBT_Central_LOG (Air, central tube strip x=[-600,+600]) +│ ├── UBT_Central_S*_T*_Wall +│ └── UBT_Central_S*_T*_Gas [sensitive] +├── UBT_TileLeft_LOG (Air, left tile block x=[-1000,-600]) +│ └── UBT_TileLeft_T*_* [sensitive] +└── UBT_TileRight_LOG (Air, right tile block x=[+600,+1000]) + └── UBT_TileRight_T*_* [sensitive] ``` -Position in world: z = 32720 mm. +## Detector Layout + +``` + Y + +1500 ┌──────────────────────────────────────────┐ + │ Top_Left tubes + Top_Right tubes │ y = [+200, +1500] mm + +200 ├───────────┬──────────────────┬────────────┤ + │ PS tiles │ Central tubes │ PS tiles │ y = [-200, +200] mm + │ (left) │ x=[-600,+600] │ (right) │ + -200 ├───────────┴──────────────────┴────────────┤ + │ Bottom_Left tubes + Bottom_Right tubes │ y = [-1500, -200] mm + -1500 └──────────────────────────────────────────┘ + -1000 -600 +600 +1000 → X (mm) +``` + +### Drift tubes + +| Region | X extent (mm) | Y extent (mm) | Z centre (mm) | +|--------|--------------|---------------|---------------| +| Top_Left | [-1000, +200] | [+200, +1500] | -5 | +| Top_Right | [-200, +1000] | [+200, +1500] | +5 | +| Bottom_Left | [-1000, +200] | [-1500, -200] | -5 | +| Bottom_Right | [-200, +1000] | [-1500, -200] | +5 | +| Central | [-600, +600] | [-200, +200] | 0 | + +Each region uses a double-staggered layer: two sub-layers separated by one tube +radius in Z and half a pitch in Y, giving full azimuthal coverage. + +Tube gas and wall volumes are placed as **siblings** in the envelope (not nested) +to avoid Geo2G4 copy-number corruption with shared logical volumes. + +### Scintillator tiles + +Two 40×40 blocks of 1×1×1 cm³ polystyrene tiles, flush with the tube layer in Z. + +## Sensitive Volumes + +All sensitive volumes are `GeoFullPhysVol` and are registered with `SHiPUBTManager`: + +| Collection | LV name pattern | Count | +|---|---|---| +| Tube gas | `UBT_*_TubeGas_LV` | ~1000+ | +| Tiles | `UBT_Tile_LV` | 3200 | ## Materials -| Material | Density | Usage | -|--------------|------------|-----------------| -| Scintillator | 1.032 g/cm³ | Detector slab | +| Material | Density | Usage | +|---|---|---| +| Mylar | 1.39 g/cm³ | Tube wall (15 µm) | +| ArCO2 | 1.842×10⁻³ g/cm³ | Drift gas (70% Ar, 30% CO2) | +| Polystyrene | 1.06 g/cm³ | Scintillator tiles | +| Air | 1.29×10⁻³ g/cm³ | Envelopes | -## Status +## Position in World + +z = 32720 mm (centre of 32520–32920 mm range, from subsystem_envelopes.csv). + +## Geant4 Integration -- [x] C++ implementation (monolithic slab) -- [ ] Implement bar segmentation with SiPM readout -- [ ] Verification against GDML +To register sensitive detectors in a Geant4 application: -## TODO +```cpp +SHiPUBTManager ubtManager; +UpstreamTaggerFactory factory(materials); +factory.build(&ubtManager); + +// After Geo2G4 conversion, iterate the G4LogicalVolumeStore and register +// your SD on volumes whose name contains "TubeGas_LV" or "Tile_LV": +auto* sd = new MyUBTSD("UBTSD"); +for (auto* lv : *G4LogicalVolumeStore::GetInstance()) { + const auto& name = lv->GetName(); + if (name.find("TubeGas_LV") != std::string::npos || + name.find("Tile_LV") != std::string::npos) + lv->SetSensitiveDetector(sd); +} +``` + +## Status -- Implement individual scintillator bar segmentation -- Add SiPM readout geometry -- Register individual bars as sensitive volumes (currently the whole slab is one volume) -- Verify slab dimensions against GDML reference +- [x] Full segmented geometry (drift tubes + tiles) +- [x] Double-staggered tube layers per region +- [x] GeoFullPhysVol sensitive volume registration via SHiPUBTManager +- [x] New materials (Mylar, ArCO2, Polystyrene) added to SHiPMaterials +- [ ] Verification of tube count against GDML reference +- [ ] SiPM readout geometry diff --git a/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h b/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h index 42f7796..62f49ca 100644 --- a/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h +++ b/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h @@ -6,29 +6,63 @@ #include #include #include +#include namespace SHiPGeometry { /** * @brief Detector manager for the Upstream Background Tagger (UBT). * - * Stores the single GeoFullPhysVol sensitive slab and satisfies the - * GeoVDetectorManager interface for downstream Geant4 integration. + * Stores the sensitive volumes of the UBT: drift tube gas volumes and + * scintillator tile volumes, all registered as GeoFullPhysVol. + * + * Two sets of sensitive volumes: + * - Drift tube gas cylinders (ArCO2 70/30) + * - Polystyrene scintillator tiles (1×1×1 cm³) + * + * The legacy single-slab interface (setSlabVolume / getFullPV) is kept + * for backwards compatibility with the monolithic placeholder. */ class SHiPUBTManager : public GeoVDetectorManager { public: SHiPUBTManager() = default; ~SHiPUBTManager() override = default; + // ---- Legacy slab interface (monolithic placeholder) -------------------- void setSlabVolume(GeoFullPhysVol* fpv) { m_slab = fpv; } GeoFullPhysVol* getFullPV() const { return m_slab; } - unsigned int getNumTreeTops() const override { return m_slab ? 1u : 0u; } + // ---- Segmented detector interface -------------------------------------- + void addTubeGasVolume(GeoFullPhysVol* fpv) { m_tubeGasVolumes.push_back(fpv); } + void addTileVolume(GeoFullPhysVol* fpv) { m_tileVolumes.push_back(fpv); } + + const std::vector& getTubeGasVolumes() const { return m_tubeGasVolumes; } + const std::vector& getTileVolumes() const { return m_tileVolumes; } + + std::size_t numTubeGasVolumes() const { return m_tubeGasVolumes.size(); } + std::size_t numTileVolumes() const { return m_tileVolumes.size(); } + + // ---- GeoVDetectorManager interface ------------------------------------ + unsigned int getNumTreeTops() const override { + // If segmented volumes exist use them, otherwise fall back to slab + const auto n = m_tubeGasVolumes.size() + m_tileVolumes.size(); + return n > 0 ? static_cast(n) : (m_slab ? 1u : 0u); + } - PVConstLink getTreeTop(unsigned int /*i*/) const override { return PVConstLink(m_slab); } + PVConstLink getTreeTop(unsigned int i) const override { + const auto nTubes = m_tubeGasVolumes.size(); + const auto nTiles = m_tileVolumes.size(); + if (nTubes + nTiles > 0) { + if (i < nTubes) return PVConstLink(m_tubeGasVolumes[i]); + if (i < nTubes + nTiles) return PVConstLink(m_tileVolumes[i - nTubes]); + } + return PVConstLink(m_slab); + } private: - GeoFullPhysVol* m_slab{nullptr}; + GeoFullPhysVol* m_slab{nullptr}; + std::vector m_tubeGasVolumes; + std::vector m_tileVolumes; }; } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h index ccd82dd..9a896d2 100644 --- a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h +++ b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h @@ -11,12 +11,35 @@ class SHiPMaterials; class SHiPUBTManager; /** - * @brief Factory for the UpstreamTagger (upstream veto tagger) geometry + * @brief Factory for the Upstream Background Tagger (UBT) geometry. * - * Creates a scintillator slab as a GeoFullPhysVol (sensitive volume). - * Based on GDML reference: Box 440×640×16 cm (full dimensions). - * Z: 32.52 to 32.92 m → centre: 32.72 m, half-length: 0.20 m - * Half-width: 2.20 m, half-height: 3.20 m + * Builds a segmented 2×3 m² tracker plane of drift tubes and scintillator tiles. + * + * ## Detector layout + * + * ``` + * Y + * +1500 ┌──────────────────────────────────────────┐ + * │ Top_Left tubes + Top_Right tubes │ y = [+200, +1500] + * +200 ├───────────┬──────────────────┬────────────┤ + * │ PS tiles │ Central tubes │ PS tiles │ y = [-200, +200] + * │ (left) │ x=[-600,+600] │ (right) │ + * -200 ├───────────┴──────────────────┴────────────┤ + * │ Bottom_Left tubes + Bottom_Right tubes │ y = [-1500, -200] + * -1500 └──────────────────────────────────────────┘ + * -1000 -600 +600 +1000 → X (mm) + * ``` + * + * ## Sensitive volumes + * All tube gas volumes (GeoFullPhysVol, LV name contains "TubeGas_LV") and + * tile volumes (GeoFullPhysVol, LV name "UBT_Tile_LV") are registered with + * the supplied SHiPUBTManager for downstream Geant4 SD registration. + * + * ## Envelope dimensions (half-extents, mm) + * halfX = 1000, halfY = 1500, halfZ = 8 + * + * ## Position in world + * z = 32720 mm (centre of the 32520–32920 mm z range) */ class UpstreamTaggerFactory { public: @@ -24,19 +47,24 @@ class UpstreamTaggerFactory { ~UpstreamTaggerFactory() = default; /** - * @brief Build the UpstreamTagger geometry. - * @param manager Optional manager to register the sensitive slab; may be null. - * @return Pointer to the GeoFullPhysVol scintillator slab. + * @brief Build the full segmented UBT plane. + * @param manager Optional manager to register sensitive volumes; may be null. + * @return Pointer to the envelope GeoPhysVol. */ GeoVPhysVol* build(SHiPUBTManager* manager = nullptr); + // Envelope half-dimensions (mm) — used by the test to check against CSV limits + static constexpr double s_halfX = 1000.0; + static constexpr double s_halfY = 1500.0; + static constexpr double s_halfZ = 8.0; + private: SHiPMaterials& m_materials; - // Dimensions from GDML: Box 440×640×16 cm → half: 220×320×8 cm (mm) - static constexpr double s_halfX = 2200.0; - static constexpr double s_halfY = 3200.0; - static constexpr double s_halfZ = 80.0; + static constexpr double s_tubeROuter_mm = 2.5; + static constexpr double s_tubeWall_mm = 0.015; + static constexpr double s_tubeHalfLen_mm = 600.0; + static constexpr double s_tileSide_mm = 10.0; }; } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp index 5390036..840d264 100644 --- a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp +++ b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp @@ -2,30 +2,268 @@ // Copyright (C) CERN for the benefit of the SHiP Collaboration #include "UpstreamTagger/UpstreamTaggerFactory.h" - -#include "SHiPGeometry/SHiPMaterials.h" #include "UpstreamTagger/SHiPUBTManager.h" +#include "SHiPGeometry/SHiPMaterials.h" #include -#include +#include #include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace GeoModelKernelUnits; namespace SHiPGeometry { -UpstreamTaggerFactory::UpstreamTaggerFactory(SHiPMaterials& materials) : m_materials(materials) {} +namespace { + +// ============================================================================ +// placeTubeSubLayer (file-scope helper) +// +// Wall and gas are placed as SIBLINGS in the envelope (not nested) to avoid +// Geo2G4 copy-number corruption that occurs when a GeoPhysVol is a daughter +// of another GeoPhysVol sharing the same GeoLogVol across many instances. +// ============================================================================ +void placeTubeSubLayer(GeoPhysVol* envelope, + GeoLogVol* wallLog, + GeoLogVol* gasLog, + double halfExtentY, + double zLocal, + double yShift, + double rOuter_mm, + const std::string& tag, + int subIdx, + SHiPUBTManager* manager) +{ + const double rOuter = rOuter_mm * mm; + const double pitch = 2.0 * rOuter; + + const double usable = 2.0 * halfExtentY - std::abs(yShift); + const int nTubes = std::max(1, static_cast(std::floor(usable / pitch)) + 1); + const double yFirst = -0.5 * (nTubes - 1) * pitch + yShift; + + const GeoTrf::Transform3D rotToX = GeoTrf::RotateY3D(-90.0 * deg); -GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) { - const GeoMaterial* scint = m_materials.requireMaterial("Scintillator"); + for (int i = 0; i < nTubes; ++i) { + const double yPos = yFirst + i * pitch; + if (std::abs(yPos) > halfExtentY + 1e-6) continue; - auto* box = new GeoBox(s_halfX, s_halfY, s_halfZ); - auto* log = new GeoLogVol("Upstream_Tagger", box, scint); - auto* fpv = new GeoFullPhysVol(log); + const std::string name = tag + "_S" + std::to_string(subIdx) + + "_T" + std::to_string(i); + const GeoTrf::Transform3D trf = GeoTrf::Translate3D(0.0, yPos, zLocal) * rotToX; - if (manager) { - manager->setSlabVolume(fpv); + // Wall (not sensitive) — placed first + auto* wallPV = new GeoPhysVol(wallLog); + envelope->add(new GeoNameTag((name + "_Wall").c_str())); + envelope->add(new GeoTransform(trf)); + envelope->add(wallPV); + + // Gas (sensitive) — sibling of wall, same transform + auto* gasFPV = new GeoFullPhysVol(gasLog); + envelope->add(new GeoNameTag((name + "_Gas").c_str())); + envelope->add(new GeoTransform(trf)); + envelope->add(gasFPV); + + if (manager) manager->addTubeGasVolume(gasFPV); } +} + +// ============================================================================ +// placeDoubleStaggeredLayer (file-scope helper) +// ============================================================================ +void placeDoubleStaggeredLayer(GeoPhysVol* envelope, + GeoLogVol* wallLog, + GeoLogVol* gasLog, + double halfExtentY, + double zCenter, + double rOuter_mm, + const std::string& tag, + SHiPUBTManager* manager) +{ + const double rOuter = rOuter_mm * mm; + placeTubeSubLayer(envelope, wallLog, gasLog, + halfExtentY, zCenter - rOuter, 0.0, + rOuter_mm, tag, 0, manager); + placeTubeSubLayer(envelope, wallLog, gasLog, + halfExtentY, zCenter + rOuter, rOuter, + rOuter_mm, tag, 1, manager); +} + +// ============================================================================ +// makeEnvelope (file-scope helper) +// ============================================================================ +GeoPhysVol* makeEnvelope(GeoPhysVol* mother, + const GeoMaterial* mat, + const std::string& tag, + double hx, double hy, double hz, + double cx, double cy, double cz) +{ + auto* log = new GeoLogVol((tag + "_LOG").c_str(), + new GeoBox(hx, hy, hz), + const_cast(mat)); + auto* pv = new GeoPhysVol(log); + mother->add(new GeoNameTag(tag.c_str())); + mother->add(new GeoTransform(GeoTrf::Translate3D(cx, cy, cz))); + mother->add(pv); + return pv; +} + +} // anonymous namespace + +// ============================================================================ +// UpstreamTaggerFactory +// ============================================================================ +UpstreamTaggerFactory::UpstreamTaggerFactory(SHiPMaterials& materials) + : m_materials(materials) +{} + +GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) +{ + // ---- Materials ---------------------------------------------------------- + const GeoMaterial* air = m_materials.requireMaterial("Air"); + const GeoMaterial* mylar = m_materials.requireMaterial("Mylar"); + const GeoMaterial* arco2 = m_materials.requireMaterial("ArCO2"); + const GeoMaterial* poly = m_materials.requireMaterial("Polystyrene"); + + // ---- Tube cross-section dimensions -------------------------------------- + const double rOuter = s_tubeROuter_mm * mm; + const double rInner = (s_tubeROuter_mm - s_tubeWall_mm) * mm; + const double rGas = rInner - 0.001 * mm; // 1 µm clearance + const double halfLen = s_tubeHalfLen_mm * mm; + + if (rInner <= 0.0 || rInner >= rOuter) + throw std::runtime_error("UBT: invalid tube wall thickness"); + + // ---- Tile dimensions ---------------------------------------------------- + const double tileHalf = 0.5 * s_tileSide_mm * mm; + + // ---- Shared logical volumes --------------------------------------------- + // One wall LV and one gas LV per region (5 regions = 5 pairs). + // They are named by region so the SD can identify them by LV name. + + auto makeTubeLVs = [&](const std::string& region) + -> std::pair + { + auto* wLV = new GeoLogVol( + ("UBT_" + region + "_TubeWall_LV").c_str(), + new GeoTube(rInner, rOuter, halfLen), + const_cast(mylar)); + auto* gLV = new GeoLogVol( + ("UBT_" + region + "_TubeGas_LV").c_str(), + new GeoTube(0.0, rGas, halfLen), + const_cast(arco2)); + return {wLV, gLV}; + }; + + auto* tileLV = new GeoLogVol("UBT_Tile_LV", + new GeoBox(tileHalf, tileHalf, tileHalf), + const_cast(poly)); + + // ---- Layout constants (all in mm) --------------------------------------- + constexpr double outerHalfY_mm = 650.0; + constexpr double outerCtrY_mm = 850.0; + constexpr double leftHalfX_mm = 600.0; + constexpr double leftCtrX_mm = -400.0; + constexpr double rightHalfX_mm = 600.0; + constexpr double rightCtrX_mm = +400.0; + constexpr double zLeft_mm = -2.0 * s_tubeROuter_mm; + constexpr double zRight_mm = +2.0 * s_tubeROuter_mm; + constexpr double outerEnvHalfZ_mm = 3.0 * s_tubeROuter_mm + 0.5; + constexpr double ctrHalfX_mm = 600.0; + constexpr double ctrHalfY_mm = 200.0; + constexpr double ctrEnvHalfZ_mm = s_tubeROuter_mm + 0.5; + constexpr double tileBlkHalfX_mm = 200.0; + constexpr double tileBlkHalfY_mm = 200.0; + constexpr double tileBlkCtrX_mm = 800.0; + + // ---- Top-level envelope ------------------------------------------------- + auto* envPV = new GeoPhysVol( + new GeoLogVol("UBT_Envelope_LV", + new GeoBox(s_halfX * mm, s_halfY * mm, s_halfZ * mm), + const_cast(air))); + + // ---- Outer bands (top + bottom) ----------------------------------------- + for (int sign : {+1, -1}) { + const std::string band = (sign > 0) ? "Top" : "Bottom"; + const double yCtr = sign * outerCtrY_mm * mm; + + // Left half-plane + { + auto [wLV, gLV] = makeTubeLVs(band + "_Left"); + auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Left", + leftHalfX_mm * mm, outerHalfY_mm * mm, + outerEnvHalfZ_mm * mm, + leftCtrX_mm * mm, yCtr, zLeft_mm * mm); + placeDoubleStaggeredLayer(env, wLV, gLV, + outerHalfY_mm * mm, 0.0, + s_tubeROuter_mm, "UBT_" + band + "_Left", manager); + } + // Right half-plane + { + auto [wLV, gLV] = makeTubeLVs(band + "_Right"); + auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Right", + rightHalfX_mm * mm, outerHalfY_mm * mm, + outerEnvHalfZ_mm * mm, + rightCtrX_mm * mm, yCtr, zRight_mm * mm); + placeDoubleStaggeredLayer(env, wLV, gLV, + outerHalfY_mm * mm, 0.0, + s_tubeROuter_mm, "UBT_" + band + "_Right", manager); + } + } + + // ---- Central tube strip ------------------------------------------------- + { + auto [wLV, gLV] = makeTubeLVs("Central"); + auto* env = makeEnvelope(envPV, air, "UBT_Central", + ctrHalfX_mm * mm, ctrHalfY_mm * mm, + ctrEnvHalfZ_mm * mm, + 0.0, 0.0, 0.0); + placeDoubleStaggeredLayer(env, wLV, gLV, + ctrHalfY_mm * mm, 0.0, + s_tubeROuter_mm, "UBT_Central", manager); + } + + // ---- Tile blocks -------------------------------------------------------- + auto placeTileBlock = [&](const std::string& blkTag, double ctrX) + { + auto* blkEnv = makeEnvelope(envPV, air, blkTag, + tileBlkHalfX_mm * mm, tileBlkHalfY_mm * mm, + tileHalf, + ctrX, 0.0, 0.0); + + const int nX = static_cast(std::round(2.0 * tileBlkHalfX_mm / s_tileSide_mm)); + const int nY = static_cast(std::round(2.0 * tileBlkHalfY_mm / s_tileSide_mm)); + const double xFirst = -(nX - 1) * 0.5 * s_tileSide_mm * mm; + const double yFirst = -(nY - 1) * 0.5 * s_tileSide_mm * mm; + + for (int ix = 0; ix < nX; ++ix) { + for (int iy = 0; iy < nY; ++iy) { + const std::string tname = blkTag + "_T" + std::to_string(ix) + + "_" + std::to_string(iy); + auto* tileFPV = new GeoFullPhysVol(tileLV); + blkEnv->add(new GeoNameTag(tname.c_str())); + blkEnv->add(new GeoTransform( + GeoTrf::Translate3D(xFirst + ix * s_tileSide_mm * mm, + yFirst + iy * s_tileSide_mm * mm, + 0.0))); + blkEnv->add(tileFPV); + if (manager) manager->addTileVolume(tileFPV); + } + } + }; + + placeTileBlock("UBT_TileLeft", -tileBlkCtrX_mm * mm); + placeTileBlock("UBT_TileRight", +tileBlkCtrX_mm * mm); - return fpv; + return envPV; } } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/test_upstreamtagger.cpp b/subsystems/UpstreamTagger/test_upstreamtagger.cpp index 5996857..f1f2f91 100644 --- a/subsystems/UpstreamTagger/test_upstreamtagger.cpp +++ b/subsystems/UpstreamTagger/test_upstreamtagger.cpp @@ -2,6 +2,7 @@ // Copyright (C) CERN for the benefit of the SHiP Collaboration #include "SHiPGeometry/SHiPMaterials.h" +#include "UpstreamTagger/SHiPUBTManager.h" #include "UpstreamTagger/UpstreamTaggerFactory.h" #include @@ -12,25 +13,50 @@ #include using SHiPGeometry::SHiPMaterials; +using SHiPGeometry::SHiPUBTManager; +using SHiPGeometry::UpstreamTaggerFactory; -// CSV limits: UpstreamTagger halfX ≤ 2200, halfY ≤ 3200, halfZ ≤ 200 -TEST_CASE("UpstreamTaggerWithinEnvelope", "[upstreamtagger]") { +// CSV envelope limits for UpstreamTagger: +// half_width = 0.75 m = 750 mm, half_height = 1.60 m = 1600 mm, halfZ = 200 mm +TEST_CASE("UBTEnvelopeWithinCSVLimits", "[upstreamtagger]") { SHiPMaterials materials; - SHiPGeometry::UpstreamTaggerFactory factory(materials); + UpstreamTaggerFactory factory(materials); GeoVPhysVol* ubt = factory.build(); REQUIRE(ubt != nullptr); + auto* box = dynamic_cast(ubt->getLogVol()->getShape()); REQUIRE(box != nullptr); - CHECK(box->getXHalfLength() <= 2200.0); - CHECK(box->getYHalfLength() <= 3200.0); + CHECK(box->getXHalfLength() <= 750.0); + CHECK(box->getYHalfLength() <= 1600.0); CHECK(box->getZHalfLength() <= 200.0); } -// UpstreamTagger slab must be a GeoVFullPhysVol (sensitive volume) -TEST_CASE("UBTHasSensitiveVolume", "[upstreamtagger]") { +TEST_CASE("UBTMaterialsExist", "[upstreamtagger]") { SHiPMaterials materials; - SHiPGeometry::UpstreamTaggerFactory factory(materials); - GeoVPhysVol* ubt = factory.build(); - REQUIRE(ubt != nullptr); - CHECK(dynamic_cast(ubt) != nullptr); + CHECK(materials.getMaterial("Mylar") != nullptr); + CHECK(materials.getMaterial("ArCO2") != nullptr); + CHECK(materials.getMaterial("Polystyrene") != nullptr); +} + +TEST_CASE("UBTManagerReceivesSensitiveVolumes", "[upstreamtagger]") { + SHiPMaterials materials; + UpstreamTaggerFactory factory(materials); + SHiPUBTManager manager; + + factory.build(&manager); + + // Expect tube gas volumes (5 regions × 2 sub-layers × ~N tubes each) + CHECK(manager.numTubeGasVolumes() > 0); + // Expect tile volumes (2 blocks × 40 × 40 = 3200) + CHECK(manager.numTileVolumes() == 3200); + // getNumTreeTops should reflect total sensitive volume count + CHECK(manager.getNumTreeTops() == manager.numTubeGasVolumes() + manager.numTileVolumes()); +} + +TEST_CASE("UBTBuildWithoutManager", "[upstreamtagger]") { + SHiPMaterials materials; + UpstreamTaggerFactory factory(materials); + // Should not crash when no manager is supplied + GeoVPhysVol* ubt = factory.build(nullptr); + CHECK(ubt != nullptr); } From 5470f54bd20bf33cbff37e29ede20203238a365a Mon Sep 17 00:00:00 2001 From: Matei Climescu Date: Wed, 11 Mar 2026 19:00:56 +0100 Subject: [PATCH 2/3] runs without issues, unable to perform final validation for now but gmex yields something --- .../UpstreamTagger/UpstreamTaggerFactory.h | 5 ++- .../src/UpstreamTaggerFactory.cpp | 41 +++++++++---------- .../UpstreamTagger/test_upstreamtagger.cpp | 14 ++----- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h index 9a896d2..5f4e0ae 100644 --- a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h +++ b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h @@ -54,8 +54,9 @@ class UpstreamTaggerFactory { GeoVPhysVol* build(SHiPUBTManager* manager = nullptr); // Envelope half-dimensions (mm) — used by the test to check against CSV limits - static constexpr double s_halfX = 1000.0; - static constexpr double s_halfY = 1500.0; + // Envelope half-dimensions match CSV: half_width=750mm, half_height=1600mm + static constexpr double s_halfX = 750.0; + static constexpr double s_halfY = 1600.0; static constexpr double s_halfZ = 8.0; private: diff --git a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp index 840d264..9ce0102 100644 --- a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp +++ b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp @@ -76,9 +76,6 @@ void placeTubeSubLayer(GeoPhysVol* envelope, } } -// ============================================================================ -// placeDoubleStaggeredLayer (file-scope helper) -// ============================================================================ void placeDoubleStaggeredLayer(GeoPhysVol* envelope, GeoLogVol* wallLog, GeoLogVol* gasLog, @@ -97,9 +94,6 @@ void placeDoubleStaggeredLayer(GeoPhysVol* envelope, rOuter_mm, tag, 1, manager); } -// ============================================================================ -// makeEnvelope (file-scope helper) -// ============================================================================ GeoPhysVol* makeEnvelope(GeoPhysVol* mother, const GeoMaterial* mat, const std::string& tag, @@ -133,7 +127,7 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) const GeoMaterial* arco2 = m_materials.requireMaterial("ArCO2"); const GeoMaterial* poly = m_materials.requireMaterial("Polystyrene"); - // ---- Tube cross-section dimensions -------------------------------------- + // ---- Tube cross-section ------------------------------------------------- const double rOuter = s_tubeROuter_mm * mm; const double rInner = (s_tubeROuter_mm - s_tubeWall_mm) * mm; const double rGas = rInner - 0.001 * mm; // 1 µm clearance @@ -142,13 +136,10 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) if (rInner <= 0.0 || rInner >= rOuter) throw std::runtime_error("UBT: invalid tube wall thickness"); - // ---- Tile dimensions ---------------------------------------------------- + // ---- Tile half-size ----------------------------------------------------- const double tileHalf = 0.5 * s_tileSide_mm * mm; - // ---- Shared logical volumes --------------------------------------------- - // One wall LV and one gas LV per region (5 regions = 5 pairs). - // They are named by region so the SD can identify them by LV name. - + // ---- Shared logical volumes (one wall+gas pair per region) -------------- auto makeTubeLVs = [&](const std::string& region) -> std::pair { @@ -167,22 +158,30 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) new GeoBox(tileHalf, tileHalf, tileHalf), const_cast(poly)); - // ---- Layout constants (all in mm) --------------------------------------- - constexpr double outerHalfY_mm = 650.0; - constexpr double outerCtrY_mm = 850.0; - constexpr double leftHalfX_mm = 600.0; - constexpr double leftCtrX_mm = -400.0; - constexpr double rightHalfX_mm = 600.0; - constexpr double rightCtrX_mm = +400.0; + // ---- Layout constants scaled to CSV envelope (all mm) ------------------- + // CSV: half_width=750mm, half_height=1600mm, halfZ(length/2)=200mm + // + // Outer band: y=[+200,+1600] top / y=[-1600,-200] bottom + constexpr double outerHalfY_mm = 700.0; // (1600-200)/2 + constexpr double outerCtrY_mm = 900.0; // (200+1600)/2 + // Left half-plane: x=[-750,+200] → halfX=475, ctrX=-275 + constexpr double leftHalfX_mm = 475.0; + constexpr double leftCtrX_mm = -275.0; + // Right half-plane: x=[-200,+750] → halfX=475, ctrX=+275 + constexpr double rightHalfX_mm = 475.0; + constexpr double rightCtrX_mm = +275.0; + // Z offset between left/right half-planes constexpr double zLeft_mm = -2.0 * s_tubeROuter_mm; constexpr double zRight_mm = +2.0 * s_tubeROuter_mm; constexpr double outerEnvHalfZ_mm = 3.0 * s_tubeROuter_mm + 0.5; + // Central strip: x=[-600,+600], y=[-200,+200] constexpr double ctrHalfX_mm = 600.0; constexpr double ctrHalfY_mm = 200.0; constexpr double ctrEnvHalfZ_mm = s_tubeROuter_mm + 0.5; - constexpr double tileBlkHalfX_mm = 200.0; + // Tile blocks: x=[-750,-600] left, x=[+600,+750] right + constexpr double tileBlkHalfX_mm = 75.0; // (750-600)/2 constexpr double tileBlkHalfY_mm = 200.0; - constexpr double tileBlkCtrX_mm = 800.0; + constexpr double tileBlkCtrX_mm = 675.0; // 600+75 // ---- Top-level envelope ------------------------------------------------- auto* envPV = new GeoPhysVol( diff --git a/subsystems/UpstreamTagger/test_upstreamtagger.cpp b/subsystems/UpstreamTagger/test_upstreamtagger.cpp index f1f2f91..c362350 100644 --- a/subsystems/UpstreamTagger/test_upstreamtagger.cpp +++ b/subsystems/UpstreamTagger/test_upstreamtagger.cpp @@ -16,8 +16,7 @@ using SHiPGeometry::SHiPMaterials; using SHiPGeometry::SHiPUBTManager; using SHiPGeometry::UpstreamTaggerFactory; -// CSV envelope limits for UpstreamTagger: -// half_width = 0.75 m = 750 mm, half_height = 1.60 m = 1600 mm, halfZ = 200 mm +// CSV row: Upstream background tagger, half_width=0.75m=750mm, half_height=1.60m=1600mm TEST_CASE("UBTEnvelopeWithinCSVLimits", "[upstreamtagger]") { SHiPMaterials materials; UpstreamTaggerFactory factory(materials); @@ -42,21 +41,16 @@ TEST_CASE("UBTManagerReceivesSensitiveVolumes", "[upstreamtagger]") { SHiPMaterials materials; UpstreamTaggerFactory factory(materials); SHiPUBTManager manager; - factory.build(&manager); - // Expect tube gas volumes (5 regions × 2 sub-layers × ~N tubes each) + // 2 tile blocks × 15 tiles in X × 40 tiles in Y = 1200 CHECK(manager.numTubeGasVolumes() > 0); - // Expect tile volumes (2 blocks × 40 × 40 = 3200) - CHECK(manager.numTileVolumes() == 3200); - // getNumTreeTops should reflect total sensitive volume count + CHECK(manager.numTileVolumes() == 1200); CHECK(manager.getNumTreeTops() == manager.numTubeGasVolumes() + manager.numTileVolumes()); } TEST_CASE("UBTBuildWithoutManager", "[upstreamtagger]") { SHiPMaterials materials; UpstreamTaggerFactory factory(materials); - // Should not crash when no manager is supplied - GeoVPhysVol* ubt = factory.build(nullptr); - CHECK(ubt != nullptr); + CHECK(factory.build(nullptr) != nullptr); } From c9f8519a4564a9184a5a5355c2919f1f21db2a51 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 09:40:32 +0000 Subject: [PATCH 3/3] style: pre-commit fixes --- src/SHiPMaterials.cpp | 13 +- .../include/UpstreamTagger/SHiPUBTManager.h | 9 +- .../UpstreamTagger/UpstreamTaggerFactory.h | 10 +- .../src/UpstreamTaggerFactory.cpp | 202 +++++++----------- .../UpstreamTagger/test_upstreamtagger.cpp | 4 +- 5 files changed, 100 insertions(+), 138 deletions(-) diff --git a/src/SHiPMaterials.cpp b/src/SHiPMaterials.cpp index 949a9e1..add541a 100644 --- a/src/SHiPMaterials.cpp +++ b/src/SHiPMaterials.cpp @@ -183,29 +183,28 @@ void SHiPMaterials::createMaterials() { // Mylar / PET (density 1.39 g/cm³): C10H8O4 → C 62.50%, H 4.20%, O 33.30% GeoMaterial* mylar = new GeoMaterial("Mylar", 1.39 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); - mylar->add(m_elements["Carbon"], 0.6250); + mylar->add(m_elements["Carbon"], 0.6250); mylar->add(m_elements["Hydrogen"], 0.0420); - mylar->add(m_elements["Oxygen"], 0.3330); + mylar->add(m_elements["Oxygen"], 0.3330); mylar->lock(); m_materials["Mylar"] = mylar; // ArCO2 70/30 drift gas (density 1.842e-3 g/cm³): Ar 67.93%, C 8.75%, O 23.32% GeoMaterial* arco2 = new GeoMaterial("ArCO2", 1.842e-3 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); - arco2->add(m_elements["Argon"], 0.6793); - arco2->add(m_elements["Carbon"], 0.0875); - arco2->add(m_elements["Oxygen"], 0.2332); + arco2->add(m_elements["Argon"], 0.6793); + arco2->add(m_elements["Carbon"], 0.0875); + arco2->add(m_elements["Oxygen"], 0.2332); arco2->lock(); m_materials["ArCO2"] = arco2; // Polystyrene (density 1.06 g/cm³): C8H8 → C 92.26%, H 7.74% GeoMaterial* polystyrene = new GeoMaterial("Polystyrene", 1.06 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3); - polystyrene->add(m_elements["Carbon"], 0.9226); + polystyrene->add(m_elements["Carbon"], 0.9226); polystyrene->add(m_elements["Hydrogen"], 0.0774); polystyrene->lock(); m_materials["Polystyrene"] = polystyrene; - } } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h b/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h index 62f49ca..19816f5 100644 --- a/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h +++ b/subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h @@ -6,6 +6,7 @@ #include #include #include + #include namespace SHiPGeometry { @@ -53,14 +54,16 @@ class SHiPUBTManager : public GeoVDetectorManager { const auto nTubes = m_tubeGasVolumes.size(); const auto nTiles = m_tileVolumes.size(); if (nTubes + nTiles > 0) { - if (i < nTubes) return PVConstLink(m_tubeGasVolumes[i]); - if (i < nTubes + nTiles) return PVConstLink(m_tileVolumes[i - nTubes]); + if (i < nTubes) + return PVConstLink(m_tubeGasVolumes[i]); + if (i < nTubes + nTiles) + return PVConstLink(m_tileVolumes[i - nTubes]); } return PVConstLink(m_slab); } private: - GeoFullPhysVol* m_slab{nullptr}; + GeoFullPhysVol* m_slab{nullptr}; std::vector m_tubeGasVolumes; std::vector m_tileVolumes; }; diff --git a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h index 5f4e0ae..487068f 100644 --- a/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h +++ b/subsystems/UpstreamTagger/include/UpstreamTagger/UpstreamTaggerFactory.h @@ -55,17 +55,17 @@ class UpstreamTaggerFactory { // Envelope half-dimensions (mm) — used by the test to check against CSV limits // Envelope half-dimensions match CSV: half_width=750mm, half_height=1600mm - static constexpr double s_halfX = 750.0; + static constexpr double s_halfX = 750.0; static constexpr double s_halfY = 1600.0; - static constexpr double s_halfZ = 8.0; + static constexpr double s_halfZ = 8.0; private: SHiPMaterials& m_materials; - static constexpr double s_tubeROuter_mm = 2.5; - static constexpr double s_tubeWall_mm = 0.015; + static constexpr double s_tubeROuter_mm = 2.5; + static constexpr double s_tubeWall_mm = 0.015; static constexpr double s_tubeHalfLen_mm = 600.0; - static constexpr double s_tileSide_mm = 10.0; + static constexpr double s_tileSide_mm = 10.0; }; } // namespace SHiPGeometry diff --git a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp index 9ce0102..31bfc34 100644 --- a/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp +++ b/subsystems/UpstreamTagger/src/UpstreamTaggerFactory.cpp @@ -2,17 +2,18 @@ // Copyright (C) CERN for the benefit of the SHiP Collaboration #include "UpstreamTagger/UpstreamTaggerFactory.h" -#include "UpstreamTagger/SHiPUBTManager.h" + #include "SHiPGeometry/SHiPMaterials.h" +#include "UpstreamTagger/SHiPUBTManager.h" #include -#include -#include -#include +#include #include +#include #include +#include #include -#include +#include #include #include @@ -32,19 +33,11 @@ namespace { // Geo2G4 copy-number corruption that occurs when a GeoPhysVol is a daughter // of another GeoPhysVol sharing the same GeoLogVol across many instances. // ============================================================================ -void placeTubeSubLayer(GeoPhysVol* envelope, - GeoLogVol* wallLog, - GeoLogVol* gasLog, - double halfExtentY, - double zLocal, - double yShift, - double rOuter_mm, - const std::string& tag, - int subIdx, - SHiPUBTManager* manager) -{ +void placeTubeSubLayer(GeoPhysVol* envelope, GeoLogVol* wallLog, GeoLogVol* gasLog, + double halfExtentY, double zLocal, double yShift, double rOuter_mm, + const std::string& tag, int subIdx, SHiPUBTManager* manager) { const double rOuter = rOuter_mm * mm; - const double pitch = 2.0 * rOuter; + const double pitch = 2.0 * rOuter; const double usable = 2.0 * halfExtentY - std::abs(yShift); const int nTubes = std::max(1, static_cast(std::floor(usable / pitch)) + 1); @@ -54,10 +47,10 @@ void placeTubeSubLayer(GeoPhysVol* envelope, for (int i = 0; i < nTubes; ++i) { const double yPos = yFirst + i * pitch; - if (std::abs(yPos) > halfExtentY + 1e-6) continue; + if (std::abs(yPos) > halfExtentY + 1e-6) + continue; - const std::string name = tag + "_S" + std::to_string(subIdx) - + "_T" + std::to_string(i); + const std::string name = tag + "_S" + std::to_string(subIdx) + "_T" + std::to_string(i); const GeoTrf::Transform3D trf = GeoTrf::Translate3D(0.0, yPos, zLocal) * rotToX; // Wall (not sensitive) — placed first @@ -72,38 +65,26 @@ void placeTubeSubLayer(GeoPhysVol* envelope, envelope->add(new GeoTransform(trf)); envelope->add(gasFPV); - if (manager) manager->addTubeGasVolume(gasFPV); + if (manager) + manager->addTubeGasVolume(gasFPV); } } -void placeDoubleStaggeredLayer(GeoPhysVol* envelope, - GeoLogVol* wallLog, - GeoLogVol* gasLog, - double halfExtentY, - double zCenter, - double rOuter_mm, - const std::string& tag, - SHiPUBTManager* manager) -{ +void placeDoubleStaggeredLayer(GeoPhysVol* envelope, GeoLogVol* wallLog, GeoLogVol* gasLog, + double halfExtentY, double zCenter, double rOuter_mm, + const std::string& tag, SHiPUBTManager* manager) { const double rOuter = rOuter_mm * mm; - placeTubeSubLayer(envelope, wallLog, gasLog, - halfExtentY, zCenter - rOuter, 0.0, - rOuter_mm, tag, 0, manager); - placeTubeSubLayer(envelope, wallLog, gasLog, - halfExtentY, zCenter + rOuter, rOuter, - rOuter_mm, tag, 1, manager); + placeTubeSubLayer(envelope, wallLog, gasLog, halfExtentY, zCenter - rOuter, 0.0, rOuter_mm, tag, + 0, manager); + placeTubeSubLayer(envelope, wallLog, gasLog, halfExtentY, zCenter + rOuter, rOuter, rOuter_mm, + tag, 1, manager); } -GeoPhysVol* makeEnvelope(GeoPhysVol* mother, - const GeoMaterial* mat, - const std::string& tag, - double hx, double hy, double hz, - double cx, double cy, double cz) -{ - auto* log = new GeoLogVol((tag + "_LOG").c_str(), - new GeoBox(hx, hy, hz), +GeoPhysVol* makeEnvelope(GeoPhysVol* mother, const GeoMaterial* mat, const std::string& tag, + double hx, double hy, double hz, double cx, double cy, double cz) { + auto* log = new GeoLogVol((tag + "_LOG").c_str(), new GeoBox(hx, hy, hz), const_cast(mat)); - auto* pv = new GeoPhysVol(log); + auto* pv = new GeoPhysVol(log); mother->add(new GeoNameTag(tag.c_str())); mother->add(new GeoTransform(GeoTrf::Translate3D(cx, cy, cz))); mother->add(pv); @@ -115,22 +96,19 @@ GeoPhysVol* makeEnvelope(GeoPhysVol* mother, // ============================================================================ // UpstreamTaggerFactory // ============================================================================ -UpstreamTaggerFactory::UpstreamTaggerFactory(SHiPMaterials& materials) - : m_materials(materials) -{} +UpstreamTaggerFactory::UpstreamTaggerFactory(SHiPMaterials& materials) : m_materials(materials) {} -GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) -{ +GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) { // ---- Materials ---------------------------------------------------------- - const GeoMaterial* air = m_materials.requireMaterial("Air"); + const GeoMaterial* air = m_materials.requireMaterial("Air"); const GeoMaterial* mylar = m_materials.requireMaterial("Mylar"); const GeoMaterial* arco2 = m_materials.requireMaterial("ArCO2"); - const GeoMaterial* poly = m_materials.requireMaterial("Polystyrene"); + const GeoMaterial* poly = m_materials.requireMaterial("Polystyrene"); // ---- Tube cross-section ------------------------------------------------- - const double rOuter = s_tubeROuter_mm * mm; - const double rInner = (s_tubeROuter_mm - s_tubeWall_mm) * mm; - const double rGas = rInner - 0.001 * mm; // 1 µm clearance + const double rOuter = s_tubeROuter_mm * mm; + const double rInner = (s_tubeROuter_mm - s_tubeWall_mm) * mm; + const double rGas = rInner - 0.001 * mm; // 1 µm clearance const double halfLen = s_tubeHalfLen_mm * mm; if (rInner <= 0.0 || rInner >= rOuter) @@ -140,54 +118,47 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) const double tileHalf = 0.5 * s_tileSide_mm * mm; // ---- Shared logical volumes (one wall+gas pair per region) -------------- - auto makeTubeLVs = [&](const std::string& region) - -> std::pair - { - auto* wLV = new GeoLogVol( - ("UBT_" + region + "_TubeWall_LV").c_str(), - new GeoTube(rInner, rOuter, halfLen), - const_cast(mylar)); - auto* gLV = new GeoLogVol( - ("UBT_" + region + "_TubeGas_LV").c_str(), - new GeoTube(0.0, rGas, halfLen), - const_cast(arco2)); + auto makeTubeLVs = [&](const std::string& region) -> std::pair { + auto* wLV = + new GeoLogVol(("UBT_" + region + "_TubeWall_LV").c_str(), + new GeoTube(rInner, rOuter, halfLen), const_cast(mylar)); + auto* gLV = new GeoLogVol(("UBT_" + region + "_TubeGas_LV").c_str(), + new GeoTube(0.0, rGas, halfLen), const_cast(arco2)); return {wLV, gLV}; }; - auto* tileLV = new GeoLogVol("UBT_Tile_LV", - new GeoBox(tileHalf, tileHalf, tileHalf), + auto* tileLV = new GeoLogVol("UBT_Tile_LV", new GeoBox(tileHalf, tileHalf, tileHalf), const_cast(poly)); // ---- Layout constants scaled to CSV envelope (all mm) ------------------- // CSV: half_width=750mm, half_height=1600mm, halfZ(length/2)=200mm // // Outer band: y=[+200,+1600] top / y=[-1600,-200] bottom - constexpr double outerHalfY_mm = 700.0; // (1600-200)/2 - constexpr double outerCtrY_mm = 900.0; // (200+1600)/2 + constexpr double outerHalfY_mm = 700.0; // (1600-200)/2 + constexpr double outerCtrY_mm = 900.0; // (200+1600)/2 // Left half-plane: x=[-750,+200] → halfX=475, ctrX=-275 - constexpr double leftHalfX_mm = 475.0; - constexpr double leftCtrX_mm = -275.0; + constexpr double leftHalfX_mm = 475.0; + constexpr double leftCtrX_mm = -275.0; // Right half-plane: x=[-200,+750] → halfX=475, ctrX=+275 - constexpr double rightHalfX_mm = 475.0; - constexpr double rightCtrX_mm = +275.0; + constexpr double rightHalfX_mm = 475.0; + constexpr double rightCtrX_mm = +275.0; // Z offset between left/right half-planes - constexpr double zLeft_mm = -2.0 * s_tubeROuter_mm; - constexpr double zRight_mm = +2.0 * s_tubeROuter_mm; + constexpr double zLeft_mm = -2.0 * s_tubeROuter_mm; + constexpr double zRight_mm = +2.0 * s_tubeROuter_mm; constexpr double outerEnvHalfZ_mm = 3.0 * s_tubeROuter_mm + 0.5; // Central strip: x=[-600,+600], y=[-200,+200] - constexpr double ctrHalfX_mm = 600.0; - constexpr double ctrHalfY_mm = 200.0; - constexpr double ctrEnvHalfZ_mm = s_tubeROuter_mm + 0.5; + constexpr double ctrHalfX_mm = 600.0; + constexpr double ctrHalfY_mm = 200.0; + constexpr double ctrEnvHalfZ_mm = s_tubeROuter_mm + 0.5; // Tile blocks: x=[-750,-600] left, x=[+600,+750] right - constexpr double tileBlkHalfX_mm = 75.0; // (750-600)/2 - constexpr double tileBlkHalfY_mm = 200.0; - constexpr double tileBlkCtrX_mm = 675.0; // 600+75 + constexpr double tileBlkHalfX_mm = 75.0; // (750-600)/2 + constexpr double tileBlkHalfY_mm = 200.0; + constexpr double tileBlkCtrX_mm = 675.0; // 600+75 // ---- Top-level envelope ------------------------------------------------- - auto* envPV = new GeoPhysVol( - new GeoLogVol("UBT_Envelope_LV", - new GeoBox(s_halfX * mm, s_halfY * mm, s_halfZ * mm), - const_cast(air))); + auto* envPV = new GeoPhysVol(new GeoLogVol("UBT_Envelope_LV", + new GeoBox(s_halfX * mm, s_halfY * mm, s_halfZ * mm), + const_cast(air))); // ---- Outer bands (top + bottom) ----------------------------------------- for (int sign : {+1, -1}) { @@ -197,46 +168,36 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) // Left half-plane { auto [wLV, gLV] = makeTubeLVs(band + "_Left"); - auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Left", - leftHalfX_mm * mm, outerHalfY_mm * mm, - outerEnvHalfZ_mm * mm, - leftCtrX_mm * mm, yCtr, zLeft_mm * mm); - placeDoubleStaggeredLayer(env, wLV, gLV, - outerHalfY_mm * mm, 0.0, - s_tubeROuter_mm, "UBT_" + band + "_Left", manager); + auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Left", leftHalfX_mm * mm, + outerHalfY_mm * mm, outerEnvHalfZ_mm * mm, leftCtrX_mm * mm, + yCtr, zLeft_mm * mm); + placeDoubleStaggeredLayer(env, wLV, gLV, outerHalfY_mm * mm, 0.0, s_tubeROuter_mm, + "UBT_" + band + "_Left", manager); } // Right half-plane { auto [wLV, gLV] = makeTubeLVs(band + "_Right"); - auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Right", - rightHalfX_mm * mm, outerHalfY_mm * mm, - outerEnvHalfZ_mm * mm, - rightCtrX_mm * mm, yCtr, zRight_mm * mm); - placeDoubleStaggeredLayer(env, wLV, gLV, - outerHalfY_mm * mm, 0.0, - s_tubeROuter_mm, "UBT_" + band + "_Right", manager); + auto* env = makeEnvelope(envPV, air, "UBT_" + band + "_Right", rightHalfX_mm * mm, + outerHalfY_mm * mm, outerEnvHalfZ_mm * mm, rightCtrX_mm * mm, + yCtr, zRight_mm * mm); + placeDoubleStaggeredLayer(env, wLV, gLV, outerHalfY_mm * mm, 0.0, s_tubeROuter_mm, + "UBT_" + band + "_Right", manager); } } // ---- Central tube strip ------------------------------------------------- { auto [wLV, gLV] = makeTubeLVs("Central"); - auto* env = makeEnvelope(envPV, air, "UBT_Central", - ctrHalfX_mm * mm, ctrHalfY_mm * mm, - ctrEnvHalfZ_mm * mm, - 0.0, 0.0, 0.0); - placeDoubleStaggeredLayer(env, wLV, gLV, - ctrHalfY_mm * mm, 0.0, - s_tubeROuter_mm, "UBT_Central", manager); + auto* env = makeEnvelope(envPV, air, "UBT_Central", ctrHalfX_mm * mm, ctrHalfY_mm * mm, + ctrEnvHalfZ_mm * mm, 0.0, 0.0, 0.0); + placeDoubleStaggeredLayer(env, wLV, gLV, ctrHalfY_mm * mm, 0.0, s_tubeROuter_mm, + "UBT_Central", manager); } // ---- Tile blocks -------------------------------------------------------- - auto placeTileBlock = [&](const std::string& blkTag, double ctrX) - { - auto* blkEnv = makeEnvelope(envPV, air, blkTag, - tileBlkHalfX_mm * mm, tileBlkHalfY_mm * mm, - tileHalf, - ctrX, 0.0, 0.0); + auto placeTileBlock = [&](const std::string& blkTag, double ctrX) { + auto* blkEnv = makeEnvelope(envPV, air, blkTag, tileBlkHalfX_mm * mm, tileBlkHalfY_mm * mm, + tileHalf, ctrX, 0.0, 0.0); const int nX = static_cast(std::round(2.0 * tileBlkHalfX_mm / s_tileSide_mm)); const int nY = static_cast(std::round(2.0 * tileBlkHalfY_mm / s_tileSide_mm)); @@ -245,21 +206,20 @@ GeoVPhysVol* UpstreamTaggerFactory::build(SHiPUBTManager* manager) for (int ix = 0; ix < nX; ++ix) { for (int iy = 0; iy < nY; ++iy) { - const std::string tname = blkTag + "_T" + std::to_string(ix) - + "_" + std::to_string(iy); + const std::string tname = + blkTag + "_T" + std::to_string(ix) + "_" + std::to_string(iy); auto* tileFPV = new GeoFullPhysVol(tileLV); blkEnv->add(new GeoNameTag(tname.c_str())); - blkEnv->add(new GeoTransform( - GeoTrf::Translate3D(xFirst + ix * s_tileSide_mm * mm, - yFirst + iy * s_tileSide_mm * mm, - 0.0))); + blkEnv->add(new GeoTransform(GeoTrf::Translate3D( + xFirst + ix * s_tileSide_mm * mm, yFirst + iy * s_tileSide_mm * mm, 0.0))); blkEnv->add(tileFPV); - if (manager) manager->addTileVolume(tileFPV); + if (manager) + manager->addTileVolume(tileFPV); } } }; - placeTileBlock("UBT_TileLeft", -tileBlkCtrX_mm * mm); + placeTileBlock("UBT_TileLeft", -tileBlkCtrX_mm * mm); placeTileBlock("UBT_TileRight", +tileBlkCtrX_mm * mm); return envPV; diff --git a/subsystems/UpstreamTagger/test_upstreamtagger.cpp b/subsystems/UpstreamTagger/test_upstreamtagger.cpp index c362350..d9265a5 100644 --- a/subsystems/UpstreamTagger/test_upstreamtagger.cpp +++ b/subsystems/UpstreamTagger/test_upstreamtagger.cpp @@ -32,8 +32,8 @@ TEST_CASE("UBTEnvelopeWithinCSVLimits", "[upstreamtagger]") { TEST_CASE("UBTMaterialsExist", "[upstreamtagger]") { SHiPMaterials materials; - CHECK(materials.getMaterial("Mylar") != nullptr); - CHECK(materials.getMaterial("ArCO2") != nullptr); + CHECK(materials.getMaterial("Mylar") != nullptr); + CHECK(materials.getMaterial("ArCO2") != nullptr); CHECK(materials.getMaterial("Polystyrene") != nullptr); }