BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSCrystalFactory.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 "BDSColours.hh"
20#include "BDSCrystal.hh"
21#include "BDSCrystalFactory.hh"
22#include "BDSCrystalInfo.hh"
23#include "BDSDebug.hh"
24#include "BDSException.hh"
25#include "BDSGlobalConstants.hh"
26#include "BDSMaterials.hh"
27#include "BDSUtilities.hh"
28
29#include "G4Box.hh"
30#include "G4DisplacedSolid.hh"
31#include "G4ExtrudedSolid.hh"
32#include "G4LogicalVolume.hh"
33#include "G4Material.hh"
34#include "G4RotationMatrix.hh"
35#include "G4String.hh"
36#include "G4ThreeVector.hh"
37#include "G4Torus.hh"
38#include "G4Tubs.hh"
39#include "G4TwoVector.hh"
40#include "G4Types.hh"
41#include "G4UserLimits.hh"
42#include "G4VisAttributes.hh"
43#include "G4Version.hh"
44
45#include "CLHEP/Units/SystemOfUnits.h"
46
47#include <algorithm>
48#include <cmath>
49#include <memory>
50#include <set>
51#include <vector>
52
53// only use the crystal extensions if using 10.4.p00 upwards
54#if G4VERSION_NUMBER > 1039
55#include "G4ExtendedMaterial.hh"
56#include "G4CrystalExtension.hh"
57#include "G4CrystalUnitCell.hh"
58#include "G4ChannelingMaterialData.hh"
59#include "G4LogicalCrystalVolume.hh"
60#endif
61
62BDSCrystalFactory::BDSCrystalFactory():
63 maxStepFactor(1.1),
64 nPoints(30)
65{
66 CleanUp();
67}
68
70{
72 crystalSolid = nullptr;
73 crystalLV = nullptr;
74 placementOffset = G4ThreeVector();
75 placementRotation = nullptr;
76}
77
79 const BDSCrystalInfo* recipe)
80{
81 CleanUp();
82
83 BDSCrystal* result = nullptr;
84 switch(recipe->shape.underlying())
85 {
86 case BDSCrystalType::box:
87 {result = CreateCrystalBox(name, recipe); break;}
88 case BDSCrystalType::cylinder:
89 {result = CreateCrystalCylinder(name, recipe); break;}
90 case BDSCrystalType::torus:
91 {result = CreateCrystalTorus(name, recipe); break;}
92 default:
93 {break;}
94 }
95 return result;
96}
97
98void BDSCrystalFactory::CommonConstruction(const G4String& nameIn,
99 const BDSCrystalInfo* recipe)
100{
101 allSolids.insert(crystalSolid);
102
103 // only in g4.10.4 onwards do we build the crystal extensions - otherwise regular LV
104#if G4VERSION_NUMBER > 1039
105 G4ExtendedMaterial* crystalMat = new G4ExtendedMaterial(nameIn+"_crystal.material", recipe->material);
106
107 crystalMat->RegisterExtension(std::unique_ptr<G4CrystalExtension>(new G4CrystalExtension(crystalMat)));
108 G4CrystalExtension* crystalExtension = dynamic_cast<G4CrystalExtension*>(crystalMat->RetrieveExtension("crystal"));
109
110 G4CrystalUnitCell* uCell = new G4CrystalUnitCell(recipe->sizeA,
111 recipe->sizeB,
112 recipe->sizeC,
113 recipe->alpha,
114 recipe->beta,
115 recipe->gamma,
116 recipe->spaceGroup);
117 crystalExtension->SetUnitCell(uCell);
118
119 crystalMat->RegisterExtension(std::unique_ptr<G4ChannelingMaterialData>(new G4ChannelingMaterialData("channeling")));
120
121 G4ChannelingMaterialData* crystalChannelingData = (G4ChannelingMaterialData*)crystalMat->RetrieveExtension("channeling");
122 G4String fileName = BDS::GetFullPath(recipe->data);
123 if (!BDS::FileExists(fileName + "_pot.txt"))
124 {throw BDSException(__METHOD_NAME__, "No such crystal data files beginning with: \"" + fileName + "\"");}
125#ifdef BDSDEBUG
126 G4cout << __METHOD_NAME__ << "Raw data path: " << recipe->data << G4endl;
127 G4cout << __METHOD_NAME__ << "Using crystal data: " << fileName << G4endl;
128#endif
129 crystalChannelingData->SetFilename(fileName);
130
131 // -ve here due to right handed coordinate system + convention of +ve bending angle bends
132 // away from beam axis. This convention is also implied by when we do a -ve x axis rotation
133 // for cylindrical crystals (including the start and sweep angle).
134 G4double bendingRadius = -recipe->BendingRadiusHorizontal();
135 crystalChannelingData->SetBR(bendingRadius);
136
137 crystalLV = new G4LogicalCrystalVolume(crystalSolid,
138 crystalMat,
139 nameIn + "_crystal_lv",
140 nullptr, nullptr, nullptr,
141 true, 0, 0, 0, recipe->miscutAngleY);
142
143 BDSAcceleratorModel::Instance()->VolumeSet("crystals")->insert(crystalLV);
144#else
145 // build logical volumes
146 crystalLV = new G4LogicalVolume(crystalSolid,
147 recipe->material,
148 nameIn + "_crystal_lv");
149#endif
150 allLogicalVolumes.insert(crystalLV);
151
153 SetUserLimits(recipe->lengthZ);
154}
155
157{
158 G4VisAttributes* crysVisAttr = new G4VisAttributes(*BDSColours::Instance()->GetColour("crystal"));
159 crysVisAttr->SetVisibility(true);
160 crysVisAttr->SetForceLineSegmentsPerCircle(200);
161 allVisAttributes.insert(crysVisAttr);
162 crystalLV->SetVisAttributes(crysVisAttr);
163}
164
166{
167 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
168 //copy the default and update with the length of the object rather than the default 1m
169 G4UserLimits* ul = BDS::CreateUserLimits(defaultUL, length*maxStepFactor);
170
171 if (ul != defaultUL) // if it's not the default register it
172 {allUserLimits.insert(ul);}
173 crystalLV->SetUserLimits(ul);
174}
175
177 const BDSExtent& extent)
178{
179 // build the BDSCrystal instance and return it
180 BDSCrystal* aCrystal = new BDSCrystal(recipe, crystalSolid, crystalLV,
181 extent, placementOffset, placementRotation);
182
183 // register objects
184 aCrystal->RegisterSolid(allSolids);
185 aCrystal->RegisterSensitiveVolume(crystalLV, BDSSDType::collimatorcomplete);
186 aCrystal->RegisterUserLimits(allUserLimits);
187 aCrystal->RegisterVisAttributes(allVisAttributes);
188 aCrystal->ExcludeLogicalVolumeFromBiasing(crystalLV); // can't double bias one LV ie with generic biasing
189
190 return aCrystal;
191}
192
194 const BDSCrystalInfo* recipe)
195{
196 crystalSolid = new G4Box(nameIn + "_solid",
197 recipe->lengthX * 0.5,
198 recipe->lengthY * 0.5,
199 recipe->lengthZ * 0.5);
200
201 CommonConstruction(nameIn, recipe);
202
203 placementOffset = G4ThreeVector(0, 0, 0);
204 BDSExtent ext = BDSExtent(recipe->lengthX * 0.5,
205 recipe->lengthY * 0.5,
206 recipe->lengthZ * 0.5);
207
208 return BuildCrystalObject(recipe, ext); // no placement offset - leave as default
209}
210
212 G4double& startAngle,
213 G4double& sweepAngle) const
214{
215 if (bendingAngle >= 0)
216 {
217 startAngle = CLHEP::twopi - 0.5 * bendingAngle;
218 sweepAngle = bendingAngle;
219 }
220 else
221 {
222 startAngle = CLHEP::pi - 0.5*std::abs(bendingAngle);
223 sweepAngle = std::abs(bendingAngle);
224 }
225}
226
228 G4double xBendingRadius,
229 G4double xThickness,
230 const BDSCrystalInfo* recipe) const
231{
232 // calculate horizontal extents - do in +ve version and flip for -ve
233 G4double xHi = xBendingRadius - (xBendingRadius - xThickness*0.5)*std::cos(std::abs(xBendingAngle)*0.5);
234 G4double xLow = -(xThickness * 0.5);
235 if (xBendingAngle > 0)
236 {
237 std::swap(xHi, xLow);
238 xHi *= -1;
239 xLow *= -1;
240 }
241 G4double dz = (xBendingRadius + xThickness*0.5) * std::sin(std::abs(xBendingAngle)*0.5);
242
243 // note this is the extent the partial cylinder would have with its intended placement.
244 // convenient for now but doesn't strictly match the solid which is built along the z axis.
245 BDSExtent ext = BDSExtent(xLow, xHi,
246 recipe->lengthY * 0.5, recipe->lengthY * 0.5,
247 -dz, dz);
248 return ext;
249}
250
252 const BDSCrystalInfo* recipe)
253{
254 G4double ba = recipe->bendingAngleYAxis; // bending angle
255
256 // if no bending angle, create a box as that's all we can create
257 if (!BDS::IsFinite(ba))
258 {return CreateCrystalBox(nameIn, recipe);}
259
260 G4double xBR = std::abs(recipe->BendingRadiusHorizontal());
261 G4double thickness = recipe->lengthX;
262
263 // calculate start angle and sweep angle
264 G4double startAngle;
265 G4double sweepAngle;
266 CalculateSolidAngles(ba, startAngle, sweepAngle);
267
268 G4Tubs* rawShape = new G4Tubs(nameIn + "_solid",
269 xBR - 0.5*thickness,
270 xBR + 0.5*thickness,
271 recipe->lengthY*0.5,
272 startAngle,
273 sweepAngle);
274
275 // crystal channelling only works in local unitX of a given solid which
276 // makes it impossible to use a cylinder. we cheat by using a G4DisplacedSolid
277 // that's a class used internally by geant4's boolean solids to rotate and translate
278 // the frame of a solid. another option was an intersection with a big box, but
279 // geant4 can't handle this.
280 G4RotationMatrix* relativeRotation = new G4RotationMatrix();
281 relativeRotation->rotateX(-CLHEP::halfpi);
282 G4double bendingRadiusH = recipe->BendingRadiusHorizontal(); // includes sign
283 G4ThreeVector offset(-bendingRadiusH,0,0);
284 crystalSolid = new G4DisplacedSolid(nameIn + "_shifted_solid",
285 rawShape,
286 relativeRotation,
287 offset);
288
289 CommonConstruction(nameIn, recipe);
290 placementOffset = G4ThreeVector();
291 BDSExtent ext = CalculateExtents(ba, xBR, thickness, recipe);
292 return BuildCrystalObject(recipe, ext);
293}
294
296 const BDSCrystalInfo* recipe)
297{
298 G4double xBA = recipe->bendingAngleYAxis; // bending angle horizontal
299 G4double xBR = std::abs(recipe->BendingRadiusHorizontal());
300 G4double zBA = recipe->bendingAngleZAxis;
301 G4double zBR = std::abs(recipe->BendingRadiusVertical());
302
303 // if no bending angle, create a box as that's all we can create
304 if (!BDS::IsFinite(xBA))
305 {return CreateCrystalBox(nameIn, recipe);}
306
307 G4double thickness = recipe->lengthX;
308
309 // calculate start angle and sweep angle
310 G4double startAngle;
311 G4double sweepAngle;
312 CalculateSolidAngles(xBA, startAngle, sweepAngle);
313
314 G4double angFraction = sweepAngle / (G4double)nPoints;
315 std::vector<G4TwoVector> points;
316
317 G4double innerRadius = xBR - thickness*0.5;
318 for (G4int i = 0; i < nPoints; i++)
319 {
320 G4double ang = startAngle + (G4double)i * angFraction;
321 G4double x = innerRadius * std::cos(ang);
322 G4double y = innerRadius * std::sin(ang);
323 points.emplace_back(G4TwoVector(x,y));
324 }
325 G4double outerRadius = xBR + thickness*0.5;
326 for (G4int i = nPoints; i > 0; i--)
327 {
328 G4double ang = startAngle + (G4double)i * angFraction;
329 G4double x = outerRadius * std::cos(ang);
330 G4double y = outerRadius * std::sin(ang);
331 points.emplace_back(G4TwoVector(x,y));
332 }
333
334 G4double zStartAngle = CLHEP::twopi - 0.5 * zBA;
335 G4double zSweepAngle = zBA;
336
337 // create vertical z sections, accumulate max/min shift at same time
338 G4double xmin = 0;
339 G4double xmax = 0;
340 G4double zAngFraction = zSweepAngle / (G4double)nPoints;
341 std::vector<G4ExtrudedSolid::ZSection> zSections;
342 for (G4int i = 0; i < nPoints; i++)
343 {
344 G4double zAng = zStartAngle + (G4double)i * zAngFraction;
345 G4double z = zBR * std::sin(zAng);
346 G4double x = zBR - (zBR * std::cos(zAng));
347 if (zBA < 0)
348 {
349 z *= -1;
350 x *= -1;
351 }
352 xmin = std::min(xmin, x);
353 xmax = std::max(xmax, x);
354 zSections.emplace_back(G4ExtrudedSolid::ZSection(z, G4TwoVector(x, 0), 1));
355 }
356
357 G4ExtrudedSolid* rawShape = new G4ExtrudedSolid(nameIn + "_solid",
358 points,
359 zSections);
360
361 // crystal channelling only works in local unitX of a given solid which
362 // makes it impossible to use a cylinder. we cheat by using a G4DisplacedSolid
363 // that's a class used internally by geant4's boolean solids to rotate and translate
364 // the frame of a solid. another option was an intersection with a big box, but
365 // geant4 can't handle this.
366 G4RotationMatrix* relativeRotation = new G4RotationMatrix();
367 relativeRotation->rotateX(-CLHEP::halfpi);
368 G4ThreeVector offset(0,0,0);
369 crystalSolid = new G4DisplacedSolid(nameIn + "_shifted_solid",
370 rawShape,
371 relativeRotation,
372 offset);
373
374 CommonConstruction(nameIn, recipe);
375
376 // placement offset - no rotation as we've rotated the solid internally
377 placementOffset = G4ThreeVector(-1*recipe->BendingRadiusHorizontal(), 0, 0);
378
379 BDSExtent ext = CalculateExtents(xBA, xBR, thickness, recipe);
380 G4double xLow = ext.XNeg() + xmin;
381 G4double xHi = ext.XPos() + xmax;
382 BDSExtent extTorus = BDSExtent(xLow, xHi, ext.YNeg(), ext.YPos(), ext.ZNeg(), ext.ZPos());
383
384 return BuildCrystalObject(recipe, extTorus);
385}
std::set< G4LogicalVolume * > * VolumeSet(const G4String &name)
Returns pointer to a set of logical volumes. If no set by that name exits, create it.
static BDSColours * Instance()
singleton pattern
Definition BDSColours.cc:32
void CommonConstruction(const G4String &nameIn, const BDSCrystalInfo *recipe)
Common construction tasks.
const G4int nPoints
Number of points to split torus into.
void CalculateSolidAngles(G4double bendingAngle, G4double &startAngle, G4double &sweepAngle) const
const G4double maxStepFactor
Fraction of length for maximum step in user limits.
BDSCrystal * CreateCrystalBox(const G4String &nameIn, const BDSCrystalInfo *recipe)
Create box geometry for a crystal.
BDSCrystal * CreateCrystalCylinder(const G4String &nameIn, const BDSCrystalInfo *recipe)
Create cylinder geometry for a crystal.
BDSExtent CalculateExtents(G4double bendingAngle, G4double xBendingRadius, G4double xThickness, const BDSCrystalInfo *recipe) const
Produce an extent for a curved crystal.
BDSCrystal * CreateCrystalTorus(const G4String &nameIn, const BDSCrystalInfo *recipe)
Create torus geometry for a cyrstal.
BDSCrystal * CreateCrystal(const G4String &nameIn, const BDSCrystalInfo *recipe)
Main interface to create a crystal.
BDSCrystal * BuildCrystalObject(const BDSCrystalInfo *recipe, const BDSExtent &extent)
build beampipe and register logical volumes
void SetUserLimits(G4double length)
Set user limits.
void SetVisAttributes()
Set visual attributes.
Holder for all information required to create a crystal.
G4String data
Potential data path.
G4double lengthZ
Z dimension.
G4Material * material
Material.
G4double bendingAngleZAxis
Bending angle about Z axis.
G4double lengthX
X dimension.
BDSCrystalType shape
Shape of geometry.
G4double bendingAngleYAxis
Bending angle about Y axis.
G4double lengthY
Y dimension.
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
G4double XPos() const
Accessor.
Definition BDSExtent.hh:65
G4double ZPos() const
Accessor.
Definition BDSExtent.hh:69
G4double XNeg() const
Accessor.
Definition BDSExtent.hh:64
G4double YNeg() const
Accessor.
Definition BDSExtent.hh:66
G4double YPos() const
Accessor.
Definition BDSExtent.hh:67
G4double ZNeg() const
Accessor.
Definition BDSExtent.hh:68
virtual void FactoryBaseCleanUp()
Empty containers for next use - factories are never deleted so can't rely on scope.
void RegisterUserLimits(G4UserLimits *userLimit)
virtual void ExcludeLogicalVolumeFromBiasing(G4LogicalVolume *lv)
void RegisterVisAttributes(G4VisAttributes *visAttribute)
void RegisterSolid(G4VSolid *solid)
void RegisterSensitiveVolume(G4LogicalVolume *sensitiveVolume, BDSSDType sensitivityType)
static BDSGlobalConstants * Instance()
Access method.
type underlying() const
return underlying value (can be used in switch statement)
G4String GetFullPath(G4String filename, bool excludeNameFromPath=false, bool useCWDForPrefix=false)
G4UserLimits * CreateUserLimits(G4UserLimits *defaultUL, G4double length, G4double fraction=1.6)
G4bool FileExists(const G4String &filename)
Checks if filename exists.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())