Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHG4OHCalDetector.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHG4OHCalDetector.cc
1 #include "PHG4OHCalDetector.h"
2 
4 #include "PHG4OHCalFieldSetup.h"
5 
7 
8 #include <phparameter/PHParameters.h>
9 
10 #include <g4main/PHG4Detector.h>
12 #include <g4main/PHG4Subsystem.h>
13 #include <g4main/PHG4Utils.h>
14 
15 #include <phfield/PHFieldConfig.h>
16 #include <phfield/PHFieldUtility.h>
17 
18 #include <phool/PHCompositeNode.h>
19 #include <phool/PHIODataNode.h>
20 #include <phool/PHNode.h> // for PHNode
21 #include <phool/PHNodeIterator.h>
22 #include <phool/PHObject.h> // for PHObject
23 #include <phool/getClass.h>
24 #include <phool/phool.h>
25 #include <phool/recoConsts.h>
26 
27 #include <g4gdml/PHG4GDMLConfig.hh>
29 
30 #include <calobase/RawTowerDefs.h> // for convert_name_...
31 #include <calobase/RawTowerGeom.h> // for RawTowerGeom
32 #include <calobase/RawTowerGeomContainer.h> // for RawTowerGeomC...
33 #include <calobase/RawTowerGeomContainer_Cylinderv1.h>
34 #include <calobase/RawTowerGeomv1.h>
35 
36 #include <TSystem.h>
37 
38 #include <Geant4/G4AssemblyVolume.hh>
39 #include <Geant4/G4LogicalVolume.hh>
40 #include <Geant4/G4Material.hh>
41 #include <Geant4/G4PVPlacement.hh>
42 #include <Geant4/G4RotationMatrix.hh>
43 #include <Geant4/G4String.hh>
44 #include <Geant4/G4SystemOfUnits.hh>
45 #include <Geant4/G4ThreeVector.hh>
46 #include <Geant4/G4Transform3D.hh>
47 #include <Geant4/G4Tubs.hh>
48 #include <Geant4/G4VPhysicalVolume.hh>
49 #include <Geant4/G4VSolid.hh>
50 
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wshadow"
53 #pragma GCC diagnostic ignored "-Wpedantic"
54 #include <Geant4/G4GDMLParser.hh>
55 #include <Geant4/G4GDMLReadStructure.hh> // for G4GDMLReadStructure
56 #pragma GCC diagnostic pop
57 
58 #include <boost/lexical_cast.hpp>
59 #include <boost/tokenizer.hpp>
60 
61 #include <cassert>
62 #include <cmath>
63 #include <cstdlib>
64 #include <iostream>
65 #include <memory> // for unique_ptr
66 #include <utility> // for pair, make_pair
67 #include <vector> // for vector, vector<>::iter...
68 
69 class PHCompositeNode;
70 
72  : PHG4Detector(subsys, Node, dnam)
73  , m_DisplayAction(dynamic_cast<PHG4OHCalDisplayAction *>(subsys->GetDisplayAction()))
74  , m_Params(parames)
75  , m_InnerRadius(m_Params->get_double_param("inner_radius") * cm)
76  , m_OuterRadius(m_Params->get_double_param("outer_radius") * cm)
77  , m_SizeZ(m_Params->get_double_param("size_z") * cm)
78  , m_NumScintiPlates(m_Params->get_int_param(PHG4HcalDefs::scipertwr) * m_Params->get_int_param("n_towers"))
79  , m_ActiveFlag(m_Params->get_int_param("active"))
80  , m_AbsorberActiveFlag(m_Params->get_int_param("absorberactive"))
81  , m_GDMPath(m_Params->get_string_param("GDMPath"))
82 {
85  PHFieldConfig *fieldconf = findNode::getClass<PHFieldConfig>(Node, PHFieldUtility::GetDSTConfigNodeName());
87  {
88  m_FieldSetup =
90  m_Params->get_string_param("IronFieldMapPath"), m_Params->get_double_param("IronFieldMapScale"),
91  m_InnerRadius - 10 * cm, // subtract 10 cm to make sure fieldmap with 2x2 grid covers it
92  m_OuterRadius + 10 * cm, // add 10 cm to make sure fieldmap with 2x2 grid covers it
93  m_SizeZ / 2. + 10 * cm // div by 2 bc G4 convention
94  );
95  }
96 }
97 
99 {
100  delete m_ScintiMotherAssembly;
102  delete m_FieldSetup;
103 }
104 
105 //_______________________________________________________________
106 //_______________________________________________________________
108 {
110  {
111  if (m_SteelAbsorberLogVolSet.find(volume->GetLogicalVolume()) != m_SteelAbsorberLogVolSet.end())
112  {
113  return -1;
114  }
115  }
116  if (m_ActiveFlag)
117  {
118  if (m_ScintiTileLogVolSet.find(volume->GetLogicalVolume()) != m_ScintiTileLogVolSet.end())
119  {
120  return 1;
121  }
122  }
123  return 0;
124 }
125 
126 void PHG4OHCalDetector::ConstructMe(G4LogicalVolume *logicWorld)
127 {
129  G4Material *Air = GetDetectorMaterial(rc->get_StringFlag("WorldMaterial"));
130  G4VSolid *hcal_envelope_cylinder = new G4Tubs("OHCal_envelope_solid", m_InnerRadius, m_OuterRadius, m_SizeZ / 2., 0, 2 * M_PI);
131  m_VolumeEnvelope = hcal_envelope_cylinder->GetCubicVolume();
132  G4LogicalVolume *hcal_envelope_log = new G4LogicalVolume(hcal_envelope_cylinder, Air, G4String("OHCal_envelope"), nullptr, nullptr, nullptr);
133  G4RotationMatrix hcal_rotm;
134  hcal_rotm.rotateX(m_Params->get_double_param("rot_x") * deg);
135  hcal_rotm.rotateY(m_Params->get_double_param("rot_y") * deg);
136  hcal_rotm.rotateZ(m_Params->get_double_param("rot_z") * deg);
137  G4VPhysicalVolume *mothervol = new G4PVPlacement(G4Transform3D(hcal_rotm, G4ThreeVector(m_Params->get_double_param("place_x") * cm, m_Params->get_double_param("place_y") * cm, m_Params->get_double_param("place_z") * cm)), hcal_envelope_log, "OHCal", logicWorld, false, false, OverlapCheck());
138  m_DisplayAction->SetMyTopVolume(mothervol);
139  ConstructOHCal(hcal_envelope_log);
140 
141  // allow installing new G4 subsystem installed inside the HCal envelope via macros, in particular its support rings.
142  PHG4Subsystem *mysys = GetMySubsystem();
143  if (mysys)
144  {
145  mysys->SetLogicalVolume(hcal_envelope_log);
146  }
147  // disable GDML export for HCal geometries for memory saving and compatibility issues
149  gdml_config->exclude_physical_vol(mothervol);
150  gdml_config->exclude_logical_vol(hcal_envelope_log);
151 
152  const G4MaterialTable *mtable = G4Material::GetMaterialTable();
153  int nMaterials = G4Material::GetNumberOfMaterials();
154  for (G4int i = 0; i < nMaterials; ++i)
155  {
156  const G4Material *mat = (*mtable)[i];
157  if (mat->GetName() == "Uniplast_scintillator")
158  {
159  if ((mat->GetIonisation()->GetBirksConstant()) == 0)
160  {
161  mat->GetIonisation()->SetBirksConstant(m_Params->get_double_param("Birk_const"));
162  }
163  }
164  }
165  if(!m_Params->get_int_param("saveg4hit")) AddGeometryNode();
166  return;
167 }
168 
169 int PHG4OHCalDetector::ConstructOHCal(G4LogicalVolume *hcalenvelope)
170 {
171  // import the staves from the gemetry file
172  std::unique_ptr<G4GDMLReadStructure> reader(new G4GDMLReadStructure());
173  G4GDMLParser gdmlParser(reader.get());
174  gdmlParser.SetOverlapCheck(OverlapCheck());
175  gdmlParser.Read(m_GDMPath, false);
176 
177  G4AssemblyVolume *abs_asym = reader->GetAssembly("sector"); // absorber
178  m_ScintiMotherAssembly = reader->GetAssembly("tileAssembly24_90"); // tiles
179 
180  // this loop is inefficient but the assignment of the scintillator id's is much simpler when having the hcal sector
181  std::vector<G4VPhysicalVolume *>::iterator it1 = abs_asym->GetVolumesIterator();
182  for (unsigned int isector = 0; isector < abs_asym->TotalImprintedVolumes(); isector++)
183  {
184  m_DisplayAction->AddSteelVolume((*it1)->GetLogicalVolume());
185  m_SteelAbsorberLogVolSet.insert((*it1)->GetLogicalVolume());
186  hcalenvelope->AddDaughter((*it1));
187  m_VolumeSteel += (*it1)->GetLogicalVolume()->GetSolid()->GetCubicVolume();
188  std::vector<G4VPhysicalVolume *>::iterator it3 = m_ScintiMotherAssembly->GetVolumesIterator();
189  unsigned int ncnt = 24 * 5 * 2;
190  unsigned int ioff = isector * ncnt;
191  // ok we always have to skip to the scintillators we want to add for every hcal sector
192  for (unsigned int j = 0; j < ioff; j++)
193  {
194  ++it3;
195  }
196  for (unsigned int j = ioff; j < ioff + ncnt; j++)
197  {
198  m_DisplayAction->AddScintiVolume((*it3)->GetLogicalVolume());
199  m_ScintiTileLogVolSet.insert((*it3)->GetLogicalVolume());
200  hcalenvelope->AddDaughter((*it3));
201  m_ScintiTilePhysVolMap.insert(std::make_pair(*it3, ExtractLayerTowerId(isector, *it3)));
202  m_VolumeScintillator += (*it3)->GetLogicalVolume()->GetSolid()->GetCubicVolume();
203  ++it3;
204  }
205 
206  ++it1;
207  }
208  // Chimney assemblies
209  G4AssemblyVolume *chimAbs_asym = reader->GetAssembly("sectorChimney"); // absorber
210  m_ChimScintiMotherAssembly = reader->GetAssembly("tileAssembly24chimney_90"); // chimney tiles
211 
212  std::vector<G4VPhysicalVolume *>::iterator it2 = chimAbs_asym->GetVolumesIterator();
213  // order sector 30,31,29
214  std::map<unsigned int, unsigned int> sectormap;
215  sectormap.insert(std::make_pair(0, 30));
216  sectormap.insert(std::make_pair(1, 31));
217  sectormap.insert(std::make_pair(2, 29));
218  for (unsigned int isector = 0; isector < chimAbs_asym->TotalImprintedVolumes(); isector++)
219  {
220  m_DisplayAction->AddChimSteelVolume((*it2)->GetLogicalVolume());
221  m_SteelAbsorberLogVolSet.insert((*it2)->GetLogicalVolume());
222 
223  hcalenvelope->AddDaughter((*it2));
224  m_VolumeSteel += (*it2)->GetLogicalVolume()->GetSolid()->GetCubicVolume();
225  std::vector<G4VPhysicalVolume *>::iterator it4 = m_ChimScintiMotherAssembly->GetVolumesIterator();
226  unsigned int ncnt = 24 * 5 * 2;
227  unsigned int ioff = isector * ncnt;
228  // ok we always have to skip to the scintillators we want to add for every hcal sector
229  for (unsigned int j = 0; j < ioff; j++)
230  {
231  ++it4;
232  }
233  for (unsigned int j = ioff; j < ioff + ncnt; j++)
234  {
235  m_DisplayAction->AddScintiVolume((*it4)->GetLogicalVolume());
236  m_ScintiTileLogVolSet.insert((*it4)->GetLogicalVolume());
237  hcalenvelope->AddDaughter((*it4));
238  m_ScintiTilePhysVolMap.insert(std::make_pair(*it4, ExtractLayerTowerId(sectormap[isector], *it4))); // chimney sectors 29-31
239  m_VolumeScintillator += (*it4)->GetLogicalVolume()->GetSolid()->GetCubicVolume();
240  ++it4;
241  }
242  ++it2;
243  }
244 
245  for (auto &logical_vol : m_SteelAbsorberLogVolSet)
246  {
247  if (m_FieldSetup) // only if we have a field defined for the steel absorber
248  {
249  logical_vol->SetFieldManager(m_FieldSetup->get_Field_Manager_Iron(), true);
250 
251  if (m_Params->get_int_param("field_check"))
252  {
253  std::cout << __PRETTY_FUNCTION__ << " : setup Field_Manager_Iron for LV "
254  << logical_vol->GetName() << " w/ # of daughter " << logical_vol->GetNoDaughters() << std::endl;
255  }
256  }
257  }
258 
259  return 0;
260 }
261 
262 void PHG4OHCalDetector::Print(const std::string &what) const
263 {
264  std::cout << "Outer Hcal Detector:" << std::endl;
265  if (what == "ALL" || what == "VOLUME")
266  {
267  std::cout << "Volume Envelope: " << m_VolumeEnvelope / cm / cm / cm << " cm^3" << std::endl;
268  std::cout << "Volume Steel: " << m_VolumeSteel / cm / cm / cm << " cm^3" << std::endl;
269  std::cout << "Volume Scintillator: " << m_VolumeScintillator / cm / cm / cm << " cm^3" << std::endl;
270  std::cout << "Volume Air: " << (m_VolumeEnvelope - m_VolumeSteel - m_VolumeScintillator) / cm / cm / cm << " cm^3" << std::endl;
271  }
272  std::cout << "******\tm_GDMPath : " << m_GDMPath << std::endl;
273 
274  return;
275 }
276 
277 std::tuple<int, int, int> PHG4OHCalDetector::GetRowColumnId(G4VPhysicalVolume *volume) const
278 {
279  auto it = m_ScintiTilePhysVolMap.find(volume);
280  if (it != m_ScintiTilePhysVolMap.end())
281  {
282  return it->second;
283  }
284  std::cout << "could not locate volume " << volume->GetName()
285  << " in Outer Hcal scintillator map" << std::endl;
286  gSystem->Exit(1);
287  // that's dumb but code checkers do not know that gSystem->Exit()
288  // terminates, so using the standard exit() makes them happy
289  exit(1);
290 }
291 
292 std::tuple<int, int, int> PHG4OHCalDetector::ExtractLayerTowerId(const unsigned int isector, G4VPhysicalVolume *volume)
293 {
294  boost::char_separator<char> sep("_");
295  boost::tokenizer<boost::char_separator<char>> tok(volume->GetName(), sep);
296  boost::tokenizer<boost::char_separator<char>>::const_iterator tokeniter;
297  int layer_id = -1;
298  int tower_id = -1;
299  for (tokeniter = tok.begin(); tokeniter != tok.end(); ++tokeniter)
300  {
301  if (*tokeniter == "impr")
302  {
303  ++tokeniter;
304  if (tokeniter != tok.end())
305  {
306  layer_id = boost::lexical_cast<int>(*tokeniter) / 2;
307  layer_id--;
308  if (layer_id < 0 || layer_id >= m_NumScintiPlates)
309  {
310  std::cout << "invalid scintillator row " << layer_id
311  << ", valid range 0 < row < " << m_NumScintiPlates << std::endl;
312  gSystem->Exit(1);
313  }
314  }
315  else
316  {
317  std::cout << PHWHERE << " Error parsing " << volume->GetName()
318  << " for mother volume number " << std::endl;
319  gSystem->Exit(1);
320  }
321  break;
322  }
323  }
324  for (tokeniter = tok.begin(); tokeniter != tok.end(); ++tokeniter)
325  {
326  if (*tokeniter == "pv")
327  {
328  ++tokeniter;
329  if (tokeniter != tok.end())
330  {
331  tower_id = boost::lexical_cast<int>(*tokeniter);
332  }
333  }
334  }
335  int column = map_towerid(tower_id);
336  int row = map_layerid(isector, layer_id);
337  return std::make_tuple(isector, row, column);
338 }
339 
340 // map gdml tower ids to our columns
341 int PHG4OHCalDetector::map_towerid(const int tower_id)
342 {
343  // odd id's go in one direction, even id's in the other one
344  // this is a shortcut to derive the observed dependency
345  // commented out after this code
346  int itwr = -1;
347  int itmp = tower_id / 2;
348  if (tower_id % 2)
349  {
350  itwr = 12 + itmp;
351  }
352  else
353  {
354  itwr = 11 - itmp;
355  }
356  return itwr;
357  // here is the mapping in long form
358  // if (tower_id == 23)
359  // {
360  // itwr = 0;
361  // }
362  // else if (tower_id == 21)
363  // {
364  // itwr = 1;
365  // }
366  // else if (tower_id ==19 )
367  // {
368  // itwr = 2;
369  // }
370  // else if (tower_id == 17)
371  // {
372  // itwr = 3;
373  // }
374  // else if (tower_id == 15)
375  // {
376  // itwr = 4;
377  // }
378  // else if (tower_id == 13)
379  // {
380  // itwr = 5;
381  // }
382  // else if (tower_id == 11)
383  // {
384  // itwr = 6;
385  // }
386  // else if (tower_id == 9)
387  // {
388  // itwr = 7;
389  // }
390  // else if (tower_id == 7)
391  // {
392  // itwr = 8;
393  // }
394  // else if (tower_id == 5)
395  // {
396  // itwr = 9;
397  // }
398  // else if (tower_id == 3)
399  // {
400  // itwr = 10;
401  // }
402  // else if (tower_id == 1)
403  // {
404  // itwr = 11;
405  // }
406  // else if (tower_id == 0)
407  // {
408  // itwr = 12;
409  // }
410  // else if (tower_id == 2)
411  // {
412  // itwr = 13;
413  // }
414  // else if (tower_id == 4)
415  // {
416  // itwr = 14;
417  // }
418  // else if (tower_id == 6)
419  // {
420  // itwr = 15;
421  // }
422  // else if (tower_id == 8)
423  // {
424  // itwr = 16;
425  // }
426  // else if (tower_id == 10)
427  // {
428  // itwr = 17;
429  // }
430  // else if (tower_id == 12)
431  // {
432  // itwr = 18;
433  // }
434  // else if (tower_id == 14)
435  // {
436  // itwr = 19;
437  // }
438  // else if (tower_id == 16)
439  // {
440  // itwr = 20;
441  // }
442  // else if (tower_id == 18)
443  // {
444  // itwr = 21;
445  // }
446  // else if (tower_id == 20)
447  // {
448  // itwr = 22;
449  // }
450  // else if (tower_id == 22)
451  // {
452  // itwr = 23;
453  // }
454  // else
455  // {
456  // std::cout << PHWHERE << " cannot map tower " << tower_id << std::endl;
457  // gSystem->Exit(1);
458  // exit(1);
459  // }
460 }
461 
462 int PHG4OHCalDetector::map_layerid(const unsigned int isector, const int layer_id)
463 {
464  int rowid = -1;
465  if (layer_id <= 60)
466  {
467  rowid = layer_id + 95;
468  }
469  else if (/* layer_id > 60 && */ layer_id < 225)
470  {
471  rowid = layer_id + 95;
472  }
473  else /* if (layer_id >= 225) */
474  {
475  rowid = layer_id - 225;
476  }
477  if (isector == 29)
478  {
479  rowid = 45 + layer_id;
480  }
481  else if (isector >= 30)
482  {
483  rowid = 75 + layer_id;
484  }
485  //shift the row index up by 5
486  rowid += 5;
487  if(rowid > 319) rowid -= 320;
488  if (rowid < 0 || rowid > 319)
489  {
490  std::cout << "bad rowid " << rowid << " for sector " << isector << ", layer_id " << layer_id << std::endl;
491  gSystem->Exit(1);
492  }
493 
494  return rowid;
495 }
496 
497 // This is dulplicated code, we can get rid of it when we have the code to make towergeom for real data reco.
499 {
500  PHNodeIterator iter(topNode());
501  PHCompositeNode *runNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "RUN"));
502  if (!runNode)
503  {
504  std::cout << PHWHERE << "Run Node missing, exiting." << std::endl;
505  gSystem->Exit(1);
506  exit(1);
507  }
508  PHNodeIterator runIter(runNode);
509  PHCompositeNode *RunDetNode = dynamic_cast<PHCompositeNode *>(runIter.findFirst("PHCompositeNode", m_SuperDetector));
510  if (!RunDetNode)
511  {
512  RunDetNode = new PHCompositeNode(m_SuperDetector);
513  runNode->addNode(RunDetNode);
514  }
515  m_TowerGeomNodeName = "TOWERGEOM_" + m_SuperDetector;
516  m_RawTowerGeom = findNode::getClass<RawTowerGeomContainer>(topNode(), m_TowerGeomNodeName);
517  if (!m_RawTowerGeom)
518  {
521  RunDetNode->addNode(newNode);
522  }
525  m_RawTowerGeom->set_radius(innerrad);
526  m_RawTowerGeom->set_thickness(thickness);
529  double geom_ref_radius = innerrad + thickness / 2.;
530  double phistart = m_Params->get_double_param("phistart");
531  if (!std::isfinite(phistart))
532  {
533  std::cout << PHWHERE << " phistart is not finite: " << phistart
534  << ", exiting now (this will crash anyway)" << std::endl;
535  gSystem->Exit(1);
536  }
537  for (int i = 0; i < m_Params->get_int_param(PHG4HcalDefs::n_towers); i++)
538  {
539  double phiend = phistart + 2. * M_PI / m_Params->get_int_param(PHG4HcalDefs::n_towers);
540  std::pair<double, double> range = std::make_pair(phiend, phistart);
541  phistart = phiend;
542  int tempi = i+1;
544  m_RawTowerGeom->set_phibounds(tempi, range);
545  }
546  double etalowbound = -m_Params->get_double_param("scinti_eta_coverage_neg");
547  for (int i = 0; i < m_Params->get_int_param("etabins"); i++)
548  {
549  // double etahibound = etalowbound + 2.2 / get_int_param("etabins");
550  double etahibound = etalowbound +
551  (m_Params->get_double_param("scinti_eta_coverage_neg") + m_Params->get_double_param("scinti_eta_coverage_pos")) / m_Params->get_int_param("etabins");
552  std::pair<double, double> range = std::make_pair(etalowbound, etahibound);
553  m_RawTowerGeom->set_etabounds(i, range);
554  etalowbound = etahibound;
555  }
556  for (int iphi = 0; iphi < m_RawTowerGeom->get_phibins(); iphi++)
557  {
558  for (int ieta = 0; ieta < m_RawTowerGeom->get_etabins(); ieta++)
559  {
561 
562  const double x(geom_ref_radius * cos(m_RawTowerGeom->get_phicenter(iphi)));
563  const double y(geom_ref_radius * sin(m_RawTowerGeom->get_phicenter(iphi)));
564  const double z(geom_ref_radius / tan(PHG4Utils::get_theta(m_RawTowerGeom->get_etacenter(ieta))));
565 
567  if (tg)
568  {
569  if (Verbosity() > 0)
570  {
571  std::cout << "IHCalDetector::InitRun - Tower geometry " << key << " already exists" << std::endl;
572  }
573 
574  if (fabs(tg->get_center_x() - x) > 1e-4)
575  {
576  std::cout << "IHCalDetector::InitRun - Fatal Error - duplicated Tower geometry " << key << " with existing x = " << tg->get_center_x() << " and expected x = " << x
577  << std::endl;
578 
579  return;
580  }
581  if (fabs(tg->get_center_y() - y) > 1e-4)
582  {
583  std::cout << "IHCalDetector::InitRun - Fatal Error - duplicated Tower geometry " << key << " with existing y = " << tg->get_center_y() << " and expected y = " << y
584  << std::endl;
585  return;
586  }
587  if (fabs(tg->get_center_z() - z) > 1e-4)
588  {
589  std::cout << "IHCalDetector::InitRun - Fatal Error - duplicated Tower geometry " << key << " with existing z= " << tg->get_center_z() << " and expected z = " << z
590  << std::endl;
591  return;
592  }
593  }
594  else
595  {
596  if (Verbosity() > 0)
597  {
598  std::cout << "IHCalDetector::InitRun - building tower geometry " << key << "" << std::endl;
599  }
600 
601  tg = new RawTowerGeomv1(key);
602 
603  tg->set_center_x(x);
604  tg->set_center_y(y);
605  tg->set_center_z(z);
607  }
608  }
609  }
610  if (Verbosity() > 0)
611  {
613  }
614 }