BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSMagnetOuterFactoryPolesBase.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) BDSIM Collaboration, 2001 - 2026.
3
4This file is part of BDSIM.
5
6BDSIM is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published
8by the Free Software Foundation version 3 of the License.
9
10BDSIM is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "BDSMagnetOuterFactoryPolesBase.hh"
19
20#include "BDSBeamPipe.hh"
21#include "BDSColours.hh"
22#include "BDSDebug.hh"
23#include "BDSException.hh"
24#include "BDSExtent.hh"
25#include "BDSSimpleComponent.hh"
26#include "BDSGlobalConstants.hh"
27#include "BDSMagnetOuter.hh"
28#include "BDSMagnetOuterFactoryCylindrical.hh" // for default geometry
29#include "BDSMagnetOuterInfo.hh"
30#include "BDSMaterials.hh"
31#include "BDSSDType.hh"
32#include "BDSUtilities.hh"
33
34#include "globals.hh" // geant4 globals / types
35#include "G4Box.hh"
36#include "G4Colour.hh"
37#include "G4CutTubs.hh"
38#include "G4ExtrudedSolid.hh"
39#include "G4LogicalVolume.hh"
40#include "G4IntersectionSolid.hh"
41#include "G4Material.hh"
42#include "G4PVPlacement.hh"
43#include "G4SubtractionSolid.hh"
44#include "G4ThreeVector.hh"
45#include "G4Tubs.hh"
46#include "G4TwoVector.hh"
47#include "G4VisAttributes.hh"
48#include "G4VSolid.hh"
49
50#include <algorithm>
51#include <cmath>
52#include <set>
53#include <string>
54#include <utility>
55#include <vector>
56
57BDSMagnetOuterFactoryPolesBase::BDSMagnetOuterFactoryPolesBase():
59{
60 CleanUpPolesBase();
61 cylindrical = new BDSMagnetOuterFactoryCylindrical();
62}
63
64BDSMagnetOuterFactoryPolesBase::~BDSMagnetOuterFactoryPolesBase()
65{
66 delete cylindrical;
67}
68
69BDSMagnetOuterFactoryPolesBase::BDSMagnetOuterFactoryPolesBase(G4double poleStopFactorIn):
70 poleFraction(0.7),
71 poleAngularFraction(0.7),
72 poleTipFraction(0.2),
73 poleAnnulusFraction(0.1),
74 bendHeightFraction(0.7),
75 poleStopFactor(poleStopFactorIn)
76{
77 // now the base class constructor should be called first which
78 // should call clean up (in the derived class) which should initialise
79 // the variables I think, but doing here just to be sure.
80 CleanUpPolesBase();
81 cylindrical = new BDSMagnetOuterFactoryCylindrical();
82}
83
89
91{
95 buildPole = true;
100 segmentAngle = 0;
101 poleAngle = 0;
102 poleTranslation = G4ThreeVector(0,0,0);
103 coilHeight = 0;
105 endPieceLength = 0;
106 endPieceInnerR = 0;
107 endPieceOuterR = 0;
108 poleIntersectionSolid = nullptr;
109 coilLeftSolid = nullptr;
110 coilRightSolid = nullptr;
111 endPieceContainerSolid = nullptr;
112 coilLeftLV = nullptr;
113 coilRightLV = nullptr;
114 endPieceCoilLV = nullptr;
115 endPieceContainerLV = nullptr;
116 endPiece = nullptr;
117 leftPoints.clear();
118 rightPoints.clear();
119 endPiecePoints.clear();
120}
121
123 G4double length,
124 const BDSBeamPipe* beamPipe,
125 G4double containerLength,
126 const BDSMagnetOuterInfo* recipe)
127{
128 if (recipe->hStyle)
129 {return CreateDipoleH(name, length, beamPipe, containerLength, recipe, false);}
130 else
131 {return CreateDipoleC(name, length, beamPipe, containerLength, recipe, false);}
132}
133
135 G4double length,
136 const BDSBeamPipe* beamPipe,
137 G4double containerLength,
138 const BDSMagnetOuterInfo* recipe)
139{
140 if (recipe->hStyle)
141 {return CreateDipoleH(name, length, beamPipe, containerLength, recipe, false);}
142 else
143 {return CreateDipoleC(name, length, beamPipe, containerLength, recipe, false);}
144}
145
147 G4double length,
148 BDSBeamPipe* beamPipe,
149 G4double containerLength,
150 const BDSMagnetOuterInfo* recipe)
151{
152 return CommonConstructor(name, length, beamPipe, 2, containerLength, recipe);
153}
154
156 G4double length,
157 BDSBeamPipe* beamPipe,
158 G4double containerLength,
159 const BDSMagnetOuterInfo* recipe)
160{
161 return CommonConstructor(name, length, beamPipe, 3, containerLength, recipe);
162}
163
165 G4double length,
166 BDSBeamPipe* beamPipe,
167 G4double containerLength,
168 const BDSMagnetOuterInfo* recipe)
169{
170 return CommonConstructor(name, length, beamPipe, 4, containerLength, recipe);
171}
172
174 G4double length,
175 BDSBeamPipe* beamPipe,
176 G4double containerLength,
177 const BDSMagnetOuterInfo* recipe)
178{
179 return CommonConstructor(name, length, beamPipe, 5, containerLength, recipe);
180}
181
183 G4double length,
184 BDSBeamPipe* beamPipe,
185 G4double containerLength,
186 const BDSMagnetOuterInfo* recipe)
187{
188 return cylindrical->CreateSolenoid(name,length,beamPipe,containerLength,recipe);
189}
190
192 G4double length,
193 BDSBeamPipe* beamPipe,
194 G4double containerLength,
195 const BDSMagnetOuterInfo* recipe)
196{
197 return cylindrical->CreateMultipole(name,length,beamPipe,containerLength,recipe);
198}
199
201 G4double length,
202 BDSBeamPipe* beamPipe,
203 G4double containerLength,
204 const BDSMagnetOuterInfo* recipe)
205{
206 return cylindrical->CreateRfCavity(name,length,beamPipe,containerLength,recipe);
207}
208
210 G4double length,
211 BDSBeamPipe* beamPipe,
212 G4double containerLength,
213 const BDSMagnetOuterInfo* recipe)
214{
215 return cylindrical->CreateMuonSpoiler(name,length,beamPipe,containerLength,recipe);
216}
217
219 G4double length,
220 const BDSBeamPipe* beamPipe,
221 G4double containerLength,
222 const BDSMagnetOuterInfo* recipe,
223 G4bool vertical)
224{
225 if (recipe->hStyle)
226 {return CreateDipoleH(name, length, beamPipe, containerLength, recipe, vertical);}
227 else
228 {return CreateDipoleC(name, length, beamPipe, containerLength, recipe, vertical);}
229}
230
233 G4double length,
234 BDSBeamPipe* beamPipe,
235 G4int order,
236 G4double magnetContainerLength,
237 const BDSMagnetOuterInfo* recipe)
238{
239 G4double horizontalWidth = recipe->horizontalWidth;
240 G4Material* outerMaterial = recipe->outerMaterial;
241 if (!outerMaterial)
242 {outerMaterial = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->EmptyMaterial());}
243 G4Colour* colour = recipe->colour;
244
245 // reset all pointers and variables to protect against bugs using previous use of factory
246 CleanUp();
247
248 CalculatePoleAndYoke(horizontalWidth, beamPipe, order);
249 if (buildPole)
250 {
251 CreatePoleSolid(name, length, order);
252 CreateCoilSolids(name, length);
253 }
254 CreateYokeAndContainerSolid(name, length, order, magnetContainerLength, magnetContainerRadius);
255 if (buildPole)
256 {IntersectPoleWithYoke(name, length, order);}
257 CreateLogicalVolumes(name, colour, outerMaterial);
259 CreateMagnetContainerComponent();
260 if (buildPole && recipe->buildEndPieces)
261 {CreateEndPiece(name);}
262 PlaceComponents(name, order); //returns vector of PVs
263 if (buildPole)
264 {PlaceComponentsCoils(name, order);}
265
266 // record extents
267 // container radius is just outerDiameter as yoke is circular
268 G4double containerRadius = horizontalWidth + lengthSafety;
269 BDSExtent ext = BDSExtent(containerRadius, containerRadius, length*0.5);
270
271 // build the BDSMagnetOuter instance and return it
272 BDSMagnetOuter* outer = new BDSMagnetOuter(containerSolid,
273 containerLV, ext,
274 magnetContainer);
275
276 outer->RegisterRotationMatrix(allRotationMatrices);
277 outer->RegisterPhysicalVolume(allPhysicalVolumes);
278 outer->RegisterVisAttributes(allVisAttributes);
279
280 // Register logical volumes and set sensitivity
281 // test if poleLV exists first as some derived classes use their own vector of
282 // poles and don't use this one - therefore it'll be null
283 if (poleLV)
284 {
285 outer->RegisterLogicalVolume(poleLV);
286 if (sensitiveOuter)
287 {outer->RegisterSensitiveVolume(poleLV, BDSSDType::energydep);}
288 }
289 outer->RegisterLogicalVolume(yokeLV);
290 if (sensitiveOuter)
291 {outer->RegisterSensitiveVolume(yokeLV, BDSSDType::energydep);}
292
293 outer->SetEndPieceBefore(endPiece);
294 outer->SetEndPieceAfter(endPiece);
295
296 SetFaceNormals(outer);
297
298 return outer;
299}
300
302 BDSBeamPipe* beamPipe,
303 G4int order)
304{
305 G4double bpRadius = beamPipe->GetContainerRadius();
306
307 // check parameters are valid
308 if (horizontalWidth*0.5 < bpRadius)
309 {
310 std::string msg = "HorizontalWidth (" + std::to_string(horizontalWidth);
311 msg += ") must be greater than 2*beampipe radius (";
312 msg += std::to_string(2*bpRadius) + ")";
313 throw BDSException(__METHOD_NAME__, msg);
314 }
315
316 // layout markers for the pole and yoke - radially out from centre
317 poleStartRadius = bpRadius + lengthSafety;
318 yokeFinishRadius = horizontalWidth*0.5 - lengthSafety;
319 G4double totalLength = yokeFinishRadius - poleStartRadius;
324
325 G4int nPoles = 2*order;
326 // full circle is divided into segments for each pole
327 segmentAngle = CLHEP::twopi / (G4double)nPoles;
329
330 G4double minimumPoleFraction = 0.05;
331 // If the space occupied by the yoke / pole is < 5% of horizontalWidth, don't bother with
332 // pole and coil - it's clearly unphysical.
333 if ( (yokeFinishRadius - yokeStartRadius) < (minimumPoleFraction * horizontalWidth) )
334 {
335 buildPole = false;
336 yokeStartRadius = poleStartRadius; // make the magnet all yoke
337 }
338}
339
341 G4double length,
342 G4int order)
343{
344 // calculate geometrical parameters first.
345 // pole is ellipse at tip, then (possibly) tapered section going outwards and then
346 // a section that is straight - ie constant width going outwards. This is later
347 // intersected with the yoke solid to give the matching shape. Therefore, the pole
348 // is made slightly too long (ie > poleFinishRadius).
349
350 // make some % of pole length the curved ellipsoidal surface at the pole tip
351 G4double poleLength = poleFinishRadius - poleStartRadius - 2*lengthSafety;
352 G4double ellipsoidHeight = poleTipFraction*poleLength; // full height of an ellipse (2*a)
353
354 // calculate the ellipsoid centre radius from the centre of the magnet.
355 G4double ellipsoidCentreY = poleStartRadius + ellipsoidHeight*0.5;
356
357 // calculate the width (2*b) of the ellipse given its height - at the centre of the
358 // ellipsoid, go out by the pole angle and that's the half width, x2.
359 G4double ellipsoidWidth = 2 * ellipsoidCentreY * std::tan(poleAngle*0.5);
360 // don't allow the pole to be wider than it is long - silly proportions - stop the scaling
361 ellipsoidWidth = std::min(ellipsoidWidth, poleLength);
362
363 // calculate the distance from the centre of the magnet where the straight sides start
364 G4double fraction = std::min(0.5, (G4double)order/10.0); // scale slightly with order
365 poleSquareStartRadius = ellipsoidCentreY + (poleFinishRadius - ellipsoidCentreY) * fraction;
366
367 // poleSquareWidth is initially calculated in CalculatePoleAndYoke - here we check it
368 // ensure pole doesn't get narrower than ellipsoid tip part
369 poleSquareWidth = std::max(poleSquareWidth, ellipsoidWidth);
370 // but ensure the square section can't protrude outside the angular segment for a single pole
371 G4double maxPoleSquareWidth = 2 * poleSquareStartRadius * std::tan(0.5*poleAngle);
372 poleSquareWidth = std::min(maxPoleSquareWidth, poleSquareWidth);
373
374 // points that define extruded polygon
375 std::vector<G4TwoVector> points;
376
377 // generate points on a ellipse in the positive quadrant for the pole tip
378 // note, the QT visualiser can't visualise the union of an ellipsoid and extruded
379 // polygon so can't use that method and this method produces a better transition
380 // between the ellipse and the pole. We just generate an extruded solid for the whole pole.
381 std::vector<G4double> xEllipse;
382 std::vector<G4double> yEllipse;
383 const G4int numAngPoints = 6;
384 const G4double iterant = CLHEP::halfpi/(G4double)numAngPoints;
385 for (G4double angle = 0; angle < CLHEP::halfpi; angle += iterant)
386 {
387 xEllipse.push_back(0.5*ellipsoidWidth*std::sin(angle));
388 yEllipse.push_back(0.5*ellipsoidHeight*std::cos(angle));
389 }
390
391 // add bottom left quadrant - miss out first point so don't reach apex.
392 for (G4int i = 0; i < (G4int)xEllipse.size()-1; i++)
393 {points.emplace_back(xEllipse[i], ellipsoidCentreY-yEllipse[i]);}
394 points.emplace_back(poleSquareWidth*0.5, poleSquareStartRadius);
395 // top points are x poleStopFactor for unambiguous intersection later on
396 points.emplace_back(poleSquareWidth*0.5, poleFinishRadius*poleStopFactor);
397 points.emplace_back(-poleSquareWidth*0.5, poleFinishRadius*poleStopFactor);
398 points.emplace_back(-poleSquareWidth*0.5, poleSquareStartRadius);
399 // add bottom right quadrant - miss out first point so don't reach apex.
400 // required start index is of course size()-1 also.
401 for (G4int i = (G4int)xEllipse.size()-2; i > 0; i--)
402 {points.emplace_back(-xEllipse[i], ellipsoidCentreY-yEllipse[i]);}
403
404 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
405 G4double zScale = 1; // the scale at each end of the points = 1
406 poleSolid = new G4ExtrudedSolid(name + "_pole_raw_solid", // name
407 points, // transverse 2d coordinates
408 length*0.5 - lengthSafety, // z half length
409 zOffsets, zScale, // dx,dy offset for each face, scaling
410 zOffsets, zScale); // dx,dy offset for each face, scaling
411
412 allSolids.insert(poleSolid);
413}
414
416 G4double length)
417{
418 // create an extruded polygon even though a square to avoid the need for
419 // individual rotated translations. This also allows the same rotation
420 // to be used for the coils as the poles.
422
423 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
424 G4double zScale = 1; // the scale at each end of the points = 1
425
426 coilLeftSolid = new G4ExtrudedSolid(name + "_coil_left_solid", // name
427 leftPoints, // transverse 2d coordinates
428 length*0.5 - lengthSafety, // z half length
429 zOffsets, zScale, // dx,dy offset for each face, scaling
430 zOffsets, zScale);// dx,dy offset for each face, scaling
431
432 coilRightSolid = new G4ExtrudedSolid(name + "_coil_right_solid", // name
433 rightPoints, // transverse 2d coordinates
434 length*0.5 - lengthSafety, // z half length
435 zOffsets, zScale, // dx,dy offset for each face, scaling
436 zOffsets, zScale);// dx,dy offset for each face, scaling
437
438 allSolids.insert(coilLeftSolid);
439 allSolids.insert(coilRightSolid);
440}
441
443{
444 G4double innerX = 0.5*poleSquareWidth + lengthSafetyLarge;
445
446 // Start the coil just after the pole becomes square in cross-section
447 // start it just beyond this point - say + 20% square section length
449
450 // At lower Y, project out in X as far as possible within the pole segment
451 G4double outerX = 0.9 * lowerY * tan (segmentAngle*0.5);
452
453 // At outer X, we find out the maximum y before hitting the inside of the yoke and
454 // back off a wee bit
455 G4double upperY = 0.95 * std::abs(std::sqrt(std::pow(yokeStartRadius,2) - std::pow(outerX,2)));
456
457 coilHeight = upperY - lowerY;
458 G4double dx = outerX - innerX;
459 // Check proportions and make roughly square.
460 if (coilHeight < dx)
461 {
462 G4double newXHeight = std::max(dx*0.5, dx - (dx-coilHeight));
463 outerX = innerX + newXHeight;
464 // recalculate upperY given the new outerX
465 upperY = 0.95 * std::abs(std::sqrt(std::pow(yokeStartRadius,2) - std::pow(outerX,2)));
466 }
467
468 // update coil height as used for end piece construction later
469 coilHeight = upperY - lowerY;
470
471 coilCentreRadius = lowerY + 0.5*coilHeight;
474
475 leftPoints.emplace_back(innerX, lowerY);
476 leftPoints.emplace_back(outerX, lowerY);
477 leftPoints.emplace_back(outerX, upperY);
478 leftPoints.emplace_back(innerX, upperY);
479
480 // must be in clockwise order
481 rightPoints.emplace_back(-innerX, lowerY);
482 rightPoints.emplace_back(-innerX, upperY);
483 rightPoints.emplace_back(-outerX, upperY);
484 rightPoints.emplace_back(-outerX, lowerY);
485
486 // this will be the eventual length along z but for now its the amplitude in y.
487 // make slightly smaller version as endPieceLength used for container dimensions
488 endPieceLength = (outerX - innerX) - lengthSafetyLarge;
489 for (G4double angle = 0; angle <= CLHEP::halfpi; angle+= CLHEP::halfpi / 8.0)
490 {
491 G4double x = outerX + endPieceLength * (cos(angle) - 1.0);
492 G4double y = endPieceLength * std::sin(angle);
493 endPiecePoints.emplace_back(x,y);
494 }
495 for (G4double angle = 0; angle <= CLHEP::halfpi; angle+= CLHEP::halfpi / 8.0)
496 {
497 G4double x = -outerX - endPieceLength * (std::sin(angle) - 1.0);
498 G4double y = endPieceLength * std::cos(angle);
499 endPiecePoints.emplace_back(x,y);
500 }
501}
502
504 G4double length,
505 G4int /*order*/,
506 G4double magnetContainerLength,
507 G4double magnetContainerRadiusIn)
508{
509 // circular yoke so pretty easy
510 yokeSolid = new G4Tubs(name + "_yoke_solid", // name
511 yokeStartRadius, // start radius
512 yokeFinishRadius, // finish radius
513 length*0.5 - lengthSafety, // z half length
514 0, // start angle
515 CLHEP::twopi); // sweep angle
516
517 poleIntersectionSolid = new G4Tubs(name + "_yoke_intersection_solid", // name
518 0, // start radius
520 length, // long half-length for unambiguous intersection
521 0, // start angle
522 CLHEP::twopi); // sweep angle
523 allSolids.insert(poleIntersectionSolid);
524
525
526 // note container must have hole in it for the beampipe to fit in!
527 // poled geometry doesn't fit tightly to beampipe so can always use a circular aperture
528 containerSolid = new G4Tubs(name + "_outer_container_solid", // name
529 poleStartRadius, // start radius
530 yokeFinishRadius + lengthSafety, // finish radius
531 length*0.5, // z half length
532 0, // start angle
533 CLHEP::twopi); // sweep angle
534 allSolids.insert(yokeSolid);
535
536 // magnet container radius calculated when poles are calculated and assigned to
537 // BDSMagnetOuterFactoryBase::magnetContainerRadius
538 BuildMagnetContainerSolidStraight(name, magnetContainerLength, magnetContainerRadiusIn);
539}
540
542 G4double /*length*/,
543 G4int /*order*/)
544{
545 // cut pole here with knowledge of yoke shape.
546 poleSolid = new G4IntersectionSolid(name + "_pole_solid", // name
547 poleSolid, // solid a
548 poleIntersectionSolid); // solid b
549}
550
552 G4Colour* colour,
553 G4Material* outerMaterial)
554{
555 BDSMagnetOuterFactoryBase::CreateLogicalVolumes(name, colour, outerMaterial);
557}
558
560{
561 if (coilLeftSolid)
562 { // if one exists, all coil solids exist
563 G4Material* coilMaterial = BDSMaterials::Instance()->GetMaterial("copper");
564 coilLeftLV = new G4LogicalVolume(coilLeftSolid,
565 coilMaterial,
566 name + "_coil_left_lv");
567
568 coilRightLV = new G4LogicalVolume(coilRightSolid,
569 coilMaterial,
570 name + "_coil_right_lv");
571
572 G4Colour* coil = BDSColours::Instance()->GetColour("coil");
573 G4VisAttributes* coilVisAttr = new G4VisAttributes(*coil);
574 coilVisAttr->SetVisibility(true);
575
576 coilLeftLV->SetVisAttributes(coilVisAttr);
577 coilRightLV->SetVisAttributes(coilVisAttr);
578 allVisAttributes.insert(coilVisAttr);
579
580 coilLeftLV->SetUserLimits(defaultUserLimits);
581 coilRightLV->SetUserLimits(defaultUserLimits);
582 }
583}
584
586 G4double& coilHeightFraction)
587{
588 const G4double lowerLimit = 0.05;
589 const G4double upperLimit = 0.98;
590 std::vector<G4double*> fractions = {&coilWidthFraction, &coilHeightFraction};
591 G4String names[2] = {"coilWidthFraction", "coilHeightFraction"};
592 for (G4int i = 0; i < 2; i++)
593 {
594 if ((*(fractions[i]) < lowerLimit) && (*(fractions[i]) >= 0))
595 {
596 *(fractions[i]) = lowerLimit;
597 G4cout << names[i] << " is below lower limit - limiting to " << lowerLimit << G4endl;
598 }
599 else if (*(fractions[i]) > upperLimit)
600 {
601 *(fractions[i]) = upperLimit;
602 G4cout << names[i] << " is above upper limit - limiting to " << upperLimit << G4endl;
603 }
604 else
605 {continue;}
606 }
607}
608
610 G4int order)
611{
612 // place the components inside the container
613 yokePV = new G4PVPlacement((G4RotationMatrix *) nullptr, // no rotation
614 G4ThreeVector(), // position
615 yokeLV, // lv to be placed
616 name + "_yoke_pv", // name
617 containerLV, // mother lv to be placed in
618 false, // no boolean operation
619 0, // copy number
620 checkOverlaps); // whether to check overlaps
621 allPhysicalVolumes.insert(yokePV);
622
623 // place poles
624 if (!buildPole)
625 {return;}
626 // else continue and place poles and coils
627 G4PVPlacement* aPolePV = nullptr;
628 for (G4int n = 0; n < 2*order; ++n)
629 {
630 G4RotationMatrix* rm = new G4RotationMatrix();
631 allRotationMatrices.insert(rm);
632 rm->rotateZ((n+0.5)*segmentAngle + CLHEP::pi*0.5);
633 G4String pvName = name + "_pole_" + std::to_string(n) + "_pv";
634 // poleTranslation is by default (0,0,0)
635 aPolePV = new G4PVPlacement(rm, // rotation
636 poleTranslation, // position
637 poleLV, // logical volume
638 pvName, // name
639 containerLV, // mother lv to be placed in
640 false, // no boolean operation
641 n, // copy number
642 checkOverlaps); // check overlaps
643 allPhysicalVolumes.insert(aPolePV);
644 }
645}
646
648 G4int order)
649{
650 if (!buildPole)
651 {return;}
652 G4PVPlacement* coilLeftPV = nullptr;
653 G4PVPlacement* coilRightPV = nullptr;
654 G4PVPlacement* endCoilPV = nullptr;
655 G4RotationMatrix* endCoilRM = new G4RotationMatrix();
656 endCoilRM->rotateX(CLHEP::halfpi);
657
658 G4ThreeVector endCoilTranslation(0,coilCentreRadius,0.5*endPieceLength);
659
660 for (G4int n = 0; n < 2*order; ++n)
661 {
662 // prepare a new rotation matrix - must be new and can't reuse the same one
663 // as the placement doesn't own it - changing the existing one will affect all
664 // previously placed objects
665 G4RotationMatrix* rm = new G4RotationMatrix();
666 G4RotationMatrix* ecrm = new G4RotationMatrix(*endCoilRM);
667 allRotationMatrices.insert(rm);
668 G4double rotationAngle = (n+0.5)*segmentAngle + CLHEP::pi*0.5;
669 rm->rotateZ((n+0.5)*segmentAngle + CLHEP::pi*0.5);
670
671 // nominally this should be around z axis, but I guess z is now y give
672 // rotation already... could do with rotate about axis in future
673 ecrm->rotateY(rotationAngle);
674
675 G4String pvName = name + "_coil_" + std::to_string(n);
676 coilLeftPV = new G4PVPlacement(rm, // rotation
677 G4ThreeVector(), // position
678 coilLeftLV, // logical volume
679 pvName + "_left_pv",// name
680 containerLV, // mother lv to be placed in
681 false, // no boolean operation
682 n, // copy number
683 checkOverlaps); // check overlaps
684
685 coilRightPV = new G4PVPlacement(rm, // rotation
686 G4ThreeVector() , // position
687 coilRightLV, // logical volume
688 pvName + "_right_pv",// name
689 containerLV, // mother lv to be placed in
690 false, // no boolean operation
691 n, // copy number
692 checkOverlaps); // check overlaps
693
694 G4ThreeVector placementOffset = G4ThreeVector(endCoilTranslation);
695 placementOffset.rotateZ(rotationAngle);
696 endCoilPV = new G4PVPlacement(ecrm, // rotation
697 placementOffset, // position
698 endPieceCoilLV, // logical volume
699 name + "_end_piece_coil_pv", // name
700 endPieceContainerLV, // mother lv to be placed in
701 false, // no boolean operation
702 n, // copy number
703 checkOverlaps); // check overlaps
706
707 allPhysicalVolumes.insert(coilLeftPV);
708 allPhysicalVolumes.insert(coilRightPV);
709 }
710 delete endCoilRM;
711}
712
714{
715 // container solid
716 endPieceContainerSolid = new G4Tubs(name + "_end_container_solid", // name
717 endPieceInnerR, // inner radius
718 endPieceOuterR, // outer radius
719 endPieceLength*0.5, // z half length
720 0, // start angle
721 CLHEP::twopi); // sweep angle
722
723 // container lv
724 G4Material* worldMaterial = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->WorldMaterial());
725 endPieceContainerLV = new G4LogicalVolume(endPieceContainerSolid, // solid
726 worldMaterial, // material
727 name + "_end_container_lv");// name
728
729 // coil end piece solid
730 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
731 G4double zScale = 1; // the scale at each end of the points = 1
732 G4VSolid* endPieceCoilSolid = new G4ExtrudedSolid(name + "_end_coil_solid", // name
733 endPiecePoints, // transverse 2d coordinates
734 coilHeight*0.5, // z half length
735 zOffsets, zScale,
736 zOffsets, zScale);
737
738 // coil end piece lv
739 G4Material* copper = BDSMaterials::Instance()->GetMaterial("copper");
740 endPieceCoilLV = new G4LogicalVolume(endPieceCoilSolid, // solid
741 copper, // material
742 name + "_end_coil_lv"); // name
743
744 // vis attributes - copy the coil ones that are already built
745 G4VisAttributes* endPieceCoilVis = new G4VisAttributes(*(coilLeftLV->GetVisAttributes()));
746 endPieceCoilLV->SetVisAttributes(endPieceCoilVis);
747 // container vis attributes are held in global constants - only need copy pointer
748 endPieceContainerLV->SetVisAttributes(containerLV->GetVisAttributes());
749
750 // user limits - don't register as using global one
751 endPieceCoilLV->SetUserLimits(defaultUserLimits);
753
754 // package it all up
755 endPiece = new BDSSimpleComponent(name + "_end_piece",
757 0,
761
762 endPiece->RegisterSolid(endPieceCoilSolid);
764 endPiece->RegisterVisAttributes(endPieceCoilVis);
765 if (sensitiveOuter)
766 {endPiece->RegisterSensitiveVolume(endPieceCoilLV, BDSSDType::energydep);}
768}
769
771 G4double angleIn,
772 G4double angleOut,
773 G4double length,
774 G4double& horizontalWidth,
775 G4Material*& material,
776 G4double& vhRatio)
777{
778 CleanUp();
779
780 if (!material)
781 {material = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->EmptyMaterial());}
782
783 // Test faces
784 if (BDS::WillIntersect(-angleIn, -angleOut, horizontalWidth, length))
785 {
786 std::string msg = "Error: Faces of magnet (section) named \"" + name + "\" will overlap!\n";
787 msg += "Length of magnet " + std::to_string(length);
788 msg += " mm is too short for the angle of the pole faces: (" + std::to_string(angleIn);
789 msg += "," + std::to_string(angleOut) + ").";
790 throw BDSException(__METHOD_NAME__, msg);
791 }
792
793 // vhRatio - don't allow a ratio greater than 10:1
794 if (vhRatio > 10)
795 {
796 vhRatio = 10;
797 G4cout << __METHOD_NAME__ << "coercing vhRatio to (maximum of) 10 for element " << name << G4endl;
798 }
799 else if (vhRatio < 0.1)
800 {
801 vhRatio = 0.1;
802 G4cout << __METHOD_NAME__ << "coercing vhRatio to (minimum of) 0.1 for element " << name << G4endl;
803 }
804}
805
807 G4bool buildVertically,
808 const BDSBeamPipe* beamPipe,
809 G4double length,
810 G4double horizontalWidth,
811 G4double angleIn,
812 G4double angleOut,
813 G4double yokeThicknessFraction,
814 G4double vhRatio,
815 G4double coilWidthFraction,
816 G4double coilHeightFraction,
817 G4double& cShapeOuterEdge,
818 G4double& poleHalfGap,
819 G4double& poleWidth,
820 G4double& poleHeight,
821 G4double& yokeWidth,
822 G4double& yokeHalfHeight,
823 G4double& yokeThickness,
824 G4double& yokeOverHang,
825 G4double& coilWidth,
826 G4double& coilHeightIn,
827 G4double& coilToYokeGap,
828 G4double& coilToPoleGap,
829 G4double& sLength,
830 G4double& containerSLength,
831 G4double& intersectionRadius)
832{
833 // swap vertical to horizontal ratio if building vertically so we can build it
834 // all horizontally here and then flip geometry later all at once
835 G4double vhRatioL = buildVertically ? 1./vhRatio : vhRatio;
836
837 G4double horizontalSize = buildVertically ? vhRatio * horizontalWidth : horizontalWidth;
838
839 // real beam pipe width
840 G4double bpHalfWidth = beamPipe->GetExtent().MaximumX();
841 G4double bpHalfHeight = beamPipe->GetExtent().MaximumY();
842 // need to flip if building vertically - as if beam pipe rotated for
843 // magnet that's always built horizontally
844 if (buildVertically)
845 {std::swap(bpHalfWidth, bpHalfHeight);}
846
847 // propose pole covers width of beam pipe
848 poleWidth = 2 * bpHalfWidth + 2*lengthSafetyLarge;
849 // take maximum of this (could be very small beam pipe) or ~1/3 of full width (normal proportion)
850 poleWidth = std::max(poleWidth, horizontalSize*0.36);
851 // in the case of a very wide beam pipe, we can't build a pole that matches
852 if (poleWidth > 0.9*horizontalSize)
853 {poleWidth = horizontalSize*0.7;} // cap at 70% of full width (think H style here)
854
855 // if building a c-shaped magnet, record (for a horizontal c-shape magnet) where
856 // the magnet containter volume should come into, between the poles.
857 cShapeOuterEdge = bpHalfWidth + lengthSafetyLarge;
858 cShapeOuterEdge = std::max(cShapeOuterEdge, 0.5 * poleWidth);
859
860 // propose gap between beam centre and pole tip
861 poleHalfGap = bpHalfHeight + lengthSafetyLarge;
862
863 // propose outer dimensions.
864 yokeWidth = horizontalSize; // horizontal (full)
865 G4double yokeHeight = horizontalSize * vhRatioL;// vertical (full)
866
867 // ensure yoke is bigger than beam pipe + small margin for minimum yoke thickness
868 // note we shouldn't get to this stage without a check already if the beam pipe will
869 // fit in the yoke, however, we check again here because we have a more accurate idea
870 // of the yoke size including vhRatio.
871 const G4double margin = 50*CLHEP::um; // minimum allowable 'yoke'
872 G4double yokeWidthLowerLimit = 2 * bpHalfWidth + margin;
873 G4double yokeHeightLowerLimit = 2 * bpHalfHeight + margin;
874 if (yokeWidth <= yokeWidthLowerLimit)
875 {yokeWidth = yokeWidthLowerLimit + margin;}
876 if (yokeHeight <= yokeHeightLowerLimit)
877 {yokeHeight = yokeHeightLowerLimit + margin;}
878
879 // propose yoke thickness
880 yokeThickness = yokeThicknessFraction * horizontalSize;
881 // if there's not enough space (given the beam pipe and outer edge)
882 // for the specified fraction of yoke, don't build pole. Also coerce
883 // yoke thickness.
884 if (yokeThickness > 0.5 * yokeHeight - poleHalfGap + lengthSafetyLarge)
885 {
886 buildPole = false;
887 yokeThickness = 0.5 * yokeHeight - poleHalfGap + lengthSafetyLarge;
888 }
889
890 // don't build pole if there's not enough room - coerce yoke thickness
891 G4double factor = hStyle ? 2.0 : 1.0; // 2x thickness for h style
892 if (factor * yokeThickness > yokeWidth - poleWidth)
893 {
894 buildPole = false;
895 yokeThickness = (yokeWidth - poleWidth) / factor;
896 }
897
898 // don't build pole if yoke tight around beam pipe - coerce yoke thickness
899 if (factor * yokeThickness > yokeWidth - 2 * bpHalfWidth)
900 {
901 buildPole = false;
902 yokeThickness = (yokeWidth - 2 * bpHalfWidth) / factor;
903 }
904
905 if (buildPole)
906 {poleHeight = 0.5 * yokeHeight - yokeThickness - poleHalfGap;}
907 else
908 {poleHeight = 0;}
909
910 // ensure the yoke isn't too thick - choose smaller of two limits
911 G4double yokeThicknessLimitHorizontal = yokeWidth - yokeWidthLowerLimit - margin*0.5;
912 G4double yokeThicknessLimitVertical = 0.5 * yokeHeight - yokeHeightLowerLimit - margin;
913 G4double yokeThicknessLimit = std::min(yokeThicknessLimitHorizontal, 2*yokeThicknessLimitVertical);
914 if (yokeThickness > yokeThicknessLimit)
915 {yokeThickness = yokeThicknessLimit;}
916 if (yokeThickness < margin)
917 {yokeThickness = margin;} // ensure minimum width of yoke
918
919 // done with yoke sizing - updated parameters
920 yokeHalfHeight = 0.5 * yokeHeight;
921
922 // prevent negative coil widths by yoke becoming too wide in the case
923 // of a wide pole
924 if (yokeThickness + poleWidth > horizontalSize - margin)
925 {yokeThickness = horizontalWidth - poleWidth - margin;}
926
927 if (hStyle)
928 {yokeOverHang = 0.5*yokeWidth - yokeThickness - 0.5*poleWidth;}
929 else
930 {yokeOverHang = yokeWidth - yokeThickness - poleWidth;}
931
932 // must ensure that:
933 // yoke length < outer container length < full magnet container length
934 // whether straight or angled
935 // yoke full length -> length - 2*lsl
936 // outer container full length -> length - 2*ls
937 // full magnet container full length -> container length
938 // if we have angled faces, make square faced solids longer for intersection.
939 sLength = BDS::CalculateSafeAngledVolumeLength(angleIn, angleOut, length, yokeWidth, yokeHalfHeight);
940 containerSLength = sLength;
941
942 intersectionRadius = std::hypot(0.5*poleWidth + yokeOverHang, poleHalfGap + poleHeight);
943 // if finite thickness yoke (independent of overall size)
944 if (yokeThickness > 0.05*horizontalSize) // nicely round edges of outer side without cutting inner
945 {intersectionRadius += 0.8 * std::hypot(yokeThickness, yokeThickness);}
946 else
947 {intersectionRadius *= 1.3;} // some margin
948
949 coilWidth = yokeOverHang * coilWidthFraction;
950 coilHeightIn = poleHeight * coilHeightFraction;
951 coilToYokeGap = (poleHeight - coilHeightIn) * 0.5;
952 coilToPoleGap = lengthSafetyLarge;
953}
954
955std::vector<G4ThreeVector> BDSMagnetOuterFactoryPolesBase::CalculateCoilDisplacements(G4double poleHalfWidthIn,
956 G4double poleHalfGapIn,
957 G4double coilWidthIn,
958 G4double coilHeightIn,
959 G4double cDY,
960 G4double& coilDY)
961{
962 // T = top, B = bottom, L = left, R = right
963 G4double coilDX = poleHalfWidthIn + 0.5*coilWidthIn + lengthSafetyLarge;
964 coilDY = poleHalfGapIn + 0.5*coilHeightIn + lengthSafetyLarge + cDY;
965
966 G4ThreeVector coilTLDisp = G4ThreeVector( coilDX, coilDY, 0);
967 G4ThreeVector coilTRDisp = G4ThreeVector(-coilDX, coilDY, 0);
968 G4ThreeVector coilBLDisp = G4ThreeVector( coilDX,-coilDY, 0);
969 G4ThreeVector coilBRDisp = G4ThreeVector(-coilDX,-coilDY, 0);
970 std::vector<G4ThreeVector> coilDisps;
971 coilDisps.push_back(coilTLDisp);
972 coilDisps.push_back(coilTRDisp);
973 coilDisps.push_back(coilBLDisp);
974 coilDisps.push_back(coilBRDisp);
975 return coilDisps;
976}
977
979 G4double length,
980 const BDSBeamPipe* beamPipe,
981 G4double containerLength,
982 const BDSMagnetOuterInfo* recipe,
983 G4bool buildVertically)
984{
985 G4double horizontalWidth = recipe->horizontalWidth;
986 G4double angleIn = recipe->angleIn;
987 G4double angleOut = recipe->angleOut;
988 G4Material* material = recipe->outerMaterial;
989 G4Colour* colour = recipe->colour;
990 G4bool buildEndPiece = recipe->buildEndPieces;
991 G4double vhRatio = recipe->vhRatio;
992 G4double coilWidthFraction = recipe->coilWidthFraction;
993 G4double coilHeightFraction = recipe->coilHeightFraction;
994 G4bool yokeOnLeft = recipe->yokeOnLeft;
995
996 DipoleCommonPreConstruction(name, angleIn, angleOut, length, horizontalWidth, material, vhRatio);
997 TestCoilFractions(coilWidthFraction, coilHeightFraction);
998
999 // 1 calculations
1000 // 2 c shaped solid
1001 // 3 angled solids for intersection
1002 // 4 intersection solids
1003 // 5 logical volumes
1004 // 6 placement
1005 // general order - yoke, container, magnet container, coils
1006
1007 G4double cShapeOuterEdge = 0;
1008 G4double poleHalfGap = 0;
1009 G4double poleWidth = 0;
1010 G4double poleHeight = 0;
1011 G4double yokeWidth = 0;
1012 G4double yokeHalfHeight = 0;
1013 G4double yokeThickness = 0;
1014 G4double yokeOverHang = 0;
1015 G4double coilWidth = 0;
1016 // coilHeight is class member
1017 G4double coilToYokeGap = 0;
1018 G4double coilToPoleGap = 0;
1019 G4double sLength = length; // 'safe' length fo intersection - default is normal length
1020 G4double containerSLength = containerLength; // similarly for the container
1021 G4double intersectionRadius = 0;
1022 DipoleCalculations(false, buildVertically, beamPipe, length, horizontalWidth, angleIn,
1023 angleOut, 0.23, vhRatio, coilWidthFraction, coilHeightFraction,
1024 cShapeOuterEdge, poleHalfGap, poleWidth, poleHeight,
1025 yokeWidth, yokeHalfHeight, yokeThickness, yokeOverHang, coilWidth,
1026 coilHeight, coilToYokeGap, coilToPoleGap,
1027 sLength, containerSLength, intersectionRadius);
1028
1029 //G4double yokeInsideX = 0.5*poleWidth - yokeWidth + yokeThickness;
1030 G4double yokeInsideX = -0.5*poleWidth - yokeOverHang;
1031
1032 // vertical offset of coil from pole tip
1033 G4double cDY = (poleHeight - coilHeight)*0.5;
1034
1035 // coil displacements
1036 std::vector<G4ThreeVector> coilDisps;
1037 G4double coilDY = 0;
1038 if (buildPole)
1039 {
1040 coilDisps= CalculateCoilDisplacements(0.5*poleWidth, poleHalfGap, coilWidth,
1041 coilHeight, cDY, coilDY);
1042 }
1043
1044 // create vectors of points for extruded solids
1045 // create yoke + pole (as one solid about 0,0,0)
1046 std::vector<G4TwoVector> yokePoints;
1047 std::vector<G4TwoVector> cPoints; // container points (for magnet outer only)
1048 std::vector<G4TwoVector> mCPoints; // magnet container points
1049
1050 // variables for extents
1051 G4double extXPos = 0;
1052 G4double extXNeg = 0;
1053 G4double extYPos = 0;
1054 G4double extYNeg = 0;
1055 // Typically, we have a positive bend angle that (by convention) causes a
1056 // bend to the -ve x direction in right-handed coordinates. Also, typically,
1057 // a C shaped magnet has the yoke to the inside so there is an aperture for
1058 // any radiation to escape to the outside. Therefore, we build the yoke like this
1059 // and flip it if required. Points are done in clock wise order from the bottom left
1060 // corner of the top pole.
1061 const G4double lsl = lengthSafetyLarge; // shortcut
1062 const G4double phw = 0.5*poleWidth; // shortcut
1063 const G4double phg = poleHalfGap; // shortcut
1064 const G4double yw = yokeWidth; // shortcut
1065 const G4double yhh = yokeHalfHeight; // shortcut
1066 yokePoints.emplace_back(phw - lsl, phg + lsl);
1067 yokePoints.emplace_back(phw - lsl, yhh - lsl);
1068 yokePoints.emplace_back(phw + lsl - yw, yhh - lsl);
1069 yokePoints.emplace_back(phw + lsl - yw, -yhh + lsl);
1070 yokePoints.emplace_back(phw - lsl, -yhh + lsl);
1071 yokePoints.emplace_back(phw - lsl, -phg - lsl);
1072 if (buildPole)
1073 {
1074 yokePoints.emplace_back(-phw + lsl, -phg - lsl);
1075 yokePoints.emplace_back(-phw + lsl, -yhh + yokeThickness);
1076 yokePoints.emplace_back( yokeInsideX - lsl, -yhh + yokeThickness);
1077 yokePoints.emplace_back( yokeInsideX - lsl, yhh - yokeThickness);
1078 yokePoints.emplace_back(-phw + lsl, yhh - yokeThickness);
1079 yokePoints.emplace_back(-phw + lsl, phg + lsl);
1080 }
1081 else
1082 {
1083 yokePoints.emplace_back(yokeInsideX - lsl, -phg - lsl);
1084 yokePoints.emplace_back(yokeInsideX - lsl, phg + lsl);
1085 }
1086
1087 // points for container for magnet outer only
1088 if (buildPole == false) // redundant point when buildPole == true
1089 {cPoints.emplace_back(phw + lsl, phg);}
1090 else
1091 {
1092 cPoints.emplace_back(phw + coilWidth + 2*lsl, phg);
1093 cPoints.emplace_back(phw + coilWidth + 2*lsl, phg + coilHeight + 2*lsl + cDY);
1094 cPoints.emplace_back(phw + lsl, phg + coilHeight + 2*lsl + cDY);
1095 }
1096 cPoints.emplace_back(phw + lsl, yhh + lsl);
1097 cPoints.emplace_back(phw - yw - lsl, yhh + lsl);
1098 cPoints.emplace_back(phw - yw - lsl, -yhh - lsl);
1099 cPoints.emplace_back(phw + lsl, -yhh - lsl);
1100 if (buildPole)
1101 {
1102 cPoints.emplace_back(phw + lsl, -phg - coilHeight - 2*lsl - cDY);
1103 cPoints.emplace_back(phw + coilWidth + 2*lsl, -phg - coilHeight - 2*lsl - cDY);
1104 cPoints.emplace_back(phw + coilWidth + 2*lsl, -phg);
1105 }
1106 else // redundant point when buildPole == true
1107 {cPoints.emplace_back(phw + lsl, -phg);}
1108 cPoints.emplace_back(yokeInsideX, -phg);
1109 cPoints.emplace_back(yokeInsideX, phg);
1110
1111 // points for container for full magnet object including beam pipe
1112 // first one in y here is -lsl to cancel +lsl to phg originally
1113 // container can be same height as beam pipe as it's always wider
1114 G4double maxLeft = std::max(phw, cShapeOuterEdge);
1115 mCPoints.emplace_back(maxLeft + lsl, phg - lsl);
1116 if (buildPole)
1117 {
1118 mCPoints.emplace_back(phw + coilWidth + 4*lsl, phg - lsl);
1119 mCPoints.emplace_back(phw + coilWidth + 4*lsl, phg + coilHeight + 3*lsl + cDY);
1120 mCPoints.emplace_back(phw + 2*lsl, phg + coilHeight + 3*lsl + cDY);
1121 }
1122 mCPoints.emplace_back(phw + 2*lsl, yhh + 2*lsl);
1123 mCPoints.emplace_back(phw - yw - 2*lsl, yhh + 2*lsl);
1124 mCPoints.emplace_back(phw - yw - 2*lsl, -yhh - 2*lsl);
1125 mCPoints.emplace_back(phw + 2*lsl, -yhh - 2*lsl);
1126 if (buildPole)
1127 {
1128 mCPoints.emplace_back(phw + 2*lsl, -phg - coilHeight - 3*lsl - cDY);
1129 mCPoints.emplace_back(phw + coilWidth + 4*lsl, -phg - coilHeight - 3*lsl - cDY);
1130 mCPoints.emplace_back(phw + coilWidth + 4*lsl, -phg + lsl);
1131 }
1132 mCPoints.emplace_back(maxLeft + lsl, -phg + lsl);
1133
1134 // extents
1135 extXPos = phw + lsl;
1136 extXNeg = phw - yw - 2*lsl;
1137 extYPos = yhh + lsl;
1138 extYNeg = -(yhh +lsl);
1139 if (buildPole)
1140 {extXPos += coilWidth + lsl;}
1141
1142 if (yokeOnLeft)
1143 {
1144 // flip x component so geometry is reflected horizontally
1145 for (auto& vec : yokePoints)
1146 {vec.setX(vec.x() * -1);}
1147 // reverse order so it's clockwise as geant4 requires
1148 std::reverse(yokePoints.begin(), yokePoints.end());
1149
1150 // same for container
1151 for (auto& vec : cPoints)
1152 {vec.setX(vec.x() * -1);}
1153 std::reverse(cPoints.begin(), cPoints.end());
1154
1155 // same for magnet container
1156 for (auto& vec : mCPoints)
1157 {vec.setX(vec.x() * -1);}
1158 std::reverse(mCPoints.begin(), mCPoints.end());
1159
1160 // flip extents
1161 std::swap(extXPos, extXNeg);
1162 }
1163
1164 // rotate if building vertically
1165 if (buildVertically)
1166 {
1167 for (auto& point : yokePoints)
1168 {point = BDS::Rotate(point, CLHEP::halfpi);}
1169 for (auto& point : cPoints)
1170 {point = BDS::Rotate(point, CLHEP::halfpi);}
1171 for (auto& point : mCPoints)
1172 {point = BDS::Rotate(point, CLHEP::halfpi);}
1173 // 'rotate' extents too
1174 std::swap(extXPos, extYPos);
1175 std::swap(extXNeg, extYNeg);
1176 std::swap(coilWidth, coilHeight); // 'rotate' coil
1177 }
1178 BDSExtent ext = BDSExtent(extXNeg, extXPos, extYNeg, extYPos,
1179 -length*0.5, length*0.5);
1180 magContExtent = ext; // copy to container variable - basically the same
1181
1182 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
1183 G4double zScale = 1; // the scale at each end of the points = 1
1184 yokeSolid = new G4ExtrudedSolid(name + "_yoke_sq_solid",// name
1185 yokePoints, // transverse 2d coordinates
1186 sLength*0.5 - lsl, // z half length
1187 zOffsets, zScale, // dx,dy offset for each face, scaling
1188 zOffsets, zScale); // dx,dy offset for each face, scaling
1189
1190 // container for magnet outer
1191 containerSolid = new G4ExtrudedSolid(name + "_outer_sq_container_solid", // name
1192 cPoints, // transverse 2d coordinates
1193 sLength*0.5 - lengthSafety, // z half length
1194 zOffsets, zScale, // dx,dy offset for each face, scaling
1195 zOffsets, zScale);// dx,dy offset for each face, scaling
1196
1197 // container for full magnet
1198 magnetContainerSolid = new G4ExtrudedSolid(name + "_sq_container_solid", // name
1199 mCPoints, // transverse 2d coordinates
1200 containerSLength*0.5, // z half length
1201 zOffsets, zScale, // dx,dy offset for each face, scaling
1202 zOffsets, zScale);// dx,dy offset for each face, scaling
1203
1204 // register existing square solids as we're going to overwrite them with intersected ones
1205 allSolids.insert(yokeSolid);
1206 allSolids.insert(containerSolid);
1207 allSolids.insert(magnetContainerSolid);
1208
1209 return DipoleCommonConstruction(name, horizontalWidth, buildEndPiece, coilWidth, length,
1210 containerLength, sLength, angleIn, angleOut,
1211 colour, material,
1212 coilDisps, buildVertically, ext, 0.5*poleWidth, poleHalfGap,
1213 cDY, coilDY, intersectionRadius);
1214}
1215
1217 G4double length,
1218 const BDSBeamPipe* beamPipe,
1219 G4double containerLength,
1220 const BDSMagnetOuterInfo* recipe,
1221 G4bool buildVertically)
1222{
1223 G4double horizontalWidth = recipe->horizontalWidth;
1224 G4double angleIn = recipe->angleIn;
1225 G4double angleOut = recipe->angleOut;
1226 G4Material* material = recipe->outerMaterial;
1227 G4Colour* colour = recipe->colour;
1228 G4bool buildEndPiece = recipe->buildEndPieces;
1229 G4double vhRatio = recipe->vhRatio;
1230 G4double coilWidthFraction = recipe->coilWidthFraction;
1231 G4double coilHeightFraction = recipe->coilHeightFraction;
1232
1233 DipoleCommonPreConstruction(name, angleIn, angleOut, length, horizontalWidth, material, vhRatio);
1234 TestCoilFractions(coilWidthFraction, coilHeightFraction);
1235
1236 // 1 calculations
1237 // 2 h shaped solid
1238 // 3 angled solids for intersection
1239 // 4 intersection solids
1240 // 5 logical volumes
1241 // 6 placement
1242 // general order - yoke, container, magnet container, coils
1243
1244 G4double cShapeOuterEdge = 0;
1245 G4double poleHalfGap = 0;
1246 G4double poleWidth = 0;
1247 G4double poleHeight = 0;
1248 G4double yokeWidth = 0;
1249 G4double yokeHalfHeight = 0;
1250 G4double yokeThickness = 0;
1251 G4double yokeOverHang = 0;
1252 G4double coilWidth = 0;
1253 // coilHeight is member variable
1254 G4double coilToYokeGap = 0;
1255 G4double coilToPoleGap = 0;
1256 G4double sLength = length; // 'safe' length fo intersection - default is normal length
1257 G4double containerSLength = containerLength; // similarly for the container
1258 G4double intersectionRadius = 0;
1259 DipoleCalculations(true, buildVertically, beamPipe, length, horizontalWidth, angleIn,
1260 angleOut, 0.12, vhRatio, coilWidthFraction, coilHeightFraction,
1261 cShapeOuterEdge, poleHalfGap, poleWidth, poleHeight,
1262 yokeWidth, yokeHalfHeight, yokeThickness, yokeOverHang,
1263 coilWidth, coilHeight, coilToYokeGap, coilToPoleGap,
1264 sLength, containerSLength, intersectionRadius);
1265
1266 // distance from axis to inside of yoke horizontally
1267 G4double yokeInsideX = 0;
1268 if (buildPole)
1269 {yokeInsideX = 0.5*poleWidth + yokeOverHang;}
1270 else
1271 {yokeInsideX = 0.5*yokeWidth - yokeThickness;}
1272 //yokeWidth*0.5 - yokeThickness;
1273 //G4double yokeInsideY = yokeHalfHeight - yokeThickness;
1274 G4double yokeInsideY = 0;
1275 if (buildPole)
1276 {yokeInsideY = poleHalfGap + poleHeight;}
1277 else
1278 {yokeInsideY = yokeHalfHeight - yokeThickness;}
1279
1280 // Vertical offset of coil from pole tip
1281 G4double cDY = (poleHeight - coilHeight)*0.5;
1282
1283 const G4double lsl = lengthSafetyLarge; // shortcut
1284
1285 // coil displacements
1286 std::vector<G4ThreeVector> coilDisps;
1287 G4double coilDY = 0;
1288 if (buildPole)
1289 {
1290 coilDisps = CalculateCoilDisplacements(0.5*poleWidth, poleHalfGap,
1291 coilWidth, coilHeight, cDY, coilDY);
1292 }
1293
1294 // these may be used later so need to be outside if statement below
1295 // create vectors of points for extruded solids
1296 std::vector<G4TwoVector> yokePoints;
1297 std::vector<G4TwoVector> cPoints; // container points (for 'magnet outer' inner only)
1298 std::vector<G4TwoVector> mCPoints; // magnet container points
1299
1300 // variables for extents
1301 G4double extXPos = 0;
1302 G4double extXNeg = 0;
1303 G4double extYPos = 0;
1304 G4double extYNeg = 0;
1305
1306 G4VSolid* yokeInnerSolid = nullptr;
1307 if (buildPole)
1308 {// use extruded solid for inner yoke shape and box for out with subtraction
1309 // create yoke + pole (as one solid about 0,0,0)
1310 // points are done in clock wise order from the bottom right corner of the top pole.
1311 const G4double phw = 0.5*poleWidth;
1312 const G4double phg = poleHalfGap;
1313 yokePoints.emplace_back( phw - lsl, phg + lsl);
1314 yokePoints.emplace_back( phw - lsl, yokeInsideY + lsl);
1315 yokePoints.emplace_back( yokeInsideX + lsl, yokeInsideY + lsl);
1316 yokePoints.emplace_back( yokeInsideX + lsl, -yokeInsideY - lsl);
1317 yokePoints.emplace_back( phw - lsl, -yokeInsideY - lsl);
1318 yokePoints.emplace_back( phw - lsl, -phg - lsl);
1319 yokePoints.emplace_back(-phw + lsl, -phg - lsl);
1320 yokePoints.emplace_back(-phw + lsl, -yokeInsideY - lsl);
1321 yokePoints.emplace_back(-yokeInsideX - lsl, -yokeInsideY - lsl);
1322 yokePoints.emplace_back(-yokeInsideX - lsl, yokeInsideY + lsl);
1323 yokePoints.emplace_back(-phw + lsl, yokeInsideY + lsl);
1324 yokePoints.emplace_back(-phw + lsl, phg + lsl);
1325
1326 // rotate if building vertically
1327 if (buildVertically)
1328 {
1329 for (auto& point : yokePoints)
1330 {point = BDS::Rotate(point, CLHEP::halfpi);}
1331 }
1332
1333 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
1334 G4double zScale = 1; // the scale at each end of the points = 1
1335 yokeInnerSolid = new G4ExtrudedSolid(name + "_yoke_inner_solid", // name
1336 yokePoints, // transverse 2d coordinates
1337 sLength, // z half length
1338 zOffsets, zScale, // dx,dy offset for each face, scaling
1339 zOffsets, zScale);// dx,dy offset for each face, scaling
1340 // note 1.0x length > 0.5 length for unambiguous subtraction
1341 }
1342 else // if build pole
1343 {// don't build pole - just build yoke -> box
1344 G4double yIX = buildVertically ? yokeInsideY : yokeInsideX;
1345 G4double yIY = buildVertically ? yokeInsideX : yokeInsideY;
1346 yokeInnerSolid = new G4Box(name + "_yoke_inner_solid", // name
1347 yIX + lsl, // x half length
1348 yIY + lsl, // y half length
1349 sLength); // z half length
1350 // note 1.0x length > 0.5 length for unambiguous subtraction
1351 }
1352
1353 // extents
1354 extXPos = 0.5*yokeWidth + lsl;
1355 extXNeg = -0.5*yokeWidth - lsl;
1356 extYPos = 0.5*yokeWidth + lsl;
1357 extYNeg = -0.5*yokeWidth - lsl;
1358
1359 if (buildVertically)
1360 {// 'rotate' extents too
1361 std::swap(extXPos, extYPos);
1362 std::swap(extXNeg, extYNeg);
1363 std::swap(coilWidth, coilHeight); // 'rotate' coil
1364 }
1365 BDSExtent ext = BDSExtent(extXNeg, extXPos, extYNeg, extYPos,
1366 -length*0.5, length*0.5);
1367 magContExtent = ext; // copy to container variable - basically the same
1368
1369 G4double yokeOuterX = 0.5*yokeWidth - lsl;
1370 G4double yokeOuterY = yokeHalfHeight - lsl;
1371 G4double yOX = buildVertically ? yokeOuterY : yokeOuterX;
1372 G4double yOY = buildVertically ? yokeOuterX : yokeOuterY;
1373 G4VSolid* yokeOuterSolid = new G4Box(name + "_yoke_outer_solid", // name
1374 yOX, // x half length
1375 yOY, // y half length
1376 sLength * 0.5 - lsl); // z half length
1377
1378 yokeSolid = new G4SubtractionSolid(name + "_yoke_solid", // name,
1379 yokeOuterSolid, // this
1380 yokeInnerSolid); // minus this
1381
1382 // container for magnet outer
1383 G4double containerdx = 0.5 * yokeWidth - yokeThickness - lsl;
1384 G4double containerdy = poleHalfGap;
1385 if (buildVertically)
1386 {std::swap(containerdx, containerdy);}
1387
1388 // full length for unambiguous subtraction
1389 G4VSolid* containerInnerSolid = new G4Box(name + "_container_inner_solid", // name
1390 containerdx,
1391 containerdy,
1392 sLength);
1393 G4double cOX = 0.5 * yokeWidth;
1394 G4double cOY = yokeHalfHeight;
1395 if (buildVertically)
1396 {std::swap(cOX, cOY);}
1397 G4VSolid* containerOuterSolid = new G4Box(name + "_container_outer_solid", // name
1398 cOX, // x half length
1399 cOY, // y half length
1400 sLength * 0.5); // z half length
1401
1402 containerSolid = new G4SubtractionSolid(name + "_outer_sq_container_solid", // name
1403 containerOuterSolid, // this
1404 containerInnerSolid); // minus this
1405
1406 // container for full magnet
1407 magnetContainerSolid = new G4Box(name + "_sq_container_solid", // name
1408 cOX + lsl, // x half length
1409 cOY + lsl, // y half length
1410 containerSLength * 0.5); // z half length
1411
1412 // register existing square solids as we're going to overwrite them with intersected ones
1413 allSolids.insert(yokeInnerSolid);
1414 allSolids.insert(yokeOuterSolid);
1415 allSolids.insert(yokeSolid);
1416 allSolids.insert(containerInnerSolid);
1417 allSolids.insert(containerOuterSolid);
1418 allSolids.insert(containerSolid);
1419 allSolids.insert(magnetContainerSolid);
1420
1421 return DipoleCommonConstruction(name, horizontalWidth, buildEndPiece, coilWidth, length,
1422 containerLength, sLength, angleIn, angleOut,
1423 colour, material,
1424 coilDisps, buildVertically, ext, poleWidth*0.5, poleHalfGap,
1425 cDY, coilDY, intersectionRadius);
1426}
1427
1428BDSMagnetOuter* BDSMagnetOuterFactoryPolesBase::DipoleCommonConstruction(const G4String& name,
1429 G4double horizontalWidth,
1430 G4bool buildEndPiece,
1431 G4double coilWidth,
1432 G4double length,
1433 G4double containerLength,
1434 G4double sLength,
1435 G4double angleIn,
1436 G4double angleOut,
1437 G4Colour* colour,
1438 G4Material* material,
1439 std::vector<G4ThreeVector>& coilDisps,
1440 G4bool buildVertically,
1441 BDSExtent& ext,
1442 G4double poleHalfWidth,
1443 G4double poleHalfGap,
1444 G4double cDY,
1445 G4double coilDY,
1446 G4double intersectionRadius)
1447{
1448 const G4double lsl = lengthSafetyLarge; // shortcut
1449
1450 // create coil - one solid that will be placed 4 times... or
1451 // if we have angled faces, they're all unique unfortunately so use a vector of solids
1452 // and place individually
1453
1454 G4VSolid* coilSolid = nullptr;
1455 std::vector<G4VSolid*> coilsSolids;
1456 std::vector<G4LogicalVolume*> coilLVs;
1457 G4bool individualCoilsSolids = false;
1458 if (buildPole)
1459 {
1460 G4double cx = 0.5 * coilWidth - lsl;
1461 G4double cy = 0.5 * coilHeight - lsl;
1462 coilSolid = new G4Box(name + "_coil_solid", // name
1463 cx, // x half width
1464 cy, // y half height
1465 sLength*0.5 - 2*lsl); // z half length - same as yoke
1466 allSolids.insert(coilSolid);
1467 }
1468
1469 // Intersect and replace solids. Do it via replacement of the base class member G4VSolid*
1470 // as the intersection is only done if one of the angles is finite.
1471 if (BDS::IsFinite(angleIn) || BDS::IsFinite(angleOut))
1472 {
1473 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn,angleOut);
1474 inputFaceNormal = faces.first;
1475 outputFaceNormal = faces.second;
1476
1477 // angled solid for magnet outer and coils
1478 G4VSolid* angledFaces = new G4CutTubs(name + "_angled_face_solid", // name
1479 0, // inner radius
1480 intersectionRadius, // outer radius
1481 length*0.5 - 2*lsl, // z half length
1482 0, // start angle
1483 CLHEP::twopi, // sweep angle
1484 inputFaceNormal, // input face normal
1485 outputFaceNormal); // output face normal
1486
1487 // angled solid for magnet outer container volume
1488 G4VSolid* angledFacesCont = new G4CutTubs(name + "_angled_face_cont_solid", // name
1489 0, // inner radius
1490 intersectionRadius, // outer radius
1491 length*0.5 - lsl, // z half length
1492 0, // start angle
1493 CLHEP::twopi, // sweep angle
1494 inputFaceNormal, // input face normal
1495 outputFaceNormal); // output face normal
1496
1497 // angled solid for full magnet container
1498 G4VSolid* angledFacesMagCont = new G4CutTubs(name + "_angled_face_mag_cont_solid", // name
1499 0, // inner radius
1500 intersectionRadius, // outer radius
1501 containerLength*0.5, // z half length
1502 0, // start angle
1503 CLHEP::twopi, // sweep angle
1504 inputFaceNormal, // input face normal
1505 outputFaceNormal); // output face normal
1506
1507 // register angled solids
1508 allSolids.insert(angledFaces);
1509 allSolids.insert(angledFacesCont);
1510 allSolids.insert(angledFacesMagCont);
1511
1512 // now do intersections overwriting existing pointers
1513 yokeSolid = new G4IntersectionSolid(name + "_yoke_solid", // name
1514 yokeSolid, // solid a - existing yoke
1515 angledFaces); // solid b
1516
1517 containerSolid = new G4IntersectionSolid(name + "_outer_container_solid", // name
1518 containerSolid, // solid a
1519 angledFacesCont); // solid b
1520
1521 magnetContainerSolid = new G4IntersectionSolid(name + "_container_solid", // name
1522 magnetContainerSolid, // solid a
1523 angledFacesMagCont); // solid b
1524
1525 individualCoilsSolids = true; // flag that we have individual coil solids
1526 if (buildPole)
1527 {
1528 for (G4int i = 0; i < 4; i++)
1529 {
1530 G4VSolid* coilS = new G4IntersectionSolid(name + "_pole_solid_" + std::to_string(i), // name
1531 angledFaces, // solid a
1532 coilSolid, // solid b
1533 (G4RotationMatrix *) nullptr, // 0 rotation
1534 coilDisps[i]); // translation
1535 coilsSolids.push_back(coilS);
1536 allSolids.insert(coilS);
1537 }
1538 }
1539 }
1540
1541 // logical volumes
1542 CreateLogicalVolumes(name, colour, material);
1543 // we only use one coil solid here so do that here
1544 G4LogicalVolume* coilLV = nullptr;
1545 if (buildPole)
1546 {
1547 G4Colour* coil = BDSColours::Instance()->GetColour("coil");
1548 G4VisAttributes* coilVis = new G4VisAttributes(*coil);
1549 coilVis->SetVisibility(true);
1550 allVisAttributes.insert(coilVis);
1551 G4Material* coilMaterial = BDSMaterials::Instance()->GetMaterial("copper");
1552 if (individualCoilsSolids)
1553 {
1554 for (G4int i = 0; i < 4; i++)
1555 {
1556 G4String theName = name + "_coil_solid_" + std::to_string(i);
1557 G4LogicalVolume* aCoilLV = new G4LogicalVolume(coilsSolids[i],
1558 coilMaterial,
1559 theName);
1560 aCoilLV->SetVisAttributes(coilVis);
1561 coilLVs.push_back(aCoilLV);
1562 allLogicalVolumes.insert(aCoilLV);
1563 }
1564 }
1565 else
1566 {
1567 coilLV = new G4LogicalVolume(coilSolid,
1568 coilMaterial,
1569 name + "_coil_lv");
1570 coilLV->SetVisAttributes(coilVis);
1571 allLogicalVolumes.insert(coilLV);
1572 }
1573 }
1574 // user limits
1575 SetUserLimits();
1576 for (auto& lv : coilLVs)
1577 {lv->SetUserLimits(defaultUserLimits);}
1578 if (coilLV)
1579 {coilLV->SetUserLimits(defaultUserLimits);}
1580
1581 // placement
1582 // place yoke+pole (one solid) together
1583 G4bool buildPoleOriginal = buildPole;
1584 buildPole = false; // this will avoid PlaceComponents trying to place poles that don't exist
1585 PlaceComponents(name, 1); // places yoke
1586 buildPole = buildPoleOriginal;
1587 // place coils
1588 if (buildPole)
1589 {
1590 G4PVPlacement* aCoilPV = nullptr;
1591 // coils intersection was done such that the coordinates of the resultant solid
1592 // are the same as the plain coil G4Box. This allows us to programmatically place
1593 // the coils here irrespective if there's one or 4 unqiue coils.
1594 for (G4int i = 0; i < 4; i++)
1595 {
1596 G4LogicalVolume* volToPlace = coilLV;
1597 G4ThreeVector displacement = G4ThreeVector();
1598 if (individualCoilsSolids)
1599 {volToPlace = coilLVs[i];}
1600 else
1601 {displacement = coilDisps[i];} // with no intersection we have to displace it
1602 G4String theName = name + "_coil_" + std::to_string(i) + "_pv";
1603 if (buildVertically)
1604 {
1605 G4RotationMatrix* rotVert = new G4RotationMatrix();
1606 rotVert->rotateZ(CLHEP::halfpi);
1607 displacement.transform(*rotVert);
1608 delete rotVert;
1609 }
1610 aCoilPV = new G4PVPlacement(nullptr, // no rotation
1611 displacement, // position
1612 volToPlace, // lv to be placed
1613 theName, // name
1614 containerLV, // mother lv to be placed in
1615 false, // no boolean operation
1616 0, // copy number
1618 allPhysicalVolumes.insert(aCoilPV);
1619 }
1620 }
1621
1622 // magnet outer instance
1623 CreateMagnetContainerComponent();
1624
1625 // build the BDSMagnetOuter instance and return it
1626 BDSMagnetOuter* outer = new BDSMagnetOuter(containerSolid,
1627 containerLV, ext,
1628 magnetContainer);
1629
1630 outer->RegisterSolid(allSolids);
1631 outer->RegisterLogicalVolume(allLogicalVolumes);
1632 outer->RegisterRotationMatrix(allRotationMatrices);
1633 outer->RegisterPhysicalVolume(allPhysicalVolumes);
1634 outer->RegisterVisAttributes(allVisAttributes);
1635
1636 outer->RegisterSolid(yokeSolid);
1637 outer->RegisterLogicalVolume(yokeLV);
1638
1639 if (sensitiveOuter)
1640 {outer->RegisterSensitiveVolume(yokeLV, BDSSDType::energydep);}
1641
1642 // no need to proceed with end pieces if we didn't build poles - just return
1643 if (!buildPole)
1644 {return outer;}
1645
1646 // continue with registration of objects and end piece construction
1647 if (sensitiveOuter)
1648 {
1649 if (individualCoilsSolids)
1650 {
1651 std::set<G4LogicalVolume*> tempLVs(coilLVs.begin(), coilLVs.end());
1652 outer->RegisterSensitiveVolume(tempLVs, BDSSDType::energydep);}
1653 else
1654 {outer->RegisterSensitiveVolume(coilLV, BDSSDType::energydep);}
1655 }
1656 // skip rest of this construction if no end pieces required
1657 if (!buildEndPiece)
1658 {
1659 SetFaceNormals(outer); // would also update end pieces if they existed
1660 return outer;
1661 }
1662
1663 // end pieces - note with bends both are likely to be different so build independently here
1664
1665 // end piece coil solids - build in x-y plane and project in z as that's the
1666 // only way with an extruded solid then rotate so it's along z
1667 // curved sections are elliptical with major axis being the projected with of the coil
1668 // at an angle and the minor being the normal coil width.
1669 // x = x0 + acos(t)cos(phi) - bsin(t)sin(phi)
1670 // y = y0 + acos(t)sin(phi) + bsin(t)cos(phi)
1671 std::vector<G4TwoVector> inEPPoints; // input face end piece points
1672 std::vector<G4TwoVector> outEPPoints; // output face end piece points
1673
1674 G4double inXO = poleHalfWidth - lsl;
1675
1676 // create an ellipse with no angle, then shear it to match the angle
1677 G4int nSegments = std::ceil((G4double)nSegmentsPerCircle / 4.0);
1678 G4double increment = CLHEP::halfpi/nSegments;
1679 G4double epWidth = buildVertically ? coilHeight : coilWidth;
1680 for (G4double t = -CLHEP::pi; t <= -CLHEP::halfpi + 1e-9; t += increment)
1681 { // left side
1682 G4double x = -inXO + epWidth*std::cos(t);
1683 G4double y = epWidth*std::sin(t);
1684 inEPPoints.emplace_back(x,y);
1685 }
1686
1687 for (G4double t = -CLHEP::halfpi; t <= 0 + 1e-9; t += increment)
1688 { // right side
1689 G4double x = inXO + epWidth*std::cos(t);
1690 G4double y = epWidth*std::sin(t);
1691 inEPPoints.emplace_back(x,y);
1692 }
1693
1694 // shear it
1695 // (x') (1 0) (x)
1696 // (y') = (tan(phi) 1) (y)
1697 // -> x' = x; y' = tan(phi)*x + y
1698 // copy the points, flip in y and shear for output angle
1699 for (const auto& point : inEPPoints) // copying this time
1700 {
1701 G4double outy = -1*(point.x()*std::tan(-angleOut) + point.y());
1702 outEPPoints.emplace_back(point.x(),outy);
1703 }
1704 for (auto& point : inEPPoints) // modify in place for shearing original points
1705 {point.setY(point.x()*std::tan(-angleIn) + point.y());}
1706
1707 G4TwoVector zOffsets(0,0); // the transverse offset of each plane from 0,0
1708 G4double zScale = 1; // the scale at each end of the points = 1
1709
1710 // these are projected by coilHeight in z as they'll be rotated later
1711 G4double epHeight = buildVertically ? coilWidth : coilHeight; // full length as used elsewhere
1712 G4VSolid* endPieceSolidIn = new G4ExtrudedSolid(name + "_end_coil_in_solid", // name
1713 inEPPoints, // transverse 2d coordinates
1714 0.5 * epHeight - lsl, // z half length
1715 zOffsets, zScale,
1716 zOffsets, zScale);
1717
1718 // these are projected by coilHeight in z as they'll be rotated later
1719 G4VSolid* endPieceSolidOut = new G4ExtrudedSolid(name + "_end_coil_out_solid", // name
1720 outEPPoints, // transverse 2d coordinates
1721 0.5 * epHeight - lsl, // z half length
1722 zOffsets, zScale,
1723 zOffsets, zScale);
1724
1725 // end piece container solids - simple extruded solid intersectd with cut tubs for
1726 // angled faces build about magnet zero so we get the coordinates right for general placement
1727 std::vector<G4TwoVector> contEPPoints;
1728 const G4double connector = 1*CLHEP::mm;
1729 G4double xmax = poleHalfWidth + epWidth + lsl;
1730 G4double ymax = poleHalfGap + epHeight + cDY + lsl;
1731 G4double yInn = poleHalfGap + cDY - lsl;
1732 contEPPoints.emplace_back(xmax + connector, ymax);
1733 contEPPoints.emplace_back(-xmax, ymax);
1734 contEPPoints.emplace_back(-xmax, yInn);
1735 contEPPoints.emplace_back( xmax, yInn);
1736 contEPPoints.emplace_back( xmax, -yInn);
1737 contEPPoints.emplace_back(-xmax, -yInn);
1738 contEPPoints.emplace_back(-xmax, -ymax);
1739 contEPPoints.emplace_back(xmax + connector, -ymax);
1740
1741 G4double containerIntersectionRadius = std::hypot(ymax, xmax);
1742
1743 if (buildVertically)
1744 {
1745 for (auto& point : contEPPoints)
1746 {point = BDS::Rotate(point, CLHEP::halfpi);}
1747 }
1748
1749 // calculate length for each end piece container volume - could be different due to different angles
1750 G4double ePInLength = epWidth + lengthSafetyLarge; // adjustable for intersection
1751 G4double ePInLengthZ = ePInLength; // copy that will be final length of object
1752 if (BDS::IsFinite(angleIn))
1753 {
1754 G4double dzIn = std::tan(std::abs(angleIn)) * horizontalWidth;
1755 ePInLength = std::max(2*ePInLength, 2*dzIn); // 2x as long for intersection
1756 }
1757
1758 BDSExtent ePExtOuter = BDSExtent(-xmax, xmax + connector,
1759 -ymax, ymax,
1760 -ePInLengthZ*0.5, ePInLengthZ*0.5);
1761 BDSExtent ePExtInner = BDSExtent(-xmax, xmax,
1762 -yInn, yInn,
1763 -ePInLengthZ*0.5, ePInLengthZ*0.5);
1764
1765 G4VSolid* ePContSolidIn = new G4ExtrudedSolid(name + "_end_coil_in_solid", // name
1766 contEPPoints, // transverse 2d coordinates
1767 0.5*ePInLength + lsl, // z half length
1768 zOffsets, zScale,
1769 zOffsets, zScale);
1770
1771 G4double ePOutLength = epWidth + lengthSafetyLarge; // adjustable for intersection
1772 G4double ePOutLengthZ = ePOutLength; // copy that will be final length of object
1773 if (BDS::IsFinite(angleOut))
1774 {
1775 G4double dzOut = std::tan(std::abs(angleOut)) * horizontalWidth;
1776 ePOutLength = std::max(2*ePOutLength, 2*dzOut); // 2x as long for intersection
1777 }
1778
1779 G4VSolid* ePContSolidOut = new G4ExtrudedSolid(name + "_end_coil_out_solid", // name
1780 contEPPoints, // transverse 2d coordinates
1781 ePOutLength*0.5, // z half length
1782 zOffsets, zScale,
1783 zOffsets, zScale);
1784
1785 // If there is a finite input face angle, the extruded solid above will be longer and
1786 // we now make a cut tubs for the angled faces to intersect it with.
1787 if (BDS::IsFinite(angleIn))
1788 {
1789
1790 G4ThreeVector inputfaceReversed = inputFaceNormal * -1;
1791 G4VSolid* ePContSolidInAng = new G4CutTubs(name + "_angled_face_solid", // name
1792 0, // inner radius
1793 containerIntersectionRadius, // outer radius
1794 ePInLengthZ*0.5, // z half length
1795 0, // start angle
1796 CLHEP::twopi, // sweep angle
1797 inputFaceNormal, // input face normal
1798 inputfaceReversed); // output face normal
1799
1800 // potential memory leak here with overwriting
1801 ePContSolidIn = new G4IntersectionSolid(name + "_end_coil_cont_solid", // name
1802 ePContSolidIn,
1803 ePContSolidInAng);
1804 }
1805
1806 if (BDS::IsFinite(angleOut))
1807 {
1808
1809 G4ThreeVector outputfaceReversed = outputFaceNormal * -1;
1810 G4VSolid* ePContSolidOutAng = new G4CutTubs(name + "_angled_face_solid", // name
1811 0, // inner radius
1812 containerIntersectionRadius, // outer radius
1813 ePOutLengthZ*0.5, // z half length
1814 0, // start angle
1815 CLHEP::twopi, // sweep angle
1816 outputfaceReversed, // input face normal
1817 outputFaceNormal); // output face normal
1818
1819 // potential memory leak here with overwriting
1820 ePContSolidOut = new G4IntersectionSolid(name + "_end_coil_cont_solid", // name
1821 ePContSolidOut,
1822 ePContSolidOutAng);
1823 }
1824
1825 // end piece logical volumes
1826 // coil end piece lv
1827 G4Material* copper = BDSMaterials::Instance()->GetMaterial("copper");
1828 G4Material* worldMaterial = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->WorldMaterial());
1829
1830 G4LogicalVolume* ePInLV = new G4LogicalVolume(endPieceSolidIn, // solid
1831 copper, // material
1832 name + "_end_coil_in_lv"); // name
1833
1834 G4LogicalVolume* ePContInLV = new G4LogicalVolume(ePContSolidIn, // solid
1835 worldMaterial, // material
1836 name + "_end_cont_in_lv"); // name
1837
1838 G4LogicalVolume* ePOutLV = new G4LogicalVolume(endPieceSolidOut, // solid
1839 copper, // material
1840 name + "_end_coil_out_lv"); // name
1841
1842 G4LogicalVolume* ePContOutLV = new G4LogicalVolume(ePContSolidOut, // solid
1843 worldMaterial, // material
1844 name + "_end_cont_out_lv"); // name
1845
1846 G4Colour* coilColour = BDSColours::Instance()->GetColour("coil");
1847 G4VisAttributes* coilVisIn = new G4VisAttributes(*coilColour);
1848 coilVisIn->SetVisibility(true);
1849 G4VisAttributes* coilVisOut = new G4VisAttributes(*coilVisIn);
1850
1851 ePInLV->SetVisAttributes(coilVisIn);
1852 ePOutLV->SetVisAttributes(coilVisOut);
1853 ePContInLV->SetVisAttributes(containerVisAttr);
1854 ePContOutLV->SetVisAttributes(containerVisAttr);
1855
1856 ePInLV->SetUserLimits(defaultUserLimits);
1857 ePOutLV->SetUserLimits(defaultUserLimits);
1858 ePContInLV->SetUserLimits(defaultUserLimits);
1859 ePContOutLV->SetUserLimits(defaultUserLimits);
1860
1861 // placements
1862 G4RotationMatrix* endCoilInRM = new G4RotationMatrix();
1863 endCoilInRM->rotateX(-CLHEP::halfpi);
1864 if (buildVertically)
1865 {endCoilInRM->rotate(CLHEP::halfpi, G4ThreeVector(0,1,0));}
1866 G4ThreeVector endCoilTranslationInTop(0, coilDY-lsl, 0.5*epWidth);
1867 G4ThreeVector endCoilTranslationInLow(0,-coilDY+lsl, 0.5*epWidth);
1868 G4RotationMatrix* vertRot = new G4RotationMatrix();
1869 vertRot->rotateZ(CLHEP::halfpi);
1870 if (buildVertically)
1871 {
1872 endCoilTranslationInTop.transform(*vertRot);
1873 endCoilTranslationInLow.transform(*vertRot);
1874 }
1875
1876
1877 G4PVPlacement* ePInTopPv = new G4PVPlacement(endCoilInRM, // rotation
1878 endCoilTranslationInTop, // position
1879 ePInLV, // logical volume
1880 name + "_end_piece_in_top_pv", // name
1881 ePContInLV, // mother lv to be placed in
1882 false, // no boolean operation
1883 0, // copy number
1884 checkOverlaps); // check overlaps
1885 G4PVPlacement* ePInLowPv = new G4PVPlacement(endCoilInRM, // rotation
1886 endCoilTranslationInLow, // position
1887 ePInLV, // logical volume
1888 name + "_end_piece_in_low_pv", // name
1889 ePContInLV, // mother lv to be placed in
1890 false, // no boolean operation
1891 0, // copy number
1892 checkOverlaps); // check overlaps
1893
1894 G4RotationMatrix* endCoilOutRM = new G4RotationMatrix(*endCoilInRM); // copy as independent objects
1895
1896 G4ThreeVector endCoilTranslationOutTop(0, coilDY, -0.5*epWidth);
1897 G4ThreeVector endCoilTranslationOutLow(0,-coilDY, -0.5*epWidth);
1898 if (buildVertically)
1899 {
1900 endCoilTranslationOutTop.transform(*vertRot);
1901 endCoilTranslationOutLow.transform(*vertRot);
1902 }
1903 delete vertRot;
1904
1905 G4PVPlacement* ePOutTopPv = new G4PVPlacement(endCoilOutRM, // rotation
1906 endCoilTranslationOutTop, // position
1907 ePOutLV, // logical volume
1908 name + "_end_piece_out_top_pv", // name
1909 ePContOutLV, // mother lv to be placed in
1910 false, // no boolean operation
1911 0, // copy number
1912 checkOverlaps); // check overlaps
1913 G4PVPlacement* ePOutLowPv = new G4PVPlacement(endCoilOutRM, // rotation
1914 endCoilTranslationOutLow, // position
1915 ePOutLV, // logical volume
1916 name + "_end_piece_out_low_pv", // name
1917 ePContOutLV, // mother lv to be placed in
1918 false, // no boolean operation
1919 0, // copy number
1920 checkOverlaps); // check overlaps
1921
1922 // packaging - geometry component
1923 G4ThreeVector inputFaceNormalR = inputFaceNormal * -1; // just -1 as parallel but opposite
1924 BDSSimpleComponent* endPieceInSC = new BDSSimpleComponent(name + "_end_piece_in",
1925 ePInLengthZ,
1926 0/*angle*/,
1927 ePContSolidIn,
1928 ePContInLV,
1929 ePExtOuter,
1930 inputFaceNormal,
1931 inputFaceNormalR);
1932
1933 endPieceInSC->RegisterPhysicalVolume(ePInTopPv);
1934 endPieceInSC->RegisterPhysicalVolume(ePInLowPv);
1935 endPieceInSC->RegisterRotationMatrix(endCoilInRM);
1936 endPieceInSC->RegisterVisAttributes(coilVisIn);
1937 endPieceInSC->RegisterLogicalVolume(ePInLV);
1938 endPieceInSC->RegisterSolid(endPieceSolidIn);
1939 if (sensitiveOuter)
1940 {endPieceInSC->RegisterSensitiveVolume(ePInLV, BDSSDType::energydep);}
1941 endPieceInSC->SetInnerExtent(ePExtInner);
1942
1943 G4ThreeVector outputFaceNormalR = outputFaceNormal * -1; // just -1 as parallel but opposite
1944 BDSSimpleComponent* endPieceOutSC = new BDSSimpleComponent(name + "_end_piece_out",
1945 ePOutLengthZ,
1946 0/*angle*/,
1947 ePContSolidOut,
1948 ePContOutLV,
1949 ePExtInner,
1950 outputFaceNormalR,
1951 outputFaceNormal);
1952
1953 endPieceOutSC->RegisterPhysicalVolume(ePOutTopPv);
1954 endPieceOutSC->RegisterPhysicalVolume(ePOutLowPv);
1955 endPieceOutSC->RegisterRotationMatrix(endCoilOutRM);
1956 endPieceOutSC->RegisterVisAttributes(coilVisOut);
1957 endPieceOutSC->RegisterLogicalVolume(ePOutLV);
1958 endPieceOutSC->RegisterSolid(endPieceSolidOut);
1959 if (sensitiveOuter)
1960 {endPieceOutSC->RegisterSensitiveVolume(ePOutLV, BDSSDType::energydep);}
1961 endPieceOutSC->SetExtent(ePExtOuter);
1962 endPieceOutSC->SetInnerExtent(ePExtInner);
1963
1964 // attach to the magnet outer
1965 outer->SetEndPieceBefore(endPieceInSC);
1966 outer->SetEndPieceAfter(endPieceOutSC);
1967
1968 // update normals of outer and end pieces
1969 SetFaceNormals(outer);
1970
1971 return outer;
1972}
A holder class for a piece of beam pipe geometry.
G4double GetContainerRadius() const
If it is circular, we need the radius.
G4Colour * GetColour(G4double red, G4double green, G4double blue, G4double alpha=1)
Get a cached anonymous colour by values.
static BDSColours * Instance()
singleton pattern
Definition BDSColours.cc:32
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
G4bool checkOverlaps
Cache of global constants variable.
G4double lengthSafety
Cache of global constants variable.
G4VisAttributes * containerVisAttr
Cache of global constants variable.
G4double lengthSafetyLarge
Cache of global constants variable.
G4int nSegmentsPerCircle
Cache of global constants variable.
G4UserLimits * defaultUserLimits
Cache of global constants variable.
void SetInnerExtent(const BDSExtent &extIn)
Set extent.
BDSExtent GetExtent() const
Accessor - see member for more info.
void RegisterRotationMatrix(G4RotationMatrix *rotationMatrix)
void RegisterLogicalVolume(G4LogicalVolume *logicalVolume)
void RegisterPhysicalVolume(G4VPhysicalVolume *physicalVolume)
void SetExtent(const BDSExtent &extIn)
Set extent.
void RegisterVisAttributes(G4VisAttributes *visAttribute)
void RegisterSolid(G4VSolid *solid)
void RegisterSensitiveVolume(G4LogicalVolume *sensitiveVolume, BDSSDType sensitivityType)
static BDSGlobalConstants * Instance()
Access method.
virtual BDSMagnetOuter * CreateSolenoid(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)=0
solenoid outer volume
virtual void CleanUp()
Empty containers for next use - factories are never deleted so can't rely on scope.
virtual void CreateLogicalVolumes(const G4String &name, G4Colour *colour, G4Material *outerMaterial)
void SetFaceNormals(BDSMagnetOuter *outer)
Copy face normals from members to an instance of outer.
G4VSolid * poleSolid
Solid for an individual pole that will be placed multiple times.
virtual BDSMagnetOuter * CreateMuonSpoiler(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)=0
muon spoiler outer volume
virtual void SetUserLimits()
Attach default user limits to all logical volumes.
G4bool sensitiveOuter
Cache of global constants variable.
virtual BDSMagnetOuter * CreateMultipole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)=0
general multipole outer volume - could be any 2N order multipole
G4VSolid * yokeSolid
Solid for outer part that connects all poles.
virtual BDSMagnetOuter * CreateRfCavity(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)=0
RF cavity outer volume.
void BuildMagnetContainerSolidStraight(const G4String &name, G4double magnetContainerLength, G4double magnetContainerRadius)
Factory that produces cylindrical magnet geometry.
Factory class for outer volume of magnets. Produces magnets with 2N-poles around the beampipe with a ...
void DipoleCommonPreConstruction(const G4String &name, G4double angleIn, G4double angleOut, G4double length, G4double &horizontalWidth, G4Material *&material, G4double &vhRatio)
virtual void CalculatePoleAndYoke(G4double horizontalWidth, BDSBeamPipe *beamPipe, G4int order)
BDSMagnetOuter * CreateDipoleH(const G4String &name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe, G4bool buildVertically)
Routine to construct an H shaped dipole magnet and can optionally be built vertically.
void CleanUpPolesBase()
Non-virtual clean up to be used in constructor.
G4double yokeFinishRadius
Finish radius of yoke geometry from magnet centre - less than horizontalWidth.
G4double segmentAngle
2PI / # of poles - angle per segment allocated for each pole.
virtual void CreateEndPiece(const G4String &name)
virtual void IntersectPoleWithYoke(const G4String &name, G4double length, G4int order)
Chop off the top of the pole to match the appropriate yoke geometry.
G4double endPieceOuterR
Outer radius for end piece container.
std::vector< G4ThreeVector > CalculateCoilDisplacements(G4double poleHalfWidthIn, G4double poleHalfGapIn, G4double coilWidthIn, G4double coilHeightIn, G4double cDY, G4double &coilDY)
virtual BDSMagnetOuter * CreateRectangularBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
rectangular bend outer volume
BDSMagnetOuter * CreateDipoleC(const G4String &name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe, G4bool buildVertically)
void DipoleCalculations(G4bool hStyle, G4bool buildVertically, const BDSBeamPipe *beamPipe, G4double length, G4double horizontalWidth, G4double angleIn, G4double angleOut, G4double yokeThicknessFraction, G4double vhRatio, G4double coilWidthFraction, G4double coilHeightFraction, G4double &cShapeOuterEdge, G4double &poleHalfGap, G4double &poleWidth, G4double &poleHeight, G4double &yokeWidth, G4double &yokeHalfHeight, G4double &yokeThickness, G4double &yokeOverHang, G4double &coilWidth, G4double &coilHeightIn, G4double &coilToYokeGap, G4double &coilToPoleGap, G4double &sLength, G4double &containerSLength, G4double &intersectionRadius)
virtual BDSMagnetOuter * CreateMuonSpoiler(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
muon spoiler outer volume
G4VSolid * coilLeftSolid
Left coil solid for one pole built upright along y axis.
virtual void CreateLogicalVolumes(const G4String &name, G4Colour *colour, G4Material *outerMaterial)
virtual BDSMagnetOuter * CreateDecapole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
decapole outer volume
BDSSimpleComponent * endPiece
Fully constructed end piece.
virtual void CreateCoilSolids(const G4String &name, G4double length)
Create the coil solids corresponding to the pole solid.
const G4double poleAngularFraction
Fraction of 2pi/Npoles that the pole will occupy - always < 1.
G4VSolid * poleIntersectionSolid
Solid used to chop off pole.
G4double poleSquareStartRadius
Radius from magnet centre that constant width section starts.
G4LogicalVolume * endPieceCoilLV
Logical volume for end piece single coil piece.
virtual BDSMagnetOuter * CreateSextupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sextupole outer volume
virtual BDSMagnetOuter * CreateSectorBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sector bend outer volume
G4double magnetContainerRadius
Radius of the container solid for the outer geometry.
G4double poleAngle
The angle allowed for the pole to occupy - less than segmentAngle.
virtual void CreatePoleSolid(const G4String &name, G4double length, G4int order)
virtual void PlaceComponents(const G4String &name, G4int order)
Place the poles and yoke in the container volume.
G4LogicalVolume * endPieceContainerLV
Logical volume for end piece container.
G4double coilCentreRadius
Radius from magnet centre that the centre of the coils exist at.
G4VSolid * endPieceContainerSolid
End piece container solid.
virtual void PlaceComponentsCoils(const G4String &name, G4int order)
If we're building coils, place two coils for each pole.
virtual void CleanUp()
Empty containers for next use - this class is never deleted so can't rely on scope.
std::vector< G4TwoVector > rightPoints
Vector of 2D points for right coil.
std::vector< G4TwoVector > endPiecePoints
Vector of 2D points for end piece looking from above down z.
void TestCoilFractions(G4double &coilWidthFraction, G4double &coilHeightFraction)
G4double poleFinishRadius
Finish radius of the pole from magnet centre.
BDSMagnetOuterFactoryBase * cylindrical
Default factory to fall back to.
virtual void CreateLogicalVolumesCoil(const G4String &name)
G4double endPieceInnerR
Inner radius for end piece container.
std::vector< G4TwoVector > leftPoints
Vector of 2D points for left coil.
G4double endPieceLength
Length of the coil end piece along what will be curvilinear S.
G4double poleStartRadius
Start radius of the pole from magnet centre.
virtual void CreateCoilPoints()
Create all the points that make up the extruded solid of the pole.
virtual BDSMagnetOuter * CreateSolenoid(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
solenoid outer volume
virtual void CreateYokeAndContainerSolid(const G4String &name, G4double length, G4int order, G4double magnetContainerLength, G4double magnetContainerRadiusIn)
G4LogicalVolume * coilRightLV
Logical volume for right coil.
virtual BDSMagnetOuter * CreateRfCavity(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
RF cavity outer volume.
G4bool buildPole
Whether or not to build poles (and therefore coils).
G4double yokeStartRadius
Start radius of yoke geometry from magnet cetnre.
G4double coilHeight
Height along y for coil for coil beside 1 upgright pole aligned with y axis.
virtual BDSMagnetOuter * CreateQuadrupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
quadrupole outer volume
virtual BDSMagnetOuter * CreateMultipole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
general multipole outer volume - could be any 2N order multipole
G4ThreeVector poleTranslation
Offste of pole for placement from magnet centre.
virtual BDSMagnetOuter * CreateOctupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
octupole outer volume
virtual BDSMagnetOuter * CommonConstructor(const G4String &name, G4double length, BDSBeamPipe *beamPipe, G4int order, G4double magnetContainerLength, const BDSMagnetOuterInfo *recipe)
Common construction tasks to all methods - assemble yoke and poles in container.
G4double poleSquareWidth
Full width of pole in constant width section.
G4LogicalVolume * coilLeftLV
Logical volume for left coil.
virtual BDSMagnetOuter * CreateKicker(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe, G4bool vertical)
horizontal and vertical kicker outer volume
Holder struct of all information required to create the outer geometry of a magnet.
G4bool hStyle
H Style for dipoles. If not, it's assumed C style.
An object for both the returned magnet outer body but also a tight fitting container for the whole ma...
static BDSMaterials * Instance()
Singleton pattern access.
G4Material * GetMaterial(G4String material) const
Get material by name.
A BDSAcceleratorComponent wrapper for BDSGeometryComponent.
G4TwoVector Rotate(const G4TwoVector &vec, const G4double &angle)
Rotate a two vector in polar coordinates by an angle.
G4bool WillIntersect(const G4ThreeVector &incomingNormal, const G4ThreeVector &outgoingNormal, const G4double &zSeparation, const BDSExtent &incomingExtent, const BDSExtent &outgoingExtent)
G4double CalculateSafeAngledVolumeLength(G4double angleIn, G4double angleOut, G4double length, G4double containerWidth, G4double containerHeight=0)
Calculate safe length of an angled volume so it fills the length of its container.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
std::pair< G4ThreeVector, G4ThreeVector > CalculateFaces(G4double angleInIn, G4double angleOutIn)
Calculate input and output normal vector.