Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The SHiP geometry is described using GeoModel and is used by the simulation and
| DecayVolume | Approximate | Rectangular vessel (should be frustum) |
| TimingDetector | Complete | 330 scintillator bars via GeoModelXML |
| UpstreamTagger | Approximate | Monolithic slab (needs bar segmentation) |
| Trackers | Envelope only | 4 empty station boxes |
| Trackers | Complete | 4 stations, 4 stereo views each, 9600 straw tubes |
| Calorimeter | Simulation-ready | ECAL + HCAL sampling layers driven by `calo.toml` (Pb/PVT/HPL + Fe/PVT) |

## Building
Expand Down
32 changes: 32 additions & 0 deletions src/SHiPMaterials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,38 @@ void SHiPMaterials::createMaterials() {
polystyrene->lock();
m_materials["Polystyrene"] = polystyrene;
}

// Mylar / PET (density 1.39 g/cm³): C10H8O4 — straw tube walls
// MW = 10*12.011 + 8*1.008 + 4*15.999 = 192.166 g/mol
{
const double awC = 12.011;
const double awH = 1.008;
const double awO = 15.999;
const double mw = 10.0 * awC + 8.0 * awH + 4.0 * awO;
GeoMaterial* mylar =
new GeoMaterial("Mylar", 1.39 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3);
mylar->add(m_elements["Carbon"], 10.0 * awC / mw);
mylar->add(m_elements["Hydrogen"], 8.0 * awH / mw);
mylar->add(m_elements["Oxygen"], 4.0 * awO / mw);
mylar->lock();
m_materials["Mylar"] = mylar;
}

// ArCO2_70_30 (density 1.56e-3 g/cm³): 70% Ar + 30% CO2 by mass —
// straw tube gas fill. The CO2 mass fraction is split into its C and O
// constituents (C: 12.011/44.009, O: 2*15.999/44.009 of the CO2 mass).
{
const double mwCO2 = 44.009;
const double fracAr = 0.70;
const double fracCO2 = 0.30;
GeoMaterial* arco2 = new GeoMaterial(
"ArCO2_70_30", 1.56e-3 * GeoModelKernelUnits::g / GeoModelKernelUnits::cm3);
arco2->add(m_elements["Argon"], fracAr);
arco2->add(m_elements["Carbon"], fracCO2 * 12.011 / mwCO2);
arco2->add(m_elements["Oxygen"], fracCO2 * 2.0 * 15.999 / mwCO2);
arco2->lock();
m_materials["ArCO2_70_30"] = arco2;
}
}

} // namespace SHiPGeometry
112 changes: 91 additions & 21 deletions subsystems/Trackers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,110 @@ Straw tube tracking stations.

## Description

The Trackers subsystem implements 4 tracking stations for the SHiP spectrometer. Each station currently consists of an empty air box at the correct z-position. The full implementation requires straw tube modules with individual straws, stereo views, and support structures.
The Trackers subsystem implements 4 straw tracking stations for the SHiP
spectrometer. Each station envelope is filled with 4 stereo views; each
view is a material frame (FairShip-style hollow rectangle) enclosing a
staggered double sub-layer of straw tubes.

## Geometry Tree
A straw is a Mylar-walled cylinder filled with an Ar/CO₂ gas mixture. The
four views per station are rotated about the beam axis by alternating
stereo angles so that crossing straws give a space point.

The spectrometer dipole between stations 2 and 3 is a separate subsystem
(see `subsystems/Magnet`) and is not described here.

## Geometry tree

```
TrackersContainer (Air, 6000×6860×6000 mm)
├─ TrackerStation_1 (Air, 6000×6860×1000 mm) z = 84070 mm
├─ TrackerStation_2 (Air) z = 86070 mm
├─ TrackerStation_3 (Air) z = 93070 mm
└─ TrackerStation_4 (Air) z = 95070 mm
/SHiP/trackers (Air, 3000 × 3430 × 6000 mm half-extents)
├─ /SHiP/trackers/station_<n> (Air, 3000 × 3430 × 500 mm) n = 1..4
│ └─ /SHiP/trackers/station_<n>/view_<v>/envelope (Air) v = 0..3
│ (rotated about Z by the view stereo angle)
│ ├─ .../frame_body (Aluminium, hollow rectangle = outer − aperture)
│ ├─ .../sublayer_0_body (Air slab, 300 straws)
│ └─ .../sublayer_1_body (Air slab, 300 straws, half-pitch staggered)
│ └─ .../straw_<i>
│ └─ straw_wall_<uid> (Mylar tube)
│ └─ straw_gas_<uid> (ArCO2_70_30 tube)
└─ /SHiP/trackers/tracker_magnet (Air, inert marker box)
```
Comment on lines 21 to 33
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced code block to satisfy markdownlint.

