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