BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSTunnelBuilder.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 "BDSTunnelBuilder.hh"
19
20#include "BDSBeamline.hh"
21#include "BDSBeamlineElement.hh"
22#include "BDSDebug.hh"
23#include "BDSException.hh"
24#include "BDSGlobalConstants.hh"
25#include "BDSTiltOffset.hh"
26#include "BDSTunnelFactory.hh"
27#include "BDSTunnelInfo.hh"
28#include "BDSTunnelSection.hh"
29#include "BDSUtilities.hh"
30
31#include "globals.hh"
32
33#include "CLHEP/Units/SystemOfUnits.h"
34
35#include <cmath>
36
37BDSTunnelBuilder::BDSTunnelBuilder(G4double tunnelMaxSegmentLengthIn):
38 displacementTolerance(50*CLHEP::cm), // maximum displacement of beamline before split
39 maxItems(50), // maximum number of items before split
40 maxLength(tunnelMaxSegmentLengthIn), // maximum length of tunnel segment
41 maxAngle(100*CLHEP::mrad), // maximum angle before split
42 minLength(10*CLHEP::m) // minimum length
43{
44 if (maxLength < 1*CLHEP::m)
45 {throw BDSException(__METHOD_NAME__, "\"tunnelMaxSegmentLength\" must be >= 1m");}
46 if (maxLength <= minLength)
47 {minLength = 0.5*maxLength;}
48}
49
50BDSTunnelBuilder::~BDSTunnelBuilder()
51{
52 try // we should not throw an exception in a destructor
54 catch (...)
55 {;}
56}
57
60 const G4double& halfWidth) const
61{
62 G4double sectionLength = 0;
63 G4double cumulativeAngle = 0;
64 G4int nItems = 1;
65 G4double cumulativeX = 0;
66
67 sectionLength = ((*proposedEnd)->GetReferencePositionEnd() - (*proposedStart)->GetReferencePositionStart()).mag();
68 BDSBeamline::const_iterator it = proposedStart;
69 for (; it != proposedEnd; ++it)
70 {
71 cumulativeAngle += (*it)->GetAngle();
72 cumulativeX += std::cos((*it)->GetTilt()) * std::sin((*it)->GetAngle());
73 nItems++;
74 }
75
76 G4bool result = false;
77
78 G4bool lengthTest = false;
79 if (sectionLength > maxLength)
80 {lengthTest = true;}
81
82 G4bool angleTest = false;
83 if (std::abs(cumulativeAngle) > maxAngle)
84 {angleTest = true;}
85
86 G4bool nItemsTest = false;
87 if (nItems > maxItems)
88 {nItemsTest = true;}
89
90 G4bool dispXTest = false;
91 if (cumulativeX > displacementTolerance)
92 {dispXTest = true;}
93
94 result = lengthTest || angleTest || nItemsTest || dispXTest;
95
96 // ensure that it's not too sharp a turn
97 G4bool lengthTestFail = sectionLength < minLength;
98 G4bool angleIsFinite = BDS::IsFinite(cumulativeAngle);
99
100 if (lengthTestFail && result && angleIsFinite)
101 {// only in the case of bent items
102 // result is positive -> split tunnel, but too short - continue to increase length
103 result = false;
104 }
105
106 // test if faces on either side of a cylindrical tunnel would touch - ie unphysical
107 // needs to be longer, so keep going by forcing 'result' to false.
108 G4double s = 1.5*halfWidth * std::abs(cumulativeAngle);
109 G4bool willOverlapFaces = sectionLength < 1.2*s;
110 if (willOverlapFaces)
111 {result = false;}
112
113#ifdef BDSDEBUG
114 G4cout << __METHOD_NAME__ << "testing cumulative parameters" << G4endl;
115 G4cout << "Cumulative Length (mm): " << sectionLength << " > " << maxLength << " test-> " << BDS::BoolToString(lengthTest) << G4endl;
116 G4cout << "Cumulative Angle (rad): " << cumulativeAngle << " > " << maxAngle << " test-> " << BDS::BoolToString(angleTest) << G4endl;
117 G4cout << "# of items: " << nItems << " > " << maxItems << " test-> " << BDS::BoolToString(nItemsTest) << G4endl;
118 G4cout << "Cumulative displacement X: " << cumulativeX << " > " << displacementTolerance
119 << " test-> " << BDS::BoolToString(dispXTest) << G4endl;
120 G4cout << "Length: " << sectionLength << " < " << minLength << " test-> " << BDS::BoolToString(lengthTestFail) << G4endl;
121 G4cout << "Overlap Faces: " << s << " < " << sectionLength << " test-> " << BDS::BoolToString(willOverlapFaces) << G4endl;
122 G4cout << "Result: " << BDS::BoolToString(result) << G4endl;
123#endif
124 return result;
125}
126
128{
129 BDSTunnelInfo* defaultModel = BDSGlobalConstants::Instance()->TunnelInfo();
130 G4double offsetX = BDSGlobalConstants::Instance()->TunnelOffsetX();
131 G4double offsetY = BDSGlobalConstants::Instance()->TunnelOffsetY();
132
133 BDSBeamline* tunnelLine = new BDSBeamline();
134
135 // temporary variables to use as we go along
136 G4int nTunnelSections = 0;
137 BDSTunnelSection* tunnelSection = nullptr;
139
140 // iterator to the BDSBeamlineElement where the previous tunnel section finished
141#ifdef BDSDEBUG
142 BDSBeamline::const_iterator previousEndElement = flatBeamline->begin();
143#endif
144
145 // iterator to the BDSBeamlineElement where the current tunnel section will begin
146 BDSBeamline::const_iterator startElement = flatBeamline->begin();
147
148 // iterator to the BDSBeamlineElement where the current tunnel section will end
149 BDSBeamline::const_iterator endElement = flatBeamline->begin();
150
151 // if a straight tunnel, just build one long segment, add it to beam line and return
152 if (BDSGlobalConstants::Instance()->BuildTunnelStraight())
153 {
154 BDSBeamlineElement* lastElement = flatBeamline->back();
155 G4double segmentLength = lastElement->GetReferencePositionEnd().z();
156 tunnelSection = tf->CreateTunnelSection(defaultModel->type, // type
157 "tunnel", // name
158 segmentLength, // length
159 defaultModel->thickness, // thickness
160 defaultModel->soilThickness, // soil thickness
161 defaultModel->material, // material
162 defaultModel->soilMaterial, // soil material
163 defaultModel->buildFloor, // build floor?
164 defaultModel->floorOffset, // floor offset
165 defaultModel->aper1, // 1st aperture param
166 defaultModel->aper2, // 2nd aperture param
167 defaultModel->visible); // is it visible
168 BDSTiltOffset* tos = new BDSTiltOffset(offsetX,offsetY,0);
169 tunnelLine->AddComponent(tunnelSection,tos);
170 return tunnelLine;
171 }
172
173 BDSBeamline::const_iterator it = flatBeamline->begin();
174
175 G4double halfWidth = defaultModel->IndicativeExtent().MaximumAbsTransverse();
176 // iterate over beam line and build tunnel segments
177 for (; it != flatBeamline->end(); ++it)
178 {
179#ifdef BDSDEBUG
180 G4cout << __METHOD_NAME__ << "iterator at: " << (*it)->GetPlacementName() << G4endl;
181 G4cout << __METHOD_NAME__ << "start iterator at: " << (*startElement)->GetPlacementName() << G4endl;
182 G4cout << __METHOD_NAME__ << "end iterator at: " << (*endElement)->GetPlacementName() << G4endl;
183#endif
184 G4bool breakIt = BreakTunnel(startElement, endElement, halfWidth);
185
186 G4bool isEnd = (it == (flatBeamline->end() - 1));
187 // remember end points to just after the last element, not the last element itself
188
189 if (isEnd)
190 {
191 endElement = it;
192#ifdef BDSDEBUG
193 G4cout << "End of beam line - forcing break in tunnel" << G4endl;
194 G4cout << "End element for tunnel set to: " << (*endElement)->GetPlacementName() << G4endl;
195 G4cout << "Start element is: " << (*startElement)->GetPlacementName() << G4endl;
196#endif
197 }
198
199 // it if matches any of the conditions, break the tunnel here (BEFORE) the item
200 // pointed to by (*it)
201 if (breakIt || isEnd)
202 {
203 // work out tunnel parameters
204 std::string name = "tunnel_" + std::to_string(nTunnelSections);
205
206 // calculate start central point of tunnel
207 G4ThreeVector startPoint = (*startElement)->GetReferencePositionStart();
208 G4ThreeVector startOffsetLocal = G4ThreeVector(offsetX, offsetY, 0);
209 // create a copy of the rotation matrix for this object
210 G4RotationMatrix* startRot = new G4RotationMatrix(*(*startElement)->GetReferenceRotationStart());
211 G4ThreeVector startOffsetGlobal = startOffsetLocal.transform(*startRot);
212 startPoint += startOffsetGlobal;
213#ifdef BDSDEBUG
214 BDS::PrintRotationMatrix(startRot, "rotation at beginning of starting element");
215#endif
216
217 // calculate end central point of tunnel
218 G4ThreeVector endPoint = (*endElement)->GetReferencePositionEnd();
219 G4ThreeVector endOffsetLocal = G4ThreeVector(offsetX, offsetY, 0);
220 G4RotationMatrix* endRot = new G4RotationMatrix(*(*endElement)->GetReferenceRotationEnd());
221 G4ThreeVector endOffsetGlobal = endOffsetLocal.transform(*endRot);
222 endPoint += endOffsetGlobal;
223
224 G4double sStart = (*startElement)->GetSPositionStart();
225 G4double sEnd = (*endElement)->GetSPositionEnd();
226 G4double sMid = 0.5*(sStart + sEnd);
227
228 // calculate mid point of the tunnel - placement position in global coords
229 // midPoint = startPoint + (endPoint - startPoint)*0.5 // reducing this becomes
230 G4ThreeVector midPoint = 0.5*(startPoint + endPoint);
231
232 // calculate mid point rotation
233 // mid point is calculated in 3d from vector points at either end. The faces
234 // are also picked up from the elements. To get the rotation, calculate the
235 // unit vectors at the mid point, which can be used to construct a rotation matrix.
236 // This starts from the unit vectors at the start of the starting element.
237 G4ThreeVector delta = midPoint - startPoint;
238 G4ThreeVector newUnitZ = delta.unit();
239 // get unit y by calculating x unit (starting element) cross direction of mid point (unit)
240 G4ThreeVector unitXPrevious = G4ThreeVector(1,0,0).transform(*startRot);
241 G4ThreeVector newUnitY = newUnitZ.cross(unitXPrevious).unit();
242 // get unit x by calculating y unit (starting element) cross direction of mid point (unit)
243 G4ThreeVector unitYPrevious = G4ThreeVector(0,1,0).transform(*startRot);
244 G4ThreeVector newUnitX = unitYPrevious.cross(newUnitZ).unit();
245
246 // create mid point rotation matrix from unit vectors at mid point
247 G4RotationMatrix* rotationMiddle = new G4RotationMatrix(newUnitX, newUnitY, newUnitZ);
248
249 // calculate length
250 G4double segmentLength = (endPoint - startPoint).mag() - 1*CLHEP::um; // -1um purely for safety purposes
251
252 // test if angled by comparing unit z vector at beginning and end
253 // this works in 3d.
254 G4ThreeVector unitZStart = G4ThreeVector(0,0,1).transform(*startRot);
255 G4ThreeVector unitZEnd = G4ThreeVector(0,0,1).transform(*endRot);
256 // TODO - this could be done with the cross product in future.
257 G4ThreeVector unitZDiff = unitZEnd - unitZStart;
258 G4bool isAngled = unitZDiff.mag() > 1e-30;
259
260#ifdef BDSDEBUG
261 G4cout << __METHOD_NAME__ << "determined tunnel segment to have the following parameters:" << G4endl;
262 G4cout << "Start element name: " << (*startElement)->GetPlacementName() << G4endl;
263 G4cout << "End element name: " << (*endElement)->GetPlacementName() << G4endl;
264 G4cout << "Start point (global): " << startPoint << G4endl;
265 G4cout << "End point (global): " << endPoint << G4endl;
266 G4cout << "Input and output unit Z: " << unitZStart << " " << unitZEnd << G4endl;
267 G4cout << "Has a finite angle: " << isAngled << G4endl;
268 G4cout << "Section length: " << segmentLength << G4endl;
269 G4cout << "Rotation start: " << *startRot << G4endl;
270 G4cout << "Rotation middle: " << *rotationMiddle << G4endl;
271 G4cout << "Rotation end: " << *endRot << G4endl;
272#endif
273
274 // create tunnel segment
275 if (isAngled)
276 { // use the angled faces
277 // make unit vectors for each face of the angled solid if required
278 // We need the rotation matrix for the input face in the frame of the tunnel
279 // segment. This really the difference between the incoming rotation matrix
280 // and the rotation matrix of the tunnel segment (middle). To get the difference
281 // we multiply the incoming by the inverse of the middle.
282 // We can then use this rotation matrix to transform a -ve z unit vector for the
283 // input face and a +ve z unit vector for the output face.
284 // The benefit is that this works in 3D and does not rely on the (cumulative)
285 // angle being averaged between the two faces - so each face can have a totally different
286 // angle as required by the tunnel builder.
287 G4RotationMatrix* middleInverse = new G4RotationMatrix(*rotationMiddle);
288 middleInverse->invert(); // now it's the inverse
289 G4RotationMatrix* inputFaceRotation = new G4RotationMatrix((*startRot) * (*middleInverse));
290 G4ThreeVector inputFace = G4ThreeVector(0,0,-1).transform(*inputFaceRotation);
291 G4RotationMatrix* outputFaceRotation = new G4RotationMatrix((*endRot) * (*middleInverse));
292 G4ThreeVector outputFace = G4ThreeVector(0,0,1).transform(*outputFaceRotation);
293
294#ifdef BDSDEBUG
295 BDS::PrintRotationMatrix(middleInverse,"middle inverse");
296 BDS::PrintRotationMatrix(inputFaceRotation, "result");
297 BDS::PrintRotationMatrix(outputFaceRotation, "result end ");
298 G4cout << "tunnel segment input face normal determined to be: " << inputFace << G4endl;
299 G4cout << "tunnel segment output face normal determined to be: " << outputFace << G4endl;
300#endif
301
302 tunnelSection = tf->CreateTunnelSectionAngled(defaultModel->type, // type
303 name, // name
304 segmentLength, // length
305 inputFace, // input face normal
306 outputFace, // output face normal
307 defaultModel->thickness, // thickness
308 defaultModel->soilThickness, // soil thickness
309 defaultModel->material, // material
310 defaultModel->soilMaterial, // soil material
311 defaultModel->buildFloor, // build floor?
312 defaultModel->floorOffset, // floor offset
313 defaultModel->aper1, // 1st aperture param
314 defaultModel->aper2, // 2nd aperture param
315 defaultModel->visible); // is it visible
316 delete middleInverse;
317 }
318 else
319 { // straight section
320 tunnelSection = tf->CreateTunnelSection(defaultModel->type, // type
321 name, // name
322 segmentLength, // length
323 defaultModel->thickness, // thickness
324 defaultModel->soilThickness, // soil thickness
325 defaultModel->material, // material
326 defaultModel->soilMaterial, // soil material
327 defaultModel->buildFloor, // build floor?
328 defaultModel->floorOffset, // floor offset
329 defaultModel->aper1, // 1st aperture param
330 defaultModel->aper2, // 2nd aperture param
331 defaultModel->visible); // is it visible
332 }
333
334 // bake the tunnel segment into a BDSBeamline element with position information
335 // create copies of rotation matrices (wasteful I know) as they're independently deleted
336 // by BDSBeamlineElement and can't double delete
337 G4RotationMatrix* startRot2 = new G4RotationMatrix(*startRot);
338 G4RotationMatrix* midRot2 = new G4RotationMatrix(*rotationMiddle);
339 G4RotationMatrix* endRot2 = new G4RotationMatrix(*endRot);
340 BDSBeamlineElement* tunnelElement = new BDSBeamlineElement(tunnelSection, // accelerator component
341 startPoint, // positionStart
342 midPoint, // positionMiddle
343 endPoint, // positionEnd
344 startRot, // rotationStart
345 rotationMiddle, // rotationMiddle
346 endRot, // rotationEnd
347 startPoint, // referencePositionStart
348 midPoint, // referencePositionMiddle
349 endPoint, // referencePositionEnd
350 startRot2, // referenceRotationStart
351 midRot2, // referenceRotationMiddle
352 endRot2, // referenceRotationEnd
353 sStart, // sPositionStart
354 sMid, // sPositionMiddle
355 sEnd, // sPositionEnd
356 0.0);
357
358 // store segment in tunnel beam line
359 tunnelLine->AddBeamlineElement(tunnelElement);
360 nTunnelSections += 1;
361
362 if (!isEnd)
363 {
364#ifdef BDSDEBUG
365 G4cout << __METHOD_NAME__ << "previous tunnel start element: " << (*startElement)->GetPlacementName() << G4endl;
366 G4cout << __METHOD_NAME__ << "previous tunnel end element: " << (*endElement)->GetPlacementName() << G4endl;
367 previousEndElement = endElement; // (copy endElement) mark the end of this element as the previous end
368#endif
369 startElement = endElement; // copy end element iterator
370 ++startElement; // next segment will begin where this one finishes
371 endElement = startElement;
372#ifdef BDSDEBUG
373 G4cout << __METHOD_NAME__ << "new start element: " << (*startElement)->GetPlacementName() << G4endl;
374 G4cout << __METHOD_NAME__ << "new tunnel start element: " << (*startElement)->GetPlacementName() << G4endl;
375 G4cout << __METHOD_NAME__ << "new tunnel previous end element: " << (*previousEndElement)->GetPlacementName() << G4endl;
376#endif
377 }
378 } // end of scope of if (break section)
379 else
380 {
381 // else: don't break the tunnel here, move on to next element
382#ifdef BDSDEBUG
383 G4cout << __METHOD_NAME__ << "moving to next item in beamline" << G4endl;
384#endif
385 ++endElement; // advance the potential end element iterator
386 }
387 } // for loop scope end
388 return tunnelLine;
389}
A class that holds a fully constructed BDSAcceleratorComponent as well as any information relevant to...
G4ThreeVector GetReferencePositionEnd() const
Accessor.
A vector of BDSBeamlineElement instances - a beamline.
BDSBeamlineElement * back() const
Return a reference to the last element.
void AddComponent(BDSAcceleratorComponent *component, BDSTiltOffset *tiltOffset=nullptr, BDSSamplerInfo *samplerInfo=nullptr, const BDSBeamlineIntegral *beamlineIntegral=nullptr)
void AddBeamlineElement(BDSBeamlineElement *element)
iterator begin()
Iterator mechanics.
iterator end()
Iterator mechanics.
BeamlineVector::const_iterator const_iterator
Iterator mechanics.
General exception with possible name of object and message.
G4double MaximumAbsTransverse() const
Return the maximum absolute value considering only x,y.
Definition BDSExtent.cc:170
static BDSGlobalConstants * Instance()
Access method.
A holder for any placement offsets and rotations for a BDSAcceleratorComponent.
BDSBeamline * BuildTunnelSections(const BDSBeamline *flatBeamLine) const
G4double maxAngle
Maximum angle before split.
G4double maxItems
Maximum number of items before split.
G4bool BreakTunnel(BDSBeamline::const_iterator proposedStart, BDSBeamline::const_iterator proposedEnd, const G4double &halfWidth) const
G4double minLength
Minimum length to angle ratio to allow a split.
G4double maxLength
Maximum length before split.
G4double displacementTolerance
A singleton class that provides an interface to all tunnel factories.
BDSTunnelSection * CreateTunnelSectionAngled(BDSTunnelType tunnelType, G4String name, G4double length, G4ThreeVector inputFaceIn, G4ThreeVector outputFaceIn, G4double tunnelThickness, G4double tunnelSoilThickness, G4Material *tunnelMaterial, G4Material *tunnelSoilMaterial, G4bool tunnelFloor, G4double tunnelFloorOffset, G4double tunnel1, G4double tunnel2, G4bool visible=true)
BDSTunnelSection * CreateTunnelSection(BDSTunnelType tunnelType, G4String name, G4double length, G4double tunnelThickness, G4double tunnelSoilThickness, G4Material *tunnelMaterial, G4Material *tunnelSoilMaterial, G4bool tunnelFloor, G4double tunnelFloorOffset, G4double tunnel1, G4double tunnel2, G4bool visible=true)
Create a tunnel section with straight input and output face.
static BDSTunnelFactory * Instance()
singleton pattern
Holder struct of all information required to create a section of tunnel.
G4bool visible
Is the tunnel visible?
G4double aper2
Tunnel aperture / shape parameter 2.
G4double aper1
Tunnel aperture / shape parameter 1.
Class that represents a section of tunnel.
void PrintRotationMatrix(G4RotationMatrix *rm, G4String keyName="unknown")
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())