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