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