Project
Loading...
Searching...
No Matches
CADGeometryUtils.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
15#include <TGeoVolume.h>
16#include <TGeoNode.h>
17#include <TGeoMaterial.h>
18#include <TGeoMedium.h>
19#include <TInterpreter.h>
20#include <TROOT.h>
21#include <TString.h>
22#include <TGlobal.h>
23#include <fairlogger/Logger.h>
24#include <atomic>
25#include <cctype>
26#include <filesystem>
27#include <fstream>
28#include <functional>
29#include <string>
30#include <unordered_map>
31#include <unordered_set>
32
33namespace o2::base
34{
35
36TGeoVolume* buildCADVolumeFromMacro(const std::string& macroFile, const std::string& instanceTag)
37{
38 if (macroFile.empty()) {
39 return nullptr;
40 }
41 auto expandedHookFileName = o2::utils::expandShellVarsInFileName(macroFile);
42 if (!std::filesystem::exists(expandedHookFileName)) {
43 LOG(error) << "External geometry macro " << expandedHookFileName << " does not exist";
44 return nullptr;
45 }
46
47 // We JIT the macro into a *unique* namespace per call. This is essential when several
48 // external geometries are present at the same time: every macro produced by
49 // O2_CADtoTGeo.py exports identically named symbols (build(), get_builder_hook_unchecked(),
50 // LoadFacets(), ...). Loading them all into the single global Cling scope would collide
51 // (the first definition wins and subsequent macros are silently ignored). By wrapping each
52 // macro body in its own namespace we keep the symbols separate. The preprocessor #include
53 // lines must stay at global scope, so we hoist them out of the namespace.
54 std::ifstream macroStream(expandedHookFileName, std::ios::in);
55 if (!macroStream.is_open()) {
56 LOG(error) << "Cannot open external geometry macro " << expandedHookFileName;
57 return nullptr;
58 }
59 std::string preamble; // #include (and other top-level preprocessor) lines -> global scope
60 std::string body; // everything else -> wrapped into a unique namespace
61 std::string line;
62 while (std::getline(macroStream, line)) {
63 auto firstNonSpace = line.find_first_not_of(" \t");
64 if (firstNonSpace != std::string::npos && line[firstNonSpace] == '#') {
65 preamble += line + "\n";
66 } else {
67 body += line + "\n";
68 }
69 }
70
71 // build a unique, valid C++ identifier for the namespace
72 static std::atomic<int> instanceCounter{0};
73 std::string ns = std::string("o2_cadgeom_") + instanceTag + "_" + std::to_string(instanceCounter++);
74 for (auto& c : ns) {
75 if (!std::isalnum(static_cast<unsigned char>(c)) && c != '_') {
76 c = '_';
77 }
78 }
79
80 const std::string wrapped = preamble + "\nnamespace " + ns + " {\n" + body + "\n}\n";
81 if (!gInterpreter->Declare(wrapped.c_str())) {
82 LOG(error) << "Failed to JIT external geometry macro " << expandedHookFileName;
83 return nullptr;
84 }
85
86 // retrieve the builder hook from the unique namespace
87 const std::string globalName = "__" + ns + "_hook__";
88 gROOT->ProcessLine(Form("std::function<TGeoVolume*()> %s = %s::get_builder_hook_unchecked();",
89 globalName.c_str(), ns.c_str()));
90 auto global = gROOT->GetGlobal(globalName.c_str());
91 if (!global) {
92 LOG(error) << "Could not retrieve geometry builder hook from macro " << expandedHookFileName;
93 return nullptr;
94 }
95 auto hook = *reinterpret_cast<std::function<TGeoVolume*()>*>(global->GetAddress());
96 LOG(info) << "CAD geometry hook initialized from file " << expandedHookFileName << " (namespace " << ns << ")";
97
98 auto top = hook();
99 if (!top) {
100 LOG(error) << "CAD geometry macro " << expandedHookFileName << " did not return a top volume";
101 }
102 return top;
103}
104
105void remapCADMedia(TGeoVolume* top, const char* modulename)
106{
107 std::unordered_map<TGeoMedium*, TGeoMedium*> medium_ptr_mapping;
108 std::unordered_set<TGeoVolume*> volumes_already_treated;
109 int counter = 1;
110
111 // The transformer function
112 auto transform_media = [&](TGeoVolume* vol_) {
113 if (volumes_already_treated.find(vol_) != volumes_already_treated.end()) {
114 // this volume was already transformed
115 return;
116 }
117 volumes_already_treated.insert(vol_);
118
119 if (dynamic_cast<TGeoVolumeAssembly*>(vol_)) {
120 // do nothing for assemblies (they don't have a medium)
121 return;
122 }
123
124 auto medium = vol_->GetMedium();
125 if (!medium) {
126 return;
127 }
128
129 auto iter = medium_ptr_mapping.find(medium);
130 if (iter != medium_ptr_mapping.end()) {
131 // This medium has already been transformed, so
132 // we just update the volume
133 vol_->SetMedium(iter->second);
134 return;
135 } else {
136 LOG(info) << "Transforming media with name " << medium->GetName() << " for volume " << vol_->GetName();
137
138 // we found a medium, not yet treated
139 auto curr_mat = medium->GetMaterial();
141
142 matmgr.Material(modulename, counter, curr_mat->GetName(), curr_mat->GetA(), curr_mat->GetZ(), curr_mat->GetDensity(), curr_mat->GetRadLen(), curr_mat->GetIntLen());
143 // TGeo medium params are stored in a flat array with the following convention
144 // fParams[0] = isvol;
145 // fParams[1] = ifield;
146 // fParams[2] = fieldm;
147 // fParams[3] = tmaxfd;
148 // fParams[4] = stemax;
149 // fParams[5] = deemax;
150 // fParams[6] = epsil;
151 // fParams[7] = stmin;
152 const auto isvol = medium->GetParam(0);
153 const auto isxfld = medium->GetParam(1);
154 const auto sxmgmx = medium->GetParam(2);
155 const auto tmaxfd = medium->GetParam(3);
156 const auto stemax = medium->GetParam(4);
157 const auto deemax = medium->GetParam(5);
158 const auto epsil = medium->GetParam(6);
159 const auto stmin = medium->GetParam(7);
160
161 matmgr.Medium(modulename, counter, medium->GetName(), counter, isvol, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin);
162
163 // there will be new Material and Medium objects; fetch them
164 auto new_med = matmgr.getTGeoMedium(modulename, counter);
165
166 // insert into cache
167 medium_ptr_mapping[medium] = new_med;
168 vol_->SetMedium(new_med);
169 counter++;
170 }
171 }; // end transformer lambda
172
173 // a generic volume walker
174 std::function<void(TGeoVolume*)> visit_volume;
175 visit_volume = [&](TGeoVolume* vol) -> void {
176 if (!vol) {
177 return;
178 }
179
180 // call the transformer
181 transform_media(vol);
182
183 // Recurse into daughters
184 const int nd = vol->GetNdaughters();
185 for (int i = 0; i < nd; ++i) {
186 TGeoNode* node = vol->GetNode(i);
187 if (!node) {
188 continue;
189 }
190 TGeoVolume* child = node->GetVolume();
191 if (!child) {
192 continue;
193 }
194
195 visit_volume(child);
196 }
197 };
198
199 visit_volume(top);
200}
201
202} // namespace o2::base
Helpers to inject CAD-derived (TGeo) geometry into O2 simulation.
std::unique_ptr< expressions::Node > node
int32_t i
uint32_t c
Definition RawData.h:2
static MaterialManager & Instance()
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint counter
Definition glcorearb.h:3987
TGeoVolume * buildCADVolumeFromMacro(const std::string &macroFile, const std::string &instanceTag)
void remapCADMedia(TGeoVolume *top, const char *modulename)
std::string expandShellVarsInFileName(std::string const &input)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"