Skip to content

Commit 0adcac6

Browse files
Allow virtual beampipe tracking detector volume to be intersection solid (#989)
### Briefly, what does this PR introduce? In preparation for changes in the shape of the beampipe in the far backward region this PR allows the tracking layers to be defined as intersection solids between any shaped beampipe and a thin box. The locations where it becomes important to track the position of the beamspot and acceptance of Low-Q2 electrons along the beamline is expected to move away from the current cone segment. This prepares for that change by ensuring that the current benchmark plots will remain valid. ### What kind of change does this PR introduce? - [x] Bug fix (issue #963 ) - [ ] New feature (issue #__) - [ ] Documentation update - [ ] Other: __ ### Please check if this PR fulfills the following: - [ ] Tests for the changes have been added - [ ] Documentation has been added / updated - [ ] Changes have been communicated to collaborators ### Does this PR introduce breaking changes? What changes might users need to make to their code? ### Does this PR change default behavior? --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent d5ce3ef commit 0adcac6

File tree

2 files changed

+74
-26
lines changed

2 files changed

+74
-26
lines changed

compact/far_backward/beamline_tracking.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
pipe_id="4"
3737
grandmother="Pipe_Q1eR_to_B2BeR"
3838
mother="Pipe_in_B2BeR_vacuum"
39-
name="Pipe_in_B2BeR_tracker"/>
39+
name="Pipe_in_B2BeR_tracker"
40+
use_cross_section="true"
41+
plane_z="B2BeR_CenterPosition-B2BeR_Length/2"
42+
plane_x="0*mm"
43+
plane_yrot="0*deg"/>
4044

4145
<slice
4246
pipe_id="5"

src/BeamPipeTracking_geo.cpp

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// SPDX-License-Identifier: LGPL-3.0-or-later
2-
// Copyright (C) 2024 Simon Gardner
2+
// Copyright (C) 2024-2025 Simon Gardner
33

44
//==========================================================================
55
//
6-
// Places a small sensitive disk of vacuum at the end of beam pipes
6+
// Places thin sensitive slices of vacuum along a beam pipe
77
//
88
//==========================================================================
99

@@ -29,7 +29,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
2929
DetElement sdet(det_name, det_id);
3030
Assembly assembly(det_name + "_assembly");
3131

32-
// Grab info for beamline magnets
32+
// Loop over each requested slice from the geometry description
3333
for (xml_coll_t slice_coll(x_det, _Unicode(slice)); slice_coll; slice_coll++) { // pipes
3434

3535
string grandmotherName = slice_coll.attr<string>(_Unicode(grandmother));
@@ -42,41 +42,85 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
4242
// Get the mother volume
4343
Volume mother_vol = mother.volume();
4444

45-
// Get mother volume shape as cone segment
46-
ConeSegment mother_shape = mother_vol.solid();
47-
48-
// Get the parameters of the mother volume
49-
double rOuter1 = mother_shape.rMax1();
50-
double rOuter2 = mother_shape.rMax2();
51-
double length = 2 * mother_shape.dZ();
52-
5345
double sensitive_thickness = 0.1 * mm;
54-
55-
//Calculate R or cone after sensitive layer
56-
57-
double rEnd = rOuter2 - (rOuter2 - rOuter1) * sensitive_thickness / length;
58-
double zPos = length / 2.0 - sensitive_thickness / 2.0;
59-
if (detStart) {
60-
rEnd = rOuter1 - (rOuter1 - rOuter2) * sensitive_thickness / length;
61-
zPos = -length / 2.0 + sensitive_thickness / 2.0;
46+
Solid sensitive_solid;
47+
Transform3D disk_transform; // Full placement transform
48+
49+
// Check if we should use plane-based cross section (default: false, use cone)
50+
bool use_plane_xs = getAttrOrDefault<bool>(slice_coll, _Unicode(use_cross_section), false);
51+
52+
if (use_plane_xs) {
53+
54+
// Get plane definition from XML (in world frame)
55+
double plane_x = getAttrOrDefault<double>(slice_coll, _Unicode(plane_x), 0.0);
56+
double plane_y = getAttrOrDefault<double>(slice_coll, _Unicode(plane_y), 0.0);
57+
double plane_z = getAttrOrDefault<double>(slice_coll, _Unicode(plane_z), 0.0);
58+
double plane_yrot = getAttrOrDefault<double>(slice_coll, _Unicode(plane_yrot), 0.0);
59+
60+
// Get the mother volume's transformation to world frame
61+
auto mother_matrix = mother.nominal().worldTransformation();
62+
const Double_t* translation = mother_matrix.GetTranslation();
63+
const Double_t* rotation = mother_matrix.GetRotationMatrix();
64+
65+
// Build Transform3D from TGeoMatrix
66+
Transform3D mother_to_world(rotation[0], rotation[1], rotation[2], translation[0],
67+
rotation[3], rotation[4], rotation[5], translation[1],
68+
rotation[6], rotation[7], rotation[8], translation[2]);
69+
70+
// Transform the plane position and rotation from world frame to mother's local frame
71+
Position plane_pos_world(plane_x, plane_y, plane_z);
72+
RotationY plane_rot_world(plane_yrot);
73+
Transform3D plane_transform_world(plane_rot_world, plane_pos_world);
74+
75+
// Apply inverse transformation to get plane in mother's local coordinates
76+
disk_transform = mother_to_world.Inverse() * plane_transform_world;
77+
78+
// Get maximum dimension for cutting box
79+
double max_dim = getAttrOrDefault<double>(slice_coll, _Unicode(max_dimension), 1.0 * m);
80+
81+
// Create a thin box perpendicular to the plane normal
82+
Box cutting_box(max_dim, max_dim, sensitive_thickness / 2);
83+
84+
// Create intersection of cutting box with mother volume (reversed order to preserve rotation)
85+
// Apply the inverse transform to the second operand (mother) to express it in the box frame
86+
sensitive_solid =
87+
IntersectionSolid(cutting_box, mother_vol.solid(), disk_transform.Inverse());
88+
89+
} else {
90+
// Use cone segment (default behavior before outline of xs is implemented in benchmark)
91+
ConeSegment mother_shape = mother_vol.solid();
92+
93+
// Get the parameters of the mother volume
94+
double rOuter1 = mother_shape.rMax1();
95+
double rOuter2 = mother_shape.rMax2();
96+
double length = 2 * mother_shape.dZ();
97+
98+
//Calculate R of cone after sensitive layer
99+
double rEnd = rOuter2 - (rOuter2 - rOuter1) * sensitive_thickness / length;
100+
double zPos = length / 2.0 - sensitive_thickness / 2.0;
101+
if (detStart) {
102+
rEnd = rOuter1 - (rOuter1 - rOuter2) * sensitive_thickness / length;
103+
zPos = -length / 2.0 + sensitive_thickness / 2.0;
104+
}
105+
106+
sensitive_solid = ConeSegment(sensitive_thickness / 2, 0.0, rOuter2, 0.0, rEnd);
107+
disk_transform = Transform3D(Rotation3D(), Position(0.0, 0.0, zPos));
62108
}
63109

64-
ConeSegment s_start_disk(sensitive_thickness / 2, 0.0, rOuter2, 0.0, rEnd);
65-
Volume v_start_disk("v_start_disk_" + motherName, s_start_disk, m_Vacuum);
110+
Volume v_start_disk("v_start_disk_" + motherName, sensitive_solid, m_Vacuum);
66111
v_start_disk.setSensitiveDetector(sens);
67112

68-
auto disk_placement = mother_vol.placeVolume(v_start_disk, Position(0.0, 0.0, zPos));
113+
auto disk_placement = mother_vol.placeVolume(v_start_disk, disk_transform);
69114
disk_placement.addPhysVolID("end", detStart);
70115
disk_placement.addPhysVolID("pipe", pipe_id);
71116
disk_placement.addPhysVolID("system", det_id);
72117

73-
DetElement slice_element(sdet, slice_name, pipe_id);
118+
DetElement slice_element(mother, slice_name, pipe_id);
74119

75120
slice_element.setPlacement(disk_placement);
76-
description.declareParent(slice_name, mother);
77121
}
78122

79-
auto pv_assembly = description.worldVolume().placeVolume(assembly, Position(0.0, 0.0, 0.0));
123+
auto pv_assembly = description.worldVolume().placeVolume(assembly);
80124
pv_assembly.addPhysVolID("system", det_id);
81125
sdet.setPlacement(pv_assembly);
82126

0 commit comments

Comments
 (0)