LDMX Software
TrackingGeometry.cxx
1
2#include "Tracking/geo/TrackingGeometry.h"
3
4#include <Acts/Surfaces/SurfaceArray.hpp>
5#include <Acts/Visualization/ObjVisualization3D.hpp>
6#include <Acts/Visualization/ViewConfig.hpp>
7#include <G4GDMLParser.hh>
8#include <G4LogicalVolume.hh>
9#include <G4Types.hh>
10#include <boost/filesystem/operations.hpp>
11#include <boost/filesystem/path.hpp>
12
13#include "Framework/Exception/Exception.h"
14#include "G4UIsession.hh"
15#include "G4strstreambuf.hh"
16
17namespace tracking::geo {
18
24class SilentG4 : public G4UIsession {
25 public:
26 SilentG4() = default;
27 ~SilentG4() = default;
28 G4UIsession* SessionStart() { return nullptr; }
29 G4int ReceiveG4cout(const G4String&) { return 0; }
30 G4int ReceiveG4cerr(const G4String&) { return 0; }
31};
32
33TrackingGeometry::TrackingGeometry(const std::string& name,
34 const Acts::GeometryContext& gctx,
35 const std::string& gdml)
36 : framework::ConditionsObject(name), gctx_{gctx}, gdml_{gdml} {
37 // Build The rotation matrix to the tracking frame
38 // Rotate the sensors to be orthogonal to X
39 double rotation_angle = M_PI * 0.5;
40
41 // 0 0 -1
42 // 0 1 0
43 // 1 0 0
44
45 // This rotation is needed to have the plane orthogonal to the X direction.
46 // Rotation of the surfaces
47 Acts::Vector3 x_pos1(cos(rotation_angle), 0., sin(rotation_angle));
48 Acts::Vector3 y_pos1(0., 1., 0.);
49 Acts::Vector3 z_pos1(-sin(rotation_angle), 0., cos(rotation_angle));
50
51 y_rot_.col(0) = x_pos1;
52 y_rot_.col(1) = y_pos1;
53 y_rot_.col(2) = z_pos1;
54
55 // Rotate the sensors to put them in the proper orientation in Z
56 Acts::Vector3 x_pos2(1., 0., 0.);
57 Acts::Vector3 y_pos2(0., cos(rotation_angle), sin(rotation_angle));
58 Acts::Vector3 z_pos2(0., -sin(rotation_angle), cos(rotation_angle));
59
60 x_rot_.col(0) = x_pos2;
61 x_rot_.col(1) = y_pos2;
62 x_rot_.col(2) = z_pos2;
63
75 std::unique_ptr<SilentG4> silence;
76 if (G4RunManager::GetRunManager() == nullptr) {
77 // no run manager ==> no simulation
78 silence = std::make_unique<SilentG4>();
79 // these lines compied from G4UImanager::SetCoutDestination
80 // to avoid creating G4UImanager unnecessarily
81 G4coutbuf.SetDestination(silence.get());
82 G4cerrbuf.SetDestination(silence.get());
83 }
84
85 // Get the world volume
86 G4GDMLParser parser;
87
88 // Validation requires internet
89 parser.Read(gdml_, false);
90
91 // Extract field map filename from GDML auxiliary data and resolve full path.
92 // GDML path: .../data/detectors/<det>/detector.gdml
93 // Field map: .../data/fieldmap/<filename>
94 const G4GDMLAuxListType* aux_list = parser.GetAuxList();
95 for (const auto& aux : *aux_list) {
96 if (aux.type == "MagneticField") {
97 for (const auto& sub : *aux.auxList) {
98 if (sub.type == "File") {
99 boost::filesystem::path fmap(std::string(sub.value));
100 if (fmap.is_absolute()) {
101 field_map_file_ = fmap.string();
102 } else {
103 boost::filesystem::path prefix = boost::filesystem::path(gdml_)
104 .parent_path()
105 .parent_path()
106 .parent_path();
107 field_map_file_ = (prefix / "fieldmap" / fmap).string();
108 }
109 break;
110 }
111 }
112 break;
113 }
114 }
115
116 if (field_map_file_.empty()) {
117 ldmx_log(warn) << "TrackingGeometry: no MagneticField/File auxiliary "
118 "entry found in '"
119 << gdml_ << "' — tracking will use a zero B-field";
120 }
121
122 f_world_phys_vol_ = parser.GetWorldVolume();
123
124 if (silence) {
125 // we created the session and silenced G4
126 // undo that now incase others have use for G4
127 // nullptr => standard (ldmx_log(trace) and std::cerr)
128 G4coutbuf.SetDestination(nullptr);
129 G4cerrbuf.SetDestination(nullptr);
130 }
131}
132
133G4VPhysicalVolume* TrackingGeometry::findDaughterByName(G4VPhysicalVolume* pvol,
134 G4String name) {
135 G4LogicalVolume* lvol = pvol->GetLogicalVolume();
136 for (G4int i = 0; i < lvol->GetNoDaughters(); i++) {
137 G4VPhysicalVolume* f_daughter_phys_vol = lvol->GetDaughter(i);
138 std::string d_name = f_daughter_phys_vol->GetName();
139 if (d_name.find(name) != std::string::npos) return f_daughter_phys_vol;
140 // if (fDaughterPhysVol->GetName() == name) return fDaughterPhysVol;
141 }
142
143 return nullptr;
144}
145
146void TrackingGeometry::getAllDaughters(G4VPhysicalVolume* pvol) {
147 G4LogicalVolume* lvol = pvol->GetLogicalVolume();
148
149 ldmx_log(trace) << "Checking daughters of ::" << pvol->GetName();
150
151 for (G4int i = 0; i < lvol->GetNoDaughters(); i++) {
152 G4VPhysicalVolume* f_daughter_phys_vol = lvol->GetDaughter(i);
153
154 ldmx_log(trace) << "name::" << f_daughter_phys_vol->GetName();
155 ldmx_log(trace) << "pos_::" << f_daughter_phys_vol->GetTranslation();
156 ldmx_log(trace)
157 << "n_dau::"
158 << f_daughter_phys_vol->GetLogicalVolume()->GetNoDaughters();
159 ldmx_log(trace) << "replica::" << f_daughter_phys_vol->IsReplicated();
160 ldmx_log(trace) << "copyNR::" << f_daughter_phys_vol->GetCopyNo();
161
162 getAllDaughters(f_daughter_phys_vol);
163 }
164}
165
166// Retrieve the layers from a physical volume
167// void TrackingGeometry::getComponentLayer(G4VPhysicalVolume* pvol,
168// std::string layer_name,
169// std::string component_type,
170// std::vector<std::reference_wrapper<G4PhysicalVolume>>
171// & components) {
172
173// G4LogicalVolume* l_vol = pvol->GetLogicalVolume();
174// for (G4int i=0; i<l_vol->GetNoDaughters(); i++) {
175
176// }
177
178//}
179
180void TrackingGeometry::dumpGeometry(const std::string& outputDir,
181 const Acts::GeometryContext& gctx) const {
182 if (!t_geometry_) return;
183
184 {
185 ldmx_log(trace) << __PRETTY_FUNCTION__;
186
187 for (auto const& surface_id : layer_surface_map_) {
188 ldmx_log(trace) << " " << surface_id.first;
189 ldmx_log(trace) << " Check the surface";
190 // surfaceId.second->toStream(gctx, ldmx_log(trace));
191 surface_id.second->toStream(gctx);
192 ldmx_log(trace) << " GeometryID: " << surface_id.second->geometryId();
193 ldmx_log(trace) << " GeometryID value: "
194 << surface_id.second->geometryId().value();
195 }
196 }
197
198 // Should fail if already exists
199 boost::filesystem::create_directory(outputDir);
200
201 double output_scalor = 1.0;
202 size_t output_precision = 6;
203
204 Acts::ObjVisualization3D obj_vis(output_precision, output_scalor);
205 Acts::ViewConfig container_view{.color = {220, 220, 220}};
206 Acts::ViewConfig volume_view{.color = {220, 220, 0}};
207 Acts::ViewConfig sensitive_view{.color = {0, 180, 240}};
208 Acts::ViewConfig passive_view{.color = {240, 280, 0}};
209 Acts::ViewConfig grid_view{.color = {220, 0, 0}};
210
211 Acts::GeometryView3D::drawTrackingVolume(
212 obj_vis, *(t_geometry_->highestTrackingVolume()), gctx, container_view,
213 volume_view, passive_view, sensitive_view, grid_view, true, "", ".");
214}
215
216// This method gets the transform from the physical volume to the tracking frame
217Acts::Transform3 TrackingGeometry::getTransform(const G4VPhysicalVolume& phex,
218 bool toTrackingFrame) const {
219 Acts::Vector3 pos(phex.GetTranslation().x(), phex.GetTranslation().y(),
220 phex.GetTranslation().z());
221
222 Acts::RotationMatrix3 rotation;
223 convertG4Rot(phex.GetRotation(), rotation);
224
225 // rotate to the tracking frame
226 if (toTrackingFrame) {
227 pos(0) = phex.GetTranslation().z();
228 pos(1) = phex.GetTranslation().x();
229 pos(2) = phex.GetTranslation().y();
230 rotation = x_rot_ * y_rot_ * rotation;
231 }
232
233 Acts::Translation3 translation(pos);
234
235 Acts::Transform3 transform(translation * rotation);
236
237 return transform;
238}
239
240// This method returns the transformation to the tracker coordinates z_->x_
241// x_->y_ y_->z_
242Acts::Transform3 TrackingGeometry::toTracker(
243 const Acts::Transform3& trans) const {
244 Acts::Vector3 pos{trans.translation()(2), trans.translation()(0),
245 trans.translation()(1)};
246
247 Acts::RotationMatrix3 rotation = trans.rotation();
248 rotation = x_rot_ * y_rot_ * rotation;
249
250 Acts::Translation3 translation(pos);
251 Acts::Transform3 transform(translation * rotation);
252
253 return transform;
254}
255
256// Convert rotation
257void TrackingGeometry::convertG4Rot(const G4RotationMatrix* g4rot,
258 Acts::RotationMatrix3& rot) const {
259 // If the rotation is the identity then g4rot will be a null ptr.
260 // So then check it and fill rot accordingly
261
262 rot = Acts::RotationMatrix3::Identity();
263
264 if (g4rot) {
265 rot(0, 0) = g4rot->xx();
266 rot(0, 1) = g4rot->xy();
267 rot(0, 2) = g4rot->xz();
268
269 rot(1, 0) = g4rot->yx();
270 rot(1, 1) = g4rot->yy();
271 rot(1, 2) = g4rot->yz();
272
273 rot(2, 0) = g4rot->zx();
274 rot(2, 1) = g4rot->zy();
275 rot(2, 2) = g4rot->zz();
276 }
277}
278
279// Convert translation
280
281Acts::Vector3 TrackingGeometry::convertG4Pos(const G4ThreeVector& g4pos) const {
282 Acts::Vector3 trans{g4pos.x(), g4pos.y(), g4pos.z()};
283
284 {
285 ldmx_log(trace) << "g4pos::" << g4pos;
286 ldmx_log(trace) << "trans" << trans;
287 }
288
289 return trans;
290}
291
292void TrackingGeometry::getSurfaces(
293 std::vector<const Acts::Surface*>& surfaces) const {
294 if (!t_geometry_)
295 EXCEPTION_RAISE("BadGeometry",
296 "TrackingGeometry::getSurfaces tGeometry is null");
297
298 const Acts::TrackingVolume* t_volume = t_geometry_->highestTrackingVolume();
299 if (t_volume->confinedVolumes()) {
300 for (auto volume : t_volume->confinedVolumes()->arrayObjects()) {
301 if (volume->confinedLayers()) {
302 for (const auto& layer : volume->confinedLayers()->arrayObjects()) {
303 if (layer->layerType() == Acts::navigation) continue;
304 for (auto surface : layer->surfaceArray()->surfaces()) {
305 if (surface) {
306 surfaces.push_back(surface);
307
308 } // surface exists
309 } // surfaces
310 } // layers objects
311 } // confined layers
312 } // volumes objects
313 } // confined volumes
314}
315
316void TrackingGeometry::makeLayerSurfacesMap() {
317 std::vector<const Acts::Surface*> surfaces;
318 getSurfaces(surfaces);
319
320 for (auto& surface : surfaces) {
321 // Layers from 1 to 14 - for the tagger
322 // unsigned int layerId = (surface->geometryId().layer() / 2) ; // Old 1
323 // sensor per layer_
324
325 unsigned int volume_id = surface->geometryId().volume();
326 unsigned int layer_id = (surface->geometryId().layer() /
327 2); // set layer_ ID from 1 to 7 for the tagger
328 // and from 1 to 6 for the recoil
329 unsigned int sensor_id =
330 surface->geometryId().sensitive() -
331 1; // set sensor ID from 0 to 1 for the tagger and from 0 to 9 for the
332 // axial sensors in the back layers of the recoil
333
334 ldmx_log(trace) << "VolumeID " << volume_id << " LayerId " << layer_id
335 << " sensorId " << sensor_id;
336
337 // surface ID = vol * 1000 + ly * 100 + sensor
338 unsigned int surface_id = volume_id * 1000 + layer_id * 100 + sensor_id;
339
340 layer_surface_map_[surface_id] = surface;
341
342 } // surfaces loop
343}
344
345} // namespace tracking::geo
This class throws away all of the messages from Geant4.
TrackingGeometry(const std::string &name, const Acts::GeometryContext &gctx, const std::string &gdml)
All classes in the ldmx-sw project use this namespace.
Visualization.