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