Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ShipSoft/FairShip/llms.txt

Use this file to discover all available pages before exploring further.

The Scattering and Neutrino Detector (SND) is positioned between the muon shield exit and the upstream entrance of the decay vessel. Its primary physics goals are to detect and measure neutrino interactions — especially from tau-neutrinos — and to search for hidden-particle scattering signatures that leave short tracks inside a dense target. SND comprises several complementary sub-systems, each addressing a different part of the physics programme. The sub-system configuration is selected by the --SND_design flag at simulation time, and the full set of geometry parameters is driven by YAML files read at runtime. This page describes all four sub-systems — NuTauTarget, nuTauTT, SiliconTarget, and MTC — together with their C++ classes, Python digitisers, and key geometry parameters.

Enabling SND at runtime

# Enable SND with the default design (design 2: MTC + SiliconTarget)
python macro/run_simScript.py --SND --SND_design 2

# Enable SND with the emulsion-based design
python macro/run_simScript.py --SND --SND_design 1

# Enable both designs in the same simulation
python macro/run_simScript.py --SND --SND_design all
In geometry_config.py, SND_design is always stored as a list so multiple designs can coexist:
def create_config(SND: bool = True, SND_design=None, ...):
    if SND_design is None:
        SND_design = [2]
    if not isinstance(SND_design, list):
        SND_design = [SND_design]
    c.SND_design = SND_design
The configure() function in python/shipDet_conf.py iterates over this list and calls the appropriate configuration function for each design number:
for design in ship_geo.SND_design:
    if design == 2:
        configure_snd_mtc(...)
        configure_snd_siliconTarget(...)
    elif design == 1:
        configure_snd_old(...)
    else:
        print(f"Warning: SND design {design} is not recognized.")

Design 1 — NuTauTarget (emulsion bricks) + nuTauTT (SciFi tracker)

Design 1 implements the classic OPERA-style emulsion brick target interleaved with SciFi tracking planes. It is configured by configure_snd_old() in python/shipDet_conf.py, which reads geometry/snd_config_old.yaml.

NuTauTarget C++ class: Target

Located in SND/EmulsionTarget/Target.h. The target is a wall of emulsion bricks, each brick consisting of alternating lead and emulsion film plates inside a protective plastic wrapping.
NuTauTarget = ROOT.Target("NuTauTarget", nuTarget_geo.Ydist, ROOT.kTRUE)
NuTauTarget.MakeNuTargetPassive(nuTarget_geo.nuTargetPassive)
NuTauTarget.SetDetectorDesign(nuTarget_geo.Design)
NuTauTarget.SetNumberBricks(nuTarget_geo.col, nuTarget_geo.row, nuTarget_geo.wall)
NuTauTarget.SetEmulsionParam(
    nuTarget_geo.EmTh,     # emulsion film thickness (cm)
    nuTarget_geo.EmX,      # emulsion film x-dimension (cm)
    nuTarget_geo.EmY,      # emulsion film y-dimension (cm)
    nuTarget_geo.PBTh,     # plastic base thickness (cm)
    2*nuTarget_geo.EmTh + nuTarget_geo.PBTh,   # EPlW: double emulsion plate width
    nuTarget_geo.LeadTh,                        # lead plate thickness (cm)
    nuTarget_geo.LeadTh + 2*nuTarget_geo.EmTh + nuTarget_geo.PBTh,  # AllPW
)

nuTauTT C++ class: TargetTracker

Located in SND/EmulsionTarget/TargetTracker.h. Each tracker plane is a SciFi mat of width scifimat_width in x and y:
NuTauTT = ROOT.TargetTracker(
    "TargetTrackers",
    snd_nuTauTT_TTX,   # total x-dimension (cm)
    snd_nuTauTT_TTY,   # total y-dimension (cm)
    snd_nuTauTT_TTZ,   # total z-dimension (cm)
    ROOT.kTRUE,
)
NuTauTT.SetSciFiParam(
    nuTauTT_geo.scifimat_width,
    snd_nuTauTT_TTX,
    snd_nuTauTT_TTY,
    nuTauTT_geo.scifimat_z,    # SciFi mat thickness (cm)
    nuTauTT_geo.support_z,     # support structure thickness (cm)
    nuTauTT_geo.honeycomb_z,   # Airex / Nomex honeycomb thickness (cm)
)
NuTauTT.SetNumberSciFi(nuTauTT_geo.n_hor_planes, nuTauTT_geo.n_vert_planes)

Key YAML parameters (geometry/snd_config_old.yaml)

