Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ComponentElmer2d.cc
Go to the documentation of this file.
1#include <math.h>
2#include <stdlib.h>
3#include <fstream>
4#include <iostream>
5
7
8namespace {
9
10void PrintErrorReadingFile(const std::string& hdr, const std::string& file,
11 const int line) {
12 std::cerr << hdr << "\n Error reading file " << file << " (line " << line
13 << ").\n";
14}
15
16}
17
18namespace Garfield {
19
21 m_is3d = false;
23
24 // Default bounding box
25 m_minBoundingBox[2] = -50;
26 m_maxBoundingBox[2] = 50;
27}
28
29ComponentElmer2d::ComponentElmer2d(const std::string& header,
30 const std::string& elist,
31 const std::string& nlist,
32 const std::string& mplist,
33 const std::string& volt,
34 const std::string& unit)
36
37 Initialise(header, elist, nlist, mplist, volt, unit);
38}
39
40bool ComponentElmer2d::Initialise(const std::string& header,
41 const std::string& elist,
42 const std::string& nlist,
43 const std::string& mplist,
44 const std::string& volt,
45 const std::string& unit) {
46 const std::string hdr = m_className + "::Initialise:";
48
49 // Keep track of the success.
50 bool ok = true;
51
52 // Buffer for reading
53 constexpr int size = 100;
54 char line[size];
55
56 // Open the header.
57 std::ifstream fheader(header);
58 if (!fheader) {
59 PrintCouldNotOpen("Initialise", header);
60 return false;
61 }
62
63 // Read the header to get the number of nodes and elements.
64 fheader.getline(line, size, '\n');
65 char* token = strtok(line, " ");
66 bool readerror = false;
67 const int nNodes = ReadInteger(token, 0, readerror);
68 token = strtok(nullptr, " ");
69 const int nElements = ReadInteger(token, 0, readerror);
70 std::cout << hdr << "\n Read " << nNodes << " nodes and " << nElements
71 << " elements from file " << header << ".\n";
72 if (readerror) {
73 PrintErrorReadingFile(hdr, header, 0);
74 fheader.close();
75 return false;
76 }
77
78 // Close the header file.
79 fheader.close();
80
81 // Open the nodes list.
82 std::ifstream fnodes(nlist);
83 if (!fnodes) {
84 PrintCouldNotOpen("Initialise", nlist);
85 return false;
86 }
87
88 // Check the value of the unit.
89 double funit = ScalingFactor(unit);
90 if (funit <= 0.) {
91 std::cerr << hdr << " Unknown length unit " << unit << " Will use cm.\n";
92 funit = 1.0;
93 }
94 if (m_debug) std::cout << " Unit scaling factor = " << funit << ".\n";
95
96 // Read the nodes from the file.
97 for (int il = 0; il < nNodes; il++) {
98 // Get a line from the nodes file.
99 fnodes.getline(line, size, '\n');
100
101 // Ignore the first two characters.
102 token = strtok(line, " ");
103 token = strtok(nullptr, " ");
104
105 // Get the node coordinates.
106 token = strtok(nullptr, " ");
107 double xnode = ReadDouble(token, -1, readerror);
108 token = strtok(nullptr, " ");
109 double ynode = ReadDouble(token, -1, readerror);
110 token = strtok(nullptr, " ");
111 double znode = ReadDouble(token, -1, readerror);
112 if (readerror) {
113 PrintErrorReadingFile(hdr, nlist, il);
114 fnodes.close();
115 return false;
116 }
117
118 // Set up and create a new node.
119 Node node;
120 node.x = xnode * funit;
121 node.y = ynode * funit;
122 node.z = znode * funit;
123 m_nodes.push_back(std::move(node));
124 }
125
126 // Close the nodes file.
127 fnodes.close();
128
129 // Read the potentials.
130 if (!LoadPotentials(volt, m_pot)) return false;
131
132 // Open the materials file.
133 std::ifstream fmplist(mplist);
134 if (!fmplist) {
135 PrintCouldNotOpen("Initialise", mplist);
136 return false;
137 }
138
139 // Read the dielectric constants from the materials file.
140 fmplist.getline(line, size, '\n');
141 token = strtok(line, " ");
142 if (readerror) {
143 std::cerr << hdr << "\n Error reading number of materials from "
144 << mplist << ".\n";
145 fmplist.close();
146 return false;
147 }
148 const unsigned int nMaterials = ReadInteger(token, 0, readerror);
149 m_materials.resize(nMaterials);
150 for (auto& material : m_materials) {
151 material.ohm = -1;
152 material.eps = -1;
153 material.medium = nullptr;
154 }
155 for (int il = 2; il < ((int)nMaterials + 2); il++) {
156 fmplist.getline(line, size, '\n');
157 token = strtok(line, " ");
158 ReadInteger(token, -1, readerror);
159 token = strtok(nullptr, " ");
160 double dc = ReadDouble(token, -1.0, readerror);
161 if (readerror) {
162 PrintErrorReadingFile(hdr, mplist, il);
163 fmplist.close();
164 return false;
165 }
166 m_materials[il - 2].eps = dc;
167 std::cout << " Set material " << il - 2 << " of "
168 << nMaterials << " to eps " << dc << ".\n";
169 }
170
171 // Close the materials file.
172 fmplist.close();
173
174 // Find lowest epsilon, check for eps = 0, set default drift medium.
175 if (!SetDefaultDriftMedium()) return false;
176
177 // Open the elements file.
178 std::ifstream felems(elist);
179 if (!felems) {
180 PrintCouldNotOpen("Initialise", elist);
181 return false;
182 }
183
184 // Read the elements and their material indices.
185 for (int il = 0; il < nElements; il++) {
186 // Get a line
187 felems.getline(line, size, '\n');
188
189 // Split into tokens.
190 token = strtok(line, " ");
191 // Read the 2nd-order element.
192 token = strtok(nullptr, " ");
193 int imat = ReadInteger(token, -1, readerror) - 1;
194 token = strtok(nullptr, " ");
195 std::vector<int> inode;
196 for (size_t k = 0; k < 8; ++k) {
197 token = strtok(nullptr, " ");
198 const int in = ReadInteger(token, -1, readerror);
199 if (!readerror) inode.push_back(in);
200 }
201 token = strtok(nullptr, " ");
202 if (inode.size() != 8) {
203 PrintErrorReadingFile(hdr, elist, il);
204 std::cerr << " Read " << inode.size() << " node indices for element"
205 << il << " (expected 8).\n";
206 felems.close();
207 return false;
208 }
209
210 if (m_debug && il < 10) {
211 std::cout << " Read nodes ";
212 for (size_t k = 0; k < 8; ++k) {
213 std::cout << inode[k];
214 if (k < 7) std::cout << ", ";
215 }
216 std::cout << "\n from element " << il + 1 << " of "
217 << nElements << " with material " << imat << ".\n";
218 }
219
220 // Check the material number and ensure that epsilon is non-negative.
221 if (imat < 0 || imat > (int)nMaterials) {
222 std::cerr << hdr << "\n Out-of-range material number on file " << elist
223 << " (line " << il << ").\n";
224 std::cerr << " Element: " << il << ", material: " << imat << "\n";
225 ok = false;
226 break;
227 }
228 if (m_materials[imat].eps < 0) {
229 std::cerr << hdr << "\n Element " << il << " in " << elist << "\n"
230 << " uses material " << imat << " which does not have\n"
231 << " a positive permittivity in " << mplist << ".\n";
232 ok = false;
233 break;
234 }
235
236 // Check the node numbers.
237 bool degenerate = false;
238 for (size_t k = 0; k < 8; ++k) {
239 if (inode[k] < 1) {
240 std::cerr << hdr << "\n Found a node number < 1 on file " << elist
241 << " (line " << il << ").\n Element: " << il
242 << ", material: " << imat << ".\n";
243 ok = false;
244 }
245 // These elements must not be degenerate.
246 for (size_t kk = k + 1; kk < 8; ++kk) {
247 if (inode[k] == inode[kk]) degenerate = true;
248 }
249 }
250 if (degenerate) {
251 std::cerr << hdr << "\n Element " << il << " of file " << elist
252 << " is degenerate,\n"
253 << " no such elements are allowed in this type of map.\n";
254 ok = false;
255 }
256 if (!ok) break;
257 Element element;
258 // Store the material reference.
259 element.matmap = imat;
260 // Store the node references.
261 // 3 -- 6 -- 2
262 // | |
263 // 7 5
264 // | |
265 // 0 -- 4 -- 1
266 // Note: Ordering of Elmer elements is described in the
267 // ElmerSolver manual (appendix D. at the time of this comment).
268 // In order to work properly with Coordinates4 and Coordinates5, the
269 // Elmer ordering 0,1,2,3,4,5,6,7 (counter-clockwise about the element)
270 // will need to be changed to 3,2,1,0,6,5,4,7 (clockwise about the element)
271 // if the normal of the defined element points in the -Z direction.
272
273 // Check the direction of the element normal, +Z or -Z.
274 double x01 = m_nodes[inode[1] - 1].x - m_nodes[inode[0] - 1].x;
275 double y01 = m_nodes[inode[1] - 1].y - m_nodes[inode[0] - 1].y;
276 double x12 = m_nodes[inode[2] - 1].x - m_nodes[inode[1] - 1].x;
277 double y12 = m_nodes[inode[2] - 1].y - m_nodes[inode[1] - 1].y;
278 const double crossprod = x01 * y12 - y01 * x12;
279 if (crossprod < 0) {
280 element.emap[3] = inode[0] - 1;
281 element.emap[2] = inode[1] - 1;
282 element.emap[1] = inode[2] - 1;
283 element.emap[0] = inode[3] - 1;
284 element.emap[6] = inode[4] - 1;
285 element.emap[5] = inode[5] - 1;
286 element.emap[4] = inode[6] - 1;
287 element.emap[7] = inode[7] - 1;
288 } else {
289 element.emap[0] = inode[0] - 1;
290 element.emap[1] = inode[1] - 1;
291 element.emap[2] = inode[2] - 1;
292 element.emap[3] = inode[3] - 1;
293 element.emap[4] = inode[4] - 1;
294 element.emap[5] = inode[5] - 1;
295 element.emap[6] = inode[6] - 1;
296 element.emap[7] = inode[7] - 1;
297 }
298 m_elements.push_back(std::move(element));
299 }
300
301 // Close the elements file.
302 felems.close();
303 if (!ok) return false;
304
305 m_degenerate.assign(m_elements.size(), false);
306 // Set the ready flag.
307 m_ready = true;
308 std::cout << " Finished.\n";
309
310 Prepare();
311 return true;
312}
313
314bool ComponentElmer2d::SetWeightingField(const std::string& wvolt,
315 const std::string& label) {
316 const std::string hdr = m_className + "::SetWeightingField:";
317 if (!m_ready) {
318 PrintNotReady("SetWeightingField");
319 std::cerr << " Weighting field cannot be added.\n";
320 return false;
321 }
322 std::cout << m_className << "::SetWeightingField:\n"
323 << " Loading field map for electrode " << label << ".\n";
324 // Check if a weighting field with the same label already exists.
325 if (m_wpot.count(label) > 0) {
326 std::cout << " Replacing existing weighting field.\n";
327 m_wpot[label].clear();
328 }
329 std::vector<double> pot(m_nodes.size(), 0.);
330 if (!LoadPotentials(wvolt, pot)) return false;
331 m_wpot[label] = std::move(pot);
332 return true;
333}
334
335bool ComponentElmer2d::LoadPotentials(const std::string& volt,
336 std::vector<double>& pot) {
337
338 // Open the voltage list.
339 std::ifstream fvolt(volt);
340 if (!fvolt) {
341 PrintCouldNotOpen("LoadPotentials", volt);
342 return false;
343 }
344
345 // Buffer for reading.
346 constexpr int size = 100;
347 char line[size];
348
349 // Read past the header.
350 int il = 1;
351 bool readstop = false;
352 while (!readstop && fvolt.getline(line, size, '\n')) {
353 char* token = strtok(line, " ");
354 if (token && strcmp(token, "Perm:") == 0) readstop = true;
355 il++;
356 }
357
358 // Should have stopped: if not, print error message.
359 if (!readstop) {
360 std::cerr << m_className << "::LoadPotentials:\n"
361 << " Error reading past header of potentials file "
362 << volt << ".\n";
363 fvolt.close();
364 return false;
365 }
366
367 // Read past the permutation map (number of lines = nNodes).
368 const auto nNodes = m_nodes.size();
369 for (size_t j = 0; j < nNodes; ++j) {
370 fvolt.getline(line, size, '\n');
371 il++;
372 }
373
374 pot.assign(nNodes, 0.);
375 // Read the potentials.
376 for (size_t j = 0; j < nNodes; ++j) {
377 fvolt.getline(line, size, '\n');
378 char* token = strtok(line, " ");
379 bool readerror = false;
380 double v = ReadDouble(token, -1, readerror);
381 if (readerror) {
382 PrintErrorReadingFile(m_className + "::LoadPotentials", volt, il);
383 fvolt.close();
384 return false;
385 }
386 // Place the potential at its appropriate index.
387 pot[j] = v;
388 }
389
390 // Close the potentials file.
391 fvolt.close();
392 std::cout << " Read potentials from file " << volt << ".\n";
393 return true;
394}
395
396void ComponentElmer2d::SetRangeZ(const double zmin, const double zmax) {
397 if (fabs(zmax - zmin) <= 0.) {
398 std::cerr << m_className << "::SetRangeZ: Zero range is not permitted.\n";
399 return;
400 }
401 m_minBoundingBox[2] = m_mapmin[2] = std::min(zmin, zmax);
402 m_maxBoundingBox[2] = m_mapmax[2] = std::max(zmin, zmax);
403}
404
405} // namespace Garfield
ComponentElmer2d()
Default constructor.
void SetRangeZ(const double zmin, const double zmax)
bool Initialise(const std::string &header="mesh.header", const std::string &elist="mesh.elements", const std::string &nlist="mesh.nodes", const std::string &mplist="dielectrics.dat", const std::string &volt="out.result", const std::string &unit="cm")
bool SetWeightingField(const std::string &prnsol, const std::string &label)
bool SetDefaultDriftMedium()
Find lowest epsilon, check for eps = 0, set default drift media flags.
static double ReadDouble(char *token, double def, bool &error)
std::array< double, 3 > m_maxBoundingBox
std::array< double, 3 > m_mapmin
std::array< double, 3 > m_minBoundingBox
static double ScalingFactor(std::string unit)
static int ReadInteger(char *token, int def, bool &error)
std::vector< Material > m_materials
ComponentFieldMap()=delete
Default constructor.
void PrintCouldNotOpen(const std::string &header, const std::string &filename) const
std::map< std::string, std::vector< double > > m_wpot
std::vector< Element > m_elements
std::array< double, 3 > m_mapmax
void Reset() override
Reset the component.
void PrintNotReady(const std::string &header) const
bool m_debug
Switch on/off debugging messages.
Definition Component.hh:371
std::string m_className
Class name.
Definition Component.hh:359
bool m_ready
Ready for use?
Definition Component.hh:368