BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
BDSVisFieldModel.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2022.
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 "BDSColourScaleMagma.hh"
20#include "BDSColourScaleViridis.hh"
21#include "BDSFieldQueryForVis.hh"
22#include "BDSFieldQueryInfo.hh"
23#include "BDSVisFieldModel.hh"
24
25#include "G4ArrowModel.hh"
26#include "G4Box.hh"
27#include "G4Circle.hh"
28#include "G4Colour.hh"
29#include "G4String.hh"
30#include "G4ThreeVector.hh"
31#include "G4Types.hh"
32#include "G4VGraphicsScene.hh"
33#include "G4VMarker.hh"
34#include "G4VisAttributes.hh"
35#include "BDSWarning.hh"
36
37#include <algorithm>
38#include <array>
39#include <cmath>
40#include <limits>
41#include <cstdio>
42#include <string>
43#include <utility>
44#include <vector>
45
47
48BDSVisFieldModel::BDSVisFieldModel(const std::vector<BDSFieldQueryInfo*>& queriesIn):
49 queries(queriesIn),
50 pointVisB(nullptr),
51 pointVisE(nullptr)
52{
53 fGlobalTag = "BDSVisFieldModel";
54 fGlobalDescription = "Field view #" + std::to_string(instanceCounter);
55 instanceCounter++;
56
57 auto bFieldColour = BDSColourScaleViridis();
58 pointVisB = new G4VisAttributes(bFieldColour.GetValue(0));
59 auto eFieldColour = BDSColourScaleMagma();
60 pointVisE = new G4VisAttributes(eFieldColour.GetValue(0));
61
62 pointVisB->SetForceSolid(true); // no default argument for g4 V < 10.3, so specify true always
63 pointVisE->SetForceSolid(true);
64}
65
66BDSVisFieldModel::~BDSVisFieldModel()
67{
68 delete pointVisB;
69 delete pointVisE;
70}
71
72void BDSVisFieldModel::DescribeYourselfTo(G4VGraphicsScene& sceneHandler)
73{
74 auto bFieldColour = BDSColourScaleViridis();
75 auto eFieldColour = BDSColourScaleMagma();
76
77 BDSFieldQueryForVis querier;
78 for (const auto& query : queries)
79 {
80 querier.QueryField(query);
81 G4double maxFieldB = querier.MaxFieldB();
82 G4double maxFieldE = querier.MaxFieldE();
83 const std::vector<std::array<G4double, 9>> xyzBEs = querier.Values();
84
85 G4double arrowLength = CalculateArrowLength(query);
86 if (arrowLength > 0.01 * std::numeric_limits<G4double>::max()) // This shouldn't happen, but is a sanity check
87 {arrowLength = 1e-3 * sceneHandler.GetExtent().GetExtentRadius();}
88
89 G4double arrowWidth = 0.3*arrowLength;
90 G4double pointSize = std::max(0.1*arrowWidth, 5.0);
91
92 G4bool drawArrows = query->drawArrows;
93 G4bool drawBoxes = query->drawBoxes;
94
95 if (!drawArrows && !drawBoxes)
96 {
97 BDS::Warning("Neither drawArrows or drawBoxes are turned on in query \"" + query->name + "\" -> skipping");
98 continue; // skip this query
99 }
100
101 G4double boxAlpha = query->boxAlpha;
102 if (boxAlpha <= 0)
103 {
104 BDS::Warning("boxAlpha in query \"" + query->name + "\" must be > 0. Setting to 0.01");
105 boxAlpha = 0.01;
106 }
107 if (boxAlpha > 1)
108 {
109 BDS::Warning("boxAlpha in query \"" + query->name + "\" must be <= 1. Setting to 1.0");
110 boxAlpha = 1.0;
111 }
112
113 G4ThreeVector boxHalfSize = BoxHalfSize(query);
114 G4AffineTransform qTransform = query->globalTransform;
115 G4RotationMatrix qRotation = qTransform.NetRotation().inverse();
116 G4Box aBox("b", boxHalfSize.x(), boxHalfSize.y(), boxHalfSize.z());
117 G4Polyhedron* baseBoxPoly = aBox.GetPolyhedron();
118
119 if (query->queryMagnetic)
120 {
121 G4Colour boxZeroColourB = pointVisB->GetColor();
122 G4Colour boxZeroColourB2(boxZeroColourB.GetRed(), boxZeroColourB.GetGreen(), boxZeroColourB.GetBlue(), boxAlpha);
123 G4VisAttributes* boxZeroVisB = new G4VisAttributes(boxZeroColourB2);
124 G4String arrowPrefix = query->name + "_B_";
125 for (const auto& xyzBE: xyzBEs)
126 {
127 // x y z Bx By Bz Ex Ey Ez
128 G4ThreeVector B(xyzBE[3], xyzBE[4], xyzBE[5]);
129 G4ThreeVector unitB = B.unit();
130 G4double bMag = B.mag();
131 G4ThreeVector midPoint(xyzBE[0], xyzBE[1], xyzBE[2]);
132
133 // GetPolyhedron returns a pointer, but it's the same object always
134 // and there's no interface to allow rebuilding that polyhedron, so
135 // if we translate it we compound that transform. No method to just
136 // set the transform, so have to copy it.
137 G4Polyhedron boxPoly(*baseBoxPoly);
138 boxPoly.Transform(G4Transform3D(qRotation, midPoint));
139
140 if (bMag == 0)
141 {
142 sceneHandler.BeginPrimitives();
143 if (drawArrows)
144 {
145 G4Circle aPoint({xyzBE[0], xyzBE[1], xyzBE[2]});
146 aPoint.SetVisAttributes(pointVisB);
147 aPoint.SetDiameter(G4VMarker::SizeType::world, pointSize);
148 aPoint.SetFillStyle(G4VMarker::FillStyle::filled);
149 sceneHandler.AddPrimitive(aPoint);
150 }
151 if (drawBoxes)
152 {
153 boxPoly.SetVisAttributes(boxZeroVisB);
154 sceneHandler.AddPrimitive(boxPoly);
155 }
156 sceneHandler.EndPrimitives();
157 continue;
158 } // don't add 0 field arrows
159 G4ThreeVector startPoint = midPoint - 0.5*arrowLength*unitB;
160 G4ThreeVector endPoint = midPoint + 0.5*arrowLength*unitB;
161 G4double normalisedValue = bMag / maxFieldB;
162 if (!std::isfinite(normalisedValue))
163 {normalisedValue = 0.0;}
164 G4Colour arrowColour = bFieldColour.GetValue(normalisedValue);
165 if (drawBoxes)
166 {
167 G4Colour boxColour(arrowColour.GetRed(), arrowColour.GetGreen(), arrowColour.GetBlue(), boxAlpha);
168 sceneHandler.BeginPrimitives();
169 boxPoly.SetVisAttributes(G4VisAttributes(boxColour));
170 sceneHandler.AddPrimitive(boxPoly);
171 sceneHandler.EndPrimitives();
172 }
173 if (drawArrows)
174 {
175 char buf[100];
176 sprintf(buf, "(%.2g, %.2g, %.2g)", xyzBE[0], xyzBE[1], xyzBE[2]);
177 std::string arrowName = buf;
178 G4ArrowModel FArrow(startPoint.x(), startPoint.y(), startPoint.z(),
179 endPoint.x(), endPoint.y(), endPoint.z(),
180 arrowWidth, arrowColour, arrowName);
181 FArrow.DescribeYourselfTo(sceneHandler);
182 }
183 }
184 delete boxZeroVisB;
185 }
186
187 if (query->queryElectric)
188 {
189 G4Colour boxZeroColourE = pointVisE->GetColor();
190 G4Colour boxZeroColourE2(boxZeroColourE.GetRed(),
191 boxZeroColourE.GetGreen(),
192 boxZeroColourE.GetBlue(),
193 boxAlpha);
194 G4VisAttributes* boxZeroVisE = new G4VisAttributes(boxZeroColourE2);
195 G4String arrowPrefix = query->name + "_E_";
196 for (const auto& xyzBE: xyzBEs)
197 {
198 // x y z Bx By Bz Ex Ey Ez
199 G4ThreeVector E(xyzBE[3], xyzBE[4], xyzBE[5]);
200 G4ThreeVector unitE = E.unit();
201 G4double eMag = E.mag();
202 G4ThreeVector midPoint(xyzBE[0], xyzBE[1], xyzBE[2]);
203
204 G4Polyhedron boxPoly(*baseBoxPoly);
205 boxPoly.Transform(G4Transform3D(qRotation, midPoint));
206 if (eMag == 0)
207 {
208 sceneHandler.BeginPrimitives();
209 if (drawArrows)
210 {
211 G4Circle aPoint({xyzBE[0], xyzBE[1], xyzBE[2]});
212 aPoint.SetVisAttributes(pointVisE);
213 aPoint.SetDiameter(G4VMarker::SizeType::world, pointSize);
214 aPoint.SetFillStyle(G4VMarker::FillStyle::filled);
215 sceneHandler.AddPrimitive(aPoint);
216 }
217 if (drawBoxes)
218 {
219 boxPoly.SetVisAttributes(boxZeroVisE);
220 sceneHandler.AddPrimitive(boxPoly);
221 }
222 sceneHandler.EndPrimitives();
223 continue;
224 } // don't add 0 field arrows
225 G4ThreeVector startPoint = midPoint - 0.5*arrowLength*unitE;
226 G4ThreeVector endPoint = midPoint + 0.5*arrowLength*unitE;
227 G4double normalisedValue = eMag / maxFieldE;
228 if (!std::isfinite(normalisedValue))
229 {normalisedValue = 0.0;}
230 G4Colour arrowColour = eFieldColour.GetValue(normalisedValue);
231 if (drawBoxes)
232 {
233 G4Colour boxColour(arrowColour.GetRed(), arrowColour.GetGreen(), arrowColour.GetBlue(), boxAlpha);
234 sceneHandler.BeginPrimitives();
235 boxPoly.SetVisAttributes(G4VisAttributes(boxColour));
236 sceneHandler.AddPrimitive(boxPoly);
237 sceneHandler.EndPrimitives();
238 }
239 if (drawArrows)
240 {
241 char buf[100];
242 sprintf(buf, "(%.2g, %.2g, %.2g)", xyzBE[0], xyzBE[1], xyzBE[2]);
243 std::string arrowName = buf;
244
245 G4ArrowModel FArrow(startPoint.x(), startPoint.y(), startPoint.z(),
246 endPoint.x(), endPoint.y(), endPoint.z(),
247 arrowWidth, arrowColour, arrowName);
248 FArrow.DescribeYourselfTo(sceneHandler);
249 }
250 }
251 delete boxZeroVisE;
252 }
253
254 querier.CleanUp();
255 }
256}
257
259{
260 G4double result = std::min({QIL(query->xInfo), QIL(query->yInfo), QIL(query->zInfo)});
261 return result;
262}
263
265{
266 if (qi.n == 1)
267 {return std::numeric_limits<G4double>::max();}
268
269 G4double step = std::abs((qi.max - qi.min) / ((G4double)qi.n));
270 return 0.8 * step;
271}
272
274{
275 G4double minSize = std::numeric_limits<double>::max();
276 G4ThreeVector result(0,0,0);
277 std::array<G4bool,3> nonZero = {false, false, false};
278 std::array<const BDSFieldQueryInfo::QueryDimensionInfo,3> dims = {qi->xInfo, qi->yInfo, qi->zInfo};
279
280 G4int nDims = 0;
281 for (G4int i = 0; i < 3; i++)
282 {
283 nonZero[i] = dims[i].n > 1;
284 if (nonZero[i])
285 {
286 nDims += 1;
287 result[i] = dims[i].StepSize();
288 minSize = std::min(minSize, result[i]);
289 }
290 }
291
292 G4double thinSize;
293 if (nDims == 1)
294 {thinSize = 0.1 * minSize;} // 20% x 0.5
295 else // 2 or 3 dimensions - only poosibly set 3rd dimension as thin-ish
296 {thinSize = 1e-2 * minSize;}
297 for (G4int i = 0; i < 3; i++)
298 {result[i] = nonZero[i] ? result[i]*0.5 : thinSize;}
299
300 return result;
301}
Colour scale based on magma colour map.
Colour scale based on viridis colour map.
Wrapper class of BDSFieldQuery that accumulates values for visualisation.
virtual void QueryField(const BDSFieldQueryInfo *query)
Reserve the vector size we need first, then proceed as normal. Avoids big copies.
virtual void CleanUp()
Call the base class method and also clear the vector of values in this class.
Holder class for all information required for a field query.
G4double CalculateArrowLength(const BDSFieldQueryInfo *query) const
Return the minimum of 0.8 x the step length in x,y,z.
const std::vector< BDSFieldQueryInfo * > queries
Cache of queries.
G4ThreeVector BoxHalfSize(const BDSFieldQueryInfo *qi) const
G4double QIL(const BDSFieldQueryInfo::QueryDimensionInfo &qi) const
Query Info Length. For one dimension, return 0.8 x step size.
G4VisAttributes * pointVisB
The vis attributes for a zero-field marker.
G4VisAttributes * pointVisE
The vis attributes for a zero-field marker.
static G4int instanceCounter