BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSBeamline.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 "BDSDebug.hh"
19#include "BDSAcceleratorComponent.hh"
20#include "BDSBeamline.hh"
21#include "BDSBeamlineElement.hh"
22#include "BDSBeamlineIntegral.hh"
23#include "BDSException.hh"
24#include "BDSExtentGlobal.hh"
25#include "BDSGlobalConstants.hh"
26#include "BDSLine.hh"
27#include "BDSOutput.hh"
28#include "BDSSamplerPlane.hh"
29#include "BDSSimpleComponent.hh"
30#include "BDSTiltOffset.hh"
31#include "BDSTransform3D.hh"
32#include "BDSUtilities.hh"
33#include "BDSWarning.hh"
34
35#include "globals.hh" // geant4 globals / types
36#include "G4RotationMatrix.hh"
37#include "G4ThreeVector.hh"
38#include "G4Transform3D.hh"
39
40#include "CLHEP/Vector/AxisAngle.h"
41
42#include <algorithm>
43#include <iterator>
44#include <ostream>
45#include <set>
46#include <vector>
47
48G4double BDSBeamline::paddingLength = -1;
49
50BDSBeamline::BDSBeamline(const G4ThreeVector& initialGlobalPosition,
51 G4RotationMatrix* initialGlobalRotation,
52 G4double initialSIn):
53 sInitial(initialSIn),
54 sMaximum(initialSIn),
55 totalChordLength(0),
56 totalArcLength(0),
57 totalAngle(0),
58 previousReferencePositionEnd(initialGlobalPosition),
59 previousSPositionEnd(sInitial),
60 transformHasJustBeenApplied(false)
61{
62 // initialise extents
63 maximumExtentPositive = G4ThreeVector(0,0,0);
64 maximumExtentNegative = G4ThreeVector(0,0,0);
65
66 // initial rotation matrix
67 if (initialGlobalRotation) // default is null
68 {previousReferenceRotationEnd = initialGlobalRotation;}
69 else
70 {previousReferenceRotationEnd = new G4RotationMatrix();}
71
72 // gap between each element added to the beam line
73 if (paddingLength <= 0)
74 {paddingLength = 3 * BDSGlobalConstants::Instance()->LengthSafety();}
75 //paddingLength = 3*CLHEP::mm;
76
77#ifdef BDSDEBUG
78 G4cout << __METHOD_NAME__ << "with initial position and rotation" << G4endl;
79 G4cout << "Initial position: " << initialGlobalPosition << G4endl;
80 G4cout << "Initial rotation: " << *previousReferenceRotationEnd << G4endl;
81#endif
82}
83
84BDSBeamline::BDSBeamline(G4Transform3D initialTransform,
85 G4double initialSIn):
86 BDSBeamline(initialTransform.getTranslation(),
87 new G4RotationMatrix(initialTransform.getRotation()),
88 initialSIn)
89{;}
90
91BDSBeamline::~BDSBeamline()
92{
93 for (auto it : *this)
94 {delete it;}
95 // special case, if empty then previousReferenceRotationEnd is not used in the first element
96 if (size()==0)
98 // components map goes out of scope - elements are already deleted - no need to
99 // explicitly delete
100}
101
102void BDSBeamline::PrintAllComponents(std::ostream& out) const
103{
104 for (const auto& element : *this)
105 {out << element;}
106}
107
109{
110 G4cout << __METHOD_NAME__ << "container size: " << sizeof(beamline) << G4endl;
111 G4cout << __METHOD_NAME__ << "beamline element cumulative size: " << sizeof(BDSBeamlineElement) * beamline.size() << G4endl;
112 G4cout << __METHOD_NAME__ << "full usage including components: " << (sizeof(BDSBeamlineElement) + sizeof(BDSAcceleratorComponent)) * beamline.size() << G4endl;
113}
114
115std::ostream& operator<< (std::ostream& out, BDSBeamline const &bl)
116{
117 out << "Beamline with " << bl.size() << " elements" << G4endl;
118 out << "Total arc length: " << bl.totalArcLength << " mm" << G4endl;
119 out << "Total chord length: " << bl.totalChordLength << " mm" << G4endl;
120
121 return out;
122}
123
125 BDSTiltOffset* tiltOffset,
126 BDSSamplerInfo* samplerInfo,
127 const BDSBeamlineIntegral* beamlineIntegral)
128{
129 if (!component)
130 {
131 G4String samplerName = samplerInfo ? samplerInfo->name : "no_sampler_name_given";
132 throw BDSException(__METHOD_NAME__, "invalid accelerator component " + samplerName);
133 }
134
135 // check the sampler name is allowed in the output
136 if (samplerInfo)
137 {
138 G4String samplerName = samplerInfo->name;
139 if (BDSOutput::InvalidSamplerName(samplerName))
140 {
141 G4cerr << __METHOD_NAME__ << "invalid sampler name \"" << samplerName << "\"" << G4endl;
143 throw BDSException(__METHOD_NAME__, "");
144 }
145 }
146
147 if (BDSLine* line = dynamic_cast<BDSLine*>(component))
148 {
149 // in the case a single component has become a line, when we have a cylindrical
150 // sample we should flag that it should cover the full 'line', however with a plane
151 // one it is only attached to the last element.
152 BDSBeamlineElement* first = nullptr;
153 BDSBeamlineElement* last = nullptr;
154 G4int sizeLine = (G4int)line->size();
155 for (G4int i = 0; i < sizeLine; ++i)
156 {
157 if (i < sizeLine-1)
158 {
159 AddSingleComponent((*line)[i], tiltOffset, nullptr, beamlineIntegral);
160 if (i == 0)
161 {first = back();}
162 }
163 else
164 {// only attach the desired sampler to the last one in the line
165 AddSingleComponent((*line)[i], tiltOffset, samplerInfo, beamlineIntegral);
166 last = back();
167 }
168 }
169 if (samplerInfo) // could be nullptr
170 {
171 if (samplerInfo->samplerType == BDSSamplerType::cylinder) // only a cylinder or plane can be attached to an element
172 {// cache the range it should cover as a cylinder
173 last->GetSamplerInfo()->startElement = first;
174 last->GetSamplerInfo()->finishElement = last;
175 // calculate the mid (i.e. mean) position and rotation
176 G4ThreeVector midRefPosition = (last->GetReferencePositionEnd() + first->GetReferencePositionStart()) / 2.0;
177 G4ThreeVector aaMidAxis;
178 G4double aaMidAngle;
179 auto aaStart = first->GetReferenceRotationStart()->axisAngle();
180 auto aaFinish = last->GetReferenceRotationEnd()->axisAngle();
181 // careful of identity rotations in AA form (axis=(0,0,1),angle=0) as our average of these would be wrong
182 if (first->GetReferenceRotationStart()->isIdentity())
183 {
184 aaMidAxis = aaFinish.axis();
185 aaMidAngle = 0.5 * aaFinish.delta();
186 }
187 else if (last->GetReferenceRotationEnd()->isIdentity())
188 {
189 aaMidAxis = aaStart.axis();
190 aaMidAngle = 0.5 * aaStart.delta();
191 }
192 else
193 {
194 aaMidAxis = (aaFinish.axis() + aaStart.axis()) / 2.0;
195 aaMidAngle = (aaFinish.delta() + aaStart.delta()) / 2.0;
196 }
197 auto aaCSampler = CLHEP::HepAxisAngle(aaMidAxis, aaMidAngle);
198 G4RotationMatrix rmCSampler = G4RotationMatrix(aaCSampler);
199 G4Transform3D trCSampler(rmCSampler, midRefPosition);
200 last->UpdateSamplerPlacementTransform(trCSampler);
201 }
202 }
203 }
204 else
205 {AddSingleComponent(component, tiltOffset, samplerInfo, beamlineIntegral);}
206 // free memory - as once the rotations are calculated, this is no longer needed
207 delete tiltOffset;
208}
209
211 BDSTiltOffset* tiltOffset,
212 BDSSamplerInfo* samplerInfo,
213 const BDSBeamlineIntegral* beamlineIntegral)
214{
215#ifdef BDSDEBUG
216 G4cout << G4endl << __METHOD_NAME__ << "adding component to beamline and calculating coordinates" << G4endl;
217 G4cout << "component name: " << component->GetName() << G4endl;
218#endif
219 // Test if it's a BDSTransform3D instance - this is a unique component that requires
220 // rotation in all dimensions and can skip normal addition as isn't a real volume
221 // that can be placed. Apply the transform and skip the rest of this function by returning
222 // This modifies the "end" coordinates, rotation and axes of the last element in the beamline
223 if (BDSTransform3D* transform = dynamic_cast<BDSTransform3D*>(component))
224 {
225 ApplyTransform3D(transform);
227 return;
228 }
229
230 // if it's not a transform3d instance, continue as normal
231 // interrogate the item
232 G4double chordLength = component->GetChordLength();
233 G4double angle = component->GetAngle();
234 G4bool hasFiniteLength = BDS::IsFinite(chordLength);
235 G4bool hasFiniteAngle = BDS::IsFinite(angle);
236 G4bool hasFiniteTilt, hasFiniteOffset;
237 G4ThreeVector offset;
238 if (tiltOffset)
239 {
240 hasFiniteTilt = tiltOffset->HasFiniteTilt();
241 hasFiniteOffset = tiltOffset->HasFiniteOffset();
242 offset = tiltOffset->GetOffset(); // returns 3Vector
243 }
244 else
245 {
246 hasFiniteTilt = false;
247 hasFiniteOffset = false;
248 offset = G4ThreeVector();
249 }
250 G4ThreeVector eP = component->GetExtentPositive() + offset;
251 G4ThreeVector eN = component->GetExtentNegative() + offset;
252 G4ThreeVector placementOffset = component->GetPlacementOffset();
253 G4bool hasFinitePlacementOffset = BDS::IsFinite(placementOffset);
254 G4ThreeVector oFNormal = component->InputFaceNormal();
255
256#ifdef BDSDEBUG
257 G4cout << "chord length " << chordLength << " mm" << G4endl;
258 G4cout << "angle " << angle << " rad" << G4endl;
259 if (tiltOffset)
260 {G4cout << "tilt offsetX offsetY " << *tiltOffset << " rad mm mm " << G4endl;}
261 else
262 {G4cout << "no tilt offset" << G4endl;}
263 G4cout << "has finite length " << hasFiniteLength << G4endl;
264 G4cout << "has finite angle " << hasFiniteAngle << G4endl;
265 G4cout << "has finite tilt " << hasFiniteTilt << G4endl;
266 G4cout << "has finite offset " << hasFiniteOffset << G4endl;
267 G4cout << "extent positive " << eP << G4endl;
268 G4cout << "extent negative " << eN << G4endl;
269 G4cout << "object placement offset " << placementOffset << G4endl;
270 G4cout << "has finite placement offset " << hasFinitePlacementOffset << G4endl;
271 G4cout << "output face normal " << oFNormal << G4endl;
272#endif
273
274 // Check this won't overlap with any previous geometry. This is only done for elements
275 // that aren't drifts as they should be built by the component factory to match any angles.
276 if (!empty() && (component->GetType() != "drift") && (component->GetType() != "thinmultipole"))
277 {// can only look back if there is an element - won't clash if no element; also add drifts always
278 G4bool keepGoing = true;
279 G4bool checkFaces = true;
280 G4double zSeparation = 0;
281 const BDSBeamlineElement* inspectedElement = back(); // remember we haven't added this new element yet
282 // find previous non drift output face.
283 G4ThreeVector iFNormal;
284 G4String clasherName = "Unknown";
285 while (keepGoing)
286 {
287 if (inspectedElement) // valid element
288 {// decrement could return nullptr so have to check if valid element
289 if ((inspectedElement->GetType() == "drift")||(inspectedElement->GetType() == "thinmultipole")) // leave keepGoing true
290 {
291 zSeparation += inspectedElement->GetChordLength();
292 inspectedElement = GetPrevious(inspectedElement); // decrement
293 }
294 else
295 {
296 keepGoing = false; // found a non drift - stop here
297 iFNormal = inspectedElement->GetAcceleratorComponent()->OutputFaceNormal();
298 clasherName = inspectedElement->GetAcceleratorComponent()->GetName();
299 }
300 }
301 else
302 {
303 keepGoing = false;
304 checkFaces = false; // got to the beginning with only drifts - don't check
305 }
306 }
307#ifdef BDSDEBUG
308 G4cout << "input face normal " << iFNormal << G4endl; // matches above debug formatting
309#endif
310
311 if (checkFaces)
312 {
313 // now do checks
314 BDSExtent extOF = inspectedElement->GetAcceleratorComponent()->GetExtent(); // output face
315 BDSExtent extIF = component->GetExtent(); // input face
316
317 G4bool willIntersect = BDS::WillIntersect(iFNormal, oFNormal, zSeparation, extIF, extOF);
318 if (willIntersect)
319 {
320 G4cout << __METHOD_NAME__ << "Error - angled faces of objects will cause overlap in beam line geometry" << G4endl;
321 G4cout << "\"" << component->GetName() << "\" will overlap with \""
322 << clasherName << "\"" << G4endl;
323 throw BDSException(__METHOD_NAME__, "");
324 }
325 }
326 }
327
328 // Calculate the reference placement rotation
329 // rotations are done first as they're required to transform the spatial displacements.
330 // if not the first element in the beamline, copy the rotation matrix (cumulative along line)
331 // from end of last component, else use initial rotation matrix (no copy to prevent memory leak)
332 G4RotationMatrix* referenceRotationStart;
333 if (empty())
334 {referenceRotationStart = previousReferenceRotationEnd;}
335 else
336 {
339 referenceRotationStart = new G4RotationMatrix(*previousReferenceRotationEnd); // always create a new copy
340 }
341
342 G4RotationMatrix* referenceRotationMiddle = new G4RotationMatrix(*referenceRotationStart);
343 G4RotationMatrix* referenceRotationEnd = new G4RotationMatrix(*referenceRotationStart);
344
345 // if the component induces an angle in the reference trajectory, rotate the mid and end point
346 // rotation matrices appropriately
347 if (hasFiniteAngle)
348 {
349 // remember our definition of angle - +ve angle bends in -ve x direction in right
350 // handed coordinate system
351 // rotate about cumulative local y axis of beamline
352 // middle rotated by half angle in local x,z plane
353 G4ThreeVector rotationAxisOfBend = G4ThreeVector(0,1,0); // nominally about local unit Y
354 G4ThreeVector rotationAxisOfBendEnd = rotationAxisOfBend; // a copy
355 if (hasFiniteTilt)
356 {
357 G4double tilt = tiltOffset->GetTilt();
358 G4RotationMatrix rotationAxisRM = G4RotationMatrix();
359 rotationAxisRM.rotateZ(tilt);
360 rotationAxisOfBend.transform(rotationAxisRM);
361 rotationAxisOfBendEnd.transform(rotationAxisRM);
362 }
363 referenceRotationMiddle->rotate(angle*0.5, rotationAxisOfBend.transform(*previousReferenceRotationEnd));
364 // end rotated by full angle in local x,z plane
365 referenceRotationEnd->rotate(angle, rotationAxisOfBendEnd.transform(*previousReferenceRotationEnd));
366 }
367
368 G4RotationMatrix* rotationStart = new G4RotationMatrix(*referenceRotationStart);
369 G4RotationMatrix* rotationMiddle = new G4RotationMatrix(*referenceRotationMiddle);
370 G4RotationMatrix* rotationEnd = new G4RotationMatrix(*referenceRotationEnd);
371 // add the tilt to the rotation matrices (around z axis)
372 if (hasFiniteTilt)
373 {
374 G4double tilt = tiltOffset->GetTilt();
375
376 // transform a unit z vector with the rotation matrices to get the local axes
377 // of rotation to apply the tilt.
378 G4ThreeVector unitZ = G4ThreeVector(0,0,1);
379 rotationStart ->rotate(tilt, unitZ.transform(*referenceRotationStart));
380 unitZ = G4ThreeVector(0,0,1);
381 rotationMiddle->rotate(tilt, unitZ.transform(*referenceRotationMiddle));
382 unitZ = G4ThreeVector(0,0,1);
383 rotationEnd ->rotate(tilt, unitZ.transform(*referenceRotationEnd));
384 }
385
386 // calculate the reference placement position
387 // if not the first item in the beamline, get the reference trajectory global position
388 // at the end of the previous element
389 if (!empty())
390 {
391 // if a transform has been applied, the previousReferencePositionEnd variable is already calculated
394 // leave a small gap for unambiguous geometry navigation. Transform that length
395 // to a unit z vector along the direction of the beam line before this component.
396 // increase it by sampler length if we're placing a sampler there.
397 G4ThreeVector pad = G4ThreeVector(0,0,paddingLength);
398 if (samplerInfo)
399 {
400 BDSSamplerType samplerType = samplerInfo->samplerType;
401 if (samplerType != BDSSamplerType::none)
402 {pad += G4ThreeVector(0,0,BDSSamplerPlane::ChordLength());}
403 }
404
405 // even if a transform has been applied that might induce a rotation, we introduce
406 // the padding length along the outgoing vector of the previous component to ensure
407 // the padding length is respected - hence we get the rotation from back() and not
408 // from the previousReferenceRotationEnd member variable
409 auto previousReferenceRotationEnd2 = back()->GetReferenceRotationEnd();
410 G4ThreeVector componentGap = pad.transform(*previousReferenceRotationEnd2);
411 previousReferencePositionEnd += componentGap;
412 }
413
414 G4ThreeVector referencePositionStart, referencePositionMiddle, referencePositionEnd;
415 if (hasFiniteLength)
416 {
417 referencePositionStart = previousReferencePositionEnd;
418
419 // calculate delta to mid point
420 G4ThreeVector md = G4ThreeVector(0, 0, 0.5 * chordLength);
421 md.transform(*referenceRotationMiddle);
422 referencePositionMiddle = referencePositionStart + md;
423 // remember the end position is the chord length along the half angle, not the full angle
424 // the particle achieves the full angle though by the end position.
425 G4ThreeVector delta = G4ThreeVector(0, 0, chordLength).transform(*referenceRotationMiddle);
426 referencePositionEnd = referencePositionStart + delta;
427 }
428 else
429 {
430 // element has no finite size so all positions are previous end position
431 // likely this is a transform3d or similar - but not hard coded just for transform3d
432 referencePositionStart = previousReferencePositionEnd;
433 referencePositionMiddle = previousReferencePositionEnd;
434 referencePositionEnd = previousReferencePositionEnd;
435 }
436
437 // add the placement offset
438 G4ThreeVector positionStart, positionMiddle, positionEnd;
439 if (hasFiniteOffset || hasFinitePlacementOffset)
440 {
441 if (hasFiniteOffset && hasFiniteAngle)
442 {// do not allow x offsets for bends as this will cause overlaps
443 G4String name = component->GetName();
444 G4String message = "element has x offset, but this will cause geometry overlaps: " + name
445 + " - omitting x offset";
446 BDS::Warning(__METHOD_NAME__, message);
447 offset.setX(0.0);
448 }
449 // note the displacement is applied in the accelerator x and y frame so use
450 // the reference rotation rather than the one with tilt already applied
451 G4ThreeVector total = offset + placementOffset;
452 G4ThreeVector displacement = total.transform(*referenceRotationMiddle);
453 positionStart = referencePositionStart + displacement;
454 positionMiddle = referencePositionMiddle + displacement;
455 positionEnd = referencePositionEnd + displacement;
456 }
457 else
458 {
459 positionStart = referencePositionStart;
460 positionMiddle = referencePositionMiddle;
461 positionEnd = referencePositionEnd;
462 }
463
464 // calculate the s position
465 // if not the first element in the beamline, get the s position at the end of the previous element
466 if (!empty())
468
469 // chord length set earlier
470 G4double arcLength = component->GetArcLength();
471
472 // integrate lengths
473 totalChordLength += chordLength;
474 totalArcLength += arcLength;
475
476 // advance s coordinate
477 G4double sPositionStart = previousSPositionEnd;
478 G4double sPositionMiddle = previousSPositionEnd + 0.5 * arcLength;
479 G4double sPositionEnd = previousSPositionEnd + arcLength;
480 sMaximum += arcLength;
481
482 // integrate angle
483 totalAngle += component->GetAngle();
484
485#ifdef BDSDEBUG
486 // feedback about calculated coordinates
487 G4cout << "calculated coordinates in mm and rad are " << G4endl;
488 G4cout << "reference position start: " << referencePositionStart << G4endl;
489 G4cout << "reference position middle: " << referencePositionMiddle << G4endl;
490 G4cout << "reference position end: " << referencePositionEnd << G4endl;
491 G4cout << "reference rotation start: " << *referenceRotationStart;
492 G4cout << "reference rotation middle: " << *referenceRotationMiddle;
493 G4cout << "reference rotation end: " << *referenceRotationEnd;
494 G4cout << "position start: " << positionStart << G4endl;
495 G4cout << "position middle: " << positionMiddle << G4endl;
496 G4cout << "position end: " << positionEnd << G4endl;
497 G4cout << "rotation start: " << *rotationStart;
498 G4cout << "rotation middle: " << *rotationMiddle;
499 G4cout << "rotation end: " << *rotationEnd;
500#endif
501
502 // construct beamline element
503 BDSTiltOffset* tiltOffsetToStore = nullptr;
504 if (tiltOffset)
505 {tiltOffsetToStore = new BDSTiltOffset(*tiltOffset);} // copy as can be used multiple times
506
507 G4double midT = 0;
508 G4double staP = 0;
509 G4double staEk = 0;
510 if (beamlineIntegral)
511 {
512 midT = beamlineIntegral->synchronousTAtMiddleOfLastElement;
513 staP = beamlineIntegral->MomentumAtStartOfLastElement();
514 staEk = beamlineIntegral->KineticEnergyAtStartOfLastElement();
515 }
516
517 BDSBeamlineElement* element;
518 element = new BDSBeamlineElement(component,
519 positionStart,
520 positionMiddle,
521 positionEnd,
522 rotationStart,
523 rotationMiddle,
524 rotationEnd,
525 referencePositionStart,
526 referencePositionMiddle,
527 referencePositionEnd,
528 referenceRotationStart,
529 referenceRotationMiddle,
530 referenceRotationEnd,
531 sPositionStart,
532 sPositionMiddle,
533 sPositionEnd,
534 midT,
535 staP,
536 staEk,
537 tiltOffsetToStore,
538 samplerInfo,
539 (G4int)beamline.size());
540
541 // calculate extents for world size determination
542 UpdateExtents(element);
543
544 // append it to the beam line
545 beamline.push_back(element);
546
547 // register the s position at the end for curvilinear transform
548 sEnd.push_back(sPositionEnd);
549
550 // register it by name
551 RegisterElement(element);
552
553 // reset flag for transform since we've now added a component
555}
556
558{
559 G4double dx = component->dx;
560 G4double dy = component->dy;
561 G4double dz = component->dz;
562
563 // test validity for potential overlaps
564 if (dz < 0)
565 {
566 G4cerr << __METHOD_NAME__ << "Caution: Transform3d: " << component->GetName() << G4endl;
567 G4cerr << __METHOD_NAME__ << "dz = " << dz << " < 0 -> could overlap previous element" << G4endl;
568 }
569
570 // if not the first element in the beamline, get information from
571 // the end of the last element in the beamline
573 {
574 BDSBeamlineElement* last = back();
577 }
578
579 // apply position
580 // transform the local dx,dy,dz displacement into the global frame then apply
581 G4ThreeVector delta = G4ThreeVector(dx, dy, dz).transform(*previousReferenceRotationEnd);
583
584 // apply rotation
585 G4RotationMatrix trRotInverse = component->rotationMatrix.inverse();
586 (*previousReferenceRotationEnd) *= trRotInverse;
587}
588
590{
591 if (!element)
592 {throw BDSException(__METHOD_NAME__, "invalid BDSBeamlineElement");}
593 if (!(element->GetAcceleratorComponent()))
594 {throw BDSException(__METHOD_NAME__, "invalid BDSAcceleratorComponent");}
595
596 // update world extent for this beam line
597 UpdateExtents(element);
598
599 // append it to the beam line
600 beamline.push_back(element);
601
602 // register it by name
603 RegisterElement(element);
604
605 // no need to update any internal variables - that's done by AddSingleComponent()
606}
607
609{
610 G4ThreeVector mEA;
611 for (int i=0; i<3; i++)
612 {mEA[i] = std::max(std::abs(maximumExtentPositive[i]), std::abs(maximumExtentNegative[i]));}
613 return mEA;
614}
615
616G4Transform3D BDSBeamline::GetGlobalEuclideanTransform(G4double s, G4double x, G4double y,
617 G4int* indexOfFoundElement) const
618{
619 // check if s is in the range of the beamline
620 G4double sStart = at(0)->GetSPositionStart();
621 if (s-sStart > totalArcLength) // need to offset start S position
622 {
623 G4String msg = "s position " + std::to_string(s/CLHEP::m) + " m is beyond length of accelerator (";
624 msg += std::to_string(totalArcLength/CLHEP::m) + " m)\nReturning identify transform";
625 BDS::Warning(__METHOD_NAME__, msg);
626 return G4Transform3D();
627 }
628
629 const auto element = GetElementFromGlobalS(s, indexOfFoundElement);
630
631#ifdef BDSDEBUG
632 G4cout << __METHOD_NAME__ << G4endl;
633 G4cout << "S position requested: " << s << G4endl;
634 G4cout << "Index: " << indexOfFoundElement << G4endl;
635 G4cout << "Element: " << *element << G4endl;
636#endif
637
638 G4double dx = 0;
639 // G4double dy = 0; // currently magnets can only bend in local x so avoid extra calculation
640
641 // difference from centre of element to point in local coords)
642 // difference in s from centre, normalised to arcLength and scaled to chordLength
643 // as s is really arc length, but we must place effectively in chord length coordinates
644 const BDSAcceleratorComponent* component = element->GetAcceleratorComponent();
645 G4double arcLength = component->GetArcLength();
646 G4double chordLength = component->GetChordLength();
647 G4double dS = s - element->GetSPositionMiddle();
648 G4double localZ = dS * (chordLength / arcLength);
649 G4double angle = component->GetAngle();
650 G4RotationMatrix rotation; // will be interpolated rotation
651 G4RotationMatrix* rotMiddle = element->GetReferenceRotationMiddle();
652 // find offset of point from centre of volume - 2 methods
653 if (BDS::IsFinite(angle))
654 {
655 // finite bend angle - interpolate position and angle along arc due to change in angle
656 // local unit z at start of element
657 G4ThreeVector localUnitY = G4ThreeVector(0,1,0);
658 localUnitY.transform(*(element->GetReferenceRotationStart()));
659 // linearly interpolate angle -> angle * (s from beginning into component)/arcLength
660 G4double partialAngle = angle * std::fabs(( (0.5*arcLength + dS) / arcLength));
661 rotation = G4RotationMatrix(*(element->GetReferenceRotationStart())); // start rotation
662 rotation.rotate(partialAngle, localUnitY); // rotate it by the partial angle about local Y
663 dx = localZ*tan(partialAngle); // calculate difference of arc from chord at that point
664 }
665 else
666 {rotation = G4RotationMatrix(*rotMiddle);}
667
668 // note, magnets only bend in local x so no need to add dy as always 0
669 G4ThreeVector dLocal = G4ThreeVector(x + dx, y /*+ dy*/, localZ);
670#ifdef BDSDEBUG
671 G4cout << "Local offset from middle: " << dLocal << G4endl;
672#endif
673 // note, rotation middle is also the same as the coordinate frame of the g4 solid
674 G4ThreeVector globalPos = element->GetReferencePositionMiddle() + dLocal.transform(*rotMiddle);
675 // construct transform3d from global position and rotation matrix
676 G4Transform3D result = G4Transform3D(rotation, globalPos);
677
678#ifdef BDSDEBUG
679 G4cout << "Global offset from middle: " << dLocal << G4endl;
680 G4cout << "Resultant global position: " << globalPos << G4endl;
681#endif
682 return result;
683}
684
686 G4int* indexOfFoundElement) const
687{
688 // find element that s position belongs to
689 auto lower = std::lower_bound(sEnd.begin(), sEnd.end(), S);
690 G4int index = G4int(lower - sEnd.begin()); // subtract iterators to get index
691 if (indexOfFoundElement)
692 {*indexOfFoundElement = index;}
693 return beamline.at(index);
694}
695
697{
698 auto lower = std::lower_bound(sEnd.begin(), sEnd.end(), S);
699 auto iter = begin();
700 std::advance(iter, std::distance(sEnd.begin(), lower));
701 return iter;
702}
703
705{
706 // search for element
707 auto result = find(beamline.begin(), beamline.end(), element);
708 if (result != beamline.end())
709 {// found
710 return GetPrevious(G4int(result - beamline.begin()));
711 }
712 else
713 {return nullptr;}
714}
715
716const BDSBeamlineElement* BDSBeamline::GetPrevious(G4int index) const
717{
718 if (index < 1 || index > (G4int)(beamline.size()-1))
719 {return nullptr;} // invalid index - inc beginning or end
720 else
721 {return beamline[index-1];}
722}
723
724const BDSBeamlineElement* BDSBeamline::GetNext(const BDSBeamlineElement* element) const
725{
726 // search for element
727 auto result = find(beamline.begin(), beamline.end(), element);
728 if (result != beamline.end())
729 {// found
730 return GetNext(G4int(result - beamline.begin()));
731 }
732 else
733 {return nullptr;}
734}
735
736const BDSBeamlineElement* BDSBeamline::GetNext(G4int index) const
737{
738 if (index < 0 || index > (G4int)(beamline.size()-2))
739 {return nullptr;} // invalid index - inc beginning or end
740 else
741 {return beamline[index+1];}
742}
743
745{
746 // check if base name already registered (can be single component placed multiple times)
747 const auto search = components.find(element->GetName());
748 if (search == components.end())
749 {// not registered
750 components[element->GetPlacementName()] = element;
751 }
752}
753
754const BDSBeamlineElement* BDSBeamline::GetElement(G4String acceleratorComponentName,
755 G4int i) const
756{
757 // build placement name based on acc component name and ith placement
758 // matches construction in BDSBeamlineElement
759 G4String suffix = "_" + std::to_string(i);
760 G4String placementName = acceleratorComponentName + suffix;
761 const auto search = components.find(placementName);
762 if (search == components.end())
763 {
764 // Try again but search including naming convention of uniquely built
765 // components. Sometimes we modify an element or build it uniquely for
766 // that position in the lattice, so we therefore add a suffix to the
767 // name for storing in the component registry.
768 // Naming will be NAME_MOD_MODNUMBER_PLACEMENTNUMBER
769 // Why not search registry? -> should be found from this beam line
770 // 1) search with starts with NAME
771 std::vector<const BDSBeamlineElement*> candidates;
772 std::for_each(this->begin(),
773 this->end(),
774 [&acceleratorComponentName,&candidates](const BDSBeamlineElement* el)
775 {if (BDS::StartsWith(el->GetPlacementName(), acceleratorComponentName)){candidates.push_back(el);};});
776
777 if (candidates.empty())
778 {return nullptr;} // nothing found
779 else
780 {// 2) of things that start with NAME, search for ones that end in _PLACEMENTNUMBER
781 auto foundItem = std::find_if(candidates.begin(),
782 candidates.end(),
783 [&suffix](const BDSBeamlineElement* el)
784 {return BDS::EndsWith(el->GetPlacementName(), suffix);});
785 return foundItem != candidates.end() ? *foundItem : nullptr;
786 }
787 }
788 else
789 {return search->second;}
790}
791
792G4Transform3D BDSBeamline::GetTransformForElement(const G4String& acceleratorComponentName,
793 G4int i) const
794{
795 const BDSBeamlineElement* result = GetElement(acceleratorComponentName, i);
796 if (!result)
797 {
798 G4cerr << __METHOD_NAME__ << "No element named \""
799 << acceleratorComponentName << "\" found for placement number "
800 << i << G4endl;
801 G4cout << "Note, this may be because the element is a bend and split into " << G4endl;
802 G4cout << "multiple sections with unique names." << G4endl;
803 throw BDSException(__METHOD_NAME__, "");
804 }
805 else
806 {return G4Transform3D(*(result->GetRotationMiddle()), result->GetPositionMiddle());}
807}
808
810{
811 // calculate extents for world size determination
812 // get the boundary points in global coordinates.
813 BDSExtentGlobal extG = element->GetExtentGlobal();
814 const auto boundaryPoints = extG.AllBoundaryPointsGlobal();
815
816 // expand maximums based on the boundary points.
817 for (const auto& point : boundaryPoints)
818 {
819 for (int i = 0; i < 3; ++i)
820 {
821 if (point[i] > maximumExtentPositive[i])
822 {maximumExtentPositive[i] = point[i];}
823 if (point[i] < maximumExtentNegative[i])
824 {maximumExtentNegative[i] = point[i];}
825 }
826 }
827#ifdef BDSDEBUG
828 G4cout << "new global extent +ve: " << maximumExtentPositive << G4endl;
829 G4cout << "new global extent -ve: " << maximumExtentNegative << G4endl;
830#endif
831}
832
833BDSBeamlineElement* BDSBeamline::ProvideEndPieceElementBefore(BDSSimpleComponent* endPiece,
834 G4int index) const
835{
836 if (!IndexOK(index))
837 {return nullptr;}
838
839 const G4double pl = BDSGlobalConstants::Instance()->LengthSafetyLarge(); // shortcut - 'padding length'
840 G4double endPieceLength = endPiece->GetChordLength();
841 BDSBeamlineElement* element = beamline[index];
842 G4RotationMatrix* elRotStart = element->GetRotationMiddle();
843 G4ThreeVector elPosStart = element->GetPositionStart() - G4ThreeVector(0,0,2*pl).transform(*elRotStart);
844 G4ThreeVector positionMiddle = elPosStart - G4ThreeVector(0,0,endPieceLength*0.5).transform(*elRotStart);
845 G4ThreeVector positionStart = elPosStart - G4ThreeVector(0,0,endPieceLength).transform(*elRotStart);
846 G4double elSPosStart = element->GetSPositionStart();
847 BDSTiltOffset* elTiltOffset = element->GetTiltOffset();
848 BDSTiltOffset* forEndPiece = nullptr;
849 if (elTiltOffset)
850 {forEndPiece = new BDSTiltOffset(*elTiltOffset);}
851 BDSBeamlineElement* result = new BDSBeamlineElement(endPiece,
852 positionStart,
853 positionMiddle,
854 elPosStart,
855 new G4RotationMatrix(*elRotStart),
856 new G4RotationMatrix(*elRotStart),
857 new G4RotationMatrix(*elRotStart),
858 positionStart,// for now the same - ie no tilt offset
859 positionMiddle,
860 elPosStart,
861 new G4RotationMatrix(*elRotStart),
862 new G4RotationMatrix(*elRotStart),
863 new G4RotationMatrix(*elRotStart),
864 elSPosStart - endPieceLength,
865 elSPosStart - 0.5*endPieceLength,
866 elSPosStart,
867 element->GetSynchronousTMiddle(),
868 element->GetStartMomentum(),
869 element->GetStartKineticEnergy(),
870 forEndPiece);
871 return result;
872}
873
875 G4int index,
876 G4bool flip) const
877{
878 if (!IndexOK(index))
879 {return nullptr;}
880
881 const G4double pl = paddingLength; // shortcut
882 G4double endPieceLength = endPiece->GetChordLength();
883 BDSBeamlineElement* element = beamline[index];
884 G4RotationMatrix* elRotEnd = new G4RotationMatrix(*(element->GetRotationMiddle()));
885 G4ThreeVector elPosEnd = element->GetPositionEnd() + G4ThreeVector(0,0,pl).transform(*elRotEnd);
886 G4ThreeVector positionMiddle = elPosEnd + G4ThreeVector(0,0,endPieceLength*0.5).transform(*elRotEnd);
887 G4ThreeVector positionEnd = elPosEnd + G4ThreeVector(0,0,endPieceLength).transform(*elRotEnd);
888 if (flip)
889 {// rotate about local unit Y direction
890 G4ThreeVector localUnitY = G4ThreeVector(0,1,0).transform(*elRotEnd);
891 elRotEnd->rotate(CLHEP::pi, localUnitY);
892 }
893 G4double elSPosEnd = element->GetSPositionEnd();
894 BDSTiltOffset* elTiltOffset = element->GetTiltOffset();
895 BDSTiltOffset* forEndPiece = nullptr;
896 if (elTiltOffset)
897 {forEndPiece = new BDSTiltOffset(*elTiltOffset);}
898 BDSBeamlineElement* result = new BDSBeamlineElement(endPiece,
899 elPosEnd,
900 positionMiddle,
901 positionEnd,
902 new G4RotationMatrix(*elRotEnd),
903 new G4RotationMatrix(*elRotEnd),
904 new G4RotationMatrix(*elRotEnd),
905 elPosEnd,
906 positionMiddle,
907 positionEnd,
908 new G4RotationMatrix(*elRotEnd),
909 new G4RotationMatrix(*elRotEnd),
910 new G4RotationMatrix(*elRotEnd),
911 elSPosEnd,
912 elSPosEnd + 0.5*endPieceLength,
913 elSPosEnd + endPieceLength,
914 element->GetSynchronousTMiddle(),
915 element->GetStartMomentum(),
916 element->GetStartKineticEnergy(),
917 forEndPiece);
918 delete elRotEnd;
919 return result;
920}
921
922G4bool BDSBeamline::IndexOK(G4int index) const
923{
924 if (index < 0 || index > (G4int)(beamline.size()-1))
925 {return false;}
926 else
927 {return true;}
928}
929
930std::vector<G4double> BDSBeamline::GetEdgeSPositions()const
931{
932 std::vector<G4double> sPos;
933 sPos.reserve(beamline.size()+1);
934 // add start position
935 sPos.push_back(GetSMinimum()/CLHEP::m);
936 for (auto element : beamline)
937 {sPos.push_back(element->GetSPositionEnd()/CLHEP::m);}
938 if (sPos.size() == 1)
939 {sPos.push_back(1*CLHEP::m);}
940 return sPos;
941}
942
944{
945 return (std::abs(totalAngle) > 0.99 * 2.0 * CLHEP::pi) and (std::abs(totalAngle) < 1.01 * 2.0 * CLHEP::pi);
946}
947
949{
951 BDSExtentGlobal extG = BDSExtentGlobal(ext, G4Transform3D());
952 return extG;
953}
954
955std::vector<G4int> BDSBeamline::GetIndicesOfElementsOfType(const G4String& type) const
956{
957 std::vector<G4int> result;
958 for (auto element : beamline)
959 {
960 if (element->GetType() == type)
961 {result.push_back(element->GetIndex());}
962 }
963 return result;
964}
965
966std::vector<G4int> BDSBeamline::GetIndicesOfElementsOfType(const std::set<G4String>& types) const
967{
968 std::vector<G4int> result;
969 for (auto element : beamline)
970 {
971 G4String type = element->GetType();
972 if (types.find(type) != types.end())
973 {result.push_back(element->GetIndex());}
974 }
975 return result;
976}
977
978std::vector<G4int> BDSBeamline::GetIndicesOfCollimators() const
979{
980 std::set<G4String> collimatorTypes = {"ecol", "rcol", "jcol", "jcoltip", "crystalcol", "element-collimator"};
981 return GetIndicesOfElementsOfType(collimatorTypes);
982}
Abstract class that represents a component of an accelerator.
virtual G4String GetName() const
The name of the component without modification.
virtual G4double GetChordLength() const
G4ThreeVector OutputFaceNormal() const
virtual G4double GetArcLength() const
G4ThreeVector InputFaceNormal() const
G4String GetType() const
Get a string describing the type of the component.
A class that holds a fully constructed BDSAcceleratorComponent as well as any information relevant to...
G4double GetChordLength() const
Accessor.
G4double GetSynchronousTMiddle() const
Accessor.
G4ThreeVector GetPositionMiddle() const
Accessor.
G4double GetStartMomentum() const
Accessor.
G4ThreeVector GetReferencePositionEnd() const
Accessor.
G4ThreeVector GetPositionEnd() const
Accessor.
G4RotationMatrix * GetReferenceRotationStart() const
Accessor.
G4ThreeVector GetPositionStart() const
Accessor.
G4String GetPlacementName() const
Accessor.
BDSTiltOffset * GetTiltOffset() const
Accessor.
void UpdateSamplerPlacementTransform(const G4Transform3D &tranfsormIn)
G4RotationMatrix * GetReferenceRotationEnd() const
Accessor.
G4String GetName() const
Accessor.
BDSSamplerInfo * GetSamplerInfo() const
Accessor.
G4int GetIndex() const
Accessor.
G4double GetSPositionEnd() const
Accessor.
G4double GetStartKineticEnergy() const
Accessor.
G4ThreeVector GetReferencePositionStart() const
Accessor.
BDSAcceleratorComponent * GetAcceleratorComponent() const
Accessor.
G4RotationMatrix * GetRotationMiddle() const
Accessor.
G4String GetType() const
Accessor.
BDSExtentGlobal GetExtentGlobal() const
Create a global extent object from the extent of the component.
G4double GetSPositionStart() const
Accessor.
A class that holds the current integrated quantities along a beam line.
A vector of BDSBeamlineElement instances - a beamline.
G4double totalChordLength
Sum of all chord lengths.
std::vector< G4double > GetEdgeSPositions() const
G4ThreeVector previousReferencePositionEnd
Current reference position at the end of the previous element.
const BDSBeamlineElement * GetPrevious(const BDSBeamlineElement *element) const
void PrintAllComponents(std::ostream &out) const
const BDSBeamlineElement * GetElement(G4String acceleratorComponentName, G4int i=0) const
Get the ith placement of an element in the beam line. Returns null pointer if not found.
BDSBeamlineElement * back() const
Return a reference to the last element.
G4ThreeVector maximumExtentPositive
maximum extent in the positive coordinates in each dimension
G4double previousSPositionEnd
Current s coordinate at the end of the previous element.
G4bool ElementAnglesSumToCircle() const
Check if the sum of the angle of all elements is two pi.
G4double totalAngle
Sum of all angles.
BDSBeamlineElement * ProvideEndPieceElementAfter(BDSSimpleComponent *endPiece, G4int index, G4bool flip=false) const
G4RotationMatrix * previousReferenceRotationEnd
Current reference rotation at the end of the previous element.
G4bool transformHasJustBeenApplied
void RegisterElement(BDSBeamlineElement *element)
void UpdateExtents(BDSBeamlineElement *element)
G4ThreeVector GetMaximumExtentAbsolute() const
Get the maximum extent absolute in each dimension.
void PrintMemoryConsumption() const
void ApplyTransform3D(BDSTransform3D *component)
static G4double paddingLength
The gap added for padding between each component.
const BDSBeamlineElement * at(int iElement) const
Return a reference to the element at i.
G4bool IndexOK(G4int index) const
Whether the supplied index will lie within the beam line vector.
BeamlineVector::size_type size() const
Get the number of elements.
void AddComponent(BDSAcceleratorComponent *component, BDSTiltOffset *tiltOffset=nullptr, BDSSamplerInfo *samplerInfo=nullptr, const BDSBeamlineIntegral *beamlineIntegral=nullptr)
void AddBeamlineElement(BDSBeamlineElement *element)
G4Transform3D GetTransformForElement(const G4String &acceleratorComponentName, G4int i=0) const
const_iterator FindFromS(G4double S) const
Returns an iterator to the beamline element at s.
G4double totalArcLength
Sum of all arc lengths.
BeamlineVector beamline
Vector of beam line elements - the data.
const BDSBeamlineElement * GetElementFromGlobalS(G4double S, G4int *indexOfFoundElement=nullptr) const
Return the element in this beam line according to a given s coordinate.
G4bool empty() const
Iterator mechanics.
G4Transform3D GetGlobalEuclideanTransform(G4double s, G4double x=0, G4double y=0, G4int *indexOfFoundElement=nullptr) const
BDSExtentGlobal GetExtentGlobal() const
Get the global extents for this beamline.
iterator begin()
Iterator mechanics.
void AddSingleComponent(BDSAcceleratorComponent *component, BDSTiltOffset *tiltOffset=nullptr, BDSSamplerInfo *samplerInfo=nullptr, const BDSBeamlineIntegral *beamlineIntegral=nullptr)
std::map< G4String, BDSBeamlineElement * > components
Map of objects by placement name stored in this beam line.
iterator end()
Iterator mechanics.
std::vector< G4int > GetIndicesOfElementsOfType(const G4String &type) const
Return vector of indices for this beam line where element of type name 'type' is found.
std::vector< G4double > sEnd
BeamlineVector::const_iterator const_iterator
Iterator mechanics.
std::vector< G4int > GetIndicesOfCollimators() const
Return indices in order of ecol, rcol, jcol and crystalcol elements.
G4ThreeVector maximumExtentNegative
maximum extent in the negative coordinates in each dimension
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions with a rotation and translation.
std::vector< G4ThreeVector > AllBoundaryPointsGlobal() const
All 8 boundary points of the bounding box.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
BDSExtent GetExtent() const
Accessor - see member for more info.
G4ThreeVector GetExtentPositive() const
Get the extent of the object in the positive direction in all dimensions.
G4ThreeVector GetExtentNegative() const
Get the extent of the object in the negative direction in all dimensions.
G4ThreeVector GetPlacementOffset() const
Accessor - see member for more info.
static BDSGlobalConstants * Instance()
Access method.
A class that hold multiple accelerator components.
Definition BDSLine.hh:37
static void PrintProtectedNames(std::ostream &out)
Feedback for protected names.
Definition BDSOutput.cc:399
static G4bool InvalidSamplerName(const G4String &samplerName)
Test whether a sampler name is invalid or not.
Definition BDSOutput.cc:394
All info required to build a sampler but not place it.
static G4double ChordLength()
Access the sampler plane length in other classes.
A BDSAcceleratorComponent wrapper for BDSGeometryComponent.
A holder for any placement offsets and rotations for a BDSAcceleratorComponent.
G4bool HasFiniteTilt() const
Inspector.
G4double GetTilt() const
Accessor.
G4bool HasFiniteOffset() const
Inspector.
G4ThreeVector GetOffset() const
More advance accessor for offset - only in x,y.
Transform in beam line that takes up no space.
G4bool WillIntersect(const G4ThreeVector &incomingNormal, const G4ThreeVector &outgoingNormal, const G4double &zSeparation, const BDSExtent &incomingExtent, const BDSExtent &outgoingExtent)
G4bool StartsWith(const std::string &expression, const std::string &prefix)
Return true if a string "expression" starts with "prefix".
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())