Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/SHiPMaterials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,32 @@ 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
122 changes: 105 additions & 17 deletions subsystems/UpstreamTagger/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,124 @@
# 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.023 g/cm³ | Detector slab |

## Status
| 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 |


## Position in World

z = 32720 mm (centre of 32520–32920 mm range, from subsystem_envelopes.csv).

- [x] C++ implementation (monolithic slab)
- [ ] Implement bar segmentation with SiPM readout
- [ ] Verification against GDML
## Geant4 Integration

## TODO
To register sensitive detectors in a Geant4 application:

```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
45 changes: 41 additions & 4 deletions subsystems/UpstreamTagger/include/UpstreamTagger/SHiPUBTManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,65 @@
#include <GeoModelKernel/GeoVDetectorManager.h>
#include <GeoModelKernel/GeoVPhysVol.h>

#include <vector>

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<GeoFullPhysVol*>& getTubeGasVolumes() const { return m_tubeGasVolumes; }
const std::vector<GeoFullPhysVol*>& 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<unsigned int>(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};
std::vector<GeoFullPhysVol*> m_tubeGasVolumes;
std::vector<GeoFullPhysVol*> m_tileVolumes;
};

} // namespace SHiPGeometry
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,61 @@ 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:
explicit UpstreamTaggerFactory(SHiPMaterials& materials);
~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
// 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:
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
Loading