EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EtmDetector.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EtmDetector.cc
1 
2 #include <math.h>
3 
4 #include <TROOT.h>
5 #include <TColor.h>
6 
7 #include <EicToyModel.h>
8 #include <EtmPalette.h>
9 #include <EtmDetector.h>
10 
11 // Some arbitrary number for better visual detector separation; 1cm kind of works;
12 //#define _GAP_WIDTH_ ( 1.0)
13 
14 #ifdef _OPENCASCADE_
15 #define Printf Printf_opencascade
16 #include <gp_Pnt.hxx>
17 #include <TopoDS_Solid.hxx>
18 #include <BRepPrimAPI_MakeCylinder.hxx>
19 #include <BRep_Builder.hxx>
20 #include <TopoDS_Compound.hxx>
21 #include <TDocStd_Document.hxx>
22 #include <XCAFApp_Application.hxx>
23 #include <XCAFDoc_ShapeTool.hxx>
24 #include <XCAFDoc_ColorTool.hxx>
25 #include <XCAFDoc_DocumentTool.hxx>
26 #include <Quantity_Color.hxx>
27 #include <NCollection_Vector.hxx>
28 #include <STEPCAFControl_Writer.hxx>
29 
30 #include <BRepBuilderAPI_MakePolygon.hxx>
31 #include <BRepBuilderAPI_MakeFace.hxx>
32 #include <BRepPrimAPI_MakeRevol.hxx>
33 #include <BRepBuilderAPI_Sewing.hxx>
34 #include <BRepBuilderAPI_MakeSolid.hxx>
35 #include <BRepBuilderAPI.hxx>
36 #include <BRepLib_MakeEdge.hxx>
37 
38 #include <gp_Trsf.hxx>
39 #include <BRepBuilderAPI_Transform.hxx>
40 
41 #include <TopExp_Explorer.hxx>
42 #undef Printf
43 #endif
44 
45 #ifdef _ETM2GEANT_
46 #include "G4PVPlacement.hh"
47 #include "G4SystemOfUnits.hh"
48 #include "G4NistManager.hh"
49 #include "G4Box.hh"
50 #include "G4Tubs.hh"
51 #include "G4GenericPolycone.hh"
52 #include "G4Polyhedra.hh"
53 #include "G4LogicalVolume.hh"
54 #include "G4VisAttributes.hh"
55 #include "G4SubtractionSolid.hh"
56 #endif
57 
58 // ---------------------------------------------------------------------------------------
59 // ---------------------------------------------------------------------------------------
60 
61 EtmDetector::EtmDetector(const EtmDetectorStack *stack, const char *label, int fillcolor,
62  int linecolor, double length):
63  EtmPolygonGroup(fillcolor, linecolor),
64  mStack(stack), //mSegmentation(1),
65  mLength(length), mOffset(0.0), mActualDistance(0.0), mG4PhysicalVolume(0), mColorAlpha(-1.0)
66 {
68 
69  mLabel = label ? new TString(label) : 0;
70 } // EtmDetector::EtmDetector()
71 
72 bool EtmDetector::IsHighlighted( void ) const
73 {
74  auto eic = EicToyModel::Instance();
75 
76  //return (!eic->mUseDetectorHighlighting || mColorAlpha != -1.0);
77  return (!eic->mUseDetectorHighlighting || (eic->mUseDetectorHighlighting && mColorAlpha != -1.0));
78 } // EtmDetector::IsHighlighted()
79 
80 double EtmDetector::GetColorAlpha( void ) const {
81  auto eic = EicToyModel::Instance();
82 
83  return (eic->mUseDetectorHighlighting && mColorAlpha != -1.0 ? mColorAlpha : 1.0);
84 } // EtmDetector::GetColorAlpha()
85 
86 // ---------------------------------------------------------------------------------------
87 // ---------------------------------------------------------------------------------------
88 
89 // ---------------------------------------------------------------------------------------
90 
92 {
93  ClearPolygons();
94  //@@@for(auto &pgroup: _mPolygonGroups)
95  //@@@pgroup->Clear();
96 
97  //@@@for(auto child: mChildren)
98  //@@@child->ClearPolygonBuffer();
99 } // EtmDetector::ClearPolygonBuffer()
100 
101 // ---------------------------------------------------------------------------------------
102 // ---------------------------------------------------------------------------------------
103 
104 EtmPolygonGroup *EtmDetector::insert(const char *tag, int linecolor)
105 {
106  auto eic = EicToyModel::Instance();
107  int fillcolor = eic->Palette().GetColor(tag);
108  if (fillcolor == -1) {
109  printf("Tag '%s' does not exist!\n", tag);
110  return 0;
111  } //if
112 
113  auto group = new EtmPolygonGroup(fillcolor, linecolor);
114  mGraphicsElements.push_back(group);
115 
116  return group;
117 } // EtmDetector::insert()
118 
119 // ---------------------------------------------------------------------------------------
120 // ---------------------------------------------------------------------------------------
121 
122 void EtmDetector::RegisterFillColor(std::map<TString, int> &legend)
123 {
124  const TString *label = GetLabel();
125 
126  if (label && !label->IsNull())
127  legend[*label] = GetFillColor();
128 
129  //for(auto group: mGraphicsElements)
130  //group->RegisterFillColor(legend);
131  //@@@for(auto child: mChildren)
132  //@@@child->RegisterFillColor(legend);
133 } // EtmDetector::RegisterFillColor()
134 
135 // ---------------------------------------------------------------------------------------
136 
137 EtmDetector *EtmDetector::trim(double etamin, double etamax, bool preserve_modified)
138 {
139  // Sanity fixes first;
140  if (etamin < 0.0) etamin = 0.0; if (etamin > 1.0) etamin = 1.0;
141  if (etamax < 0.0) etamax = 0.0; if (etamax > 1.0) etamax = 1.0;
142 
143  if (!preserve_modified || mEdgeTrimming[0] == _DEFAULT_TRIM_VALUE_)
144  mEdgeTrimming[0] = etamin;
145  if (!preserve_modified || mEdgeTrimming[1] == _DEFAULT_TRIM_VALUE_)
146  mEdgeTrimming[1] = etamax;
147 
148  return this;
149 } // EtmDetector::trim()
150 
151 // ---------------------------------------------------------------------------------------
152 
154 {
155  auto eic = EicToyModel::Instance();
156 
157  if (refdet != mStack->get(0)) {
158  // A clear mistake on user side -> do nothing; FIXME: warning, please;
159  if (mStack == refdet->mStack) return this;
160 
161  // This also should not happen; FIXME: issue a warning;
162  if (mStack != eic->mid() && refdet->mStack != eic->mid() &&
163  !(mStack == eic->vtx() && refdet->mStack != eic->mid()))
164  return this;
165 
166  eic->AddBoundaryModificationRequest(this, refdet, toffset, how);
167  } else {
168  // Reference detector is not specified; one can try to guess;
169  if (mStack == eic->mid() || mStack == eic->vtx()) {
170  eic->AddBoundaryModificationRequest(this, eic->bck()->get(0), toffset, how);
171  eic->AddBoundaryModificationRequest(this, eic->fwd()->get(0), toffset, how);
172  }
173  else
174  eic->AddBoundaryModificationRequest(this, eic->mid()->get(0), toffset, how);
175  } //if
176 
177  return this;
178 } // EtmDetector::stretch()
179 
180 // ---------------------------------------------------------------------------------------
181 
182 // FIXME: well, this is crappy way of coding: give mDummyDetector of either my own stack
183 // or of some other stack in order to indicate a necessity of a special treatment;
184 
186 {
187  return stretch(mStack->get(0), tlength, how);
188 } // EtmDetector::stretch()
189 
191 {
192  return stretch(refstack->get(0), tlength, how);
193 } // EtmDetector::stretch()
194 
195 // ---------------------------------------------------------------------------------------
196 
197 void EtmDetector::Build( void )
198 {
199  auto eic = EicToyModel::Instance();
200  TVector2 ip = eic->GetIpLocation();
201 
203 
204  // If color=0, it is a space holder -> skip the actual drawing;
205  if (GetFillColor() == _GAP_COLOR_ || *GetLabel() == _MARKER_) return;
206 
207  //if (!ConditionChecker(distance - mLength/2 >= 0.0,
208  // "Not enough space for detector '%s'", GetLabel()->Data()))
209  //return 0.0;
210 
211  {
212  //if (*GetLabel() == _MARKER_) return;// 0.0;//-eic->SafetyClearance();
213  std::vector<TVector2> sides[2], vertices;
214  //+++double dgap = eic->VisualClearance() - eic->SafetyClearance();
215 
216  for(unsigned vh=0; vh<2; vh++)
217  for(unsigned tb=0; tb<2; tb++) {
218  for(unsigned bf=0; bf<2; bf++) {
219  auto boundary = mStack->EtaBoundary(bf);
220  double sign = bf == tb ? -1.0 : 1.0;
221  auto &vtxarr = boundary->mCrackNodes[vh][tb];
222  //std::vector<TVector2> qvtxarr;
223 
224  TVector2 axis(mStack->AlignmentAxis().X(), (tb ? -1.0 : 1.0)*mStack->AlignmentAxis().Y());
225 
226  {
227 #if _LATER_
228  std::vector<EtmLine2D> lines;
229  for(unsigned ivtx=1; ivtx<vtxarr.size(); ivtx++)
230  lines.push_back(EtmLine2D(vtxarr[ivtx-1], (vtxarr[ivtx]-vtxarr[ivtx-1]).Unit()));
231 
232  for(unsigned iln=0; iln<lines.size(); iln++) {
233  auto &line = lines[iln];
234 
235  // FIXME: need to account for crack width here!;
236  //if (line.N().X() && line.N().Y())
237  line.Shift(sign*(eic->SafetyClearance()/2)*line.N());
238  } //for iln
239 
240  qvtxarr.push_back(lines[0].X());
241  //for(unsigned iln=1; iln<lines.size(); iln++)
242  //assert(!lines[iln-1].IsParallel(lines[iln]));
243  for(unsigned iln=1; iln<lines.size(); iln++)
244  if (!lines[iln-1].IsParallel(lines[iln]))
245  qvtxarr.push_back(lines[iln-1].Cross(lines[iln]));
246  // FIXME: come on!;
247  qvtxarr.push_back(vtxarr[vtxarr.size()-1] +
248  (sign*eic->SafetyClearance()/2)*lines[lines.size()-1].N());
249 #endif
250 
251  // FIXME: 1E-6 stuff;
252  auto from = EtmLine2D(ip + (mActualDistance - length()/2 + 1E-6/* + dgap/2*/)*axis,
253  axis.Rotate(-M_PI/2));
254  auto to = EtmLine2D(ip + (mActualDistance + length()/2 - 1E-6/* - dgap/2*/)*axis,
255  axis.Rotate(-M_PI/2));
256  // Range check;
257  {
258  //double dmax = fabs(to.X().X());
259  bool central = mStack == eic->vtx() || mStack == eic->mid();
260 
261  // Yes, I know they are exclusive;
262  if ( central && fabs(to.X().Y()) > eic->GetIrRegionRadius()) return;
263  if (!central && fabs(to.X().X()) > eic->GetIrRegionLength()/2) return;
264  }
265 
266  //sides[mm] = eic->Band(qvtxarr, from, to);
267  sides[bf] = eic->Band( vtxarr, from, to);
268 
269  {
270  double trim = mEdgeTrimming[bf];
271  // FIXME: do it better (high |eta| edges are on the "wrong side" from
272  // the alignment axis);
273  if (mStack == eic->bck() && !bf && trim != 0.0 && trim != 1.0) trim = 1.0;
274  if (mStack == eic->fwd() && bf && trim != 0.0 && trim != 1.0) trim = 1.0;
275 
276  // Otherwise no need to do anything (full edge fill);
277  if (trim < 1.0) {
278  double dmin = 0.0, dmax = 0.0;
279  // FIXME: bottom half does not work well;
280  EtmLine2D thrust(ip, axis);
281 
282  for(unsigned ivtx=0; ivtx<sides[bf].size(); ivtx++) {
283  auto vtx = sides[bf][ivtx];
284  double d = thrust.Distance(vtx);
285  if (!ivtx || d < dmin) dmin = d;
286  if (!ivtx || d > dmax) dmax = d;
287  } //for vtx
288 
289  // FIXME: well, there whould be some floating point precision cutoff
290  // indicating that say two lines are parallel (even if their normal
291  // vectores are not exactly equal, as calculated);
292  if (fabs(dmax - dmin) > 1E-6) {
293  //double range = (dmax - dmin)*trim, limit = mm ? dmax - range : dmin + range;
294  double range = (dmax - dmin)*(tb ? 1.0 - trim : trim), limit = bf ? dmax - range : dmin + range;
295 
296  if (trim) {
297  EtmLine2D cut(ip + limit*thrust.N(), -sign*axis);
298  //EtmLine2D cut(ip + limit*thrust.N(), -1*axis);
299 
300  auto halves = EicToyModel::GetIntersection(sides[bf], cut);
301 
302  sides[bf].clear();
303  for(auto &vtx: halves.first)
304  sides[bf].push_back(vtx);
305  sides[bf].push_back(to.Cross(cut));
306  } else {
307  for(auto &vtx: sides[bf]) {
308  double d = thrust.Distance(vtx);
309  TVector2 along = vtx - d*thrust.N();
310 
311  vtx = along + limit*thrust.N();
312  } //for vtx
313  } //if
314  } //if
315  } //if
316  }
317  }
318  } //for bf
319 
320  {
321  // FIXME: optimize;
322  vertices = sides[0];
323 
324  for(unsigned ivtx=0; ivtx<sides[1].size(); ivtx++)
325  vertices.push_back(sides[1][sides[1].size()-1-ivtx]);
326 
327  //printf("%s -> %d %d %d\n", mLabel.Data(), sides[0].size(), sides[1].size(), vertices.size());
328  AddPolygon(vertices);
329  }
330  } //for tb
331  }
332 
333  // If a child detector exists, build it as well, recursively; not interested
334  // in the return value for the children;
335  //for(auto child: mChildren)
336  //child->Build(ip, axis, distance + child->mOffset, boundaries, tb, false);
337 } // EtmDetector::Build()
338 
339 // ---------------------------------------------------------------------------------------
340 
341 unsigned EtmDetector::GetOrder( void ) const
342 {
343  unsigned counter = 0;
344 
345  for(unsigned id=0; id<mStack->DetectorCount(); id++) {
346  auto det = mStack->GetDetector(id);
347 
348  if (*det->GetLabel() == *GetLabel()) {
349  if (det == this) return counter;
350 
351  counter++;
352  } //if
353  } //for id
354 
355  // Make commpiler happy; cannot happen;
356  return 0;
357 } // EtmDetector::GetOrder()
358 
359 // ---------------------------------------------------------------------------------------
360 // ---------------------------------------------------------------------------------------
361 
362 G4VPhysicalVolume *EtmDetector::PlaceG4Volume(G4LogicalVolume *world, const char *name)
363 {
364  // Can be a GAP or a MARKER detector;
365  if (!Polygons().size()) return 0;
366 
367  // Of course the volume should be placed only once;
369 
370 #ifdef _ETM2GEANT_
371  auto polygon = Polygons()[0];
372  unsigned dim = polygon.size();
373  double cff = cm/etm::cm, z[dim], r[dim];
374 
375  auto eic = EicToyModel::Instance();
376 
377  // Treat Z-offsets differently for central/vertex and endcap detectors;
378  TVector2 ip = eic->GetIpLocation();
379  double z0 = (mStack == eic->bck() || mStack == eic->fwd()) ?
380  (ip + mActualDistance*mStack->AlignmentAxis()).X() : ip.X();
381 
382  for(unsigned ivtx=0; ivtx<dim; ivtx++) {
383  auto &pt = polygon[ivtx];
384 
385  z[ivtx] = (pt.X() - z0)*cff;
386  r[ivtx] = pt.Y()*cff;
387  } //for vtx
388 
389  // NB: take order (like TRD 0/1/2) into account as well;
390  TString label;
391  if (name)
392  label = TString(name);
393  else
394  label.Form("%s.%s.%02d", mStack->GetLabel().Data(), GetLabel()->Data(), GetOrder());
395 
396  // Either a G4GenericPolycone or a G4Polyhedra; FIXME: for now can not mix them;
397  unsigned segmentation = eic->GetAzimuthalSegmentation();
398 
399  G4VSolid *vpol;
400  if (segmentation)
401  vpol = new G4Polyhedra (label.Data(), 0., 360.*deg, segmentation, dim, r, z);
402  else
403  vpol = new G4GenericPolycone(label.Data(), 0., 360.*deg, dim, r, z);
404 
405  {
406  auto vc = eic->GetVacuumChamber();
407  bool vc_cut_required = vc;
408 
409  // Figure out whether a cut is needed at all; these are mutually exclusive of
410  // course; fine, no staggered elseif's;
411  //#if 1//_LATER_
412  if (mStack == eic->vtx() && eic->vtx()->GetDetector(0) != this) vc_cut_required = false;
413  if (mStack == eic->mid() &&
414  // Either vertex stack is populated or this is not the first central stack
415  // detector -> under no sane configuration it can sit next to the beam pipe;
416  (eic->vtx()->DetectorCount() || (eic->mid()->GetDetector(0) != this)))
417  vc_cut_required = false;
418  //#else
419  // FIXME: keep it simple for the time being;
420  //if (mStack == eic->vtx() || mStack == eic->mid()) vc_cut_required = false;
421  //#endif
422  //vc_cut_required = false;
423 
424  // Check whether a vacuum chamber boolean cut is possible (and required);
425  // NB: CutThisSolid() may still return the same pointer if VGM is not compiled in;
426  // FIXME: a memory leak;
427  auto vout = vc && vc_cut_required ? vc->CutThisSolid(vpol, z0*cff) : vpol;
428 
429  // Air, what else can it be?;
430  {
431  auto air = G4NistManager::Instance()->FindOrBuildMaterial("G4_AIR");
432 
433  auto lout = new G4LogicalVolume(vout, air, label.Data(), 0, 0, 0);
434 
435  {
436  // Extract ROOT color attributes and assign them to GEANT volumes;
437  auto rcolor = gROOT->GetColor(GetFillColor());
438 
439  G4VisAttributes* visAtt = new G4VisAttributes();
440  //new G4VisAttributes(G4Colour(rcolor->GetRed(), rcolor->GetGreen(), rcolor->GetBlue()));
441  visAtt->SetColor(rcolor->GetRed(), rcolor->GetGreen(), rcolor->GetBlue(), 0.3);
442  visAtt->SetVisibility(true);
443  visAtt->SetForceWireframe(false);
444  visAtt->SetForceSolid(true);
445 
446  lout->SetVisAttributes(visAtt);
447  }
448 
450  new G4PVPlacement(0, G4ThreeVector(0, 0, z0*cff), lout, label.Data(), world, false, 0);
451 
452  return mG4PhysicalVolume;
453  }
454  }
455 #else
456  return 0;
457 #endif
458 } // EtmDetector::PlaceG4Volume()
459 
460 // ---------------------------------------------------------------------------------------
461 
462 G4VPhysicalVolume *EtmDetector::PlaceG4Volume(G4VPhysicalVolume *world, const char *name)
463 {
464 #ifdef _ETM2GEANT_
465  return PlaceG4Volume(world->GetLogicalVolume(), name);
466 #else
467  return 0;
468 #endif
469 } // EtmDetector::PlaceG4Volume()
470 
471 // ---------------------------------------------------------------------------------------
472 
473 G4VPhysicalVolume *EtmDetector::GetG4Volume( void )
474 {
475  // If the volume was defioned and placed already, just return the pointer;
477 
478  // Otherwise check, whether the world volume was defined; act accordingly;
479  auto eic = EicToyModel::Instance();
480 
481  auto world = eic->GetG4World();
482  if (!world) {
483  printf("\n\n World volume was not define yet, sorry!\n\n");
484  return 0;//exit(0);
485  } //if
486 
487  return PlaceG4Volume(world);
488 } // EtmDetector::GetG4Volume()
489 
490 // ---------------------------------------------------------------------------------------
491 // ---------------------------------------------------------------------------------------
492 
493 std::vector<std::pair<const TColor*, const TopoDS_Shape*> > EtmDetector::BuildCADmodel( void )
494 {
495  std::vector<std::pair<const TColor*, const TopoDS_Shape*> > shape;
496 
497 #ifdef _OPENCASCADE_
498  auto eic = EicToyModel::Instance();
499 
500  auto rcolor = gROOT->GetColor(GetFillColor());
501 
502  // Can be a GAP or a MARKER detector;
503  if (!Polygons().size()) return shape;
504 
505  auto polygon = Polygons()[0];
506  unsigned dim = polygon.size();
507  // For CAD export there is seemingly no need to play z0 back and forth games; also
508  // the conversion coefficient is 0.0; keep both for historic reasons;
509  double cff = 1.0, z0 = 0.0;
510 
511  // Create OpenCascade polygon;
512  auto fpoly = BRepBuilderAPI_MakePolygon();
513  for(auto const &pt: polygon)
514  fpoly.Add(gp_Pnt(pt.Y()*cff, 0.0, (pt.X()-z0)*cff));
515  fpoly.Close();
516 
517  // Build a 2D face out of it; then either create a revolution body or a polyhedra;
518  auto fface = BRepBuilderAPI_MakeFace(fpoly);
519  gp_Ax1 axis(gp_Pnt(0,0,0), gp_Dir(0,0,1));
520 
521  //std::vector<TopoDS_Shape> solid;
522  if (eic->GetAzimuthalSegmentation()) {
523  double alfa = 2*M_PI/eic->GetAzimuthalSegmentation();
524  auto rpoly = BRepBuilderAPI_MakePolygon();
525  std::vector<gp_Pnt> front, rear;
526  std::vector<TopoDS_Face> sides;
527 
528  for(auto const &pt: polygon) {
529  double xx = pt.Y()*cff, yy = 0.0, zz = (pt.X()-z0)*cff;
530  double xq = cos(alfa)*xx + sin(alfa)*yy;
531  double yq = -sin(alfa)*xx + cos(alfa)*yy;
532 
533  front.push_back(gp_Pnt(xx, yy, zz));
534  rear .push_back(gp_Pnt(xq, yq, zz));
535 
536  rpoly.Add(gp_Pnt(xq, yq, zz));
537  } //for ivtx
538  rpoly.Close();
539 
540  auto rface = BRepBuilderAPI_MakeFace(rpoly);
541 
542  for(unsigned ivtx=0; ivtx<dim; ivtx++) {
543  auto spoly = BRepBuilderAPI_MakePolygon();
544 
545  spoly.Add(front[ ivtx ]);
546  spoly.Add( rear[ ivtx ]);
547  spoly.Add( rear[(ivtx+1)%dim]);
548  spoly.Add(front[(ivtx+1)%dim]);
549  spoly.Close();
550 
551  sides.push_back(BRepBuilderAPI_MakeFace(spoly));
552  } //for ivtx
553 
554  BRepBuilderAPI_Sewing *sew = new BRepBuilderAPI_Sewing();
555  sew->Add(fface);
556  sew->Add(rface);
557  for(unsigned ivtx=0; ivtx<dim; ivtx++)
558  sew->Add(sides[ivtx]);
559 
560  sew->Perform();
561  auto result = sew->SewedShape();
562 
563  for (TopExp_Explorer itf(result,TopAbs_SHELL); itf.More(); itf.Next()) {
564  TopoDS_Shell &shell = (TopoDS_Shell&)itf.Current();
565 
566  // FIXME: perhaps not the most efficient way, but it works;
567  for(unsigned iq=0; iq<eic->GetAzimuthalSegmentation(); iq++) {
568  gp_Trsf rZ;
569  rZ.SetRotation(axis, alfa*iq);
570  BRepBuilderAPI_Transform rot(rZ);
571 
572  auto buffer = BRepBuilderAPI_MakeSolid(shell).Solid();
573  rot.Perform(buffer);
574  //solid.push_back(rot.ModifiedShape(buffer));
575  shape.push_back(std::make_pair(rcolor, new TopoDS_Shape(rot.ModifiedShape(buffer))));
576  } //for iq
577 
578  // FIXME: there is of course exactly one shape expected;
579  break;
580  } //for itf
581  }
582  else
583  shape.push_back(std::make_pair(rcolor, new TopoDS_Shape(BRepPrimAPI_MakeRevol(fface, axis))));
584 #endif
585 
586  return shape;
587 } // EtmDetector::BuildCADmodel()
588 
589 // ---------------------------------------------------------------------------------------
590 
591 void EtmDetector::Export(const char *fname)
592 {
594 } // EtmDetector::Export()
595 
596 // ---------------------------------------------------------------------------------------
597 // ---------------------------------------------------------------------------------------
598