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