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