Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ViewCell.cc
Go to the documentation of this file.
1#include <cmath>
2#include <iostream>
3
4#include <TEllipse.h>
5#include <TGeoBBox.h>
6#include <TGeoPgon.h>
7#include <TLine.h>
8#include <TMarker.h>
9#include <TPolyLine.h>
10
13#include "Garfield/Plotting.hh"
14#include "Garfield/ViewCell.hh"
15
16namespace Garfield {
17
18ViewCell::ViewCell() : ViewBase("ViewCell") {}
19
21 if (!comp) {
22 std::cerr << m_className << "::SetComponent: Null pointer.\n";
23 return;
24 }
25
26 m_component = comp;
27}
28
30 if (!comp) {
31 std::cerr << m_className << "::SetComponent: Null pointer.\n";
32 return;
33 }
34 m_nebem = comp;
35}
36
37void ViewCell::SetArea(const double xmin, const double ymin, const double zmin,
38 const double xmax, const double ymax,
39 const double zmax) {
40 // Check range, assign if non-null
41 if (xmin == xmax || ymin == ymax || zmin == zmax) {
42 std::cerr << m_className << "::SetArea: Null area range not permitted.\n";
43 return;
44 }
45 m_xMin = std::min(xmin, xmax);
46 m_yMin = std::min(ymin, ymax);
47 m_zMin = std::min(zmin, zmax);
48 m_xMax = std::max(xmin, xmax);
49 m_yMax = std::max(ymin, ymax);
50 m_zMax = std::max(zmin, zmax);
51 m_hasUserArea = true;
52}
53
55 if (!Plot(false)) {
56 std::cerr << m_className << "::Plot2d: Error creating plot.\n";
57 }
58}
59
61 if (!Plot(true)) {
62 std::cerr << m_className << "::Plot3d: Error creating plot.\n";
63 }
64}
65
66bool ViewCell::Plot(const bool use3d) {
67 if (!m_component && !m_nebem) {
68 std::cerr << m_className << "::Plot: Component is not defined.\n";
69 return false;
70 }
71
72 double pmin = 0., pmax = 0.;
73 if (m_component) {
74 if (!m_component->GetVoltageRange(pmin, pmax)) {
75 std::cerr << m_className << "::Plot: Component is not ready.\n";
76 return false;
77 }
78 } else {
79 if (!m_nebem->GetVoltageRange(pmin, pmax)) {
80 std::cerr << m_className << "::Plot: Component is not ready.\n";
81 return false;
82 }
83 }
84
85 // Get the bounding box
86 double x0 = m_xMin, y0 = m_yMin, z0 = m_zMin;
87 double x1 = m_xMax, y1 = m_yMax, z1 = m_zMax;
88 if (!m_hasUserArea) {
89 if (m_component) {
90 if (!m_component->GetBoundingBox(x0, y0, z0, x1, y1, z1)) {
91 std::cerr << m_className << "::Plot:\n"
92 << " Bounding box cannot be determined.\n"
93 << " Call SetArea first.\n";
94 return false;
95 }
96 } else {
97 if (!m_nebem->GetBoundingBox(x0, y0, z0, x1, y1, z1)) {
98 std::cerr << m_className << "::Plot:\n"
99 << " Bounding box cannot be determined.\n"
100 << " Call SetArea first.\n";
101 return false;
102 }
103 }
104 }
105 // Get the max. half-length in z.
106 const double dx = std::max(fabs(x0), fabs(x1));
107 const double dy = std::max(fabs(y0), fabs(y1));
108 const double dz = std::max(fabs(z0), fabs(z1));
109
110 if (!m_canvas) {
111 m_canvas = new TCanvas();
112 if (!use3d) m_canvas->SetTitle(m_label.c_str());
114 }
115 m_canvas->cd();
116
117 if (!use3d) {
118 bool empty = false;
119 if (!gPad ||
120 (gPad->GetListOfPrimitives()->GetSize() == 0 && gPad->GetX1() == 0 &&
121 gPad->GetX2() == 1 && gPad->GetY1() == 0 && gPad->GetY2() == 1)) {
122 empty = true;
123 }
124 const double bm = m_canvas->GetBottomMargin();
125 const double lm = m_canvas->GetLeftMargin();
126 const double rm = m_canvas->GetRightMargin();
127 const double tm = m_canvas->GetTopMargin();
128 if (!empty) {
129 TPad* pad = new TPad("cell", "", 0, 0, 1, 1);
130 pad->SetFillStyle(0);
131 pad->SetFrameFillStyle(0);
132 pad->Draw();
133 pad->cd();
134 }
135 gPad->Range(x0 - (x1 - x0) * (lm / (1. - rm - lm)),
136 y0 - (y1 - y0) * (bm / (1. - tm - lm)),
137 x1 + (x1 - x0) * (rm / (1. - rm - lm)),
138 y1 + (y1 - y0) * (tm / (1. - tm - lm)));
139 }
140
141 if (m_nebem) return PlotNeBem(use3d);
142
143 // Get the cell type.
144 const std::string cellType = m_component->GetCellType();
145
146 // Get the x/y periodicities.
147 double sx = 0., sy = 0.;
148 const bool perX = m_component->GetPeriodicityX(sx);
149 const bool perY = m_component->GetPeriodicityY(sy);
150 // Determine the number of periods present in the cell.
151 const int nMinX = perX ? int(x0 / sx) - 1 : 0;
152 const int nMaxX = perX ? int(x1 / sx) + 1 : 0;
153 const int nMinY = perY ? int(y0 / sy) - 1 : 0;
154 const int nMaxY = perY ? int(y1 / sy) + 1 : 0;
155 // Get the phi periodicity.
156 double sphi = 0.;
157 const bool perPhi = m_component->GetPeriodicityPhi(sphi);
158 const int nPhi = perPhi ? int(360. / sphi) : 0;
159 sphi *= DegreeToRad;
160 if (use3d) SetupGeo(dx, dy, dz);
161 const bool polar = m_component->IsPolar();
162
163 // Get the number of wires.
164 const unsigned int nWires = m_component->GetNumberOfWires();
165 std::vector<std::string> wireTypes;
166 // Loop over the wires.
167 for (unsigned int i = 0; i < nWires; ++i) {
168 double xw = 0., yw = 0., dw = 0., vw = 0., lw = 0., qw = 0.;
169 std::string lbl;
170 int type = -1;
171 int nTrap;
172 m_component->GetWire(i, xw, yw, dw, vw, lbl, lw, qw, nTrap);
173 // Check if other wires with the same label already exist.
174 if (wireTypes.empty()) {
175 wireTypes.push_back(lbl);
176 type = 0;
177 } else {
178 const unsigned int nWireTypes = wireTypes.size();
179 for (unsigned int j = 0; j < nWireTypes; ++j) {
180 if (lbl == wireTypes[j]) {
181 type = j;
182 break;
183 }
184 }
185 if (type < 0) {
186 type = wireTypes.size();
187 wireTypes.push_back(lbl);
188 }
189 }
190 if (polar) {
191 const double r = xw;
192 for (int j = 0; j <= nPhi; ++j) {
193 const double phi = yw * Garfield::DegreeToRad + j * sphi;
194 const double x = r * cos(phi);
195 const double y = r * sin(phi);
196 if (use3d) {
197 PlotWire(x, y, dw, type, std::min(0.5 * lw, dz));
198 } else {
199 PlotWire(x, y, dw, type);
200 }
201 }
202 continue;
203 }
204 for (int nx = nMinX; nx <= nMaxX; ++nx) {
205 const double x = xw + nx * sx;
206 if (x + 0.5 * dw <= x0 || x - 0.5 * dw >= x1) continue;
207 for (int ny = nMinY; ny <= nMaxY; ++ny) {
208 const double y = yw + ny * sy;
209 if (y + 0.5 * dw <= y0 || y - 0.5 * dw >= y1) continue;
210 if (use3d) {
211 PlotWire(x, y, dw, type, std::min(0.5 * lw, dz));
212 } else {
213 PlotWire(x, y, dw, type);
214 }
215 }
216 }
217 }
218
219 // Draw the x planes.
220 const unsigned int nPlanesX = m_component->GetNumberOfPlanesX();
221 for (unsigned int i = 0; i < nPlanesX; ++i) {
222 double xp = 0., vp = 0.;
223 std::string lbl;
224 m_component->GetPlaneX(i, xp, vp, lbl);
225 for (int nx = nMinX; nx <= nMaxX; ++nx) {
226 const double x = xp + nx * sx;
227 if (x < x0 || x > x1) continue;
228 if (use3d) {
229 const double width = std::min(0.01 * dx, 0.01 * dy);
230 PlotPlane(width, dy, dz, x, 0);
231 } else {
232 PlotPlane(x, y0, x, y1);
233 }
234 }
235 }
236
237 // Draw the y planes.
238 const unsigned int nPlanesY = m_component->GetNumberOfPlanesY();
239 for (unsigned int i = 0; i < nPlanesY; ++i) {
240 double yp = 0., vp = 0.;
241 std::string lbl;
242 m_component->GetPlaneY(i, yp, vp, lbl);
243 for (int ny = nMinY; ny <= nMaxY; ++ny) {
244 const double y = yp + ny * sy;
245 if (y < y0 || y > y1) continue;
246 if (use3d) {
247 const double width = std::min(0.01 * dx, 0.01 * dy);
248 PlotPlane(dx, width, dz, 0, y);
249 } else {
250 PlotPlane(x0, y, x1, y);
251 }
252 }
253 }
254
255 // Draw the r and phi planes.
256 const unsigned int nPlanesR = m_component->GetNumberOfPlanesR();
257 const unsigned int nPlanesPhi = m_component->GetNumberOfPlanesPhi();
258 if (nPlanesR > 0 || nPlanesPhi > 0) {
259 double vp = 0.;
260 std::string lbl;
261 std::array<double, 2> phi = {0., 360.};
262 double dphi = 360.;
263 if (nPlanesPhi == 2 && !m_component->GetPeriodicityPhi(dphi)) {
264 m_component->GetPlanePhi(0, phi[0], vp, lbl);
265 m_component->GetPlanePhi(1, phi[1], vp, lbl);
266 }
267 double w = 0.01 * std::min(dx, dy);
268 std::array<double, 2> r = {0., 0.};
269 if (nPlanesR > 0) {
270 m_component->GetPlaneR(0, r[0], vp, lbl);
271 w = 0.02 * r[0];
272 }
273 if (nPlanesR == 2) {
274 m_component->GetPlaneR(1, r[1], vp, lbl);
275 w = 0.01 * (r[1] - r[0]);
276 }
277 for (unsigned int i = 0; i < nPlanesR; ++i) {
278 if (use3d) {
279 const auto med = m_geo->GetMedium("Metal");
280 const auto tubs = m_geo->MakeTubs("Plane", med, r[i] - w, r[i] + w,
281 dz, phi[0], phi[1]);
282 tubs->SetLineColor(kGreen - 5);
283 tubs->SetTransparency(75);
284 m_geo->GetTopVolume()->AddNode(tubs, 1);
285 } else {
286 TEllipse circle;
287 circle.SetDrawOption("same");
288 circle.SetFillStyle(0);
289 circle.SetNoEdges();
290 circle.DrawEllipse(0, 0, r[i], r[i], phi[0], phi[1], 0);
291 }
292 }
293 if (nPlanesR == 1) {
294 std::swap(r[0], r[1]);
295 } else if (nPlanesR == 0) {
296 r[1] = std::max(dx, dy);
297 }
298 for (unsigned int i = 0; i < nPlanesPhi; ++i) {
299 const double cp = cos(phi[i] * DegreeToRad);
300 const double sp = sin(phi[i] * DegreeToRad);
301 if (use3d) {
302 const auto med = m_geo->GetMedium("Metal");
303 const double dr = 0.5 * (r[1] - r[0]);
304 TGeoVolume* plane = m_geo->MakeBox("Plane", med, dr, w, dz);
305 plane->SetLineColor(kGreen - 5);
306 plane->SetTransparency(75);
307 TGeoRotation* rot = new TGeoRotation("", phi[i], 0, 0);
308 const double rm = 0.5 * (r[0] + r[1]);
309 TGeoCombiTrans* tr = new TGeoCombiTrans(cp * rm, sp * rm, 0, rot);
310 m_geo->GetTopVolume()->AddNode(plane, 1, tr);
311 } else {
312 PlotPlane(r[0] * cp, r[0] * sp, r[1] * cp, r[1] * sp);
313 }
314 }
315 }
316 double rt = 0., vt = 0.;
317 int nt = 0;
318 std::string lbl;
319 if (m_component->GetTube(rt, vt, nt, lbl)) {
320 if (use3d) {
321 PlotTube(0.98 * rt, 1.02 * rt, nt, dz);
322 } else {
323 PlotTube(0., 0., rt, nt);
324 }
325 }
326
327 if (use3d) {
328 m_geo->CloseGeometry();
329 m_geo->GetTopNode()->Draw("ogl");
330 } else {
331 m_canvas->Update();
332 }
333
334 return true;
335}
336
337bool ViewCell::PlotNeBem(const bool use3d) {
338
339 if (use3d) {
340 std::cerr << m_className << "::PlotNeBem: 3D plot not implemented yet.\n";
341 return false;
342 }
343
344 // Draw the regions.
345 const unsigned int nRegions = m_nebem->GetNumberOfRegions();
346 for (unsigned int i = nRegions; i-- > 0;) {
347 std::vector<double> xv;
348 std::vector<double> yv;
349 Medium* medium = nullptr;
350 unsigned int bctype = 1;
351 double v = 0.;
352 if (!m_nebem->GetRegion(i, xv, yv, medium, bctype, v)) continue;
353 const unsigned int n = xv.size();
354 if (n < 3) continue;
355 TLine line;
356 line.SetDrawOption("same");
357 if (bctype == 4) {
358 line.SetLineStyle(2);
359 } else {
360 line.SetLineStyle(1);
361 }
362 for (unsigned int j = 0; j < n; ++j) {
363 const unsigned int k = j < n - 1 ? j + 1 : 0;
364 line.DrawLine(xv[j], yv[j], xv[k], yv[k]);
365 }
366 }
367
368 // Draw the wires.
369 const unsigned int nWires = m_nebem->GetNumberOfWires();
370 for (unsigned int i = 0; i < nWires; ++i) {
371 double x = 0., y = 0., d = 0., v = 0., q = 0.;
372 if (!m_nebem->GetWire(i, x, y, d, v, q)) continue;
373 PlotWire(x, y, d, 0);
374 }
375
376 // Draw the straight-line segments.
377 const unsigned int nSegments = m_nebem->GetNumberOfSegments();
378 for (unsigned int i = 0; i < nSegments; ++i) {
379 double x0 = 0., y0 = 0., x1 = 0., y1 = 0., v = 0.;
380 if (!m_nebem->GetSegment(i, x0, y0, x1, y1, v)) continue;
381 PlotPlane(x0, y0, x1, y1);
382 }
383
384 m_canvas->Update();
385 return true;
386}
387
388void ViewCell::SetupGeo(const double dx, const double dy, const double dz) {
389
390 if (!m_geo) {
391 gGeoManager = nullptr;
392 m_geo.reset(new TGeoManager("ViewCellGeoManager", m_label.c_str()));
393 TGeoMaterial* matVacuum = new TGeoMaterial("Vacuum", 0., 0., 0.);
394 TGeoMaterial* matMetal = new TGeoMaterial("Metal", 63.546, 29., 8.92);
395 TGeoMedium* medVacuum = new TGeoMedium("Vacuum", 0, matVacuum);
396 TGeoMedium* medMetal = new TGeoMedium("Metal", 1, matMetal);
397 m_geo->AddMaterial(matVacuum);
398 m_geo->AddMaterial(medMetal->GetMaterial());
399 TGeoVolume* world =
400 m_geo->MakeBox("World", medVacuum, 1.05 * dx, 1.05 * dy, 1.05 * dz);
401 m_geo->SetTopVolume(world);
402 } else {
403 TGeoVolume* top = m_geo->GetTopVolume();
404 TGeoBBox* box = dynamic_cast<TGeoBBox*>(top);
405 double halfLenghts[3] = {1.05 * dx, 1.05 * dy, 1.05 * dz};
406 if (box) box->SetDimensions(halfLenghts);
407 }
408}
409
410void ViewCell::PlotWire(const double x, const double y, const double d,
411 const int type) {
412 if (m_useWireMarker) {
413 int markerStyle = 1;
414 if (type == 0) {
415 markerStyle = kFullCircle;
416 } else if (type == 1) {
417 markerStyle = kOpenCircle;
418 } else if (type == 2) {
419 markerStyle = kFullSquare;
420 } else if (type == 3) {
421 markerStyle = kOpenSquare;
422 } else {
423 markerStyle = 26 + type;
424 }
425 TMarker marker;
426 marker.SetMarkerStyle(markerStyle);
427 marker.SetDrawOption("Psame");
428 marker.DrawMarker(x, y);
429 return;
430 }
431
432 TEllipse circle;
433 circle.SetDrawOption("same");
434 circle.SetFillColor(kWhite);
435 circle.DrawEllipse(x, y, 0.5 * d, 0.5 * d, 0, 360, 0);
436}
437
438void ViewCell::PlotWire(const double x, const double y, const double d,
439 const int type, const double lz) {
440
441 const auto medium = m_geo->GetMedium("Metal");
442 TGeoVolume* wire = m_geo->MakeTube("Wire", medium, 0., 0.5 * d, lz);
443 switch (type) {
444 case 0:
445 wire->SetLineColor(kGray + 2);
446 break;
447 case 1:
448 wire->SetLineColor(kRed + 2);
449 break;
450 case 2:
451 wire->SetLineColor(kPink + 3);
452 break;
453 case 3:
454 wire->SetLineColor(kCyan + 3);
455 break;
456 default:
457 wire->SetLineColor(kBlue + type);
458 break;
459 }
460 m_geo->GetTopVolume()->AddNode(wire, 1, new TGeoTranslation(x, y, 0.));
461}
462
463void ViewCell::PlotTube(const double x0, const double y0, const double r,
464 const int n) {
465 if (n <= 0) {
466 TEllipse circle;
467 circle.SetDrawOption("same");
468 circle.SetFillStyle(0);
469 circle.DrawEllipse(x0, y0, r, r, 0, 360, 0);
470 return;
471 }
472
473 double* x = new double[n + 1];
474 double* y = new double[n + 1];
475 for (int i = 0; i <= n; ++i) {
476 const double phi = i * TwoPi / double(n);
477 x[i] = x0 + r * cos(phi);
478 y[i] = y0 + r * sin(phi);
479 }
480 TPolyLine pline;
481 pline.SetDrawOption("same");
482 pline.DrawPolyLine(n + 1, x, y);
483 delete[] x;
484 delete[] y;
485}
486
487void ViewCell::PlotTube(const double x0, const double y0,
488 const double r1, const double r2, const int n,
489 const double lz) {
490
491 TGeoVolume* tube = nullptr;
492 if (n <= 0) {
493 // Round tube.
494 tube = m_geo->MakeTube("Tube", m_geo->GetMedium("Metal"), r1, r2, lz);
495 } else {
496 tube = m_geo->MakePgon("Tube", m_geo->GetMedium("Metal"), 0., 360., n, 2);
497 TGeoPgon* pgon = dynamic_cast<TGeoPgon*>(tube->GetShape());
498 pgon->DefineSection(0, -lz, r1, r2);
499 pgon->DefineSection(1, +lz, r1, r2);
500 }
501 tube->SetLineColor(kGreen + 2);
502 tube->SetTransparency(75);
503 m_geo->GetTopVolume()->AddNode(tube, 1, new TGeoTranslation(x0, y0, 0));
504}
505
506void ViewCell::PlotPlane(const double x0, const double y0,
507 const double x1, const double y1) {
508
509 TLine line;
510 line.SetDrawOption("same");
511 line.DrawLine(x0, y0, x1, y1);
512}
513
514void ViewCell::PlotPlane(const double dx, const double dy, const double dz,
515 const double x0, const double y0) {
516
517 const auto medium = m_geo->GetMedium("Metal");
518 TGeoVolume* plane = m_geo->MakeBox("Plane", medium, dx, dy, dz);
519 plane->SetLineColor(kGreen - 5);
520 plane->SetTransparency(75);
521 m_geo->GetTopVolume()->AddNode(plane, 1, new TGeoTranslation(x0, y0, 0));
522}
523
524}
bool GetVoltageRange(double &pmin, double &pmax) override
Calculate the voltage range [V].
bool GetPeriodicityY(double &s)
Get the periodic length in the y-direction.
bool IsPolar() const
Are polar coordinates being used?
unsigned int GetNumberOfWires() const
Get the number of wires.
bool GetWire(const unsigned int i, double &x, double &y, double &diameter, double &voltage, std::string &label, double &length, double &charge, int &ntrap) const
Retrieve the parameters of a wire.
bool GetPlaneR(const unsigned int i, double &r, double &voltage, std::string &label) const
Retrieve the parameters of a plane at constant radius.
unsigned int GetNumberOfPlanesY() const
Get the number of equipotential planes at constant y.
unsigned int GetNumberOfPlanesX() const
Get the number of equipotential planes at constant x.
bool GetPeriodicityX(double &s)
Get the periodic length in the x-direction.
bool GetTube(double &r, double &voltage, int &nEdges, std::string &label) const
Retrieve the tube parameters.
bool GetPlaneX(const unsigned int i, double &x, double &voltage, std::string &label) const
Retrieve the parameters of a plane at constant x.
bool GetPeriodicityPhi(double &s)
Get the periodicity [degree] in phi.
bool GetPlanePhi(const unsigned int i, double &phi, double &voltage, std::string &label) const
Retrieve the parameters of a plane at constant phi.
bool GetBoundingBox(double &x0, double &y0, double &z0, double &x1, double &y1, double &z1) override
Get the bounding box coordinates.
unsigned int GetNumberOfPlanesPhi() const
Get the number of equipotential planes at constant phi.
bool GetPlaneY(const unsigned int i, double &y, double &voltage, std::string &label) const
Retrieve the parameters of a plane at constant y.
unsigned int GetNumberOfPlanesR() const
Get the number of equipotential planes at constant radius.
Two-dimensional implementation of the nearly exact Boundary Element Method.
unsigned int GetNumberOfSegments() const
Return the number of conducting straight-line segments.
bool GetVoltageRange(double &vmin, double &vmax) override
Calculate the voltage range [V].
bool GetSegment(const unsigned int i, double &x0, double &y0, double &x1, double &x2, double &v) const
Return the coordinates and voltage of a given straight-line segment.
bool GetBoundingBox(double &xmin, double &ymin, double &zmin, double &xmax, double &ymax, double &zmax) override
Get the bounding box coordinates.
unsigned int GetNumberOfRegions() const
Return the number of regions.
bool GetRegion(const unsigned int i, std::vector< double > &xv, std::vector< double > &yv, Medium *&medium, unsigned int &bctype, double &v)
Return the properties of a given region.
bool GetWire(const unsigned int i, double &x, double &y, double &d, double &v, double &q) const
Return the coordinates, diameter, potential and charge of a given wire.
unsigned int GetNumberOfWires() const
Return the number of wires.
Base class for visualization classes.
Definition: ViewBase.hh:10
std::string m_className
Definition: ViewBase.hh:28
bool m_hasExternalCanvas
Definition: ViewBase.hh:35
TCanvas * m_canvas
Definition: ViewBase.hh:34
void Plot3d()
Make a three-dimensional drawing of the cell geometry (using TGeo).
Definition: ViewCell.cc:60
void SetArea()
Take the plot range from the bounding box of the component class.
Definition: ViewCell.hh:33
void SetComponent(ComponentAnalyticField *comp)
Set the component for which to draw the cell geometry.
Definition: ViewCell.cc:20
ViewCell()
Constructor.
Definition: ViewCell.cc:18
void Plot2d()
Make a two-dimensional drawing of the cell geometry.
Definition: ViewCell.cc:54
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:432
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:384