Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHG4MvtxDetector.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHG4MvtxDetector.cc
1 #include "PHG4MvtxDetector.h"
2 
3 #include "PHG4MvtxDefs.h"
5 #include "PHG4MvtxSupport.h"
6 
8 
10 
11 #include <phparameter/PHParameters.h>
12 #include <phparameter/PHParametersContainer.h>
13 
14 #include <g4main/PHG4Detector.h> // for PHG4Detector
15 #include <g4main/PHG4DisplayAction.h> // for PHG4DisplayAction
16 #include <g4main/PHG4Subsystem.h> // for PHG4Subsystem
17 
18 #include <phool/PHCompositeNode.h>
19 #include <phool/PHIODataNode.h>
20 #include <phool/PHNode.h> // for PHNode
21 #include <phool/PHNodeIterator.h> // for PHNodeIterator
22 #include <phool/PHObject.h> // for PHObject
23 #include <phool/getClass.h>
24 
25 #include <Geant4/G4AssemblyVolume.hh>
26 #include <Geant4/G4LogicalVolume.hh>
27 #include <Geant4/G4Material.hh>
28 #include <Geant4/G4PVPlacement.hh>
29 #include <Geant4/G4Polycone.hh>
30 #include <Geant4/G4RotationMatrix.hh> // for G4RotationMatrix
31 #include <Geant4/G4String.hh> // for G4String
32 #include <Geant4/G4SystemOfUnits.hh>
33 #include <Geant4/G4ThreeVector.hh> // for G4ThreeVector
34 #include <Geant4/G4Transform3D.hh> // for G4Transform3D
35 #include <Geant4/G4Tubs.hh>
36 #include <Geant4/G4Types.hh> // for G4double
37 #include <Geant4/G4VPhysicalVolume.hh> // for G4VPhysicalVolume
38 
39 // xerces has some shadowed variables
40 #pragma GCC diagnostic push
41 #pragma GCC diagnostic ignored "-Wshadow"
42 #include <Geant4/G4GDMLParser.hh>
43 #include <Geant4/G4GDMLReadStructure.hh> // for G4GDMLReadStructure
44 #pragma GCC diagnostic pop
45 
46 #include <cmath>
47 #include <cstdio> // for sprintf
48 #include <iostream> // for operator<<, basic...
49 #include <memory>
50 #include <sstream>
51 #include <utility> // for pair, make_pair
52 #include <vector> // for vector, vector<>:...
53 
54 namespace mvtxGeomDef
55 {
56  const double wrap_rmax = (107.7 + 0.3) * mm; // 300 um Marging from SB Flange
57  const double wrap_rmin = 22 * mm;
58  const double wrap_smallCylR = 55.00 * mm; // CYSS Ext Nose + 905 um
59  const double wrap_CYSSFlgN_Z = (177.5 + 12.5) * mm;
60  const double wrap_CYSSNose_Z = -245 * mm;
61  const double wrap_CYSSHead_Z = -315 * mm;
62  const double wrap_SBCyl_Z = -1800 * mm; // SB Cyl (1650 mm + 15 cm Margin)
63 } // namespace mvtxGeomDef
64 
66  : PHG4Detector(subsys, Node, dnam)
67  , m_DisplayAction(dynamic_cast<PHG4MvtxDisplayAction*>(subsys->GetDisplayAction()))
68  , m_ParamsContainer(_paramsContainer)
69  , m_StaveGeometryFile(_paramsContainer->GetParameters(PHG4MvtxDefs::GLOBAL)->get_string_param("stave_geometry_file"))
70 
71 {
72  if (Verbosity() > 0)
73  {
74  std::cout << "PHG4MvtxDetector constructor called" << std::endl;
75  }
76 
77  for (int ilayer = 0; ilayer < n_Layers; ++ilayer)
78  {
79  const PHParameters* params = m_ParamsContainer->GetParameters(ilayer);
80  m_IsLayerActive[ilayer] = params->get_int_param("active");
81  m_IsLayerSupportActive[ilayer] = params->get_int_param("supportactive");
82  m_IsBlackHole[ilayer] = params->get_int_param("blackhole");
83  m_N_staves[ilayer] = params->get_int_param("N_staves");
84  m_nominal_radius[ilayer] = params->get_double_param("layer_nominal_radius");
85  m_nominal_phitilt[ilayer] = params->get_double_param("phitilt");
86  m_nominal_phi0[ilayer] = params->get_double_param("phi0");
88  }
89 
90  if (Verbosity() > 0)
91  {
92  std::cout << "PHG4MvtxDetector constructor: making Mvtx detector. " << std::endl;
93  }
94 }
95 
97 {
98  // Is this volume one of the sensors?
99  // Checks if pointer matches one of our stored sensors for this layer
100  if (m_SensorPV.find(volume) != m_SensorPV.end())
101  {
102  if (Verbosity() > 0)
103  {
104  std::cout << " -- PHG4MvtxTDetector::IsSensor --" << std::endl;
105  std::cout << " volume Name : " << volume->GetName() << std::endl;
106  std::cout << " -----------------------------------------" << std::endl;
107  }
108  return 1;
109  }
111  {
112  if (m_SupportLV.find(volume->GetLogicalVolume()) != m_SupportLV.end())
113  {
114  return -1;
115  }
116  }
117  return 0;
118 }
119 
120 int PHG4MvtxDetector::IsInMvtx(G4VPhysicalVolume* volume, int& layer, int& stave) const
121 {
122  // Does this stave belong to this layer?
123  // Since the Assembly volume read from GDML does not give unique pointers
124  // to sensors, we need to check the stave, which is unique
125  auto iter = m_StavePV.find(volume);
126  if (iter != m_StavePV.end())
127  {
128  std::tie(layer, stave) = iter->second;
129  if (Verbosity() > 0)
130  {
131  std::cout << " -- PHG4MvtxDetector::IsInMvtx --" << std::endl;
132  std::cout << " layer: " << layer << std::endl;
133  std::cout << " stave: " << stave << std::endl;
134  std::cout << " volume Name : " << volume->GetName() << std::endl;
135  std::cout << " stave Name : " << iter->first->GetName() << std::endl;
136  std::cout << " -----------------------------------------" << std::endl;
137  }
138  return 1;
139  }
140 
141  return 0;
142 }
143 
145 {
146  // Get Mvtx layer from stave index in the Mvtx
147  // Mvtx stave index start from 0, i.e index = 0 for stave 0 in layer 0
148  int lay = 0;
149  while (!(index < m_N_staves[lay]))
150  {
151  index -= m_N_staves[lay];
152  lay++;
153  }
154  return lay;
155 }
156 
158 {
159  // Get stave index in the layer from stave index in the Mvtx
160  int lay = 0;
161  while (!(index < m_N_staves[lay]))
162  {
163  index -= m_N_staves[lay];
164  lay++;
165  }
166  return index;
167 }
168 
169 void PHG4MvtxDetector::ConstructMe(G4LogicalVolume* logicWorld)
170 {
171  // This is called from PHG4PhenixDetector::Construct()
172  if (Verbosity() > 0)
173  {
174  std::cout << std::endl
175  << "PHG4MvtxDetector::Construct called for Mvtx " << std::endl;
176  }
177 
178  const G4int numZPlanes = 4;
179  const G4double zPlane[numZPlanes] = {mvtxGeomDef::wrap_SBCyl_Z,
183 
184  const G4double rInner[numZPlanes] = {mvtxGeomDef::wrap_rmin, mvtxGeomDef::wrap_rmin,
185  mvtxGeomDef::wrap_rmin, mvtxGeomDef::wrap_rmin};
186 
187  const G4double rOuter[numZPlanes] = {mvtxGeomDef::wrap_rmax, mvtxGeomDef::wrap_rmax,
189  mvtxGeomDef::wrap_smallCylR};
190 
191  auto mvtxWrapSol = new G4Polycone("sol_MVTX_Wrapper", 0, 2.0 * M_PI,
192  numZPlanes, zPlane, rInner, rOuter);
193 
194  auto world_mat = logicWorld->GetMaterial();
195 
196  auto logicMVTX = new G4LogicalVolume(mvtxWrapSol, world_mat, "log_MVTX_Wrapper");
197 
198  G4RotationMatrix Ra;
199  G4ThreeVector Ta;
200  G4Transform3D Tr(Ra, Ta);
201  new G4PVPlacement(Tr, logicMVTX, "MVTX_Wrapper",
202  logicWorld, false, 0, false);
203 
204  // the tracking layers are placed directly in the world volume,
205  // since some layers are (touching) double layers
206  // this reads in the ITS stave geometry from a file and constructs the layer from it
207  ConstructMvtx(logicMVTX);
208  ConstructMvtxPassiveVol(logicMVTX);
209 
210  AddGeometryNode();
211  return;
212 }
213 
214 int PHG4MvtxDetector::ConstructMvtx(G4LogicalVolume* trackerenvelope)
215 {
216  if (Verbosity() > 0)
217  {
218  std::cout << " PHG4MvtxDetector::ConstructMvtx:" << std::endl;
219  std::cout << std::endl;
220  }
221  //===================================
222  // Import the stave physical volume here
223  //===================================
224 
225  // import the staves from the gemetry file
226  std::unique_ptr<G4GDMLReadStructure> reader(new G4GDMLReadStructure());
227  G4GDMLParser gdmlParser(reader.get());
228  gdmlParser.Read(m_StaveGeometryFile, false);
229 
230  // figure out which assembly we want
231  char assemblyname[500];
232  sprintf(assemblyname, "MVTXStave");
233 
234  if (Verbosity() > 0)
235  {
236  std::cout << "Geting the stave assembly named " << assemblyname << std::endl;
237  }
238  G4AssemblyVolume* av_ITSUStave = reader->GetAssembly(assemblyname);
239 
240  for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
241  {
242  if (m_IsLayerActive[ilayer])
243  {
244  if (Verbosity() > 0)
245  {
246  std::cout << std::endl;
247  std::cout << " Constructing Layer " << ilayer << std::endl;
248  }
249  ConstructMvtx_Layer(ilayer, av_ITSUStave, trackerenvelope);
250  }
251  }
252  FillPVArray(av_ITSUStave);
253  SetDisplayProperty(av_ITSUStave);
254 
255  return 0;
256 }
257 
258 int PHG4MvtxDetector::ConstructMvtx_Layer(int layer, G4AssemblyVolume* av_ITSUStave, G4LogicalVolume*& trackerenvelope)
259 {
260  //=========================================
261  // Now we populate the whole layer with the staves
262  //==========================================
263 
264  int N_staves = m_N_staves[layer];
265  G4double layer_nominal_radius = m_nominal_radius[layer];
266  G4double phitilt = m_nominal_phitilt[layer];
267  G4double phi0 = m_nominal_phi0[layer]; // YCM: azimuthal offset for the first stave
268 
269  if (N_staves < 0)
270  {
271  // The user did not specify how many staves to use for this layer, so we calculate the best value
272  // We choose a phistep that fits N_staves into this radius, but with an arclength separation of AT LEAST arcstep
273  // ideally, the radius would be chosen so that numstaves = N_staves exactly, to get the closest spacing of staves possible without overlaps
274  double arcstep = 12.25;
275  double numstaves = 2.0 * M_PI * layer_nominal_radius / arcstep; // this is just to print out
276  N_staves = int(2.0 * M_PI * layer_nominal_radius / arcstep); // this is the number of staves used
277 
278  // Also suggest the ideal radius for this layer
279  if (Verbosity() > 0)
280  {
281  std::cout << " Calculated N_staves for layer " /*<< layer*/
282  << " layer_nominal_radius " << layer_nominal_radius
283  << " ITS arcstep " << arcstep
284  << " circumference divided by arcstep " << numstaves
285  << " N_staves " << N_staves
286  << std::endl;
287  std::cout << "A radius for this layer of " << (double) N_staves * arcstep / (2.0 * M_PI) + 0.01 << " or "
288  << (double) (N_staves + 1) * arcstep / (2.0 * M_PI) + 0.01 << " would produce perfect stave spacing" << std::endl;
289  }
290  }
291 
292  G4double phistep = get_phistep(layer); // this produces even stave spacing
293  double z_location = 0.0;
294 
295  if (Verbosity() > 0)
296  {
297  std::cout << " layer " /*<< layer*/
298  << " layer_nominal_radius " << layer_nominal_radius
299  << " N_staves " << N_staves
300  << " phistep " << phistep
301  << " phitilt " << phitilt
302  << " phi0 " << phi0
303  << std::endl;
304  }
305 
306  // The stave starts out at (0,0,0) and oriented so that the sensors face upward in y
307  // So we need to rotate the sensor 90 degrees before placing it using phi_offset
308  // note that the gdml file uses a negative phi_offset - different coord system, apparently - the following works
309  double phi_offset = M_PI / 2.0;
310 
311  for (int iphi = 0; iphi < N_staves; iphi++)
312  {
313  // Place the ladder segment envelopes at the correct z and phi
314  // This is the azimuthal angle at which we place the stave
315  // phi0 is the azimuthal offset for the first stave
316  G4double phi_rotation = phi0 + (double) iphi * phistep;
317 
318  G4RotationMatrix Ra;
319  G4ThreeVector Ta;
320 
321  if (Verbosity() > 0)
322  {
323  std::cout << "phi_offset = " << phi_offset << " iphi " << iphi << " phi_rotation = " << phi_rotation << " phitilt " << phitilt << std::endl;
324  }
325  // It is first rotated in phi by the azimuthal angle phi_rotation, plus the 90 degrees needed to point the face of the sensor at the origin, plus the tilt (if a tilt is appropriate)
326 
327  // note - if this is layer 0-2, phitilt is the additional tilt for clearance. Otherwise it is zero
328  Ra.rotateZ(phi_rotation + phi_offset + phitilt);
329  // Then translated as follows
330 
331  Ta.setX(layer_nominal_radius * cos(phi_rotation));
332  Ta.setY(layer_nominal_radius * sin(phi_rotation));
333  Ta.setZ(z_location);
334 
335  if (Verbosity() > 0)
336  {
337  std::cout << " iphi " << iphi << " phi_rotation " << phi_rotation
338  << " x " << layer_nominal_radius * cos(phi_rotation)
339  << " y " << layer_nominal_radius * sin(phi_rotation)
340  << " z " << z_location
341  << std::endl;
342  }
343  G4Transform3D Tr(Ra, Ta);
344 
345  av_ITSUStave->MakeImprint(trackerenvelope, Tr, 0, OverlapCheck());
346  }
347 
348  if (Verbosity() > 0)
349  {
350  std::cout << "This layer has a total of " << N_staves << " staves" << std::endl;
351  }
352  return 0;
353 }
354 
356 {
357  if (Verbosity() > 0)
358  {
359  std::cout << " PHG4MvtxDetector::ConstructMvtxServices:" << std::endl;
360  std::cout << std::endl;
361  }
362 
363  // Now construct EWs, service barrel, CYSS, cones and cables
364  PHG4MvtxSupport* mvtxSupportSystem = new PHG4MvtxSupport(this, m_DisplayAction, OverlapCheck());
365  mvtxSupportSystem->ConstructMvtxSupport(lv);
366 
367  delete mvtxSupportSystem;
368 
369  return 0;
370 }
371 
372 void PHG4MvtxDetector::SetDisplayProperty(G4AssemblyVolume* av)
373 {
374  // std::cout <<"SetDisplayProperty - G4AssemblyVolume w/ TotalImprintedVolumes "<<av->TotalImprintedVolumes()
375  // <<"/"<<av->GetImprintsCount()<<std::endl;
376 
377  std::vector<G4VPhysicalVolume*>::iterator it = av->GetVolumesIterator();
378 
379  int nDaughters = av->TotalImprintedVolumes();
380  for (int i = 0; i < nDaughters; ++i, ++it)
381  {
382  // std::cout <<"SetDisplayProperty - AV["<<i<<"] = "<<(*it)->GetName()<<std::endl;
383  G4VPhysicalVolume* pv = (*it);
384 
385  G4LogicalVolume* worldLogical = pv->GetLogicalVolume();
386  SetDisplayProperty(worldLogical);
387  }
388 }
389 
390 void PHG4MvtxDetector::SetDisplayProperty(G4LogicalVolume* lv)
391 {
392  std::string material_name(lv->GetMaterial()->GetName());
393 
394  if (Verbosity() >= 50)
395  {
396  std::cout << "SetDisplayProperty - LV " << lv->GetName() << " built with "
397  << material_name << std::endl;
398  }
399  std::vector<std::string> matname = {"SI", "KAPTON", "ALUMINUM", "Carbon", "M60J3K", "WATER"};
400  bool found = false;
401  for (const std::string& nam : matname)
402  {
403  if (material_name.find(nam) != std::string::npos)
404  {
405  m_DisplayAction->AddVolume(lv, nam);
406  if (Verbosity() >= 50)
407  {
408  std::cout << "SetDisplayProperty - LV " << lv->GetName() << " display with " << nam << std::endl;
409  }
410  found = true;
411  break;
412  }
413  }
414  if (!found)
415  {
416  m_DisplayAction->AddVolume(lv, "ANYTHING_ELSE");
417  }
418  int nDaughters = lv->GetNoDaughters();
419  for (int i = 0; i < nDaughters; ++i)
420  {
421  G4VPhysicalVolume* pv = lv->GetDaughter(i);
422 
423  // std::cout <<"SetDisplayProperty - PV["<<i<<"] = "<<pv->GetName()<<std::endl;
424 
425  G4LogicalVolume* worldLogical = pv->GetLogicalVolume();
426  SetDisplayProperty(worldLogical);
427  }
428 }
429 
431 {
432  int active = 0;
433  for (auto& isAct : m_IsLayerActive)
434  {
435  active |= isAct;
436  }
437  if (active) // At least one layer is active
438  {
439  // ostringstream geonode;
440  std::string geonode = "CYLINDERGEOM_" + ((m_SuperDetector != "NONE") ? m_SuperDetector : m_Detector);
441  PHG4CylinderGeomContainer* geo = findNode::getClass<PHG4CylinderGeomContainer>(topNode(), geonode);
442  if (!geo)
443  {
444  geo = new PHG4CylinderGeomContainer();
445  PHNodeIterator iter(topNode());
446  PHCompositeNode* runNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "RUN"));
447  PHIODataNode<PHObject>* newNode = new PHIODataNode<PHObject>(geo, geonode, "PHObject");
448  runNode->addNode(newNode);
449  }
450  // here in the detector class we have internal units(mm), convert to cm
451  // before putting into the geom object
452  for (unsigned short ilayer = 0; ilayer < n_Layers; ++ilayer)
453  {
454  CylinderGeom_Mvtx* mygeom = new CylinderGeom_Mvtx(ilayer,
455  m_N_staves[ilayer],
456  m_nominal_radius[ilayer] / cm,
457  get_phistep(ilayer) / rad,
458  m_nominal_phitilt[ilayer] / rad,
459  m_nominal_phi0[ilayer] / rad);
460  geo->AddLayerGeom(ilayer, mygeom);
461  } // loop per layers
462  if (Verbosity())
463  {
464  geo->identify();
465  }
466  } // is active
467 } // AddGeometryNode
468 
469 void PHG4MvtxDetector::FillPVArray(G4AssemblyVolume* av)
470 {
471  if (Verbosity() > 0)
472  {
473  std::cout << "-- FillPVArray --" << std::endl;
474  }
475  std::vector<G4VPhysicalVolume*>::iterator it = av->GetVolumesIterator();
476 
477  int nDaughters = av->TotalImprintedVolumes();
478  for (int i = 0; i < nDaughters; ++i, ++it)
479  {
480  G4VPhysicalVolume* pv = (*it);
481 
482  G4LogicalVolume* worldLogical = pv->GetLogicalVolume();
483  // we only care about the staves, which contain the sensors, not the structures
484  if (pv->GetName().find("MVTXHalfStave_pv") != std::string::npos)
485  {
486  int layer = get_layer(m_StavePV.size());
487  int stave = get_stave(m_StavePV.size());
488 
489  m_StavePV.insert(std::make_pair(pv, std::make_tuple(layer, stave)));
490 
491  if (Verbosity() > 0)
492  {
493  std::cout << "Mvtx layer id " << layer << std::endl;
494  std::cout << "Stave in layer id " << stave << std::endl;
495  std::cout << "Mvtx stave count " << m_StavePV.size() << std::endl;
496  std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
497  std::cout << " LV[" << i << "] = " << worldLogical->GetName() << std::endl;
498  }
499 
500  FindSensor(worldLogical);
501  }
502  else // in case of stave structure
503  {
504  if (Verbosity() > 0)
505  {
506  std::cout << "FillPVArray - AV[" << i << "] = " << (*it)->GetName() << std::endl;
507  std::cout << " LV[" << i << "] = " << worldLogical->GetName() << std::endl;
508  }
509  }
510  }
511 }
512 
513 void PHG4MvtxDetector::FindSensor(G4LogicalVolume* lv)
514 {
515  int nDaughters = lv->GetNoDaughters();
516  for (int i = 0; i < nDaughters; ++i)
517  {
518  G4VPhysicalVolume* pv = lv->GetDaughter(i);
519  if (Verbosity() > 0)
520  {
521  std::cout << " PV[" << i << "]: " << pv->GetName() << std::endl;
522  }
523  if (pv->GetName().find("MVTXSensor_") != std::string::npos)
524  {
525  m_SensorPV.insert(pv);
526 
527  if (Verbosity() > 0)
528  {
529  std::cout << " Adding Sensor Vol <" << pv->GetName() << " (" << m_SensorPV.size() << ")>" << std::endl;
530  }
531  }
532 
533  G4LogicalVolume* worldLogical = pv->GetLogicalVolume();
534 
535  if (Verbosity() > 0)
536  {
537  std::cout << " LV[" << i << "]: " << worldLogical->GetName() << std::endl;
538  }
539  FindSensor(worldLogical);
540  }
541 }