BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSBendBuilder.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2024.
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 "globals.hh" // geant4 globals / types
20
21#include "BDSAcceleratorComponent.hh"
22#include "BDSBeamPipeInfo.hh"
23#include "BDSBendBuilder.hh"
24#include "BDSComponentFactory.hh"
25#include "BDSDebug.hh"
26#include "BDSException.hh"
27#include "BDSFieldType.hh"
28#include "BDSGlobalConstants.hh"
29#include "BDSIntegratorSet.hh"
30#include "BDSIntegratorType.hh"
31#include "BDSIntegratorSetType.hh"
32#include "BDSMagnetStrength.hh"
33#include "BDSLine.hh"
34#include "BDSMagnet.hh"
35#include "BDSMagnetOuterInfo.hh"
36#include "BDSModulatorInfo.hh"
37#include "BDSUtilities.hh"
38
39#include "parser/element.h"
40#include "parser/elementtype.h"
41
42#include "G4Transform3D.hh"
43
44using namespace GMAD;
45
47{
48 G4bool finiteAngle = BDS::IsFinite((*st)["angle"]);
49 G4bool finiteField = BDS::IsFinite((*st)["field"]);
50 return !(finiteAngle || finiteField);
51}
52
53BDSAcceleratorComponent* BDS::BuildSBendLine(const G4String& elementName,
54 const Element* element,
56 G4double brho,
57 const BDSIntegratorSet* integratorSet,
58 G4double incomingFaceAngle,
59 G4double outgoingFaceAngle,
60 G4bool buildFringeFields,
61 const GMAD::Element* prevElement,
62 const GMAD::Element* nextElement,
63 BDSModulatorInfo* fieldModulator)
64{
65 const G4String baseName = elementName;
66 const G4double thinElementArcLength = BDSGlobalConstants::Instance()->ThinElementLength();
67 const G4bool yokeOnLeft = BDSComponentFactory::YokeOnLeft(element,st);
68 const G4double arcLength = element->l * CLHEP::m;
69 const G4double angle = (*st)["angle"];
70 G4double fint = element->fint;
71 G4double fintx = element->fintx;
72 G4double hgap = element->hgap * CLHEP::m;
73
74 // Note for tilted dipoles, the geometry is tilted but the curvilinear world isn't,
75 // therefore we tilt the field to match the geometry.
76 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
77
78 G4bool buildFringeIncoming = buildFringeFields;
79 G4bool buildFringeOutgoing = buildFringeFields;
80
81 G4bool finiteK1 = BDS::IsFinite((*st)["k1"]);
82 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
83
84 if (buildFringeFields)
85 {
86 // perform series of checks on fringe field parameters
87 // nominally, don't build the fringe element if there's no face angle
88 if (!BDS::IsFinite(element->e1))
89 {buildFringeIncoming = false;} // by default false
90 if (!BDS::IsFinite(element->e2))
91 {buildFringeOutgoing = false;}
92
93 // fintx = -1 is how madx writes out that fintx should default to fint
94 // it's also our default. If by default fint=0 and fintx=0, no fringes
95 // would be built. If finite fint and -1 for fintx both will be built.
96 // if fint=0 and fintx != -1, only the outgoin will be built
97 if (fintx == -1)
98 {fintx = fint;}
99
100 // however if finite hgap and fint or fintx specified, there is an effect
101 if (BDS::IsFinite(fint) && BDS::IsFinite(hgap))
102 {buildFringeIncoming = true;}
103 if (BDS::IsFinite(fintx) && BDS::IsFinite(hgap))
104 {buildFringeOutgoing = true;}
105
106 // don't build fringe elements if a non-matrix integrator set is used and
107 // only a pole face effect is specified. The angled pole face geometry is
108 // constructed so a thin integrated pole face kick shouldn't be applied as well
109 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
110 {
111 // no hgap check - finite hgap but 0 fint/x will not produce a fringe kick so fringes won't be built anyway
112 if (BDS::IsFinite(element->e1) && !BDS::IsFinite(fint))
113 {buildFringeIncoming = false;}
114 if (BDS::IsFinite(element->e2) && !BDS::IsFinite(fintx))
115 {buildFringeOutgoing = false;}
116 }
117
118 // overriding checks - don't build fringe field if we're butt against another
119 // sbend.
120 if (prevElement)
121 {
122 if (prevElement->type == ElementType::_SBEND)
123 {
124 buildFringeIncoming = false;
125 if (BDS::IsFinite(prevElement->e2 - element->e1))
126 {throw BDSException( __METHOD_NAME__, prevElement->name + " e2 clashes with " + elementName + " e1");}
127 }
128 }
129 if (nextElement)
130 {
131 if (nextElement->type == ElementType::_SBEND)
132 {buildFringeOutgoing = false;}
133 // check above on clashing sbends with e1 e2 will be used for next bend.
134 }
135
136 if (!BDS::IsFinite(angle))
137 {// no fringe fields if magnet of zero angle, ie strength
138 buildFringeIncoming = false;
139 buildFringeOutgoing = false;
140 }
141 }// end of checks
142
143 // Calculate number of sbends to split parent into
144 G4int nSBends = BDS::CalculateNSBendSegments(arcLength, angle, incomingFaceAngle,
145 outgoingFaceAngle);
146
147 G4bool zeroStrength = BDS::ZeroStrengthDipole(st);
148 if (zeroStrength)
149 {
150 buildFringeIncoming = false;
151 buildFringeOutgoing = false;
152 }
153
154 // If no angle, means no fringe strength, so even if there are pole face rotations, we don't
155 // need the fringe elements. If there's only 1 segment we may still have a finite
156 // angle and therefore finite strength and the pole face rotations will require fringe
157 // elements
158 if (!BDS::IsFinite(angle))
159 {
160 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
161 BDSFieldInfo* vacuumField = nullptr;
162 BDSFieldInfo* outerField = nullptr;
163 // prepare one sbend segment
164 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, -incomingFaceAngle,
165 -outgoingFaceAngle);
166 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(baseName, element,
167 -incomingFaceAngle,
168 -outgoingFaceAngle,
169 bpInfo,
170 yokeOnLeft);
171 if (!zeroStrength)
172 {
173 vacuumField = new BDSFieldInfo(dipoleFieldType,
174 brho,
175 intType,
176 st,
177 true,
178 fieldTiltOffset);
179 vacuumField->SetModulatorInfo(fieldModulator);
181 BDSFieldType::dipole,
182 bpInfo,
183 mgInfo,
184 fieldTiltOffset,
185 integratorSet,
186 brho,
188 fieldModulator);
189 }
190 BDSMagnet* oneBend = new BDSMagnet(BDSMagnetType::sectorbend,
191 baseName,
192 arcLength,
193 bpInfo,
194 mgInfo,
195 vacuumField,
196 -angle,
197 outerField); // angle 0, but should be minus for 3d cart.
198 return oneBend;
199 }
200
201 // We're definitely building a line now.
202 BDSLine* sbendline = new BDSLine(baseName); // line for resultant sbend
203
204 // length of bends minus the fringe segments
205 G4double centralArcLength = arcLength;
206 G4double centralAngle = angle;
207 G4double oneFringeAngle = 0;
208 if (BDS::IsFinite(angle))
209 {oneFringeAngle = (thinElementArcLength / arcLength) * angle;}
210
211 if (buildFringeIncoming)
212 {
213 centralArcLength -= thinElementArcLength;
214 centralAngle -= oneFringeAngle;
215 }
216 if (buildFringeOutgoing)
217 {
218 centralArcLength -= thinElementArcLength;
219 centralAngle -= oneFringeAngle;
220 }
221
222 // calculate their angles and length
223 G4double semiAngle = centralAngle / (G4double) nSBends;
224 G4double semiArcLength = centralArcLength / (G4double) nSBends;
225
226 BDSMagnetStrength* semiStrength = new BDSMagnetStrength(*st); // the copy is crucial to copy the field strength
227 (*semiStrength)["angle"] = semiAngle;
228 (*semiStrength)["length"] = semiArcLength;
229
230 G4double zExtentIn = 0;
231 G4double zExtentOut = 0;
232 G4bool fadeIn = true;
233 G4bool fadeOut = true;
234
235 // unlike an rbend, the sbend will mostly likely be split up into segments.
236 // we must check that the faces of each segment (varying from e1 to e2) will
237 // not overlap given the outer diameter.
238 // calculate extent along z due poleface rotation at half the horizontal width.
239 // note this is only w.r.t. pole face rotation and not the general bending angle
240 G4double horizontalWidth = BDSComponentFactory::PrepareHorizontalWidth(element);
241 if (incomingFaceAngle > 0)
242 {zExtentIn = 0.5*horizontalWidth*std::tan(incomingFaceAngle - 0.5*std::abs(semiAngle));}
243 else if (incomingFaceAngle < 0)
244 {zExtentIn = 0.5*horizontalWidth*std::tan(0.5*std::abs(semiAngle) + incomingFaceAngle);}
245 if (outgoingFaceAngle > 0)
246 {zExtentOut = 0.5*horizontalWidth*std::tan(outgoingFaceAngle - 0.5*std::abs(semiAngle));}
247 else if (outgoingFaceAngle < 0)
248 {zExtentOut = 0.5*horizontalWidth*std::tan(0.5*std::abs(semiAngle) + outgoingFaceAngle);}
249
250 // decide if segment angles fade or not depending on the extents
251 if (std::abs(zExtentIn) < semiArcLength/4.0)
252 {fadeIn = false;}
253 if (std::abs(zExtentOut) < semiArcLength/4.0)
254 {fadeOut = false;}
255
256 // field recipe for one segment of the sbend
257 G4String centralName = baseName + "_even_ang";
258 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
259 BDSFieldInfo* semiVacuumField = nullptr;
260 BDSFieldInfo* semiOuterField = nullptr;
261 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, 0.5*semiAngle, 0.5*semiAngle);
262 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(centralName, element,
263 0.5*semiAngle, 0.5*semiAngle, bpInfo,
264 yokeOnLeft);
265 if (!zeroStrength)
266 {
267 semiVacuumField = new BDSFieldInfo(dipoleFieldType,
268 brho,
269 intType,
270 semiStrength,
271 true,
272 fieldTiltOffset);
273 semiVacuumField->SetModulatorInfo(fieldModulator);
274 semiOuterField = BDSComponentFactory::PrepareMagnetOuterFieldInfo(semiStrength,
275 BDSFieldType::dipole,
276 bpInfo,
277 mgInfo,
278 fieldTiltOffset,
279 integratorSet,
280 brho,
282 fieldModulator);
283 }
284 mgInfo->name = centralName;
285 BDSMagnet* centralWedge = new BDSMagnet(BDSMagnetType::sectorbend,
286 centralName,
287 semiArcLength,
288 bpInfo,
289 mgInfo,
290 semiVacuumField,
291 -semiAngle,
292 semiOuterField); // minus for 3d cartesian conversion
293
294 // check magnet outer info
295 auto magnetOuterInfoCheck = BDSComponentFactory::PrepareMagnetOuterInfo("checking", element,
296 -incomingFaceAngle,
297 -outgoingFaceAngle,
298 bpInfo, yokeOnLeft);
299 // minus for conversion to 3d cartesian
300 G4double minimalRadius = 2*magnetOuterInfoCheck->MinimumIntersectionRadiusRequired();
301 // extra pedantic check for dipoles with certain geometry types
302 if (!magnetOuterInfoCheck->hStyle)
303 {// in the case of C-shaped poled dipoles, the full 'horizontalWidth' is shifted to the yoke side
304 switch (magnetOuterInfoCheck->geometryType.underlying())
305 {
306 case BDSMagnetGeometryType::polescircular:
307 case BDSMagnetGeometryType::polesfacet:
308 case BDSMagnetGeometryType::polesfacetcrop:
309 case BDSMagnetGeometryType::polessquare:
310 {
311 minimalRadius *= element->yokeOnInside ? 2.0 : 0.5;
312 break;
313 }
314 default:
315 {break;}
316 }
317 }
318 BDSComponentFactory::CheckBendLengthAngleWidthCombo(semiArcLength, -semiAngle, minimalRadius, element->name);
319 delete magnetOuterInfoCheck; // clean up
320
321 // build incoming fringe field if required
322 if (buildFringeIncoming)
323 {
324 G4double e1 = element->e1;
325 // if using a non-matrix integrator set, this code should only be reached if a finite fint is specified.
326 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so set
327 // e1 to 0 in the magnet strength object so only fringe effects are applied
328 if (!integratorSet->IsMatrixIntegratorSet()
329 && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
330 && BDS::IsFinite(fint))
331 {e1 = 0;}
332
333 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
334 e1, element->e2, fintx, true);
335 G4String segmentName = baseName + "_e1_fringe";
336 G4double fringeAngleIn = 0.5*oneFringeAngle - incomingFaceAngle;
337 G4double fringeAngleOut = 0.5*oneFringeAngle + incomingFaceAngle;
338 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, fringeAngleIn, fringeAngleOut,
339 segmentName, fringeStIn, brho,
340 integratorSet, dipoleFieldType, fieldModulator);
341 sbendline->AddComponent(startfringe);
342 }
343
344 // logic for elements in the sbend line:
345 // reuse central segment for all segment of in/out half if no poleface angle(s)
346 // if small poleface, new first/last segment, reuse central wedge for remainder of in/out half
347 // otherwise fade in/out faces for all segments in app. halves.
348 // 'central' one is definitely used for the central part, but also it's just a segment
349 // with even incoming and outgoing face angles w.r.t. the chord.
350 G4double segmentAngleIn = 0;
351 G4double segmentAngleOut = 0;
352 G4int numberOfUniqueComponents = 1; // used for naming purposes
353 BDSMagnet* oneBend = nullptr;
354 G4bool centralWedgeUsed = false; // keep track to avoid memory leak
355 const G4int numSegmentsEitherSide = (nSBends - 1) / 2;
356 for (G4int i = 0; i < nSBends; ++i)
357 {
358 G4String name = baseName;
359 if (i < numSegmentsEitherSide)
360 {// first half of magnet
361 if (!BDS::IsFinite(incomingFaceAngle)) // no pole face rotation so just repeat central segment
362 {oneBend = centralWedge;}
363 else if (fadeIn) // build incremented angled segment
364 {
365 name += "_"+std::to_string(numberOfUniqueComponents);
366 numberOfUniqueComponents++;
367 BDS::UpdateSegmentAngles(i,nSBends,semiAngle,incomingFaceAngle,outgoingFaceAngle,segmentAngleIn,segmentAngleOut);
368 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
369 segmentAngleIn, segmentAngleOut, semiStrength,
370 brho, integratorSet, yokeOnLeft, semiOuterField);
371 }
372 else
373 {// finite pole face, but not strong so build one angled, then repeat the rest to save memory
374 if (i == 0) // the first one is unique
375 {
376 name += "_"+std::to_string(numberOfUniqueComponents);
377 numberOfUniqueComponents++;
378 segmentAngleIn = 0.5*semiAngle - incomingFaceAngle; // whole pole face angle
379 segmentAngleOut = 0.5*semiAngle; // even matching angle
380 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
381 segmentAngleIn, segmentAngleOut, semiStrength,
382 brho, integratorSet, yokeOnLeft, semiOuterField);
383 }
384 else // others afterwards are a repeat of the even angled one
385 {oneBend = centralWedge;}
386 }
387 }
388 else if (i > numSegmentsEitherSide) // i is zero counting
389 {// second half of magnet
390 if (!BDS::IsFinite(outgoingFaceAngle)) // no pole face rotation so just repeat central segment
391 {oneBend = centralWedge;}
392 else if (fadeOut) // build incremented angled segment
393 {
394 name += "_"+std::to_string(numberOfUniqueComponents);
395 numberOfUniqueComponents++;
396 BDS::UpdateSegmentAngles(i,nSBends,semiAngle,incomingFaceAngle,outgoingFaceAngle,segmentAngleIn,segmentAngleOut);
397 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
398 segmentAngleIn, segmentAngleOut, semiStrength,
399 brho, integratorSet, yokeOnLeft, semiOuterField);
400 }
401 else
402 {// finite pole face, but not strong so build only one unique angled on output face
403 if (i == (nSBends-1)) // last one
404 {
405 name += "_"+std::to_string(numberOfUniqueComponents);
406 numberOfUniqueComponents++;
407 segmentAngleIn = 0.5*semiAngle;
408 segmentAngleOut = 0.5*semiAngle - outgoingFaceAngle;
409 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
410 segmentAngleIn, segmentAngleOut, semiStrength,
411 brho, integratorSet, yokeOnLeft, semiOuterField);
412 }
413 else // after central, but before unique end piece - even angled.
414 {oneBend = centralWedge;}
415 }
416 }
417 else // the middle piece
418 {oneBend = centralWedge;}
419
420 // append to the line
421 sbendline->AddComponent(oneBend);
422
423 centralWedgeUsed = centralWedgeUsed || (oneBend == centralWedge);
424 }
425
426 if (!centralWedgeUsed)
427 {delete centralWedge;}
428
429 //Last element should be fringe if poleface specified
430 if (buildFringeOutgoing)
431 {
432 G4double e2 = element->e2;
433 // if using a non-matrix integrator set, this code should only be reached if a finite fintx is specified.
434 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so set
435 // e2 to 0 in the magnet strength object so only fringe effects are applied
436 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
437 && BDS::IsFinite(fintx))
438 {e2 = 0;}
439
440 BDSMagnetStrength* fringeStOut = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
441 element->e1, e2, fintx, false);
442 G4double fringeAngleIn = 0.5*oneFringeAngle + outgoingFaceAngle;
443 G4double fringeAngleOut = 0.5*oneFringeAngle - outgoingFaceAngle;
444 G4String segmentName = baseName + "_e2_fringe";
445
446 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, fringeAngleIn, fringeAngleOut,
447 segmentName, fringeStOut, brho,
448 integratorSet, dipoleFieldType);
449 sbendline->AddComponent(endfringe);
450 }
451 return sbendline;
452}
453
454void BDS::UpdateSegmentAngles(G4int index,
455 G4int nSBends,
456 G4double semiAngle,
457 G4double incomingFaceAngle,
458 G4double outgoingFaceAngle,
459 G4double& segmentAngleIn,
460 G4double& segmentAngleOut)
461{
462 // nSBends is always odd.
463 // index assumed 0 counting.
464 G4bool central = index == (nSBends - 1) / 2;
465 if (central)
466 {// shouldn't happen, but just in case
467 segmentAngleIn = 0.5*semiAngle;
468 segmentAngleOut = 0.5*semiAngle;
469 return;
470 }
471
472 G4bool firstHalf = index + 1 < (nSBends + 1) / 2;
473 G4int numberToFadeOver = (nSBends - 1) / 2;
474 if (firstHalf)
475 {// fade from incomingFaceAngle to 0
476 G4double delta = incomingFaceAngle / (G4double) numberToFadeOver;
477 G4double inputFaceAngle = incomingFaceAngle - ((G4double)index * delta);
478 G4double outputFaceAngle = incomingFaceAngle - ((G4double)(index+1) * delta);
479 // if incomingFaceAngle was 0, each would have 1/2 of semiAngle
480 segmentAngleIn = 0.5*semiAngle - inputFaceAngle;
481 segmentAngleOut = 0.5*semiAngle + outputFaceAngle;
482 }
483 else
484 {// second half - fade from 0 to outgoingFaceAngle
485 G4double delta = outgoingFaceAngle / (G4double) numberToFadeOver;
486 G4int secondHalfIndex = index - ((nSBends + 1) / 2);
487 // here we fade from 0 contribution to maximum (opposite of above if statement)
488 G4double inputFaceAngle = outgoingFaceAngle - ( (G4double)(numberToFadeOver - secondHalfIndex) * delta);
489 G4double outputFaceAngle = outgoingFaceAngle - ( (G4double)(numberToFadeOver - (secondHalfIndex + 1)) * delta);
490 // if incomingFaceAngle was 0, each would have 1/2 of semiAngle
491 segmentAngleIn = 0.5*semiAngle + inputFaceAngle;
492 segmentAngleOut = 0.5*semiAngle - outputFaceAngle;
493 }
494}
495
497 const G4String& name,
498 G4double arcLength,
499 G4double angle,
500 G4double angleIn,
501 G4double angleOut,
502 const BDSMagnetStrength* strength,
503 G4double brho,
504 const BDSIntegratorSet* integratorSet,
505 G4bool yokeOnLeft,
506 const BDSFieldInfo* outerFieldIn,
507 BDSModulatorInfo* fieldModulator)
508{
509 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, angleIn, angleOut);
510
511 BDSMagnetStrength* strengthCopy = new BDSMagnetStrength(*strength); // the copy is crucial to copy the field strength
512 auto magnetOuterInfo = BDSComponentFactory::PrepareMagnetOuterInfo(name, element, angleIn, angleOut, bpInfo, yokeOnLeft);
513 // set the name to the desired one rather than the one from the element
514 magnetOuterInfo->name = name;
515
516 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
517
518 G4bool finiteK1 = BDS::IsFinite((*strength)["k1"]);
519 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
520 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
521 BDSFieldInfo* vacuumField = nullptr;
522 BDSFieldInfo* outerField = nullptr;
523 G4bool zeroStrength = BDS::ZeroStrengthDipole(strengthCopy);
524 if (!zeroStrength)
525 {
526 vacuumField = new BDSFieldInfo(dipoleFieldType,
527 brho,
528 intType,
529 strengthCopy,
530 true,
531 fieldTiltOffset);
532 vacuumField->SetModulatorInfo(fieldModulator);
533 outerField = new BDSFieldInfo(*outerFieldIn);
534 }
535
536 BDSMagnet* magnet = new BDSMagnet(BDSMagnetType::sectorbend,
537 name,
538 arcLength,
539 bpInfo,
540 magnetOuterInfo,
541 vacuumField,
542 -angle,
543 outerField);
544
545 return magnet;
546}
547
548BDSLine* BDS::BuildRBendLine(const G4String& elementName,
549 const Element* element,
550 const Element* prevElement,
551 const Element* nextElement,
552 G4double brho,
554 const BDSIntegratorSet* integratorSet,
555 G4double incomingFaceAngle,
556 G4double outgoingFaceAngle,
557 G4bool buildFringeFields,
558 BDSModulatorInfo* fieldModulator)
559{
560 const G4String name = elementName;
561 BDSLine* rbendline = new BDSLine(name); // line for resultant rbend
562
563 const G4double thinElementArcLength = BDSGlobalConstants::Instance()->ThinElementLength();
564 const G4bool yokeOnLeft = BDSComponentFactory::YokeOnLeft(element, st);
565 G4bool buildFringeIncoming = buildFringeFields;
566 G4bool buildFringeOutgoing = buildFringeFields;
567 G4double fint = element->fint;
568 G4double fintx = element->fintx;
569 G4double hgap = element->hgap * CLHEP::m;
570
571 // Angle here is in the 'strength' convention of +ve angle -> -ve x deflection
572 const G4double angle = (*st)["angle"];
573 const G4double arcLength = (*st)["length"];
574
575 G4bool zeroStrength = BDS::ZeroStrengthDipole(st);
576
577 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
578
579 G4bool finiteK1 = BDS::IsFinite((*st)["k1"]);
580 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
581
582 // Here, 'no face angle' really means that the rbend becomes an sbend.
583 // Calculate how far away we are from an sbend.
584 G4double incomingFaceAngleWRTSBend = incomingFaceAngle + angle*0.5;
585 G4double outgoingFaceangleWRTSBend = outgoingFaceAngle + angle*0.5;
586 if (!BDS::IsFinite(incomingFaceAngleWRTSBend) && (!integratorSet->IsMatrixIntegratorSet()))
587 {buildFringeIncoming = false;}
588 if (!BDS::IsFinite(outgoingFaceangleWRTSBend) && (!integratorSet->IsMatrixIntegratorSet()))
589 {buildFringeOutgoing = false;}
590
591 // fintx = -1 is how madx writes out that fintx should default to fint
592 // it's also our default. If by default fint=0 and fintx=0, no fringes
593 // would be built. If finite fint and -1 for fintx both will be built.
594 // if fint=0 and fintx != -1, only the outgoin will be built
595 if (fintx == -1)
596 {fintx = fint;}
597
598 // however if finite hgap and fint or fintx specified, there is an effect
599 if (BDS::IsFinite(fint) && BDS::IsFinite(hgap))
600 {buildFringeIncoming = true;}
601 if (BDS::IsFinite(fintx) && BDS::IsFinite(hgap))
602 {buildFringeOutgoing = true;}
603
604 // the poleface angles to be used in tracking only.
605 G4double trackingPolefaceAngleIn = element->e1;
606 G4double trackingPolefaceAngleOut = element->e2;
607 if (integratorSet->IsMatrixIntegratorSet())
608 {// for this integrator set we track the rbend as an sbend with extra pole face rotation
609 trackingPolefaceAngleIn += angle*0.5;
610 trackingPolefaceAngleOut += angle*0.5;
611 }
612
613 G4double e1 = -incomingFaceAngle;
614 G4double e2 = -outgoingFaceAngle;
615
616 if (prevElement)
617 {
618 if (BDS::IsFinite(prevElement->e2) && BDS::IsFinite(element->e1))
619 {
620 throw BDSException(__METHOD_NAME__, prevElement->name + " has finite e2!\n Clashes with " + elementName + " with finite e1");
621 }
622 if (prevElement->type == ElementType::_RBEND)
623 {
624 buildFringeIncoming = false;
625 e1 = angle*0.5;
626 }
627 }
628 if (nextElement)
629 {
630 if (BDS::IsFinite(nextElement->e1) && BDS::IsFinite(element->e2))
631 {
632 throw BDSException(__METHOD_NAME__ + nextElement->name + " has finite e1!\n Clashes with " + elementName + " with finite e2");
633 }
634 if (nextElement->type == ElementType::_RBEND)
635 {
636 buildFringeOutgoing = false;
637 e2 = angle*0.5;
638 }
639 }
640
641 if (zeroStrength)
642 {
643 buildFringeIncoming = false;
644 buildFringeOutgoing = false;
645 }
646
647 // used for debugging purposes to forefully try out one and not the other fringe
648 //buildFringeIncoming = false;
649 //buildFringeOutgoing = false;
650
651 // if we're building the fringe elements, we reduce the length of the central section
652 // and proportion the bending by their length w.r.t. the full length of the total rbend.
653 // angles of the faces are in order:
654 // angleIn / fringeInOutputAngle / centralInputFaceAngle / centralOutputFaceAngle
655 // fringeOutInputAngle / angleOut
656 // default face angles for an rbend are 0 - ie parallel faces, plus any pole face rotation
657 // angle in and out of total rbend are nominally the face angles.
658 G4double angleIn = incomingFaceAngle;
659 G4double angleOut = outgoingFaceAngle;
660 G4double fringeInOutputAngle = 0;
661 G4double centralInputFaceAngle = angleIn;
662 G4double centralOutputFaceAngle = angleOut;
663 G4double fringeOutInputAngle = 0;
664 G4double centralArcLength = arcLength; // full length to start with
665 G4double centralAngle = angle;
666 G4double oneFringeAngle = 0;
667 if (BDS::IsFinite(angle))
668 {oneFringeAngle = (thinElementArcLength / arcLength) * angle;}
669
670 if (buildFringeIncoming && buildFringeOutgoing)
671 {// both
672 centralArcLength -= 2*thinElementArcLength;
673 centralAngle -= 2*oneFringeAngle;
674 angleIn = e1 + (0.5*oneFringeAngle - 0.5*angle);
675 fringeInOutputAngle = -angleIn;
676 centralInputFaceAngle = e1;
677 centralOutputFaceAngle = e2;
678 fringeOutInputAngle = - (e2 + (0.5*oneFringeAngle - 0.5*angle));
679 angleOut = -fringeOutInputAngle;
680 }
681 else if (buildFringeIncoming)
682 {// incoming only
683 centralArcLength -= thinElementArcLength;
684 centralAngle -= oneFringeAngle;
685 angleIn = e1 + (0.5*oneFringeAngle - 0.5*angle);
686 fringeInOutputAngle = -angleIn; // fringe built the same
687 centralInputFaceAngle = e1 + 0.5*oneFringeAngle;
688 centralOutputFaceAngle = e2 - 0.5*oneFringeAngle;
689 }
690 else if (buildFringeOutgoing)
691 {// outgoing only
692 centralArcLength -= thinElementArcLength;
693 centralAngle -= oneFringeAngle;
694 centralInputFaceAngle = e1 - 0.5*oneFringeAngle;
695 centralOutputFaceAngle = e2 + 0.5*oneFringeAngle;
696 fringeOutInputAngle = - (e2 + (0.5*oneFringeAngle - 0.5*angle));;
697 angleOut = e2 + (0.5*oneFringeAngle - 0.5*angle);;
698 }
699 else
700 {// neither
701 // centralArcLength the same
702 // centralAngle the same
703 centralInputFaceAngle = e1;
704 centralOutputFaceAngle = e2;
705 }
706
707 if (buildFringeIncoming)
708 {
709 G4double trackingPolefaceAngle = trackingPolefaceAngleIn;
710 // if using a non-matrix integrator set, this code should only be reached if a finite fint is specified.
711 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so subtract
712 // e1 for in the magnet strength object so only the fringe & the natural rbend angled face effects are applied
713 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
714 && BDS::IsFinite(fint))
715 {trackingPolefaceAngle -= element->e1;}
716
717 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
718 trackingPolefaceAngle, trackingPolefaceAngleOut,
719 fintx, true);
720 G4String fringeName = name + "_e1_fringe";
721
722 // element used for beam pipe materials etc - not strength, angle or length.
723 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, angleIn, fringeInOutputAngle,
724 fringeName,
725 fringeStIn, brho,
726 integratorSet, dipoleFieldType, fieldModulator);
727 rbendline->AddComponent(startfringe);
728 }
729
730 // override copied length and angle
731 (*st)["length"] = centralArcLength;
732 (*st)["angle"] = centralAngle;
733
734 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
735 BDSFieldInfo* vacuumField = nullptr;
736 BDSFieldInfo* outerField = nullptr;
737 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, centralInputFaceAngle, centralOutputFaceAngle);
738 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(elementName, element, centralInputFaceAngle, centralOutputFaceAngle, bpInfo, yokeOnLeft);
739 mgInfo->name = elementName;
740 if (!zeroStrength)
741 {
742 vacuumField = new BDSFieldInfo(dipoleFieldType,
743 brho,
744 intType,
745 st,
746 true,
747 fieldTiltOffset);
748 vacuumField->SetModulatorInfo(fieldModulator);
750 BDSFieldType::dipole,
751 bpInfo,
752 mgInfo,
753 fieldTiltOffset,
754 integratorSet,
755 brho,
757 fieldModulator);
758 }
759
760 // Here we change from the strength angle convention of +ve angle corresponds to
761 // deflection in negative x, to correct 3d +ve angle corresponds to deflection in
762 // positive x. Hence, angle sign flip for construction.
763 BDSMagnet* oneBend = new BDSMagnet(BDSMagnetType::rectangularbend,
764 elementName+"_centre",
765 centralArcLength,
766 bpInfo,
767 mgInfo,
768 vacuumField,
769 -centralAngle,
770 outerField);
771
772 rbendline->AddComponent(oneBend);
773
774 //Last element should be fringe if poleface specified
775 if (buildFringeOutgoing)
776 {
777 G4double trackingPolefaceAngle = trackingPolefaceAngleOut;
778 // if using a non-matrix integrator set, this code should only be reached if a finite fintx is specified.
779 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so subtract
780 // e2 for in the magnet strength object so only the fringe & the natural rbend angled face effects are applied
781 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
782 && BDS::IsFinite(fintx))
783 {trackingPolefaceAngle -= element->e2;}
784 BDSMagnetStrength* fringeStOut = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
785 trackingPolefaceAngleIn, trackingPolefaceAngle,
786 fintx, false);
787 G4String fringeName = name + "_e2_fringe";
788
789 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, fringeOutInputAngle, angleOut,
790 fringeName,
791 fringeStOut, brho,
792 integratorSet, dipoleFieldType);
793 rbendline->AddComponent(endfringe);
794 }
795
796 return rbendline;
797}
798
800 G4double angleIn,
801 G4double angleOut,
802 const G4String& name,
804 G4double brho,
805 const BDSIntegratorSet* integratorSet,
806 BDSFieldType dipoleFieldType,
807 BDSModulatorInfo* fieldModulator)
808{
809 BDSBeamPipeInfo* beamPipeInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, angleIn, angleOut);
810 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
811 auto magnetOuterInfo = BDSComponentFactory::PrepareMagnetOuterInfo(name, element,
812 angleIn, angleOut, beamPipeInfo);
813 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
814 magnetOuterInfo->name = name;
815 magnetOuterInfo->buildEndPieces = false;
816
817 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
818
819 BDSIntegratorType intType = integratorSet->dipoleFringe;
820 BDSFieldInfo* vacuumField = new BDSFieldInfo(dipoleFieldType,
821 brho,
822 intType,
823 st,
824 true,
825 fieldTiltOffset);
826 vacuumField->SetModulatorInfo(fieldModulator);
827 vacuumField->SetFieldAsThin();
828
829 return new BDSMagnet(BDSMagnetType::dipolefringe,
830 name,
831 (*st)["length"],
832 beamPipeInfo,
833 magnetOuterInfo,
834 vacuumField,
835 -(*st)["angle"],
836 nullptr,
837 true);
838}
839
840G4int BDS::CalculateNSBendSegments(G4double length,
841 G4double angle,
842 G4double incomingFaceAngle,
843 G4double outgoingFaceAngle,
844 G4double aperturePrecision)
845{
846 // Split a bend into equal segments such that the maximum distance between the
847 // chord and arc is 1mm.
848
849 // from formula: L/2 / N tan (angle/N) < precision. (L=physical length)
850 // add poleface rotations onto angle as absolute number (just to be safe)
851 G4double totalAngle = std::abs(angle) + std::abs(incomingFaceAngle) + std::abs(outgoingFaceAngle);
852 G4int nSBends = (G4int) ceil(std::sqrt(length*totalAngle/2/aperturePrecision));
853 if (nSBends==0)
854 {nSBends = 1;} // can happen in case angle = 0
855 if (BDSGlobalConstants::Instance()->DontSplitSBends())
856 {nSBends = 1;} // use for debugging
857 if (nSBends % 2 == 0)
858 {nSBends += 1;} // always have odd number of poles for poleface rotations
859#ifdef BDSDEBUG
860 G4cout << __METHOD_NAME__ << " splitting sbend into " << nSBends << " sbends" << G4endl;
861#endif
862 return nSBends;
863}
864
866 const Element* element)
867{
868 // default is dipole integrator
869 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::dipole);
870
871 // check for finite k1 and change integrator type if needed
872 if (BDS::IsFinite(element->k1))
873 {intType = integratorSet->Integrator(BDSFieldType::dipolequadrupole);}
874
875 //if (BDS::IsFinite(element->tilt))
876 // {intType = BDSIntegratorType::dipolerodrigues2;}
877
878 return intType;
879}
880
881BDSMagnetStrength* BDS::GetFringeMagnetStrength(const Element* element,
882 const BDSMagnetStrength* st,
883 G4double fringeAngle,
884 G4double e1,
885 G4double e2,
886 G4double fintx,
887 G4bool isEntrance)
888{
889 BDSMagnetStrength* fringeSt = new BDSMagnetStrength(*st);
890 (*fringeSt)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
891 (*fringeSt)["angle"] = fringeAngle;
892 (*fringeSt)["e1"] = e1; // supply separately as it may be modified for rbends
893 (*fringeSt)["e2"] = e2; // supply separately as it may be modified for rbends
894 (*fringeSt)["fint"] = element->fint;
895 (*fringeSt)["fintx"] = fintx; // supply separately as it may be modified to match madx behaviour
896 (*fringeSt)["fintk2"] = element->fintK2;
897 (*fringeSt)["fintk2"] = element->fintxK2;
898 (*fringeSt)["hgap"] = element->hgap * CLHEP::m;
899 (*fringeSt)["isentrance"] = isEntrance;
900 (*fringeSt)["h1"] = element->h1 / CLHEP::m;
901 (*fringeSt)["h2"] = element->h2 / CLHEP::m;
902 return fringeSt;
903}
Abstract class that represents a component of an accelerator.
Holder class for all information required to describe a beam pipe model.
BDSBeamPipeType beamPipeType
Public member for direct access.
static G4bool YokeOnLeft(const GMAD::Element *el, const BDSMagnetStrength *st)
static G4double PrepareHorizontalWidth(GMAD::Element const *el, G4double defaultHorizontalWidth=-1)
Prepare the element horizontal width in Geant4 units - if not set, use the global default.
static G4Transform3D CreateFieldTransform(const BDSTiltOffset *tiltOffset)
Create a transform from a tilt offset. If nullptr, returns identity transform.
static BDSFieldInfo * PrepareMagnetOuterFieldInfo(const BDSMagnetStrength *vacuumSt, const BDSFieldType &fieldType, const BDSBeamPipeInfo *bpInfo, const BDSMagnetOuterInfo *outerInfo, const G4Transform3D &fieldTransform, const BDSIntegratorSet *integratorSetIn, G4double brhoIn, G4double outerFieldScaling=1.0, BDSModulatorInfo *modulatorInfo=nullptr)
Prepare the field definition for the yoke of a magnet.
static BDSMagnetOuterInfo * PrepareMagnetOuterInfo(const G4String &elementNameIn, const GMAD::Element *el, const BDSMagnetStrength *st, const BDSBeamPipeInfo *beamPipe, G4double defaultHorizontalWidth=-1, G4double defaultVHRatio=1.0, G4double defaultCoilWidthFraction=-1, G4double defaultCoilHeightFraction=-1)
static void CheckBendLengthAngleWidthCombo(G4double arcLength, G4double angle, G4double horizontalWidth, const G4String &name="not given")
static BDSBeamPipeInfo * PrepareBeamPipeInfo(GMAD::Element const *el, const G4ThreeVector &inputFaceNormal=G4ThreeVector(0, 0,-1), const G4ThreeVector &outputFaceNormal=G4ThreeVector(0, 0, 1))
static G4double ScalingFieldOuter(const GMAD::Element *ele)
Get the scaling factor for a particular outer field depending on the global and individual setting.
General exception with possible name of object and message.
All info required to build complete field of any type.
void SetFieldAsThin()
Set thin to allow geant tracking error controls to be set seperately for thin elements.
static BDSGlobalConstants * Instance()
Access method.
Which integrator to use for each type of magnet / field object.
G4bool IsMatrixIntegratorSet() const
Accessor for bool of is the integrator set matrix style.
BDSIntegratorType Integrator(const BDSFieldType field) const
Get appropriate integrator based on the field type.
A class that hold multiple accelerator components.
Definition BDSLine.hh:38
void AddComponent(BDSAcceleratorComponent *component)
Add a component to the line.
Definition BDSLine.cc:28
Efficient storage of magnet strengths.
Abstract base class that implements features common to all magnets.
Definition BDSMagnet.hh:45
Holder class for all information required to describe a modulator.
G4bool ZeroStrengthDipole(const BDSMagnetStrength *st)
Return whether finite angle or field for a dipole.
G4int CalculateNSBendSegments(G4double length, G4double angle, G4double incomingFaceAngle=0, G4double outgoingFaceAngle=0, G4double aperturePrecision=1.0)
BDSMagnet * BuildSingleSBend(const GMAD::Element *element, const G4String &name, G4double arcLength, G4double angle, G4double angleIn, G4double angleOut, const BDSMagnetStrength *strength, G4double brho, const BDSIntegratorSet *integratorSet, G4bool yokeOnLeft, const BDSFieldInfo *outerFieldIn, BDSModulatorInfo *fieldModulator=nullptr)
Function to return a single sector bend section.
BDSMagnet * BuildDipoleFringe(const GMAD::Element *element, G4double angleIn, G4double angleOut, const G4String &name, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, BDSFieldType dipoleFieldType, BDSModulatorInfo *fieldModulator=nullptr)
BDSLine * BuildRBendLine(const G4String &elementName, const GMAD::Element *element, const GMAD::Element *prevElement, const GMAD::Element *nextElement, G4double brho, BDSMagnetStrength *st, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, BDSModulatorInfo *fieldModulator=nullptr)
BDSIntegratorType GetDipoleIntegratorType(const BDSIntegratorSet *integratorSet, const GMAD::Element *element)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
BDSAcceleratorComponent * BuildSBendLine(const G4String &elementName, const GMAD::Element *element, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, const GMAD::Element *prevElement, const GMAD::Element *nextElement, BDSModulatorInfo *fieldModulator=nullptr)
Parser namespace for GMAD language. Combination of Geant4 and MAD.
Element class.
Definition element.h:45
double hgap
half distance of pole separation for purposes of fringe fields - 'half gap'
Definition element.h:68
double fintx
fringe field integral at the dipole exit
Definition element.h:65
double h2
output pole face curvature for bends
Definition element.h:70
double h1
input pole face curvature for bends
Definition element.h:69
double fintxK2
second fringe field integral at the dipole exit - for TRANSPORT matching
Definition element.h:67
double fint
fringe field integral at the dipole entrance
Definition element.h:64
double e2
output pole face rotation for bends
Definition element.h:63
double l
length in metres
Definition element.h:51
double e1
input pole face rotation for bends
Definition element.h:62
double k1
quadrupole
Definition element.h:56
double fintK2
second fringe field integral at the dipole entrance - for TRANSPORT matching
Definition element.h:66
ElementType type
element enum
Definition element.h:46