SHiP Geometry
SHiP experiment geometry implementation using GeoModel.
Loading...
Searching...
No Matches
SBTConstants.h
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright (C) CERN for the benefit of the SHiP Collaboration
3
4#pragma once
5
6#include <algorithm>
7#include <string_view>
8
19
33struct SBTParams {
34 // ── Frustum envelope ────────────────────────────────────────────────
35 double xHalfEntrance = 1000.0;
36 double yHalfEntrance = 1500.0;
37 double xHalfExit = 2000.0;
38 double yHalfExit = 3000.0;
39 double totalLength = 50000.0;
40 int nSubFrustum = 10;
41 double yFloor = -3000.0;
42 double zEntrance = -25000.0;
43
44 // ── H-beam cross-section (HEA 260 approximation) ────────────────────
45 double hbeamHeight = 250.0;
46 double hbeamFlangeWidth = 260.0;
47 double hbeamFlangeThickness = 12.5;
48 double hbeamWebThickness = 7.5;
49
50 // ── Sensor containers / cells ───────────────────────────────────────
51 double containerThickness = 225.0;
52 double cellWallThickness = 5.0;
53 int nCells = 6;
54 double sensorClearance = 1.0;
55
56 // ── Helium decay region ─────────────────────────────────────────────
57 // Gap left between the helium and the innermost SBT material, measured
58 // along the coordinate axis. Must be >= 0; SBTEnvelope enforces that and
59 // is the only consumer. It is not a safety margin — the default of 1 um is
60 // simply enough to stop the helium and the SBT sharing a surface, which
61 // Geant4's navigator handles badly. 0 is legal and gives exact
62 // face-to-face contact.
63 double heliumClearance = 0.001;
64};
65
75inline constexpr SBTParams kSBT{};
76
77// ── Air container enclosing the SBT structure + sensors and helium ──────
78// The experiment's fixed envelope allocation for the decay region. Not an SBT
79// dimension and deliberately not part of SBTParams: nothing is derived from
80// it, and the three static_asserts at the bottom of this header are bounds the
81// SBT must fit *inside*, not parameters it is built *from*.
82inline constexpr double kEnvelopeHalfX = 2200.0;
83inline constexpr double kEnvelopeHalfY = 3300.0;
84inline constexpr double kEnvelopeHalfZ = 25200.0;
85
86// ── Materials (names in SHiPMaterials) ──────────────────────────────────
87inline constexpr std::string_view kAirMaterial = "Air"; // envelope container
88inline constexpr std::string_view kSteelMaterial = "Iron"; // H-beam structure
89inline constexpr std::string_view kWallMaterial = "Aluminium"; // sensor cell walls
90inline constexpr std::string_view kScintillatorMaterial = "LAB"; // sensor cells
91inline constexpr std::string_view kHeliumMaterial = "PressurisedHe90"; // decay region
92
93// ── Derived quantities ──────────────────────────────────────────────────
94//
95// Convention for everything below: the parameters come last and default to
96// kSBT. Last rather than first because most of these already have a
97// meaningful leading argument (a Z, a half-width) and a defaulted parameter
98// cannot precede a non-defaulted one. The parameter is named `params`, not
99// `p`, because `p` is the conventional loop variable for a HeliumPiece in
100// SBTEnvelope and in the tests.
101
103constexpr double subLength(const SBTParams& params = kSBT) {
104 return params.totalLength / params.nSubFrustum;
105}
107constexpr double webHeight(const SBTParams& params = kSBT) {
108 return params.hbeamHeight - 2.0 * params.hbeamFlangeThickness;
109}
111constexpr int nWalls(const SBTParams& params = kSBT) {
112 return params.nCells + 1;
113}
114
115// ── Frustum profile ─────────────────────────────────────────────────────
117constexpr double zExit(const SBTParams& params = kSBT) {
118 return params.zEntrance + params.totalLength;
119}
121constexpr double xGrowth(const SBTParams& params = kSBT) {
122 return (params.xHalfExit - params.xHalfEntrance) / params.totalLength;
123}
125constexpr double yGrowth(const SBTParams& params = kSBT) {
126 return (params.yHalfExit - params.yHalfEntrance) / params.totalLength;
127}
129constexpr double xHalfAt(double z_mm, const SBTParams& params = kSBT) {
130 return params.xHalfEntrance + (z_mm - params.zEntrance) * xGrowth(params);
131}
133constexpr double yHalfAt(double z_mm, const SBTParams& params = kSBT) {
134 return params.yHalfEntrance + (z_mm - params.zEntrance) * yGrowth(params);
135}
137constexpr double zSubLo(int s, const SBTParams& params = kSBT) {
138 return params.zEntrance + s * subLength(params);
139}
140
141// ── H-beam cross-section primitives ─────────────────────────────────────
143constexpr double hbeamFlangeOffset(const SBTParams& params = kSBT) {
144 return 0.5 * params.hbeamHeight - 0.5 * params.hbeamFlangeThickness;
145}
147constexpr double hbeamHalfHeight(const SBTParams& params = kSBT) {
148 return 0.5 * params.hbeamHeight;
149}
150
151// ══════════════════════════════════════════════════════════════════════
152// PLACEMENT PRIMITIVES — the single source of truth.
153//
154// These are THE definitions of where SBT material sits. Both builders
155// place volumes with them, and SBTEnvelope derives the helium inner
156// envelope from them, so a change to any rule below propagates to the
157// helium automatically and the two can never drift apart.
158// Do not open-code these expressions anywhere else.
159// ══════════════════════════════════════════════════════════════════════
160
164constexpr double zSplitOffset(const SBTParams& params = kSBT) {
165 return 0.5 * params.hbeamHeight + 0.5 * params.hbeamFlangeThickness;
166}
167
168// --- Side (±X) scintillator containers -------------------------------
170constexpr double sideContainerHalfThickness(const SBTParams& params = kSBT) {
171 return 0.5 * params.containerThickness;
172}
174constexpr double sideContainerCentreX(double x_half_mm, const SBTParams& params = kSBT) {
175 return x_half_mm - 0.5 * params.hbeamFlangeWidth - sideContainerHalfThickness(params);
176}
178constexpr double sideSensorInnerX(double x_half_mm, const SBTParams& params = kSBT) {
179 return sideContainerCentreX(x_half_mm, params) - sideContainerHalfThickness(params);
180}
181
182// --- Top/bottom (±Y) scintillator containers -------------------------
184constexpr double topBottomContainerHalfThickness(const SBTParams& params = kSBT) {
185 return 0.5 * params.containerThickness - params.sensorClearance;
186}
188constexpr double topBottomContainerCentreY(double y_half_mm, const SBTParams& params = kSBT) {
189 return y_half_mm - 0.5 * params.containerThickness;
190}
192constexpr double topBottomSensorInnerY(double y_half_mm, const SBTParams& params = kSBT) {
193 return topBottomContainerCentreY(y_half_mm, params) - topBottomContainerHalfThickness(params);
194}
204constexpr double topBottomAvailX(double x_half_mm, const SBTParams& params = kSBT) {
205 return x_half_mm - 0.5 * params.hbeamFlangeWidth - 1.0;
206}
207
208// --- Top/bottom longitudinal beams -----------------------------------
209// NOTE: these beams *straddle* the scintillator plane — the outer flange
210// sits above it, the web is omitted (it would pass through the cells),
211// and the INNER FLANGE HANGS BELOW IT, INSIDE THE DECAY REGION. It, not
212// the scintillator, is the innermost material in ±Y.
214constexpr double longBeamCentreY(double y_half_mm, const SBTParams& params = kSBT) {
215 return y_half_mm - 0.5 * webHeight(params);
216}
217
218namespace detail {
219
235constexpr double sqrtConst(double v) {
236 if (v <= 0.0) {
237 return 0.0;
238 }
239 double x = v;
240 double prev = 0.0;
241 for (int i = 0; i < 200 && x != prev; ++i) {
242 prev = x;
243 x = 0.5 * (x + v / x);
244 }
245 return x;
246}
247
249constexpr double longBeamTaper(const SBTParams& params = kSBT) {
250 const double g = yGrowth(params);
251 return sqrtConst(1.0 + g * g);
252}
253
254} // namespace detail
255
261constexpr double longBeamInnerY(double y_half_mm, const SBTParams& params = kSBT) {
262 return longBeamCentreY(y_half_mm, params) -
263 hbeamHalfHeight(params) * detail::longBeamTaper(params);
264}
265
266// ── Compile-time validation ─────────────────────────────────────────────
267//
268// Whether an arbitrary SBTParams is buildable at all is SBTEnvelope's
269// leavesDecayRegion(), which the tests exercise on configurations that must
270// not build. What follows constrains the *shipped* parameters only.
271
276constexpr bool isWellFormed(const SBTParams& params = kSBT) {
277 return params.totalLength > 0.0 && params.xHalfEntrance > 0.0 && params.yHalfEntrance > 0.0 &&
278 params.xHalfExit > 0.0 && params.yHalfExit > 0.0 && params.hbeamFlangeWidth > 0.0 &&
279 params.hbeamFlangeThickness > 0.0 && params.hbeamWebThickness > 0.0 &&
280 params.containerThickness > 0.0 && params.cellWallThickness > 0.0 &&
281 params.nSubFrustum > 0 && params.nCells > 0 && params.sensorClearance > 0.0 &&
282 params.sensorClearance < 0.5 * params.containerThickness &&
283 params.hbeamHeight > 2.0 * params.hbeamFlangeThickness && params.heliumClearance >= 0.0;
284}
285
286static_assert(isWellFormed(kSBT),
287 "the shipped SBT parameters are not self-consistent: check positivity, the "
288 "sensor clearance against the container thickness, and the flange thickness "
289 "against the beam height");
290static_assert(zSplitOffset(kSBT) < subLength(kSBT),
291 "the sensor containers' flat piece no longer fits inside a sub-frustum; "
292 "reduce nSubFrustum or hbeamHeight");
293
294// The SBT structure must fit the fixed decay-volume envelope. Bounds on the
295// outermost structure reach; with the current values the reaches are
296// ~2130 / ~3259 / ~25130 mm, inside the 2200 / 3300 / 25200 envelope.
297// X: vertical columns sit at the wider of the two frustum faces, with a
298// half-flange overhang.
299// Y: top/bottom cross-beams are shifted a full beam-height above yHalfExit
300// (plus a small frustum-growth term); +10 pads the assembly standoff.
301// Z: the structure spans [zEntrance, zExit()]; cross-beam flanges extend
302// half a flange-width beyond the end rows.
303//
304// These bound kSBT against the envelope allocation and are deliberately not
305// part of isWellFormed(): the parameter sweep in test_decayvolume varies the
306// frustum well past this allocation on purpose, and builds into a generous
307// throwaway container precisely because of it.
308static_assert(std::max(kSBT.xHalfEntrance, kSBT.xHalfExit) + 0.5 * kSBT.hbeamFlangeWidth <=
310 "SBT structure pierces the decay-volume envelope in X");
311static_assert(kSBT.yHalfExit + kSBT.hbeamHeight + 0.5 * kSBT.hbeamFlangeWidth * yGrowth(kSBT) +
312 10.0 <=
314 "SBT structure pierces the decay-volume envelope in Y");
315static_assert(std::max(kSBT.zEntrance < 0.0 ? -kSBT.zEntrance : kSBT.zEntrance,
316 zExit(kSBT) < 0.0 ? -zExit(kSBT) : zExit(kSBT)) +
317 0.5 * kSBT.hbeamFlangeWidth <=
319 "SBT structure pierces the decay-volume envelope in Z");
320
321} // namespace SHiPGeometry::SBT
constexpr double sqrtConst(double v)
Square root of v, usable in a constant expression.
Definition SBTConstants.h:235
constexpr double longBeamTaper(const SBTParams &params=kSBT)
Taper factor of a top/bottom longitudinal beam, 1/cos(atan(yGrowth)).
Definition SBTConstants.h:249
Compile-time parameters of the Surround Background Tagger (SBT).
Definition SBTConstants.h:18
constexpr double topBottomAvailX(double x_half_mm, const SBTParams &params=kSBT)
Half-extent in X available to top/bottom containers (mm).
Definition SBTConstants.h:204
constexpr std::string_view kWallMaterial
Definition SBTConstants.h:89
constexpr double zSubLo(int s, const SBTParams &params=kSBT)
Z of the start of sub-frustum s (mm).
Definition SBTConstants.h:137
constexpr SBTParams kSBT
The shipped SBT.
Definition SBTConstants.h:75
constexpr double topBottomContainerHalfThickness(const SBTParams &params=kSBT)
Half-thickness of a top/bottom container in Y (mm).
Definition SBTConstants.h:184
constexpr double yGrowth(const SBTParams &params=kSBT)
dy_half/dz of the frustum (dimensionless).
Definition SBTConstants.h:125
constexpr double kEnvelopeHalfY
Definition SBTConstants.h:83
constexpr std::string_view kSteelMaterial
Definition SBTConstants.h:88
constexpr double webHeight(const SBTParams &params=kSBT)
Clear web height = height - 2*flange thickness (mm).
Definition SBTConstants.h:107
constexpr double hbeamFlangeOffset(const SBTParams &params=kSBT)
Offset of a flange's mid-plane from the beam axis (mm).
Definition SBTConstants.h:143
constexpr std::string_view kScintillatorMaterial
Definition SBTConstants.h:90
constexpr std::string_view kHeliumMaterial
Definition SBTConstants.h:91
constexpr std::string_view kAirMaterial
Definition SBTConstants.h:87
constexpr double yHalfAt(double z_mm, const SBTParams &params=kSBT)
Half-extent of the frustum envelope in Y at a given Z (mm).
Definition SBTConstants.h:133
constexpr double hbeamHalfHeight(const SBTParams &params=kSBT)
Reach of a flange's outer surface from the beam axis (mm) = h/2.
Definition SBTConstants.h:147
constexpr double sideContainerCentreX(double x_half_mm, const SBTParams &params=kSBT)
|X| of a side container's centroid, given the local frustum half-width.
Definition SBTConstants.h:174
constexpr double subLength(const SBTParams &params=kSBT)
Length of one sub-frustum along Z (mm).
Definition SBTConstants.h:103
constexpr double kEnvelopeHalfZ
Definition SBTConstants.h:84
constexpr double topBottomContainerCentreY(double y_half_mm, const SBTParams &params=kSBT)
|Y| of a top/bottom container's centroid (mm).
Definition SBTConstants.h:188
constexpr double kEnvelopeHalfX
Definition SBTConstants.h:82
constexpr double topBottomSensorInnerY(double y_half_mm, const SBTParams &params=kSBT)
|Y| of a top/bottom container's innermost face (mm).
Definition SBTConstants.h:192
constexpr double xHalfAt(double z_mm, const SBTParams &params=kSBT)
Half-extent of the frustum envelope in X at a given Z (mm).
Definition SBTConstants.h:129
constexpr bool isWellFormed(const SBTParams &params=kSBT)
Are params self-consistent, before asking whether they leave a decay region? Every dimension positive...
Definition SBTConstants.h:276
constexpr double zExit(const SBTParams &params=kSBT)
Exit-face Z in the DecayVolume local frame (mm).
Definition SBTConstants.h:117
constexpr double xGrowth(const SBTParams &params=kSBT)
dx_half/dz of the frustum (dimensionless).
Definition SBTConstants.h:121
constexpr int nWalls(const SBTParams &params=kSBT)
Number of aluminium walls per container (nCells + 1).
Definition SBTConstants.h:111
constexpr double sideContainerHalfThickness(const SBTParams &params=kSBT)
Half-thickness of a side container in X (mm).
Definition SBTConstants.h:170
constexpr double longBeamInnerY(double y_half_mm, const SBTParams &params=kSBT)
|Y| reached by a longitudinal beam's inner flange surface (mm).
Definition SBTConstants.h:261
constexpr double longBeamCentreY(double y_half_mm, const SBTParams &params=kSBT)
|Y| of a top/bottom longitudinal beam's axis (mm).
Definition SBTConstants.h:214
constexpr double sideSensorInnerX(double x_half_mm, const SBTParams &params=kSBT)
|X| of a side container's innermost face (mm).
Definition SBTConstants.h:178
constexpr double zSplitOffset(const SBTParams &params=kSBT)
Z offset, from the start of a sub-frustum, of the column front-flange outer edge.
Definition SBTConstants.h:164
Every dimension the SBT geometry is parameterised by (mm).
Definition SBTConstants.h:33
double xHalfEntrance
Definition SBTConstants.h:35
double yHalfExit
Definition SBTConstants.h:38
double yFloor
Definition SBTConstants.h:41
double yHalfEntrance
Definition SBTConstants.h:36
double containerThickness
Definition SBTConstants.h:51
double xHalfExit
Definition SBTConstants.h:37
double totalLength
Definition SBTConstants.h:39
double hbeamFlangeThickness
Definition SBTConstants.h:47
double cellWallThickness
Definition SBTConstants.h:52
double heliumClearance
Definition SBTConstants.h:63
int nCells
Definition SBTConstants.h:53
double hbeamWebThickness
Definition SBTConstants.h:48
double sensorClearance
Definition SBTConstants.h:54
int nSubFrustum
Definition SBTConstants.h:40
double hbeamFlangeWidth
Definition SBTConstants.h:46
double hbeamHeight
Definition SBTConstants.h:45
double zEntrance
Definition SBTConstants.h:42