Geant4 11.2.2
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4Polyhedra.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26// Implementation of G4Polyhedra, a CSG polyhedra,
27// as an inherited class of G4VCSGfaceted.
28//
29// Utility classes:
30// * G4EnclosingCylinder: decided a quick check of geometry would be a
31// good idea (for CPU speed). If the quick check fails, the regular
32// full-blown G4VCSGfaceted version is invoked.
33// * G4ReduciblePolygon: Really meant as a check of input parameters,
34// this utility class also "converts" the GEANT3-like PGON/PCON
35// arguments into the newer ones.
36// Both these classes are implemented outside this file because they are
37// shared with G4Polycone.
38//
39// Author: David C. Williams ([email protected])
40// --------------------------------------------------------------------
41
42#include "G4Polyhedra.hh"
43
44#if !defined(G4GEOM_USE_UPOLYHEDRA)
45
46#include "G4PolyhedraSide.hh"
47#include "G4PolyPhiFace.hh"
48
49#include "G4GeomTools.hh"
50#include "G4VoxelLimits.hh"
51#include "G4AffineTransform.hh"
52#include "G4BoundingEnvelope.hh"
53
54#include "G4QuickRand.hh"
55
57#include "G4ReduciblePolygon.hh"
59
60namespace
61{
62 G4Mutex surface_elementsMutex = G4MUTEX_INITIALIZER;
63}
64
65using namespace CLHEP;
66
67// Constructor (GEANT3 style parameters)
68//
69// GEANT3 PGON radii are specified in the distance to the norm of each face.
70//
72 G4double phiStart,
73 G4double thePhiTotal,
74 G4int theNumSide,
75 G4int numZPlanes,
76 const G4double zPlane[],
77 const G4double rInner[],
78 const G4double rOuter[] )
79 : G4VCSGfaceted( name )
80{
81 if (theNumSide <= 0)
82 {
83 std::ostringstream message;
84 message << "Solid must have at least one side - " << GetName() << G4endl
85 << " No sides specified !";
86 G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
87 FatalErrorInArgument, message);
88 }
89
90 //
91 // Calculate conversion factor from G3 radius to G4 radius
92 //
93 G4double phiTotal = thePhiTotal;
94 if ( (phiTotal <=0) || (phiTotal >= twopi*(1-DBL_EPSILON)) )
95 { phiTotal = twopi; }
96 G4double convertRad = std::cos(0.5*phiTotal/theNumSide);
97
98 //
99 // Some historical stuff
100 //
102
103 original_parameters->numSide = theNumSide;
106 original_parameters->Num_z_planes = numZPlanes;
107 original_parameters->Z_values = new G4double[numZPlanes];
108 original_parameters->Rmin = new G4double[numZPlanes];
109 original_parameters->Rmax = new G4double[numZPlanes];
110
111 for (G4int i=0; i<numZPlanes; ++i)
112 {
113 if (( i < numZPlanes-1) && ( zPlane[i] == zPlane[i+1] ))
114 {
115 if( (rInner[i] > rOuter[i+1])
116 ||(rInner[i+1] > rOuter[i]) )
117 {
118 DumpInfo();
119 std::ostringstream message;
120 message << "Cannot create a Polyhedra with no contiguous segments."
121 << G4endl
122 << " Segments are not contiguous !" << G4endl
123 << " rMin[" << i << "] = " << rInner[i]
124 << " -- rMax[" << i+1 << "] = " << rOuter[i+1] << G4endl
125 << " rMin[" << i+1 << "] = " << rInner[i+1]
126 << " -- rMax[" << i << "] = " << rOuter[i];
127 G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
128 FatalErrorInArgument, message);
129 }
130 }
131 original_parameters->Z_values[i] = zPlane[i];
132 original_parameters->Rmin[i] = rInner[i]/convertRad;
133 original_parameters->Rmax[i] = rOuter[i]/convertRad;
134 }
135
136
137 //
138 // Build RZ polygon using special PCON/PGON GEANT3 constructor
139 //
140 auto rz = new G4ReduciblePolygon( rInner, rOuter, zPlane, numZPlanes );
141 rz->ScaleA( 1/convertRad );
142
143 //
144 // Do the real work
145 //
146 Create( phiStart, phiTotal, theNumSide, rz );
147
148 delete rz;
149}
150
151// Constructor (generic parameters)
152//
154 G4double phiStart,
155 G4double phiTotal,
156 G4int theNumSide,
157 G4int numRZ,
158 const G4double r[],
159 const G4double z[] )
160 : G4VCSGfaceted( name ), genericPgon(true)
161{
162 if (theNumSide <= 0)
163 {
164 std::ostringstream message;
165 message << "Solid must have at least one side - " << GetName() << G4endl
166 << " No sides specified !";
167 G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
168 FatalErrorInArgument, message);
169 }
170
171 auto rz = new G4ReduciblePolygon( r, z, numRZ );
172
173 Create( phiStart, phiTotal, theNumSide, rz );
174
175 // Set original_parameters struct for consistency
176 //
178
179 delete rz;
180}
181
182// Create
183//
184// Generic create routine, called by each constructor
185// after conversion of arguments
186//
188 G4double phiTotal,
189 G4int theNumSide,
191{
192 //
193 // Perform checks of rz values
194 //
195 if (rz->Amin() < 0.0)
196 {
197 std::ostringstream message;
198 message << "Illegal input parameters - " << GetName() << G4endl
199 << " All R values must be >= 0 !";
200 G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
201 FatalErrorInArgument, message);
202 }
203
204 G4double rzArea = rz->Area();
205 if (rzArea < -kCarTolerance)
206 {
207 rz->ReverseOrder();
208 }
209 else if (rzArea < kCarTolerance)
210 {
211 std::ostringstream message;
212 message << "Illegal input parameters - " << GetName() << G4endl
213 << " R/Z cross section is zero or near zero: " << rzArea;
214 G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
215 FatalErrorInArgument, message);
216 }
217
220 {
221 std::ostringstream message;
222 message << "Illegal input parameters - " << GetName() << G4endl
223 << " Too few unique R/Z values !";
224 G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
225 FatalErrorInArgument, message);
226 }
227
228 if (rz->CrossesItself( 1/kInfinity ))
229 {
230 std::ostringstream message;
231 message << "Illegal input parameters - " << GetName() << G4endl
232 << " R/Z segments cross !";
233 G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
234 FatalErrorInArgument, message);
235 }
236
237 numCorner = rz->NumVertices();
238
239
240 startPhi = phiStart;
241 while( startPhi < 0 ) // Loop checking, 13.08.2015, G.Cosmo
242 startPhi += twopi;
243 //
244 // Phi opening? Account for some possible roundoff, and interpret
245 // nonsense value as representing no phi opening
246 //
247 if ( (phiTotal <= 0) || (phiTotal > twopi*(1-DBL_EPSILON)) )
248 {
249 phiIsOpen = false;
250 endPhi = startPhi + twopi;
251 }
252 else
253 {
254 phiIsOpen = true;
255 endPhi = startPhi + phiTotal;
256 }
257
258 //
259 // Save number sides
260 //
261 numSide = theNumSide;
262
263 //
264 // Allocate corner array.
265 //
267
268 //
269 // Copy corners
270 //
272
274 iterRZ.Begin();
275 do // Loop checking, 13.08.2015, G.Cosmo
276 {
277 next->r = iterRZ.GetA();
278 next->z = iterRZ.GetB();
279 } while( ++next, iterRZ.Next() );
280
281 //
282 // Allocate face pointer array
283 //
285 faces = new G4VCSGface*[numFace];
286
287 //
288 // Construct side faces
289 //
290 // To do so properly, we need to keep track of four successive RZ
291 // corners.
292 //
293 // But! Don't construct a face if both points are at zero radius!
294 //
295 G4PolyhedraSideRZ* corner = corners,
296 * prev = corners + numCorner-1,
297 * nextNext;
298 G4VCSGface** face = faces;
299 do // Loop checking, 13.08.2015, G.Cosmo
300 {
301 next = corner+1;
302 if (next >= corners+numCorner) next = corners;
303 nextNext = next+1;
304 if (nextNext >= corners+numCorner) nextNext = corners;
305
306 if (corner->r < 1/kInfinity && next->r < 1/kInfinity) continue;
307/*
308 // We must decide here if we can dare declare one of our faces
309 // as having a "valid" normal (i.e. allBehind = true). This
310 // is never possible if the face faces "inward" in r *unless*
311 // we have only one side
312 //
313 G4bool allBehind;
314 if ((corner->z > next->z) && (numSide > 1))
315 {
316 allBehind = false;
317 }
318 else
319 {
320 //
321 // Otherwise, it is only true if the line passing
322 // through the two points of the segment do not
323 // split the r/z cross section
324 //
325 allBehind = !rz->BisectedBy( corner->r, corner->z,
326 next->r, next->z, kCarTolerance );
327 }
328*/
329 *face++ = new G4PolyhedraSide( prev, corner, next, nextNext,
331 } while( prev=corner, corner=next, corner > corners );
332
333 if (phiIsOpen)
334 {
335 //
336 // Construct phi open edges
337 //
338 *face++ = new G4PolyPhiFace( rz, startPhi, phiTotal/numSide, endPhi );
339 *face++ = new G4PolyPhiFace( rz, endPhi, phiTotal/numSide, startPhi );
340 }
341
342 //
343 // We might have dropped a face or two: recalculate numFace
344 //
345 numFace = (G4int)(face-faces);
346
347 //
348 // Make enclosingCylinder
349 //
351 new G4EnclosingCylinder( rz, phiIsOpen, phiStart, phiTotal );
352}
353
354// Fake default constructor - sets only member data and allocates memory
355// for usage restricted to object persistency.
356//
358 : G4VCSGfaceted(a), startPhi(0.), endPhi(0.)
359{
360}
361
362// Destructor
363//
365{
366 delete [] corners;
367 delete original_parameters;
368 delete enclosingCylinder;
369 delete fElements;
370 delete fpPolyhedron;
371 corners = nullptr;
372 original_parameters = nullptr;
373 enclosingCylinder = nullptr;
374 fElements = nullptr;
375 fpPolyhedron = nullptr;
376}
377
378// Copy constructor
379//
381 : G4VCSGfaceted( source )
382{
383 CopyStuff( source );
384}
385
386// Assignment operator
387//
389{
390 if (this == &source) return *this;
391
392 G4VCSGfaceted::operator=( source );
393
394 delete [] corners;
395 delete original_parameters;
396 delete enclosingCylinder;
397
398 CopyStuff( source );
399
400 return *this;
401}
402
403// CopyStuff
404//
406{
407 //
408 // Simple stuff
409 //
410 numSide = source.numSide;
411 startPhi = source.startPhi;
412 endPhi = source.endPhi;
413 phiIsOpen = source.phiIsOpen;
414 numCorner = source.numCorner;
415 genericPgon= source.genericPgon;
416
417 //
418 // The corner array
419 //
421
423 * sourceCorn = source.corners;
424 do // Loop checking, 13.08.2015, G.Cosmo
425 {
426 *corn = *sourceCorn;
427 } while( ++sourceCorn, ++corn < corners+numCorner );
428
429 //
430 // Original parameters
431 //
432 if (source.original_parameters != nullptr)
433 {
436 }
437
438 //
439 // Enclosing cylinder
440 //
442
443 //
444 // Surface elements
445 //
446 delete fElements;
447 fElements = nullptr;
448
449 //
450 // Polyhedron
451 //
452 fRebuildPolyhedron = false;
453 delete fpPolyhedron;
454 fpPolyhedron = nullptr;
455}
456
457// Reset
458//
459// Recalculates and reshapes the solid, given pre-assigned scaled
460// original_parameters.
461//
463{
464 if (genericPgon)
465 {
466 std::ostringstream message;
467 message << "Solid " << GetName() << " built using generic construct."
468 << G4endl << "Not applicable to the generic construct !";
469 G4Exception("G4Polyhedra::Reset()", "GeomSolids1001",
470 JustWarning, message, "Parameters NOT resetted.");
471 return true;
472 }
473
474 //
475 // Clear old setup
476 //
478 delete [] corners;
479 delete enclosingCylinder;
480 delete fElements;
481 corners = nullptr;
482 fElements = nullptr;
483 enclosingCylinder = nullptr;
484
485 //
486 // Rebuild polyhedra
487 //
495 delete rz;
496
497 return false;
498}
499
500// Inside
501//
502// This is an override of G4VCSGfaceted::Inside, created in order
503// to speed things up by first checking with G4EnclosingCylinder.
504//
506{
507 //
508 // Quick test
509 //
511
512 //
513 // Long answer
514 //
515 return G4VCSGfaceted::Inside(p);
516}
517
518// DistanceToIn
519//
520// This is an override of G4VCSGfaceted::Inside, created in order
521// to speed things up by first checking with G4EnclosingCylinder.
522//
524 const G4ThreeVector& v ) const
525{
526 //
527 // Quick test
528 //
530 return kInfinity;
531
532 //
533 // Long answer
534 //
535 return G4VCSGfaceted::DistanceToIn( p, v );
536}
537
538// DistanceToIn
539//
544
545// Get bounding box
546//
548 G4ThreeVector& pMax) const
549{
550 G4double rmin = kInfinity, rmax = -kInfinity;
551 G4double zmin = kInfinity, zmax = -kInfinity;
552 for (G4int i=0; i<GetNumRZCorner(); ++i)
553 {
554 G4PolyhedraSideRZ corner = GetCorner(i);
555 if (corner.r < rmin) rmin = corner.r;
556 if (corner.r > rmax) rmax = corner.r;
557 if (corner.z < zmin) zmin = corner.z;
558 if (corner.z > zmax) zmax = corner.z;
559 }
560
561 G4double sphi = GetStartPhi();
562 G4double ephi = GetEndPhi();
563 G4double dphi = IsOpen() ? ephi-sphi : twopi;
564 G4int ksteps = GetNumSide();
565 G4double astep = dphi/ksteps;
566 G4double sinStep = std::sin(astep);
567 G4double cosStep = std::cos(astep);
568
569 G4double sinCur = GetSinStartPhi();
570 G4double cosCur = GetCosStartPhi();
571 if (!IsOpen()) rmin = 0.;
572 G4double xmin = rmin*cosCur, xmax = xmin;
573 G4double ymin = rmin*sinCur, ymax = ymin;
574 for (G4int k=0; k<ksteps+1; ++k)
575 {
576 G4double x = rmax*cosCur;
577 if (x < xmin) xmin = x;
578 if (x > xmax) xmax = x;
579 G4double y = rmax*sinCur;
580 if (y < ymin) ymin = y;
581 if (y > ymax) ymax = y;
582 if (rmin > 0)
583 {
584 G4double xx = rmin*cosCur;
585 if (xx < xmin) xmin = xx;
586 if (xx > xmax) xmax = xx;
587 G4double yy = rmin*sinCur;
588 if (yy < ymin) ymin = yy;
589 if (yy > ymax) ymax = yy;
590 }
591 G4double sinTmp = sinCur;
592 sinCur = sinCur*cosStep + cosCur*sinStep;
593 cosCur = cosCur*cosStep - sinTmp*sinStep;
594 }
595 pMin.set(xmin,ymin,zmin);
596 pMax.set(xmax,ymax,zmax);
597
598 // Check correctness of the bounding box
599 //
600 if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
601 {
602 std::ostringstream message;
603 message << "Bad bounding box (min >= max) for solid: "
604 << GetName() << " !"
605 << "\npMin = " << pMin
606 << "\npMax = " << pMax;
607 G4Exception("G4Polyhedra::BoundingLimits()", "GeomMgt0001",
608 JustWarning, message);
609 DumpInfo();
610 }
611}
612
613// Calculate extent under transform and specified limit
614//
616 const G4VoxelLimits& pVoxelLimit,
617 const G4AffineTransform& pTransform,
618 G4double& pMin, G4double& pMax) const
619{
620 G4ThreeVector bmin, bmax;
621 G4bool exist;
622
623 // Check bounding box (bbox)
624 //
625 BoundingLimits(bmin,bmax);
626 G4BoundingEnvelope bbox(bmin,bmax);
627#ifdef G4BBOX_EXTENT
628 return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
629#endif
630 if (bbox.BoundingBoxVsVoxelLimits(pAxis,pVoxelLimit,pTransform,pMin,pMax))
631 {
632 return exist = pMin < pMax;
633 }
634
635 // To find the extent, RZ contour of the polycone is subdivided
636 // in triangles. The extent is calculated as cumulative extent of
637 // all sub-polycones formed by rotation of triangles around Z
638 //
639 G4TwoVectorList contourRZ;
640 G4TwoVectorList triangles;
641 std::vector<G4int> iout;
642 G4double eminlim = pVoxelLimit.GetMinExtent(pAxis);
643 G4double emaxlim = pVoxelLimit.GetMaxExtent(pAxis);
644
645 // get RZ contour, ensure anticlockwise order of corners
646 for (G4int i=0; i<GetNumRZCorner(); ++i)
647 {
648 G4PolyhedraSideRZ corner = GetCorner(i);
649 contourRZ.emplace_back(corner.r,corner.z);
650 }
652 G4double area = G4GeomTools::PolygonArea(contourRZ);
653 if (area < 0.) std::reverse(contourRZ.begin(),contourRZ.end());
654
655 // triangulate RZ countour
656 if (!G4GeomTools::TriangulatePolygon(contourRZ,triangles))
657 {
658 std::ostringstream message;
659 message << "Triangulation of RZ contour has failed for solid: "
660 << GetName() << " !"
661 << "\nExtent has been calculated using boundary box";
662 G4Exception("G4Polyhedra::CalculateExtent()",
663 "GeomMgt1002",JustWarning,message);
664 return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
665 }
666
667 // set trigonometric values
668 G4double sphi = GetStartPhi();
669 G4double ephi = GetEndPhi();
670 G4double dphi = IsOpen() ? ephi-sphi : twopi;
671 G4int ksteps = GetNumSide();
672 G4double astep = dphi/ksteps;
673 G4double sinStep = std::sin(astep);
674 G4double cosStep = std::cos(astep);
675 G4double sinStart = GetSinStartPhi();
676 G4double cosStart = GetCosStartPhi();
677
678 // allocate vector lists
679 std::vector<const G4ThreeVectorList *> polygons;
680 polygons.resize(ksteps+1);
681 for (G4int k=0; k<ksteps+1; ++k)
682 {
683 polygons[k] = new G4ThreeVectorList(3);
684 }
685
686 // main loop along triangles
687 pMin = kInfinity;
688 pMax = -kInfinity;
689 G4int ntria = (G4int)triangles.size()/3;
690 for (G4int i=0; i<ntria; ++i)
691 {
692 G4double sinCur = sinStart;
693 G4double cosCur = cosStart;
694 G4int i3 = i*3;
695 for (G4int k=0; k<ksteps+1; ++k) // rotate triangle
696 {
697 auto ptr = const_cast<G4ThreeVectorList*>(polygons[k]);
698 auto iter = ptr->begin();
699 iter->set(triangles[i3+0].x()*cosCur,
700 triangles[i3+0].x()*sinCur,
701 triangles[i3+0].y());
702 iter++;
703 iter->set(triangles[i3+1].x()*cosCur,
704 triangles[i3+1].x()*sinCur,
705 triangles[i3+1].y());
706 iter++;
707 iter->set(triangles[i3+2].x()*cosCur,
708 triangles[i3+2].x()*sinCur,
709 triangles[i3+2].y());
710
711 G4double sinTmp = sinCur;
712 sinCur = sinCur*cosStep + cosCur*sinStep;
713 cosCur = cosCur*cosStep - sinTmp*sinStep;
714 }
715
716 // set sub-envelope and adjust extent
717 G4double emin,emax;
718 G4BoundingEnvelope benv(polygons);
719 if (!benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,emin,emax)) continue;
720 if (emin < pMin) pMin = emin;
721 if (emax > pMax) pMax = emax;
722 if (eminlim > pMin && emaxlim < pMax) break; // max possible extent
723 }
724 // free memory
725 for (G4int k=0; k<ksteps+1; ++k) { delete polygons[k]; polygons[k]=nullptr;}
726 return (pMin < pMax);
727}
728
729// ComputeDimensions
730//
732 const G4int n,
733 const G4VPhysicalVolume* pRep )
734{
735 p->ComputeDimensions(*this,n,pRep);
736}
737
738// GetEntityType
739//
741{
742 return {"G4Polyhedra"};
743}
744
745// Make a clone of the object
746//
748{
749 return new G4Polyhedra(*this);
750}
751
752// Stream object contents to an output stream
753//
754std::ostream& G4Polyhedra::StreamInfo( std::ostream& os ) const
755{
756 G4long oldprc = os.precision(16);
757 os << "-----------------------------------------------------------\n"
758 << " *** Dump for solid - " << GetName() << " ***\n"
759 << " ===================================================\n"
760 << " Solid type: G4Polyhedra\n"
761 << " Parameters: \n"
762 << " starting phi angle : " << startPhi/degree << " degrees \n"
763 << " ending phi angle : " << endPhi/degree << " degrees \n"
764 << " number of sides : " << numSide << " \n";
765 G4int i=0;
766 if (!genericPgon)
767 {
769 os << " number of Z planes: " << numPlanes << "\n"
770 << " Z values: \n";
771 for (i=0; i<numPlanes; ++i)
772 {
773 os << " Z plane " << i << ": "
774 << original_parameters->Z_values[i] << "\n";
775 }
776 os << " Tangent distances to inner surface (Rmin): \n";
777 for (i=0; i<numPlanes; ++i)
778 {
779 os << " Z plane " << i << ": "
780 << original_parameters->Rmin[i] << "\n";
781 }
782 os << " Tangent distances to outer surface (Rmax): \n";
783 for (i=0; i<numPlanes; ++i)
784 {
785 os << " Z plane " << i << ": "
786 << original_parameters->Rmax[i] << "\n";
787 }
788 }
789 os << " number of RZ points: " << numCorner << "\n"
790 << " RZ values (corners): \n";
791 for (i=0; i<numCorner; ++i)
792 {
793 os << " "
794 << corners[i].r << ", " << corners[i].z << "\n";
795 }
796 os << "-----------------------------------------------------------\n";
797 os.precision(oldprc);
798
799 return os;
800}
801
802//////////////////////////////////////////////////////////////////////////
803//
804// Return volume
805
807{
808 if (fCubicVolume == 0.)
809 {
810 G4double total = 0.;
811 G4int nrz = GetNumRZCorner();
812 G4PolyhedraSideRZ a = GetCorner(nrz - 1);
813 for (G4int i=0; i<nrz; ++i)
814 {
816 total += (b.r*b.r + b.r*a.r + a.r*a.r)*(b.z - a.z);
817 a = b;
818 }
819 fCubicVolume = std::abs(total)*
820 std::sin((GetEndPhi() - GetStartPhi())/GetNumSide())*GetNumSide()/6.;
821 }
822 return fCubicVolume;
823}
824
825//////////////////////////////////////////////////////////////////////////
826//
827// Return surface area
828
830{
831 if (fSurfaceArea == 0.)
832 {
833 G4double total = 0.;
834 G4int nrz = GetNumRZCorner();
835 if (IsOpen())
836 {
837 G4PolyhedraSideRZ a = GetCorner(nrz - 1);
838 for (G4int i=0; i<nrz; ++i)
839 {
841 total += a.r*b.z - a.z*b.r;
842 a = b;
843 }
844 total = std::abs(total);
845 }
847 G4double cosa = std::cos(alp);
848 G4double sina = std::sin(alp);
849 G4PolyhedraSideRZ a = GetCorner(nrz - 1);
850 for (G4int i=0; i<nrz; ++i)
851 {
853 G4ThreeVector p1(a.r, 0, a.z);
854 G4ThreeVector p2(a.r*cosa, a.r*sina, a.z);
855 G4ThreeVector p3(b.r*cosa, b.r*sina, b.z);
856 G4ThreeVector p4(b.r, 0, b.z);
857 total += GetNumSide()*(G4GeomTools::QuadAreaNormal(p1, p2, p3, p4)).mag();
858 a = b;
859 }
860 fSurfaceArea = total;
861 }
862 return fSurfaceArea;
863}
864
865//////////////////////////////////////////////////////////////////////////
866//
867// Set vector of surface elements, auxiliary method for sampling
868// random points on surface
869
871{
872 fElements = new std::vector<G4Polyhedra::surface_element>;
873 G4double total = 0.;
874 G4int nrz = GetNumRZCorner();
875
876 // set lateral surface elements
877 G4double dphi = (GetEndPhi() - GetStartPhi())/GetNumSide();
878 G4double cosa = std::cos(dphi);
879 G4double sina = std::sin(dphi);
880 G4int ia = nrz - 1;
881 for (G4int ib=0; ib<nrz; ++ib)
882 {
886 selem.i0 = ia;
887 selem.i1 = ib;
888 ia = ib;
889 if (a.r == 0. && b.r == 0.) continue;
890 G4ThreeVector p1(a.r, 0, a.z);
891 G4ThreeVector p2(a.r*cosa, a.r*sina, a.z);
892 G4ThreeVector p3(b.r*cosa, b.r*sina, b.z);
893 G4ThreeVector p4(b.r, 0, b.z);
894 if (a.r > 0.)
895 {
896 selem.i2 = -1;
897 total += GetNumSide()*(G4GeomTools::TriangleAreaNormal(p1, p2, p3)).mag();
898 selem.area = total;
899 fElements->push_back(selem);
900 }
901 if (b.r > 0.)
902 {
903 selem.i2 = -2;
904 total += GetNumSide()*(G4GeomTools::TriangleAreaNormal(p1, p3, p4)).mag();
905 selem.area = total;
906 fElements->push_back(selem);
907 }
908 }
909
910 // set elements for phi cuts
911 if (IsOpen())
912 {
913 G4TwoVectorList contourRZ;
914 std::vector<G4int> triangles;
915 for (G4int i=0; i<nrz; ++i)
916 {
917 G4PolyhedraSideRZ corner = GetCorner(i);
918 contourRZ.emplace_back(corner.r, corner.z);
919 }
920 G4GeomTools::TriangulatePolygon(contourRZ, triangles);
921 auto ntria = (G4int)triangles.size();
922 for (G4int i=0; i<ntria; i+=3)
923 {
925 selem.i0 = triangles[i];
926 selem.i1 = triangles[i+1];
927 selem.i2 = triangles[i+2];
928 G4PolyhedraSideRZ a = GetCorner(selem.i0);
929 G4PolyhedraSideRZ b = GetCorner(selem.i1);
930 G4PolyhedraSideRZ c = GetCorner(selem.i2);
931 G4double stria =
932 std::abs(G4GeomTools::TriangleArea(a.r, a.z, b.r, b.z, c.r, c.z));
933 total += stria;
934 selem.area = total;
935 fElements->push_back(selem); // start phi
936 total += stria;
937 selem.area = total;
938 selem.i0 += nrz;
939 fElements->push_back(selem); // end phi
940 }
941 }
942}
943
944//////////////////////////////////////////////////////////////////////////
945//
946// Generate random point on surface
947
949{
950 // Set surface elements
951 if (fElements == nullptr)
952 {
953 G4AutoLock l(&surface_elementsMutex);
955 l.unlock();
956 }
957
958 // Select surface element
960 selem = fElements->back();
961 G4double select = selem.area*G4QuickRand();
962 auto it = std::lower_bound(fElements->begin(), fElements->end(), select,
963 [](const G4Polyhedra::surface_element& x, G4double val)
964 -> G4bool { return x.area < val; });
965
966 // Generate random point
967 G4double x = 0, y = 0, z = 0;
968 G4double u = G4QuickRand();
969 G4double v = G4QuickRand();
970 if (u + v > 1.) { u = 1. - u; v = 1. - v; }
971 G4int i0 = (*it).i0;
972 G4int i1 = (*it).i1;
973 G4int i2 = (*it).i2;
974 if (i2 < 0) // lateral surface
975 {
976 // sample point
977 G4int nside = GetNumSide();
978 G4double dphi = (GetEndPhi() - GetStartPhi())/nside;
979 G4double cosa = std::cos(dphi);
980 G4double sina = std::sin(dphi);
983 G4ThreeVector p0(a.r, 0, a.z);
984 G4ThreeVector p1(b.r, 0, b.z);
985 G4ThreeVector p2(b.r*cosa, b.r*sina, b.z);
986 if (i2 == -1) p1.set(a.r*cosa, a.r*sina, a.z);
987 p0 += (p1 - p0)*u + (p2 - p0)*v;
988 // find selected side and rotate point
989 G4double scurr = (*it).area;
990 G4double sprev = (it == fElements->begin()) ? 0. : (*(--it)).area;
991 G4int iside = nside*(select - sprev)/(scurr - sprev);
992 if (iside == 0 && GetStartPhi() == 0.)
993 {
994 x = p0.x();
995 y = p0.y();
996 z = p0.z();
997 }
998 else
999 {
1000 if (iside == nside) --iside; // iside must be less then nside
1001 G4double phi = iside*dphi + GetStartPhi();
1002 G4double cosphi = std::cos(phi);
1003 G4double sinphi = std::sin(phi);
1004 x = p0.x()*cosphi - p0.y()*sinphi;
1005 y = p0.x()*sinphi + p0.y()*cosphi;
1006 z = p0.z();
1007 }
1008 }
1009 else // phi cut
1010 {
1011 G4int nrz = GetNumRZCorner();
1012 G4double phi = (i0 < nrz) ? GetStartPhi() : GetEndPhi();
1013 if (i0 >= nrz) { i0 -= nrz; }
1017 G4double r = (p1.r - p0.r)*u + (p2.r - p0.r)*v + p0.r;
1018 x = r*std::cos(phi);
1019 y = r*std::sin(phi);
1020 z = (p1.z - p0.z)*u + (p2.z - p0.z)*v + p0.z;
1021 }
1022 return {x, y, z};
1023}
1024
1025//////////////////////////////////////////////////////////////////////////
1026//
1027// CreatePolyhedron
1028
1030{
1031 std::vector<G4TwoVector> rz(numCorner);
1032 for (G4int i = 0; i < numCorner; ++i)
1033 rz[i].set(corners[i].r, corners[i].z);
1034 return new G4PolyhedronPgon(startPhi, endPhi - startPhi, numSide, rz);
1035}
1036
1037// SetOriginalParameters
1038//
1040{
1041 G4int numPlanes = numCorner;
1042 G4bool isConvertible = true;
1043 G4double Zmax=rz->Bmax();
1044 rz->StartWithZMin();
1045
1046 // Prepare vectors for storage
1047 //
1048 std::vector<G4double> Z;
1049 std::vector<G4double> Rmin;
1050 std::vector<G4double> Rmax;
1051
1052 G4int countPlanes=1;
1053 G4int icurr=0;
1054 G4int icurl=0;
1055
1056 // first plane Z=Z[0]
1057 //
1058 Z.push_back(corners[0].z);
1059 G4double Zprev=Z[0];
1060 if (Zprev == corners[1].z)
1061 {
1062 Rmin.push_back(corners[0].r);
1063 Rmax.push_back (corners[1].r);icurr=1;
1064 }
1065 else if (Zprev == corners[numPlanes-1].z)
1066 {
1067 Rmin.push_back(corners[numPlanes-1].r);
1068 Rmax.push_back (corners[0].r);
1069 icurl=numPlanes-1;
1070 }
1071 else
1072 {
1073 Rmin.push_back(corners[0].r);
1074 Rmax.push_back (corners[0].r);
1075 }
1076
1077 // next planes until last
1078 //
1079 G4int inextr=0, inextl=0;
1080 for (G4int i=0; i < numPlanes-2; ++i)
1081 {
1082 inextr=1+icurr;
1083 inextl=(icurl <= 0)? numPlanes-1 : icurl-1;
1084
1085 if((corners[inextr].z >= Zmax) & (corners[inextl].z >= Zmax)) { break; }
1086
1087 G4double Zleft = corners[inextl].z;
1088 G4double Zright = corners[inextr].z;
1089 if(Zright>Zleft)
1090 {
1091 Z.push_back(Zleft);
1092 countPlanes++;
1093 G4double difZr=corners[inextr].z - corners[icurr].z;
1094 G4double difZl=corners[inextl].z - corners[icurl].z;
1095
1096 if(std::fabs(difZl) < kCarTolerance)
1097 {
1098 if(std::fabs(difZr) < kCarTolerance)
1099 {
1100 Rmin.push_back(corners[inextl].r);
1101 Rmax.push_back(corners[icurr].r);
1102 }
1103 else
1104 {
1105 Rmin.push_back(corners[inextl].r);
1106 Rmax.push_back(corners[icurr].r + (Zleft-corners[icurr].z)/difZr
1107 *(corners[inextr].r - corners[icurr].r));
1108 }
1109 }
1110 else if (difZl >= kCarTolerance)
1111 {
1112 if(std::fabs(difZr) < kCarTolerance)
1113 {
1114 Rmin.push_back(corners[icurl].r);
1115 Rmax.push_back(corners[icurr].r);
1116 }
1117 else
1118 {
1119 Rmin.push_back(corners[icurl].r);
1120 Rmax.push_back(corners[icurr].r + (Zleft-corners[icurr].z)/difZr
1121 *(corners[inextr].r - corners[icurr].r));
1122 }
1123 }
1124 else
1125 {
1126 isConvertible=false; break;
1127 }
1128 icurl=(icurl == 0)? numPlanes-1 : icurl-1;
1129 }
1130 else if(std::fabs(Zright-Zleft)<kCarTolerance) // Zright=Zleft
1131 {
1132 Z.push_back(Zleft);
1133 ++countPlanes;
1134 ++icurr;
1135
1136 icurl=(icurl == 0)? numPlanes-1 : icurl-1;
1137
1138 Rmin.push_back(corners[inextl].r);
1139 Rmax.push_back (corners[inextr].r);
1140 }
1141 else // Zright<Zleft
1142 {
1143 Z.push_back(Zright);
1144 ++countPlanes;
1145
1146 G4double difZr=corners[inextr].z - corners[icurr].z;
1147 G4double difZl=corners[inextl].z - corners[icurl].z;
1148 if(std::fabs(difZr) < kCarTolerance)
1149 {
1150 if(std::fabs(difZl) < kCarTolerance)
1151 {
1152 Rmax.push_back(corners[inextr].r);
1153 Rmin.push_back(corners[icurr].r);
1154 }
1155 else
1156 {
1157 Rmin.push_back(corners[icurl].r + (Zright-corners[icurl].z)/difZl
1158 * (corners[inextl].r - corners[icurl].r));
1159 Rmax.push_back(corners[inextr].r);
1160 }
1161 ++icurr;
1162 } // plate
1163 else if (difZr >= kCarTolerance)
1164 {
1165 if(std::fabs(difZl) < kCarTolerance)
1166 {
1167 Rmax.push_back(corners[inextr].r);
1168 Rmin.push_back (corners[icurr].r);
1169 }
1170 else
1171 {
1172 Rmax.push_back(corners[inextr].r);
1173 Rmin.push_back (corners[icurl].r+(Zright-corners[icurl].z)/difZl
1174 * (corners[inextl].r - corners[icurl].r));
1175 }
1176 ++icurr;
1177 }
1178 else
1179 {
1180 isConvertible=false; break;
1181 }
1182 }
1183 } // end for loop
1184
1185 // last plane Z=Zmax
1186 //
1187 Z.push_back(Zmax);
1188 ++countPlanes;
1189 inextr=1+icurr;
1190 inextl=(icurl <= 0)? numPlanes-1 : icurl-1;
1191
1192 Rmax.push_back(corners[inextr].r);
1193 Rmin.push_back(corners[inextl].r);
1194
1195 // Set original parameters Rmin,Rmax,Z
1196 //
1197 if(isConvertible)
1198 {
1201 original_parameters->Z_values = new G4double[countPlanes];
1202 original_parameters->Rmin = new G4double[countPlanes];
1203 original_parameters->Rmax = new G4double[countPlanes];
1204
1205 for(G4int j=0; j < countPlanes; ++j)
1206 {
1207 original_parameters->Z_values[j] = Z[j];
1208 original_parameters->Rmax[j] = Rmax[j];
1209 original_parameters->Rmin[j] = Rmin[j];
1210 }
1213 original_parameters->Num_z_planes = countPlanes;
1214
1215 }
1216 else // Set parameters(r,z) with Rmin==0 as convention
1217 {
1218#ifdef G4SPECSDEBUG
1219 std::ostringstream message;
1220 message << "Polyhedra " << GetName() << G4endl
1221 << "cannot be converted to Polyhedra with (Rmin,Rmaz,Z) parameters!";
1222 G4Exception("G4Polyhedra::SetOriginalParameters()",
1223 "GeomSolids0002", JustWarning, message);
1224#endif
1227 original_parameters->Z_values = new G4double[numPlanes];
1228 original_parameters->Rmin = new G4double[numPlanes];
1229 original_parameters->Rmax = new G4double[numPlanes];
1230
1231 for(G4int j=0; j < numPlanes; ++j)
1232 {
1235 original_parameters->Rmin[j] = 0.0;
1236 }
1239 original_parameters->Num_z_planes = numPlanes;
1240 }
1241}
1242
1243#endif
std::vector< G4ThreeVector > G4ThreeVectorList
@ JustWarning
@ FatalErrorInArgument
void G4Exception(const char *originOfException, const char *exceptionCode, G4ExceptionSeverity severity, const char *description)
std::vector< G4TwoVector > G4TwoVectorList
G4double G4QuickRand()
#define G4MUTEX_INITIALIZER
std::mutex G4Mutex
double G4double
Definition G4Types.hh:83
long G4long
Definition G4Types.hh:87
bool G4bool
Definition G4Types.hh:86
int G4int
Definition G4Types.hh:85
#define G4endl
Definition G4ios.hh:67
double z() const
double x() const
double y() const
void set(double x, double y, double z)
G4bool BoundingBoxVsVoxelLimits(const EAxis pAxis, const G4VoxelLimits &pVoxelLimits, const G4Transform3D &pTransform3D, G4double &pMin, G4double &pMax) const
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimits, const G4Transform3D &pTransform3D, G4double &pMin, G4double &pMax) const
G4bool ShouldMiss(const G4ThreeVector &p, const G4ThreeVector &v) const
G4bool MustBeOutside(const G4ThreeVector &p) const
static G4ThreeVector QuadAreaNormal(const G4ThreeVector &A, const G4ThreeVector &B, const G4ThreeVector &C, const G4ThreeVector &D)
static G4bool TriangulatePolygon(const G4TwoVectorList &polygon, G4TwoVectorList &result)
static void RemoveRedundantVertices(G4TwoVectorList &polygon, std::vector< G4int > &iout, G4double tolerance=0.0)
static G4double TriangleArea(G4double Ax, G4double Ay, G4double Bx, G4double By, G4double Cx, G4double Cy)
static G4double PolygonArea(const G4TwoVectorList &polygon)
static G4ThreeVector TriangleAreaNormal(const G4ThreeVector &A, const G4ThreeVector &B, const G4ThreeVector &C)
EInside Inside(const G4ThreeVector &p) const override
G4Polyhedra & operator=(const G4Polyhedra &source)
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const override
void ComputeDimensions(G4VPVParameterisation *p, const G4int n, const G4VPhysicalVolume *pRep) override
G4GeometryType GetEntityType() const override
G4PolyhedraHistorical * original_parameters
G4double GetSurfaceArea() override
G4PolyhedraSideRZ * corners
G4Polyhedra(const G4String &name, G4double phiStart, G4double phiTotal, G4int numSide, G4int numZPlanes, const G4double zPlane[], const G4double rInner[], const G4double rOuter[])
G4int GetNumRZCorner() const
G4bool CalculateExtent(const EAxis pAxis, const G4VoxelLimits &pVoxelLimit, const G4AffineTransform &pTransform, G4double &pmin, G4double &pmax) const override
G4bool genericPgon
G4ThreeVector GetPointOnSurface() const override
void CopyStuff(const G4Polyhedra &source)
G4double GetSinStartPhi() const
void Create(G4double phiStart, G4double phiTotal, G4int numSide, G4ReduciblePolygon *rz)
G4bool IsOpen() const
G4double endPhi
G4double GetEndPhi() const
std::vector< surface_element > * fElements
void SetOriginalParameters(G4PolyhedraHistorical *pars)
G4VSolid * Clone() const override
~G4Polyhedra() override
G4bool phiIsOpen
G4Polyhedron * CreatePolyhedron() const override
G4double startPhi
G4int GetNumSide() const
void BoundingLimits(G4ThreeVector &pMin, G4ThreeVector &pMax) const override
G4PolyhedraSideRZ GetCorner(const G4int index) const
std::ostream & StreamInfo(std::ostream &os) const override
G4bool Reset()
G4double GetStartPhi() const
G4EnclosingCylinder * enclosingCylinder
G4double GetCosStartPhi() const
void SetSurfaceElements() const
G4double GetCubicVolume() override
G4double Amin() const
G4bool RemoveDuplicateVertices(G4double tolerance)
G4bool RemoveRedundantVertices(G4double tolerance)
G4bool CrossesItself(G4double tolerance)
G4double Bmax() const
G4double DistanceToIn(const G4ThreeVector &p, const G4ThreeVector &v) const override
G4VCSGfaceted & operator=(const G4VCSGfaceted &source)
G4double fCubicVolume
EInside Inside(const G4ThreeVector &p) const override
G4VCSGface ** faces
G4Polyhedron * fpPolyhedron
G4bool fRebuildPolyhedron
G4double fSurfaceArea
virtual void ComputeDimensions(G4Box &, const G4int, const G4VPhysicalVolume *) const
G4String GetName() const
void DumpInfo() const
G4double kCarTolerance
Definition G4VSolid.hh:299
G4double GetMinExtent(const EAxis pAxis) const
G4double GetMaxExtent(const EAxis pAxis) const
EAxis
Definition geomdefs.hh:54
EInside
Definition geomdefs.hh:67
@ kOutside
Definition geomdefs.hh:68
#define DBL_EPSILON
Definition templates.hh:66