driftlet 0.11.4 → 0.13.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.
package/README.md CHANGED
@@ -8,21 +8,33 @@
8
8
  [![test](https://github.com/markblundeberg/driftlet/actions/workflows/test.yml/badge.svg)](https://github.com/markblundeberg/driftlet/actions/workflows/test.yml)
9
9
  [![npm](https://img.shields.io/npm/v/driftlet)](https://www.npmjs.com/package/driftlet)
10
10
 
11
- **driftlet is a fast, pure-JavaScript solver for 1D drift–diffusion–reaction problems**, built
12
- to be the engine of live, thermodynamically honest web demos of charge transport: in
13
- semiconductors, electrochemical cells, membranes and solid ionic conductors. It handles any mix
14
- of charged and neutral species, Poisson electrostatics (or strict neutrality), bulk and
15
- interfacial reactions, heterointerfaces, metals and external circuits, in steady state, in time
16
- and as small-signal impedance. It's small enough to run in a web page: no dependencies,
17
- millisecond solves.
11
+ **driftlet simulates charged species moving through one-dimensional devices**: semiconductor
12
+ junctions and solar cells, electrochemical cells and batteries, double layers, membranes and
13
+ nerves. It's a small JavaScript library with no dependencies, fast enough to solve live in a
14
+ web page as you drag a slider.
18
15
 
19
16
  [Live demos](https://markblundeberg.github.io/driftlet/demos/) · [npm](https://www.npmjs.com/package/driftlet) ·
20
17
  [reading level diagrams](docs/visualization.md) · [device reference](docs/device.md) ·
21
18
  [agent guide](llms.txt)
22
19
 
20
+ **One kind of physics.** Those look like different subjects, but underneath they're the same
21
+ one. Each is a cast of species (electrons, holes, ions, neutral molecules) that drift and
22
+ diffuse, each down its own electrochemical potential; their charge sets the electric field,
23
+ reactions turn one into another, and at interfaces they pass from one material to the next or
24
+ are stopped. A pn junction is electrons and holes in silicon. A silver electrode is Ag⁺ and NO₃⁻
25
+ in water, with Ag⁺ trading places with the metal's electrons at its surface. A nerve is Na⁺ and
26
+ K⁺ on either side of a membrane, crossing it through channels that open with voltage, and the
27
+ impulse travels along the fibre on the current those same ions carry inside it. Even the levels
28
+ each field draws are one quantity: a quasi-Fermi level, an electrode potential and a Nernst
29
+ potential are each a species' electrochemical potential. driftlet solves that one problem, in
30
+ steady state, in time and as small-signal impedance, so a new field is mostly a new cast, and
31
+ the work that makes it reliable in one field makes it reliable in the others.
32
+
23
33
  **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
34
+ weekend's work; one that keeps working on the devices people actually build is not. In real
35
+ devices concentrations span 30 orders of magnitude and double layers are a millionth of the
36
+ device's length, and there a plain Newton solver fails or, worse, quietly converges to a wrong
37
+ answer. driftlet is tested two ways: every physics feature against an analytic result or an independent code, and
26
38
  thousands of randomly generated devices (junctions, MOS capacitors, solar cells, electrolyte
27
39
  cells, electrodes) put through cold solves, sweeps, transients and impedance, every result
28
40
  checked. A solve that fails reports it, with what went wrong where it can tell.
@@ -44,7 +56,7 @@ import { bandDiagram } from 'driftlet/plot'; // a level diagram as an SVG str
44
56
  ```
45
57
 
46
58
  Or straight from a CDN in a page, pinned to a version:
47
- `https://cdn.jsdelivr.net/npm/driftlet@0.11.4/src/index.js` (and `src/kit.js`, `src/plot.js`).
59
+ `https://cdn.jsdelivr.net/npm/driftlet@0.13.0/src/index.js` (and `src/kit.js`, `src/plot.js`).
48
60
  TypeScript declarations ship in the package. Writing a demo with an LLM agent? Point it at
49
61
  [`llms.txt`](llms.txt).
50
62
 
@@ -141,25 +153,27 @@ them locally, serve the repository root (e.g. `python3 -m http.server`) and open
141
153
  | **Intercalation host**: cycling between cutoffs against the OCV | **Organic solar cell**: excitons diffusing to a donor/acceptor interface, against theory | **Perovskite hysteresis**: mobile ions and scan rate, against IonMonger |
142
154
  | [![charged nanochannel](demos/screenshots/channel.png)](https://markblundeberg.github.io/driftlet/demos/channel.html) | [![resting potential](demos/screenshots/cell.png)](https://markblundeberg.github.io/driftlet/demos/cell.html) | [![liquid junctions](demos/screenshots/junction.png)](https://markblundeberg.github.io/driftlet/demos/junction.html) |
143
155
  | **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 |
156
+ | [![action potential](demos/screenshots/axon.png)](https://markblundeberg.github.io/driftlet/demos/axon.html) | [![propagation](demos/screenshots/propagation.png)](https://markblundeberg.github.io/driftlet/demos/propagation.html) | [![myelin](demos/screenshots/myelin.png)](https://markblundeberg.github.io/driftlet/demos/myelin.html) |
157
+ | **Action potential**: Hodgkin and Huxley's gated channels, each ion's driving force through the spike, against the HH equations | **Propagation**: a spike travelling along an axon at HH's speed, out of the ions' drift; which ions carry it | **Myelin**: saltatory conduction, node to node, against a compartmental cable; a race with a bare axon |
144
158
 
145
159
  ## Is it for your problem?
146
160
 
147
161
  The same equations go by different names in different fields. If your problem is one-dimensional
148
162
  (planar, or radial: spheres and cylinders, or any cross-section varying along x), it's probably here:
149
163
 
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.
164
+ | If you work on | you may call it | start from |
165
+ |---|---|---|
166
+ | Semiconductor devices | drift–diffusion, van Roosbroeck, quasi-Fermi levels, Scharfetter–Gummel; pn, Schottky, MOS, heterojunctions, thin-film transistors | the [first example](#a-semiconductor-junction); [pn](https://markblundeberg.github.io/driftlet/demos/pn.html), [MOS](https://markblundeberg.github.io/driftlet/demos/mos.html) and [transistor](https://markblundeberg.github.io/driftlet/demos/saturation.html#transistor) demos |
167
+ | Solar cells | photogeneration, Beer–Lambert, radiative and SRH recombination, excitons, mobile ions | [solar](https://markblundeberg.github.io/driftlet/demos/solar.html), [organic](https://markblundeberg.github.io/driftlet/demos/organic.html) and [perovskite](https://markblundeberg.github.io/driftlet/demos/perovskite.html) demos |
168
+ | Electrochemistry, corrosion | Nernst–Planck, concentration polarization, limiting current, Butler–Volmer, Warburg, cyclic voltammetry, salt bridges, liquid junctions, mixed potentials | the [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), [limiting current](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 |
169
+ | Batteries, intercalation | OCV, insertion hosts, chemical diffusion | [insertion](https://markblundeberg.github.io/driftlet/demos/insertion.html) demo |
170
+ | Double layers, colloids | Poisson–Boltzmann, Gouy–Chapman–Stern, Debye screening, crowding (Bikerman) | [double-layer](https://markblundeberg.github.io/driftlet/demos/double-layer.html) demo |
171
+ | Membranes, nanofluidics | Donnan, ion exchange, Teorell–Meyer–Sievers, charged nanochannels and pores | [membrane](https://markblundeberg.github.io/driftlet/demos/membrane.html) and [nanochannel](https://markblundeberg.github.io/driftlet/demos/channel.html) demos |
172
+ | Solid-state ionics | mixed ionic–electronic conduction, defect chemistry, mobile ions | [`statistics`](test/statistics.test.js) and [`reactions`](test/reactions.test.js) tests |
173
+ | Cell physiology, neuroscience | resting potential, Nernst, Goldman–Hodgkin–Katz, the Na⁺/K⁺ pump, patch pipettes, Hodgkin–Huxley, action potentials, the cable equation, myelin | [resting-potential](https://markblundeberg.github.io/driftlet/demos/cell.html), [action-potential](https://markblundeberg.github.io/driftlet/demos/axon.html), [propagation](https://markblundeberg.github.io/driftlet/demos/propagation.html) and [myelin](https://markblundeberg.github.io/driftlet/demos/myelin.html) demos |
174
+
175
+ The tests are worked setups, each with its analytic check, so they double as recipes; the
176
+ [validation table](docs/reliability.md#the-validation-table) lists them all.
163
177
 
164
178
  ## How to think about it
165
179
 
@@ -178,10 +192,12 @@ driftlet insists on thermodynamically honest concepts ([conventions](docs/conven
178
192
  - The species voltage `V_i = μ̄_i / (z_i F)` and standard level `V°_i` are available as views,
179
193
  alongside redox levels: [reading level diagrams](docs/visualization.md) explains them.
180
194
 
195
+ ## What it does
196
+
181
197
  A device is a line of **regions** (each a **material** plus a length, fixed charge and
182
198
  initial composition), joined at **interfaces**, with a **contact** at each end. The contacts
183
199
  and any internal **ports** are its **terminals**, each held at a voltage or driven by a
184
- current. Supported physics:
200
+ current. Within that:
185
201
 
186
202
  - any mix of charged and neutral species; per-material diffusivities and standard potentials;
187
203
  species absent from some materials;
@@ -194,11 +210,15 @@ current. Supported physics:
194
210
  conductivity: Schottky and MOS gates as regions, electrodes with reactions at their faces,
195
211
  bipolar electrodes;
196
212
  - contacts as outside phases with known levels, joined by the same laws as internal faces:
197
- ohmic contacts, reversible electrodes, baths, pinned barriers, conductance links, gates and
213
+ ohmic contacts, reversible electrodes, baths, pinned barriers, conductance links, surface
214
+ velocities (thermionic emission over a Schottky barrier, surface recombination), gates and
198
215
  Stern layers;
199
216
  - interfaces with explicit alignment, blocking, interface resistance, and Butler–Volmer
200
217
  reactions with participants on either side (electrode reactions, ion and electron transfer,
201
218
  mixed potentials), and a choice of electrostatic law (pinned, neutral, Helmholtz);
219
+ - membranes as capacitive faces with ion permeabilities (Goldman–Hodgkin–Katz), pumps as
220
+ reactions on them and voltage-gated channels with Hodgkin–Huxley kinetics; or spread along a
221
+ region through a port, as an axon's wall;
202
222
  - bulk reactions with thermodynamically consistent mass action (recombination, water
203
223
  autoionisation, …);
204
224
  - imposed flow (advection) and current-free eddy mixing;
@@ -233,55 +253,11 @@ How it works numerically is in [numerics](docs/numerics.md).
233
253
 
234
254
  ## Validation
235
255
 
236
- Every physics feature is tested against analytic results (`npm test`, node's built-in runner).
237
-
238
- | What | Checked against | Tolerance |
239
- |---|---|---|
240
- | Equilibrium of any device | μ̄ of every species flat; zero current | 1e-9 RT or better |
241
- | Gouy–Chapman double layer | analytic charge and full nonlinear profile | 2e-3 |
242
- | Debye screening (linear limit) | gate in series with `ε/λ_D` | 1e-3 |
243
- | Donnan potential | analytic partition, between floating layers and real baths | 1e-8 |
244
- | Heterointerfaces with unequal dipoles (A \| B \| A) | per-face analytic double-layer split | 3e-3 |
245
- | Floating island | Gauss's law; conserved amounts through gate sweeps | 1e-12 |
246
- | pn junction | exact built-in potential; depletion charge; short-diode J–V | 1e-12; 1%; 2e-3 |
247
- | Long pn diode with recombination | Shockley J–V incl. depletion recombination | 2e-3 |
248
- | Haynes–Shockley pulse | an injected hole packet's drift, spread and decay at the ambipolar μ*, D*, 1/τ*; nothing lost while it goes in | 2e-4; 1e-2; 2e-3 |
249
- | A packet laid down as a `c0` profile | the same rates from a Gaussian at t = 0: μ*E t, σ₀² + 2D*t, e^(−t/τ*) | 3e-4; 1e-2; 1e-3 |
250
- | Beer–Lambert photogeneration in an n⁺p cell | J_sc = qΦ∫αe^(−αx)η(x)dx with the emitter's, depletion layer's and base's collection, α from 1e3 to 1e7 /m on one coarse grid | 5e-4 (3e-3 in the emitter) |
251
- | Organic bilayer: excitons | J_sc from excitons diffusing to the donor/acceptor interface (Beer–Lambert, a blocking anode, the splitting reaction's finite velocity); V_oc from charge-transfer detailed balance | 5e-4; 1e-6 V |
252
- | SRH recombination | bulk: G = R_SRH(n, p) with n = N_D + p, midgap and shallow traps, low to high injection; at a face: zero in the dark, the law between the two edge nodes, every pair collected or recombined | 1e-6; 1e-9 |
253
- | Perovskite J–V hysteresis, against IonMonger | an independent code (finite elements, Octave): mobile iodide vacancies, interface SRH, scans from 1 mV/s to 1 kV/s — hysteresis index, P_max, V_oc, the whole loop (as current where it's gentle, as a voltage offset near V_oc, where it falls at up to 1100 mA/cm² per volt) | 1.5e-3; 0.05 mW/cm²; 2 mV; 0.03 mA/cm² and 1 mV (0.010 and 0.31 mV found) |
254
- | Illuminated long pn diode | `J_sc = qG(L_n + L_p + W)` from a cold start; superposition at low injection | 5e-3; 1e-2 |
255
- | Schottky barrier (metal region \| n-Si) | surface density from the alignment; depletion charge | 5e-3; 2% |
256
- | Charged pore or nanochannel (resolved Donnan layers at each mouth) | conductance against salt, down to its plateau, as Teorell–Meyer–Sievers in a long pore (short ones short by end layers ∝ 1/L); the zero-current voltage across a salt gradient; a symmetric pore's I–V odd, rectification with charge on one side or bipolar | 1%; 5e-3 to 1e-4; 1e-9 |
257
- | Cell membrane (a capacitive face with permeabilities) | GHK resting potential (neutral or resolved solutions) and current–voltage curve; the same as a resolved 5 nm lipid layer; charging at I/C; with the Na⁺/K⁺ pump in a closed cell, Mullins–Noda, the static head 3Δμ̄_Na − 2Δμ̄_K = ΔG_ATP, and Donnan with the pump off | 1e-5 V; 3e-3; 2e-5 V; 1%; 1e-9 |
258
- | Liquid junctions in mixtures | Henderson's formula against JPCalc's published values; driftlet's steady junction against Planck by shooting and the stationary Nernst–Planck codes LJPcalc and JLJP, a patch pipette's K-gluconate among them; a free-diffusion junction constant in time, between the two | 0.015 mV; 2e-4 and 0.02 mV; 5e-3 mV |
259
- | Liquid junction, open circuit | cell EMF 2t₊(RT/F) ln(c₁/c₂); Planck diffusion potential | 1e-4 |
260
- | Salt diffusion mode from a `c0` profile | c̄ + a·sin(πx/2L) decaying at π²D/4L², D = 2D₊D₋/(D₊ + D₋) | 2e-5 |
261
- | Concentration polarization | `i = i_lim tanh(V/4V_T)`, incl. galvanostatic and load modes | 2e-4 |
262
- | Butler–Volmer electrode | Nernst equilibrium; mixed kinetic/diffusion closed form | 5e-4 |
263
- | Redox couple between inert electrodes | Nernst level of Fe³⁺/Fe²⁺; the couple's total conserved at equilibrium and under current | 1e-9; 1e-10 |
264
- | Interface conductance and ion transfer | series 1/G; BV rate law at the interface state | 1e-4; 1e-8 |
265
- | Mass action | `c(H⁺)c(OH⁻) = K_w` from standard potentials; moiety conservation | 1e-9 |
266
- | Second law | free energy in through the terminals (Σ N μ̄ over every species) equals the dissipation, every term ≥ 0: a pn diode, an open-circuit junction running on chemical input alone, electrodes with a bipolar plate, a port and a contact behind conductances | 1e-10 |
267
- | Transients | RC charging of a gated island; water relaxation rate | 1%; 2e-3 |
268
- | Time integration | BE first order, BDF2 second order; adaptive error control | ratios 2, 4 |
269
- | Advection and eddy mixing | exact convection–diffusion profile; `D + D_mix`; junction EMF unchanged by mixing | 1e-12; 1e-12; 1e-5 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
277
- | Conservation | per step, and against time-integrated contact fluxes | 1e-11 relative |
278
- | Strictly neutral limit (ε = 0) | Planck EMF; polarization with no overlimiting; Donnan at neutral faces | 1e-5; 1e-4; 1e-9 |
279
- | Fermi–Dirac statistics | `𝓕_{±1/2}` vs quadrature; degenerate bulk; accumulation charge via `𝓕_{3/2}` | 1e-13; 1e-12; 1e-3 |
280
- | Crowded double layer (lattice gas) | Kilic–Bazant–Ajdari charge, up to `ψ = 40 V_T`; custom function reproduces it | 5e-4; 1e-10 |
281
- | Non-ideal transport | steady flux −(D/L)ΔP of the grand potential (lattice exact, Redlich–Kister O(h²)) | 1e-12; 1e-4 |
282
- | Debye–Hückel | junction EMF 2t₊(RT/F) ln(a₁/a₂) with activities | 1e-6 |
283
- | Data library | ion μ° against the electrochemical series and `K_w`; D against limiting conductivities; band data against `n_i` | 10 mV; 0.5%; 15% |
284
- | Intercalation host (OCV) | composition vs table and isotherm; chemical diffusion flux and relaxation rate | 1e-12; 1e-5; 1e-4 |
256
+ Every physics feature lands with a test against an analytic result, or against an independent
257
+ code where there isn't one: over 300 tests (`npm test`), from Shockley's diode equation and the
258
+ Gouy–Chapman double layer to Hodgkin and Huxley's conduction velocity, and against IonMonger,
259
+ JPCalc, LJPcalc and JLJP. [How far to trust it](docs/reliability.md) lists every check with its
260
+ tolerance, alongside the random-device stress tests and the known limits.
285
261
 
286
262
  ## Performance
287
263
 
package/docs/device.md CHANGED
@@ -275,7 +275,76 @@ Na⁺/K⁺-ATPase between the outside (left) and the cytoplasm (right):
275
275
  together). Membrane potentials are $`\phi`$ differences between the two solutions, $`\phi_{\mathrm{in}} - \phi_{\mathrm{out}}`$,
276
276
  read from `sol.phi` (or a `'phi'` probe in a transient). A bath contact's terminal voltage is
277
277
  its reference species' level instead, what an electrode reversible to that species would read,
278
- so between two different solutions it differs from $`\Delta\phi`$ by that species' Nernst term.
278
+ so between two different solutions it differs from $`\Delta\phi`$ by that species' Nernst term;
279
+ a bath given no reference has its $`\phi`$ as its terminal voltage, and between two of those the
280
+ terminals read $`\Delta\phi`$ itself.
281
+
282
+ ### Voltage-gated channels
283
+
284
+ A permeability can be gated, as Hodgkin and Huxley's Na⁺ and K⁺ channels are: the face lists its
285
+ `gates`, each a fraction $`x`$ in [0, 1] with first-order kinetics in the voltage across the face,
286
+ $`V = \phi_{\mathrm{right}} - \phi_{\mathrm{left}}`$ at its two edge nodes,
287
+
288
+ ```math
289
+ \frac{dx}{dt} = \alpha(V)\,(1 - x) - \beta(V)\,x ,
290
+ ```
291
+
292
+ and a link names the gates that scale its $`P`$ (or a conductance link's $`G`$), with their exponents:
293
+ $`P\,m^3 h`$ for `{ m: 3, h: 1 }`. (A membrane along a region instead, an axon's, is a
294
+ [port](#a-membrane-through-a-window-gated-channels).)
295
+ Each of $`\alpha`$ and $`\beta`$ is one of NeuroML's three standard forms, with `rate` in 1/s and `midpoint`
296
+ and `scale` in volts:
297
+
298
+ | `type` | rate |
299
+ | --- | --- |
300
+ | `'exp'` | $`\mathtt{rate}\; e^{(V - \mathtt{midpoint})/\mathtt{scale}}`$ |
301
+ | `'sigmoid'` | $`\mathtt{rate} / (1 + e^{(\mathtt{midpoint} - V)/\mathtt{scale}})`$ |
302
+ | `'expLinear'` | $`\mathtt{rate}\; y/(1 - e^{-y})`$, $`y = (V - \mathtt{midpoint})/\mathtt{scale}`$ |
303
+
304
+ With the outside on the left, $`V`$ is the membrane potential $`\phi_{\mathrm{in}} - \phi_{\mathrm{out}}`$. Hodgkin and
305
+ Huxley's squid axon (at their 6.3 °C):
306
+
307
+ ```js nocheck
308
+ {
309
+ phi: { type: 'capacitive', C: 0.01 },
310
+ gates: {
311
+ m: { alpha: { type: 'expLinear', rate: 1000, midpoint: -0.040, scale: 0.010 }, beta: { type: 'exp', rate: 4000, midpoint: -0.065, scale: -0.018 } },
312
+ h: { alpha: { type: 'exp', rate: 70, midpoint: -0.065, scale: -0.020 }, beta: { type: 'sigmoid', rate: 1000, midpoint: -0.035, scale: 0.010 } },
313
+ n: { alpha: { type: 'expLinear', rate: 100, midpoint: -0.055, scale: 0.010 }, beta: { type: 'exp', rate: 125, midpoint: -0.065, scale: -0.080 } },
314
+ },
315
+ species: {
316
+ 'Na+': { type: 'permeability', P: 1.16e-6, gates: { m: 3, h: 1 } },
317
+ 'K+': { type: 'permeability', P: 1.33e-6, gates: { n: 4 } },
318
+ 'Cl-': { type: 'permeability', P: 6.4e-9 }, // the leak
319
+ 'A-': 'blocked',
320
+ },
321
+ }
322
+ ```
323
+
324
+ The gates scale permeabilities and nothing else: they hold no charge (the small gating current
325
+ of the channels' voltage sensors is left out, as Hodgkin and Huxley left it out), and each ion
326
+ still crosses by electrodiffusion down its own $`\bar\mu`$, so the dissipation stays positive whatever
327
+ they do. In steady state each gate is at $`\alpha/(\alpha + \beta)`$; each solution reports them as
328
+ `interfaces[f].gates` (`{ m, h, n }`) with the voltage they follow, `interfaces[f].V`.
329
+
330
+ Two things differ from the textbook equations. The currents are GHK's, which rectify: a channel's
331
+ conductance depends on the voltage, so a permeability matches a conductance $`g`$ at one voltage
332
+ only (above, the chord conductances at rest, −65 mV, for Hodgkin and Huxley's 120, 36 and 0.3
333
+ mS/cm²; matched at the reversal potentials instead, the rest depolarises itself and the axon
334
+ fires on its own). Their own linear channels are conductance links: $`G\,(V_{i,L} - V_{i,R})`$
335
+ toward +x is $`g\,(E_i - V)`$, with $`E_i`$ the Nernst potential at the edge concentrations, so
336
+ `{ type: 'conductance', G: 360, gates: { n: 4 } }` is their K⁺ channel exactly
337
+ (`hodgkinHuxley({ T, linear: true })` writes all three). Between two baths whose terminal
338
+ voltage is their $`\phi`$, a held step is their voltage clamp, and the K⁺ current at its end is
339
+ $`g_K n_\infty^4 (V - E_K)`$ to 2e-3. And a cell without a pump has no resting steady state: `solve()` finds its
340
+ Donnan equilibrium, every permeant ion level across the membrane, which the real cell reaches
341
+ only over hours. Start a transient from the concentrations given (it settles to rest within
342
+ milliseconds), or keep the gradients with the Na⁺/K⁺ pump on the same face. Started from the
343
+ same state, a squid axon's action potential follows the space-clamped Hodgkin–Huxley equations
344
+ with GHK currents to within 1 mV; and in small signals, with the membrane held at rest between
345
+ two baths, `impedance()` gives the gates' inductance that Cole measured (the impedance inductive
346
+ below ~50 Hz and resonant near 60 Hz), the linearised equations' to 5e-4
347
+ ([`gates`](../test/gates.test.js) test).
279
348
 
280
349
  ### Faces next to a conductor
281
350
 
@@ -385,13 +454,22 @@ contacts: {
385
454
  at $`V + \mathtt{offset}`$, $`J = G \cdot (V_{\mathrm{out}} - V_i)`$, G in S/m².
386
455
  - `{ type: 'exchange', k, mu }` (neutral species):
387
456
  $`N_{\mathrm{in}} = k \cdot (\mu_{\mathrm{out}} - \mu)/RT`$, k in mol/(m²·s).
457
+ - `{ type: 'velocity', v, offset }` (or `mu`, for a neutral species): a surface velocity,
458
+ $`N_{\mathrm{in}} = v\,(c_{\mathrm{eq}} - c)`$ with v in m/s and $`c_{\mathrm{eq}}`$ the end node's
459
+ concentration in equilibrium with the outside level, i.e.
460
+ $`v\,c\,(e^{(\bar\mu_{\mathrm{out}} - \bar\mu)/RT} - 1)`$, which keeps the flux's sign that of the
461
+ level difference whatever the statistics. It lies between `'blocked'` (v → 0) and
462
+ `'equilibrium'` (v → ∞): a contact's surface recombination velocity, or thermionic emission
463
+ over a Schottky barrier (below).
388
464
  - `'blocked'`: no flux.
389
465
 
390
466
  **`bath`** (instead of `species` and `phi`): the outside phase is a neutral composition `c`
391
467
  held in place by a charged `reference` species (as for a reversible reference electrode, e.g.
392
468
  Cl⁻ for Ag/AgCl), which is the terminal. Every bath species is in equilibrium at the level its
393
469
  composition implies, and the $`\phi`$ law is `'bulk'`. An optional `offset` places the reference
394
- species relative to V.
470
+ species relative to V. Without a `reference`, V is the bath's own $`\phi`$: an ideal salt bridge
471
+ (no junction potential), the convention by which membrane potentials are $`\phi`$ differences.
472
+ Between two such baths, a held $`V_{\mathrm{right}} - V_{\mathrm{left}}`$ is a voltage clamp.
395
473
 
396
474
  **`phi`**, required whenever any species or reaction connects at the contact (default
397
475
  `'neutral'`):
@@ -409,6 +487,18 @@ species relative to V.
409
487
  means. For example, a Schottky barrier $`\phi_B`$ on n-type material with
410
488
  $`\mu^\circ_{\mathrm{e}^-} = 0`$ is `zeroCharge: φ_B`.
411
489
 
490
+ A Schottky contact is that pinned barrier with its carriers passing at their emission velocities:
491
+ `{ V, terminal: 'e-', species: { 'e-': { type: 'velocity', v }, 'h+': { type: 'velocity', v: vp, offset: 0 } }, phi: 'pinned', zeroCharge: φ_B }`,
492
+ with Bethe's thermionic-emission velocity $`v = A^* T^2/(F N_c)`$ ($`N_c`$ in mol/m³, the data
493
+ library's `cRef` for e⁻; about $`2 \times 10^4`$ m/s in n-Si with $`A^*`$ = 112 A/(cm²K²)). The
494
+ forward current is then thermionic emission's, $`J = A^* T^2 e^{-\phi_B/V_T}(e^{V/V_T} - 1)`$,
495
+ lowered where the drift–diffusion through the depletion region can't keep up (Crowell and Sze's
496
+ combination): 4–6% short of it for a 0.85 eV barrier on 1e16 cm⁻³ n-Si from 0.1 to 0.4 V
497
+ ([`contacts`](../test/contacts.test.js) test). With `'equilibrium'` links instead (an infinite
498
+ velocity) the barrier passes whatever the semiconductor brings to it, which is the diffusion
499
+ theory of the Schottky diode: there, 17 to 27 times more current. Neither has image-force
500
+ lowering or tunnelling.
501
+
412
502
  Like every alignment, `zeroCharge` is a property of that interface. To estimate it from vacuum
413
503
  levels, `vacuumZeroCharge(def, W, inside)` from `driftlet/kit` takes the work function W of the
414
504
  conductor at the terminal voltage and the inside material's anchor and offset (see the
@@ -432,6 +522,7 @@ any combination:
432
522
  | `reactions`, `area` | an [electrode spread through the window](#electrodes-spread-through-a-window) | a thin film on a metal, a crevice, a porous electrode |
433
523
  | `surface` | [species on that electrode's sites](#an-electrodes-surface-adsorbates-and-passive-films) | adsorbates, a passive film |
434
524
  | `capacitance`, `area` | [charge held across a capacitance](#a-capacitance-through-a-window) | a gate along a channel, a double layer |
525
+ | `gates` | [voltage-gated channels](#a-membrane-through-a-window-gated-channels) on its conductance links | an axon's membrane along it |
435
526
 
436
527
  ```js nocheck
437
528
  ports: [{
@@ -618,6 +709,44 @@ below threshold through saturation (second order in the grid); a porous electrod
618
709
  impedance $`\sqrt{r/y}\coth(L\sqrt{ry})`$, the double layer in series with the ions' chemical
619
710
  capacitance; and the gate's low-frequency impedance against $`dQ/dV_G`$ from steady states.
620
711
 
712
+ ### A membrane through a window: gated channels
713
+
714
+ A port with a capacitance can carry [gates](#voltage-gated-channels), as a face can: at each node of
715
+ its window, each gate is a fraction open with Hodgkin–Huxley kinetics in the voltage across the
716
+ port's capacitance there, $`V_m = \phi - (V - \mathtt{zeroCharge})`$, and its conductance links name
717
+ the gates that scale them (`G × m³h`). That's a membrane all along a region, between it and an
718
+ outside held by the port: an axon's, with x running along the axon. Per volume of axoplasm, a
719
+ cylinder of radius a has $`2/a`$ of membrane (`area`). Give the port the outside's composition as a
720
+ `bath`: its V is then the outside's $`\phi`$ (held at 0, with `zeroCharge: 0`), and each linked ion's
721
+ level there follows from the composition, $`(\mu^\circ + RT\ln(c_{out}/c_{\mathrm{ref}}))/(zF)`$ against
722
+ it, as an `offset` would give it. (Only the linked ions' levels are taken, so the bath needn't list
723
+ or balance the rest.) A conductance link's current is then $`G\,(V_{i,out} - V_i)`$, which is
724
+ $`g\,a_m (E_i - V_m)`$: Hodgkin and Huxley's linear channel, with $`E_i`$ the Nernst potential at the
725
+ inside's concentration there. The kit's `hodgkinHuxley({ T, area })` writes their links so:
726
+
727
+ ```js nocheck
728
+ const hh = hodgkinHuxley({ T, area: 2 / 238e-6 }); // a squid giant axon, radius 238 µm
729
+ ports: [{
730
+ name: 'membrane', region: 'axon', V: 0, area: 2 / 238e-6,
731
+ capacitance: { C: 0.01, zeroCharge: 0 },
732
+ bath: { c: { 'Na+': 367, 'K+': 18.6, 'Cl-': 459 } }, // sea water, as far as the channels go
733
+ gates: hh.gates,
734
+ species: hh.species, // Na⁺ (G × m³h), K⁺ (G × n⁴), and the leak (Cl⁻), G = g × area
735
+ }]
736
+ ```
737
+
738
+ With the axoplasm strictly neutral and the outside held (no resistance outside), the ions'
739
+ drift along x and the membrane's charging are cable theory, without its being put in: a squid
740
+ axon's action potential propagates at Hodgkin and Huxley's speed, 18.73 m/s for their 18.5 °C
741
+ axon (they computed 18.8 by hand), the cable equation's own to 2e-3
742
+ ([`cable`](../test/cable.test.js) test). A myelinated axon is regions of one axoplasm, each node
743
+ of Ranvier and each internode its own, with a port each: the nodes' gated, the internodes' a small
744
+ capacitance (myelin) and nothing else. The grid refines at every node's faces, and the spike jumps
745
+ node to node, arriving at each within 1 µs of a compartmental cable's prediction (the same test).
746
+ Each solution's `ports[k]` reports the gates at each node of the window, `gates[name][j]`,
747
+ and the voltage they follow, `Vm[j]`. A gated window can't overlap another port's gated or
748
+ surfaced one.
749
+
621
750
  ### What a solution reports
622
751
 
623
752
  Each solution's `ports[k]`, as the port has them:
@@ -630,6 +759,7 @@ Each solution's `ports[k]`, as the port has them:
630
759
  | `rates[q][j]` | reaction q's forward rate at node j, mol/(m²·s) of electrode (reaction first, then node) |
631
760
  | `coverage[name][j]`, `bare[j]` | with a surface: each species' coverage θ, and the bare fraction θ₀ to full precision |
632
761
  | `sigma[j]`, `charge` | with a capacitance: σ per area of electrode (C/m²), and the total the port's side holds |
762
+ | `gates[name][j]`, `Vm[j]` | with gates: each gate's fraction open, and the voltage across the capacitance that they follow (V) |
633
763
 
634
764
  The arrays are typed (`Float64Array`), so `Array.from` them before mapping to anything but
635
765
  numbers. The kit's [`polarization()`](kit.md#polarization-curves-an-evans-diagram) gives each reaction's rate at a spot of the
@@ -739,12 +869,14 @@ const now = dev.solution(); // snapshot of the current state
739
869
  - `solve()` finds the steady state from the current state without advancing time. If every
740
870
  species is fed by a contact, it solves the steady equations directly. Otherwise conserved
741
871
  amounts (blocked species, reactive moieties) are kept exactly. If a direct solve fails at a
742
- bias (a cold start far from equilibrium), it solves with both terminals level and ramps the
743
- right terminal's voltage to its target; with generation reactions (below), it ramps their
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.
872
+ bias, it ramps the contacts' voltages from where they were last solved (the left one first,
873
+ where it moved), or from level terminals on a cold start; with generation reactions (below),
874
+ it ramps their rates up from nearly nothing. A terminal driven by a current (open circuit,
875
+ say) is held at a voltage instead, the voltage marched until the current crosses its target,
876
+ and floated from there. If 20 V either way brings no crossing (a current beyond the limiting
877
+ one, or a device that only stores what comes in, which has no steady state at a current), the
878
+ solve fails with a warning giving the range of currents the held voltages passed. A solve that
879
+ fails reports the state it failed in, then leaves the device as it was before it.
748
880
  - `step(dt, { method })` advances the transient by dt seconds, halving internally where Newton
749
881
  needs it. `method` is `'be'` (backward Euler, the default) or `'bdf2'`.
750
882
  - `advance(tEnd, opts)` integrates adaptively to `tEnd` with variable-step BDF2, controlling
@@ -762,7 +894,9 @@ const now = dev.solution(); // snapshot of the current state
762
894
  `probes: [{ x, species, quantity, region }]` the trace also reads inside the device,
763
895
  as `trace.probes[k]` beside `trace.t`: a species' concentration (`quantity: 'c'`, the default,
764
896
  mol/m³) or species voltage (`'V'`), or $`\phi`$ itself (`'phi'`, no species), linearly between the
765
- nodes around `x` (at an interface, `region` picks the side). That's what a detector at `x` sees. Probes read at accepted steps, which grow
897
+ nodes around `x` (at an interface, `region` picks the side). That's what a detector at `x` sees.
898
+ At a face, `{ interface: f, species }` reads a species' flux through it (mol/(m²·s), toward +x,
899
+ as `interfaces[f].N`), and `{ interface: f, gate }` a gate's fraction open. Probes read at accepted steps, which grow
766
900
  as a transient slows, so give `dtMax` to resolve a signal in time.
767
901
  - `impedance(frequencies, { terminal, profiles })` solves the steady state, then linearises about
768
902
  it: $`Z(f) = \delta V/\delta I`$ in Ω·m² at one terminal (`'right'` by default), with I into the
package/docs/kit.md CHANGED
@@ -57,8 +57,12 @@ console.log(`I = ${sol.current.toFixed(1)} A/m²`);
57
57
  - **Contacts** are the first and last items. `ohmic(drive, carriers)` holds each carrier in
58
58
  equilibrium with a metal at the terminal voltage (default `['e-', 'h+']`, an
59
59
  infinite-recombination contact; `['e-']` alone is selective, and is the current collector on
60
- a metal region). `bath(c, reference, drive, { offset })` is a bath (`offset` places the reference species
61
- relative to the terminal voltage, e.g. to read a solution's potentials against SHE). A drive is a voltage, a waveform, or
60
+ a metal region). `bath(c, reference, drive, { offset })` is a bath whose terminal voltage is
61
+ the level of its `reference` species, as an electrode reversible to it would read (`offset`
62
+ places that species relative to the terminal voltage, e.g. to read a solution's potentials
63
+ against SHE). `bath(c, drive)`, without a reference, is a bath whose terminal voltage is its
64
+ φ: an ideal salt bridge, the convention of membrane potentials, so that between two such baths
65
+ V_right − V_left is the φ difference a voltage clamp sets. A drive is a voltage, a waveform, or
62
66
  `{ V }`, `{ I }`, `{ V, R }`.
63
67
  - **`library`** takes pieces with `species` and `materials`, merged with any given directly
64
68
  (`combine()` does the merging on its own). A species or material given twice must be given
@@ -142,6 +146,25 @@ Concentrations are held, so these are the curves of that instant (for a mixed po
142
146
  kinetic picture). Away from the actual level the composition beside the electrode would change,
143
147
  which is the transport the full solve accounts for.
144
148
 
149
+ ## Channels
150
+
151
+ **`hodgkinHuxley({ inside, outside, T, g, leak, leakZ, at, names, linear, area })`**: Hodgkin and Huxley's
152
+ squid-axon channels for a [membrane face](device.md#voltage-gated-channels) with the outside on its
153
+ left: `{ gates, species }`, the face's `gates` (m, h and n, their 1952 rates at 6.3 °C scaled to
154
+ `T` by Q₁₀ = 3) and links for Na⁺ (P·m³h), K⁺ (P·n⁴) and the leak (one ion, `leak`, of charge
155
+ `leakZ`: Cl⁻ by default). The currents are GHK's, which rectify, so each permeability is the one
156
+ whose chord conductance at `at` (−65 mV) is HH's `g` (120, 36 and 0.3 mS/cm²) at the
157
+ concentrations given. Spread it into the face beside its $`\phi`$ law, adding the other species'
158
+ links: `{ phi: { type: 'capacitive', C: 0.01 }, gates: hh.gates, species: { ...hh.species, 'A-': 'blocked' } }`.
159
+ With `linear: true` (and no concentrations) the face gets HH's own linear channels instead:
160
+ conductance links, `G` = g, gated the same, whose currents are g(V − E) with E each ion's Nernst
161
+ level at the concentrations either side; between two `bath`s without a reference species, that's
162
+ their voltage clamp. For a [membrane port](device.md#a-membrane-through-a-window-gated-channels) along a region (an axon
163
+ along x), give `area`, the membrane per volume (2/a for radius a), in place of `inside`, `outside`
164
+ and `at`: the links are then their linear conductances, `G` = g × area, gated the same, each ion's
165
+ reversal potential following from the port's `bath` and the concentrations inside.
166
+ `ghkCurrent(z, inside, outside, V, T)` is the GHK current (outward, A/m²) at unit permeability.
167
+
145
168
  ## Sources
146
169
 
147
170
  Helpers that write sources as plain data:
package/docs/numerics.md CHANGED
@@ -210,9 +210,16 @@ flux node's own rows are the interface laws:
210
210
  | species, blocked or absent on one side | $`N_f = 0`$ |
211
211
  | species, conductance | $`N_f - G V_T (\eta_L - \eta_R)/(z^2 F) = 0`$ |
212
212
  | 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`$ |
213
+ | gate $`q`$ | $`(x_q - x_q^{\mathrm{old}})/dt - \alpha(V)(1 - x_q) + \beta(V)\,x_q = 0`$, $`V = V_T(\hat\phi_R - \hat\phi_L)`$; a gated permeability's $`P`$ takes $`\prod x^p`$ |
213
214
 
214
215
  Every row couples only neighbours, with no penalty terms. A face reaction's rate is an unknown
215
- of the face block, so the two edge nodes it couples meet only through the block between them.
216
+ of the face block, so the two edge nodes it couples meet only through the block between them;
217
+ so is a gate's fraction open, its row coupling it to the two edge nodes' $`\hat\phi`$, and the gated
218
+ fluxes to it, all within the face's block and its neighbours'. Newton's convergence and the time
219
+ step's error count a gate's change as they count a potential's in thermal units (1e-3 of $`m`$ is
220
+ 0.3% of $`m^3`$), and a gate is evaluated in [0, 1] where Newton's iterates stray past its ends. A
221
+ membrane port's gates are unknowns of each window node's block, after its surface's coverages,
222
+ with the same row in the voltage across the port's capacitance there.
216
223
  A species can take part in a reaction at a face and also cross it by a link (its flux slot)
217
224
  at the same time; where it exists on both sides, that link must be given explicitly. A continuity
218
225
  row has a zero diagonal block, but block Thomas still sees a non-singular block there once the
@@ -24,17 +24,74 @@ Every physics feature is checked against an analytic result, or an independent c
24
24
  there isn't one: `npm test`, over 300 tests, each a worked device with its closed form. They include
25
25
  Shockley's diode equation, depletion and MOS charge, collection theory under Beer–Lambert light,
26
26
  Gouy–Chapman and Kilic–Bazant–Ajdari double layers, Donnan and Teorell–Meyer–Sievers
27
- partitions, Goldman–Hodgkin–Katz, the limiting current and Butler–Volmer kinetics, the
27
+ partitions, Goldman–Hodgkin–Katz, Hodgkin and Huxley's action potential and its conduction
28
+ velocity, the limiting current and Butler–Volmer kinetics, the
28
29
  Macdonald and finite-length Warburg impedances, Cottrell and spherical diffusion, and the
29
30
  second law (the free energy in through the terminals equals what's dissipated, every term
30
31
  non-negative). Against other codes: IonMonger's perovskite J–V hysteresis (finite elements,
31
- Octave), and liquid-junction potentials against JPCalc, LJPcalc and JLJP. The README's
32
- [validation table](../README.md#validation) lists each with its tolerance.
32
+ Octave), and liquid-junction potentials against JPCalc, LJPcalc and JLJP. The
33
+ [table below](#the-validation-table) lists each with its tolerance.
33
34
 
34
35
  Each solution can check itself too: `check()` from `driftlet/kit` balances every species'
35
36
  ledger (what comes in through each terminal, is made or destroyed), the conservation
36
37
  bookkeeping of a transient, and the grid (the device solved again on a grid twice as fine).
37
38
 
39
+ ### The validation table
40
+
41
+ Each check, with the tolerance it's held to. The tests are worked setups, so they double as
42
+ recipes.
43
+
44
+ | What | Checked against | Tolerance |
45
+ |---|---|---|
46
+ | Equilibrium of any device | μ̄ of every species flat; zero current | 1e-9 RT or better |
47
+ | Gouy–Chapman double layer | analytic charge and full nonlinear profile | 2e-3 |
48
+ | Debye screening (linear limit) | gate in series with `ε/λ_D` | 1e-3 |
49
+ | Donnan potential | analytic partition, between floating layers and real baths | 1e-8 |
50
+ | Heterointerfaces with unequal dipoles (A \| B \| A) | per-face analytic double-layer split | 3e-3 |
51
+ | Floating island | Gauss's law; conserved amounts through gate sweeps | 1e-12 |
52
+ | pn junction | exact built-in potential; depletion charge; short-diode J–V | 1e-12; 1%; 2e-3 |
53
+ | Long pn diode with recombination | Shockley J–V incl. depletion recombination | 2e-3 |
54
+ | Haynes–Shockley pulse | an injected hole packet's drift, spread and decay at the ambipolar μ*, D*, 1/τ*; nothing lost while it goes in | 2e-4; 1e-2; 2e-3 |
55
+ | A packet laid down as a `c0` profile | the same rates from a Gaussian at t = 0: μ*E t, σ₀² + 2D*t, e^(−t/τ*) | 3e-4; 1e-2; 1e-3 |
56
+ | Beer–Lambert photogeneration in an n⁺p cell | J_sc = qΦ∫αe^(−αx)η(x)dx with the emitter's, depletion layer's and base's collection, α from 1e3 to 1e7 /m on one coarse grid | 5e-4 (3e-3 in the emitter) |
57
+ | Organic bilayer: excitons | J_sc from excitons diffusing to the donor/acceptor interface (Beer–Lambert, a blocking anode, the splitting reaction's finite velocity); V_oc from charge-transfer detailed balance | 5e-4; 1e-6 V |
58
+ | SRH recombination | bulk: G = R_SRH(n, p) with n = N_D + p, midgap and shallow traps, low to high injection; at a face: zero in the dark, the law between the two edge nodes, every pair collected or recombined | 1e-6; 1e-9 |
59
+ | Perovskite J–V hysteresis, against IonMonger | an independent code (finite elements, Octave): mobile iodide vacancies, interface SRH, scans from 1 mV/s to 1 kV/s — hysteresis index, P_max, V_oc, the whole loop (as current where it's gentle, as a voltage offset near V_oc, where it falls at up to 1100 mA/cm² per volt) | 1.5e-3; 0.05 mW/cm²; 2 mV; 0.03 mA/cm² and 1 mV (0.010 and 0.31 mV found) |
60
+ | Illuminated long pn diode | `J_sc = qG(L_n + L_p + W)` from a cold start; superposition at low injection | 5e-3; 1e-2 |
61
+ | Schottky barrier (metal region \| n-Si) | surface density from the alignment; depletion charge | 5e-3; 2% |
62
+ | Surface velocity at a contact | minority electrons through a base to a contact of velocity S, n₀(e^(V/V_T) − 1)/(W/D + 1/S), S from 10 to 1e5 m/s; a Schottky diode's thermionic emission, short of A*T²e^(−φ_B/V_T)(e^(V/V_T) − 1) by its depletion region in series, the same swept as built cold | 1e-5; 3–7%; 1e-6 |
63
+ | Charged pore or nanochannel (resolved Donnan layers at each mouth) | conductance against salt, down to its plateau, as Teorell–Meyer–Sievers in a long pore (short ones short by end layers ∝ 1/L); the zero-current voltage across a salt gradient; a symmetric pore's I–V odd, rectification with charge on one side or bipolar | 1%; 5e-3 to 1e-4; 1e-9 |
64
+ | Cell membrane (a capacitive face with permeabilities) | GHK resting potential (neutral or resolved solutions) and current–voltage curve; the same as a resolved 5 nm lipid layer; charging at I/C; with the Na⁺/K⁺ pump in a closed cell, Mullins–Noda, the static head 3Δμ̄_Na − 2Δμ̄_K = ΔG_ATP, and Donnan with the pump off | 1e-5 V; 3e-3; 2e-5 V; 1%; 1e-9 |
65
+ | Voltage-gated channels (Hodgkin–Huxley gates on a membrane face) | each gate at α/(α + β) under a voltage clamp, and relaxing at α + β after a step; HH's voltage clamp with their linear channels between baths, the K⁺ current g_K n∞⁴ (V − E_K); a squid axon's action potential against the space-clamped HH equations with GHK currents; its impedance at rest (inductive, resonant) against the linearised equations | 1e-12; 2e-4; 2e-3; 1 mV; 5e-4 |
66
+ | A propagating action potential (a squid axon along x: gated membrane port, ions drifting in the axoplasm) | rest at HH's −65 mV and gate values; conduction velocity against the cable equation solved directly, and HH's 18.8 m/s; a myelinated axon's saltatory conduction, node by node, against a compartmental cable | 0.01 mV, 1e-4; 2e-3; 1%; 1 µs of ~25 per node |
67
+ | Liquid junctions in mixtures | Henderson's formula against JPCalc's published values; driftlet's steady junction against Planck by shooting and the stationary Nernst–Planck codes LJPcalc and JLJP, a patch pipette's K-gluconate among them; a free-diffusion junction constant in time, between the two | 0.015 mV; 2e-4 and 0.02 mV; 5e-3 mV |
68
+ | Liquid junction, open circuit | cell EMF 2t₊(RT/F) ln(c₁/c₂); Planck diffusion potential | 1e-4 |
69
+ | Salt diffusion mode from a `c0` profile | c̄ + a·sin(πx/2L) decaying at π²D/4L², D = 2D₊D₋/(D₊ + D₋) | 2e-5 |
70
+ | Concentration polarization | `i = i_lim tanh(V/4V_T)`, incl. galvanostatic and load modes | 2e-4 |
71
+ | Butler–Volmer electrode | Nernst equilibrium; mixed kinetic/diffusion closed form | 5e-4 |
72
+ | Redox couple between inert electrodes | Nernst level of Fe³⁺/Fe²⁺; the couple's total conserved at equilibrium and under current | 1e-9; 1e-10 |
73
+ | Interface conductance and ion transfer | series 1/G; BV rate law at the interface state | 1e-4; 1e-8 |
74
+ | Mass action | `c(H⁺)c(OH⁻) = K_w` from standard potentials; moiety conservation | 1e-9 |
75
+ | Second law | free energy in through the terminals (Σ N μ̄ over every species) equals the dissipation, every term ≥ 0: a pn diode, an open-circuit junction running on chemical input alone, electrodes with a bipolar plate, a port and a contact behind conductances | 1e-10 |
76
+ | Transients | RC charging of a gated island; water relaxation rate | 1%; 2e-3 |
77
+ | Time integration | BE first order, BDF2 second order; adaptive error control | ratios 2, 4 |
78
+ | Advection and eddy mixing | exact convection–diffusion profile; `D + D_mix`; junction EMF unchanged by mixing | 1e-12; 1e-12; 1e-5 |
79
+ | 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 |
80
+ | 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 |
81
+ | 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 |
82
+ | 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 |
83
+ | 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 |
84
+ | 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 |
85
+ | 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; a MOS capacitor with a metal gate and a channel port, C = dQ/dV at the gate, passive at the back | 3e-4; 3e-5; 1e-6; 1e-4, 1e-6; 1e-4; 1e-3 |
86
+ | Conservation | per step, and against time-integrated contact fluxes | 1e-11 relative |
87
+ | Strictly neutral limit (ε = 0) | Planck EMF; polarization with no overlimiting; Donnan at neutral faces | 1e-5; 1e-4; 1e-9 |
88
+ | Fermi–Dirac statistics | `𝓕_{±1/2}` vs quadrature; degenerate bulk; accumulation charge via `𝓕_{3/2}` | 1e-13; 1e-12; 1e-3 |
89
+ | Crowded double layer (lattice gas) | Kilic–Bazant–Ajdari charge, up to `ψ = 40 V_T`; custom function reproduces it | 5e-4; 1e-10 |
90
+ | Non-ideal transport | steady flux −(D/L)ΔP of the grand potential (lattice exact, Redlich–Kister O(h²)) | 1e-12; 1e-4 |
91
+ | Debye–Hückel | junction EMF 2t₊(RT/F) ln(a₁/a₂) with activities | 1e-6 |
92
+ | Data library | ion μ° against the electrochemical series and `K_w`; D against limiting conductivities; band data against `n_i` | 10 mV; 0.5%; 15% |
93
+ | Intercalation host (OCV) | composition vs table and isotherm; chemical diffusion flux and relaxation rate | 1e-12; 1e-5; 1e-4 |
94
+
38
95
  ## Robust: random devices
39
96
 
40
97
  The validation suite shows driftlet is right where it converges. Whether it converges, on
@@ -146,6 +146,16 @@ conducting when it's empty. With no fixed charge, the carriers are only those th
146
146
  the ions. The carrier's `cRef` plays no part (only the combination's $`\mu^\circ`$ and the ion's
147
147
  `cRef` enter), but it must still be given.
148
148
 
149
+ The ion's `D` is its dilute-limit coefficient, as everywhere: the chemical diffusivity, $`D`$
150
+ times the thermodynamic factor, follows from the OCV's slope. For an ideal-solution curve,
151
+ $`E = E^\circ - (RT/F)\ln(x/(1-x))`$, it's $`D/(1-x)`$, so a host filling toward $`x = 1`$
152
+ diffuses ever faster, and a $`D`$ measured mid-range isn't the one to give. (A constant chemical
153
+ diffusivity isn't expressible.) A host that only stores what comes in, filling at a constant
154
+ current through its collector, has no steady state: `advance()` it from its `c0`. In a sphere,
155
+ `geometry: { type: 'spherical', r0: 0 }`, with the centre sealed (`{ phi: 'neutral' }`, every
156
+ species blocked), the ion held at the surface by its reference and the electrons fed by a port
157
+ there (a current into the device: negative, to fill a cathode).
158
+
149
159
  An insertion host belongs with [conductor regions](device.md#materials) more than with the other
150
160
  statistics here. Both are conductors with no $`\phi`$ of their own:
151
161
  - **A metal** is the one-carrier case: its Fermi level is its only unknown.