ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4Ellipsoid.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file G4Ellipsoid.cc
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 // class G4Ellipsoid
27 //
28 // Implementation of G4Ellipsoid class
29 //
30 // 10.11.99 G.Horton-Smith: first writing, based on G4Sphere class
31 // 25.02.05 G.Guerrieri: Revised
32 // 15.12.19 E.Tcherniaev: Complete revision
33 // --------------------------------------------------------------------
34 
35 #include "G4Ellipsoid.hh"
36 
37 #if !(defined(G4GEOM_USE_UELLIPSOID) && defined(G4GEOM_USE_SYS_USOLIDS))
38 
39 #include "globals.hh"
40 
41 #include "G4VoxelLimits.hh"
42 #include "G4AffineTransform.hh"
43 #include "G4GeometryTolerance.hh"
44 #include "G4BoundingEnvelope.hh"
45 #include "G4RandomTools.hh"
46 #include "G4QuickRand.hh"
47 
48 #include "G4VPVParameterisation.hh"
49 
50 #include "G4VGraphicsScene.hh"
51 #include "G4VisExtent.hh"
52 
53 #include "G4AutoLock.hh"
54 
55 namespace
56 {
57  G4Mutex polyhedronMutex = G4MUTEX_INITIALIZER;
58  G4Mutex lateralareaMutex = G4MUTEX_INITIALIZER;
59 }
60 
61 using namespace CLHEP;
62 
64 //
65 // Constructor
66 
68  G4double xSemiAxis,
69  G4double ySemiAxis,
70  G4double zSemiAxis,
71  G4double zBottomCut,
72  G4double zTopCut)
73  : G4VSolid(name), fDx(xSemiAxis), fDy(ySemiAxis), fDz(zSemiAxis),
74  fZBottomCut(zBottomCut), fZTopCut(zTopCut)
75 {
77 }
78 
80 //
81 // Fake default constructor - sets only member data and allocates memory
82 // for usage restricted to object persistency
83 
85  : G4VSolid(a), fDx(0.), fDy(0.), fDz(0.), fZBottomCut(0.), fZTopCut(0.)
86 {
87 }
88 
90 //
91 // Destructor
92 
94 {
95  delete fpPolyhedron; fpPolyhedron = nullptr;
96 }
97 
99 //
100 // Copy constructor
101 
103  : G4VSolid(rhs),
104  fDx(rhs.fDx), fDy(rhs.fDy), fDz(rhs.fDz),
105  fZBottomCut(rhs.fZBottomCut), fZTopCut(rhs.fZTopCut),
106  halfTolerance(rhs.halfTolerance),
107  fXmax(rhs.fXmax), fYmax(rhs.fYmax),
108  fRsph(rhs.fRsph), fR(rhs.fR),
109  fSx(rhs.fSx), fSy(rhs.fSy), fSz(rhs.fSz),
110  fZMidCut(rhs.fZMidCut), fZDimCut(rhs.fZDimCut),
111  fQ1(rhs.fQ1), fQ2(rhs.fQ2),
112  fCubicVolume(rhs.fCubicVolume),
113  fSurfaceArea(rhs.fSurfaceArea),
114  fLateralArea(rhs.fLateralArea)
115 {
116 }
117 
119 //
120 // Assignment operator
121 
123 {
124  // Check assignment to self
125  //
126  if (this == &rhs) { return *this; }
127 
128  // Copy base class data
129  //
130  G4VSolid::operator=(rhs);
131 
132  // Copy data
133  //
134  fDx = rhs.fDx;
135  fDy = rhs.fDy;
136  fDz = rhs.fDz;
137  fZBottomCut = rhs.fZBottomCut;
138  fZTopCut = rhs.fZTopCut;
139 
141  fXmax = rhs.fXmax;
142  fYmax = rhs.fYmax;
143  fRsph = rhs.fRsph;
144  fR = rhs.fR;
145  fSx = rhs.fSx;
146  fSy = rhs.fSy;
147  fSz = rhs.fSz;
148  fZMidCut = rhs.fZMidCut;
149  fZDimCut = rhs.fZDimCut;
150  fQ1 = rhs.fQ1;
151  fQ2 = rhs.fQ2;
152 
156 
157  fRebuildPolyhedron = false;
158  delete fpPolyhedron; fpPolyhedron = nullptr;
159 
160  return *this;
161 }
162 
164 //
165 // Check parameters and make precalculation
166 
168 {
169  halfTolerance = 0.5 * kCarTolerance; // half tolerance
170  G4double dmin = 2. * kCarTolerance;
171 
172  // Check dimensions
173  //
174  if (fDx < dmin || fDy < dmin || fDz < dmin)
175  {
176  std::ostringstream message;
177  message << "Invalid (too small or negative) dimensions for Solid: "
178  << GetName() << "\n"
179  << " semi-axis x: " << fDx << "\n"
180  << " semi-axis y: " << fDy << "\n"
181  << " semi-axis z: " << fDz;
182  G4Exception("G4Ellipsoid::CheckParameters()", "GeomSolids0002",
183  FatalException, message);
184  }
185  G4double A = fDx;
186  G4double B = fDy;
187  G4double C = fDz;
188 
189  // Check cuts
190  //
191  if (fZBottomCut == 0. && fZTopCut == 0.)
192  {
193  fZBottomCut = -C;
194  fZTopCut = C;
195  }
196  if (fZBottomCut >= C || fZTopCut <= -C || fZBottomCut >= fZTopCut)
197  {
198  std::ostringstream message;
199  message << "Invalid Z cuts for Solid: "
200  << GetName() << "\n"
201  << " bottom cut: " << fZBottomCut << "\n"
202  << " top cut: " << fZTopCut;
203  G4Exception("G4Ellipsoid::CheckParameters()", "GeomSolids0002",
204  FatalException, message);
205 
206  }
208  fZTopCut = std::min(fZTopCut, C);
209 
210  // Set extent in x and y
211  fXmax = A;
212  fYmax = B;
213  if (fZBottomCut > 0.)
214  {
216  G4double scale = std::sqrt((1. - ratio) * (1 + ratio));
217  fXmax *= scale;
218  fYmax *= scale;
219  }
220  if (fZTopCut < 0.)
221  {
222  G4double ratio = fZTopCut / C;
223  G4double scale = std::sqrt((1. - ratio) * (1 + ratio));
224  fXmax *= scale;
225  fYmax *= scale;
226  }
227 
228  // Set scale factors
229  fRsph = std::max(std::max(A, B), C); // bounding sphere
230  fR = std::min(std::min(A, B), C); // radius of sphere after scaling
231  fSx = fR / A; // X scale factor
232  fSy = fR / B; // Y scale factor
233  fSz = fR / C; // Z scale factor
234 
235  // Scaled cuts
236  fZMidCut = 0.5 * (fZTopCut + fZBottomCut) * fSz; // middle position
237  fZDimCut = 0.5 * (fZTopCut - fZBottomCut) * fSz; // half distance
238 
239  // Coefficients for approximation of distance: Q1 * (x^2 + y^2 + z^2) - Q2
240  fQ1 = 0.5 / fR;
241  fQ2 = 0.5 * fR + halfTolerance * halfTolerance * fQ1;
242 
243  fCubicVolume = 0.; // volume
244  fSurfaceArea = 0.; // surface area
245  fLateralArea = 0.; // lateral surface area
246 }
247 
249 //
250 // Dispatch to parameterisation for replication mechanism dimension
251 // computation & modification
252 
254  const G4int n,
255  const G4VPhysicalVolume* pRep)
256 {
257  p->ComputeDimensions(*this,n,pRep);
258 }
259 
261 //
262 // Get bounding box
263 
265  G4ThreeVector& pMax) const
266 {
267  pMin.set(-fXmax,-fYmax, fZBottomCut);
268  pMax.set( fXmax, fYmax, fZTopCut);
269 }
270 
272 //
273 // Calculate extent under transform and specified limits
274 
275 G4bool
277  const G4VoxelLimits& pVoxelLimit,
278  const G4AffineTransform& pTransform,
279  G4double& pMin, G4double& pMax) const
280 {
281  G4ThreeVector bmin, bmax;
282 
283  // Get bounding box
284  BoundingLimits(bmin,bmax);
285 
286  // Find extent
287  G4BoundingEnvelope bbox(bmin,bmax);
288  return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
289 }
290 
292 //
293 // Return position of point: inside/outside/on surface
294 
296 {
297  G4double x = p.x() * fSx;
298  G4double y = p.y() * fSy;
299  G4double z = p.z() * fSz;
300  G4double rr = x * x + y * y + z * z;
301  G4double distZ = std::abs(z - fZMidCut) - fZDimCut;
302  G4double distR = fQ1 * rr - fQ2;
303  G4double dist = std::max(distZ, distR);
304 
305  if (dist > halfTolerance) return kOutside;
306  return (dist > -halfTolerance) ? kSurface : kInside;
307 }
308 
310 //
311 // Return unit normal to surface at p
312 
314 {
315  G4ThreeVector norm(0., 0., 0.);
316  G4int nsurf = 0;
317 
318  // Check cuts
319  G4double x = p.x() * fSx;
320  G4double y = p.y() * fSy;
321  G4double z = p.z() * fSz;
322  G4double distZ = std::abs(z - fZMidCut) - fZDimCut;
323  if (std::abs(distZ) <= halfTolerance)
324  {
325  norm.setZ(std::copysign(1., z - fZMidCut));
326  ++nsurf;
327  }
328 
329  // Check lateral surface
330  G4double distR = fQ1*(x*x + y*y + z*z) - fQ2;
331  if (std::abs(distR) <= halfTolerance)
332  {
333  // normal = (p.x/a^2, p.y/b^2, p.z/c^2)
334  norm += G4ThreeVector(x*fSx, y*fSy, z*fSz).unit();
335  ++nsurf;
336  }
337 
338  // Return normal
339  if (nsurf == 1) return norm;
340  else if (nsurf > 1) return norm.unit(); // edge
341  else
342  {
343 #ifdef G4SPECSDEBUG
344  std::ostringstream message;
345  G4int oldprc = message.precision(16);
346  message << "Point p is not on surface (!?) of solid: "
347  << GetName() << "\n";
348  message << "Position:\n";
349  message << " p.x() = " << p.x()/mm << " mm\n";
350  message << " p.y() = " << p.y()/mm << " mm\n";
351  message << " p.z() = " << p.z()/mm << " mm";
352  G4cout.precision(oldprc);
353  G4Exception("G4Ellipsoid::SurfaceNormal(p)", "GeomSolids1002",
354  JustWarning, message );
355  DumpInfo();
356 #endif
357  return ApproxSurfaceNormal(p);
358  }
359 }
360 
362 //
363 // Find surface nearest to point and return corresponding normal.
364 // This method normally should not be called.
365 
367 {
368  G4double x = p.x() * fSx;
369  G4double y = p.y() * fSy;
370  G4double z = p.z() * fSz;
371  G4double rr = x * x + y * y + z * z;
372  G4double distZ = std::abs(z - fZMidCut) - fZDimCut;
373  G4double distR = std::sqrt(rr) - fR;
374  if (distR > distZ && rr > 0.) // distR > distZ is correct!
375  return G4ThreeVector(x*fSx, y*fSy, z*fSz).unit();
376  else
377  return G4ThreeVector(0., 0., std::copysign(1., z - fZMidCut));
378 }
379 
381 //
382 // Calculate distance to shape from outside along normalised vector
383 
385  const G4ThreeVector& v) const
386 {
387  G4double offset = 0.;
388  G4ThreeVector pcur = p;
389 
390  // Check if point is flying away, relative to bounding box
391  //
392  G4double safex = std::abs(p.x()) - fXmax;
393  G4double safey = std::abs(p.y()) - fYmax;
394  G4double safet = p.z() - fZTopCut;
395  G4double safeb = fZBottomCut - p.z();
396 
397  if (safex >= -halfTolerance && p.x() * v.x() >= 0.) return kInfinity;
398  if (safey >= -halfTolerance && p.y() * v.y() >= 0.) return kInfinity;
399  if (safet >= -halfTolerance && v.z() >= 0.) return kInfinity;
400  if (safeb >= -halfTolerance && v.z() <= 0.) return kInfinity;
401 
402  // Relocate point, if required
403  //
404  G4double safe = std::max(std::max(std::max(safex, safey), safet), safeb);
405  if (safe > 32. * fRsph)
406  {
407  offset = (1. - 1.e-08) * safe - 2. * fRsph;
408  pcur += offset * v;
409  G4double dist = DistanceToIn(pcur, v);
410  return (dist == kInfinity) ? kInfinity : dist + offset;
411  }
412 
413  // Scale ellipsoid to sphere
414  //
415  G4double px = pcur.x() * fSx;
416  G4double py = pcur.y() * fSy;
417  G4double pz = pcur.z() * fSz;
418  G4double vx = v.x() * fSx;
419  G4double vy = v.y() * fSy;
420  G4double vz = v.z() * fSz;
421 
422  // Check if point is leaving the solid
423  //
424  G4double dzcut = fZDimCut;
425  G4double pzcut = pz - fZMidCut;
426  G4double distZ = std::abs(pzcut) - dzcut;
427  if (distZ >= -halfTolerance && pzcut * vz >= 0.) return kInfinity;
428 
429  G4double rr = px * px + py * py + pz * pz;
430  G4double pv = px * vx + py * vy + pz * vz;
431  G4double distR = fQ1 * rr - fQ2;
432  if (distR >= -halfTolerance && pv >= 0.) return kInfinity;
433 
434  G4double A = vx * vx + vy * vy + vz * vz;
435  G4double B = pv;
436  G4double C = rr - fR * fR;
437  G4double D = B * B - A * C;
438  // scratch^2 = R^2 - (R - halfTolerance)^2 = 2 * R * halfTolerance
439  G4double EPS = A * A * fR * kCarTolerance; // discriminant at scratching
440  if (D <= EPS) return kInfinity; // no intersection or scratching
441 
442  // Find intersection with Z planes
443  //
444  G4double invz = (vz == 0) ? DBL_MAX : -1./vz;
445  G4double dz = std::copysign(dzcut, invz);
446  G4double tzmin = (pzcut - dz) * invz;
447  G4double tzmax = (pzcut + dz) * invz;
448 
449  // Find intersection with lateral surface
450  //
451  G4double tmp = -B - std::copysign(std::sqrt(D), B);
452  G4double t1 = tmp / A;
453  G4double t2 = C / tmp;
454  G4double trmin = std::min(t1, t2);
455  G4double trmax = std::max(t1, t2);
456 
457  // Return distance
458  //
459  G4double tmin = std::max(tzmin, trmin);
460  G4double tmax = std::min(tzmax, trmax);
461 
462  if (tmax - tmin <= halfTolerance) return kInfinity; // touch or no hit
463  return (tmin < halfTolerance) ? offset : tmin + offset;
464 }
465 
467 //
468 // Estimate distance to surface from outside
469 
471 {
472  G4double px = p.x();
473  G4double py = p.y();
474  G4double pz = p.z();
475 
476  // Safety distance to bounding box
477  G4double distX = std::abs(px) - fXmax;
478  G4double distY = std::abs(py) - fYmax;
479  G4double distZ = std::max(pz - fZTopCut, fZBottomCut - pz);
480  G4double distB = std::max(std::max(distX, distY), distZ);
481 
482  // Safety distance to lateral surface
483  G4double x = px * fSx;
484  G4double y = py * fSy;
485  G4double z = pz * fSz;
486  G4double distR = std::sqrt(x*x + y*y + z*z) - fR;
487 
488  // Return safety to in
489  G4double dist = std::max(distB, distR);
490  return (dist < 0.) ? 0. : dist;
491 }
492 
494 //
495 // Calculate distance to surface from inside along normalised vector
496 
498  const G4ThreeVector& v,
499  const G4bool calcNorm,
500  G4bool* validNorm,
501  G4ThreeVector* n ) const
502 {
503  // Check if point flying away relative to Z planes
504  //
505  G4double pz = p.z() * fSz;
506  G4double vz = v.z() * fSz;
507  G4double dzcut = fZDimCut;
508  G4double pzcut = pz - fZMidCut;
509  G4double distZ = std::abs(pzcut) - dzcut;
510  if (distZ >= -halfTolerance && pzcut * vz > 0.)
511  {
512  if (calcNorm)
513  {
514  *validNorm = true;
515  n->set(0., 0., std::copysign(1., pzcut));
516  }
517  return 0.;
518  }
519 
520  // Check if point is flying away relative to lateral surface
521  //
522  G4double px = p.x() * fSx;
523  G4double py = p.y() * fSy;
524  G4double vx = v.x() * fSx;
525  G4double vy = v.y() * fSy;
526  G4double rr = px * px + py * py + pz * pz;
527  G4double pv = px * vx + py * vy + pz * vz;
528  G4double distR = fQ1 * rr - fQ2;
529  if (distR >= -halfTolerance && pv > 0.)
530  {
531  if (calcNorm)
532  {
533  *validNorm = true;
534  *n = G4ThreeVector(px*fSx, py*fSy, pz*fSz).unit();
535  }
536  return 0.;
537  }
538 
539  // Just in case check if point is outside (normally it should never be)
540  //
541  if (std::max(distZ, distR) > halfTolerance)
542  {
543 #ifdef G4SPECSDEBUG
544  std::ostringstream message;
545  G4int oldprc = message.precision(16);
546  message << "Point p is outside (!?) of solid: "
547  << GetName() << G4endl;
548  message << "Position: " << p << G4endl;;
549  message << "Direction: " << v;
550  G4cout.precision(oldprc);
551  G4Exception("G4Ellipsoid::DistanceToOut(p,v)", "GeomSolids1002",
552  JustWarning, message );
553  DumpInfo();
554 #endif
555  if (calcNorm)
556  {
557  *validNorm = true;
558  *n = ApproxSurfaceNormal(p);
559  }
560  return 0.;
561  }
562 
563  // Set coefficients of quadratic equation: A t^2 + 2B t + C = 0
564  //
565  G4double A = vx * vx + vy * vy + vz * vz;
566  G4double B = pv;
567  G4double C = rr - fR * fR;
568  G4double D = B * B - A * C;
569  // It is expected that the point is located inside the sphere, so
570  // max term in the expression for discriminant is A * R^2 and
571  // max calculation error can be derived as follows:
572  // A * (1 + 2e) * R^2 * (1 + 2e) = A * R^2 + (4 * A * R^2 * e)
573  G4double EPS = 4. * A * fR * fR * DBL_EPSILON; // calculation error
574 
575  if (D <= EPS) // no intersection
576  {
577  if (calcNorm)
578  {
579  *validNorm = true;
580  *n = G4ThreeVector(px*fSx, py*fSy, pz*fSz).unit();
581  }
582  return 0.;
583  }
584 
585  // Find intersection with Z cuts
586  //
587  G4double tzmax = (vz == 0.) ? DBL_MAX : (std::copysign(dzcut, vz) - pzcut) / vz;
588 
589  // Find intersection with lateral surface
590  //
591  G4double tmp = -B - std::copysign(std::sqrt(D), B);
592  G4double trmax = (tmp < 0.) ? C/tmp : tmp/A;
593 
594  // Find distance and set normal, if required
595  //
596  G4double tmax = std::min(tzmax, trmax);
597  //if (tmax < halfTolerance) tmax = 0.;
598 
599  if (calcNorm)
600  {
601  *validNorm = true;
602  if (tmax == tzmax)
603  {
604  G4double pznew = pz + tmax * vz;
605  n->set(0., 0., (pznew > fZMidCut) ? 1. : -1.);
606  }
607  else
608  {
609  G4double nx = (px + tmax * vx) * fSx;
610  G4double ny = (py + tmax * vy) * fSy;
611  G4double nz = (pz + tmax * vz) * fSz;
612  *n = G4ThreeVector(nx, ny, nz).unit();
613  }
614  }
615  return tmax;
616 }
617 
619 //
620 // Estimate distance to surface from inside
621 
623 {
624  // Safety distance in z direction
625  G4double distZ = std::min(fZTopCut - p.z(), p.z() - fZBottomCut);
626 
627  // Safety distance to lateral surface
628  G4double x = p.x() * fSx;
629  G4double y = p.y() * fSy;
630  G4double z = p.z() * fSz;
631  G4double distR = fR - std::sqrt(x*x + y*y + z*z);
632 
633  // Return safety to out
634  G4double dist = std::min(distZ, distR);
635  return (dist < 0.) ? 0. : dist;
636 }
637 
639 //
640 // Return entity type
641 
643 {
644  return G4String("G4Ellipsoid");
645 }
646 
648 //
649 // Make a clone of the object
650 
652 {
653  return new G4Ellipsoid(*this);
654 }
655 
657 //
658 // Stream object contents to output stream
659 
660 std::ostream& G4Ellipsoid::StreamInfo( std::ostream& os ) const
661 {
662  G4int oldprc = os.precision(16);
663  os << "-----------------------------------------------------------\n"
664  << " *** Dump for solid - " << GetName() << " ***\n"
665  << " ===================================================\n"
666  << " Solid type: " << GetEntityType() << "\n"
667  << " Parameters: \n"
668  << " semi-axis x: " << GetDx()/mm << " mm \n"
669  << " semi-axis y: " << GetDy()/mm << " mm \n"
670  << " semi-axis z: " << GetDz()/mm << " mm \n"
671  << " lower cut in z: " << GetZBottomCut()/mm << " mm \n"
672  << " upper cut in z: " << GetZTopCut()/mm << " mm \n"
673  << "-----------------------------------------------------------\n";
674  os.precision(oldprc);
675  return os;
676 }
677 
679 //
680 // Return volume
681 
683 {
684  if (fCubicVolume == 0.)
685  {
686  G4double piAB_3 = CLHEP::pi * fDx * fDy / 3.;
687  fCubicVolume = 4. * piAB_3 * fDz;
688  if (fZBottomCut > -fDz)
689  {
690  G4double hbot = 1. + fZBottomCut / fDz;
691  fCubicVolume -= piAB_3 * hbot * hbot * (2. * fDz - fZBottomCut);
692  }
693  if (fZTopCut < fDz)
694  {
695  G4double htop = 1. - fZTopCut / fDz;
696  fCubicVolume -= piAB_3 * htop * htop * (2. * fDz + fZTopCut);
697  }
698  }
699  return fCubicVolume;
700 }
701 
703 //
704 // Calculate area of lateral surface
705 
707 {
708  const G4int Nphi = 100;
709  const G4int Nz = 200;
710  G4double rho[Nz + 1];
711 
712  // Set array of rho
713  G4double zbot = fZBottomCut / fDz;
714  G4double ztop = fZTopCut / fDz;
715  G4double dz = (ztop - zbot) / Nz;
716  for (G4int iz = 0; iz < Nz; ++iz)
717  {
718  G4double z = zbot + iz * dz;
719  rho[iz] = std::sqrt((1. + z) * (1. - z));
720  }
721  rho[Nz] = std::sqrt((1. + ztop) * (1. - ztop));
722 
723  // Compute area
724  zbot = fZBottomCut;
725  ztop = fZTopCut;
726  dz = (ztop - zbot) / Nz;
727  G4double area = 0.;
728  G4double dphi = CLHEP::halfpi / Nphi;
729  for (G4int iphi = 0; iphi < Nphi; ++iphi)
730  {
731  G4double phi1 = iphi * dphi;
732  G4double phi2 = (iphi == Nphi - 1) ? CLHEP::halfpi : phi1 + dphi;
733  G4double cos1 = std::cos(phi1) * fDx;
734  G4double cos2 = std::cos(phi2) * fDx;
735  G4double sin1 = std::sin(phi1) * fDy;
736  G4double sin2 = std::sin(phi2) * fDy;
737  for (G4int iz = 0; iz < Nz; ++iz)
738  {
739  G4double z1 = zbot + iz * dz;
740  G4double z2 = (iz == Nz - 1) ? ztop : z1 + dz;
741  G4double rho1 = rho[iz];
742  G4double rho2 = rho[iz + 1];
743  G4ThreeVector p1(rho1 * cos1, rho1 * sin1, z1);
744  G4ThreeVector p2(rho1 * cos2, rho1 * sin2, z1);
745  G4ThreeVector p3(rho2 * cos1, rho2 * sin1, z2);
746  G4ThreeVector p4(rho2 * cos2, rho2 * sin2, z2);
747  area += ((p4 - p1).cross(p3 - p2)).mag();
748  }
749  }
750  return 2. * area;
751 }
752 
754 //
755 // Return surface area
756 
758 {
759  if (fSurfaceArea == 0.)
760  {
761  G4double piAB = CLHEP::pi * fDx * fDy;
763  if (fZBottomCut > -fDz)
764  {
765  G4double hbot = 1. + fZBottomCut / fDz;
766  fSurfaceArea += piAB * hbot * (2. - hbot);
767  }
768  if (fZTopCut < fDz)
769  {
770  G4double htop = 1. - fZTopCut / fDz;
771  fSurfaceArea += piAB * htop * (2. - htop);
772  }
773  }
774  return fSurfaceArea;
775 }
776 
778 //
779 // Return random point on surface
780 
782 {
783  G4double A = GetDx();
784  G4double B = GetDy();
785  G4double C = GetDz();
786  G4double Zbot = GetZBottomCut();
787  G4double Ztop = GetZTopCut();
788 
789  // Calculate cut areas
790  G4double Hbot = 1. + Zbot / C;
791  G4double Htop = 1. - Ztop / C;
792  G4double piAB = CLHEP::pi * A * B;
793  G4double Sbot = piAB * Hbot * (2. - Hbot);
794  G4double Stop = piAB * Htop * (2. - Htop);
795 
796  // Get area of lateral surface
797  if (fLateralArea == 0.)
798  {
799  G4AutoLock l(&lateralareaMutex);
801  l.unlock();
802  }
803  G4double Slat = fLateralArea;
804 
805  // Select surface (0 - bottom cut, 1 - lateral surface, 2 - top cut)
806  G4double select = (Sbot + Slat + Stop) * G4QuickRand();
807  G4int k = 0;
808  if (select > Sbot) k = 1;
809  if (select > Sbot + Slat) k = 2;
810 
811  // Pick random point on selected surface (rejection sampling)
813  switch (k)
814  {
815  case 0: // bootom z-cut
816  {
817  G4double scale = std::sqrt(Hbot * (2. - Hbot));
818  G4TwoVector rho = G4RandomPointInEllipse(A * scale, B * scale);
819  p.set(rho.x(), rho.y(), Zbot);
820  break;
821  }
822  case 1: // lateral surface
823  {
824  G4double x, y, z;
825  G4double mu_max = std::max(std::max(A * B, A * C), B * C);
826  for (G4int i = 0; i < 1000; ++i)
827  {
828  // generate random point on unit sphere
829  z = (Zbot + (Ztop - Zbot) * G4QuickRand()) / C;
830  G4double rho = std::sqrt((1. + z) * (1. - z));
832  x = rho * std::cos(phi);
833  y = rho * std::sin(phi);
834  // check acceptance
835  G4double xbc = x * B * C;
836  G4double yac = y * A * C;
837  G4double zab = z * A * B;
838  G4double mu = std::sqrt(xbc * xbc + yac * yac + zab * zab);
839  if (mu_max * G4QuickRand() <= mu) break;
840  }
841  p.set(A * x, B * y, C * z);
842  break;
843  }
844  case 2: // top z-cut
845  {
846  G4double scale = std::sqrt(Htop * (2. - Htop));
847  G4TwoVector rho = G4RandomPointInEllipse(A * scale, B * scale);
848  p.set(rho.x(), rho.y(), Ztop);
849  break;
850  }
851  }
852  return p;
853 }
854 
856 //
857 // Methods for visualisation
858 
860 {
861  scene.AddSolid(*this);
862 }
863 
865 //
866 // Return vis extent
867 
869 {
871 }
872 
874 //
875 // Create polyhedron
876 
878 {
880 }
881 
883 //
884 // Return pointer to polyhedron
885 
887 {
888  if (fpPolyhedron == nullptr ||
892  {
893  G4AutoLock l(&polyhedronMutex);
894  delete fpPolyhedron;
896  fRebuildPolyhedron = false;
897  l.unlock();
898  }
899  return fpPolyhedron;
900 }
901 
902 #endif // !defined(G4GEOM_USE_UELLIPSOID) || !defined(G4GEOM_USE_SYS_USOLIDS)