BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSMagnetOuterFactoryCylindrical.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 "BDSMagnetOuterFactoryCylindrical.hh"
19
20#include "BDSBeamPipe.hh"
21#include "BDSColours.hh"
22#include "BDSDebug.hh"
23#include "BDSExtent.hh"
24#include "BDSGeometryComponent.hh"
25#include "BDSGlobalConstants.hh"
26#include "BDSMagnetOuter.hh"
27#include "BDSMagnetOuterInfo.hh"
28#include "BDSMaterials.hh"
29#include "BDSSDType.hh"
30#include "BDSUtilities.hh" // for calculateorientation
31
32#include "globals.hh" // geant4 globals / types
33#include "G4CutTubs.hh"
34#include "G4LogicalVolume.hh"
35#include "G4Material.hh"
36#include "G4PVPlacement.hh"
37#include "G4SubtractionSolid.hh"
38#include "G4ThreeVector.hh"
39#include "G4Tubs.hh"
40#include "G4VisAttributes.hh"
41#include "G4VSolid.hh"
42
43#include <algorithm> // for std::max
44#include <cmath>
45#include <set>
46#include <utility> // for std::pair
47
48BDSMagnetOuterFactoryCylindrical::BDSMagnetOuterFactoryCylindrical():
49 magnetContainerRadius(0)
50{;}
51
53{
55 magnetContainerRadius = 0;
56}
57
59 G4double length,
60 const BDSBeamPipe* beamPipe,
61 G4double containerLength,
62 const BDSMagnetOuterInfo* recipe)
63{
64 G4double horizontalWidth = recipe->horizontalWidth;
65 G4double angleIn = recipe->angleIn;
66 G4double angleOut = recipe->angleOut;
67
68 // clear up variables
69 CleanUp();
70
71 // test input parameters - set global options as default if not specified
72 TestInputParameters(beamPipe,horizontalWidth);
73
74 // Simple cylinder if no poleface rotation, otherwise angled.
75 if (!BDS::IsFinite(angleIn) && !BDS::IsFinite(angleOut))
76 {
77 CreateCylindricalSolids(name,length, beamPipe, containerLength, horizontalWidth);
78 BuildMagnetContainerSolidStraight(name, containerLength, magnetContainerRadius);
79 }
80 else
81 {
82 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn,angleOut);
83 inputFaceNormal = faces.first;
84 outputFaceNormal = faces.second;
85
86 CreateCylindricalSolidsAngled(name, length, beamPipe, containerLength, horizontalWidth);
87 BuildMagnetContainerSolidAngled(name, containerLength, magnetContainerRadius);
88 }
89 return CommonFinalConstructor(name, length, recipe);
90}
91
93 G4double length,
94 const BDSBeamPipe* beamPipe,
95 G4double containerLength,
96 const BDSMagnetOuterInfo* recipe)
97{
98 G4double horizontalWidth = recipe->horizontalWidth;
99 G4double angleIn = recipe->angleIn;
100 G4double angleOut = recipe->angleOut;
101
102 //rectangular bends currently just make a shorter straight volume, so ignore angle for now
103 // clear up variables
104 CleanUp();
105
106 // test input parameters - set global options as default if not specified
107 TestInputParameters(beamPipe, horizontalWidth);
108
109 // Simple cylinder if no poleface rotation, otherwise angled.
110 if ((!BDS::IsFinite(angleIn)) && !BDS::IsFinite(angleOut))
111 {
112 CreateCylindricalSolids(name,length, beamPipe, containerLength, horizontalWidth);
113 BuildMagnetContainerSolidStraight(name,containerLength,0.5*horizontalWidth);
114 }
115 else
116 {
117 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn,angleOut);
118 inputFaceNormal = faces.first;
119 outputFaceNormal = faces.second;
120
121 CreateCylindricalSolidsAngled(name, length, beamPipe, containerLength, horizontalWidth);
122 BuildMagnetContainerSolidAngled(name, containerLength, 0.5*horizontalWidth);
123 }
124
125 return CommonFinalConstructor(name, length, recipe);
126}
127
129 G4double length,
130 BDSBeamPipe* beamPipe,
131 G4double containerLength,
132 const BDSMagnetOuterInfo* recipe)
133{
134 G4double horizontalWidth = recipe->horizontalWidth;
135 CleanUp();
136 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
137 return CommonFinalConstructor(name, length, recipe);
138}
139
141 G4double length,
142 BDSBeamPipe* beamPipe,
143 G4double containerLength,
144 const BDSMagnetOuterInfo* recipe)
145{
146 G4double horizontalWidth = recipe->horizontalWidth;
147 CleanUp();
148 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
149 return CommonFinalConstructor(name, length, recipe);
150}
151
153 G4double length,
154 BDSBeamPipe* beamPipe,
155 G4double containerLength,
156 const BDSMagnetOuterInfo* recipe)
157{
158 G4double horizontalWidth = recipe->horizontalWidth;
159 CleanUp();
160 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
161 return CommonFinalConstructor(name, length, recipe);
162}
163
165 G4double length,
166 BDSBeamPipe* beamPipe,
167 G4double containerLength,
168 const BDSMagnetOuterInfo* recipe)
169{
170 G4double horizontalWidth = recipe->horizontalWidth;
171 CleanUp();
172 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
173 return CommonFinalConstructor(name, length, recipe);
174}
175
177 G4double length,
178 BDSBeamPipe* beamPipe,
179 G4double containerLength,
180 const BDSMagnetOuterInfo* recipe)
181{
182 G4double horizontalWidth = recipe->horizontalWidth;
183 CleanUp();
184 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
185 auto result = CommonFinalConstructor(name, length, recipe);
186
187 // NOTE this is a reproduction of some calculations in component factory for now
188 G4double innerRadius = beamPipe->GetContainerRadius() + lengthSafetyLarge;
189 G4double outerRadius = horizontalWidth * 0.5;
190
191 if ( ((outerRadius - innerRadius)/outerRadius) < 0.05 )
192 {return result;} // mostly beam pipe with thin yoke - don't build more geometry
193
194 // build a current cylinder sheet but of the same material - purely to indicate to the user
195 // the geometry used for the yoke field calculation
196 G4double coilRadius = innerRadius + 0.25*(outerRadius - innerRadius);
197 G4double coilThickness = 0.05*coilRadius; // arbitrary 5%
198 G4VSolid* sheetSolid = new G4Tubs(name + "_sheet_solid",
199 coilRadius-coilThickness,
200 coilRadius+coilThickness,
201 length*0.5*0.8,
202 0, CLHEP::twopi);
203 G4LogicalVolume* sheetLV = new G4LogicalVolume(sheetSolid,
204 recipe->outerMaterial,
205 name + "_sheet_lv");
206 yokeLV = result->GetContainerLogicalVolume()->GetDaughter(0)->GetLogicalVolume();
207 auto sheetColour = BDSColours::Instance()->GetColour("copper");
208 auto sheetVis = new G4VisAttributes(*sheetColour);
209 sheetLV->SetVisAttributes(sheetVis);
210 auto pv = new G4PVPlacement(nullptr, // no rotation
211 G4ThreeVector(), // position
212 sheetLV, // lv to be placed
213 name + "_sheet_pv", // name
214 yokeLV, // mother lv to be placed in
215 false, // no boolean operation
216 0, // copy number
218 result->RegisterSolid(sheetSolid);
219 result->RegisterLogicalVolume(sheetLV);
220 result->RegisterPhysicalVolume(pv);
221 result->RegisterVisAttributes(sheetVis);
222 return result;
223}
224
226 G4double length,
227 BDSBeamPipe* beamPipe,
228 G4double containerLength,
229 const BDSMagnetOuterInfo* recipe)
230{
231 G4double horizontalWidth = recipe->horizontalWidth;
232 CleanUp();
233 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
234 return CommonFinalConstructor(name, length, recipe);
235}
236
238 G4double length,
239 BDSBeamPipe* beamPipe,
240 G4double containerLength,
241 const BDSMagnetOuterInfo* recipe)
242{
243 G4double horizontalWidth = recipe->horizontalWidth;
244 CleanUp();
245 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
246 return CommonFinalConstructor(name, length, recipe);
247}
248
250 G4double length,
251 BDSBeamPipe* beamPipe,
252 G4double containerLength,
253 const BDSMagnetOuterInfo* recipe)
254{
255 G4double horizontalWidth = recipe->horizontalWidth;
256 CleanUp();
257 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
258 return CommonFinalConstructor(name, length, recipe);
259}
260
262 G4double length,
263 const BDSBeamPipe* beamPipe,
264 G4double containerLength,
265 const BDSMagnetOuterInfo* recipe,
266 G4bool /*vertical*/)
267{
268 G4double horizontalWidth = recipe->horizontalWidth;
269 CleanUp();
270 // in this factory, h and v kickers will look the same so ignore bool vertical
271 // have to retain it though for virtual base class compatibility
272 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
273 return CommonFinalConstructor(name, length, recipe);}
274
276
278 G4double length,
279 const BDSBeamPipe* beamPipe,
280 G4double magnetContainerLength,
281 G4double horizontalWidth)
282{
283 // build the container for the whole magnet object - this horizontal width should be
284 // larger than the magnet outer piece width which is just 'horizontalWidth' wide.
285 magnetContainerRadius = (0.5 * horizontalWidth) + lengthSafetyLarge;
286 BuildMagnetContainerSolidStraight(name, magnetContainerLength, magnetContainerRadius);
287
288 if (beamPipe->ContainerIsCircular())
289 {
290 //circular beampipe so we can simply use its radius
291 yokeSolid = new G4Tubs(name + "_yoke_solid", // name
292 beamPipe->GetContainerRadius() + 2*lengthSafetyLarge, // inner radius
293 horizontalWidth*0.5, // outer radius
294 length*0.5-lengthSafety, // half length
295 0, // rotation start angle
296 CLHEP::twopi); // rotation finish angle
297
298 //container is similar but slightly wider and hollow (to allow placement of beampipe)
299 // inner radius of container is less than inner radius of yoke to contain it but still
300 // bigger than the outer radius of the beam pipe
301 containerSolid = new G4Tubs(name + "_container_solid", // name
302 beamPipe->GetContainerRadius() + lengthSafetyLarge, // inner radius
303 horizontalWidth*0.5 + lengthSafetyLarge,// outer radius
304 length*0.5, // half length
305 0, // rotation start angle
306 CLHEP::twopi); // rotation finish angle
307 }
308 else
309 {
310 G4VSolid* yokeSolidCylinder = new G4Tubs(name + "_yoke_solid_cylinder",// name
311 0, // inner radius - for unambiguous subtraction
312 horizontalWidth*0.5, // outer radius
313 length*0.5-lengthSafety, // half length
314 0, // rotation start angle
315 CLHEP::twopi); // rotation finish angle
316 allSolids.insert(yokeSolidCylinder);
317 yokeSolid = new G4SubtractionSolid(name + "_yoke_solid",
318 yokeSolidCylinder,
319 beamPipe->GetContainerSubtractionSolid());
320
321 //container is similar but slightly wider
322 G4VSolid* containerSolidCylinder = new G4Tubs(name + "_container_solid_cylinder",// name
323 0, // inner radius - for unambiguous subtraction
324 horizontalWidth*0.5 + lengthSafetyLarge, // outer radius
325 length*0.5, // half length
326 0, // rotation start angle
327 CLHEP::twopi); // rotation finish angle
328 allSolids.insert(containerSolidCylinder);
329 containerSolid = new G4SubtractionSolid(name + "_container_solid",
330 containerSolidCylinder,
331 beamPipe->GetContainerSubtractionSolid());
332 }
333 allSolids.insert(yokeSolid);
334}
335
336// Function for cylinder with angled faces - for pole face rotation in dipoles.
337void BDSMagnetOuterFactoryCylindrical::CreateCylindricalSolidsAngled(G4String name,
338 G4double length,
339 const BDSBeamPipe* beamPipe,
340 G4double magnetContainerLength,
341 G4double horizontalWidth)
342{
343 // build the container for the whole magnet object - this horizontal width should be
344 // larger than the magnet outer piece width which is just 'horizontalWidth' wide.
345 magnetContainerRadius = (0.5 * horizontalWidth) + lengthSafety;
346 BuildMagnetContainerSolidStraight(name, magnetContainerLength, magnetContainerRadius);
347
348 if (beamPipe->ContainerIsCircular())
349 {
350 //circular beampipe so we can simply use its radius
351 yokeSolid = new G4CutTubs(name + "_yoke_solid", // name
352 beamPipe->GetContainerRadius() + 2*lengthSafety, // inner radius
353 horizontalWidth*0.5, // outer radius
354 length*0.5-lengthSafety, // half length
355 0, // rotation start angle
356 CLHEP::twopi, // rotation finish angle
357 inputFaceNormal, // input face normal
358 outputFaceNormal); // output face normal);
359
360 //container is similar but slightly wider and hollow (to allow placement of beampipe)
361 containerSolid = new G4CutTubs(name + "_container_solid", // name
362 beamPipe->GetContainerRadius() + lengthSafety, // inner radius
363 horizontalWidth*0.5 + lengthSafety,// outer radius
364 length*0.5, // half length
365 0, // rotation start angle
366 CLHEP::twopi, // rotation finish angle
367 inputFaceNormal, // input face normal
368 outputFaceNormal); // output face normal);
369 }
370 else
371 {
372 G4VSolid* yokeSolidCylinder = new G4CutTubs(name + "_yoke_solid_cylinder",// name
373 0, // inner radius - for unambiguous subtraction
374 horizontalWidth*0.5, // outer radius
375 length*0.5-lengthSafety,// half length
376 0, // rotation start angle
377 CLHEP::twopi, // rotation finish angle
378 inputFaceNormal, // input face normal
379 outputFaceNormal); // output face normal);
380 allSolids.insert(yokeSolidCylinder);
381 yokeSolid = new G4SubtractionSolid(name + "_yoke_solid",
382 yokeSolidCylinder,
383 beamPipe->GetContainerSubtractionSolid());
384
385 //container is similar but slightly wider
386 G4VSolid* containerSolidCylinder = new G4CutTubs(name + "_container_solid_cylinder",// name
387 0, // inner radius - for unambiguous subtraction
388 horizontalWidth*0.5 + lengthSafety,// outer radius
389 length*0.5, // half length
390 0, // rotation start angle
391 CLHEP::twopi, // rotation finish angle
392 inputFaceNormal, // input face normal
393 outputFaceNormal);// output face normal);
394 allSolids.insert(containerSolidCylinder);
395 containerSolid = new G4SubtractionSolid(name + "_container_solid",
396 containerSolidCylinder,
397 beamPipe->GetContainerSubtractionSolid());
398 }
399 allSolids.insert(yokeSolid);
400}
401
403 G4double& horizontalWidth)
404{
405 // ensure box size is bigger than the beampipe
406 if (beamPipe->ContainerIsCircular())
407 {
408 // if it's circular, just check radius
409 if (horizontalWidth < 2*(beamPipe->GetContainerRadius()) )
410 {horizontalWidth = 2*(beamPipe->GetContainerRadius()) + 1*CLHEP::mm;}
411 }
412 else
413 {
414 // it's not circular - have a look at extents
415 // +ve - -ve
416 G4double extentX = beamPipe->GetExtent().DX();
417 G4double extentY = beamPipe->GetExtent().DY();
418 if ( (horizontalWidth < extentX) || (horizontalWidth < extentY) )
419 {
420 // horizontalWidth isn't sufficient for range in x or y
421 horizontalWidth = std::max(extentX,extentY) + 1*CLHEP::mm;
422 }
423 }
424}
425
427 G4double length,
428 const BDSMagnetOuterInfo* recipe)
429{
430 G4Material* outerMaterial = recipe->outerMaterial;
431 if (!outerMaterial)
432 {outerMaterial = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->EmptyMaterial());}
433 G4Colour* colour = recipe->colour;
434
435 CreateLogicalVolumes(name, colour, outerMaterial);
437 CreateMagnetContainerComponent();
438
439 // PLACEMENT
440 // place the components inside the container
441 // note we don't need the pointer for anything - it's registered upon construction with g4
442 yokePV = new G4PVPlacement(nullptr, // no rotation
443 G4ThreeVector(), // position
444 yokeLV, // lv to be placed
445 name + "_yoke_pv", // name
446 containerLV, // mother lv to be placed in
447 false, // no boolean operation
448 0, // copy number
449 checkOverlaps); // whether to check overlaps
450
451 // record extents
452 // container radius is the same for all methods as all cylindrical
453 BDSExtent ext = BDSExtent(magnetContainerRadius, magnetContainerRadius, length*0.5);
454
455 // build the BDSMagnetOuter instance and return it
456 BDSMagnetOuter* outer = new BDSMagnetOuter(containerSolid,
457 containerLV, ext,
458 magnetContainer);
459
460 SetFaceNormals(outer);
461
462 // register all objects that go with the final geometry component (from internal sets)
463 outer->RegisterSolid(allSolids);
464 outer->RegisterLogicalVolume(yokeLV);
465 if (sensitiveOuter)
466 {outer->RegisterSensitiveVolume(yokeLV, BDSSDType::energydep);}
467 outer->RegisterPhysicalVolume(yokePV);
468 outer->RegisterVisAttributes(outerVisAttributes);
469 // no rotation matrices used in this factory
470 return outer;
471}
472
473
A holder class for a piece of beam pipe geometry.
G4VSolid * GetContainerSubtractionSolid() const
default destructor sufficient as G4 manages solids and LVs
G4double GetContainerRadius() const
If it is circular, we need the radius.
G4bool ContainerIsCircular() const
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
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
G4double DX() const
The difference in a dimension.
Definition BDSExtent.hh:82
G4double DY() const
The difference in a dimension.
Definition BDSExtent.hh:83
G4bool checkOverlaps
Cache of global constants variable.
G4double lengthSafety
Cache of global constants variable.
G4double lengthSafetyLarge
Cache of global constants variable.
BDSExtent GetExtent() const
Accessor - see member for more info.
void RegisterLogicalVolume(G4LogicalVolume *logicalVolume)
void RegisterPhysicalVolume(G4VPhysicalVolume *physicalVolume)
void RegisterVisAttributes(G4VisAttributes *visAttribute)
void RegisterSolid(G4VSolid *solid)
void RegisterSensitiveVolume(G4LogicalVolume *sensitiveVolume, BDSSDType sensitivityType)
static BDSGlobalConstants * Instance()
Access method.
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.
virtual void SetUserLimits()
Attach default user limits to all logical volumes.
G4bool sensitiveOuter
Cache of global constants variable.
G4VSolid * yokeSolid
Solid for outer part that connects all poles.
void BuildMagnetContainerSolidStraight(const G4String &name, G4double magnetContainerLength, G4double magnetContainerRadius)
void BuildMagnetContainerSolidAngled(const G4String &name, G4double magnetContainerLength, G4double magnetContainerRadius, G4bool flatFaces=false)
virtual BDSMagnetOuter * CreateKicker(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe, G4bool vertical)
horizontal and vertical kicker outer volume
virtual BDSMagnetOuter * CreateMuonSpoiler(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
muon spoiler outer volume
virtual BDSMagnetOuter * CreateSextupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sextupole outer volume
void CreateCylindricalSolids(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double magnetContainerLength, G4double boxSize)
Only really one function needed for this factory private to this factory only.
virtual BDSMagnetOuter * CreateRectangularBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
rectangular bend 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
virtual BDSMagnetOuter * CreateSectorBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sector bend outer volume
virtual BDSMagnetOuter * CreateDecapole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
decapole outer volume
BDSMagnetOuter * CommonFinalConstructor(G4String name, G4double length, const BDSMagnetOuterInfo *recipe)
virtual BDSMagnetOuter * CreateSolenoid(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
solenoid outer volume
virtual BDSMagnetOuter * CreateQuadrupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
quadrupole outer volume
virtual BDSMagnetOuter * CreateOctupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
octupole outer volume
virtual BDSMagnetOuter * CreateRfCavity(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
RF cavity outer volume.
virtual void CleanUp()
Empty containers for next use - factories are never deleted so can't rely on scope.
void TestInputParameters(const BDSBeamPipe *beamPipe, G4double &horizontalWidthIn)
test inputs for no null pointers or overlapping volumes due to poorly defined sizes
Holder struct of all information required to create the outer geometry of a magnet.
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.
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.