The geometry tree fence is missing a language identifier (MD040). Adding text keeps the current rendering and clears lint noise.

Proposed patch
-```
+```text
 /SHiP/trackers (Air, 3000 × 3430 × 6000 mm half-extents)
  ├─ /SHiP/trackers/station_<n> (Air, 3000 × 3430 × 500 mm)   n = 1..4
  │    └─ /SHiP/trackers/station_<n>/view_<v>/envelope (Air)   v = 0..3
@@
-```
+```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 21-21: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@subsystems/Trackers/README.md` around lines 21 - 33, Add the language tag
"text" to the fenced code block that contains the geometry tree starting with
"/SHiP/trackers (Air, 3000 × 3430 × 6000 mm half-extents)" by changing the
opening triple backticks to ```text so the block is recognized by markdownlint
(MD040) while keeping the content unchanged.


Station Z positions (centres): 84070, 86070, 93070, 95070 mm.
Position in world: centred at z = 89570 mm (average of stations 1 and 4).
Stations 1-2 are upstream of the magnet, stations 3-4 downstream.

## Tracker magnet

`/SHiP/trackers/tracker_magnet` is an inert, air-filled marker volume for
the tracker magnet, centred at z = 86820 mm with a 460 mm Z extent.

It is **not** a physically-scaled dipole. The spectrometer dipole proper is
the separate `Magnet` subsystem (iron yoke + coils) occupying z = 87.07 to
92.07 m. The only space inside the trackers container that is clear of both
the straw stations and that yoke is the ~0.5 m gap between station 2 and the
yoke, so the marker is sized and placed to fit there without overlap.

The marker exists so the tracker magnet has a named placeholder in the
geometry; simulation/field code can locate it by its log-volume name. A
physically-scaled magnet, if required, belongs in the `Magnet` subsystem.

## Parameters

| Parameter | Value |
| --- | --- |
| Stations | 4 |
| Stereo views per station | 4 |
| Sub-layers per view | 2 (half-pitch staggered) |
| Straws per sub-layer | 300 |
| Straws total | 4 × 4 × 2 × 300 = 9600 |
| Straw outer diameter | 20 mm |
| Straw length | 4000 mm (along X) |
| Straw wall | 30 µm Mylar |
| Straw fill | Ar/CO₂ 70/30 by mass |
| View aperture | 4000 × 6000 mm (X × Y) |
| View stereo angles | +2.3°, −2.3°, +2.3°, −2.3° about Z |
| Frame bar width | 100 mm (X and Y) |
| Frame material | Aluminium |

## Materials

| Material | Density | Usage |
|----------|-------------|----------------------|
| Air | 1.29 mg/cm³ | Container & stations |
| Material | Density | Composition | Notes |
| --- | --- | --- | --- |
| Air | 1.29 mg/cm³ | already in catalog | container, station, view, sub-layer envelopes |
| Aluminium | 2.70 g/cm³ | already in catalog | view frames |
| Mylar | 1.39 g/cm³ | C₁₀H₈O₄, mass-fraction-normalised | straw walls |
| ArCO2_70_30 | 1.56 mg/cm³ | 70/30 Ar/CO₂ by mass | straw gas fill |

Mylar and ArCO2_70_30 are added by this subsystem to the central
`SHiPMaterials` catalog; Air and Aluminium were already present. All
elements required (C, H, O, Ar) were already in the element catalog.

## Tests

`test_trackers.cpp` exercises:

