driftlet 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,629 @@
1
+ # Numerics
2
+
3
+ How driftlet turns the physics into equations and solves them. The guiding constraint: every
4
+ equation couples a node only to its neighbours, so the Jacobian is **block-tridiagonal** and
5
+ each Newton iteration is a single block-Thomas sweep. That constraint is what makes
6
+ interactive speeds possible, so non-local physics is out of scope.
7
+
8
+ ## The equations
9
+
10
+ In one dimension, inside each region, every species $`i`$ present there obeys a balance, a flux
11
+ law and its material's statistics, and the electrostatic potential obeys Poisson's equation:
12
+
13
+ ```math
14
+ \begin{aligned}
15
+ &\frac{\partial c_i}{\partial t} + \frac{\partial N_i}{\partial x} = \sum_r \nu_{ir}\, r_r
16
+ && \quad\text{(conservation, with bulk reactions } r\text{)} \\
17
+ &N_i = -\frac{D_i c_i}{RT}\frac{\partial\bar\mu_i}{\partial x} + v\, c_i
18
+ - \frac{D_{\mathrm{mix}}}{RT}\Big(P\,\frac{\partial\bar\mu}{\partial x}\Big)_i
19
+ && \quad\text{(drift and diffusion, advection, eddy mixing)} \\
20
+ &\bar\mu_i = \mu^\circ_i + z_i F\phi + RT\zeta_i, \qquad c_i = c_i(\zeta)
21
+ && \quad\text{(the material's statistics)} \\
22
+ &-\frac{\partial}{\partial x}\Big(\varepsilon\frac{\partial\phi}{\partial x}\Big) = F\sum_i z_i c_i + \rho_{\mathrm{fixed}}
23
+ && \quad\text{(Poisson)}
24
+ \end{aligned}
25
+ ```
26
+
27
+ Each reaction runs at $`r = k_f \prod c^\nu\,(1 - e^{-A/RT})`$, its affinity $`A`$ from the reactants'
28
+ and products' $`\bar\mu`$. In a strictly neutral material ($`\varepsilon = 0`$) Poisson's equation becomes
29
+ the constraint $`F\sum_i z_i c_i + \rho_{\mathrm{fixed}} = 0`$. Some materials need less:
30
+
31
+ - A **conductor region** (a metal, a fast ion conductor) has only its carrier, with no storage in
32
+ its bulk: $`\partial J/\partial x = 0`$ with Ohm's law $`J = -\sigma\,\partial V/\partial x`$, and no $`\phi`$.
33
+ - An **insertion host** holds its ion and carrier as one neutral combination, so $`\phi`$ cancels
34
+ from everything there and is left undefined.
35
+ - A species absent from a material has no equations in it.
36
+
37
+ At each interface the laws are local to the face: per species, continuity of $`\bar\mu`$, a
38
+ conductance, a blocked face, or face reactions with their own rates; for $`\phi`$, an alignment
39
+ (dipole), neutrality or a Helmholtz capacitance, with the displacement continuous apart from any
40
+ sheet charge. Contacts and ports join the device to outside phases with known levels, and the
41
+ terminals to their circuits ([the device reference](device.md) has every option). The rest of
42
+ this page is how these become a block-tridiagonal Newton system.
43
+
44
+ ## Unknowns
45
+
46
+ At a node of an ordinary region (a dielectric, a semiconductor, a resolved electrolyte), the
47
+ unknowns are:
48
+
49
+ - $`\hat\phi = F\phi/RT`$, the dimensionless (bookkeeping) electrostatic potential;
50
+ - $`\eta_i = \bar\mu_i/RT`$, the dimensionless electrochemical potential of each species present.
51
+
52
+ Each block holds only the unknowns that exist at its node, up to $`1 + (\text{number of species})`$.
53
+ A conductor region's nodes hold its carrier's $`\eta`$ and the flux through its single segment
54
+ ([metal regions](#metal-regions)); an insertion host has no $`\phi`$ ([statistics](#statistics)), nor
55
+ does a charge-free region that nothing fixes it in (an oxide between silicon and a gate keeps its
56
+ $`\phi`$); a species absent from a material has no $`\eta`$ there; and each
57
+ interface adds a block of its own, for what crosses it ([interfaces](#interfaces-doubled-nodes-plus-a-flux-node)).
58
+
59
+ Concentrations follow from these through each material's statistics, $`c = c(\zeta)`$ with
60
+ $`\zeta_i = \eta_i - \mu^\circ_i/RT - z_i \hat\phi`$ (ideal: $`c_i = c_{\mathrm{ref},i} \cdot e^{\zeta_i}`$;
61
+ see [Statistics](#statistics) below). Working in $`\bar\mu`$ rather than $`c`$ is a deliberate
62
+ trade-off:
63
+
64
+ - **For $`\bar\mu`$:** concentrations are positive automatically, with no clamping (which would itself
65
+ break conservation). Equilibrium is exactly flat $`\bar\mu`$. Contacts, alignments, affinities and
66
+ Butler–Volmer are all natural in $`\bar\mu`$.
67
+ - **Against $`\bar\mu`$:**
68
+ - Storage is nonlinear in the unknowns, so conservation holds to Newton's tolerance rather
69
+ than trivially (see [Conservation](#conservation)).
70
+ - A species' equations scale with its own concentration, so where it's scarce (minority
71
+ carriers), its $`\bar\mu`$ is weakly determined and Newton can swing it wildly. Damping and time-step
72
+ control deal with that.
73
+
74
+ Concentration variables (the usual TCAD choice) would conserve every iteration exactly, but
75
+ their positivity fixes are fiddly or non-conservative. Slotboom variables $`\exp(\eta)`$ overflow at
76
+ modest voltages.
77
+
78
+ ### Extra precision where it matters: compensated μ̄
79
+
80
+ A majority carrier carrying a small current needs a quasi-Fermi step between neighbouring nodes
81
+ far below the resolution of $`\eta`$ itself. For example, holes in 1e16 cm⁻³ p-silicon carrying a
82
+ µA/m² reverse current need $`\Delta\eta \approx`$ 3e-19 across a cell, while $`\eta \approx 20`$ has an
83
+ ulp of 3.5e-15. In plain doubles that current would come out as exactly zero. (Concentration variables
84
+ hit the same wall, as cancellation between huge drift and diffusion terms.)
85
+
86
+ So every unknown is stored as a compensated double-double, `u + uLo`:
87
+
88
+ - Only *differences* of unknowns use the low part:
89
+ $`\Delta\eta = (\mathrm{hi}_R - \mathrm{hi}_L) + (\mathrm{lo}_R - \mathrm{lo}_L)`$. Neighbouring
90
+ values are close, so $`\mathrm{hi}_R - \mathrm{hi}_L`$ is exact (Sterbenz), and $`\Delta\eta`$ is
91
+ recovered to ~1e-30 absolute.
92
+ - Updates use an error-free two-sum.
93
+ - The Jacobian and linear solve stay in plain doubles. Newton's fixed point is set by the
94
+ residual, not the Jacobian, so an inexact Jacobian only slows convergence. This is the
95
+ standard mixed-precision idea: residual in high precision, correction in low.
96
+
97
+ The cost is a few flops per unknown per iteration, lost in the noise. With it, left and right
98
+ contact currents agree to ~1e-10 down to µA/m² leakage; without it they disagreed by 3%.
99
+
100
+ ## Grid and geometry
101
+
102
+ Nodes sit at positions $`x_k`$, and segments join consecutive nodes. Each node owns a box reaching
103
+ halfway to each neighbour (a vertex-centred finite volume, the "box method"):
104
+ $`\mathrm{vol}_k = (h_{k-1} + h_k)/2`$.
105
+
106
+ Every region boundary is a **doubled node**: two nodes at the same $`x`$, one per region, each
107
+ owning only a half-box on its own side. So no segment ever straddles a material step, and every
108
+ box is entirely one material.
109
+
110
+ ```
111
+ region A region B
112
+ o-------o------o----o--o-o-o|o-o-o--o----o------o-------o
113
+ ^ doubled node: L and R at the same x
114
+ ```
115
+
116
+ Grids grade geometrically from `hmin` at each region end up to `hmax`. They're never refined
117
+ automatically, to avoid performance surprises. Instead, solutions warn when a double layer the
118
+ model is meant to resolve is coarser than the local Debye length.
119
+
120
+ ### What lives where
121
+
122
+ | Place | Holds |
123
+ |---|---|
124
+ | **Node** (box centre) | the unknowns $`\hat\phi`$, $`\eta_i`$, and hence $`c_i`$. The node's region supplies $`\mu^\circ`$, $`c_{\mathrm{ref}}`$, presence and fixed charge. Everything volumetric, times the box volume: storage $`\mathrm{vol}\cdot(c - c_{\mathrm{old}})/dt`$, space charge $`\mathrm{vol}\cdot(F\sum z c + \rho_{\mathrm{fixed}})`$, bulk reaction rates $`\mathrm{vol}\cdot\nu\cdot r`$. One Poisson row and one balance row per species. |
125
+ | **Segment** (always within one material) | particle fluxes $`N_i`$ (Scharfetter–Gummel, with the segment's $`D`$) and displacement $`D = -\varepsilon\Delta\phi/h`$ (the segment's $`\varepsilon`$). Each is computed **once** and added with + to the left box and − to the right box, which is what makes conservation telescope exactly. |
126
+ | **Interface** (zero width) | a zero-volume **flux node** between the doubled nodes (below). Its unknowns are what crosses ($`D_f`$ and each $`N_{f,i}`$); its rows are the surface's laws. |
127
+ | **Contact** | conditions on the outer face of the end node's half-box (below). |
128
+
129
+ Output arrays are indexed by node, so each interface $`x`$ appears twice and steps plot as true
130
+ vertical lines. Nothing is averaged across a material step: no harmonic means of $`D`$ or $`\varepsilon`$
131
+ across an interface, because no segment crosses one.
132
+
133
+ ## Fluxes
134
+
135
+ Within a segment (constant $`D`$, $`\mu^\circ`$, $`\varepsilon`$), the Scharfetter–Gummel flux is the exact
136
+ constant-flux solution for a linear potential:
137
+
138
+ ```math
139
+ N = \frac{D}{h} \left[ B(\Delta)\, c_L - B(-\Delta)\, c_R \right], \quad \Delta = z (\hat\phi_R - \hat\phi_L), \quad B(x) = \frac{x}{e^x - 1}
140
+ ```
141
+
142
+ It's evaluated in an algebraically identical, better-conditioned form. Using
143
+ $`B(-\Delta) = B(\Delta)e^\Delta`$ and $`c_R e^\Delta = c_L e^{\Delta\eta}`$:
144
+
145
+ ```math
146
+ N = -\frac{D}{h} \cdot B(\Delta) \cdot c_L \cdot \mathrm{expm1}(\eta_R - \eta_L)
147
+ ```
148
+
149
+ This is exactly zero at equilibrium (flat $`\bar\mu`$), and it's precise relative to the quasi-Fermi
150
+ difference instead of being a cancellation of two large terms. $`B`$ and $`B'`$ are evaluated from a
151
+ Taylor series near 0 and with `expm1` elsewhere, finite for all arguments.
152
+
153
+ **Advection** at velocity $`v`$ adds to the drift: in the segment's Scharfetter–Gummel flux, the
154
+ potential and the quasi-Fermi difference are both shifted by the cell Péclet number
155
+ $`\mathrm{Pe} = v h/D`$, $`\Delta \to \Delta - \mathrm{Pe}`$, $`\Delta\eta \to \Delta\eta - \mathrm{Pe}`$.
156
+ That's the exact constant-flux solution of $`N = v c - D(c' + c \psi')`$ across the cell, so pure
157
+ convection–diffusion is exact on any grid, upwinding itself automatically at large $`\mathrm{Pe}`$.
158
+
159
+ **Eddy mixing** is discretised on each segment as
160
+ $`N_i = -(D_{\mathrm{mix}}/h) \sum_j \bar P_{ij} \Delta\eta_j`$, where $`\bar P`$ is built from the
161
+ logarithmic mean of each concentration across the segment. The logarithmic mean makes a neutral
162
+ species' mixing flux exactly $`-D_{\mathrm{mix}} \Delta c/h`$. The flux is exactly zero at
163
+ equilibrium, and exactly current-free ($`z^\mathsf{T}\bar P = 0`$ for any $`\bar P`$ built this way). Its
164
+ full Jacobian, including the dependence of $`\bar P`$ on both ends, keeps Newton quadratic.
165
+
166
+ With non-ideal statistics, $`\ln c = \zeta - \mathrm{ex}`$, where the excess
167
+ $`\mathrm{ex} = \zeta - \ln(c/c_{\mathrm{ref}})`$ is zero for ideal statistics. The excess enters
168
+ exactly like an extra potential, taken linear along the segment as $`\phi`$ is, so the same formula
169
+ holds with $`\Delta = z(\hat\phi_R - \hat\phi_L) + \mathrm{ex}_R - \mathrm{ex}_L`$. That's the
170
+ excess-chemical-potential generalisation of Scharfetter–Gummel: still exactly zero at
171
+ equilibrium, since the expm1 factor is untouched. It's exact for a single diffusing species on
172
+ a lattice (whose excess is proportional to its grand potential, linear in $`x`$ at steady state)
173
+ and second order otherwise. Its Jacobian couples each flux to every species at both nodes through
174
+ $`K`$, which fills the blocks but keeps them tridiagonal.
175
+
176
+ ## Interfaces: doubled nodes plus a flux node
177
+
178
+ In the linear system, a zero-volume flux node sits between the two sides' nodes:
179
+
180
+ ```
181
+ … [node L] — [flux node f] — [node R] …
182
+ φ̂_L, η_L D_f, N_f,i φ̂_R, η_R
183
+ ```
184
+
185
+ Node L's balance rows treat $`D_f`$ and $`N_{f,i}`$ as outflow, and node R's treat them as inflow. The
186
+ flux node's own rows are the interface laws:
187
+
188
+ | Law | Row |
189
+ |---|---|
190
+ | $`\phi`$, `pinned` | $`\hat\phi_R - \hat\phi_L = \mathtt{dipole}/V_T`$ |
191
+ | $`\phi`$, `neutral` | $`D_f = 0`$ |
192
+ | $`\phi`$, `capacitive` | $`D_f + C \cdot V_T \cdot (\hat\phi_R - \hat\phi_L - \mathtt{dipole}/V_T) = 0`$ |
193
+ | species, equilibrium | $`\eta_R - \eta_L = 0`$ |
194
+ | species, blocked or absent on one side | $`N_f = 0`$ |
195
+ | species, conductance | $`N_f - G V_T (\eta_L - \eta_R)/(z^2 F) = 0`$ |
196
+ | face reaction $`k`$ | $`r_k - \mathrm{rate}_k(\mathrm{state}_L, \mathrm{state}_R) = 0`$, and each participant's edge node takes $`\nu \cdot r_k`$ |
197
+
198
+ Every row couples only neighbours, with no penalty terms. A face reaction's rate is an unknown
199
+ of the face block, so the two edge nodes it couples meet only through the block between them.
200
+ A species can take part in a reaction at a face and also cross it by a link (its flux slot)
201
+ at the same time; where it exists on both sides, that link must be given explicitly. A continuity
202
+ row has a zero diagonal block, but block Thomas still sees a non-singular block there once the
203
+ preceding elimination has run. Interface fluxes come out as unknowns, which is what the
204
+ solution reports per interface.
205
+
206
+ (An earlier design kept only the doubled nodes and put the flux in the right node's slot, with
207
+ the value read from the left node. That breaks tridiagonality, because the right node's other
208
+ segment then reaches two nodes back.)
209
+
210
+ A **sheet charge** at a face is booked in node R's Poisson row. With a `pinned` law that's
211
+ immaterial: the jump is fixed either way, and only the reported $`D_f`$ shifts by $`\sigma`$. With a
212
+ capacitive law it would matter, since the charge then sits on one plate or the other.
213
+
214
+ ## Contacts
215
+
216
+ The end node's balance rows are complete except for the flux through the outer face. So
217
+ **before** a row is replaced by a contact condition, its residual *is* that flux (or
218
+ displacement). Contact fluxes and terminal currents are recovered exactly this way, with no
219
+ extra unknowns. Then:
220
+
221
+ - **Species in equilibrium with the outside phase:** the balance row becomes the Dirichlet row
222
+ $`\eta = \mathtt{target}`$ (the known outside level).
223
+ - **$`\phi`$ `bulk`:** the Poisson row becomes local neutrality (total charge, mobile plus fixed, is
224
+ zero). The recorded residual is the outside's surface charge.
225
+ - **$`\phi`$ `pinned`:** the Poisson row becomes the Dirichlet row
226
+ $`\phi_{\mathrm{edge}} = V - \mathtt{zeroCharge}`$, again with the residual recorded as the outside's
227
+ charge.
228
+ - **$`\phi`$ `capacitive`:** a term $`C \cdot ((V - \mathtt{zeroCharge}) - \phi_{\mathrm{edge}})`$ joins the
229
+ Poisson row.
230
+ - **$`\phi`$ `neutral`:** nothing is added ($`D = 0`$ at the face).
231
+ - **Conductance links** add their fluxes to the balance rows. (Contacts carry no reactions:
232
+ an electrode is a conductor region with reactions at its face.)
233
+
234
+ A left-end "contact flux node" would have a singular first block for block Thomas, which is why
235
+ contact fluxes are read from residuals instead. After each step these readouts (contact and port
236
+ fluxes, contact displacements, the last segment's current) are evaluated at the converged state
237
+ from only the boxes they come from, the two end nodes and the port windows with the segments and
238
+ faces that touch them, in the same order as a full assembly, so they're identical to one.
239
+
240
+ ### Terminals
241
+
242
+ Every terminal (a contact, or a port) has a voltage $`V`$, held by its source or floating, and a
243
+ current into the device, $`I`$. Assembly records, for each:
244
+ - **$`B = \partial\,\mathrm{res}/\partial V`$**, a column: where its outside levels enter (Dirichlet
245
+ rows, conductance links, a gate's or pinned $`\phi`$ law);
246
+ - **$`I`$ and $`C = \partial I/\partial x`$**, a row. A contact's current is read from its end box
247
+ before the contact's own terms go in: the box's balance residuals are what must come through
248
+ the contact (as for the flux readouts), plus the displacement through its $`\phi`$ law,
249
+ $`(D_{\mathrm{in}} - D_{\mathrm{in},0})/dt`$. A port's is its sources over its window, with a held
250
+ level's read from the row it replaces.
251
+
252
+ A floating terminal's voltage is an extra unknown, with its circuit law as an extra row:
253
+ $`I - I_{\mathrm{set}} = 0`$ (driven by a current) or $`I - (V_{\mathrm{src}} - V)/R = 0`$ (behind a
254
+ resistance). These don't fit the block-tridiagonal matrix $`T`$ (a port couples to its whole
255
+ window), so they're solved by bordering, together with the spectators' conservation rows (see
256
+ [steady state](#steady-state)): with $`y = T^{-1}\,\mathrm{rhs}`$, $`X_k = T^{-1} B_k`$ and
257
+ $`Q_q = T^{-1} e_q`$ (the response to a unit pin), the update is
258
+ $`\delta = y + \sum Q_q \mu_q - \sum X_k\, \delta V_k`$, and the $`\mu`$ (pins) and $`\delta V`$ (terminals)
259
+ come from a small dense system of the extra rows. Each costs one more back-substitution per
260
+ Newton iteration.
261
+
262
+ Sources are read at the end of a step (implicit), or at the present time in a steady solve.
263
+ `advance()` lands on every waveform breakpoint and restarts its order there.
264
+
265
+ ## Statistics
266
+
267
+ Each node evaluates its material's statistics once per Newton iteration: $`c(\zeta)`$, the Jacobian
268
+ $`K = \partial c/\partial\zeta`$ ($`n \times n`$, symmetric positive definite), and the excess. Every
269
+ $`c`$-dependent term then differentiates through $`K`$:
270
+
271
+ - storage and space charge: $`\partial c_i/\partial\eta_j = K_{ij}`$,
272
+ $`\partial c_i/\partial\hat\phi = -(Kz)_i`$;
273
+ - reaction prefactors: $`\partial \ln c_i/\partial\eta_j = K_{ij}/c_i`$;
274
+ - the $`\phi`$ row's response, and the Debye length in resolution warnings, use $`z^\mathsf{T} K z`$, the
275
+ charge capacitance, in place of $`\sum z^2 c`$.
276
+
277
+ Nodes of ideal materials keep a separate fast path with $`K = \mathrm{diag}(c)`$ implied.
278
+
279
+ Model evaluation:
280
+
281
+ - **Explicit models** (Fermi–Dirac, lattice gas, insertion) evaluate $`c(\zeta)`$ in closed form.
282
+ - **Implicit models** solve a small problem per node: Redlich–Kister and tabulated OCVs by
283
+ safeguarded Newton in $`\mathrm{logit}(x)`$; Debye–Hückel by Newton in $`\ln c`$, whose excess
284
+ Hessian is rank one, so each step is a Sherman–Morrison update.
285
+ - **Fermi–Dirac integrals** $`\mathcal{F}_{1/2}`$ and $`\mathcal{F}_{-1/2}`$ use:
286
+ - an alternating series below $`x = -2`$;
287
+ - Sommerfeld's expansion above 50;
288
+ - piecewise Chebyshev fits (degree 23, five intervals) in between, built on first use
289
+ (~4 ms) from a trapezoid quadrature in $`u = \sqrt{t}`$.
290
+
291
+ Relative error is ~6e-15.
292
+
293
+ Where a *concentration* is prescribed rather than a potential (bath compositions, spectators'
294
+ initial amounts), each model inverts itself for the $`\zeta`$ of those species, holding the others.
295
+ The lattice gas does this in closed form, and the others with the same scalar or small Newton
296
+ iterations.
297
+
298
+ An insertion host's species depend on $`\phi`$ only through their neutral combination, where it
299
+ cancels, so its $`\phi`$ rows are identity rows ($`\phi`$ undefined, like a region with no charged
300
+ species). The current-continuity constraint between ion and carrier comes from their balance rows.
301
+
302
+ ## Metal regions
303
+
304
+ A metal node's only unknown is the carrier's $`\eta`$, its Fermi level. Its $`\phi`$ slot is free, and
305
+ it carries the carrier flux $`J`$ through the segment to the node's right instead. Each metal
306
+ segment then has two rows:
307
+
308
+ ```math
309
+ \begin{aligned}
310
+ &\eta_R - \eta_L + J/g = 0 && \quad\text{(Ohm's law, } g = \sigma RT/(z^2F^2h)\text{)} \\
311
+ &\dots + J\ (\text{out of } L),\ -J\ (\text{into } R) && \quad\text{in the carrier balances}
312
+ \end{aligned}
313
+ ```
314
+
315
+ This mixed form matters. A floating metal, held only by weak conductances $`G`$ such as its
316
+ electrode reactions, has its overall level set by $`G`$ alone, while every entry of an $`\eta`$-only
317
+ Jacobian is of order $`g`$. Eliminating it then computes the last pivot as $`g - g^2/(g + G) \approx G`$
318
+ with an absolute error of order $`\varepsilon \cdot g`$: digits are lost in proportion to $`g/G`$, and
319
+ beyond $`g/G \approx`$ 1e15 the pivot is noise or zero. ($`G`$ is already lost when the diagonal
320
+ $`g + G`$ is stored.) In mixed form the matrix holds $`1/g`$ instead, every coefficient is O(1), and
321
+ the limit $`\sigma \to \infty`$ is simply the constraint $`\eta_R = \eta_L`$. A
322
+ Grassmann–Taksar–Heyman-style elimination, which carries each row's leakage separately so that
323
+ pivots are built as sums, would also keep $`\eta`$ alone exact, but it needs to know which rows form
324
+ a conductance network.
325
+
326
+ A metal region is a single cell. Nothing is stored in its bulk, so $`\eta`$ is linear across it in
327
+ transients too, and one segment is exact. That also keeps $`g = \sigma RT/(z^2F^2L)`$ as small as the
328
+ region allows.
329
+
330
+ A metal's bulk holds no charge. Its surface charge at a capacitive face is the face's
331
+ displacement, booked as a sheet of excess carriers in the edge node's half-box. That gives the
332
+ edge node a storage term in $`D_f`$ (the neighbouring block), so transients, conservation and
333
+ impedance all see the charging current. A `pinned` law isn't offered at a metal face.
334
+ With nothing on the metal side depending on $`D_f`$, the steady system's flux block would be
335
+ singular to left-to-right elimination, and the charging current would need the charge read two
336
+ blocks away.
337
+
338
+ Electrode reactions at a conductor's face take its carrier at its edge node, like any face
339
+ reaction. Whether a stretch is fed is decided by its conserved combinations: weightings $`w`$ of
340
+ the stretches' amounts that no reaction changes ($`w \cdot \nu = 0`$ for every face and bulk
341
+ reaction) and that nothing outside feeds ($`w = 0`$ on stretches reached by a contact or a port),
342
+ the null space of that stoichiometry. A stretch in none of them is fed. So Ag⁺ between silver
343
+ electrodes is fed (Ag⁺ + e⁻ ⇌ Ag(s), the electrons fed by the contacts), and the steady equations
344
+ are solved directly; Fe³⁺ and Fe²⁺ between platinum electrodes aren't, since Fe³⁺ + e⁻ ⇌ Fe²⁺
345
+ conserves the iron whatever the electrons do, so their total is kept by huge steps (below). A
346
+ floating conductor starts uncharged, with its carrier's level in equilibrium with the first
347
+ reaction on its left face that takes it.
348
+
349
+ ## Internal ports
350
+
351
+ A port adds a source per volume to the balance rows of the nodes in its window, after every
352
+ other term at those nodes and before the contacts. A held (`'equilibrium'`) level replaces the
353
+ balance row with a Dirichlet row. The port's flux is then that row's residual, read just
354
+ before replacement, exactly as at a contact. Conductance and exchange links are linear in
355
+ $`(\bar\mu_{\mathrm{out}} - \bar\mu)`$. Everything stays on the node's own block. A contact's flux
356
+ readout at an end node already includes any port source there, so contact and port fluxes always
357
+ balance. A stretch reached by a port counts as fed, and its conservation intake includes the
358
+ port's flux. On a metal, a port spans both nodes of its single cell, with $`G`$ per area spread as
359
+ $`G/L`$ per volume, so each node gets half.
360
+
361
+ ## Bulk reactions
362
+
363
+ $`r = k_f \prod c_R^\nu \cdot (-\mathrm{expm1}(-a))`$, with $`a = A/RT`$ computed from the
364
+ compensated $`\eta`$. That's mass action with the reverse rate implied by the standard potentials:
365
+ exactly zero at $`A = 0`$, and free of cancellation near equilibrium. Sources enter each balance as
366
+ $`\nu \cdot r`$, with one $`r`$ per reaction per node, so every moiety (a combination that no reaction
367
+ changes) telescopes exactly.
368
+
369
+ ## Assembly
370
+
371
+ Each region is assembled by the kernel for its kind, over all its nodes and segments at once:
372
+ - **conductor:** its carrier's balance, and Ohm's law in mixed form (below);
373
+ - **dilute** (ideal statistics, the fast path): storage, space charge and Scharfetter–Gummel
374
+ fluxes, written straight into the blocks by local index; a dielectric is the case with no
375
+ species;
376
+ - **concentrated** (any statistics): the same through $`K = \partial c/\partial\zeta`$ and the
377
+ excess potential.
378
+
379
+ Faces, ports and contacts follow. Every kernel writes only the unknowns its region has.
380
+
381
+ ## Linear algebra
382
+
383
+ Block Thomas with partial pivoting *within* each diagonal block (zero diagonal entries are
384
+ fine) but not *between* blocks. Rows are equilibrated (each divided by its largest Jacobian
385
+ entry) before factorisation. Storage is flat `Float64Array`s, and factor and solve allocate
386
+ nothing. For 300 nodes × 7 unknowns, one factor and solve takes ~0.4 ms in Node 22.
387
+
388
+ Block sizes differ from node to node: each block holds only the unknowns that exist there. A
389
+ node has $`M = 1 + n`$ slots ($`\hat\phi`$ and each species' $`\eta`$), but these aren't unknowns:
390
+ - a species absent from its material;
391
+ - $`\phi`$ where it's undefined;
392
+ - a conductor node's other species, and the flux slot of its last node;
393
+ - a blocked interface flux, or the displacement of a `'neutral'` face;
394
+ - a floating terminal's spare slots.
395
+
396
+ The state vector keeps every slot (one that isn't an unknown keeps its value), while the
397
+ residual, the update and the Jacobian are compact: assembly writes each entry straight into
398
+ blocks of $`m_b \times m_b`$, $`m_b \times m_{b-1}`$ and $`m_b \times m_{b+1}`$, through each slot's row
399
+ within its block, and drops terms in slots that aren't unknowns. Elimination costs $`\sum m_b^3`$
400
+ instead of $`n \cdot M^3`$, which pays wherever species are confined to some regions: for n-Si
401
+ against KCl, three unknowns of five at every node, it's a fifth of the work. A finite-difference
402
+ test checks the compact Jacobian column by column on devices that cover every assembly path.
403
+
404
+ ## Newton
405
+
406
+ - The update of the device's potentials ($`\hat\phi`$, $`\eta`$) is limited to 10 thermal units per
407
+ iteration by uniform scaling, which scales a floating terminal voltage's update too. That
408
+ voltage doesn't count toward the limit: it enters only linearly (conductance links, held
409
+ levels, a capacitive face), so a large swing in it is safe. When it did count, a port driven by
410
+ a current pulse, its voltage collapsing by hundreds of volts as the current switched off,
411
+ dragged every unknown along 10 thermal units at a time (and a swing past 1e4 tripped the
412
+ divergence check): the strong-injection Haynes–Shockley benchmark took 55% more
413
+ factorisations.
414
+ - Converged when a full, undamped update is below 1e-10 (thermal units). Quadratic
415
+ convergence makes the remaining residual negligible.
416
+ - Or converged as far as round-off allows: updates already below 1e-6 thermal units (~26 nV)
417
+ that have stopped shrinking for two iterations. A badly conditioned system's round-off floor
418
+ can sit above 1e-10: a strictly neutral material on a short step, where $`\phi`$ is fixed only
419
+ through fluxes that the storage term dwarfs (a condition number of about $`h^2/(D \cdot dt)`$,
420
+ much reduced by the change of variables below), converges quadratically to ~1e-9 and then
421
+ rattles there. Short steps come right after every waveform breakpoint, so without this a
422
+ cyclic voltammogram in a neutral electrolyte stalled at its turns.
423
+ - Clear divergence (device updates beyond 1e4, or ten times the first after six iterations)
424
+ bails out early, so the caller can take a smaller step.
425
+ - When a steady solve fails, the solution's warnings say how nearly singular the system was,
426
+ and where. A running error bound through the factorisation compares each pivot with the
427
+ magnitudes it was formed from, $`\log_{10}(\sum|\mathrm{terms}| / |\mathrm{pivot}|)`$: the digits
428
+ lost. A part of the device held only weakly, such as a floating region coupled through tiny
429
+ conductances, loses about $`\log_{10}(g/G)`$ and fails near 15. It's not reported for solves that
430
+ converge, since digits can be lost harmlessly too (a saturated species whose $`\eta`$ nothing
431
+ depends on).
432
+ - The last iteration usually only confirms convergence (updates go like 4e-3, 7e-6, 3e-11).
433
+ Stopping one iteration early on a convergence-rate estimate would save a third of the work,
434
+ but leave ~1e-11 in $`\eta`$ per step, which shows up as conservation drift, so it isn't done.
435
+ - Tried and rejected for large jumps (the retry at 3 thermal units per iteration takes 30–50
436
+ iterations for a 1.4 V jump on a pn diode): per-component logarithmic damping, as in
437
+ SPICE junction limiting, and an adaptive limit that grows while updates shrink. Both
438
+ diverged more often and cost more overall.
439
+
440
+ ## Time stepping
441
+
442
+ - **Backward Euler** is the default for `step(dt)`. It recursively halves the interval wherever
443
+ Newton fails, always landing exactly on $`t + dt`$. Minority carriers that must rise by many
444
+ orders of magnitude after a bias step are the typical cause.
445
+ - **Contact displacement** at the start of a step is carried over from the end of the previous
446
+ one, including across `set()`, so a step change in a gate voltage shows up as displacement
447
+ current.
448
+ - **BDF2** (`method: 'bdf2'`) is the variable-step second-order backward difference formula.
449
+ With $`\omega = dt/dt_{\mathrm{prev}}`$, its storage term is
450
+ $`[a_0 c - (1+\omega) c_n + \omega^2/(1+\omega)\, c_{n-1}]/dt`$, where $`a_0 = (1+2\omega)/(1+\omega)`$.
451
+ - It's written in backward-Euler form, $`(c - c^*)/(dt/a_0)`$ with the history reference
452
+ $`c^* = [(1+\omega) c_n - \omega^2/(1+\omega)\, c_{n-1}]/a_0`$. So assembly is shared, and the
453
+ displacement histories (for terminal currents) take the same combination.
454
+ - Conservation still telescopes. Closed amounts stay exact, since $`c^*`$ has the same amount
455
+ as $`c_n`$ when $`c_n`$ and $`c_{n-1}`$ did. Open stretches add the history term
456
+ $`\sum v (c^* - c_n)`$ to their intake.
457
+ - A step ratio above 2 falls back to backward Euler for that step, to stay safely within
458
+ variable-step BDF2's zero-stability limit ($`1 + \sqrt{2}`$).
459
+ - **Adaptive stepping** (`advance`):
460
+ - The local error of each step is estimated against an explicit predictor through the
461
+ previous states: quadratic after a BDF2 step, scaled by $`C_c/(C_c + C_p)`$, with
462
+ $`C_c = h^3(1+\omega)^2/(\omega(1+2\omega))`$ and $`C_p = h(h+h_1)(h+h_1+h_2)`$; linear after
463
+ backward Euler, scaled by $`h/(2h + h_1)`$. The first step, with no history, is checked by step
464
+ doubling.
465
+ - The error is measured in thermal units over every state potential ($`\hat\phi`$ where defined,
466
+ each present $`\eta`$, a floating terminal voltage). That includes species far below their
467
+ largest concentration, such as minority carriers ahead of a diffusion front. Weighting
468
+ them down (a mixed relative/absolute tolerance,
469
+ $`\delta\eta \cdot c/(c + 10^{-10} c_{\mathrm{max}})`$) took a third fewer steps on a pn turn-on,
470
+ but the tail's errors are swept into the front, and at matched accuracy of the terminal
471
+ current it cost 50% more.
472
+ - A step is rejected above `tol`. The next step size is
473
+ $`h \cdot \min(2, \max(0.2, 0.9\, (\mathtt{tol}/\mathrm{err})^{1/(p+1)}))`$.
474
+ - Newton failure quarters the step; on the first step after a start or a jump in a device
475
+ with a strictly neutral material, longer steps are tried first, since there shorter ones are
476
+ worse conditioned.
477
+ - A target within round-off of the present time (1e-10 relative), such as an animation
478
+ frame's that lands a hair past a breakpoint just reached, is snapped to rather than
479
+ stepped to: a step of 1e-13 s can't be resolved.
480
+ - Newton starts each step from the state extrapolated through the last three, which saves
481
+ about a third of the iterations.
482
+ - The wall-clock budget is checked between steps.
483
+
484
+ ## Small-signal impedance
485
+
486
+ About a steady state, a small sinusoidal excitation at one terminal gives, to first order,
487
+
488
+ ```math
489
+ \begin{aligned}
490
+ &(J + i\omega M)\, \delta x + \sum_k B_k\, \delta V_k = -B_T\, \delta V_T && \quad\text{(the grid's rows)} \\
491
+ &(C_k + i\omega C'_k)\, \delta x + (\partial I_k/\partial V_k + i\omega\dots)\, \delta V_k = \delta I_T\, \delta_{kT} && \quad\text{(each floating terminal's circuit row)}
492
+ \end{aligned}
493
+ ```
494
+
495
+ - **$`J`$** is the steady Jacobian, **$`M`$** the Jacobian of the time-derivative terms (storage
496
+ $`v \cdot K`$, displacement), and likewise $`C`$ and $`C'`$ for the terminal currents. The
497
+ time-derivative parts are read off two assemblies, $`J(dt) = J + M/dt`$, with a tiny $`dt`$ so that
498
+ the subtraction loses nothing.
499
+ - **At the measured terminal $`T`$,** a held voltage is perturbed ($`\delta V_T = 1`$, and $`\delta I_T`$
500
+ read from its $`C`$ row), or a driven current ($`\delta I_T = 1`$, and $`\delta V_T`$ solved for).
501
+ $`Z = \delta V_T/\delta I_T`$, with $`I`$ into the device. The other terminals keep their drives:
502
+ held ones at AC ground, driven ones open.
503
+
504
+ Each frequency costs one complex block-Thomas factorisation (rows scaled by their largest
505
+ entry), plus one back-substitution per floating terminal for the bordering. Conserved (blocked)
506
+ species make $`J`$ singular, but $`J + i\omega M`$ isn't for $`\omega > 0`$: at low frequency a blocking
507
+ device looks like a capacitor, as it should.
508
+
509
+ The factorised system is used as a preconditioner, not trusted alone: each solve runs GMRES with
510
+ $`J \cdot v`$ taken from the residual itself, by a central difference ($`\eta`$ perturbed in the
511
+ state's low word, $`\hat\phi`$ and the face unknowns in the high one). The assembled $`J`$ holds a
512
+ flux's dependence on $`\eta_L`$ and $`\eta_R`$ as two entries; in an inversion layer both are huge
513
+ (~1e11), and for a nearly uniform $`\delta\eta`$ they cancel to round-off of order 1e-5, while the
514
+ residual takes the $`\eta`$ difference first, in double-double. That round-off had given a MOS
515
+ capacitor's inversion layer, fed by minority diffusion from a bulk with ~1e3 electrons per cm³ (a
516
+ time constant of minutes), an exchange path that followed the gate at 1 Hz. Where the plain solve
517
+ is already accurate, one application of the operator confirms it; the bench's impedance cases
518
+ take about twice as long as before.
519
+
520
+ ## Steady state
521
+
522
+ - **If every species stretch is fed by a contact,** nothing is conserved on its own, and the
523
+ true steady equations are solved directly ($`dt = \infty`$, no storage term). This matters
524
+ because slow physics can take seconds, far beyond any $`L^2/D`$ estimate, and stepping would
525
+ never finish. An example is exponentially scarce minority carriers slowly filling an inversion
526
+ layer behind a Schottky contact.
527
+ - **Spectators** (a species blocked all round, mobile throughout its stretch) are solved
528
+ directly too. In steady state the sum of a spectator's balance rows over its stretch is zero
529
+ identically, so one of them (the first node's) is redundant, and it's replaced by the
530
+ conservation of the amount, $`\sum v c = \mathtt{amount}`$. That row is dense, so the solve is
531
+ bordered: the matrix is factorised with a pin (an identity row) in its place, which makes the
532
+ stretch's level a well-conditioned unknown, and the response to a unit pin (one extra
533
+ back-substitution per spectator) is added in the amount that satisfies the constraint, from
534
+ a $`k \times k`$ system for $`k`$ spectators.
535
+ - **Conserved combinations** (moieties) of reacting stretches are solved the same way: the
536
+ total iron of Fe³⁺, Fe²⁺ and FeCl²⁺ in a closed cell with a complexation reaction, say. The
537
+ basis of combinations $`w`$ comes from the null space of the stoichiometry. In steady state the
538
+ $`w`$-weighted sum of their balance rows is zero identically (the reactions cancel by
539
+ $`w \cdot \nu = 0`$, and nothing crosses the stretches' ends), so one of them is replaced by
540
+ $`\sum_k w_k \sum v c_k =`$ the combination's amount, bordered like a spectator's. (Spectators
541
+ are the one-stretch case.)
542
+
543
+ Holding the amount through a storage term at huge $`dt`$ instead is badly conditioned: the
544
+ stretch's level is then held only by $`v \cdot c/dt`$, against internal conductances $`D \cdot c/h`$
545
+ larger by $`D \cdot dt/(hL)`$, around 1e12 for a micron-scale cell (see
546
+ [metal regions](#metal-regions) for the same cancellation). Newton then needs dozens of
547
+ iterations, or fails.
548
+ - **Otherwise** (immobile spectators, which conserve node by node, or a combination that
549
+ includes a floating conductor), backward-Euler steps at a huge $`dt`$ (10⁶ × the slowest
550
+ diffusion time) keep the storage term, which pins each conserved amount exactly: sum a
551
+ species' rows and the fluxes cancel. $`dt`$ grows ×10 (capped) while the state still moves.
552
+ Before each huge step, each spectator's level is shifted uniformly to restore its amount
553
+ exactly (in one step for ideal statistics, by Newton on the shift otherwise). It guards
554
+ against round-off creeping through the vanishing storage term. The solve finishes with one
555
+ step at the base giant $`dt`$, where pinning is tight.
556
+ - **If a direct solve diverges,** it's retried once with tighter damping (3 thermal units per
557
+ iteration). That's enough for most large jumps, such as a cold start at forward bias.
558
+ - **Generation continuation.** A device with generation reactions (species made only from, or
559
+ turned only into, fixed reservoirs, such as photogeneration from a photon reservoir) can be
560
+ held far from equilibrium even with its terminals level, where bias continuation can't help.
561
+ If a direct solve fails there, the generation rates are scaled down to 10⁻¹² and ramped back
562
+ up, ×100 a step while each solve converges (warm from the last) and by the square root of the
563
+ factor when one doesn't. An illuminated 80 µm silicon diode solves cold this way in about 80
564
+ iterations.
565
+ - **If that fails too,** source continuation ramps the right terminal's voltage to its target.
566
+ It ramps from the voltage of the last converged solve when the state is that solution, and
567
+ otherwise from level terminals, where a cold start is consistent. The ramp step starts at
568
+ 1/8 of the way, grows ×1.5 on success and shrinks ×4 on failure.
569
+ - **If Newton still fails,** $`dt`$ ramps up from a small value (pseudo-transient continuation),
570
+ ending in the direct solve where applicable.
571
+
572
+ `solve()` does not advance the clock.
573
+
574
+ ## Conservation
575
+
576
+ - **Discretely exact.** Face fluxes are computed once and added ± to both neighbours, the
577
+ interface flux unknowns do the same, and reactions are $`\nu \cdot r`$. So summed over boxes,
578
+ everything telescopes, and the *converged* discrete solution conserves every amount and total
579
+ charge to round-off.
580
+ - **The iterate conserves to Newton's tolerance.** Storage is nonlinear in $`\eta`$, so each step's
581
+ drift is its summed residual. Converging to 1e-10 in $`\eta`$ makes that ~1e-15 per step in
582
+ practice.
583
+ - **Reported.** Every solution's `conservation` lists each species stretch: its amount, its
584
+ reference, the time-integrated flux through its contacts, and the resulting drift.
585
+
586
+ ## Strictly neutral materials and unresolved double layers
587
+
588
+ A material with $`\varepsilon = 0`$ has local neutrality in place of Poisson, and no displacement
589
+ along its segments. $`\phi`$ there is a bookkeeping multiplier, fixed by neutrality given the
590
+ $`\bar\mu`$'s. It's reported as NaN if no charged species is present and nothing couples the region
591
+ electrostatically. The bulk $`\varepsilon \to 0`$ limit is smooth: the Poisson row simply
592
+ degenerates into neutrality. The same Newton count holds from $`\varepsilon_r = 78.5`$ down to 1e-6,
593
+ as in a linear prototype that motivated the design.
594
+
595
+ Interfaces are where it gets subtle. With a `pinned` law and double layers the grid can't
596
+ resolve (because $`\varepsilon`$ is tiny, or the cells are coarse), the charge the interface needs is
597
+ crammed into the two half-boxes beside it. The bulk stays right, but the interface compositions
598
+ and $`D_f`$ become mesh-dependent. The true $`\varepsilon \to 0`$ limit has no interface charge and a
599
+ free Donnan-type jump, which is the `neutral` law. The defaults follow from this: `neutral`
600
+ between two $`\varepsilon = 0`$ materials, `pinned` otherwise (and `pinned` between two
601
+ $`\varepsilon = 0`$ materials is an error: with no field on either side, nothing would determine its
602
+ charge). Solutions warn when a resolved-model double layer is under-resolved.
603
+
604
+ On a transient step, strictly neutral nodes are solved in better-conditioned terms. There, a
605
+ change of $`\hat\phi`$ with every $`\eta_i`$ shifted by $`z_i`$ times it leaves every concentration as it
606
+ was, so storage and neutrality don't see it. Only the fluxes do. But in $`(\hat\phi, \eta)`$ storage
607
+ and neutrality see it as pairs of huge entries that cancel, and round-off in that cancellation, of
608
+ order storage/flux ~ $`h^2/(D \cdot dt)`$ times the range of concentrations, swamps the fluxes that
609
+ fix it: a trace ion beside 3 M KCl lost about ten digits, and Newton stalled. So at each node that
610
+ stays neutral (interior nodes, and edges at `neutral` faces; not the edge of a capacitive or
611
+ pinned face, which holds the face's charge), assembled without storage and neutrality:
612
+ - rows: the balance of the most abundant charged species (by $`z^2 c`$) becomes
613
+ $`\sum (z_i/z_k) \times`$ each balance, which without storage is current continuity;
614
+ - columns: the unknowns become $`\hat\phi'`$ and $`\eta'_i = \eta_i - z_i \hat\phi`$ (the update is
615
+ mapped back), in which storage and neutrality have no $`\hat\phi'`$ term at all;
616
+ - then storage (on the other balances) and neutrality go in, exactly, in those terms.
617
+
618
+ A start-of-step net charge (round-off in a solved state) isn't carried over: the neutrality row
619
+ holds the new state neutral. Steady solves and the impedance keep the plain rows (there's no
620
+ storage at $`dt = \infty`$, and the impedance reads the storage matrix from them).
621
+
622
+ A sub-grid Gouy–Chapman law, treating the diffuse layers analytically when $`\lambda_D \ll h`$, is on
623
+ the [roadmap](../ROADMAP.md). It would handle macroscopic devices with real double-layer charge at
624
+ any grid, and tends to `neutral` as $`\varepsilon \to 0`$.
625
+
626
+ At an $`\varepsilon = 0`$ | $`\varepsilon > 0`$ face, the neutral side's boundary half-box holds the
627
+ face's charge. That slightly perturbs the carrier density there, by an amount that depends on the
628
+ mesh (0.3% on a Schottky test with the metal as an $`\varepsilon = 0`$ region). For a metal, a metal
629
+ region avoids this: its surface charge is a sheet that leaves its Fermi level untouched.