BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSCollimatorCrystal.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 "BDSAcceleratorComponent.hh"
19#include "BDSAcceleratorModel.hh"
20#include "BDSBeamPipe.hh"
21#include "BDSBeamPipeFactory.hh"
22#include "BDSBeamPipeInfo.hh"
23#include "BDSCollimatorCrystal.hh"
24#include "BDSCrystalFactory.hh"
25#include "BDSCrystalInfo.hh"
26#include "BDSDebug.hh"
27#include "BDSException.hh"
28#include "BDSGeometryComponent.hh"
29#include "BDSSDType.hh"
30#include "BDSUtilities.hh"
31#include "BDSWarning.hh"
32
33#include "globals.hh" // geant4 globals / types
34#include "G4Material.hh"
35#include "G4PVPlacement.hh"
36#include "G4RotationMatrix.hh"
37#include "G4ThreeVector.hh"
38
39#include <cmath>
40
41BDSCollimatorCrystal::BDSCollimatorCrystal(const G4String& nameIn,
42 G4double lengthIn,
43 BDSBeamPipeInfo* beamPipeInfoIn,
44 BDSCrystalInfo* crystalInfoLeftIn,
45 BDSCrystalInfo* crystalInfoRightIn,
46 G4double halfGapLeftIn,
47 G4double halfGapRightIn,
48 G4double angleYAxisLeftIn,
49 G4double angleYAxisRightIn):
50 BDSAcceleratorComponent(nameIn, lengthIn, 0, "crystalcol", beamPipeInfoIn),
51 crystalInfoLeft(crystalInfoLeftIn),
52 crystalInfoRight(crystalInfoRightIn),
53 halfGapLeft(halfGapLeftIn),
54 halfGapRight(halfGapRightIn),
55 angleYAxisLeft(angleYAxisLeftIn),
56 angleYAxisRight(angleYAxisRightIn),
57 crystalLeft(nullptr),
58 crystalRight(nullptr)
59{
60#ifdef USE_SIXTRACKLINK
61 G4cout << "Left " << crystalInfoLeftIn << G4endl;
62 G4cout << "Right " << crystalInfoRightIn << G4endl;
63#endif
64 if (crystalInfoLeft)
65 {crystalInfoLeft->bendingAngleYAxis *= -1.0;}
66#ifdef USE_SIXTRACKLINK
67 G4cout << "halfGapLeftIn " << halfGapLeftIn << G4endl;
68 G4cout << "halfGapRightIn " << halfGapRightIn << G4endl;
69#endif
70}
71
72BDSCollimatorCrystal::~BDSCollimatorCrystal()
73{
74 delete crystalInfoLeft;
75 delete crystalInfoRight;
76}
77
79{
80 // we don't need to set user limits because that is done in the beam pipe factory
82 BDSBeamPipe* pipe = factory->CreateBeamPipe(name,
85
86 RegisterDaughter(pipe);
87
88 // make the beam pipe container, this object's container
89 containerLogicalVolume = pipe->GetContainerLogicalVolume();
90 containerSolid = pipe->GetContainerSolid();
91
92 // register vacuum volume (for biasing)
94
95 // update extents
96 InheritExtents(pipe);
97
98 // update faces
101
103 if (crystalInfoLeft)
104 {
105 crystalLeft = fac->CreateCrystal(name + "_left", crystalInfoLeft);
106 RegisterDaughter(crystalLeft);
107 RegisterCrystalLVs(crystalLeft);
108 }
110 {
111 crystalRight = fac->CreateCrystal(name + "_right", crystalInfoRight);
112 RegisterDaughter(crystalRight);
113 RegisterCrystalLVs(crystalRight);
114 }
115 delete fac;
116
117 if (crystalLeft)
118 {
119 G4ThreeVector objectOffset = crystalLeft->GetPlacementOffset();
120 G4double dx = TransverseOffsetToEdge(crystalLeft, angleYAxisLeft, true);
121 G4ThreeVector colOffsetL = G4ThreeVector(halfGapLeft+dx,0,0);
122 G4ThreeVector placementOffsetL = objectOffset + colOffsetL; // 'L' in p offset to avoid class with BDSGeometry Component member
123 G4RotationMatrix* placementRot = crystalLeft->GetPlacementRotation();
124 if (BDS::IsFinite(angleYAxisLeft))
125 {
126 if (!placementRot)
127 {
128 placementRot = new G4RotationMatrix();
129 RegisterRotationMatrix(placementRot);
130 }
131 G4ThreeVector localUnitY = G4ThreeVector(0,1,0).transform(*placementRot);
132 placementRot->rotate(-angleYAxisLeft, localUnitY); // rotate about local unitY
133 // note minus sign to rotate *away* from centre
134 }
135
136 // check if it'll fit..
137 // use the collOffsetL as the specific solid might require a large offset (e.g. cylinder or torus)
138 BDSExtent extShifted = (crystalLeft->GetExtent()).Translate(colOffsetL);
139 BDSExtent thisExtent = GetExtent(); // actually outer extent of beam pipe
140 G4bool safe = thisExtent.Encompasses(extShifted);
141 // second stricter check - TODO - use aperture check in future
144 0.5*chordLength);
145 G4bool safe2 = innerRadius.Encompasses(extShifted);
146 if (!safe || !safe2)
147 {BDS::Warning(__METHOD_NAME__, "Left crystal potential overlap in component \"" + name +"\"");}
148 LongitudinalOverlap(crystalLeft->GetExtent(), angleYAxisLeft, "Left");
149
150#ifdef USE_SIXTRACKLINK
151 G4cout << "left crystal placement offset " << placementOffsetL << G4endl;
152 if (placementRot)
153 {
154 G4cout << "left crystal placement rotation " << *placementRot << G4endl;
155 }
156#endif
157
158 G4LogicalVolume* vac = *(GetAcceleratorVacuumLogicalVolumes().begin()); // take the first one
159 auto cL = new G4PVPlacement(placementRot,
160 placementOffsetL,
161 crystalLeft->GetContainerLogicalVolume(),
162 name + "_crystal_left_pv",
163 vac,
164 false,
165 0,
166 true); // always check
168#ifdef USE_SIXTRACKLINK
169 G4cout << "Placement of left crystal " << placementOffsetL << G4endl;
170#endif
171 }
172 if (crystalRight)
173 {
174 G4ThreeVector objectOffset = crystalRight->GetPlacementOffset();
175 G4double dx = TransverseOffsetToEdge(crystalRight, angleYAxisRight, false);
176 G4ThreeVector colOffsetR = G4ThreeVector(-(halfGapRight+dx),0,0); // -ve as r.h. coord system
177 G4ThreeVector placementOffsetL = objectOffset + colOffsetR;
178 G4RotationMatrix* placementRot = crystalRight->GetPlacementRotation();
179 if (BDS::IsFinite(angleYAxisRight))
180 {
181 if (!placementRot)
182 {
183 placementRot = new G4RotationMatrix();
184 RegisterRotationMatrix(placementRot);
185 }
186 G4ThreeVector localUnitY = G4ThreeVector(0,1,0).transform(*placementRot);
187 placementRot->rotate(angleYAxisRight, localUnitY); // rotate about local unitY
188 }
189
190 // check if it'll fit..
191 BDSExtent extShifted = (crystalRight->GetExtent()).Translate(colOffsetR);
192 BDSExtent thisExtent = GetExtent();
193 G4bool safe = thisExtent.Encompasses(extShifted);
194 // second stricter check - TODO - use aperture check in future
197 0.5*chordLength);
198 G4bool safe2 = innerRadius.Encompasses(extShifted);
199 if (!safe || !safe2)
200 {BDS::Warning(__METHOD_NAME__, "Right crystal potential overlap in component \"" + name +"\"");}
201 LongitudinalOverlap(crystalRight->GetExtent(), angleYAxisRight, "Right");
202
203#ifdef USE_SIXTRACKLINK
204 G4cout << "right crystal placement offset " << placementOffsetL << G4endl;
205 if (placementRot)
206 {
207 G4cout << "right crystal placement rotation " << *placementRot << G4endl;
208 }
209#endif
210
211 G4LogicalVolume* vac = *(GetAcceleratorVacuumLogicalVolumes().begin()); // take the first one
212 auto cR = new G4PVPlacement(placementRot,
213 placementOffsetL,
214 crystalRight->GetContainerLogicalVolume(),
215 name + "_crystal_right_pv",
216 vac,
217 false,
218 0,
219 true); // always check
221 }
222 if (!crystalLeft && !crystalRight)
223 {throw BDSException(__METHOD_NAME__, "Error in crystal construction in component \"" + name + "\"");}
224}
225
227{
228 auto bpMat = GetBeamPipeInfo()->beamPipeMaterial;
229 if (bpMat)
230 {return bpMat->GetName();}
231 else
232 {return BDSAcceleratorComponent::Material();} // none
233}
234
236 G4double crystalAngle,
237 const G4String& side) const
238{
239 G4double zExt = extCrystal.MaximumZ();
240 G4double dz = zExt*std::tan(crystalAngle);
241
242 G4bool overlap = 2*zExt + 2*std::abs(dz) > (chordLength - 2*lengthSafety);
243
244 if (overlap)
245 {
246 G4cout << "Crystal of length " << 2*zExt/CLHEP::mm << " mm is at angle "
247 << crystalAngle / CLHEP::mrad << " mrad and container is "
248 << chordLength/CLHEP::m << " m long." << G4endl;
249 throw BDSException(__METHOD_NAME__, side + " crystal won't fit in collimator due to rotation.");
250 }
251}
252
254{
255 auto crystals = BDSAcceleratorModel::Instance()->VolumeSet("crystals");
256 auto collimators = BDSAcceleratorModel::Instance()->VolumeSet("collimators");
257 for (auto lv : crystal->GetAllLogicalVolumes())
258 {
259 crystals->insert(lv);
260 collimators->insert(lv);
261 }
262}
263
265 G4double placementAngle,
266 G4bool left) const
267{
268 const BDSCrystalInfo* recipe = crystal->recipe;
269 G4double result = 0;
270 G4double factor = left ? 1.0 : -1.0;
271 switch (recipe->shape.underlying())
272 {
273 case BDSCrystalType::box:
274 {
275 result = 0.5*recipe->lengthZ * std::sin(placementAngle);
276 result += 0.5*recipe->lengthX * std::cos(placementAngle);
277 break;
278 }
279 case BDSCrystalType::cylinder:
280 case BDSCrystalType::torus:
281 {
282 G4double halfAngle = 0.5 * recipe->bendingAngleYAxis;
283 if (!BDS::IsFinite(halfAngle)) // like a box
284 {
285 result = 0.5*recipe->lengthZ * std::sin(placementAngle);
286 result += 0.5*recipe->lengthX * std::cos(placementAngle);
287 }
288 else
289 {
290 // use 2-vectors to do all the maths for us
291 // 2d vector from centre of curvature to middle of crystal
292 G4TwoVector a(recipe->BendingRadiusHorizontal(), 0);
293 G4TwoVector b(a); // copy it
294 b.rotate(-halfAngle); // rotate to the front face centre
295 // difference between these two is the vector from centre of crystal frame (which is the
296 // middle of the crystal half way along z) to the front centre face
297 G4TwoVector dxy = b - a;
298 // construct a perpendicular vector for the width along the front face
299 // 'factor' is because for the left crystal we want the right inside front face and the left inside
300 // front face for the right crystal
301 G4TwoVector frontFaceX = dxy.orthogonal() * factor;
302 // 1/2 the width x this unit vector gives a vector from the centre of the front face
303 // to the inside front edge
304 G4TwoVector frontCentreToEdge = frontFaceX.unit() * 0.5*recipe->lengthX;
305 // add this to the one from the middle of the crystal
306 G4TwoVector resultV = dxy + frontCentreToEdge;
307 // update by placement angle
308 resultV.rotate(-factor*placementAngle);
309 // result is horizontal (x) component of this - the sign should be right throughout
310 result = -factor * resultV.x();
311 }
312 break;
313 }
314 default:
315 {break;}
316 }
317
318 return result;
319}
Abstract class that represents a component of an accelerator.
virtual G4String Material() const
Return the name of a material associated with the component - ie the primary material.
void SetAcceleratorVacuumLogicalVolume(G4LogicalVolume *accVacLVIn)
const G4String name
Const protected member variable that may not be changed by derived classes.
virtual void SetOutputFaceNormal(const G4ThreeVector &output)
Allow updating of face normals. Virtual so derived class may apply it to daughters.
static G4double lengthSafety
Useful variable often used in construction.
virtual std::set< G4LogicalVolume * > GetAcceleratorVacuumLogicalVolumes() const
Access the 'vacuum' volume(s) in this component if any. Default is empty set.
BDSBeamPipeInfo * GetBeamPipeInfo() const
virtual void SetInputFaceNormal(const G4ThreeVector &input)
Allow updating of face normals. Virtual so derived class may apply it to daughters.
G4double chordLength
Protected member variable that can be modified by derived classes.
BDSBeamPipeInfo * beamPipeInfo
Optional beam pipe recipe that is written out to the survey if it exists.
std::set< G4LogicalVolume * > * VolumeSet(const G4String &name)
Returns pointer to a set of logical volumes. If no set by that name exits, create it.
The main interface for using the beam pipe factories.
static BDSBeamPipeFactory * Instance()
Singleton accessor.
Holder class for all information required to describe a beam pipe model.
G4double IndicativeRadiusInner() const
Return an indicative inner extent for the beam pipe vacuum.
G4Material * beamPipeMaterial
Public member for direct access.
A holder class for a piece of beam pipe geometry.
G4LogicalVolume * GetVacuumLogicalVolume() const
Access the vacuum volume to set fields and limits.
G4ThreeVector InputFaceNormal() const
Accessor.
G4ThreeVector OutputFaceNormal() const
Accessor.
void RegisterCrystalLVs(const BDSCrystal *crystal) const
Register logical volumes in crystals and collimator sets in accelerator model.
BDSCrystalInfo * crystalInfoRight
Model associated with right crystal.
BDSCollimatorCrystal()=delete
No default constructor.
virtual G4String Material() const
Override base class version and return crystal material.
BDSCrystalInfo * crystalInfoLeft
Model associated with left crystal.
virtual void Build()
Construct geometry.
G4double TransverseOffsetToEdge(const BDSCrystal *crystal, G4double placementAngle, G4bool left) const
void LongitudinalOverlap(const BDSExtent &extCrystal, G4double crystalAngle, const G4String &side) const
Abstract base class for crystal factory classes.
BDSCrystal * CreateCrystal(const G4String &nameIn, const BDSCrystalInfo *recipe)
Main interface to create a crystal.
Holder for all information required to create a crystal.
G4double lengthZ
Z dimension.
G4double lengthX
X dimension.
BDSCrystalType shape
Shape of geometry.
G4double bendingAngleYAxis
Bending angle about Y axis.
An object for both a crystal from a factory.
Definition BDSCrystal.hh:39
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
G4bool Encompasses(const G4ThreeVector &point) const
Return whether the extent encompasses the point. True if point lies inside the extent.
Definition BDSExtent.cc:189
G4LogicalVolume * GetContainerLogicalVolume() const
Accessor - see member for more info.
void InheritExtents(BDSGeometryComponent const *const anotherComponent)
Update the extents of this object with those of another object.
G4RotationMatrix * GetPlacementRotation() const
Accessor - see member for more info.
BDSExtent GetExtent() const
Accessor - see member for more info.
void RegisterRotationMatrix(G4RotationMatrix *rotationMatrix)
void RegisterDaughter(BDSGeometryComponent *anotherComponent)
virtual std::set< G4LogicalVolume * > GetAllLogicalVolumes() const
Access all logical volumes belonging to this component.
void RegisterPhysicalVolume(G4VPhysicalVolume *physicalVolume)
G4VSolid * GetContainerSolid() const
Accessor - see member for more info.
G4ThreeVector GetPlacementOffset() const
Accessor - see member for more info.
type underlying() const
return underlying value (can be used in switch statement)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())