BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSCollimatorJaw.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 "BDSAcceleratorModel.hh"
19#include "BDSCollimatorJaw.hh"
20#include "BDSBeamPipeInfo.hh"
21#include "BDSColours.hh"
22#include "BDSDebug.hh"
23#include "BDSException.hh"
24#include "BDSMaterials.hh"
25#include "BDSSDType.hh"
26#include "BDSUtilities.hh"
27
28#include "G4Box.hh"
29#include "G4Para.hh"
30#include "G4GenericTrap.hh"
31#include "G4LogicalVolume.hh"
32#include "G4PVPlacement.hh"
33#include "G4VisAttributes.hh"
34
35#include <cmath>
36#include <vector>
37#include <set>
38
39BDSCollimatorJaw::BDSCollimatorJaw(const G4String& nameIn,
40 G4double lengthIn,
41 G4double horizontalWidthIn,
42 G4double xHalfGapIn,
43 G4double yHalfHeightIn,
44 G4double xSizeLeftIn,
45 G4double xSizeRightIn,
46 G4double leftJawTiltIn,
47 G4double rightJawTiltIn,
48 G4bool buildLeftJawIn,
49 G4bool buildRightJawIn,
50 G4Material* collimatorMaterialIn,
51 G4Material* vacuumMaterialIn,
52 G4Colour* colourIn,
53 const G4String& objectType):
54BDSCollimator(nameIn, lengthIn, horizontalWidthIn, objectType, collimatorMaterialIn, vacuumMaterialIn,
55 xHalfGapIn, yHalfHeightIn, xHalfGapIn, yHalfHeightIn, colourIn),
56 xSizeLeft(xSizeLeftIn),
57 xSizeRight(xSizeRightIn),
58 xHalfGap(xHalfGapIn),
59 jawTiltLeft(leftJawTiltIn),
60 jawTiltRight(rightJawTiltIn),
61 yHalfHeight(yHalfHeightIn),
62 buildLeftJaw(buildLeftJawIn),
63 buildRightJaw(buildRightJawIn),
64 buildAperture(true),
65 leftJawHalfGap(0),
66 rightJawHalfGap(0),
67 leftJawWidth(0),
68 rightJawWidth(0),
69 vacuumWidth(0),
70 collimatorLV(nullptr)
71{
72 if (!BDS::IsFinite(xHalfGap) && !BDS::IsFinite(xSizeLeft) && !BDS::IsFinite(xSizeRight))
73 {buildAperture = false;}
74
75 if (!colour)
76 {colour = BDSColours::Instance()->GetColour("collimator");}
77
78 if (std::abs(xSizeLeft) > 0.5*horizontalWidth)
79 {
80 G4cerr << __METHOD_NAME__ << "jcol \"" << name
81 << "\" left jaw offset is greater the element half width, jaw "
82 << "will not be constructed" << G4endl;
83 buildLeftJaw = false;
84 }
85 if (std::abs(xSizeRight) > 0.5*horizontalWidth)
86 {
87 G4cerr << __METHOD_NAME__ << "jcol \"" << name
88 << "\" right jaw offset is greater the element half width, jaw "
89 << "will not be constructed" << G4endl;
90 buildRightJaw = false;
91 }
92
93 // set half height to half horizontal width if zero - finite height required.
94 if (!BDS::IsFinite(yHalfHeight))
95 {yHalfHeight = 0.5*horizontalWidth;}
96
97 Calculations();
98}
99
100BDSCollimatorJaw::~BDSCollimatorJaw()
101{;}
102
104{
105 // set each jaws half gap default to aperture half size
106 leftJawHalfGap = xHalfGap;
107 rightJawHalfGap = xHalfGap;
108
109 // update jaw half gap with offsets
110 // if one jaw is not constructed, set the opening to xSize/2 for the aperture vacuum volume creation
112 {leftJawHalfGap = buildLeftJaw ? xSizeLeft : 0.5 * horizontalWidth;}
114 {rightJawHalfGap = buildRightJaw ? xSizeRight : 0.5 * horizontalWidth;}
115
116 // jaws have to fit inside containerLogicalVolume so calculate full jaw widths given offsets
117 leftJawWidth = 0.5 * horizontalWidth - leftJawHalfGap;
118 rightJawWidth = 0.5 * horizontalWidth - rightJawHalfGap;
119 vacuumWidth = 0.5 * (leftJawHalfGap + rightJawHalfGap);
120
121 // centre of jaw and vacuum volumes for placements
122 G4double leftJawCentre = 0.5*leftJawWidth + leftJawHalfGap;
123 G4double rightJawCentre = 0.5*rightJawWidth + rightJawHalfGap;
124 G4double vacuumCentre = 0.5*(leftJawHalfGap - rightJawHalfGap);
125
126 leftJawPos = G4ThreeVector(leftJawCentre, 0, 0);
127 rightJawPos = G4ThreeVector(-rightJawCentre, 0, 0);
128 vacuumOffset = G4ThreeVector(vacuumCentre, 0, 0);
129}
130
132{
133 // BDSCollimator::CheckParameters() <- we replace this and don't call it - 'tapered' is never set
134 G4double totalGap = leftJawHalfGap + rightJawHalfGap;
135 if (totalGap < 1e-3 && buildAperture) // 1um minimum, could also be negative
136 {throw BDSException(__METHOD_NAME__, "gap too small (<1um) for \"" + name + "\"");}
137
138 if (horizontalWidth - 2*lengthSafetyLarge < totalGap)
139 {throw BDSException(__METHOD_NAME__, "horizontalWidth too small for the total gap width in \"" + name + "\"");}
140
141 if (BDS::IsFinite(yHalfHeight) && (yHalfHeight < 1e-3)) // 1um minimum
142 {throw BDSException(__METHOD_NAME__, "insufficient ysize for \"" + name + "\"");}
143
145 {throw BDSException(__METHOD_NAME__, "no jaws being built for \"" + name + "\"");}
146
147 // the remaining checks only apply to the jaw and vacuum geometry
148 if (!buildAperture)
149 {return;}
150
151 // jaw solids have a half width of jawWidth/2 - lengthSafety - for jcoltip this is the bulk
152 // jaw width after the space for the tip has been removed in the derived class
153 if (buildLeftJaw && (leftJawWidth * 0.5 - lengthSafety < 1e-3)) // 1um minimum, could also be negative
154 {throw BDSException(__METHOD_NAME__, "left jaw too thin given horizontalWidth and aperture for \"" + name + "\"");}
155 if (buildRightJaw && (rightJawWidth * 0.5 - lengthSafety < 1e-3)) // 1um minimum, could also be negative
156 {throw BDSException(__METHOD_NAME__, "right jaw too thin given horizontalWidth and aperture for \"" + name + "\"");}
157
158 if (std::abs(jawTiltLeft) > 0.5*CLHEP::halfpi)
159 {throw BDSException(__METHOD_NAME__, "|jawTiltLeft| is over pi/4 radians for \"" + name + "\"");}
160 if (std::abs(jawTiltRight) > 0.5*CLHEP::halfpi)
161 {throw BDSException(__METHOD_NAME__, "|jawTiltRight| is over pi/4 radians for \"" + name + "\"");}
162
163 // shift of each jaw face at the ends of the element due to its tilt - tilt is ignored for a
164 // jaw that isn't built - uses the half gaps from Calculations(), which is called in the constructor
165 G4double tiltShiftLeft = buildLeftJaw ? std::tan(jawTiltLeft) * chordLength * 0.5 : 0;
166 G4double tiltShiftRight = buildRightJaw ? std::tan(jawTiltRight) * chordLength * 0.5 : 0;
167
168 G4double gapIn = totalGap - tiltShiftLeft + tiltShiftRight;
169 G4double gapOut = totalGap + tiltShiftLeft - tiltShiftRight;
170 if (gapIn <= 0 || gapOut <= 0)
171 {throw BDSException(__METHOD_NAME__, "the tilts plus centre gap will cause the jaws to collide in \"" + name + "\"");}
172
173 // vacuum full width at each end of the element - see vacuum construction in Build()
174 if (std::min(gapIn, gapOut) * 0.5 - lengthSafety < 1e-3) // 1um minimum
175 {throw BDSException(__METHOD_NAME__, "insufficient aperture between jaws in \"" + name + "\"");}
176}
177
179{
180 G4double horizontalHalfWidth = horizontalWidth * 0.5;
181 if (jawTiltLeft != 0 || jawTiltRight != 0)
182 {
183 // The box must encompass everything, so pick the largest absolute angle
184 G4double maxTilt = std::max(std::abs(jawTiltLeft), std::abs(jawTiltRight));
185 horizontalHalfWidth = horizontalWidth * 0.5 + chordLength * 0.5 * std::sin(maxTilt);
186 }
187
188 // For the case of jaw tilt, adjust the horizontal size, but keep the container length the same
189 // This results in small drifts either side of the collimator, but preserves the overall size
190 containerSolid = new G4Box(name + "_container_solid",
191 horizontalHalfWidth,
193 chordLength*0.5);
194
195 containerLogicalVolume = new G4LogicalVolume(containerSolid,
197 name + "_container_lv");
198 BDSExtent ext(horizontalHalfWidth, yHalfHeight, chordLength*0.5);
199 SetExtent(ext);
200}
201
203{
205 BDSAcceleratorComponent::Build(); // calls BuildContainer and sets limits and vis for container
206
207 G4VisAttributes* collimatorVisAttr = new G4VisAttributes(*colour);
208 RegisterVisAttributes(collimatorVisAttr);
209
210 // get appropriate user limits for jaw material
211 G4UserLimits* collUserLimits = CollimatorUserLimits();
212
213 // build jaws as appropriate
215 {
216 G4VSolid* leftJawSolid = nullptr;
217 if (jawTiltLeft != 0)
218 {
219 // Adjust the length of the parallelepiped to match the inside edges in Z
220 // Due to the straight parallelepiped edges, it will never match the volume an angled box,
221 // so it is chosen to underestimate the volume, but preserve the jaw x-y cutting plane.
222 G4double leftHalfLength = chordLength * 0.5 * std::cos(jawTiltLeft);
223
224 leftJawSolid = new G4Para(name + "_leftjaw_solid",
225 leftJawWidth * 0.5 - lengthSafety,
227 leftHalfLength - lengthSafety,
228 0, jawTiltLeft, 0);
229 }
230 else
231 {
232 leftJawSolid = new G4Box(name + "_leftjaw_solid",
233 leftJawWidth * 0.5 - lengthSafety,
235 chordLength * 0.5 - lengthSafety);
236 }
237
238 RegisterSolid(leftJawSolid);
239
240 G4LogicalVolume* leftJawLV = new G4LogicalVolume(leftJawSolid, // solid
241 collimatorMaterial, // material
242 name + "_leftjaw_lv"); // name
243 leftJawLV->SetVisAttributes(collimatorVisAttr);
244
245 // user limits - provided by BDSAcceleratorComponent
246 leftJawLV->SetUserLimits(collUserLimits);
247
248 // register with base class (BDSGeometryComponent)
249 RegisterLogicalVolume(leftJawLV);
250 // register it in a set of collimator logical volumes
251 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(leftJawLV);
252 if (sensitiveOuter)
253 {RegisterSensitiveVolume(leftJawLV, BDSSDType::collimatorcomplete);}
254
255 // place the jaw
256 G4PVPlacement* leftJawPV = new G4PVPlacement(nullptr, // rotation
257 leftJawPos, // position
258 leftJawLV, // its logical volume
259 name + "_leftjaw_pv", // its name
260 containerLogicalVolume, // its mother volume
261 false, // no boolean operation
262 0, // copy number
264 RegisterPhysicalVolume(leftJawPV);
265 }
267 {
268 G4VSolid* rightJawSolid = nullptr;
269
270 if (jawTiltRight != 0)
271 {
272 // Adjust the length of the parallelepiped to match the inside edges in Z
273 // Due to the straight parallelepiped edges, it will never match the volume an angled box,
274 // so it is chosen to underestimate the volume, but preserve the jaw x-y cutting plane.
275 G4double rightHalfLength = chordLength * 0.5 * std::cos(jawTiltRight);
276
277 rightJawSolid = new G4Para(name + "_rightjaw_solid",
278 rightJawWidth * 0.5 - lengthSafety,
280 rightHalfLength - lengthSafety,
281 0, jawTiltRight, 0);
282 }
283 else
284 {
285 rightJawSolid = new G4Box(name + "_rightjaw_solid",
286 rightJawWidth * 0.5 - lengthSafety,
288 chordLength * 0.5 - lengthSafety);
289 }
290
291 RegisterSolid(rightJawSolid);
292
293 G4LogicalVolume* rightJawLV = new G4LogicalVolume(rightJawSolid, // solid
294 collimatorMaterial, // material
295 name + "_rightjaw_lv"); // name
296 rightJawLV->SetVisAttributes(collimatorVisAttr);
297 rightJawLV->SetUserLimits(collUserLimits);
298 RegisterLogicalVolume(rightJawLV);
299 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(rightJawLV);
300 if (sensitiveOuter)
301 {RegisterSensitiveVolume(rightJawLV, BDSSDType::collimatorcomplete);}
302
303 // place the jaw
304 G4PVPlacement* rightJawPV = new G4PVPlacement(nullptr, // rotation
305 rightJawPos, // position
306 rightJawLV, // its logical volume
307 name + "_rightjaw_pv", // its name
308 containerLogicalVolume, // its mother volume
309 false, // no boolean operation
310 0, // copy number
312 RegisterPhysicalVolume(rightJawPV);
313 }
314 // if no aperture but the code has got to this stage, build the collimator as a simple box.
315 if (!buildAperture)
316 {
317 collimatorSolid = new G4Box(name + "_block_solid",
320 chordLength * 0.5 - lengthSafety);
322
323 collimatorLV = new G4LogicalVolume(collimatorSolid, collimatorMaterial, name + "_block_lv");
324 collimatorLV->SetVisAttributes(collimatorVisAttr);
325 collimatorLV->SetUserLimits(collUserLimits);
327 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(collimatorLV);
328 if (sensitiveOuter)
329 {RegisterSensitiveVolume(collimatorLV, BDSSDType::collimatorcomplete);}
330
331 // place the jaw
332 G4PVPlacement* collimatorPV = new G4PVPlacement(nullptr, // rotation
333 (G4ThreeVector) 0, // position
334 collimatorLV, // its logical volume
335 name + "_pv", // its name
336 containerLogicalVolume, // its mother volume
337 false, // no boolean operation
338 0, // copy number
340 RegisterPhysicalVolume(collimatorPV);
341 }
342
343 // build and place the vacuum volume only if the aperture is finite.
344 if (buildAperture)
345 {
346 if (jawTiltLeft != 0 || jawTiltRight != 0)
347 {
349 G4double tiltLeft = buildLeftJaw ? jawTiltLeft : 0.;
350 G4double tiltRight = buildRightJaw ? jawTiltRight : 0.;
351
352 G4double tiltShiftLeftDownstream = std::tan(tiltLeft) * chordLength * 0.5;
353 G4double tiltShiftRightDownstream = std::tan(tiltRight) * chordLength * 0.5;
354
355 G4double xGapLeftUpstream = leftJawHalfGap - tiltShiftLeftDownstream;
356 G4double xGapLeftDownstream = leftJawHalfGap + tiltShiftLeftDownstream;
357 G4double xGapRightUpstream = -rightJawHalfGap - tiltShiftRightDownstream;
358 G4double xGapRightDownstream = -rightJawHalfGap + tiltShiftRightDownstream;
359
360 std::vector<G4TwoVector> vertices {G4TwoVector(xGapRightUpstream + lengthSafety, -(yHalfHeight - lengthSafety)),
361 G4TwoVector(xGapRightUpstream + lengthSafety, (yHalfHeight - lengthSafety)),
362 G4TwoVector(xGapLeftUpstream - lengthSafety, (yHalfHeight - lengthSafety)),
363 G4TwoVector(xGapLeftUpstream - lengthSafety, -(yHalfHeight - lengthSafety)),
364 G4TwoVector(xGapRightDownstream + lengthSafety, -(yHalfHeight - lengthSafety)),
365 G4TwoVector(xGapRightDownstream + lengthSafety, (yHalfHeight - lengthSafety)),
366 G4TwoVector(xGapLeftDownstream - lengthSafety, (yHalfHeight - lengthSafety)),
367 G4TwoVector(xGapLeftDownstream - lengthSafety, -(yHalfHeight - lengthSafety))};
368
369 vacuumSolid = new G4GenericTrap(name + "_vacuum_solid",
371 vertices);
372 // For tilted jaws, the vacuum trapezoid is constructed from absolute coordinates
373 // so need to zero the vacuum offset, which is intended for a box.
374 vacuumOffset = G4ThreeVector(0, 0, 0);
375 }
376 else
377 {
378 vacuumSolid = new G4Box(name + "_vacuum_solid", // name
379 vacuumWidth - lengthSafety, // x half width
380 yHalfHeight - lengthSafety, // y half width
381 chordLength * 0.5); // z half length
382 }
383
385
386 G4LogicalVolume* vacuumLV = new G4LogicalVolume(vacuumSolid, // solid
387 vacuumMaterial, // material
388 name + "_vacuum_lv"); // name
389
390 vacuumLV->SetVisAttributes(containerVisAttr);
391 vacuumLV->SetUserLimits(userLimits);
393 RegisterLogicalVolume(vacuumLV);
394 if (sensitiveVacuum)
395 {RegisterSensitiveVolume(vacuumLV, BDSSDType::energydepvacuum);}
396
397 G4PVPlacement* vacPV = new G4PVPlacement(nullptr, // rotation
398 vacuumOffset, // position
399 vacuumLV, // its logical volume
400 name + "_vacuum_pv", // its name
401 containerLogicalVolume, // its mother volume
402 false, // no boolean operation
403 0, // copy number
406 }
407}
G4UserLimits * userLimits
Cache of user limits.
void SetAcceleratorVacuumLogicalVolume(G4LogicalVolume *accVacLVIn)
const G4String name
Const protected member variable that may not be changed by derived classes.
static G4bool sensitiveOuter
Useful variable often used in construction.
static G4double lengthSafety
Useful variable often used in construction.
static G4bool checkOverlaps
Useful variable often used in construction.
G4double chordLength
Protected member variable that can be modified by derived classes.
static G4VisAttributes * containerVisAttr
Useful variable often used in construction.
static G4bool sensitiveVacuum
Useful variable often used in construction.
std::set< G4LogicalVolume * > * VolumeSet(const G4String &name)
Returns pointer to a set of logical volumes. If no set by that name exits, create it.
G4bool buildAperture
Build aperture or not.
virtual void Build() override
Override function in BDSCollimator for totally different construction.
G4double xSizeRight
Offset of jaw 2.
virtual void BuildContainerLogicalVolume() override
Override function in BDSCollimator for different size based container.
G4double xSizeLeft
Offset of jaw 1.
G4double jawTiltLeft
Tilt of jaw 1 (angle in x-z plane)
G4double jawTiltRight
Tilt of jaw 2 (angle in x-z plane)
void Calculations()
Calculate offsets and sizes.
G4double yHalfHeight
Half height of each jaw.
G4bool buildRightJaw
Build right jaw or not.
virtual void CheckParameters() override
G4double xHalfGap
Half gap separation between jaws.
G4bool buildLeftJaw
Build left jaw or not.
BDSCollimatorJaw()=delete
Private default constructor to force the use of the supplied one.
G4LogicalVolume * collimatorLV
In case of no aperture, cache this volume for derived classes to place inside.
Base class for collimators with common construction.
G4UserLimits * CollimatorUserLimits()
Return either default user limits or custom ones based on optional minimumKineticEnergy.
G4Material * vacuumMaterial
Vacuum material.
G4double horizontalWidth
Horizontal width.
G4VSolid * vacuumSolid
Geometrical objects:
G4Material * collimatorMaterial
Material.
G4VSolid * collimatorSolid
Geometrical objects:
G4Colour * colour
Colour of collimator.
G4Colour * GetColour(G4double red, G4double green, G4double blue, G4double alpha=1)
Get a cached anonymous colour by values.
static BDSColours * Instance()
singleton pattern
Definition BDSColours.cc:32
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
void RegisterLogicalVolume(G4LogicalVolume *logicalVolume)
void RegisterPhysicalVolume(G4VPhysicalVolume *physicalVolume)
void SetExtent(const BDSExtent &extIn)
Set extent.
void RegisterVisAttributes(G4VisAttributes *visAttribute)
void RegisterSolid(G4VSolid *solid)
void RegisterSensitiveVolume(G4LogicalVolume *sensitiveVolume, BDSSDType sensitivityType)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())