ParameterDefaultDescription
n_plates36Number of lead+emulsion plates per brick
LeadTh0.1 cmLead plate thickness
EmTh0.0070 cmEmulsion film thickness
PBTh0.0175 cmPlastic base thickness
EmX40 cmEmulsion film x-dimension
EmY40 cmEmulsion film y-dimension
BrPackX0 cmExtra x-packaging around brick
BrPackY0 cmExtra y-packaging around brick
BrPackZ0 cmExtra z-packaging around brick
row1Number of brick rows
col1Number of brick columns
wall5Number of brick walls (along z)
Design4Detector design index
nuTargetPassivetrueOnly passive material (no active readout in bricks)
BaseY20 cmBase support structure height
PillarX50 cmSupport pillar x-dimension
PillarZ50 cmSupport pillar z-dimension

Brick and target dimension formulas

# Brick z-dimension (depth along beam)
snd_nuTarget_BrZ = (
    n_plates * LeadTh
    + (n_plates + 1) * (2 * EmTh + PBTh)
    + BrPackZ
)

# Total z-dimension of the NuTauTarget assembly
snd_nuTarget_zdim = wall * snd_nuTarget_BrZ + (wall + 1) * snd_nuTauTT_TTZ

Design 2 — MTC (Muon Tracker for Charm)

The MTC (MTCDetector, SND/MTC/MTCDetector.h) is a SciFi + scintillator spectrometer placed at the downstream end of the muon shield. It is designed to reconstruct the charge and momentum of muons from charm decays. Configuration is read from geometry/MTC_config.yaml.

MTC constructor call

mtc = ROOT.MTCDetector("MTC", ROOT.kTRUE)
mtc.SetMTCParameters(
    ship_geo.mtc_geo.width,
    ship_geo.mtc_geo.height,
    ship_geo.mtc_geo.angle,          # stereo angle of SciFi planes
    ship_geo.mtc_geo.ironThick,      # iron absorber thickness per layer
    ship_geo.mtc_geo.sciFiThick,     # SciFi plane thickness
    ship_geo.mtc_geo.num_of_agg_channels,
    ship_geo.mtc_geo.scint_cell_size,
    ship_geo.mtc_geo.scintThick,     # scintillator plane thickness
    ship_geo.mtc_geo.nLayers,        # total number of layers
    ship_geo.mtc_geo.zPosition,      # z-centre of MTC
    ship_geo.mtc_geo.fieldY,         # magnetic field Bfield in y
)
When zPosition == "auto", the MTC is placed at the centre of the last muon-shield magnet:
mtc_total_length = (ironThick + sciFiThick + scintThick) * nLayers
ship_geo.mtc_geo.zPosition = ship_geo.muShield.Entrance[-1] + mtc_total_length / 2

Python digitiser: MTCDetector

python/detectors/MTCDetector.py inherits from BaseDetector. It reads MTCDetPoint MC hits, maps SiPM channels to SciFi fibres using SciFiMapping, and produces MTCDetHit objects. A fibre ID example:
fiberID = 123051820
  → 1:  MTC unique detector ID
  → 23: layer number
  → 0:  station type (0 = +5°, 1 = −5°, 2 = scintillator plane)
  → 5:  z-layer number (0–5)
  → 1820: fibre index within the layer
The MTC also notifies the muon shield that space must be reserved for it:
# python/shipDet_conf.py
MuonShield.SetSNDSpace(
    hole    = True,
    hole_dx = (ship_geo.mtc_geo.width  + 5.0 * u.cm) / 2.0,
    hole_dy = (ship_geo.mtc_geo.height + 5.0 * u.cm) / 2.0,
)

Design 2 — SiliconTarget

The SiliconTarget (SND/SiliconTarget/SiliconTarget.h) provides silicon strip tracking as an alternative to emulsion bricks for precision vertexing. It is placed slightly upstream of the MTC. Configuration is read from geometry/SiliconTarget_config.yaml.
SiliconTarget = ROOT.SiliconTarget("SiliconTarget", ROOT.kTRUE)
SiliconTarget.SetSiliconTargetParameters(
    ship_geo.SiliconTarget_geo.targetWidth,
    ship_geo.SiliconTarget_geo.targetHeight,
    ship_geo.SiliconTarget_geo.sensorWidth,
    ship_geo.SiliconTarget_geo.sensorLength,
    ship_geo.SiliconTarget_geo.nLayers,
    ship_geo.SiliconTarget_geo.zPosition,
    ship_geo.SiliconTarget_geo.targetThickness,   # thickness per layer (cm)
    ship_geo.SiliconTarget_geo.targetSpacing,     # z-spacing between layers (cm)
    ship_geo.SiliconTarget_geo.moduleOffset,
)
When zPosition == "auto", the detector is placed relative to the second-to-last muon-shield magnet:
SiliconTarget_total_length = targetSpacing * nLayers
ship_geo.SiliconTarget_geo.zPosition = (
    ship_geo.muShield.Entrance[-1]
    - ship_geo.muShield.Zgap[-1]
    - SiliconTarget_total_length / 2
)
The digitisation configuration is in python/SiliconTarget-digi-config.json.

