PLaSK library
Loading...
Searching...
No Matches
capacitance2d.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include "capacitance2d.hpp"
15
16namespace plask { namespace electrical { namespace capacitance {
17
18template <typename Geometry2DType>
21 outAcVoltage(this, &Capacitance2DSolver<Geometry2DType>::getVoltage),
22 outAcCurrentDensity(this, &Capacitance2DSolver<Geometry2DType>::getCurrentDensities) {
24 inTemperature = 300.;
25}
26
27template <typename Geometry2DType>
29 while (source.requireTagOrEnd()) parseConfiguration(source, manager);
30}
31
32template <typename Geometry2DType>
34 std::string param = source.getNodeName();
35
36 if (param == "ac-voltage") {
37 this->readBoundaryConditions(manager, source, voltage_boundary);
38 } else if (param == "signal") {
39 frequency = source.getAttribute<double>("frequency", frequency);
40 } else if (!this->parseFemConfiguration(source, manager)) {
41 this->parseStandardConfiguration(source, manager);
42 }
43}
44
45template <typename Geometry2DType> Capacitance2DSolver<Geometry2DType>::~Capacitance2DSolver() {}
46
47template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::setupActiveRegions() {
48 this->invalidate();
49
50 if (!this->geometry || !this->mesh) return;
51
52 this->setupMaskedMesh();
53
54 auto points = this->mesh->getElementMesh();
55
56 std::vector<typename Active::Region> regions;
57
58 for (size_t r = 0; r < points->axis[1]->size(); ++r) {
59 size_t prev = 0;
60 shared_ptr<Material> material;
61 for (size_t c = 0; c < points->axis[0]->size(); ++c) { // In the (possible) active region
62 auto point = points->at(c, r);
63 size_t num = isActive(point);
64
65 if (num) { // here we are inside the active region
66 if (regions.size() >= num && regions[num - 1].warn) {
67 if (!material)
68 material = this->geometry->getMaterial(points->at(c, r));
69 else if (*material != *this->geometry->getMaterial(points->at(c, r))) {
70 writelog(LOG_WARNING, "Junction {} is laterally non-uniform", num - 1);
71 regions[num - 1].warn = false;
72 }
73 }
74 regions.resize(max(regions.size(), num));
75 auto& reg = regions[num - 1];
76 if (prev != num) { // this region starts in the current row
77 if (reg.top < r) {
78 throw Exception("{0}: Junction {1} is disjoint", this->getId(), num - 1);
79 }
80 if (reg.bottom >= r)
81 reg.bottom = r; // first row
82 else if (reg.rowr <= c)
83 throw Exception("{0}: Junction {1} is disjoint", this->getId(), num - 1);
84 reg.top = r + 1;
85 reg.rowl = c;
86 if (reg.left > reg.rowl) reg.left = reg.rowl;
87 }
88 }
89 if (prev && prev != num) { // previous region ended
90 auto& reg = regions[prev - 1];
91 if (reg.bottom < r && reg.rowl >= c) throw Exception("{0}: Junction {1} is disjoint", this->getId(), prev - 1);
92 reg.rowr = c;
93 if (reg.right < reg.rowr) reg.right = reg.rowr;
94 }
95 prev = num;
96 }
97 if (prev) // junction reached the edge
98 regions[prev - 1].rowr = regions[prev - 1].right = points->axis[0]->size();
99 }
100
101 size_t condsize = 0;
102 active.clear();
103 active.reserve(regions.size());
104 size_t i = 0;
105 for (auto& reg : regions) {
106 if (reg.bottom == std::numeric_limits<size_t>::max()) reg.bottom = reg.top = 0;
107 active.emplace_back(condsize, reg.left, reg.right, reg.bottom, reg.top,
108 this->mesh->axis[1]->at(reg.top) - this->mesh->axis[1]->at(reg.bottom));
109 condsize += reg.right - reg.left;
110 this->writelog(LOG_DETAIL, "Detected junction {0} thickness = {1}nm", i++, 1e3 * active.back().height);
111 this->writelog(LOG_DEBUG, "Junction {0} span: [{1},{3}]-[{2},{4}]", i - 1, reg.left, reg.right, reg.bottom, reg.top);
112 }
113}
114
115template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::onInitialize() {
116 if (!this->geometry) throw NoGeometryException(this->getId());
117 if (!this->mesh) throw NoMeshException(this->getId());
118 setupActiveRegions();
119 potentials.reset(this->maskedMesh->size(), 0.);
120}
121
122template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::onInvalidate() {
123 potentials.reset();
124 conductivities.reset();
125 currents.reset();
126}
127
128template <>
130 dcomplex&,
131 dcomplex&,
132 dcomplex&,
133 dcomplex&,
134 dcomplex&,
135 dcomplex&,
136 dcomplex&,
137 dcomplex&,
138 dcomplex&,
139 dcomplex,
140 double,
141 const Vec<2, double>&) {
142 return;
143}
144
145template <>
147 dcomplex& k33,
148 dcomplex& k22,
149 dcomplex& k11,
150 dcomplex& k43,
151 dcomplex& k21,
152 dcomplex& k42,
153 dcomplex& k31,
154 dcomplex& k32,
155 dcomplex& k41,
156 dcomplex,
157 double,
158 const Vec<2, double>& midpoint) {
159 double r = midpoint.rad_r();
160 k44 = r * k44;
161 k33 = r * k33;
162 k22 = r * k22;
163 k11 = r * k11;
164 k43 = r * k43;
165 k21 = r * k21;
166 k42 = r * k42;
167 k31 = r * k31;
168 k32 = r * k32;
169 k41 = r * k41;
170}
171
173template <typename Geometry2DType>
178 this->writelog(LOG_DETAIL, "Setting up matrix system ({})", A.describe());
179
180 A.clear();
181 B.fill(0.);
182
183 // Set stiffness matrix and load vector
184 for (auto e : this->maskedMesh->elements()) {
185 size_t i = e.getIndex();
186
187 // nodes numbers for the current element
188 size_t loleftno = e.getLoLoIndex();
189 size_t lorghtno = e.getUpLoIndex();
190 size_t upleftno = e.getLoUpIndex();
191 size_t uprghtno = e.getUpUpIndex();
192
193 // element size
194 double elemwidth = e.getUpper0() - e.getLower0();
195 double elemheight = e.getUpper1() - e.getLower1();
196
197 Vec<2, double> midpoint = e.getMidpoint();
198
199 dcomplex kx = this->conductivities[i].c00;
200 dcomplex ky = this->conductivities[i].c11;
201
202 kx *= elemheight;
203 kx /= elemwidth;
204 ky *= elemwidth;
205 ky /= elemheight;
206
207 // set symmetric matrix components
208 dcomplex k44, k33, k22, k11, k43, k21, k42, k31, k32, k41;
209
210 k44 = k33 = k22 = k11 = (kx + ky) / 3.;
211 k43 = k21 = (-2. * kx + ky) / 6.;
212 k42 = k31 = -(kx + ky) / 6.;
213 k32 = k41 = (kx - 2. * ky) / 6.;
214
215 // set stiffness matrix
216 setLocalMatrix(k44, k33, k22, k11, k43, k21, k42, k31, k32, k41, ky, elemwidth, midpoint);
217
218 A(loleftno, loleftno) += k11;
219 A(lorghtno, lorghtno) += k22;
220 A(uprghtno, uprghtno) += k33;
221 A(upleftno, upleftno) += k44;
222
223 A(lorghtno, loleftno) += k21;
224 A(uprghtno, loleftno) += k31;
225 A(upleftno, loleftno) += k41;
226 A(uprghtno, lorghtno) += k32;
227 A(upleftno, lorghtno) += k42;
228 A(upleftno, uprghtno) += k43;
229 }
230
231 A.applyBC(bvoltage, B);
232
233#ifndef NDEBUG
234 dcomplex* aend = A.data + A.size;
235 for (dcomplex* pa = A.data; pa != aend; ++pa) {
236 if (isnan(*pa) || isinf(pa->real()) || isinf(pa->imag()))
237 throw ComputationError(this->getId(), "error in stiffness matrix at position {0} ({1})", pa - A.data,
238 isnan(*pa) ? "nan" : "inf");
239 }
240#endif
241}
242
243template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::loadConductivities() {
244 auto midmesh = this->maskedMesh->getElementMesh();
245 auto temperature = inTemperature(midmesh);
246 auto conductivity = inDifferentialConductivity(midmesh);
247
248 conductivities.reset(midmesh->size());
249
250 for (auto e : this->maskedMesh->elements()) {
251 size_t i = e.getIndex();
252 Vec<2, double> midpoint = e.getMidpoint();
253 double imag =
254 2e9 * M_PI * phys::epsilon0 * frequency * this->geometry->getMaterial(midpoint)->eps(temperature[i]); // GHz -> Hz
255 conductivities[i] = Tensor2<dcomplex>(dcomplex(conductivity[i].c00, imag), dcomplex(conductivity[i].c11, imag));
256 }
257}
258
259template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::compute() {
260 this->initCalculation();
261
262 // Store boundary conditions for current mesh
263 auto vconst = voltage_boundary(this->maskedMesh, this->geometry);
264
265 this->writelog(LOG_INFO, "Running AC calculations for {} GHz", frequency);
266
267 std::unique_ptr<FemMatrix<dcomplex>> pA(this->getMatrix());
268 FemMatrix<dcomplex>& A = *pA.get();
269
270#ifndef NDEBUG
271 if (!potentials.unique()) this->writelog(LOG_DEBUG, "Voltage data held by something else...");
272#endif
273 potentials = potentials.claim();
274 loadConductivities();
275
276 DataVector<dcomplex> rhs(potentials.size());
277
278 setMatrix(A, rhs, vconst);
279 A.solve(rhs, potentials);
280
281 currents.reset();
282
283 outAcVoltage.fireChanged();
284 outAcCurrentDensity.fireChanged();
285}
286
287template <typename Geometry2DType>
289 bool active_current) const {
290 size_t index = element.getIndex();
291 size_t loleftno = element.getLoLoIndex();
292 size_t lorghtno = element.getUpLoIndex();
293 size_t upleftno = element.getLoUpIndex();
294 size_t uprghtno = element.getUpUpIndex();
295 dcomplex dvx = -0.05 * (-potentials[loleftno] + potentials[lorghtno] - potentials[upleftno] + potentials[uprghtno]) /
296 (element.getUpper0() - element.getLower0()); // [j] = kA/cm²
297 dcomplex dvy = -0.05 * (-potentials[loleftno] - potentials[lorghtno] + potentials[upleftno] + potentials[uprghtno]) /
298 (element.getUpper1() - element.getLower1()); // [j] = kA/cm²
299
300 if (!active_current)
301 return vec(conductivities[index].c00 * dvx, conductivities[index].c11 * dvy);
302 else
303 return vec(conductivities[index].c00.real() * dvx, conductivities[index].c11.real() * dvy);
304}
305
306template <typename Geometry2DType> void Capacitance2DSolver<Geometry2DType>::computeCurrentDensities() {
307 this->writelog(LOG_DEBUG, "Computing current densities");
308 currents.reset(this->maskedMesh->getElementsCount(), vec(0., 0.));
309 for (auto element : this->maskedMesh->elements()) {
310 currents[element.getIndex()] = getElementCurrentDensity(element);
311 }
312}
313
314template <>
316 if (!potentials) throw NoValue("current densities");
317 this->writelog(LOG_DETAIL, "Computing total current");
318 dcomplex result = 0.;
319 for (size_t i = 0; i < mesh->axis[0]->size() - 1; ++i) {
320 auto element = maskedMesh->element(i, vindex);
321 if (!onlyactive || isActive(element.getMidpoint())) {
323 result += getElementCurrentDensity(element, active_current).c1 * element.getSize0();
324 }
325 }
326 }
327 if (this->getGeometry()->isSymmetric(Geometry::DIRECTION_TRAN)) result *= 2.;
328 return result * geometry->getExtrusion()->getLength() * 0.01; // kA/cm² µm² --> mA;
329}
330
331template <>
333 if (!potentials) throw NoValue("current densities");
334 this->writelog(LOG_DETAIL, "Computing total current");
335 dcomplex result = 0.;
336 for (size_t i = 0; i < mesh->axis[0]->size() - 1; ++i) {
337 auto element = maskedMesh->element(i, vindex);
338 if (!onlyactive || isActive(element.getMidpoint())) {
339 size_t index = element.getIndex();
341 double rin = element.getLower0(), rout = element.getUpper0();
342 result += getElementCurrentDensity(element, active_current).c1 * (rout * rout - rin * rin);
343 }
344 }
345 }
346 return result * plask::PI * 0.01; // kA/cm² µm² --> mA
347}
348
349template <typename Geometry2DType>
351 if (!potentials) throw NoValue("current");
352 if (nact >= this->active.size()) throw BadInput(this->getId(), "wrong active region number");
353 const auto& act = this->active[nact];
354 // Find the average of the active region
355 size_t level = (act.bottom + act.top) / 2;
356 return integrateCurrent(level, active_current, true);
357}
358
359template <typename Geometry2DType> dcomplex Capacitance2DSolver<Geometry2DType>::getImpedance() const {
360 if (!potentials) throw NoValue("impedance");
361 if (this->voltage_boundary.size() != 2)
362 throw Exception("{0}: Impedance can be computed only for 2-terminal structures", this->getId());
363 dcomplex voltage = this->voltage_boundary[0].value - this->voltage_boundary[1].value;
364 dcomplex current = 1e-3 * getAcCurrent(); // Convert from mA to A
365 dcomplex impedance = voltage / current;
366 if (impedance.real() < 0.) impedance = -impedance; // make sure that the real part is positive
367 return impedance;
368}
369
370template <typename Geometry2DType> dcomplex Capacitance2DSolver<Geometry2DType>::getS11(dcomplex Z0) const {
371 return (getImpedance() - Z0) / (getImpedance() + Z0);
372}
373
374template <typename Geometry2DType>
376 InterpolationMethod method) const {
377 if (!potentials) throw NoValue("voltage");
378 this->writelog(LOG_DEBUG, "Getting voltage");
379 if (method == INTERPOLATION_DEFAULT) method = INTERPOLATION_LINEAR;
380 if (this->maskedMesh->full())
381 return interpolate(this->mesh, potentials, dest_mesh, method, this->geometry);
382 else
383 return interpolate(this->maskedMesh, potentials, dest_mesh, method, this->geometry);
384}
385
386template <typename Geometry2DType>
388 InterpolationMethod method) {
389 if (!potentials) throw NoValue("current density");
390 this->writelog(LOG_DEBUG, "Getting current densities");
391 if (!currents) computeCurrentDensities();
392 if (method == INTERPOLATION_DEFAULT) method = INTERPOLATION_LINEAR;
394 if (this->maskedMesh->full()) {
395 auto result = interpolate(this->mesh->getElementMesh(), currents, dest_mesh, method, flags);
396 return LazyData<Vec<2, dcomplex>>(result.size(), [result, this, flags, dest_mesh](size_t i) {
397 return this->geometry->getChildBoundingBox().contains(flags.wrap(dest_mesh->at(i))) ? result[i]
398 : Vec<2, dcomplex>(0., 0.);
399 });
400 } else {
401 auto result = interpolate(this->maskedMesh->getElementMesh(), currents, dest_mesh, method, flags);
402 return LazyData<Vec<2, dcomplex>>(result.size(), [result](size_t i) {
403 // Masked mesh always returns NaN outside of itself
404 auto val = result[i];
405 return isnan(val) ? Vec<2, dcomplex>(0., 0.) : val;
406 });
407 }
408 assert(false); // should not be reached
409}
410
411template <> std::string Capacitance2DSolver<Geometry2DCartesian>::getClassName() const { return "Capacitance2D"; }
412template <> std::string Capacitance2DSolver<Geometry2DCylindrical>::getClassName() const { return "CapacitanceCyl"; }
413
416
417}}} // namespace plask::electrical::capacitance