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) Royal Holloway,
3University of London 2001 - 2023.
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 "BDSAcceleratorComponent.hh"
20#include "BDSAcceleratorModel.hh"
21#include "BDSBeamPipe.hh"
22#include "BDSBeamPipeFactory.hh"
23#include "BDSBeamPipeInfo.hh"
24#include "BDSCollimatorCrystal.hh"
25#include "BDSCrystalFactory.hh"
26#include "BDSCrystalInfo.hh"
27#include "BDSDebug.hh"
28#include "BDSException.hh"
29#include "BDSGeometryComponent.hh"
30#include "BDSSDType.hh"
31#include "BDSUtilities.hh"
32#include "BDSWarning.hh"
33
34#include "globals.hh" // geant4 globals / types
35#include "G4Material.hh"
36#include "G4PVPlacement.hh"
37#include "G4RotationMatrix.hh"
38#include "G4ThreeVector.hh"
39
40#include <cmath>
41
42BDSCollimatorCrystal::BDSCollimatorCrystal(const G4String& nameIn,
43 G4double lengthIn,
44 BDSBeamPipeInfo* beamPipeInfoIn,
45 BDSCrystalInfo* crystalInfoLeftIn,
46 BDSCrystalInfo* crystalInfoRightIn,
47 G4double halfGapLeftIn,
48 G4double halfGapRightIn,
49 G4double angleYAxisLeftIn,
50 G4double angleYAxisRightIn):
51 BDSAcceleratorComponent(nameIn, lengthIn, 0, "crystalcol", beamPipeInfoIn),
52 crystalInfoLeft(crystalInfoLeftIn),
53 crystalInfoRight(crystalInfoRightIn),
54 halfGapLeft(halfGapLeftIn),
55 halfGapRight(halfGapRightIn),
56 angleYAxisLeft(angleYAxisLeftIn),
57 angleYAxisRight(angleYAxisRightIn),
58 crystalLeft(nullptr),
59 crystalRight(nullptr)
60{
61#ifdef USE_SIXTRACKLINK
62 G4cout << "Left " << crystalInfoLeftIn << G4endl;
63 G4cout << "Right " << crystalInfoRightIn << G4endl;
64#endif
65 if (crystalInfoLeft)
66 {crystalInfoLeft->bendingAngleYAxis *= -1.0;}
67#ifdef USE_SIXTRACKLINK
68 G4cout << "halfGapLeftIn " << halfGapLeftIn << G4endl;
69 G4cout << "halfGapRightIn " << halfGapRightIn << G4endl;
70#endif
71}
72
73BDSCollimatorCrystal::~BDSCollimatorCrystal()
74{
75 delete crystalInfoLeft;
76 delete crystalInfoRight;
77}
78
80{
81 // we don't need to set user limits because that is done in the beam pipe factory
83 BDSBeamPipe* pipe = factory->CreateBeamPipe(name,
86
87 RegisterDaughter(pipe);
88
89 // make the beam pipe container, this object's container
90 containerLogicalVolume = pipe->GetContainerLogicalVolume();
91 containerSolid = pipe->GetContainerSolid();
92
93 // register vacuum volume (for biasing)
95
96 // update extents
97 InheritExtents(pipe);
98
99 // update faces
102
104 if (crystalInfoLeft)
105 {
106 crystalLeft = fac->CreateCrystal(name + "_left", crystalInfoLeft);
107 RegisterDaughter(crystalLeft);
108 RegisterCrystalLVs(crystalLeft);
109 }
111 {
112 crystalRight = fac->CreateCrystal(name + "_right", crystalInfoRight);
113 RegisterDaughter(crystalRight);
114 RegisterCrystalLVs(crystalRight);
115 }
116 delete fac;
117
118 if (crystalLeft)
119 {
120 G4ThreeVector objectOffset = crystalLeft->GetPlacementOffset();
121 G4double dx = TransverseOffsetToEdge(crystalLeft, angleYAxisLeft, true);
122 G4ThreeVector colOffsetL = G4ThreeVector(halfGapLeft+dx,0,0);
123 G4ThreeVector placementOffsetL = objectOffset + colOffsetL; // 'L' in p offset to avoid class with BDSGeometry Component member
124 G4RotationMatrix* placementRot = crystalLeft->GetPlacementRotation();
125 if (BDS::IsFinite(angleYAxisLeft))
126 {
127 if (!placementRot)
128 {
129 placementRot = new G4RotationMatrix();
130 RegisterRotationMatrix(placementRot);
131 }
132 G4ThreeVector localUnitY = G4ThreeVector(0,1,0).transform(*placementRot);
133 placementRot->rotate(-angleYAxisLeft, localUnitY); // rotate about local unitY
134 // note minus sign to rotate *away* from centre
135 }
136
137 // check if it'll fit..
138 // use the collOffsetL as the specific solid might require a large offset (e.g. cylinder or torus)
139 BDSExtent extShifted = (crystalLeft->GetExtent()).Translate(colOffsetL);
140 BDSExtent thisExtent = GetExtent(); // actually outer extent of beam pipe
141 G4bool safe = thisExtent.Encompasses(extShifted);
142 // second stricter check - TODO - use aperture check in future
145 0.5*chordLength);
146 G4bool safe2 = innerRadius.Encompasses(extShifted);
147 if (!safe || !safe2)
148 {BDS::Warning(__METHOD_NAME__, "Left crystal potential overlap in component \"" + name +"\"");}
149 LongitudinalOverlap(crystalLeft->GetExtent(), angleYAxisLeft, "Left");
150
151#ifdef USE_SIXTRACKLINK
152 G4cout << "left crystal placement offset " << placementOffsetL << G4endl;
153 if (placementRot)
154 {
155 G4cout << "left crystal placement rotation " << *placementRot << G4endl;
156 }
157#endif
158
159 G4LogicalVolume* vac = *(GetAcceleratorVacuumLogicalVolumes().begin()); // take the first one
160 auto cL = new G4PVPlacement(placementRot,
161 placementOffsetL,
162 crystalLeft->GetContainerLogicalVolume(),
163 name + "_crystal_left_pv",
164 vac,
165 false,
166 0,
167 true); // always check
169#ifdef USE_SIXTRACKLINK
170 G4cout << "Placement of left crystal " << placementOffsetL << G4endl;
171#endif
172 }
173 if (crystalRight)
174 {
175 G4ThreeVector objectOffset = crystalRight->GetPlacementOffset();
176 G4double dx = TransverseOffsetToEdge(crystalRight, angleYAxisRight, false);
177 G4ThreeVector colOffsetR = G4ThreeVector(-(halfGapRight+dx),0,0); // -ve as r.h. coord system
178 G4ThreeVector placementOffsetL = objectOffset + colOffsetR;
179 G4RotationMatrix* placementRot = crystalRight->GetPlacementRotation();
180 if (BDS::IsFinite(angleYAxisRight))
181 {
182 if (!placementRot)
183 {
184 placementRot = new G4RotationMatrix();
185 RegisterRotationMatrix(placementRot);
186 }
187 G4ThreeVector localUnitY = G4ThreeVector(0,1,0).transform(*placementRot);
188 placementRot->rotate(angleYAxisRight, localUnitY); // rotate about local unitY
189 }
190
191 // check if it'll fit..
192 BDSExtent extShifted = (crystalRight->GetExtent()).Translate(colOffsetR);
193 BDSExtent thisExtent = GetExtent();
194 G4bool safe = thisExtent.Encompasses(extShifted);
195 // second stricter check - TODO - use aperture check in future
198 0.5*chordLength);
199 G4bool safe2 = innerRadius.Encompasses(extShifted);
200 if (!safe || !safe2)
201 {BDS::Warning(__METHOD_NAME__, "Right crystal potential overlap in component \"" + name +"\"");}
202 LongitudinalOverlap(crystalRight->GetExtent(), angleYAxisRight, "Right");
203
204#ifdef USE_SIXTRACKLINK
205 G4cout << "right crystal placement offset " << placementOffsetL << G4endl;
206 if (placementRot)
207 {
208 G4cout << "right crystal placement rotation " << *placementRot << G4endl;
209 }
210#endif
211
212 G4LogicalVolume* vac = *(GetAcceleratorVacuumLogicalVolumes().begin()); // take the first one
213 auto cR = new G4PVPlacement(placementRot,
214 placementOffsetL,
215 crystalRight->GetContainerLogicalVolume(),
216 name + "_crystal_right_pv",
217 vac,
218 false,
219 0,
220 true); // always check
222 }
223 if (!crystalLeft && !crystalRight)
224 {throw BDSException(__METHOD_NAME__, "Error in crystal construction in component \"" + name + "\"");}
225}
226
228{
229 auto bpMat = GetBeamPipeInfo()->beamPipeMaterial;
230 if (bpMat)
231 {return bpMat->GetName();}
232 else
233 {return BDSAcceleratorComponent::Material();} // none
234}
235
237 G4double crystalAngle,
238 const G4String& side) const
239{
240 G4double zExt = extCrystal.MaximumZ();
241 G4double dz = zExt*std::tan(crystalAngle);
242
243 G4bool overlap = 2*zExt + 2*std::abs(dz) > (chordLength - 2*lengthSafety);
244
245 if (overlap)
246 {
247 G4cout << "Crystal of length " << 2*zExt/CLHEP::mm << " mm is at angle "
248 << crystalAngle / CLHEP::mrad << " mrad and container is "
249 << chordLength/CLHEP::m << " m long." << G4endl;
250 throw BDSException(__METHOD_NAME__, side + " crystal won't fit in collimator due to rotation.");
251 }
252}
253
255{
256 auto crystals = BDSAcceleratorModel::Instance()->VolumeSet("crystals");
257 auto collimators = BDSAcceleratorModel::Instance()->VolumeSet("collimators");
258 for (auto lv : crystal->GetAllLogicalVolumes())
259 {
260 crystals->insert(lv);
261 collimators->insert(lv);
262 }
263}
264
266 G4double placementAngle,
267 G4bool left) const
268{
269 const BDSCrystalInfo* recipe = crystal->recipe;
270 G4double result = 0;
271 G4double factor = left ? 1.0 : -1.0;
272 switch (recipe->shape.underlying())
273 {
274 case BDSCrystalType::box:
275 {
276 result = 0.5*recipe->lengthZ * std::sin(placementAngle);
277 result += 0.5*recipe->lengthX * std::cos(placementAngle);
278 break;
279 }
280 case BDSCrystalType::cylinder:
281 case BDSCrystalType::torus:
282 {
283 G4double halfAngle = 0.5 * recipe->bendingAngleYAxis;
284 if (!BDS::IsFinite(halfAngle)) // like a box
285 {
286 result = 0.5*recipe->lengthZ * std::sin(placementAngle);
287 result += 0.5*recipe->lengthX * std::cos(placementAngle);
288 }
289 else
290 {
291 // use 2-vectors to do all the maths for us
292 // 2d vector from centre of curvature to middle of crystal
293 G4TwoVector a(recipe->BendingRadiusHorizontal(), 0);
294 G4TwoVector b(a); // copy it
295 b.rotate(-halfAngle); // rotate to the front face centre
296 // difference between these two is the vector from centre of crystal frame (which is the
297 // middle of the crystal half way along z) to the front centre face
298 G4TwoVector dxy = b - a;
299 // construct a perpendicular vector for the width along the front face
300 // 'factor' is because for the left crystal we want the right inside front face and the left inside
301 // front face for the right crystal
302 G4TwoVector frontFaceX = dxy.orthogonal() * factor;
303 // 1/2 the width x this unit vector gives a vector from the centre of the front face
304 // to the inside front edge
305 G4TwoVector frontCentreToEdge = frontFaceX.unit() * 0.5*recipe->lengthX;
306 // add this to the one from the middle of the crystal
307 G4TwoVector resultV = dxy + frontCentreToEdge;
308 // update by placement angle
309 resultV.rotate(-factor*placementAngle);
310 // result is horizontal (x) component of this - the sign should be right throughout
311 result = -factor * resultV.x();
312 }
313 break;
314 }
315 default:
316 {break;}
317 }
318
319 return result;
320}
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.
Definition: BDSBeamPipe.hh:45
G4LogicalVolume * GetVacuumLogicalVolume() const
Access the vacuum volume to set fields and limits.
Definition: BDSBeamPipe.hh:65
G4ThreeVector InputFaceNormal() const
Accessor.
Definition: BDSBeamPipe.hh:73
G4ThreeVector OutputFaceNormal() const
Accessor.
Definition: BDSBeamPipe.hh:74
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:40
General exception with possible name of object and message.
Definition: BDSException.hh:35
Holder for +- extents in 3 dimensions.
Definition: BDSExtent.hh:39
G4bool Encompasses(const G4ThreeVector &point) const
Return whether the extent encompasses the point. True if point lies inside the extent.
Definition: BDSExtent.cc:190
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())