ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHG4OuterHcalDetector.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHG4OuterHcalDetector.cc
2 
3 #include "PHG4HcalDefs.h"
6 
7 #include <phparameter/PHParameters.h>
8 
9 #include <g4main/PHG4Detector.h>
11 #include <g4main/PHG4Subsystem.h>
12 #include <g4main/PHG4Utils.h>
13 
14 #include <phool/phool.h>
15 #include <phool/recoConsts.h>
16 
17 #include <TSystem.h>
18 
19 #include <Geant4/G4AssemblyVolume.hh>
20 #include <Geant4/G4Box.hh>
21 #include <Geant4/G4ExtrudedSolid.hh>
22 #include <Geant4/G4IntersectionSolid.hh>
23 #include <Geant4/G4LogicalVolume.hh>
24 #include <Geant4/G4Material.hh>
25 #include <Geant4/G4PVPlacement.hh>
26 #include <Geant4/G4RotationMatrix.hh>
27 #include <Geant4/G4String.hh>
28 #include <Geant4/G4SubtractionSolid.hh>
29 #include <Geant4/G4SystemOfUnits.hh>
30 #include <Geant4/G4ThreeVector.hh>
31 #include <Geant4/G4Transform3D.hh>
32 #include <Geant4/G4Tubs.hh>
33 #include <Geant4/G4TwoVector.hh>
34 #include <Geant4/G4UserLimits.hh>
35 #include <Geant4/G4VPhysicalVolume.hh>
36 #include <Geant4/G4VSolid.hh>
37 
38 #include <CGAL/Boolean_set_operations_2.h>
39 #include <CGAL/Circular_kernel_intersections.h>
40 #include <CGAL/Exact_circular_kernel_2.h>
41 #include <CGAL/Object.h>
42 #include <CGAL/point_generators_2.h>
43 
44 #include <boost/lexical_cast.hpp>
45 #include <boost/tokenizer.hpp>
46 
47 #include <algorithm>
48 #include <cmath>
49 #include <cstdlib>
50 #include <iostream>
51 #include <iterator>
52 #include <sstream>
53 
55 
56 typedef CGAL::Circle_2<PHG4OuterHcalDetector::Circular_k> Circle_2;
57 typedef CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k> Circular_arc_point_2;
58 typedef CGAL::Line_2<PHG4OuterHcalDetector::Circular_k> Line_2;
59 typedef CGAL::Segment_2<PHG4OuterHcalDetector::Circular_k> Segment_2;
60 
61 using namespace std;
62 
63 // just for debugging if you want a single layer of scintillators at the center of the world
64 //#define SCINTITEST
65 
66 // face touches the boundary instead of the corner, subtracting 1 permille from the total
67 // scintilator length takes care of this
68 static double subtract_from_scinti_x = 0.1 * mm;
69 
71  : PHG4Detector(subsys, Node, dnam)
72  , m_DisplayAction(dynamic_cast<PHG4OuterHcalDisplayAction *>(subsys->GetDisplayAction()))
73  , m_FieldSetup(nullptr)
74  , m_Params(parames)
75  , m_ScintiMotherAssembly(nullptr)
76  , m_SteelCutoutForMagnetG4Solid(nullptr)
77  , m_InnerRadius(m_Params->get_double_param("inner_radius") * cm)
78  , m_OuterRadius(m_Params->get_double_param("outer_radius") * cm)
79  , m_SizeZ(m_Params->get_double_param("size_z") * cm)
80  , m_ScintiTileX(NAN)
81  , m_ScintiTileXLower(NAN)
82  , m_ScintiTileXUpper(NAN)
83  , m_ScintiTileZ(m_SizeZ)
84  , m_ScintiTileThickness(m_Params->get_double_param("scinti_tile_thickness") * cm)
85  , m_ScintiGap(m_Params->get_double_param("scinti_gap") * cm)
86  , m_ScintiInnerRadius(m_Params->get_double_param("scinti_inner_radius") * cm)
87  , m_ScintiOuterRadius(m_Params->get_double_param("scinti_outer_radius") * cm)
88  , m_TiltAngle(m_Params->get_double_param("tilt_angle") * deg)
89  , m_EnvelopeInnerRadius(m_InnerRadius)
90  , m_EnvelopeOuterRadius(m_OuterRadius)
91  , m_EnvelopeZ(m_SizeZ)
92  , m_VolumeEnvelope(NAN)
93  , m_VolumeSteel(NAN)
94  , m_VolumeScintillator(NAN)
95  , m_NumScintiPlates(m_Params->get_int_param(PHG4HcalDefs::scipertwr) * m_Params->get_int_param("n_towers"))
96  , m_NumScintiTiles(m_Params->get_int_param("n_scinti_tiles"))
97  , m_ActiveFlag(m_Params->get_int_param("active"))
98  , m_AbsorberActiveFlag(m_Params->get_int_param("absorberactive"))
99  , m_Layer(0)
100  , m_ScintiLogicNamePrefix("HcalOuterScinti")
101 {
102  m_ScintiTilesVec.assign(2 * m_NumScintiTiles, static_cast<G4VSolid *>(nullptr));
103 }
104 
106 {
107  delete m_ScintiMotherAssembly;
108  delete m_FieldSetup;
109 }
110 
111 //_______________________________________________________________
112 //_______________________________________________________________
114 {
116  {
117  if (m_SteelAbsorberVec.find(volume) != m_SteelAbsorberVec.end())
118  {
119  return -1;
120  }
121  }
122  if (m_ActiveFlag)
123  {
124  if (m_ScintiTilePhysVolMap.find(volume) != m_ScintiTilePhysVolMap.end())
125  {
126  return 1;
127  }
128  }
129  return 0;
130 }
131 
132 G4VSolid *
134 {
135  double mid_radius = m_InnerRadius + (m_OuterRadius - m_InnerRadius) / 2.;
136  PHG4OuterHcalDetector::Point_2 p_in_1(mid_radius, 0); // center of scintillator
137 
138  // length of upper edge (middle till outer circle intersect
139  // x/y coordinate of end of center vertical
140  double xcoord = m_ScintiTileThickness / 2. * sin(fabs(m_TiltAngle) / rad) + mid_radius;
141  double ycoord = m_ScintiTileThickness / 2. * cos(fabs(m_TiltAngle) / rad) + 0;
142  PHG4OuterHcalDetector::Point_2 p_upperedge(xcoord, ycoord);
143  Line_2 s2(p_in_1, p_upperedge); // center vertical
144 
145  Line_2 perp = s2.perpendicular(p_upperedge);
147  Circle_2 outer_circle(sc1, sc2, sc3);
148  vector<CGAL::Object> res;
149  CGAL::intersection(outer_circle, perp, std::back_inserter(res));
151  vector<CGAL::Object>::const_iterator iter;
152  for (iter = res.begin(); iter != res.end(); ++iter)
153  {
154  CGAL::Object obj = *iter;
155  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
156  {
157  if (CGAL::to_double(point->first.x()) > CGAL::to_double(p_upperedge.x()))
158  {
159  double deltax = CGAL::to_double(point->first.x()) - CGAL::to_double(p_upperedge.x());
160  double deltay = CGAL::to_double(point->first.y()) - CGAL::to_double(p_upperedge.y());
161  // the scintillator is twice as long
162  m_ScintiTileXUpper = sqrt(deltax * deltax + deltay * deltay); //
163  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
164  upperright = pntmp;
165  }
166  }
167  else
168  {
169  cout << "CGAL::Object type not pair..." << endl;
170  }
171  }
172  // length of lower edge (middle till inner circle intersect
173  xcoord = mid_radius - m_ScintiTileThickness / 2. * sin(fabs(m_TiltAngle) / rad);
174  ycoord = 0 - m_ScintiTileThickness / 2. * cos(fabs(m_TiltAngle) / rad);
175  PHG4OuterHcalDetector::Point_2 p_loweredge(xcoord, ycoord);
176  Line_2 s3(p_in_1, p_loweredge);
177  Line_2 l_lower = s3.perpendicular(p_loweredge);
179  Circle_2 inner_circle(ic1, ic2, ic3);
180  res.clear();
181  CGAL::intersection(inner_circle, l_lower, std::back_inserter(res));
183  // we have 2 intersections - we want the one furthest to the right (largest x). The correct one is
184  // certainly > 0 but the second one depends on the tilt angle and might also be > 0
185  double minx = 0;
186  for (iter = res.begin(); iter != res.end(); ++iter)
187  {
188  CGAL::Object obj = *iter;
189  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
190  {
191  if (CGAL::to_double(point->first.x()) > minx)
192  {
193  minx = CGAL::to_double(point->first.x());
194  double deltax = CGAL::to_double(point->first.x()) - CGAL::to_double(p_loweredge.x());
195  double deltay = CGAL::to_double(point->first.y()) - CGAL::to_double(p_loweredge.y());
196  m_ScintiTileXLower = sqrt(deltax * deltax + deltay * deltay);
197  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
198  lowerleft = pntmp;
199  }
200  }
201  }
204  G4VSolid *scintibox = new G4Box("ScintiTile", m_ScintiTileX / 2., m_ScintiTileThickness / 2., m_ScintiTileZ / 2.);
206  return scintibox;
207 }
208 
209 G4VSolid *
211 {
212  // calculate steel plate on top of the scinti box. Lower edge is the upper edge of
213  // the scintibox + 1/2 the airgap
214  double mid_radius = m_InnerRadius + (m_OuterRadius - m_InnerRadius) / 2.;
215  // first the lower edge, just like the scinti box, just add the air gap
216  // and calculate intersection of edge with inner and outer radius.
217  PHG4OuterHcalDetector::Point_2 p_in_1(mid_radius, 0); // center of lower scintillator
218  double angle_mid_scinti = M_PI / 2. + m_TiltAngle / rad;
219  double xcoord = m_ScintiGap / 2. * cos(angle_mid_scinti / rad) + mid_radius;
220  double ycoord = m_ScintiGap / 2. * sin(angle_mid_scinti / rad) + 0;
221  PHG4OuterHcalDetector::Point_2 p_loweredge(xcoord, ycoord);
222  Line_2 s2(p_in_1, p_loweredge); // center vertical
223  Line_2 perp = s2.perpendicular(p_loweredge); // that is the lower edge of the steel plate
225  Circle_2 inner_circle(sc1, sc2, sc3);
226  vector<CGAL::Object> res;
227  CGAL::intersection(inner_circle, perp, std::back_inserter(res));
229  vector<CGAL::Object>::const_iterator iter;
230  for (iter = res.begin(); iter != res.end(); ++iter)
231  {
232  CGAL::Object obj = *iter;
233  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
234  {
235  if (CGAL::to_double(point->first.x()) > 0)
236  {
237  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
238  lowerleft = pntmp;
239  }
240  }
241  else
242  {
243  cout << "CGAL::Object type not pair..." << endl;
244  }
245  }
247  Circle_2 outer_circle(so1, so2, so3);
248  res.clear(); // just clear the content from the last intersection search
249  CGAL::intersection(outer_circle, perp, std::back_inserter(res));
251  for (iter = res.begin(); iter != res.end(); ++iter)
252  {
253  CGAL::Object obj = *iter;
254  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
255  {
256  if (CGAL::to_double(point->first.x()) > CGAL::to_double(p_loweredge.x()))
257  {
258  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
259  lowerright = pntmp;
260  }
261  }
262  else
263  {
264  cout << "CGAL::Object type not pair..." << endl;
265  }
266  }
267  // now we have the lower left and rigth corner, now find the upper edge
268  // find the center of the upper scintilator
269 
270  double phi_midpoint = 2 * M_PI / m_NumScintiPlates;
271  double xmidpoint = cos(phi_midpoint) * mid_radius;
272  double ymidpoint = sin(phi_midpoint) * mid_radius;
273  // angle of perp line at center of scintillator
274  angle_mid_scinti = (M_PI / 2. - phi_midpoint) - (M_PI / 2. + m_TiltAngle / rad);
275  double xcoordup = xmidpoint - m_ScintiGap / 2. * sin(angle_mid_scinti / rad);
276  double ycoordup = ymidpoint - m_ScintiGap / 2. * cos(angle_mid_scinti / rad);
279  PHG4OuterHcalDetector::Point_2 mid_upperscint(xmidpoint, ymidpoint);
280  PHG4OuterHcalDetector::Point_2 p_upperedge(xcoordup, ycoordup);
281  {
282  Line_2 sup(mid_upperscint, p_upperedge); // center vertical
283  Line_2 perp = sup.perpendicular(p_upperedge); // that is the upper edge of the steel plate
285  Circle_2 inner_circle(sc1, sc2, sc3);
286  vector<CGAL::Object> res;
287  CGAL::intersection(inner_circle, perp, std::back_inserter(res));
288  vector<CGAL::Object>::const_iterator iter;
289  double pxmax = 0.;
290  for (iter = res.begin(); iter != res.end(); ++iter)
291  {
292  CGAL::Object obj = *iter;
293  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
294  {
295  if (CGAL::to_double(point->first.x()) > pxmax)
296  {
297  pxmax = CGAL::to_double(point->first.x());
298  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
299  upperleft = pntmp;
300  }
301  }
302  else
303  {
304  cout << "CGAL::Object type not pair..." << endl;
305  }
306  }
308  Circle_2 outer_circle(so1, so2, so3);
309  res.clear(); // just clear the content from the last intersection search
310  CGAL::intersection(outer_circle, perp, std::back_inserter(res));
311  for (iter = res.begin(); iter != res.end(); ++iter)
312  {
313  CGAL::Object obj = *iter;
314  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
315  {
316  if (CGAL::to_double(point->first.x()) > CGAL::to_double(p_loweredge.x()))
317  {
318  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
319  upperright = pntmp;
320  }
321  }
322  else
323  {
324  cout << "CGAL::Object type not pair..." << endl;
325  }
326  }
327  }
328  // the left corners are on a secant with the inner boundary, they need to be shifted
329  // to be a tangent at the center
330  ShiftSecantToTangent(lowerleft, upperleft, upperright, lowerright);
331  G4TwoVector v1(CGAL::to_double(upperleft.x()), CGAL::to_double(upperleft.y()));
332  G4TwoVector v2(CGAL::to_double(upperright.x()), CGAL::to_double(upperright.y()));
333  G4TwoVector v3(CGAL::to_double(lowerright.x()), CGAL::to_double(lowerright.y()));
334  G4TwoVector v4(CGAL::to_double(lowerleft.x()), CGAL::to_double(lowerleft.y()));
335  std::vector<G4TwoVector> vertexes;
336  vertexes.push_back(v1);
337  vertexes.push_back(v2);
338  vertexes.push_back(v3);
339  vertexes.push_back(v4);
340  G4TwoVector zero(0, 0);
341  G4VSolid *steel_plate_uncut = new G4ExtrudedSolid("SteelPlateUnCut",
342  vertexes,
343  m_SizeZ / 2.0,
344  zero, 1.0,
345  zero, 1.0);
346 
347  m_VolumeSteel = steel_plate_uncut->GetCubicVolume() * m_NumScintiPlates;
348  // now cut out space for magnet at the ends
350  {
351  return steel_plate_uncut;
352  }
353  G4RotationMatrix *rotm = new G4RotationMatrix();
354  rotm->rotateX(-90 * deg);
355  G4VSolid *steel_firstcut_solid = new G4SubtractionSolid("SteelPlateFirstCut", steel_plate_uncut, m_SteelCutoutForMagnetG4Solid, rotm, G4ThreeVector(0, 0, 0));
356  delete rotm;
357  // DisplayVolume(steel_plate_uncut, hcalenvelope);
358  // DisplayVolume(m_SteelCutoutForMagnetG4Solid, hcalenvelope);
359  // DisplayVolume(m_SteelCutoutForMagnetG4Solid, hcalenvelope,rotm);
360  // DisplayVolume(steel_firstcut_solid, hcalenvelope);
361  rotm = new G4RotationMatrix();
362  rotm->rotateX(90 * deg);
363  G4VSolid *steel_cut_solid = new G4SubtractionSolid("SteelPlateCut", steel_firstcut_solid, m_SteelCutoutForMagnetG4Solid, rotm, G4ThreeVector(0, 0, 0));
364  // DisplayVolume(steel_cut_solid, hcalenvelope);
365  delete rotm;
366  return steel_cut_solid;
367 }
368 
370 {
371  Line_2 secant(lowleft, upleft);
372  Segment_2 upedge(upleft, upright);
373  Segment_2 lowedge(lowleft, lowright);
374  double xmid = (CGAL::to_double(lowleft.x()) + CGAL::to_double(upleft.x())) / 2.;
375  double ymid = (CGAL::to_double(lowleft.y()) + CGAL::to_double(upleft.y())) / 2.;
376  PHG4OuterHcalDetector::Point_2 midpoint(xmid, ymid);
377  Line_2 sekperp = secant.perpendicular(midpoint);
379  Circle_2 inner_circle(sc1, sc2, sc3);
380  vector<CGAL::Object> res;
381  CGAL::intersection(inner_circle, sekperp, std::back_inserter(res));
382  vector<CGAL::Object>::const_iterator iter;
383  double pxmax = 0.;
385  for (iter = res.begin(); iter != res.end(); ++iter)
386  {
387  CGAL::Object obj = *iter;
388  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
389  {
390  if (CGAL::to_double(point->first.x()) > pxmax)
391  {
392  pxmax = CGAL::to_double(point->first.x());
393  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
394  tangtouch = pntmp;
395  }
396  }
397  else
398  {
399  cout << "CGAL::Object type not pair..." << endl;
400  }
401  }
402  Line_2 leftside = sekperp.perpendicular(tangtouch);
403  CGAL::Object result = CGAL::intersection(upedge, leftside);
404  if (const PHG4OuterHcalDetector::Point_2 *ipoint = CGAL::object_cast<PHG4OuterHcalDetector::Point_2>(&result))
405  {
406  upleft = *ipoint;
407  }
408  result = CGAL::intersection(lowedge, leftside);
409  if (const PHG4OuterHcalDetector::Point_2 *ipoint = CGAL::object_cast<PHG4OuterHcalDetector::Point_2>(&result))
410  {
411  lowleft = *ipoint;
412  }
413  return;
414 }
415 
417 {
418 #ifdef SCINTITEST
419  ConstructOuterHcal(logicWorld);
420  return;
421 #endif
423  G4Material *Air = GetDetectorMaterial(rc->get_StringFlag("WorldMaterial"));
424  G4VSolid *hcal_envelope_cylinder = new G4Tubs("OuterHcal_envelope_solid", m_EnvelopeInnerRadius, m_EnvelopeOuterRadius, m_EnvelopeZ / 2., 0, 2 * M_PI);
425  m_VolumeEnvelope = hcal_envelope_cylinder->GetCubicVolume();
426  G4LogicalVolume *hcal_envelope_log = new G4LogicalVolume(hcal_envelope_cylinder, Air, G4String("OuterHcal_envelope"), 0, 0, 0);
427  G4RotationMatrix hcal_rotm;
428  hcal_rotm.rotateX(m_Params->get_double_param("rot_x") * deg);
429  hcal_rotm.rotateY(m_Params->get_double_param("rot_y") * deg);
430  hcal_rotm.rotateZ(m_Params->get_double_param("rot_z") * deg);
431  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, "OuterHcal", logicWorld, 0, false, OverlapCheck());
432  m_DisplayAction->SetMyTopVolume(mothervol);
433  ConstructOuterHcal(hcal_envelope_log);
434  vector<G4VPhysicalVolume *>::iterator it = m_ScintiMotherAssembly->GetVolumesIterator();
435  for (unsigned int i = 0; i < m_ScintiMotherAssembly->TotalImprintedVolumes(); i++)
436  {
437  // G4AssemblyVolumes naming convention:
438  // av_WWW_impr_XXX_YYY_ZZZ
439  // where:
440 
441  // WWW - assembly volume instance number
442  // XXX - assembly volume imprint number
443  // YYY - the name of the placed logical volume
444  // ZZZ - the logical volume index inside the assembly volume
445  // e.g. av_1_impr_82_HcalInnerScinti_11_pv_11
446  // 82 the number of the scintillator mother volume
447  // HcalInnerScinti_11: name of scintillator slat
448  // 11: number of scintillator slat logical volume
449  // use boost tokenizer to separate the _, then take value
450  // after "impr" for mother volume and after "pv" for scintillator slat
451  // use boost lexical cast for string -> int conversion
452  // the CopyNo is the mother volume + scinti id
453  // so we can use the CopyNo rather than decoding the string further
454  // looking for "pv"
455  boost::char_separator<char> sep("_");
456  boost::tokenizer<boost::char_separator<char>> tok((*it)->GetName(), sep);
457  boost::tokenizer<boost::char_separator<char>>::const_iterator tokeniter;
458  for (tokeniter = tok.begin(); tokeniter != tok.end(); ++tokeniter)
459  {
460  if (*tokeniter == "impr")
461  {
462  ++tokeniter;
463  if (tokeniter != tok.end())
464  {
465  int layer_id = boost::lexical_cast<int>(*tokeniter);
466  // check detector description, for assemblyvolumes it is not possible
467  // to give the first volume id=0, so they go from id=1 to id=n.
468  // I am not going to start with fortran again - our indices start
469  // at zero, id=0 to id=n-1. So subtract one here
470  int tower_id = (*it)->GetCopyNo() - layer_id;
471  layer_id--;
472  pair<int, int> layer_twr = make_pair(layer_id, tower_id);
473  m_ScintiTilePhysVolMap.insert(pair<G4VPhysicalVolume *, pair<int, int>>(*it, layer_twr));
474  if (layer_id < 0 || layer_id >= m_NumScintiPlates)
475  {
476  cout << "invalid scintillator row " << layer_id
477  << ", valid range 0 < row < " << m_NumScintiPlates << endl;
478  gSystem->Exit(1);
479  }
480  }
481  else
482  {
483  cout << PHWHERE << " Error parsing " << (*it)->GetName()
484  << " for mother volume number " << endl;
485  gSystem->Exit(1);
486  }
487  break;
488  }
489  }
490  ++it;
491  }
492  return;
493 }
494 
496 {
498  SetTiltViaNcross(); // if number of crossings is set, use it to determine tilt
499  CheckTiltAngle(); // die if the tilt angle is out of range
500  // the needed steel cutout volume for the magnet is constructed with
501  // the scintillators since we have the theta angle at that point
502 
503  // call field setup here where we have the calculated tilt angle if number
504  // of crossings is given
506  m_NumScintiPlates, /*G4int steelPlates*/
507  m_ScintiGap, /*G4double scintiGap*/
508  m_TiltAngle); /*G4double tiltAngle*/
509 
511 #ifdef SCINTITEST
512  return 0;
513 #endif
514  G4VSolid *steel_plate = ConstructSteelPlate(hcalenvelope);
515  // DisplayVolume(steel_plate_4 ,hcalenvelope);
516  G4LogicalVolume *steel_logical = new G4LogicalVolume(steel_plate, GetDetectorMaterial(m_Params->get_string_param("material")), "HcalOuterSteelPlate", 0, 0, 0);
517  m_DisplayAction->AddSteelVolume(steel_logical);
518  double phi = 0;
519  double deltaphi = 2 * M_PI / m_NumScintiPlates;
520  ostringstream name;
521  double middlerad = m_OuterRadius - (m_OuterRadius - m_InnerRadius) / 2.;
522  // okay this is crude. Since the inner and outer radius of the scintillator is different from the inner/outer
523  // radius of the steel so the scintillator needs some shifting to get the gaps right
524  // basically the first shift (sumshift) puts it at the inner radius of the steel
525  // then it needs to be shifted up by twice the difference between the inner steel and inner scintillator radius
526  // since sumshift is div by 2 for G4 but we need to shift the full delta(inner radius)
527  double scinti_tile_orig_length = m_ScintiTileXUpper + m_ScintiTileXLower - subtract_from_scinti_x;
528  double shiftup = (m_ScintiInnerRadius - m_InnerRadius) / cos(m_TiltAngle / rad);
529  double sumshift = scinti_tile_orig_length - m_ScintiTileX;
530  sumshift = sumshift - 2 * shiftup;
531  double shiftslat = fabs(m_ScintiTileXLower - m_ScintiTileXUpper) / 2. + sumshift / 2.;
532  // calculate phi offset (copied from code inside following loop):
533  // first get the center point (phi=0) so it's middlerad/0
534  // then shift the scintillator center as documented in loop
535  // then
536  // for positive tilt angles we need the lower left corner of the scintillator
537  // for negative tilt angles we nee the upper right corner of the scintillator
538  // as it turns out the code uses the middle of the face of the scintillator
539  // as reference, if this is a problem the code needs to be modified to
540  // actually calculate the corner (but the math of the construction is that
541  // the middle of the scintillator sits at zero)
542  double xp = cos(phi) * middlerad;
543  double yp = sin(phi) * middlerad;
544  xp -= cos((-m_TiltAngle) / rad - phi) * shiftslat;
545  yp += sin((-m_TiltAngle) / rad - phi) * shiftslat;
546  if (m_TiltAngle > 0)
547  {
548  double xo = xp - (m_ScintiTileX / 2.) * cos(m_TiltAngle / rad);
549  double yo = yp - (m_ScintiTileX / 2.) * sin(m_TiltAngle / rad);
550  phi = -atan(yo / xo);
551  }
552  else if (m_TiltAngle < 0)
553  {
554  double xo = xp + (m_ScintiTileX / 2.) * cos(m_TiltAngle / rad);
555  double yo = yp + (m_ScintiTileX / 2.) * sin(m_TiltAngle / rad);
556  phi = -atan(yo / xo);
557  }
558  // else (for m_TiltAngle = 0) phi stays zero
559  for (int i = 0; i < m_NumScintiPlates; i++)
560  {
561  G4RotationMatrix *Rot = new G4RotationMatrix();
562  double ypos = sin(phi) * middlerad;
563  double xpos = cos(phi) * middlerad;
564  // the center of the scintillator is not the center of the inner hcal
565  // but depends on the tilt angle. Therefore we need to shift
566  // the center from the mid point
567  ypos += sin((-m_TiltAngle) / rad - phi) * shiftslat;
568  xpos -= cos((-m_TiltAngle) / rad - phi) * shiftslat;
569  Rot->rotateZ(phi * rad + m_TiltAngle);
570  G4ThreeVector g4vec(xpos, ypos, 0);
571  // great the MakeImprint always adds 1 to the copy number and 0 has a
572  // special meaning (which then also adds 1). Basically our volume names
573  // will start at 1 instead of 0 and there is nothing short of patching this
574  // method. I'll take care of this in the decoding of the volume name
575  // AAAAAAARHGS
576  m_ScintiMotherAssembly->MakeImprint(hcalenvelope, g4vec, Rot, i, OverlapCheck());
577  delete Rot;
578  Rot = new G4RotationMatrix();
579  Rot->rotateZ(-phi * rad);
580  name.str("");
581  name << "OuterHcalSteel_" << i;
582  m_SteelAbsorberVec.insert(new G4PVPlacement(Rot, G4ThreeVector(0, 0, 0), steel_logical, name.str().c_str(), hcalenvelope, 0, i, OverlapCheck()));
583  phi += deltaphi;
584  }
585  hcalenvelope->SetFieldManager(m_FieldSetup->get_Field_Manager_Gap(), false);
586 
587  steel_logical->SetFieldManager(m_FieldSetup->get_Field_Manager_Iron(), true);
588  return 0;
589 }
590 
592 {
593  G4VSolid *bigtile = ConstructScintillatorBox(hcalenvelope);
594  // eta->theta
595  G4double delta_eta = m_Params->get_double_param("scinti_eta_coverage") / m_NumScintiTiles;
596  G4double eta = 0;
597  G4double theta;
598  G4double x[4];
599  G4double z[4];
600  ostringstream name;
601  double overhang = (m_ScintiTileX - (m_ScintiOuterRadius - m_ScintiInnerRadius)) / 2.;
602  double offset = 1 * cm + overhang; // add 1cm to make sure the G4ExtrudedSolid
603  // is larger than the tile so we do not have
604  // funny edge effects when overlapping vols
605  double magnet_cutout_x = (m_Params->get_double_param("magnet_cutout_scinti_radius") * cm - m_ScintiInnerRadius) / cos(m_TiltAngle / rad);
606  double x_inner = m_ScintiInnerRadius - overhang;
607  double inner_offset = offset;
608  // coordinates like the steel plates:
609  // 0/0 upper left
610  // 1/1 upper right
611  // 2/2 lower right
612  // 3/3 lower left
613  // sorry they are different than the coordinates used for the scintilators
614  // here, this is why the indices are seemingly mixed up
615  double xsteelcut[4];
616  double zsteelcut[4];
617  fill_n(zsteelcut, 4, NAN);
619  double steel_offset = 1 * cm + steel_overhang; // add 1cm to make sure the G4ExtrudedSolid
620  double steel_x_inner = m_InnerRadius - steel_overhang;
621  double steel_magnet_cutout_x = (m_Params->get_double_param("magnet_cutout_radius") * cm - m_InnerRadius) / cos(m_TiltAngle / rad);
622  double steel_inner_offset = steel_offset;
623  xsteelcut[0] = steel_x_inner + steel_magnet_cutout_x;
624  xsteelcut[1] = xsteelcut[0];
625  xsteelcut[2] = m_InnerRadius - steel_offset;
626  xsteelcut[3] = xsteelcut[2];
627  double scinti_gap_neighbor = m_Params->get_double_param("scinti_gap_neighbor") * cm;
628  for (int i = 0; i < m_NumScintiTiles; i++)
629  {
630  if (i >= m_Params->get_int_param("magnet_cutout_first_scinti"))
631  {
632  x_inner = m_ScintiInnerRadius - overhang + magnet_cutout_x;
633  inner_offset = offset - magnet_cutout_x;
634  }
635  theta = M_PI / 2 - PHG4Utils::get_theta(eta); // theta = 90 for eta=0
636  x[0] = x_inner;
637  z[0] = tan(theta) * m_ScintiInnerRadius;
638  x[1] = m_ScintiOuterRadius + overhang; // since the tile is tilted, x is not at the outer radius but beyond
639  z[1] = tan(theta) * m_ScintiOuterRadius;
640  if (i >= m_Params->get_int_param("magnet_cutout_first_scinti"))
641  {
642  z[0] = tan(theta) * (m_ScintiInnerRadius + (m_Params->get_double_param("magnet_cutout_scinti_radius") * cm - m_ScintiInnerRadius));
643  }
644  eta += delta_eta;
645  theta = M_PI / 2 - PHG4Utils::get_theta(eta); // theta = 90 for eta=0
646  x[2] = x_inner;
647  z[2] = tan(theta) * m_ScintiInnerRadius;
648  if (i >= m_Params->get_int_param("magnet_cutout_first_scinti"))
649  {
650  z[2] = tan(theta) * (m_ScintiInnerRadius + (m_Params->get_double_param("magnet_cutout_scinti_radius") * cm - m_ScintiInnerRadius));
651  }
652  x[3] = m_ScintiOuterRadius + overhang; // since the tile is tilted, x is not at the outer radius but beyond
653  z[3] = tan(theta) * m_ScintiOuterRadius;
654  // apply gap between scintillators
655  z[0] += scinti_gap_neighbor / 2.;
656  z[1] += scinti_gap_neighbor / 2.;
657  z[2] -= scinti_gap_neighbor / 2.;
658  z[3] -= scinti_gap_neighbor / 2.;
659  PHG4OuterHcalDetector::Point_2 leftsidelow(z[0], x[0]);
660  PHG4OuterHcalDetector::Point_2 leftsidehigh(z[1], x[1]);
661  x[0] = m_ScintiInnerRadius - inner_offset;
662  z[0] = x_at_y(leftsidelow, leftsidehigh, x[0]);
663  x[1] = m_ScintiOuterRadius + offset;
664  z[1] = x_at_y(leftsidelow, leftsidehigh, x[1]);
665  PHG4OuterHcalDetector::Point_2 rightsidelow(z[2], x[2]);
666  PHG4OuterHcalDetector::Point_2 rightsidehigh(z[3], x[3]);
667  x[2] = m_ScintiOuterRadius + offset;
668  z[2] = x_at_y(rightsidelow, rightsidehigh, x[2]);
669  x[3] = m_ScintiInnerRadius - inner_offset;
670  z[3] = x_at_y(rightsidelow, rightsidehigh, x[3]);
671  // store corner points of extruded solid we need to subtract from steel
672  if (i == m_Params->get_int_param("magnet_cutout_first_scinti"))
673  {
674  double x0 = m_InnerRadius - (steel_inner_offset - steel_magnet_cutout_x);
675  double z0 = x_at_y(leftsidelow, leftsidehigh, x0);
676  double xpos = m_InnerRadius - steel_offset;
677  zsteelcut[0] = z0;
678  zsteelcut[3] = x_at_y(leftsidelow, leftsidehigh, xpos);
679  }
680  double x2 = m_OuterRadius + steel_offset;
681  double z2 = x_at_y(rightsidelow, rightsidehigh, x2);
682  zsteelcut[1] = z2 + 1 * cm;
683  zsteelcut[2] = z2 + 1 * cm;
684  vector<G4TwoVector> vertexes;
685  for (int j = 0; j < 4; j++)
686  {
687  G4TwoVector v(x[j], z[j]);
688  vertexes.push_back(v);
689  }
690  G4TwoVector zero(0, 0);
691 
692  G4VSolid *scinti = new G4ExtrudedSolid("ScintillatorTile",
693  vertexes,
694  m_ScintiTileThickness + 0.2 * mm,
695  zero, 1.0,
696  zero, 1.0);
697  G4RotationMatrix *rotm = new G4RotationMatrix();
698  rotm->rotateX(-90 * deg);
699  name.str("");
700  name << "scintillator_" << i << "_left";
701  G4VSolid *scinti_tile = new G4IntersectionSolid(name.str(), bigtile, scinti, rotm, G4ThreeVector(-(m_ScintiInnerRadius + m_ScintiOuterRadius) / 2., 0, 0));
702  delete rotm;
703  m_ScintiTilesVec[i + m_NumScintiTiles] = scinti_tile;
704  rotm = new G4RotationMatrix();
705  rotm->rotateX(90 * deg);
706  name.str("");
707  name << "scintillator_" << i << "_right";
708  scinti_tile = new G4IntersectionSolid(name.str(), bigtile, scinti, rotm, G4ThreeVector(-(m_ScintiInnerRadius + m_ScintiOuterRadius) / 2., 0, 0));
709  delete rotm;
710  m_ScintiTilesVec[m_NumScintiTiles - i - 1] = scinti_tile;
711  }
712 #ifdef SCINTITEST
713  for (unsigned int i = 0; i < m_ScintiTilesVec.size(); i++)
714  {
715  if (m_ScintiTilesVec[i])
716  {
717  DisplayVolume(m_ScintiTilesVec[i], hcalenvelope);
718  }
719  }
720 #endif
721 
722  vector<G4TwoVector> vertexes;
723  for (int j = 0; j < 4; j++)
724  {
725  if (!isfinite(zsteelcut[j]))
726  {
727  return;
728  }
729  G4TwoVector v(xsteelcut[j], zsteelcut[j]);
730  vertexes.push_back(v);
731  }
732  G4TwoVector zero(0, 0);
733  m_SteelCutoutForMagnetG4Solid = new G4ExtrudedSolid("ScintillatorTile",
734  vertexes,
735  m_ScintiTileThickness + 20 * cm,
736  zero, 1.0,
737  zero, 1.0);
738  return;
739 }
740 
741 G4double
743 {
744  double xret = NAN;
745  double x[2];
746  x[0] = CGAL::to_double(p0.x());
747  x[1] = CGAL::to_double(p1.x());
748  Line_2 l(p0, p1);
749  double newx = fabs(x[0]) + fabs(x[1]);
750  PHG4OuterHcalDetector::Point_2 p0new(-newx, yin);
751  PHG4OuterHcalDetector::Point_2 p1new(newx, yin);
752  Segment_2 s(p0new, p1new);
753  CGAL::Object result = CGAL::intersection(l, s);
754  if (const PHG4OuterHcalDetector::Point_2 *ipoint = CGAL::object_cast<PHG4OuterHcalDetector::Point_2>(&result))
755  {
756  xret = CGAL::to_double(ipoint->x());
757  }
758  else
759  {
760  cout << PHWHERE << " failed for y = " << yin << endl;
761  cout << "p0(x): " << CGAL::to_double(p0.x()) << ", p0(y): " << CGAL::to_double(p0.y()) << endl;
762  cout << "p1(x): " << CGAL::to_double(p1.x()) << ", p1(y): " << CGAL::to_double(p1.y()) << endl;
763  exit(1);
764  }
765  return xret;
766 }
767 
770 {
771 #ifdef SCINTITEST
772  ConstructHcalSingleScintillators(hcalenvelope);
773  return nullptr;
774 #endif
775  ConstructHcalSingleScintillators(hcalenvelope);
776  G4AssemblyVolume *assmeblyvol = new G4AssemblyVolume();
777  ostringstream name;
778  G4ThreeVector g4vec;
779  double steplimits = m_Params->get_double_param("steplimits") * cm;
780  for (unsigned int i = 0; i < m_ScintiTilesVec.size(); i++)
781  {
782  name.str("");
783  name << m_ScintiLogicNamePrefix << i;
784  G4UserLimits *g4userlimits = nullptr;
785  if (isfinite(steplimits))
786  {
787  g4userlimits = new G4UserLimits(steplimits);
788  }
789  G4LogicalVolume *scinti_tile_logic = new G4LogicalVolume(m_ScintiTilesVec[i], GetDetectorMaterial("G4_POLYSTYRENE"), name.str().c_str(), nullptr, nullptr, g4userlimits);
790  m_DisplayAction->AddScintiVolume(scinti_tile_logic);
791  assmeblyvol->AddPlacedVolume(scinti_tile_logic, g4vec, nullptr);
792 
793  //field after burner
794  scinti_tile_logic->SetFieldManager(m_FieldSetup->get_Field_Manager_Gap(), true);
795  }
796  return assmeblyvol;
797 }
798 
800 {
801  // just make sure the parameters make a bit of sense
803  {
804  cout << PHWHERE << ": Inner Radius " << m_InnerRadius / cm
805  << " cm larger than Outer Radius " << m_OuterRadius / cm
806  << " cm" << endl;
807  gSystem->Exit(1);
808  }
810  {
811  cout << PHWHERE << "Scintillator thickness " << m_ScintiTileThickness / cm
812  << " cm larger than scintillator gap " << m_ScintiGap / cm
813  << " cm" << endl;
814  gSystem->Exit(1);
815  }
817  {
818  cout << PHWHERE << "Scintillator outer radius " << m_ScintiOuterRadius / cm
819  << " cm smaller than scintillator inner radius " << m_ScintiInnerRadius / cm
820  << " cm" << endl;
821  gSystem->Exit(1);
822  }
824  {
825  cout << PHWHERE << "Scintillator outer radius " << m_ScintiOuterRadius / cm
826  << " cm smaller than inner radius " << m_InnerRadius / cm
827  << " cm" << endl;
828  gSystem->Exit(1);
829  }
831  {
832  cout << PHWHERE << "Scintillator inner radius " << m_ScintiInnerRadius / cm
833  << " cm larger than inner radius " << m_InnerRadius / cm
834  << " cm" << endl;
835  gSystem->Exit(1);
836  }
837  if (m_Params->get_double_param("magnet_cutout_scinti_radius") * cm < m_ScintiInnerRadius)
838  {
839  cout << PHWHERE << "Magnet scintillator cutout radius " << m_Params->get_double_param("magnet_cutout_scinti_radius")
840  << " cm smaller than inner scintillator radius " << m_ScintiInnerRadius / cm
841  << " cm" << endl;
842  gSystem->Exit(1);
843  }
844  if (m_Params->get_double_param("magnet_cutout_radius") * cm < m_InnerRadius)
845  {
846  cout << PHWHERE << "Magnet steel cutout radius " << m_Params->get_double_param("magnet_cutout_radius")
847  << " cm smaller than inner radius " << m_InnerRadius / cm
848  << " cm" << endl;
849  gSystem->Exit(1);
850  }
851 
852  return 0;
853 }
854 
856 {
857  int ncross = m_Params->get_int_param("ncross");
858  if (!ncross || isfinite(m_TiltAngle))
859  {
860  // mark ncross parameter as not used
861  m_Params->set_int_param("ncross", 0);
862  return;
863  }
864  if ((isfinite(m_TiltAngle)) && (Verbosity() > 0))
865  {
866  cout << "both number of crossings and tilt angle are set" << endl;
867  cout << "using number of crossings to determine tilt angle" << endl;
868  }
869  double mid_radius = m_InnerRadius + (m_OuterRadius - m_InnerRadius) / 2.;
870  double deltaphi = (2 * M_PI / m_NumScintiPlates) * ncross;
871  PHG4OuterHcalDetector::Point_2 pnull(0, 0);
873  PHG4OuterHcalDetector::Point_2 phightmp(1, tan(deltaphi));
875  Circle_2 inner_circle(pin1, pin2, pin3);
876  PHG4OuterHcalDetector::Point_2 pmid1(mid_radius, 0), pmid2(0, mid_radius), pmid3(-mid_radius, 0);
877  Circle_2 mid_circle(pmid1, pmid2, pmid3);
879  Circle_2 outer_circle(pout1, pout2, pout3);
880  Line_2 l_up(pnull, phightmp);
881  vector<CGAL::Object> res;
882  CGAL::intersection(outer_circle, l_up, std::back_inserter(res));
884  vector<CGAL::Object>::const_iterator iter;
885  for (iter = res.begin(); iter != res.end(); ++iter)
886  {
887  CGAL::Object obj = *iter;
888  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
889  {
890  if (CGAL::to_double(point->first.x()) > 0)
891  {
892  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
893  upperright = pntmp;
894  }
895  }
896  else
897  {
898  cout << "CGAL::Object type not pair..." << endl;
899  exit(1);
900  }
901  }
902  Line_2 l_right(plow, upperright);
903  res.clear();
905  CGAL::intersection(mid_circle, l_right, std::back_inserter(res));
906  for (iter = res.begin(); iter != res.end(); ++iter)
907  {
908  CGAL::Object obj = *iter;
909  if (const std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned> *point = CGAL::object_cast<std::pair<CGAL::Circular_arc_point_2<PHG4OuterHcalDetector::Circular_k>, unsigned>>(&obj))
910  {
911  if (CGAL::to_double(point->first.x()) > 0)
912  {
913  PHG4OuterHcalDetector::Point_2 pntmp(CGAL::to_double(point->first.x()), CGAL::to_double(point->first.y()));
914  midpoint = pntmp;
915  }
916  }
917  else
918  {
919  cout << "CGAL::Object type not pair..." << endl;
920  exit(1);
921  }
922  }
923  // length left side
924  double ll = sqrt((CGAL::to_double(midpoint.x()) - m_InnerRadius) * (CGAL::to_double(midpoint.x()) - m_InnerRadius) + CGAL::to_double(midpoint.y()) * CGAL::to_double(midpoint.y()));
925  double upside = sqrt(CGAL::to_double(midpoint.x()) * CGAL::to_double(midpoint.x()) + CGAL::to_double(midpoint.y()) * CGAL::to_double(midpoint.y()));
926  // c^2 = a^2+b^2 - 2ab*cos(gamma)
927  // gamma = acos((a^2+b^2=c^2)/2ab
928  double tiltangle = acos((ll * ll + upside * upside - m_InnerRadius * m_InnerRadius) / (2 * ll * upside));
929  tiltangle = tiltangle * rad;
930  m_TiltAngle = copysign(tiltangle, ncross);
931  m_Params->set_double_param("tilt_angle", m_TiltAngle / deg);
932  return;
933 }
934 
935 // check if tilt angle is reasonable - too large, no intersections with inner radius
937 {
938  if (fabs(m_TiltAngle) >= M_PI)
939  {
940  cout << PHWHERE << "invalid tilt angle, abs(tilt) >= 90 deg: " << (m_TiltAngle / deg)
941  << endl;
942  exit(1);
943  }
944 
945  double mid_radius = m_InnerRadius + (m_OuterRadius - m_InnerRadius) / 2.;
946  PHG4OuterHcalDetector::Point_2 pmid(mid_radius, 0); // center of scintillator
947  double xcoord = 0;
948  double ycoord = mid_radius * tan(m_TiltAngle / rad);
949  PHG4OuterHcalDetector::Point_2 pxnull(xcoord, ycoord);
950  Line_2 s2(pmid, pxnull);
952  Circle_2 inner_circle(sc1, sc2, sc3);
953  vector<CGAL::Object> res;
954  CGAL::intersection(inner_circle, s2, std::back_inserter(res));
955  if (res.size() == 0)
956  {
957  cout << PHWHERE << " Tilt angle " << (m_TiltAngle / deg)
958  << " too large, no intersection with inner radius" << endl;
959  exit(1);
960  }
961  return 0;
962 }
963 
964 void PHG4OuterHcalDetector::Print(const string &what) const
965 {
966  cout << "Outer Hcal Detector:" << endl;
967  if (what == "ALL" || what == "VOLUME")
968  {
969  cout << "Volume Envelope: " << m_VolumeEnvelope / cm / cm / cm << " cm^3" << endl;
970  cout << "Volume Steel: " << m_VolumeSteel / cm / cm / cm << " cm^3" << endl;
971  cout << "Volume Scintillator: " << m_VolumeScintillator / cm / cm / cm << " cm^3" << endl;
972  cout << "Volume Air: " << (m_VolumeEnvelope - m_VolumeSteel - m_VolumeScintillator) / cm / cm / cm << " cm^3" << endl;
973  }
974  return;
975 }
976 
978 {
979  auto it = m_ScintiTilePhysVolMap.find(volume);
980  if (it != m_ScintiTilePhysVolMap.end())
981  {
982  return it->second;
983  }
984  cout << "could not locate volume " << volume->GetName()
985  << " in Inner Hcal scintillator map" << endl;
986  gSystem->Exit(1);
987  // that's dumb but code checkers do not know that gSystem->Exit()
988  // terminates, so using the standard exit() makes them happy
989  exit(1);
990 }