driftlet 0.10.0 → 0.11.4
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.
- package/README.md +46 -36
- package/docs/device.md +24 -6
- package/docs/numerics.md +180 -21
- package/docs/reliability.md +122 -0
- package/llms.txt +11 -6
- package/package.json +2 -1
- package/src/blockTridiagonal.js +17 -4
- package/src/checks.js +17 -10
- package/src/difference.js +142 -0
- package/src/index.js +22 -6
- package/src/solution.js +8 -2
- package/src/solver.js +964 -125
- package/types/blockTridiagonal.d.ts +9 -1
- package/types/difference.d.ts +30 -0
- package/types/index.d.ts +2 -1
- package/types/solver.d.ts +49 -8
package/README.md
CHANGED
|
@@ -13,35 +13,24 @@ to be the engine of live, thermodynamically honest web demos of charge transport
|
|
|
13
13
|
semiconductors, electrochemical cells, membranes and solid ionic conductors. It handles any mix
|
|
14
14
|
of charged and neutral species, Poisson electrostatics (or strict neutrality), bulk and
|
|
15
15
|
interfacial reactions, heterointerfaces, metals and external circuits, in steady state, in time
|
|
16
|
-
and as small-signal impedance.
|
|
17
|
-
|
|
16
|
+
and as small-signal impedance. It's small enough to run in a web page: no dependencies,
|
|
17
|
+
millisecond solves.
|
|
18
18
|
|
|
19
19
|
[Live demos](https://markblundeberg.github.io/driftlet/demos/) · [npm](https://www.npmjs.com/package/driftlet) ·
|
|
20
20
|
[reading level diagrams](docs/visualization.md) · [device reference](docs/device.md) ·
|
|
21
21
|
[agent guide](llms.txt)
|
|
22
22
|
|
|
23
|
+
**Built to be relied on.** A drift–diffusion solver that handles a textbook pn junction is a
|
|
24
|
+
weekend's work; one that keeps working on the devices people actually build is not. driftlet is
|
|
25
|
+
tested two ways: every physics feature against an analytic result or an independent code, and
|
|
26
|
+
thousands of randomly generated devices (junctions, MOS capacitors, solar cells, electrolyte
|
|
27
|
+
cells, electrodes) put through cold solves, sweeps, transients and impedance, every result
|
|
28
|
+
checked. A solve that fails reports it, with what went wrong where it can tell.
|
|
29
|
+
[How far to trust it](docs/reliability.md) has the details, the numbers and the known limits.
|
|
30
|
+
|
|
23
31
|
**Status: 0.x.** Released on npm and used by the demos, but the API may still change between
|
|
24
32
|
minor versions.
|
|
25
33
|
|
|
26
|
-
## Is it for your problem?
|
|
27
|
-
|
|
28
|
-
The same equations go by different names in different fields. If your problem is one-dimensional
|
|
29
|
-
(planar, or radial: spheres and cylinders, or any cross-section varying along x), it's probably here:
|
|
30
|
-
|
|
31
|
-
| If you work on | you may call it | start from | checked against |
|
|
32
|
-
|---|---|---|---|
|
|
33
|
-
| Semiconductor devices | drift–diffusion, van Roosbroeck, quasi-Fermi levels, Scharfetter–Gummel; pn, Schottky, MOS, heterojunctions; thin-film transistors (gradual channel, in the [saturation demo](https://markblundeberg.github.io/driftlet/demos/saturation.html#transistor)) | [first example](#a-semiconductor-junction), [pn](https://markblundeberg.github.io/driftlet/demos/pn.html) and [MOS](https://markblundeberg.github.io/driftlet/demos/mos.html) demos, [`contacts`](test/contacts.test.js), [`metal`](test/metal.test.js) tests | exact built-in potential, Shockley J–V, depletion charge, MOS C–V |
|
|
34
|
-
| Solar cells | photogeneration, radiative and SRH recombination | [solar](https://markblundeberg.github.io/driftlet/demos/solar.html) [organic (excitons)](https://markblundeberg.github.io/driftlet/demos/organic.html) and [perovskite (mobile ions)](https://markblundeberg.github.io/driftlet/demos/perovskite.html) demos, [`ionmonger`](test/ionmonger.test.js), [`reactions`](test/reactions.test.js) and [`generation`](test/generation.test.js) tests: generation is a reaction from a photon reservoir (uniform, or Beer–Lambert with `photogeneration()`), SRH as a rate law, in the bulk and at faces | `J_sc = qG(L_n + L_p + W)`; J_sc under Beer–Lambert against collection theory; Shockley J–V in the dark |
|
|
35
|
-
| Electrochemistry, corrosion | Nernst–Planck, concentration polarization, limiting current, Butler–Volmer, Warburg, cyclic voltammetry, salt bridges and liquid junctions, mixed potentials, electrodes under a film | [second example](#an-electrochemical-cell), [cyclic voltammetry](https://markblundeberg.github.io/driftlet/demos/redox.html), [Daniell cell](https://markblundeberg.github.io/driftlet/demos/daniell.html), [saturation](https://markblundeberg.github.io/driftlet/demos/saturation.html), [impedance](https://markblundeberg.github.io/driftlet/demos/impedance.html) and [liquid-junction](https://markblundeberg.github.io/driftlet/demos/junction.html) demos, [`circuit`](test/circuit.test.js), [`kinetics`](test/kinetics.test.js), [`impedance`](test/impedance.test.js), [`junction`](test/junction.test.js), [`ports`](test/ports.test.js) tests | `i_lim·tanh(V/4V_T)`, Butler–Volmer closed form, finite-length Warburg; junction potentials against Henderson, Planck, JPCalc and LJPcalc; Wagner–Traud |
|
|
36
|
-
| Batteries, intercalation | OCV, insertion hosts, chemical diffusion | [insertion demo](https://markblundeberg.github.io/driftlet/demos/insertion.html), [`statistics`](test/statistics.test.js) test | composition vs OCV, π²D/4L² relaxation |
|
|
37
|
-
| Double layers, colloids | Poisson–Boltzmann, Gouy–Chapman–Stern, Debye screening, crowding (Bikerman) | [double-layer demo](https://markblundeberg.github.io/driftlet/demos/double-layer.html), [`equilibrium`](test/equilibrium.test.js) test | Gouy–Chapman charge and profile, Kilic–Bazant–Ajdari |
|
|
38
|
-
| Membranes, desalination | Donnan, ion exchange, liquid junctions, water dissociation | [membrane demo](https://markblundeberg.github.io/driftlet/demos/membrane.html), [`equilibrium`](test/equilibrium.test.js), [`neutral`](test/neutral.test.js) tests | Donnan partition, Planck EMF |
|
|
39
|
-
| Solid-state ionics | mixed ionic–electronic conduction, defect chemistry, mobile ions | [`statistics`](test/statistics.test.js), [`reactions`](test/reactions.test.js) tests | mass action from standard potentials |
|
|
40
|
-
| Biophysics, nanofluidics | charged nanochannels and pores, resting potentials, Goldman–Hodgkin–Katz, Nernst, Donnan, pumps and leaks | [charged-nanochannel](https://markblundeberg.github.io/driftlet/demos/channel.html), [resting-potential](https://markblundeberg.github.io/driftlet/demos/cell.html) and [liquid-junction](https://markblundeberg.github.io/driftlet/demos/junction.html) (a patch pipette's) demos, [`pore`](test/pore.test.js) test; a membrane as a face: a capacitor with ion permeabilities, and the Na⁺/K⁺ pump as a reaction on it ([`membrane`](test/membrane.test.js) test) | GHK potential and current–voltage curve; a resolved lipid layer; Mullins–Noda; the pump's static head; Donnan |
|
|
41
|
-
|
|
42
|
-
The tests are worked setups, each with its analytic check, so they double as recipes. The
|
|
43
|
-
[validation table](#validation) lists them all.
|
|
44
|
-
|
|
45
34
|
## Install
|
|
46
35
|
|
|
47
36
|
```sh
|
|
@@ -55,7 +44,7 @@ import { bandDiagram } from 'driftlet/plot'; // a level diagram as an SVG str
|
|
|
55
44
|
```
|
|
56
45
|
|
|
57
46
|
Or straight from a CDN in a page, pinned to a version:
|
|
58
|
-
`https://cdn.jsdelivr.net/npm/driftlet@0.
|
|
47
|
+
`https://cdn.jsdelivr.net/npm/driftlet@0.11.4/src/index.js` (and `src/kit.js`, `src/plot.js`).
|
|
59
48
|
TypeScript declarations ship in the package. Writing a demo with an LLM agent? Point it at
|
|
60
49
|
[`llms.txt`](llms.txt).
|
|
61
50
|
|
|
@@ -143,7 +132,7 @@ them locally, serve the repository root (e.g. `python3 -m http.server`) and open
|
|
|
143
132
|
| | | |
|
|
144
133
|
|---|---|---|
|
|
145
134
|
| [](https://markblundeberg.github.io/driftlet/demos/pn.html) | [](https://markblundeberg.github.io/driftlet/demos/solar.html) | [](https://markblundeberg.github.io/driftlet/demos/mos.html) |
|
|
146
|
-
| **pn junction**: quasi-Fermi levels under bias; I–V against Shockley | **Solar cell**: J<sub>sc</sub> and V<sub>oc</sub> against collection theory | **MOS capacitor**: band bending; C–V
|
|
135
|
+
| **pn junction**: quasi-Fermi levels under bias; I–V against Shockley | **Solar cell**: J<sub>sc</sub> and V<sub>oc</sub> against collection theory | **MOS capacitor**: band bending; C–V at any frequency, low to high |
|
|
147
136
|
| [](https://markblundeberg.github.io/driftlet/demos/redox.html) | [](https://markblundeberg.github.io/driftlet/demos/daniell.html) | [](https://markblundeberg.github.io/driftlet/demos/saturation.html) |
|
|
148
137
|
| **Cyclic voltammetry**: a Fermi level against a redox level | **Daniell cell**: a real salt bridge, leaking, ion by ion | **Saturation**: Ag \| AgNO₃ \| Ag up to its limiting current, and a thin-film transistor pinching off, against the charge-sheet model |
|
|
149
138
|
| [](https://markblundeberg.github.io/driftlet/demos/impedance.html) | [](https://markblundeberg.github.io/driftlet/demos/membrane.html) | [](https://markblundeberg.github.io/driftlet/demos/double-layer.html) |
|
|
@@ -153,6 +142,25 @@ them locally, serve the repository root (e.g. `python3 -m http.server`) and open
|
|
|
153
142
|
| [](https://markblundeberg.github.io/driftlet/demos/channel.html) | [](https://markblundeberg.github.io/driftlet/demos/cell.html) | [](https://markblundeberg.github.io/driftlet/demos/junction.html) |
|
|
154
143
|
| **Charged nanochannel**: selectivity, overlapping Donnan layers and rectification, against Teorell–Meyer–Sievers | **Resting potential**: leaks and the Na⁺/K⁺ pump, against Mullins–Noda; the run-down to Donnan | **Liquid junctions**: a patch pipette's against Henderson, Planck and LJPcalc; a charged frit |
|
|
155
144
|
|
|
145
|
+
## Is it for your problem?
|
|
146
|
+
|
|
147
|
+
The same equations go by different names in different fields. If your problem is one-dimensional
|
|
148
|
+
(planar, or radial: spheres and cylinders, or any cross-section varying along x), it's probably here:
|
|
149
|
+
|
|
150
|
+
| If you work on | you may call it | start from | checked against |
|
|
151
|
+
|---|---|---|---|
|
|
152
|
+
| Semiconductor devices | drift–diffusion, van Roosbroeck, quasi-Fermi levels, Scharfetter–Gummel; pn, Schottky, MOS, heterojunctions; thin-film transistors (gradual channel, in the [saturation demo](https://markblundeberg.github.io/driftlet/demos/saturation.html#transistor)) | [first example](#a-semiconductor-junction), [pn](https://markblundeberg.github.io/driftlet/demos/pn.html) and [MOS](https://markblundeberg.github.io/driftlet/demos/mos.html) demos, [`contacts`](test/contacts.test.js), [`metal`](test/metal.test.js) tests | exact built-in potential, Shockley J–V, depletion charge, MOS C–V |
|
|
153
|
+
| Solar cells | photogeneration, radiative and SRH recombination | [solar](https://markblundeberg.github.io/driftlet/demos/solar.html) [organic (excitons)](https://markblundeberg.github.io/driftlet/demos/organic.html) and [perovskite (mobile ions)](https://markblundeberg.github.io/driftlet/demos/perovskite.html) demos, [`ionmonger`](test/ionmonger.test.js), [`reactions`](test/reactions.test.js) and [`generation`](test/generation.test.js) tests: generation is a reaction from a photon reservoir (uniform, or Beer–Lambert with `photogeneration()`), SRH as a rate law, in the bulk and at faces | `J_sc = qG(L_n + L_p + W)`; J_sc under Beer–Lambert against collection theory; Shockley J–V in the dark |
|
|
154
|
+
| Electrochemistry, corrosion | Nernst–Planck, concentration polarization, limiting current, Butler–Volmer, Warburg, cyclic voltammetry, salt bridges and liquid junctions, mixed potentials, electrodes under a film | [second example](#an-electrochemical-cell), [cyclic voltammetry](https://markblundeberg.github.io/driftlet/demos/redox.html), [Daniell cell](https://markblundeberg.github.io/driftlet/demos/daniell.html), [saturation](https://markblundeberg.github.io/driftlet/demos/saturation.html), [impedance](https://markblundeberg.github.io/driftlet/demos/impedance.html) and [liquid-junction](https://markblundeberg.github.io/driftlet/demos/junction.html) demos, [`circuit`](test/circuit.test.js), [`kinetics`](test/kinetics.test.js), [`impedance`](test/impedance.test.js), [`junction`](test/junction.test.js), [`ports`](test/ports.test.js) tests | `i_lim·tanh(V/4V_T)`, Butler–Volmer closed form, finite-length Warburg; junction potentials against Henderson, Planck, JPCalc and LJPcalc; Wagner–Traud |
|
|
155
|
+
| Batteries, intercalation | OCV, insertion hosts, chemical diffusion | [insertion demo](https://markblundeberg.github.io/driftlet/demos/insertion.html), [`statistics`](test/statistics.test.js) test | composition vs OCV, π²D/4L² relaxation |
|
|
156
|
+
| Double layers, colloids | Poisson–Boltzmann, Gouy–Chapman–Stern, Debye screening, crowding (Bikerman) | [double-layer demo](https://markblundeberg.github.io/driftlet/demos/double-layer.html), [`equilibrium`](test/equilibrium.test.js) test | Gouy–Chapman charge and profile, Kilic–Bazant–Ajdari |
|
|
157
|
+
| Membranes, desalination | Donnan, ion exchange, liquid junctions, water dissociation | [membrane demo](https://markblundeberg.github.io/driftlet/demos/membrane.html), [`equilibrium`](test/equilibrium.test.js), [`neutral`](test/neutral.test.js) tests | Donnan partition, Planck EMF |
|
|
158
|
+
| Solid-state ionics | mixed ionic–electronic conduction, defect chemistry, mobile ions | [`statistics`](test/statistics.test.js), [`reactions`](test/reactions.test.js) tests | mass action from standard potentials |
|
|
159
|
+
| Biophysics, nanofluidics | charged nanochannels and pores, resting potentials, Goldman–Hodgkin–Katz, Nernst, Donnan, pumps and leaks | [charged-nanochannel](https://markblundeberg.github.io/driftlet/demos/channel.html), [resting-potential](https://markblundeberg.github.io/driftlet/demos/cell.html) and [liquid-junction](https://markblundeberg.github.io/driftlet/demos/junction.html) (a patch pipette's) demos, [`pore`](test/pore.test.js) test; a membrane as a face: a capacitor with ion permeabilities, and the Na⁺/K⁺ pump as a reaction on it ([`membrane`](test/membrane.test.js) test) | GHK potential and current–voltage curve; a resolved lipid layer; Mullins–Noda; the pump's static head; Donnan |
|
|
160
|
+
|
|
161
|
+
The tests are worked setups, each with its analytic check, so they double as recipes. The
|
|
162
|
+
[validation table](#validation) lists them all.
|
|
163
|
+
|
|
156
164
|
## How to think about it
|
|
157
165
|
|
|
158
166
|
driftlet insists on thermodynamically honest concepts ([conventions](docs/conventions.md)):
|
|
@@ -260,12 +268,12 @@ Every physics feature is tested against analytic results (`npm test`, node's bui
|
|
|
260
268
|
| Time integration | BE first order, BDF2 second order; adaptive error control | ratios 2, 4 |
|
|
261
269
|
| Advection and eddy mixing | exact convection–diffusion profile; `D + D_mix`; junction EMF unchanged by mixing | 1e-12; 1e-12; 1e-5 |
|
|
262
270
|
| Metal regions | ohmic; Schottky face and MOS gate equal their contact forms; Ag \| AgNO₃ \| Ag with metal electrodes; bipolar electrode at V/2 | 1e-12; 1e-9; 1e-9; 1e-9 |
|
|
263
|
-
| Internal ports | transmission line σV tanh(L/λ)/λ (O(h²)); held level; MOS low-frequency C–V with a grounded channel | 1e-4; 1e-9; 2e-3 |
|
|
271
|
+
| Internal ports | transmission line σV tanh(L/λ)/λ (O(h²)); held level; MOS low-frequency C–V with a grounded channel, and without one the same equilibrium, and a gate step filling the inversion layer to it | 1e-4; 1e-9; 2e-3; 1e-6; 1e-3 |
|
|
264
272
|
| Cross-sections (spherical, cylindrical, any A(x)) | steady diffusion to a sphere and a cylinder (exact on any grid); Cottrell with the spherical term; uptake by a sphere filling from its surface; Debye–Hückel around a charged sphere, potential and charge | 1e-10; 3e-4; 2e-4; 1e-3, 3e-4 |
|
|
265
273
|
| A capacitance through a port | a thin-film transistor against the charge-sheet model (below threshold to saturation, second order); de Levie impedance; gate impedance against dQ/dV | 5e-4; 1e-3; 1e-6 |
|
|
266
274
|
| Electrode surfaces (coverage) | Langmuir isotherm against potential; the charge to fill a surface; the active–passive curve, blocking exactly (1 − θ) | 1e-9; 1e-3; 1e-9 |
|
|
267
275
|
| Electrodes spread through a port | transmission line with the reaction's linear kinetics as the conductance (O(h²)), rates along it; Wagner–Traud mixed potential of two Butler–Volmer couples, floating | 1e-4, 2e-3; 1e-6 V |
|
|
268
|
-
| Impedance | Macdonald blocking-electrode spectrum, 100 Hz–1 GHz; finite-length Warburg (Ag \| AgNO₃ \| Ag); DC limit = differential resistance | 3e-4; 3e-5; 1e-6 |
|
|
276
|
+
| Impedance | Macdonald blocking-electrode spectrum, 100 Hz–1 GHz; finite-length Warburg (Ag \| AgNO₃ \| Ag); DC limit = differential resistance; a p⁺n⁺ junction without recombination, Re Y → dI/dV and C = dQ/dV; a redox electrode against a bath at its open circuit, Re Y → dI/dV | 3e-4; 3e-5; 1e-6; 1e-4, 1e-6; 1e-4 |
|
|
269
277
|
| Conservation | per step, and against time-integrated contact fluxes | 1e-11 relative |
|
|
270
278
|
| Strictly neutral limit (ε = 0) | Planck EMF; polarization with no overlimiting; Donnan at neutral faces | 1e-5; 1e-4; 1e-9 |
|
|
271
279
|
| Fermi–Dirac statistics | `𝓕_{±1/2}` vs quadrature; degenerate bulk; accumulation charge via `𝓕_{3/2}` | 1e-13; 1e-12; 1e-3 |
|
|
@@ -284,17 +292,17 @@ checked in CI; times are from a desktop Ryzen 7600X in Node 22:
|
|
|
284
292
|
| Task | Time | Factorisations |
|
|
285
293
|
|---|---|---|
|
|
286
294
|
| Linear solve, 300 nodes × 7 unknowns (factor + solve) | 0.4 ms | 1 |
|
|
287
|
-
| pn diode (264 nodes): cold equilibrium |
|
|
288
|
-
| pn diode: I–V sweep 0 → 0.6 V, 31 points |
|
|
289
|
-
| pn diode: warm jump from +0.4 V to −1 V |
|
|
290
|
-
| pn diode: adaptive transient, 0 → 0.5 V, 100 ns |
|
|
291
|
-
| pn diode: impedance at 20 frequencies |
|
|
292
|
-
| MOS with a metal gate: C–V sweep, 26 points |
|
|
293
|
-
| n-Si \| KCl (4 species, 3 unknowns per node): Mott–Schottky sweep, 31 points |
|
|
294
|
-
| n-Si \| KCl: impedance at 20 frequencies |
|
|
295
|
+
| pn diode (264 nodes): cold equilibrium | 3 ms | 9 |
|
|
296
|
+
| pn diode: I–V sweep 0 → 0.6 V, 31 points | 29 ms | 137 |
|
|
297
|
+
| pn diode: warm jump from +0.4 V to −1 V | 8 ms | 51 |
|
|
298
|
+
| pn diode: adaptive transient, 0 → 0.5 V, 100 ns | 165 ms | 926 |
|
|
299
|
+
| pn diode: impedance at 20 frequencies | 14 ms | 21 |
|
|
300
|
+
| MOS with a metal gate: C–V sweep, 26 points | 9 ms | 126 |
|
|
301
|
+
| n-Si \| KCl (4 species, 3 unknowns per node): Mott–Schottky sweep, 31 points | 36 ms | 156 |
|
|
302
|
+
| n-Si \| KCl: impedance at 20 frequencies | 16 ms | 21 |
|
|
295
303
|
| Ag \| AgNO₃ \| Ag with double layers: sweep 0 → 0.1 V, 21 points | 12 ms | 81 |
|
|
296
|
-
| Ag \| AgNO₃ \| Ag, neutral: adaptive transient over 1 s |
|
|
297
|
-
| Silver electrodes with a bipolar plate: sweep 0 → 1 V, 11 points |
|
|
304
|
+
| Ag \| AgNO₃ \| Ag, neutral: adaptive transient over 1 s | 24 ms | 186 |
|
|
305
|
+
| Silver electrodes with a bipolar plate: sweep 0 → 1 V, 11 points | 25 ms | 54 |
|
|
298
306
|
|
|
299
307
|
The library has no dependencies and does no DOM access, so it runs in a Web Worker. (A device
|
|
300
308
|
definition is plain data and can be posted to a worker. Devices using custom-function
|
|
@@ -307,12 +315,14 @@ No dependencies. Tests use node's built-in runner (Node ≥ 22):
|
|
|
307
315
|
```sh
|
|
308
316
|
npm test # the validation suite
|
|
309
317
|
npm run bench # typical workloads against bench/baseline.json (--save to update it)
|
|
318
|
+
npm run stress # random devices, every result checked (--n 500 for more; see docs/reliability.md)
|
|
319
|
+
npm run hard # the corpus of once-hard cases against bench/hard.json
|
|
310
320
|
npm run types # TypeScript declarations from the JSDoc, into types/ (fetches TypeScript via npx)
|
|
311
321
|
npm run smoke # every demo in headless Chrome, every control to its extremes (CHROME=… to choose)
|
|
312
322
|
npm run shots # retake the demos' screenshots, each caught at a set state (tools/shots.js)
|
|
313
323
|
```
|
|
314
324
|
|
|
315
|
-
Design notes and plans: [numerics](docs/numerics.md), [conventions](docs/conventions.md),
|
|
325
|
+
Design notes and plans: [reliability](docs/reliability.md), [numerics](docs/numerics.md), [conventions](docs/conventions.md),
|
|
316
326
|
[statistics](docs/statistics.md), [the kit](docs/kit.md), [reading level diagrams](docs/visualization.md),
|
|
317
327
|
[data](docs/data.md), [roadmap](ROADMAP.md).
|
|
318
328
|
|
package/docs/device.md
CHANGED
|
@@ -594,10 +594,20 @@ ports: [{ name: 'gate', region: 'channel', V: 1.5, area: 1 / 10e-9, capacitance:
|
|
|
594
594
|
ports: [{ name: 'iron', region: 'film', terminal: 'e-', V: 0, area: 1e4, capacitance: { C: 0.2, zeroCharge: -0.4 }, reactions: [/* … */] }]
|
|
595
595
|
```
|
|
596
596
|
|
|
597
|
+
A volumetric capacitance $`C^*`$ (F/m³, an organic electrochemical transistor's) goes in per
|
|
598
|
+
area of a nominal electrode: `area: 1 / t, capacitance: { C: Cstar * t }` for a film t thick
|
|
599
|
+
(`area: 1, C: Cstar` is the same physics, but reads to `describe()` like a unit slip). To read the
|
|
600
|
+
gate's voltage as the film's effective one, put the undisturbed film at φ = 0 (its carrier's
|
|
601
|
+
`cRef` at its doping, `mu0: 0`) and leave `zeroCharge` at 0. The gate charges the carriers
|
|
602
|
+
through their own chemical capacitance too, in series, so $`dQ/dV_G`$ comes out below $`C^*`$
|
|
603
|
+
(about 12% for holes at 1e20 cm⁻³ against 40 F/cm³).
|
|
604
|
+
|
|
597
605
|
That's the gradual-channel approximation of a field-effect transistor, exact for a thin film
|
|
598
606
|
with no body (a TFT, an organic or oxide transistor) while the channel is long against the gate
|
|
599
607
|
dielectric, and failing where it pinches off (where the 2D field takes over). It's also the
|
|
600
|
-
Bernards model of an organic electrochemical transistor (a volumetric capacitance
|
|
608
|
+
Bernards model of an organic electrochemical transistor (a volumetric capacitance; the
|
|
609
|
+
Bernards–Malliaras current is the charge-sheet formula below without its $`V_T`$ term, so they
|
|
610
|
+
part near pinch-off, where driftlet keeps the diffusion that formula drops), cable theory
|
|
601
611
|
for an axon's membrane, and a porous electrode's double layer. The gate's flat level pins the
|
|
602
612
|
channel's standard level through C, as a blocked spectator's flat level does in an electrolyte,
|
|
603
613
|
and the current saturates as the carriers run out at the drain, in both.
|
|
@@ -636,7 +646,7 @@ contacts: {
|
|
|
636
646
|
right: { I: -2, ... }, // driven by a current into the device, A/m² (I: 0 is open circuit)
|
|
637
647
|
},
|
|
638
648
|
ports: [{ name: 'ref', I: 0, ... }, // a reference electrode: no current, its voltage read off
|
|
639
|
-
{ name: 'wire', V: 0, R: 1e-3, ... }], // a source V behind a series resistance R (Ω·m
|
|
649
|
+
{ name: 'wire', V: 0, R: 1e-3, ... }], // a source V behind a series resistance R (Ω·m², or Ω with a geometry)
|
|
640
650
|
```
|
|
641
651
|
|
|
642
652
|
- **`V`**: the terminal's voltage, the shift of its outside phase's ladder, as the voltage of
|
|
@@ -703,7 +713,8 @@ its middle, and nothing passes the end. Only an end may have $`A = 0`$. Flow (`v
|
|
|
703
713
|
planar devices.
|
|
704
714
|
|
|
705
715
|
With a cross-section, the solution's currents, contact and port fluxes, terminal currents and
|
|
706
|
-
`charge` are totals (A, mol/s; per metre for cylinders), and so are `I` drives
|
|
716
|
+
`charge` are totals (A, mol/s; per metre for cylinders), and so are `I` drives, a series `R`
|
|
717
|
+
and the impedance (Ω, not Ω·m²). Face laws,
|
|
707
718
|
rates, `interfaces[f].N` and `D`, and concentrations stay per area or per volume. Validated against
|
|
708
719
|
steady diffusion to a sphere and a cylinder (exact), Cottrell's transient with the spherical term,
|
|
709
720
|
uptake by a sphere filling from its surface, and Debye–Hückel screening around a charged sphere
|
|
@@ -715,6 +726,7 @@ uptake by a sphere filling from its surface, and Debye–Hückel screening aroun
|
|
|
715
726
|
const dev = new Device(def);
|
|
716
727
|
const sol = dev.solve(); // steady state (or equilibrium)
|
|
717
728
|
dev.set({ contacts: { right: { V: 0.3 } } }); // deep-merged change; the state is kept as a warm start
|
|
729
|
+
dev.set({ ports: { gate: { V: 0.2 } } }); // ports patched by name (an array replaces them all)
|
|
718
730
|
const sol2 = dev.solve();
|
|
719
731
|
const tr = dev.step(1e-6); // one backward-Euler step (auto-subdivided if needed)
|
|
720
732
|
const tr2 = dev.step(1e-6, { method: 'bdf2' }); // second-order BDF2 once a previous step exists
|
|
@@ -729,17 +741,23 @@ const now = dev.solution(); // snapshot of the current state
|
|
|
729
741
|
amounts (blocked species, reactive moieties) are kept exactly. If a direct solve fails at a
|
|
730
742
|
bias (a cold start far from equilibrium), it solves with both terminals level and ramps the
|
|
731
743
|
right terminal's voltage to its target; with generation reactions (below), it ramps their
|
|
732
|
-
rates up from nearly nothing.
|
|
744
|
+
rates up from nearly nothing. A terminal driven by a current (open circuit, say) is held at a
|
|
745
|
+
voltage instead, the voltage marched until the current crosses its target, and floated from
|
|
746
|
+
there. If 20 V either way brings no crossing (a current beyond the limiting one), the solve
|
|
747
|
+
fails with a warning giving the range of currents the held voltages passed.
|
|
733
748
|
- `step(dt, { method })` advances the transient by dt seconds, halving internally where Newton
|
|
734
749
|
needs it. `method` is `'be'` (backward Euler, the default) or `'bdf2'`.
|
|
735
750
|
- `advance(tEnd, opts)` integrates adaptively to `tEnd` with variable-step BDF2, controlling
|
|
736
751
|
the local error per step to `tol` (default 1e-3) in thermal units of every potential ($`\phi`$ and
|
|
737
|
-
each $`\bar\mu/RT`$): roughly 0.1% in concentrations.
|
|
752
|
+
each $`\bar\mu/RT`$): roughly 0.1% in concentrations per step. That's local: over a long decay
|
|
753
|
+
the errors add up, and a small current at the end of one can be tens of percent off, so tighten
|
|
754
|
+
`tol` (1e-5, say) before fitting a time constant to a tail. It lands exactly on `tEnd`. Options: `tol`,
|
|
738
755
|
`dt0` (first step), `dtMax`, `budgetMs` (return after this much wall time, with
|
|
739
756
|
`done: false`), `maxSteps`, `method`. The step size carries over between calls, so an
|
|
740
757
|
animation can call `advance(tNext, { budgetMs })` once per frame. The solution adds `done`,
|
|
741
758
|
`rejected`, and a `trace` of terminal current and voltage after every accepted step (the
|
|
742
|
-
|
|
759
|
+
current is the right contact's, toward +x, as `sol.current`; a port's is in `sol.terminals`
|
|
760
|
+
after each call; the voltage is the right contact's minus the left's: what a meter between the terminals reads,
|
|
743
761
|
which at a bath is its reference species' level, not $`\phi`$). With
|
|
744
762
|
`probes: [{ x, species, quantity, region }]` the trace also reads inside the device,
|
|
745
763
|
as `trace.probes[k]` beside `trace.t`: a species' concentration (`quantity: 'c'`, the default,
|
package/docs/numerics.md
CHANGED
|
@@ -420,6 +420,29 @@ instead of $`n \cdot M^3`$, which pays wherever species are confined to some reg
|
|
|
420
420
|
against KCl, three unknowns of five at every node, it's a fifth of the work. A finite-difference
|
|
421
421
|
test checks the compact Jacobian column by column on devices that cover every assembly path.
|
|
422
422
|
|
|
423
|
+
### Exact J·v
|
|
424
|
+
|
|
425
|
+
The assembled matrix loses what a flux's two entries share. A Scharfetter–Gummel flux depends on
|
|
426
|
+
$`\eta`$ at its two ends through $`G`$ and $`-G(1 + E)`$, with $`E = \operatorname{expm1}(\Delta\eta)`$;
|
|
427
|
+
for a nearly uniform $`v`$ they cancel, and $`J \cdot v`$ keeps only round-off of $`G|v|`$, which in
|
|
428
|
+
an inversion layer's 0.1 nm cells ($`G`$ ~ 1e11) dwarfs the flux. Where a solve needs
|
|
429
|
+
$`J \cdot v`$ itself (GMRES, in the impedance and in Newton's refined solves), the dilute kernels'
|
|
430
|
+
terms are kept aside instead, each evaluated on differences first:
|
|
431
|
+
|
|
432
|
+
```math
|
|
433
|
+
\delta N = a\,(\delta\eta_L - z\,\delta\hat\phi_L) + b\,(\delta\eta_R - \delta\eta_L) + d\,(\delta\hat\phi_R - \delta\hat\phi_L),
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
with $`a = -GE`$ (through $`c_L`$), $`b = -G(1 + E)`$ and $`d`$ the drift's. A node's storage and space
|
|
437
|
+
charge depend on $`\delta\eta_i - z_i\,\delta\hat\phi`$ alone, the displacement on
|
|
438
|
+
$`\delta\hat\phi_R - \delta\hat\phi_L`$. Everything else (faces, contacts, ports, reactions,
|
|
439
|
+
concentrated and metal regions) is assembled into a matrix of its own. Rows that a contact or a
|
|
440
|
+
steady solve replaces drop the kept terms; the transformed rows of strictly neutral nodes combine
|
|
441
|
+
them as they combine the matrix's. A region's uniform shift ($`z_i s`$ on each $`\eta_i`$, $`s`$ on
|
|
442
|
+
$`\hat\phi`$) then gives exactly zero inside it, as it must. It replaced a central difference of
|
|
443
|
+
the residual, whose truncation (~1e-9 of $`|J||v|`$) swamped the answer for such a shift, and
|
|
444
|
+
which cost four assemblies per product.
|
|
445
|
+
|
|
423
446
|
## Newton
|
|
424
447
|
|
|
425
448
|
- The update of the device's potentials ($`\hat\phi`$, $`\eta`$) is limited to 10 thermal units per
|
|
@@ -439,8 +462,37 @@ test checks the compact Jacobian column by column on devices that cover every as
|
|
|
439
462
|
much reduced by the change of variables below), converges quadratically to ~1e-9 and then
|
|
440
463
|
rattles there. Short steps come right after every waveform breakpoint, so without this a
|
|
441
464
|
cyclic voltammogram in a neutral electrolyte stalled at its turns.
|
|
442
|
-
-
|
|
443
|
-
|
|
465
|
+
- **Refined solves, where the factorisation loses a mode.** The assembled $`J`$ holds a flux's
|
|
466
|
+
dependence on $`\eta`$ at its two ends as two entries; where they're huge, eliminating them loses
|
|
467
|
+
what they share to round-off. In a MOS capacitor's inversion layer the electrons' cells, 0.1 nm
|
|
468
|
+
wide, are coupled about $`h^2/(D\,dt)`$ ~ 1e-18 times more tightly than they store charge over a
|
|
469
|
+
second, so the layer's overall level, set by its storage and the trickle of minority electrons
|
|
470
|
+
from the bulk, is lost, and Newton's update there is noise. When Newton stalls short of
|
|
471
|
+
convergence (updates below 1e-4 that stopped shrinking quadratically), or its undamped updates
|
|
472
|
+
grow twice running (a bipolar stack's floating base, whose holes are held ~1e14 more weakly
|
|
473
|
+
than they move within it, kept Newton wandering a thermal unit at a time), the solve is refined:
|
|
474
|
+
GMRES on $`J\delta = r`$, the factorised system as the preconditioner and $`J \cdot v`$ exact, each
|
|
475
|
+
flux on its $`\eta`$ difference (see [exact J·v](#exact-jv)), as the impedance does. A lost mode
|
|
476
|
+
takes GMRES a couple of iterations. The refined update is kept where GMRES converged and it
|
|
477
|
+
moves no level by a thermal unit or more; otherwise refining stops for the rest of the solve.
|
|
478
|
+
A MOS capacitor's gate step now reaches the low-frequency charge in ~70 steps (it took 80,000,
|
|
479
|
+
and ended 1.5% off). Once refined, a solve stays refined: a GaAs junction without
|
|
480
|
+
recombination, whose minority carriers reach it only from the contacts, passes ~1.5e-7 A/m²,
|
|
481
|
+
which plain solves past the first refinement left to round-off (cold and warm solves 1e-3
|
|
482
|
+
apart), and refined ones give to 1e-14. (When $`J \cdot v`$ was a central difference of the
|
|
483
|
+
residual, its noise made refining past the first lost mode chase round-off, so it was kept
|
|
484
|
+
only where its correction dwarfed the plain update.)
|
|
485
|
+
- Clear divergence (device updates beyond 1e4 after the first iteration, or ten times the
|
|
486
|
+
first after six) bails out early, so the caller can take a smaller step. The first update is
|
|
487
|
+
exempt: light flooding a population that starts at ~1e3 per m³ predicts, linearised in
|
|
488
|
+
$`\eta`$, a change of 1e10 thermal units, which damping covers in a few iterations; bailing
|
|
489
|
+
out there had a lit phototransistor's pseudo-transient fail at every step above 1e-21 s.
|
|
490
|
+
- A pivot that cancels to exactly zero in the block factorisation is perturbed to 1e-15 of its
|
|
491
|
+
block's largest entry (static pivoting) rather than failing the solve. GaAs minority carriers,
|
|
492
|
+
~1 per m³ beside a face, are held through conductances 1e-16 of the face flux's coefficient in
|
|
493
|
+
their row, and the elimination cancels them exactly: such devices now solve, even at
|
|
494
|
+
equilibrium. Newton's updates stay meaningful (the factorisation only amplifies the residual,
|
|
495
|
+
so a small update still means a small residual), and its refined solves correct the rest.
|
|
444
496
|
- When a steady solve fails, the solution's warnings say how nearly singular the system was,
|
|
445
497
|
and where. A running error bound through the factorisation compares each pivot with the
|
|
446
498
|
magnitudes it was formed from, $`\log_{10}(\sum|\mathrm{terms}| / |\mathrm{pivot}|)`$: the digits
|
|
@@ -481,6 +533,13 @@ test checks the compact Jacobian column by column on devices that cover every as
|
|
|
481
533
|
$`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
|
|
482
534
|
backward Euler, scaled by $`h/(2h + h_1)`$. The first step, with no history, is checked by step
|
|
483
535
|
doubling.
|
|
536
|
+
- A first step whose error doesn't shrink with it is taken anyway. A jump in a level held at a
|
|
537
|
+
boundary (a bath's, after a voltage step) starts a profile self-similar in $`x/\sqrt{t}`$, so a
|
|
538
|
+
first step of any length errs alike (0.11 thermal units over ten decades of step, in a
|
|
539
|
+
0.6 M electrolyte with resolved double layers), down to the grid's own diffusion time,
|
|
540
|
+
where Newton runs out of digits. Three tries in a row, each within a factor 2 of the last,
|
|
541
|
+
mark the error as the jump's; backward Euler damps it as it should, and the steps grow
|
|
542
|
+
from there. Transients that start with a jump take 5–30% fewer factorisations for it.
|
|
484
543
|
- The state a first step starts from needn't satisfy the algebraic equations ($`\hat\phi`$ in a
|
|
485
544
|
strictly neutral material, an interface's unknowns), after a start or a jump; they jump in
|
|
486
545
|
the first instant. So the history starts from the first step's half step, not from there,
|
|
@@ -497,9 +556,9 @@ test checks the compact Jacobian column by column on devices that cover every as
|
|
|
497
556
|
current it cost 50% more.
|
|
498
557
|
- A step is rejected above `tol`. The next step size is
|
|
499
558
|
$`h \cdot \min(2, \max(0.2, 0.9\, (\mathtt{tol}/\mathrm{err})^{1/(p+1)}))`$.
|
|
500
|
-
- Newton failure quarters the step
|
|
501
|
-
|
|
502
|
-
|
|
559
|
+
- Newton failure quarters the step. (Strictly neutral materials had tried longer first steps
|
|
560
|
+
instead, shorter ones being worse conditioned there; with their ends and starts fixed, no
|
|
561
|
+
case needs it.)
|
|
503
562
|
- A target within round-off of the present time (1e-10 relative), such as an animation
|
|
504
563
|
frame's that lands a hair past a breakpoint just reached, is snapped to rather than
|
|
505
564
|
stepped to: a step of 1e-13 s can't be resolved.
|
|
@@ -533,15 +592,42 @@ species make $`J`$ singular, but $`J + i\omega M`$ isn't for $`\omega > 0`$: at
|
|
|
533
592
|
device looks like a capacitor, as it should.
|
|
534
593
|
|
|
535
594
|
The factorised system is used as a preconditioner, not trusted alone: each solve runs GMRES with
|
|
536
|
-
$`J \cdot v`$
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
is
|
|
544
|
-
|
|
595
|
+
an exact $`J \cdot v`$ (see [exact J·v](#exact-jv)). The assembled $`J`$ holds a flux's dependence
|
|
596
|
+
on $`\eta_L`$ and $`\eta_R`$ as two entries; in an inversion layer both are huge (~1e11), and for a
|
|
597
|
+
nearly uniform $`\delta\eta`$ they cancel to round-off of order 1e-5. That round-off had given a
|
|
598
|
+
MOS capacitor's inversion layer, fed by minority diffusion from a bulk with ~1e3 electrons per cm³
|
|
599
|
+
(a time constant of minutes), an exchange path that followed the gate at 1 Hz. Where the plain
|
|
600
|
+
solve is already accurate, one application of the operator confirms it. GMRES judges convergence only by the true residual,
|
|
601
|
+
recomputed at each restart: its own running estimate can drift far from it where the
|
|
602
|
+
preconditioner is nearly singular (an inversion layer's exchange, lost in the assembled
|
|
603
|
+
$`J`$, at a frequency of µHz), and had declared solves converged that weren't. It keeps its
|
|
604
|
+
best iterate, never worse than the factorised solve.
|
|
605
|
+
|
|
606
|
+
The current is read where its error is smallest. At a contact it is a sum of terms that can
|
|
607
|
+
be far larger than itself: across a junction without recombination, between heavily doped
|
|
608
|
+
sides, what passes is minority carriers of ~1e8 per m³, while the contact's terms are its
|
|
609
|
+
majority conductances times a $`\delta\eta`$ cancelling its neighbours' to 1e-17, and their
|
|
610
|
+
round-off reads as a constant conductance of order 1 S/m², a silicon p⁺n⁺ junction's capacitance
|
|
611
|
+
9× off at 100 Hz. Between the two contacts of a device without ports, the total current
|
|
612
|
+
(conduction plus displacement, $`i\omega\,\delta D`$) is the same through every cut, so it is also
|
|
613
|
+
read across each segment of an ideal region, linearised as the Scharfetter–Gummel flux is, and
|
|
614
|
+
the reading with the least estimated error is taken: in that junction, the depletion region's;
|
|
615
|
+
in a MOS capacitor, the oxide's displacement. A reading's error is its terms' round-off, and the
|
|
616
|
+
solves' error in $`\delta x`$ (their residual, relative to its largest entry) through its
|
|
617
|
+
coefficients: a contact's reading is a flux too (a held contact's balance row is replaced by its
|
|
618
|
+
level, so nothing solved pins it), and in a neutral bulk the current crosses a huge conductance
|
|
619
|
+
on a tiny gradient.
|
|
620
|
+
|
|
621
|
+
Where even the best reading's estimated error exceeds 1% of the current, the response is split
|
|
622
|
+
in two: a uniform shift of each region (every $`\eta_i`$ by $`z_i s_r`$ and $`\hat\phi`$ by $`s_r`$, with
|
|
623
|
+
$`s_r`$ fitted to the solve so far), which changes no flux and no concentration, so its $`J \cdot e`$ is
|
|
624
|
+
exactly zero inside each region and is taken at the faces and ends;
|
|
625
|
+
and the rest, small, solved for to GMRES's floor with its error now relative to itself, and read
|
|
626
|
+
separately. An electrolyte at its open circuit needs it: the response to its terminal is nearly
|
|
627
|
+
such a shift, and its current is the slope of levels uniform to 1e-14 of the response, more than
|
|
628
|
+
a double holds. A redox electrode's DC conductance had come out 2–40% low; now it matches the
|
|
629
|
+
steady dI/dV to six digits. (Not with ports, whose exchange sees the shift.) The real part of a nearly ideal capacitor, 1e-7 of $`|Z|`$, is still
|
|
630
|
+
only resolved to ~1e-4 of $`|Z|`$.
|
|
545
631
|
|
|
546
632
|
## Steady state
|
|
547
633
|
|
|
@@ -550,6 +636,28 @@ take about twice as long as before.
|
|
|
550
636
|
because slow physics can take seconds, far beyond any $`L^2/D`$ estimate, and stepping would
|
|
551
637
|
never finish. An example is exponentially scarce minority carriers slowly filling an inversion
|
|
552
638
|
layer behind a Schottky contact.
|
|
639
|
+
- **A flat level,** where a stretch's steady state is known outright: a species that no reaction
|
|
640
|
+
or port touches, reached by one contact only (blocked at the other end), carries no flux in a
|
|
641
|
+
steady state, so its level is that contact's throughout. Steady solves (not the impedance,
|
|
642
|
+
where it carries a current) pin it there in place of its balances, rather than find it
|
|
643
|
+
through its own conduction, which can be all but nothing: a MOS capacitor's inversion
|
|
644
|
+
electrons reach the back contact only through a bulk with ~1e3 of them per cm³, and found that
|
|
645
|
+
way, the solve converged at some gate voltages and grids and not others. (Not through flow or
|
|
646
|
+
mixing, nor a concentrated material's cross-diffusion, where zero flux isn't a flat level.)
|
|
647
|
+
At a face inside such a stretch whose link holds the level continuous, the face's flux of the
|
|
648
|
+
species, which only the replaced edge balances set, is pinned to zero too.
|
|
649
|
+
- **Continuation for a terminal driven by a current.** A cold steady solve at open circuit can
|
|
650
|
+
fail where the same state is easy warm (a solar cell in dim light on a fine grid). The
|
|
651
|
+
terminal is held at a voltage instead, its steady solves having their own continuation, and
|
|
652
|
+
the voltage marched from the start's, the step growing, until the current crosses its target:
|
|
653
|
+
up where the current is below it, since raising a terminal's voltage raises the current into
|
|
654
|
+
a passive device (the other way if 20 V brings no crossing). Bisection narrows the crossing
|
|
655
|
+
to 0.1 mV, and the terminal is floated from beside it. Floated, the system can lose what held
|
|
656
|
+
it doesn't (a closed Fe³⁺/Fe²⁺ cell driven near its limit, Fe³⁺ 20 orders below Fe²⁺: 33 digits
|
|
657
|
+
short), but the current-driven steady state is the held one at the voltage that passes the
|
|
658
|
+
target, so where the floated solve fails, held solves find that voltage instead, by regula
|
|
659
|
+
falsi within the bracket, to 1e-10 of the current. The voltage continuation's own start,
|
|
660
|
+
with the terminals level, ramps the light up when there is some.
|
|
553
661
|
- **Spectators** (a species blocked all round, mobile throughout its stretch) are solved
|
|
554
662
|
directly too. In steady state the sum of a spectator's balance rows over its stretch is zero
|
|
555
663
|
identically, so one of them (the first node's) is redundant, and it's replaced by the
|
|
@@ -557,7 +665,14 @@ take about twice as long as before.
|
|
|
557
665
|
bordered: the matrix is factorised with a pin (an identity row) in its place, which makes the
|
|
558
666
|
stretch's level a well-conditioned unknown, and the response to a unit pin (one extra
|
|
559
667
|
back-substitution per spectator) is added in the amount that satisfies the constraint, from
|
|
560
|
-
a $`k \times k`$ system for $`k`$ spectators.
|
|
668
|
+
a $`k \times k`$ system for $`k`$ spectators. Within one ideal region (no flow or mixing), a
|
|
669
|
+
spectator's level is flat at steady state, at a value its amount fixes, so its other balance
|
|
670
|
+
rows are replaced by $`\eta_g - \eta_{g-1} = 0`$, rather than found through its own conduction.
|
|
671
|
+
Past a closed zinc cell's limiting current, extended space charge at the cathode excludes the
|
|
672
|
+
blocked sulfate to ~1e-25 mol/m³, and the chain of conductances through it had lost the steady
|
|
673
|
+
system 14 digits: steady solves failed from 0.7 V, and took 37,000 iterations at 0.6 V. (Not
|
|
674
|
+
across a face: the rows that would hold the level there and pin its flux leave a diagonal
|
|
675
|
+
block singular.)
|
|
561
676
|
- **Conserved combinations** (moieties) of reacting stretches are solved the same way: the
|
|
562
677
|
total iron of Fe³⁺, Fe²⁺ and FeCl²⁺ in a closed cell with a complexation reaction, say. The
|
|
563
678
|
basis of combinations $`w`$ comes from the null space of the stoichiometry. In steady state the
|
|
@@ -571,6 +686,36 @@ take about twice as long as before.
|
|
|
571
686
|
larger by $`D \cdot dt/(hL)`$, around 1e12 for a micron-scale cell (see
|
|
572
687
|
[metal regions](#metal-regions) for the same cancellation). Newton then needs dozens of
|
|
573
688
|
iterations, or fails.
|
|
689
|
+
- **Islands**: the pieces of a stretch between its faces, where nothing else feeds a piece (no
|
|
690
|
+
contact, port or reaction). In steady state such a piece's level is held only through its
|
|
691
|
+
faces' fluxes, which can be far weaker than its own conduction: a face conductance of
|
|
692
|
+
1e-15 S/m² against a region conducting ~4e2 S/m, or neighbours that conduct 1e20 times less
|
|
693
|
+
than it. Eliminated, the level was lost to round-off below anything Newton could see (the
|
|
694
|
+
plain solves gave no update along it, so nothing stalled), and solves converged to a wrong one
|
|
695
|
+
silently: currents 2× or 16× off. Summed over the piece's nodes, its balance rows are its
|
|
696
|
+
faces' flux unknowns alone, $`A_R u_R - A_L u_L`$: the internal fluxes cancel exactly, by
|
|
697
|
+
construction rather than in round-off. Across a face that holds $`\bar\mu`$ level, $`u`$ is
|
|
698
|
+
set only by the edge balances, and eliminated it can come out of the piece's own, as
|
|
699
|
+
$`G\,\Delta\eta`$ with its two $`\eta`$ all but equal; so there the outside edge node's
|
|
700
|
+
balance is added too, $`u`$ cancels, and the outside's first segment carries the flux. (Seen
|
|
701
|
+
through one face of two, a level behind neighbours 1e20 times less conductive was overshot
|
|
702
|
+
twofold, and Newton swung it from one contact's level to the other's.) That sum can replace
|
|
703
|
+
the first node's row, bordered like a spectator's amount. The response to the unit pin is the island's uniform shift, with whatever
|
|
704
|
+
$`\hat\phi`$ and the other species do along with it, so no mode needs guessing; refined solves
|
|
705
|
+
carry the pinned rows too.
|
|
706
|
+
|
|
707
|
+
Newton exact along such a level can be worse than Newton blind to it, far from the solution
|
|
708
|
+
(cold at bias, a bipolar stack's floating base took 1e5 thermal units in its first update), so
|
|
709
|
+
the summed rows go in only once the plain solves converge. An island at the wrong level then
|
|
710
|
+
shows: it takes in a different current than it passes on, so the terminal currents don't add
|
|
711
|
+
up to zero (to 1e-9 of the largest). Newton then carries on with the summed rows, refined from
|
|
712
|
+
their second update. A current that tiny against the conduction around it rides on $`\eta`$
|
|
713
|
+
differences (~1e-28 for 5 fA/m² through 1e4 mol/m³ at D = 1e-4 m²/s) that the last update
|
|
714
|
+
leaves known only to its own round-off, so where the currents still don't add up, one more
|
|
715
|
+
update polishes them. If Newton doesn't converge again within a dozen updates (a GaAs stack's
|
|
716
|
+
minority carriers, whose own rows rest on static pivots, at 1e-17 A/m² of noise), the plain
|
|
717
|
+
solution stands; and a steady solve that fails outright is tried once more with the summed rows
|
|
718
|
+
in from the start. Such devices now solve directly, their currents right to ~1e-15.
|
|
574
719
|
- **Immobile combinations** (trap states X⁰ and X⁻ under e⁻ + X⁰ = X⁻, with D = 0) conserve
|
|
575
720
|
node by node, since nothing carries them anywhere. At each node the combination's weighted sum
|
|
576
721
|
replaces one of its balance rows, kept at what it was when the solve began. The row is local
|
|
@@ -588,16 +733,26 @@ take about twice as long as before.
|
|
|
588
733
|
- **Generation continuation.** A device with generation reactions (species made only from, or
|
|
589
734
|
turned only into, fixed reservoirs, such as photogeneration from a photon reservoir) can be
|
|
590
735
|
held far from equilibrium even with its terminals level, where bias continuation can't help.
|
|
591
|
-
If a direct solve fails there, the generation rates are scaled down to 10
|
|
736
|
+
If a direct solve fails there, the generation rates are scaled down to 10⁻³⁰ and ramped back
|
|
592
737
|
up, ×100 a step while each solve converges (warm from the last) and by the square root of the
|
|
593
|
-
factor when one doesn't
|
|
594
|
-
|
|
738
|
+
factor when one doesn't, giving up when that factor falls below 1.5 (crawling, which time
|
|
739
|
+
steps do better); the pseudo-transient ramp then starts from the dimmer light's solution
|
|
740
|
+
rather than cold. (At 10⁻¹², a GaAs layer's generation still exceeded its equilibrium
|
|
741
|
+
recombination, ~3e-24 mol/m³ of minority electrons, a billionfold: the first step was the
|
|
742
|
+
whole jump.) An illuminated 80 µm silicon diode solves cold this way in about 80
|
|
743
|
+
iterations. (A lit n⁺pn stack with a floating base crawled for 20,000 iterations: at a
|
|
744
|
+
trillionth of the light, the base's level is held by couplings of ~1e-17.)
|
|
595
745
|
- **If that fails too,** source continuation ramps the right terminal's voltage to its target.
|
|
596
746
|
It ramps from the voltage of the last converged solve when the state is that solution, and
|
|
597
|
-
otherwise from level terminals, where a cold start is consistent.
|
|
747
|
+
otherwise from level terminals, where a cold start is consistent. (A device first solved
|
|
748
|
+
after a `set()` was laid out for its new voltages; if the level start fails from there, it's
|
|
749
|
+
laid out again, level, and retried.) The ramp step starts at
|
|
598
750
|
1/8 of the way, grows ×1.5 on success and shrinks ×4 on failure.
|
|
599
751
|
- **If Newton still fails,** $`dt`$ ramps up from a small value (pseudo-transient continuation),
|
|
600
|
-
ending in the direct solve where applicable.
|
|
752
|
+
ending in the direct solve where applicable. After a failed step it grows ×2, then ×1.5 more
|
|
753
|
+
each success, back to ×10. Once steps reach the slowest diffusion time, the direct solve is
|
|
754
|
+
tried from there, once a decade of $`dt`$ (the state kept if it fails): with immobile traps and
|
|
755
|
+
ions of D = 1e-17 m²/s, steps from 1e8 to 1e9 s kept failing on the way to the giant step.
|
|
601
756
|
|
|
602
757
|
`solve()` does not advance the clock.
|
|
603
758
|
|
|
@@ -648,7 +803,11 @@ neutrality:
|
|
|
648
803
|
- then storage (on the other balances) and neutrality go in, exactly, in those terms.
|
|
649
804
|
|
|
650
805
|
A start-of-step net charge (round-off in a solved state) isn't carried over: the neutrality row
|
|
651
|
-
holds the new state neutral.
|
|
806
|
+
holds the new state neutral. An end node that its contact holds (a bath, an electrode's level)
|
|
807
|
+
keeps its plain rows, but the contact's current there is read before the ions' storage goes in,
|
|
808
|
+
and a bulk law doesn't count the box's net charge as displacement: under neutrality neither
|
|
809
|
+
carries charge, and read from the box's balance their round-off (∝ c/dt) cancelled only roughly.
|
|
810
|
+
A bath floating on 1 µM beside 3 M KCl had rattled at 2e-5 thermal units on steps of 1e-14 s. Steady solves and the impedance keep the plain rows (there's no
|
|
652
811
|
storage at $`dt = \infty`$, and the impedance reads the storage matrix from them).
|
|
653
812
|
|
|
654
813
|
A sub-grid Gouy–Chapman law, treating the diffuse layers analytically when $`\lambda_D \ll h`$, is on
|