Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHG4SectorConstructor.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHG4SectorConstructor.cc
1 
11 
12 #include <g4main/PHG4Detector.h>
13 #include <g4main/PHG4DisplayAction.h> // for PHG4DisplayAction
14 #include <g4main/PHG4Subsystem.h> // for PHG4Subsystem
15 
16 #include <Geant4/G4Box.hh>
17 #include <Geant4/G4DisplacedSolid.hh> // for G4DisplacedSolid
18 #include <Geant4/G4Exception.hh> // for G4Exception
19 #include <Geant4/G4ExceptionSeverity.hh> // for FatalException, JustWarning
20 #include <Geant4/G4IntersectionSolid.hh>
21 #include <Geant4/G4LogicalVolume.hh>
22 #include <Geant4/G4PVPlacement.hh>
23 #include <Geant4/G4PhysicalConstants.hh> // for pi
24 #include <Geant4/G4Sphere.hh>
25 #include <Geant4/G4String.hh>
26 #include <Geant4/G4SystemOfUnits.hh> // for cm, um, perCent
27 #include <Geant4/G4ThreeVector.hh> // for G4ThreeVector
28 #include <Geant4/G4Transform3D.hh> // for G4Transform3D, G4RotateX3D
29 #include <Geant4/G4Tubs.hh>
30 #include <Geant4/G4Types.hh> // for G4int
31 
32 #include <algorithm> // for max
33 #include <cassert>
34 #include <climits>
35 #include <cmath>
36 #include <iostream>
37 #include <sstream>
38 
39 class G4Material;
40 
42  : overlapcheck_sector(false)
43  , name_base(name)
44  , m_DisplayAction(dynamic_cast<PHG4SectorDisplayAction *>(subsys->GetDisplayAction()))
45  , m_Verbosity(0)
46 {
47 }
48 
50 {
51  // geometry checks
52  if (geom.get_total_thickness() == 0)
53  {
54  G4Exception(
55  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
56  __FILE__, FatalException,
57  " detector configured with zero thickness!");
58  }
59 
60  if (geom.get_min_polar_angle() == geom.get_max_polar_angle())
61  {
62  G4Exception(
63  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
64  __FILE__, FatalException, "min_polar_angle = max_polar_angle!");
65  }
66  if (geom.get_min_polar_angle() > geom.get_max_polar_angle())
67  {
68  G4Exception(
69  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
70  __FILE__, JustWarning,
71  "min and max polar angle got reversed. Correcting them.");
72 
73  const double t = geom.get_max_polar_angle();
74  geom.set_max_polar_angle(geom.get_min_polar_angle());
75  geom.set_min_polar_angle(t);
76  }
77  if ((geom.get_min_polar_edge() == Sector_Geometry::kFlatEdge or geom.get_max_polar_edge() == Sector_Geometry::kFlatEdge) and geom.get_N_Sector() <= 2)
78  {
79  G4Exception(
80  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
81  __FILE__, FatalException,
82  "can NOT use flat edge for single or double sector detector!");
83  }
84 
85  const G4Transform3D transform_Det_to_Hall =
86  G4RotateX3D(-geom.get_normal_polar_angle()) * G4TranslateZ3D(
87  geom.get_normal_start() + geom.get_total_thickness() / 2);
88 
89  const G4Transform3D transform_Hall_to_Det(transform_Det_to_Hall.inverse());
90 
91  // during GDML export, numerical value may change at the large digit and go beyond the 0 or pi limit.
92  // therefore recess 0/pi by a small amount to avoid such problem
93  static const double epsilon = std::numeric_limits<float>::epsilon();
94  const double sph_min_polar_angle =
95  (geom.get_min_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_min_polar_angle() : (0 + epsilon);
96  const double sph_max_polar_angle =
97  (geom.get_max_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_polar_angle() : (pi - epsilon);
98 
99  G4VSolid *SecConeBoundary_Hall = new G4Sphere("SecConeBoundary_Hall", //
100  geom.get_normal_start(), geom.get_max_R(), // G4double pRmin, G4double pRmax,
101  pi / 2 - pi / geom.get_N_Sector(), 2 * pi / geom.get_N_Sector(), // G4double pSPhi, G4double pDPhi,
102  sph_min_polar_angle, sph_max_polar_angle - sph_min_polar_angle //G4double pSTheta, G4double pDTheta
103  );
104 
105  G4VSolid *SecConeBoundary_Det = new G4DisplacedSolid("SecConeBoundary_Det",
106  SecConeBoundary_Hall, transform_Hall_to_Det);
107 
108  G4VSolid *Boundary_Det = SecConeBoundary_Det;
109 
110  if (geom.get_min_polar_edge() != Sector_Geometry::kConeEdge or geom.get_max_polar_edge() != Sector_Geometry::kConeEdge)
111  {
112  // build a flat edge
113 
114  const double sph_min_polar_size =
115  (geom.get_min_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_R() : geom.get_normal_start() * tan(geom.get_normal_polar_angle() - geom.get_min_polar_angle());
116  const double sph_max_polar_size =
117  (geom.get_max_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_R() : geom.get_normal_start() * tan(geom.get_max_polar_angle() - geom.get_normal_polar_angle());
118 
119  G4VSolid *BoxBoundary_Det = new G4Box("BoxBoundary_Det",
120  geom.get_max_R(), (sph_min_polar_size + sph_max_polar_size) / 2,
121  geom.get_total_thickness());
122  G4VSolid *BoxBoundary_Det_Place = new G4DisplacedSolid(
123  "BoxBoundary_Det_Place", BoxBoundary_Det, nullptr,
124  G4ThreeVector(0, (sph_max_polar_size - sph_min_polar_size) / 2, 0));
125 
126  Boundary_Det = new G4IntersectionSolid("Boundary_Det",
127  BoxBoundary_Det_Place, SecConeBoundary_Det);
128  }
129 
130  G4VSolid *DetectorBox_Det = Construct_Sectors_Plane("DetectorBox_Det",
131  -geom.get_total_thickness() / 2, geom.get_total_thickness(),
132  Boundary_Det);
133 
134  G4Material *p_mat = PHG4Detector::GetDetectorMaterial(geom.get_material());
135 
136  G4LogicalVolume *DetectorLog_Det = new G4LogicalVolume(DetectorBox_Det, //
137  p_mat, name_base + "_Log");
138  RegisterLogicalVolume(DetectorLog_Det);
139 
140  for (G4int sec = 0; sec < geom.get_N_Sector(); sec++)
141  {
142  RegisterPhysicalVolume(
143  new G4PVPlacement(
144  G4RotateZ3D(2 * pi / geom.get_N_Sector() * sec) * transform_Det_to_Hall, DetectorLog_Det,
145  name_base + "_Physical", WorldLog, false, sec, overlapcheck_sector));
146  }
147 
148  // construct layers
149  double z_start = -geom.get_total_thickness() / 2;
150 
151  for (const auto &l : geom.layer_list)
152  {
153  if (l.percentage_filled > 100. || l.percentage_filled < 0)
154  {
155  std::ostringstream strstr;
156  strstr << name_base << " have invalid layer ";
157  strstr << l.name << " with percentage_filled =" << l.percentage_filled;
158 
159  G4Exception(
160  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(), __FILE__, FatalException,
161  strstr.str().c_str());
162  }
163 
164  const std::string layer_name = name_base + "_" + l.name;
165 
166  G4VSolid *LayerSol_Det = Construct_Sectors_Plane(layer_name + "_Sol",
167  z_start, l.depth * l.percentage_filled * perCent, Boundary_Det);
168 
169  G4LogicalVolume *LayerLog_Det = new G4LogicalVolume(LayerSol_Det, //
170  PHG4Detector::GetDetectorMaterial(l.material), layer_name + "_Log");
171  RegisterLogicalVolume(LayerLog_Det);
172 
173  RegisterPhysicalVolume(
174  new G4PVPlacement(nullptr, G4ThreeVector(), LayerLog_Det,
175  layer_name + "_Physical", DetectorLog_Det, false, 0, overlapcheck_sector),
176  l.active);
177 
178  z_start += l.depth;
179  }
180 
181  if (std::abs(z_start - geom.get_total_thickness() / 2) > 1 * um)
182  {
183  std::ostringstream strstr;
184  strstr << name_base << " - accumulated thickness = "
185  << (z_start + geom.get_total_thickness() / 2) / um
186  << " um expected thickness = " << geom.get_total_thickness() / um
187  << " um";
188  G4Exception(
189  (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
190  __FILE__, FatalException, strstr.str().c_str());
191  }
192 
193  m_DisplayAction->AddVolume(DetectorLog_Det, "DetectorBox");
194  if (Verbosity() > 1)
195  {
196  std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base
197  << " - total thickness = " << geom.get_total_thickness() / cm << " cm"
198  << std::endl;
199  std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base << " - "
200  << map_log_vol.size() << " logical volume constructed" << std::endl;
201  std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base << " - "
202  << map_phy_vol.size() << " physical volume constructed; "
203  << map_active_phy_vol.size() << " is active." << std::endl;
204  }
205 }
206 
207 G4VSolid *
209  const std::string &name, //
210  const double start_z, //
211  const double thickness, //
212  G4VSolid *SecConeBoundary_Det //
213 )
214 {
215  assert(SecConeBoundary_Det);
216 
217  G4VSolid *Sol_Raw = new G4Tubs(name + "_Raw", //const G4String& pName,
218  0, // G4double pRMin,
219  geom.get_max_R(), // G4double pRMax,
220  thickness / 2, // G4double pDz,
221  0, // G4double pSPhi,
222  2 * pi // G4double pDPhi
223  );
224 
225  G4VSolid *Sol_Place = new G4DisplacedSolid(name + "_Place", Sol_Raw, nullptr,
226  G4ThreeVector(0, 0, start_z + thickness / 2));
227 
228  G4VSolid *Sol = new G4IntersectionSolid(name, Sol_Place,
229  SecConeBoundary_Det);
230 
231  return Sol;
232  // return Sol_Place;
233 }
234 
236 {
237  N_Sector = 8;
238  material = "G4_AIR";
239 
241  normal_polar_angle = 0;
242 
244  min_polar_angle = .1;
245 
247  max_polar_angle = .3;
248 
250  normal_start = 305 * cm;
251 
252  min_polar_edge = kConeEdge;
253 
254  max_polar_edge = kConeEdge;
255 }
256 
257 G4LogicalVolume *
259 {
260  if (!v)
261  {
262  std::cout
263  << "PHG4SectorConstructor::RegisterVolume - Error - invalid volume!"
264  << std::endl;
265  return v;
266  }
267  if (map_log_vol.find(v->GetName()) != map_log_vol.end())
268  {
269  std::cout << "PHG4SectorConstructor::RegisterVolume - Warning - replacing "
270  << v->GetName() << std::endl;
271  }
272 
273  map_log_vol[v->GetName()] = v;
274 
275  return v;
276 }
277 
278 G4PVPlacement *
280  const bool active)
281 {
282  if (!v)
283  {
284  std::cout
285  << "PHG4SectorConstructor::RegisterPhysicalVolume - Error - invalid volume!"
286  << std::endl;
287  return v;
288  }
289 
290  phy_vol_idx_t id(v->GetName(), v->GetCopyNo());
291 
292  if (map_phy_vol.find(id) != map_phy_vol.end())
293  {
294  std::cout
295  << "PHG4SectorConstructor::RegisterPhysicalVolume - Warning - replacing "
296  << v->GetName() << "[" << v->GetCopyNo() << "]" << std::endl;
297  }
298 
299  map_phy_vol[id] = v;
300 
301  if (active)
302  map_active_phy_vol[id] = v;
303 
304  return v;
305 }
306 
307 double
309 {
310  double sum = 0;
311  for (const auto &it : layer_list)
312  {
313  sum += it.depth;
314  }
315  return sum;
316 }
317 
318 double
320 {
321  // Geometry check
322  assert(std::abs(min_polar_angle - normal_polar_angle) < pi / 2);
323  assert(std::abs(max_polar_angle - normal_polar_angle) < pi / 2);
324 
325  if (N_Sector <= 2)
326  {
327  // Geometry check
328  if (cos(min_polar_angle + normal_polar_angle) <= 0)
329  {
330  std::stringstream strstr;
331  strstr << "Geometry check failed. " << std::endl;
332  strstr << "normal_polar_angle = " << normal_polar_angle << std::endl;
333  strstr << "min_polar_angle = " << min_polar_angle << std::endl;
334  strstr << "max_polar_angle = " << max_polar_angle << std::endl;
335  strstr << "cos(min_polar_angle + normal_polar_angle) = "
336  << cos(min_polar_angle + normal_polar_angle) << std::endl;
337 
338  G4Exception("Sector_Geometry::get_max_R", __FILE__, FatalException,
339  strstr.str().c_str());
340  }
341  if (cos(max_polar_angle + normal_polar_angle) <= 0)
342  {
343  std::stringstream strstr;
344  strstr << "Geometry check failed. " << std::endl;
345  strstr << "normal_polar_angle = " << normal_polar_angle << std::endl;
346  strstr << "min_polar_angle = " << min_polar_angle << std::endl;
347  strstr << "max_polar_angle = " << max_polar_angle << std::endl;
348  strstr << "cos(max_polar_angle + normal_polar_angle) = "
349  << cos(max_polar_angle + normal_polar_angle) << std::endl;
350 
351  G4Exception("Sector_Geometry::get_max_R", __FILE__, FatalException,
352  strstr.str().c_str());
353  }
354 
355  const double max_tan_angle = std::max(
356  std::abs(tan(min_polar_angle + normal_polar_angle)),
357  std::abs(tan(max_polar_angle + normal_polar_angle)));
358 
359  return (get_normal_start() + get_total_thickness()) * sqrt(1 + max_tan_angle * max_tan_angle);
360  }
361  else
362  {
363  const double max_angle = std::max(
364  std::abs(min_polar_angle - normal_polar_angle),
365  std::abs(max_polar_angle - normal_polar_angle));
366 
367  return (get_normal_start() + get_total_thickness()) * sqrt(1 + pow(tan(max_angle), 2) + pow(tan(2 * pi / N_Sector), 2)) * 2;
368  }
369 }
370 
374 void PHG4Sector::Sector_Geometry::AddLayers_DriftVol_COMPASS(const double drift_vol_thickness)
375 {
376  // (drift chamber) is enclosed by two Mylarr [52] cathode foils of 25um thickness,
377  // coated with about 10um of graphite,
378 
379  AddLayer("EntranceWindow", "G4_MYLAR", 25 * um, false, 100);
380  AddLayer("Cathode", "G4_GRAPHITE", 10 * um, false, 100);
381  AddLayer("DrftVol", material, drift_vol_thickness, true, 100);
382 }
383 
389 {
390  // Internal HBD structure
391  // From doi:10.1016/j.nima.2011.04.015
392  // Component Material X0 (cm) Thickness (cm) Area (%) Rad. Length (%)
393  // Mesh SS 1.67 0.003 11.5 0.021 <- not used for GEMs trackers
394  // AddLayer("Mesh", "Steel",
395  // 0.003 * cm, false, 11.5);
396 
397  // // GEM frames FR4 17.1 0.15x4 6.5 0.228 <- not used for GEMs trackers
398  // AddLayer("Frame0", "G10",
399  // 0.15 * cm, false, 6.5);
400 
401  for (int gem = 1; gem <= n_GEM_layers; gem++)
402  {
403  std::stringstream sid;
404  sid << gem;
405 
406  // GEM Copper 1.43 0.0005x6 64 0.134
407  AddLayer(G4String("GEMFrontCu") + G4String(sid.str()), "G4_Cu",
408  0.0005 * cm, false, 64);
409 
410  // GEM Kapton 28.6 0.005x3 64 0.034
411  AddLayer(G4String("GEMKapton") + G4String(sid.str()), "G4_KAPTON",
412  0.005 * cm, false, 64);
413 
414  // GEM Copper 1.43 0.0005x6 64 0.134
415  AddLayer(G4String("GEMBackCu") + G4String(sid.str()), "G4_Cu",
416  0.0005 * cm, false, 64);
417 
418  // GEM frames FR4 17.1 0.15x4 6.5 0.228
419  AddLayer(G4String("Frame") + G4String(sid.str()), "G10", 0.15 * cm, false,
420  6.5);
421  }
422 
423  // PCB Kapton 28.6 0.005 100 0.017
424  AddLayer(G4String("PCBKapton"), "G4_KAPTON", 0.005 * cm, false, 100);
425 
426  // PCB Copper 1.43 0.0005 80 0.028
427  AddLayer(G4String("PCBCu"), "G4_Cu", 0.0005 * cm, false, 80);
428 
429  // Facesheet FR4 17.1 0.025x2 100 0.292
430  AddLayer("Facesheet", "G10", 0.025 * 2 * cm, false, 100);
431 
432  // Panel core Honeycomb 8170 1.905 100 0.023 <- very thin-X0 stuff, ignore
433 
434  // Total vessel 0.82
435  // Readout
436 }
437 
443 {
444  // Readout
445  // Readout board FR4/copper 17.1/1.43 0.05/0.001 100 0.367
446  AddLayer(G4String("ReadoutFR4"), "G10", 0.05 * cm, false, 100);
447  AddLayer(G4String("ReadoutCu"), "G4_Cu", 0.001 * cm, false, 100);
448 
449  // Preamps + sockets Copper 1.43 0.0005 100 0.66
450  // Total readout 1.03
451  AddLayer(G4String("SocketsCu"), "G4_Cu", 0.0005 * cm, false, 100);
452 }
453 
459  const double expansion_length, std::string radiator)
460 {
461  if (radiator == "Default")
462  {
463  radiator = "ePHENIX_AeroGel";
464  }
465 
466  // Readout board FR4/copper 17.1/1.43 0.05/0.001 100 0.367
467  AddLayer("EntranceWindow", "G10", 0.05 * cm, false, 100);
468  AddLayer("AeroGel", radiator, radiator_length, false, 100);
469  AddLayer("ExpansionVol", "G4_AIR", expansion_length, false, 100);
470 
471  //Some readout
472  AddLayer(G4String("ReadoutFR4"), "G10", 0.05 * cm, false, 100);
473  AddLayer(G4String("ReadoutCu"), "G4_Cu", 0.001 * cm, false, 100);
474  AddLayer(G4String("SocketsCu"), "G4_Cu", 0.0005 * cm, false, 100);
475 }