- `TrackersWithinEnvelope` — station 1 exists and its box stays within the
CSV envelope limits (≤ 3000 × 3500 × 500 mm half-extents).
- `TrackersHasFourStations` — all 4 station volumes are present.
- `TrackersStationHasViews` — each station holds 4 stereo views.
- `TrackersViewHasFrameAndSubLayers` — a view holds a frame plus two
sub-layers, and each sub-layer carries the full straw count.
- `TrackersHasTrackerMagnet` — the inert `tracker_magnet` marker exists and
fits in the gap before the spectrometer-magnet yoke (no overlap).

## Status

- [x] C++ implementation (envelope only)
- [ ] Implement straw tube geometry
- [ ] Add stereo views and support structures
- [ ] Verification against GDML
- [x] C++ implementation (straw-level geometry)
- [x] 4 stations × 4 stereo views
- [x] Straw tubes (Mylar wall + Ar/CO₂ gas)
- [x] Staggered double sub-layers
- [x] FairShip-style view frames
- [x] Inert TrackerMagnet marker volume
- [x] Mylar and ArCO2_70_30 materials
- [ ] Verification against a reference GDML

## TODO

- Implement straw tube modules within each station (major work)
- Individual straw tubes (mylar + gas)
- 4 views per station (Y, U, V, Y') with stereo angles
- Support frames and service volumes
- Add straw tube gas material (Ar/CO2 mixture) to SHiPMaterials
- Add mylar material to SHiPMaterials
- Verify station positions against GDML reference
- Verify straw pitch, view spacing and station positions against the GDML
reference once the tracker design is fixed.
- Add support frames and service volumes beyond the per-view frame.
119 changes: 110 additions & 9 deletions subsystems/Trackers/include/Trackers/TrackersFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,151 @@

#pragma once

#include <array>
#include <string>

class GeoPhysVol;

namespace SHiPGeometry {

class SHiPMaterials;

/**
* @brief Factory for the Trackers (straw tube tracking stations) geometry
* @brief Factory for the Trackers (straw tube tracking stations) geometry.
*
* Creates 4 tracking stations from GDML reference (statbox solid):
* Builds 4 straw tracking stations for the SHiP spectrometer. Station
* envelopes and z-positions follow the GDML reference / subsystem_envelopes.csv:
* - Station 1: Z 83.57-84.57 m → centre 84.07 m
* - Station 2: Z 85.57-86.57 m → centre 86.07 m
* - Station 3: Z 92.57-93.57 m → centre 93.07 m
* - Station 4: Z 94.57-95.57 m → centre 95.07 m
* GDML statbox: x=600 cm, y=686 cm, z=100 cm → half: 300×343×50 cm
* Station envelope (GDML statbox): half 3000 × 3430 × 500 mm.
*
* Each station envelope is filled with 4 stereo views. A view is a material
* frame (FairShip-style hollow rectangle) enclosing a staggered double
* sub-layer of straw tubes:
* - Straw: 20 mm outer diameter, 4 m long, horizontal (along X).
* - Wall: 30 µm Mylar; fill: Ar/CO₂ 70/30 by mass.
* - View stereo angles about the beam axis Z: +2.3°, -2.3°, +2.3°, -2.3°.
*
* The spectrometer dipole between stations 2 and 3 is a separate subsystem
* (see subsystems/Magnet) and is intentionally not described here.
*/
class TrackersFactory {
public:
explicit TrackersFactory(SHiPMaterials& materials);
~TrackersFactory() = default;

/**
* @brief Build the Trackers geometry
* @return Pointer to container volume with all 4 stations
* @brief Build the Trackers geometry.
* @return Pointer to the container volume holding all 4 stations.
*/
GeoPhysVol* build();

// ── Straw / view geometry constants (mm) ────────────────────────────
static constexpr int s_nStations = 4;
static constexpr int s_nViews = 4; ///< stereo views per station
static constexpr int s_nSubLayers = 2; ///< staggered straw layers per view

static constexpr double s_strawRadius = 10.0; ///< 1 cm radius (2 cm diam)
static constexpr double s_strawLength = 4000.0; ///< 4 m, along X
static constexpr double s_wallThickness = 0.030; ///< 30 µm Mylar wall

/// Active aperture inside a view frame (X = straw length region, Y = pitch).
static constexpr double s_apertureX = 4000.0;
static constexpr double s_apertureY = 6000.0;

/// Straws per sub-layer (aperture height / straw diameter).
static constexpr int s_nStraws = static_cast<int>(s_apertureY / (2.0 * s_strawRadius));

static constexpr double s_stereoAngleDeg = 2.3; ///< |stereo angle| per view

// View frame (FairShip-style hollow rectangle).
static constexpr double s_frameBarX = 100.0; ///< frame bar width in X
static constexpr double s_frameBarY = 100.0; ///< frame bar width in Y
static constexpr double s_frameHalfZ = 22.0; ///< frame half-thickness in Z

// ── Tracker magnet ──────────────────────────────────────────────────
// An inert, air-filled marker volume named "TrackerMagnet", placed in
// the clear gap between station 2 and the spectrometer-magnet yoke.
//
// NOTE: this is NOT a physically-scaled dipole. The spectrometer dipole
// proper is the separate Magnet subsystem (iron yoke + coils) occupying
// z = 87.07-92.07 m. The only free space inside the trackers container
// and clear of that yoke is a ~0.5 m gap, so this marker is sized to fit
// there. It exists so the tracker magnet has a named placeholder in the
// geometry; simulation/field code can locate it by the name
// "/SHiP/trackers/tracker_magnet".
static constexpr double s_trackerMagnetZ = 86820.0; ///< centre, mm
static constexpr double s_trackerMagnetHalfZ = 230.0; ///< half-depth, mm

private:
SHiPMaterials& m_materials;

// Station dimensions from GDML statbox (mm)
/// Frame material name in the central SHiPMaterials catalogue.
std::string m_frameMaterialName = "Aluminium";

// ── Station envelope from GDML statbox (mm) ─────────────────────────
static constexpr double s_halfX = 3000.0; // 300 cm
static constexpr double s_halfY = 3430.0; // 343 cm (GDML y=686 cm)
static constexpr double s_halfY = 3430.0; // 343 cm (GDML y = 686 cm)
static constexpr double s_halfZ = 500.0; // 50 cm

// Station Z positions (centres, in mm from origin)
// Station Z positions (centres, mm from origin).
static constexpr double s_station1Z = 84070.0; // 84.07 m
static constexpr double s_station2Z = 86070.0; // 86.07 m
static constexpr double s_station3Z = 93070.0; // 93.07 m
static constexpr double s_station4Z = 95070.0; // 95.07 m

// Container dimensions (spans all stations)
// Container dimensions (spans all stations).
static constexpr double s_containerHalfZ = (s_station4Z - s_station1Z) / 2.0 + s_halfZ;
static constexpr double s_containerCentreZ = (s_station1Z + s_station4Z) / 2.0;

// ── Internal builders ───────────────────────────────────────────────

/**
* @brief Build one station envelope and place its 4 stereo views.
* @param stationIndex 0-based station index (0..3).
* @return The station envelope physical volume.
*/
GeoPhysVol* buildStation(int stationIndex);

/**
* @brief Build one stereo view: a frame plus two staggered sub-layers.
* @param stationIndex 0-based parent station index, used for unique names.
* @param viewIndex 0-based view index within the station (0..3).
* @return The (unrotated) view physical volume.
*/
GeoPhysVol* buildView(int stationIndex, int viewIndex);

/**
* @brief Build the FairShip-style hollow-rectangle frame for one view.
* @param stationIndex 0-based parent station index, used for unique names.
* @param viewIndex 0-based view index within the station.
*/
GeoPhysVol* buildFrame(int stationIndex, int viewIndex);

/**
* @brief Build one sub-layer of s_nStraws parallel straws.
* @param stationIndex 0-based parent station index, used for unique names.
* @param viewIndex 0-based parent view index.
* @param shifted true for the half-pitch-staggered sub-layer.
*/
GeoPhysVol* buildSubLayer(int stationIndex, int viewIndex, bool shifted);

/**
* @brief Build a single straw: a Mylar wall cylinder with a gas core.
* @param uid Globally unique straw identifier, used for unique names.
*/
GeoPhysVol* buildStraw(int uid);

/**
* @brief Build the inert TrackerMagnet marker volume (air-filled box).
*
* Placed in the gap between station 2 and the spectrometer-magnet yoke.
* See the note on s_trackerMagnetZ for why this is a marker rather than
* a physically-scaled dipole.
*/
GeoPhysVol* buildTrackerMagnet();
};

} // namespace SHiPGeometry
Loading