BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSCollimatorTipJaw.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2024.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSAcceleratorModel.hh"
20#include "BDSCollimatorTipJaw.hh"
21#include "BDSBeamPipeInfo.hh"
22#include "BDSColours.hh"
23#include "BDSDebug.hh"
24#include "BDSException.hh"
25#include "BDSMaterials.hh"
26#include "BDSSDType.hh"
27#include "BDSUtilities.hh"
28#include "BDSTipCollimator.hh"
29
30#include "G4Box.hh"
31#include "G4Para.hh"
32#include "G4GenericTrap.hh"
33#include "G4LogicalVolume.hh"
34#include "G4PVPlacement.hh"
35#include "G4VisAttributes.hh"
36
37#include <cmath>
38#include <vector>
39#include <map>
40#include <set>
41
42BDSCollimatorTipJaw::BDSCollimatorTipJaw(const G4String& nameIn,
43 G4double lengthIn,
44 G4double horizontalWidthIn,
45 G4double xHalfGapIn,
46 G4double yHalfHeightIn,
47 G4double xSizeLeftIn,
48 G4double xSizeRightIn,
49 G4double leftJawTiltIn,
50 G4double rightJawTiltIn,
51 G4double tipThicknessIn,
52 G4bool buildLeftJawIn,
53 G4bool buildRightJawIn,
54 G4Material* collimatorMaterialIn,
55 G4Material* collimatorTipMaterialIn,
56 G4Material* vacuumMaterialIn,
57 G4Colour* colourIn,
58 G4Colour* tipColourIn):
59BDSTipCollimator(nameIn, lengthIn, horizontalWidthIn, "jcoltip", collimatorMaterialIn, collimatorTipMaterialIn, vacuumMaterialIn,
60 xHalfGapIn, yHalfHeightIn, xHalfGapIn, yHalfHeightIn, colourIn, tipColourIn, tipThicknessIn),
61 jawSolid(nullptr),
62 xSizeLeft(xSizeLeftIn),
63 xSizeRight(xSizeRightIn),
64 xHalfGap(xHalfGapIn),
65 jawTiltLeft(leftJawTiltIn),
66 jawTiltRight(rightJawTiltIn),
67 tipThickness(tipThicknessIn),
68 jawHalfWidth(0),
69 yHalfHeight(yHalfHeightIn),
70 buildLeftJaw(buildLeftJawIn),
71 buildRightJaw(buildRightJawIn),
72 buildAperture(true)
73{
74 jawHalfWidth = 0.5 * (0.5*horizontalWidth - lengthSafetyLarge - xHalfGap);
75}
76
77BDSCollimatorTipJaw::~BDSCollimatorTipJaw()
78{;}
79
81{
82 if (!colour)
83 {colour = BDSColours::Instance()->GetColour("collimator");}
84 if (!tipColour)
85 {tipColour = BDSColours::Instance()->GetColour("collimatorTip");}
86
87 if (jawHalfWidth < 1e-3) // 1um minimum, could also be negative
88 {throw BDSException(__METHOD_NAME__, "horizontalWidth insufficient given xsize of jcoltip \"" + name + "\"");}
89
90 // set half height to half horizontal width if zero - finite height required.
93
94 if (BDS::IsFinite(yHalfHeight) && (yHalfHeight < 1e-3)) // 1um minimum
95 {throw BDSException(__METHOD_NAME__, "insufficient ysize for jcoltip \"" + name + "\"");}
96
97 if ((yHalfHeight < 0) || ((yHalfHeight > 0) && (yHalfHeight < 1e-3))) // 1um minimum and not negative
98 {throw BDSException(__METHOD_NAME__, "insufficient ysize for jcoltip \"" + name + "\"");}
99
100 if (xSizeLeft < 0)
101 {throw BDSException(__METHOD_NAME__, "left jcoltip jaw cannot have negative half aperture size: \"" + name + "\"");}
102 if (xSizeRight < 0)
103 {throw BDSException(__METHOD_NAME__, "left jcoltip jaw cannot have negative half aperture size: \"" + name + "\"");}
104
105 if (std::abs(xSizeLeft) > 0.5*horizontalWidth)
106 {
107 G4cerr << __METHOD_NAME__ << "jcoltip \"" << name
108 << "\" left jaw offset is greater the element half width, jaw "
109 << "will not be constructed" << G4endl;
110 buildLeftJaw = false;
111 }
112 if (std::abs(xSizeRight) > 0.5*horizontalWidth)
113 {
114 G4cerr << __METHOD_NAME__ << "jcoltip \"" << name
115 << "\" right jaw offset is greater the element half width, jaw "
116 << "will not be constructed" << G4endl;
117 buildRightJaw = false;
118 }
119
120 if (std::abs(jawTiltLeft) > 0 && std::tan(std::abs(jawTiltLeft)) * chordLength / 2. > std::max(xHalfGap, xSizeLeft))
121 {throw BDSException(__METHOD_NAME__, "tilted left jaw not allowed to cross the mid-plane: \"" + name + "\"");}
122
123 if (std::abs(jawTiltRight) > 0 && std::tan(std::abs(jawTiltRight)) * chordLength / 2. > std::max(xHalfGap, xSizeLeft))
124 {throw BDSException(__METHOD_NAME__, "tilted right jaw not allowed to cross the mid-plane: \"" + name + "\"");}
125
127 {throw BDSException(__METHOD_NAME__, "no jaws being built: \"" + name + "\"");}
128
130 {buildAperture = false;}
131}
132
134{
135 G4double horizontalHalfWidth = horizontalWidth * 0.5;
136 if (jawTiltLeft != 0 || jawTiltRight != 0)
137 {
138 // The box must encompass everything, so pick the largest absolute angle
139 horizontalHalfWidth = horizontalWidth * 0.5 + chordLength * 0.5 * std::sin(std::max(std::abs(jawTiltLeft), std::abs(jawTiltRight)));
140 }
141
142 // For the case of jaw tilt, adjust the horizontal size, but keep the container length the same
143 // This results in small drifts either side of the collimator, but preserves the overall size
144 containerSolid = new G4Box(name + "_container_solid",
145 horizontalHalfWidth,
147 chordLength*0.5);
148
149 containerLogicalVolume = new G4LogicalVolume(containerSolid,
151 name + "_container_lv");
152 BDSExtent ext(horizontalHalfWidth, yHalfHeight, chordLength*0.5);
153 SetExtent(ext);
154}
155
157{
160
161 // set each jaws half gap default to aperture half size
162 G4double leftJawHalfGap = xHalfGap;
163 G4double rightJawHalfGap = xHalfGap;
164
165 // update jaw half gap with offsets
166 // if one jaw is not constructed, set the opening to xSize/2 for the aperture vacuum volume creation
168 {
169 if (buildLeftJaw)
170 {leftJawHalfGap = xSizeLeft;}
171 else
172 {leftJawHalfGap = 0.5 * horizontalWidth;}
173 }
174
176 {
177 if (buildRightJaw)
178 {rightJawHalfGap = xSizeRight;}
179 else
180 {rightJawHalfGap = 0.5 * horizontalWidth;}
181 }
182
183 // jaws have to fit inside containerLogicalVolume so calculate full jaw widths given offsets
184 G4double leftJawWidth = 0.5 * horizontalWidth - leftJawHalfGap;
185 G4double rightJawWidth = 0.5 * horizontalWidth - rightJawHalfGap;
186 G4double vacuumWidth = 0.5 * (leftJawHalfGap + rightJawHalfGap);
187
188 // centre of jaw and vacuum volumes for placements
189 G4double leftJawCentre = 0.5*leftJawWidth + leftJawHalfGap;
190 G4double rightJawCentre = 0.5*rightJawWidth + rightJawHalfGap;
191 G4double vacuumCentre = 0.5*(leftJawHalfGap - rightJawHalfGap);
192
193 G4ThreeVector leftJawPos = G4ThreeVector(leftJawCentre, 0, 0);
194 G4ThreeVector rightJawPos = G4ThreeVector(-rightJawCentre, 0, 0);
195 G4ThreeVector vacuumOffset = G4ThreeVector(vacuumCentre, 0, 0);
196
197 G4VisAttributes* collimatorVisAttr = new G4VisAttributes(*colour);
198 G4VisAttributes* tipVisAttr = new G4VisAttributes(*tipColour);
199
200 RegisterVisAttributes(collimatorVisAttr);
201 RegisterVisAttributes(tipVisAttr);
202
203 // get appropriate user limits for jaw material
204 G4UserLimits* collUserLimits = CollimatorUserLimits();
205
206 // build jaws as appropriate
208 {
209 G4VSolid* leftJawSolid = nullptr;
210 G4VSolid* leftJawTipSolid = nullptr;
211
212 if (jawTiltLeft != 0)
213 {
214 // Adjust the length of the parallelepiped to match the inside edges in Z
215 // Due to the straight parallelepiped edges, it will never match the volume an angled box,
216 // so it is chosen to underestimate the volume, but preserve the jaw x-y cutting plane.
217 G4double leftHalfLength = chordLength * 0.5 * std::cos(jawTiltLeft);
218
219 leftJawSolid = new G4Para(name + "_leftjaw_solid",
220 (leftJawWidth - tipThickness) * 0.5 - lengthSafety,
222 leftHalfLength - lengthSafety,
223 0,
225 0);
226
227 leftJawTipSolid = new G4Para(name + "_leftjawtip_solid",
230 leftHalfLength - lengthSafety,
231 0,
233 0);
234 }
235 else
236 {
237 leftJawSolid = new G4Box(name + "_leftjaw_solid",
238 (leftJawWidth - tipThickness) * 0.5 - lengthSafety,
240 chordLength * 0.5 - lengthSafety);
241
242 leftJawTipSolid = new G4Box(name + "_leftjawtip_solid",
245 chordLength * 0.5 - lengthSafety);
246 }
247
248 RegisterSolid(leftJawSolid);
249 RegisterSolid(leftJawTipSolid);
250
251 G4LogicalVolume* leftJawLV = new G4LogicalVolume(leftJawSolid, // solid
252 collimatorMaterial, // material
253 name + "_leftjaw_lv"); // name
254 leftJawLV->SetVisAttributes(collimatorVisAttr);
255
256 // user limits - provided by BDSAcceleratorComponent
257 leftJawLV->SetUserLimits(collUserLimits);
258
259 // register with base class (BDSGeometryComponent)
260 RegisterLogicalVolume(leftJawLV);
261 // register it in a set of collimator logical volumes
262 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(leftJawLV);
263 if (sensitiveOuter)
264 {RegisterSensitiveVolume(leftJawLV, BDSSDType::collimatorcomplete);}
265
266 G4LogicalVolume* leftJawTipLV = new G4LogicalVolume(leftJawTipSolid,
268 name + "_leftjawtip_lv");
269 leftJawTipLV->SetVisAttributes(tipVisAttr);
270 leftJawTipLV->SetUserLimits(collUserLimits);
271 RegisterLogicalVolume(leftJawTipLV);
272 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(leftJawTipLV);
273 if (sensitiveOuter)
274 {RegisterSensitiveVolume(leftJawTipLV, BDSSDType::collimatorcomplete);}
275
276 // place the tip
277 G4PVPlacement* leftJawTipPV = new G4PVPlacement(nullptr, // rotation
278 leftJawPos - G4ThreeVector((leftJawWidth - tipThickness) * 0.5, 0, 0), // position
279 leftJawTipLV, // logical volume
280 name + "_leftjawtip_pv", // name
281 containerLogicalVolume, // its mother volume
282 false, // no boolean operation
283 0, // copy number
285 RegisterPhysicalVolume(leftJawTipPV);
286
287 // place the jaw
288 G4PVPlacement* leftJawPV = new G4PVPlacement(nullptr, // rotation
289 leftJawPos + G4ThreeVector(tipThickness * 0.5, 0, 0), // position
290 leftJawLV, // logical volume
291 name + "_leftjaw_pv", // name
292 containerLogicalVolume, // its mother volume
293 false, // no boolean operation
294 1, // copy number
296 RegisterPhysicalVolume(leftJawPV);
297 }
299 {
300 G4VSolid* rightJawSolid = nullptr;
301 G4VSolid* rightJawTipSolid = nullptr;
302
303 if (jawTiltRight != 0)
304 {
305 // Adjust the length of the parallelepiped to match the inside edges in Z
306 // Due to the straight parallelepiped edges, it will never match the volume an angled box,
307 // so it is chosen to underestimate the volume, but preserve the jaw x-y cutting plane.
308 G4double rightHalfLength = chordLength * 0.5 * std::cos(jawTiltRight);
309
310 rightJawSolid = new G4Para(name + "_rightjaw_solid",
311 (rightJawWidth - tipThickness) * 0.5 - lengthSafety,
313 rightHalfLength - lengthSafety,
314 0,
316 0);
317
318 rightJawTipSolid = new G4Para(name + "_rightjawtip_solid",
321 rightHalfLength - lengthSafety,
322 0,
324 0);
325 }
326 else
327 {
328 rightJawSolid = new G4Box(name + "_rightjaw_solid",
329 (rightJawWidth - tipThickness) * 0.5 - lengthSafety,
331 chordLength * 0.5 - lengthSafety);
332
333 rightJawTipSolid = new G4Box(name + "_rightjawtip_solid",
336 chordLength * 0.5 - lengthSafety);
337 }
338
339 RegisterSolid(rightJawSolid);
340 RegisterSolid(rightJawTipSolid);
341
342 G4LogicalVolume* rightJawLV = new G4LogicalVolume(rightJawSolid,
344 name + "_rightjaw_lv");
345 rightJawLV->SetVisAttributes(collimatorVisAttr);
346
347 // user limits - provided by BDSAcceleratorComponent
348 rightJawLV->SetUserLimits(collUserLimits);
349
350 // register with base class (BDSGeometryComponent)
351 RegisterLogicalVolume(rightJawLV);
352 // register it in a set of collimator logical volumes
353 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(rightJawLV);
354 if (sensitiveOuter)
355 {RegisterSensitiveVolume(rightJawLV, BDSSDType::collimatorcomplete);}
356
357 G4LogicalVolume* rightJawTipLV = new G4LogicalVolume(rightJawTipSolid,
359 name + "_rightjawtip_lv");
360 rightJawTipLV->SetVisAttributes(tipVisAttr);
361 rightJawTipLV->SetUserLimits(collUserLimits);
362 RegisterLogicalVolume(rightJawTipLV);
363 BDSAcceleratorModel::Instance()->VolumeSet("collimators")->insert(rightJawTipLV);
364 if (sensitiveOuter)
365 {RegisterSensitiveVolume(rightJawTipLV, BDSSDType::collimatorcomplete);}
366
367 // place the tip
368 G4PVPlacement* rightJawTipPV = new G4PVPlacement(nullptr, // rotation
369 rightJawPos + G4ThreeVector((rightJawWidth - tipThickness) * 0.5, 0, 0), // position
370 rightJawTipLV, // its logical volume
371 name + "_rightjawtip_pv", // its name
372 containerLogicalVolume, // its mother volume
373 false, // no boolean operation
374 0, // copy number
376 RegisterPhysicalVolume(rightJawTipPV);
377
378 // place the jaw
379 G4PVPlacement* rightJawPV = new G4PVPlacement(nullptr, // rotation
380 rightJawPos - G4ThreeVector(tipThickness * 0.5, 0, 0), // position
381 rightJawLV, // logical volume
382 name + "_rightjaw_pv", // name
383 containerLogicalVolume, // its mother volume
384 false, // no boolean operation
385 1, // copy number
387 RegisterPhysicalVolume(rightJawPV);
388 }
389 // if no aperture but the code has got to this stage, build the collimator as a simple box.
390 if (!buildAperture)
391 {
392 collimatorSolid = new G4Box(name + "_solid",
395 chordLength * 0.5 - lengthSafety);
397
398 G4LogicalVolume* collimatorLV = new G4LogicalVolume(collimatorSolid,
400 name + "_lv");
401 collimatorLV->SetVisAttributes(collimatorVisAttr);
402
403 // user limits - provided by BDSAcceleratorComponent - don't use collUserLimits
404 collimatorLV->SetUserLimits(userLimits);
405
406 // register with base class (BDSGeometryComponent)
407 RegisterLogicalVolume(collimatorLV);
408 if (sensitiveOuter)
409 {RegisterSensitiveVolume(collimatorLV, BDSSDType::collimatorcomplete);}
410
411 // place the collimator
412 G4PVPlacement* collimatorPV = new G4PVPlacement(nullptr,
413 (G4ThreeVector) 0,
414 collimatorLV,
415 name + "_pv",
416 containerLogicalVolume,
417 false,
418 0,
420 RegisterPhysicalVolume(collimatorPV);
421 }
422
423 // build and place the vacuum volume only if the aperture is finite.
424 if (buildAperture)
425 {
426 if (jawTiltLeft != 0 || jawTiltRight != 0)
427 {
429 G4double tiltLeft = buildLeftJaw ? jawTiltLeft : 0.;
430 G4double tiltRight = buildRightJaw ? jawTiltRight : 0.;
431
434 G4double halfLengthLeftEff = (chordLength * 0.5) / std::cos(tiltLeft);
435 G4double halfLengthRightEff = (chordLength * 0.5) / std::cos(tiltRight);
436
439 G4double xGapLeftUpstream = -halfLengthLeftEff * std::sin(tiltLeft) + leftJawHalfGap;
440 G4double xGapLeftDownstream = halfLengthLeftEff * std::sin(tiltLeft) + leftJawHalfGap;
441 G4double xGapRightUpstream = -halfLengthRightEff * std::sin(tiltRight) - rightJawHalfGap;
442 G4double xGapRightDownstream = halfLengthRightEff * std::sin(tiltRight) - rightJawHalfGap;
443
444 std::vector<G4TwoVector> vertices {G4TwoVector(xGapRightUpstream + lengthSafety, -(yHalfHeight - lengthSafety)),
445 G4TwoVector(xGapRightUpstream + lengthSafety, (yHalfHeight - lengthSafety)),
446 G4TwoVector(xGapLeftUpstream - lengthSafety, (yHalfHeight - lengthSafety)),
447 G4TwoVector(xGapLeftUpstream - lengthSafety, -(yHalfHeight - lengthSafety)),
448 G4TwoVector(xGapRightDownstream + lengthSafety, -(yHalfHeight - lengthSafety)),
449 G4TwoVector(xGapRightDownstream + lengthSafety, (yHalfHeight - lengthSafety)),
450 G4TwoVector(xGapLeftDownstream - lengthSafety, (yHalfHeight - lengthSafety)),
451 G4TwoVector(xGapLeftDownstream - lengthSafety, -(yHalfHeight - lengthSafety))};
452
453 vacuumSolid = new G4GenericTrap(name + "_vacuum_solid",
455 vertices);
456 // The for tilted jaws, the vacuum trapezoid is constructed from absolute coordinates
457 // need to rese the vacuum offset, which is intended for a box
458 vacuumOffset = G4ThreeVector(0, 0, 0);
459 }
460 else
461 {
462 vacuumSolid = new G4Box(name + "_vacuum_solid", // name
463 vacuumWidth - lengthSafety, // x half width
464 yHalfHeight - lengthSafety, // y half width
465 chordLength * 0.5); // z half length
466 }
467
469
470 G4LogicalVolume* vacuumLV = new G4LogicalVolume(vacuumSolid, // solid
471 vacuumMaterial, // material
472 name + "_vacuum_lv"); // name
473
474 vacuumLV->SetVisAttributes(containerVisAttr);
475 vacuumLV->SetUserLimits(userLimits);
477 RegisterLogicalVolume(vacuumLV);
478 if (sensitiveVacuum)
479 {RegisterSensitiveVolume(vacuumLV, BDSSDType::energydepvacuum);}
480
481 G4PVPlacement* vacPV = new G4PVPlacement(nullptr, // rotation
482 vacuumOffset, // position
483 vacuumLV, // its logical volume
484 name + "_vacuum_pv", // its name
485 containerLogicalVolume, // its mother volume
486 false, // no boolean operation
487 0, // copy number
490 }
491}
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.
G4double jawTiltRight
Tilt of jaw 2 (angle in x-z plane)
G4double tipThickness
Thickness of collimator tip.
G4double jawTiltLeft
Tilt of jaw 1 (angle in x-z plane)
G4double xHalfGap
Half gap separation between jaws.
BDSCollimatorTipJaw()=delete
Private default constructor to force the use of the supplied one.
G4bool buildRightJaw
Build right jaw or not.
G4double xSizeLeft
Offset of jaw 1.
G4bool buildAperture
Build aperture or not.
virtual void CheckParameters() override
virtual void Build() override
Override function in BDSCollimator for totally different construction.
G4double jawHalfWidth
Half width of each jaw.
G4double xSizeRight
Offset of jaw 2.
G4bool buildLeftJaw
Build left jaw or not.
virtual void BuildContainerLogicalVolume() override
Override function in BDSCollimator for different size based container.
G4double yHalfHeight
Half height of each jaw.
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:33
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:39
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)
Base class for collimators with a bulk material and a tip of a different material.
G4double horizontalWidth
Horizontal width.
G4Colour * tipColour
Colour of collimator tip.
G4VSolid * collimatorSolid
Geometrical objects:
G4Material * collimatorTipMaterial
Tip material.
G4Material * vacuumMaterial
Vacuum material.
G4Colour * colour
Colour of collimator.
G4Material * collimatorMaterial
Material.
G4UserLimits * CollimatorUserLimits()
Return either default user limits or custom ones based on optional minimumKineticEnergy.
G4VSolid * vacuumSolid
Geometrical objects:
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())