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