Timing detector (TimeDet / timeDetector)

The timing detector is closely associated with the SND readout for track-to-vertex association. The TimeDet class (TimeDet/TimeDet.h) builds two orthogonal scintillator bar layers. Its Python digitiser timeDetector (python/detectors/timeDetector.py) converts TimeDetPoint MC hits to TimeDetHit objects, selecting the earliest valid hit per bar cell:
# python/detectors/timeDetector.py (excerpt)
class timeDetector(BaseDetector):
    def digitize(self) -> None:
        earliest_per_det_id = {}
        for index, point in enumerate(self.intree.TimeDetPoint):
            hit = ROOT.TimeDetHit(point, self.intree.t0)
            self.det.push_back(hit)
            if hit.isValid():
                detector_id = hit.GetDetectorID()
                if detector_id in earliest_per_det_id:
                    times = hit.GetMeasurements()
                    earliest = earliest_per_det_id[detector_id]
                    ref_times = self.det[earliest].GetMeasurements()
                    if ref_times[0] > times[0] or ref_times[1] > times[1]:
                        self.det[earliest].setInvalid()
                        earliest_per_det_id[detector_id] = index
                    else:
                        self.det[index].setInvalid()
                else:
                    earliest_per_det_id[detector_id] = index
TimeDet geometry parameters from geometry_config.py:
ParameterValueDescription
dzBarRow1.2 cmz-spacing for row bars
dzBarCol2.4 cmz-spacing for column bars
zBar1.0 cmbar thickness
DX225 cmhalf-width of detector
DY325 cmhalf-height of detector

SND source-file map

SND/
├── EmulsionTarget/
│   ├── Target.h / Target.cxx          ← NuTauTarget geometry (design 1)
│   ├── TargetTracker.h / .cxx         ← nuTauTT SciFi tracker (design 1)
│   ├── TargetPoint.h / .cxx           ← MC hit point for target
│   └── TTPoint.h / .cxx               ← MC hit point for tracker
├── MTC/
│   ├── MTCDetector.h / MTCDetector.cxx ← MTC geometry (design 2)
│   ├── MTCDetPoint.h / .cxx            ← MC hit point
│   └── MTCDetHit.h / .cxx              ← digitised hit
└── SiliconTarget/
    ├── SiliconTarget.h / .cxx          ← Si strip tracker (design 2)
    ├── SiliconTargetPoint.h / .cxx     ← MC hit point
    └── SiliconTargetHit.h / .cxx       ← digitised hit
The YAML configuration files for all SND sub-systems are stored under $FAIRSHIP/geometry/: snd_config_old.yaml (design 1), MTC_config.yaml (design 2 MTC), and SiliconTarget_config.yaml (design 2 silicon). These files must be present at runtime; the code opens them with open(yaml_file) and raises FileNotFoundError if they are missing.

Checking the SND geometry

1
Run a short simulation with SND enabled
2
python macro/run_simScript.py \
    --SND --SND_design 2 \
    --shieldName TRY_2025 \
    --nEvents 10 \
    --output sim_snd_test.root
4
python macro/getGeoInformation.py \
    --geometry sim_snd_test.root \
    --level 3
5
Look for MTC, SiliconTarget, NuTauTarget, and TargetTrackers volumes in the output.
6
Inspect MC hit branches
7
import ROOT
f = ROOT.TFile.Open("sim_snd_test.root")
tree = f.Get("cbmsim")
tree.GetListOfBranches().Print()  # shows MTCDetPoint, SiliconTargetPoint, etc.

for event in tree:
    for hit in event.MTCDetPoint:
        print(hit.GetDetectorID(), hit.GetTrackID(), hit.GetTime())

Detector Overview

Full downstream map of all SHiP sub-detectors with C++ class and Python digitiser references.

Muon Shield

The MTC interacts with the muon shield — learn how SetSNDSpace carves the aperture.

Build docs developers (and LLMs) love