driftlet 1.2.1 → 1.2.2
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 +2 -2
- package/docs/internals.md +86 -0
- package/docs/numerics.md +12 -1
- package/docs/reliability.md +15 -4
- package/llms.txt +6 -6
- package/package.json +1 -1
- package/src/lawRows.js +562 -0
- package/src/laws.js +693 -0
- package/src/solver.js +63 -1006
- package/types/lawRows.d.ts +5 -0
- package/types/laws.d.ts +86 -0
- package/types/solver.d.ts +78 -43
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ import { bandDiagram } from 'driftlet/plot'; // a level diagram as an SVG str
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Or straight from a CDN in a page, pinned to a version:
|
|
59
|
-
`https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
59
|
+
`https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/index.js` (and `src/kit.js`, `src/plot.js`).
|
|
60
60
|
TypeScript declarations ship in the package. Writing a demo with an LLM agent? Point it at
|
|
61
61
|
[`llms.txt`](llms.txt).
|
|
62
62
|
|
|
@@ -300,7 +300,7 @@ npm run shots # retake the demos' screenshots, each caught at a set state (to
|
|
|
300
300
|
|
|
301
301
|
Design notes and plans: [reliability](docs/reliability.md), [numerics](docs/numerics.md), [conventions](docs/conventions.md),
|
|
302
302
|
[statistics](docs/statistics.md), [the kit](docs/kit.md), [reading level diagrams](docs/visualization.md),
|
|
303
|
-
[data](docs/data.md), [roadmap](ROADMAP.md).
|
|
303
|
+
[data](docs/data.md), [internals](docs/internals.md) (the source, module by module), [roadmap](ROADMAP.md).
|
|
304
304
|
|
|
305
305
|
## Credits
|
|
306
306
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Internals
|
|
2
|
+
|
|
3
|
+
A map of the source for anyone reading or reviewing it: what each module is for, the layout of
|
|
4
|
+
the solver's state that the rest assumes, and which modules state a contract. The user-facing
|
|
5
|
+
reference is the other docs; this one is about the code.
|
|
6
|
+
|
|
7
|
+
## The rule: a module states its contract
|
|
8
|
+
|
|
9
|
+
A module whose correctness can't be seen from its call sites says at its head what it reads,
|
|
10
|
+
what it returns or writes, and what must hold of the result, so that it can be checked on its
|
|
11
|
+
own, without the rest of the library in mind. Where the contract and the code disagree, one of
|
|
12
|
+
them is wrong: fix both, with a test, in the same commit.
|
|
13
|
+
|
|
14
|
+
| Module | Contract |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `src/laws.js` | yes: the steady system's conservation laws |
|
|
17
|
+
| `src/lawRows.js` | yes: the laws written into the Newton system, and the bordered solve |
|
|
18
|
+
| `src/difference.js` | header: the dilute kernels in difference form |
|
|
19
|
+
| `src/blockTridiagonal.js` | header: the block Thomas solver |
|
|
20
|
+
| `src/statistics.js` | header: each model's `evaluate` / `invert` |
|
|
21
|
+
| `src/solver.js` | not yet (below) |
|
|
22
|
+
|
|
23
|
+
## From a definition to a solution
|
|
24
|
+
|
|
25
|
+
- `src/device.js`: `normalizeDevice(def)` validates a plain definition and returns the
|
|
26
|
+
normalised `model` (species, materials, regions, interfaces, contacts, ports, terminals, the
|
|
27
|
+
grid from `src/grid.js`). Every later stage reads the model and never the definition.
|
|
28
|
+
- `src/index.js`: `Device`, the public object. It holds the model, builds a `Solver` on first
|
|
29
|
+
use, and turns solver states into `Solution`s (`src/solution.js`).
|
|
30
|
+
- `src/solver.js`: `Solver`, the numerical core (below).
|
|
31
|
+
- The kit (`src/kit.js` and what it re-exports: stack, data, sources, channels, checks,
|
|
32
|
+
describe, traces, live, recorder, particles, polarization, junction, devices) works on
|
|
33
|
+
definitions and solutions only, never on a solver's internals. `src/plot.js` draws
|
|
34
|
+
solutions.
|
|
35
|
+
|
|
36
|
+
## The solver's state
|
|
37
|
+
|
|
38
|
+
- **Blocks.** One per grid node and one per face between regions: node g is block
|
|
39
|
+
`blockOfNode[g]`, face f's flux block is `blockOfFace[f]`, in order along x.
|
|
40
|
+
- **Slots.** Each block has `M = 1 + n + extra` slots: slot 0 is φ̂ (at a face its
|
|
41
|
+
displacement D, at a metal's node its carriers' segment flux J), slots `1 … n` the species'
|
|
42
|
+
η = μ̄/RT, then a face's reaction rates and gates' fractions, or at an electrode port's nodes
|
|
43
|
+
its surface's coverages (as η) and a membrane port's gates. Row and unknown `o = b·M + slot`.
|
|
44
|
+
- **Compact indices.** Only active slots enter the linear system: `loc[o]` is a slot's place in
|
|
45
|
+
its block (−1: inactive), `rix[o]` its index in compact vectors, with `rix = N` (the sink) for
|
|
46
|
+
inactive ones. `res`, `delta` and the Jacobian (`sys`, a `BlockTridiagonal`) are compact.
|
|
47
|
+
- **Unknowns** are `u + uLo`, a double-double, so that differences of η keep their digits; the
|
|
48
|
+
Jacobian and solves are plain doubles. Concentrations `c[g·n + i]` and coverages
|
|
49
|
+
`th[g·nSurf + q]` follow from `u` (`computeConcentrations`).
|
|
50
|
+
- **Terminals:** 0 the left contact, 1 the right, `2 + k` port k. A floating terminal (driven by
|
|
51
|
+
a current, or behind a resistance) has its voltage as an extra unknown and its circuit row
|
|
52
|
+
beside the block system, solved by bordering (`solveBordered` in `src/lawRows.js`: `termB`
|
|
53
|
+
its column, `termC` its row, `termDI` and `termDX` its own and other terminals' ∂/∂V).
|
|
54
|
+
- **Stretches:** each connected run of regions where a species is present
|
|
55
|
+
(`_findStretches`), the unit that conservation counts in.
|
|
56
|
+
|
|
57
|
+
## The solver's sections
|
|
58
|
+
|
|
59
|
+
By method, in file order:
|
|
60
|
+
|
|
61
|
+
- **Setup:** the constructor (layout, per-node data, φ-undefined regions, terminals),
|
|
62
|
+
`_activeSlots`, `_findStretches` (stretches, flat stretches), `_laws` and `_structure` (the
|
|
63
|
+
laws, from `src/laws.js`), `_directSteady`.
|
|
64
|
+
- **Applying the laws** is `src/lawRows.js` (`captureLaws`, `captureLocal`, `snapFlat`,
|
|
65
|
+
`applyLaws`, `solveBordered`), called from `assemble`, `newton`, `_refine` and the steady
|
|
66
|
+
solves; `_replaceRow` and `_j`, which it uses, stay in the solver.
|
|
67
|
+
- **Starting state:** `initFromComposition`, `_surfaceStart`.
|
|
68
|
+
- **Statistics and assembly:** `computeConcentrations`, `_nodeStatistics`, `assemble` and the
|
|
69
|
+
kernels it calls (nodes, segments, faces, bulk reactions, ports, contacts, charge rows),
|
|
70
|
+
`_assembleDifference` (with `src/difference.js`).
|
|
71
|
+
- **Newton:** `newton`, `_refine`, `_equilibrate`, `_kirchhoff`, `_noteConditioning`.
|
|
72
|
+
- **Time steps:** `advance`, `step`, `integrate` (BDF with error control, breakpoints from the
|
|
73
|
+
sources), `_predict`, `_errorNorm`, `_snapshot` / `_restore`.
|
|
74
|
+
- **Small signal:** `impedance` and the helpers after it.
|
|
75
|
+
- **Bookkeeping:** `segmentFluxes`, `faceOneWay`, `_assembleBookkeeping`, the contacts' intake.
|
|
76
|
+
- **Steady solves:** `solveSteady`, `_steadyFromHere`, the continuations (floating, generation,
|
|
77
|
+
a terminal's level), `_steadySteps`.
|
|
78
|
+
|
|
79
|
+
The rest of `src/solver.js` is to be split along these lines, each piece with its contract.
|
|
80
|
+
|
|
81
|
+
## Reviewing one module
|
|
82
|
+
|
|
83
|
+
A review of one module reads its contract, its code and its tests, and nothing else unless the
|
|
84
|
+
contract points there. Each claim cites `file:line` with the line quoted, so it can be checked
|
|
85
|
+
mechanically, and a finding comes with the device or script that shows it. A claim about code
|
|
86
|
+
not in view is a question, not a finding.
|
package/docs/numerics.md
CHANGED
|
@@ -730,18 +730,29 @@ the terms that cancel:
|
|
|
730
730
|
port or contact at no current that passes K⁺ keeps $`n_K + Q/F`$ (a contact's Q its
|
|
731
731
|
displacement $`D_\text{in}`$), and two floating electrodes sharing a closed Fe³⁺ keep the iron
|
|
732
732
|
with both their charges, the law in one's circuit row moving with the other's voltage too.
|
|
733
|
+
A charge is kept only in a circuit row, once: the first combination through it takes one,
|
|
734
|
+
and the rest have its multiple taken off (a capacitor passing K⁺ under K⁺ + L⁻ = KL keeps
|
|
735
|
+
$`n_K - n_L + D/F`$ there, and $`n_L + n_{KL}`$ in a balance row).
|
|
733
736
|
At any other current such a terminal only stores what comes in, which has no steady state,
|
|
734
737
|
and the solve says so at once.
|
|
735
738
|
- an **immobile** combination (trap states X⁰ and X⁻ under e⁻ + X⁰ = X⁻, with D = 0) keeps it
|
|
736
739
|
node by node, since nothing carries it anywhere, as do an electrode's surface species
|
|
737
740
|
(adsorbates that only turn into each other keep their total on every site), its row local,
|
|
738
741
|
one per node in its own block, a coverage counted as the sites it fills per volume,
|
|
739
|
-
$`\Gamma a \theta`$, beside the concentrations.
|
|
742
|
+
$`\Gamma a \theta`$, beside the concentrations. Each node's rows are chosen by elimination
|
|
743
|
+
over the slots there, as below. Immobile means D = 0 in one region with no terminal feeding
|
|
744
|
+
it: a species linked across a face is carried between the face's two nodes, and one a
|
|
745
|
+
port links is carried between the port's nodes, kept with the port's charge in its circuit
|
|
746
|
+
row. (The null space is taken with the immobile and
|
|
740
747
|
surface columns first, so that these come out apart from the combinations that move.) A combination of immobile
|
|
741
748
|
stretches with a moving one (the electrons the traps hold, $`e^- - X^0`$, in a closed film) is
|
|
742
749
|
an amount like any other, its immobile parts summed over their nodes;
|
|
743
750
|
- a **floating metal** keeps the charge on its faces, its sheets' displacement.
|
|
744
751
|
|
|
752
|
+
A combination that none of these rows can keep (through a stretch immobile in part only, a
|
|
753
|
+
face reaction joining two immobile stretches at a face, a charge with no circuit row left) is
|
|
754
|
+
set aside, and the steady solve then goes by time steps, which keep every amount themselves.
|
|
755
|
+
|
|
745
756
|
A closed stretch keeps the amount it was given (its `c0`, or what it held when `set()`
|
|
746
757
|
closed it); a combination behind a terminal at no current keeps what the state holds when the
|
|
747
758
|
solve begins. The amount's row is dense, so it's bordered (see [terminals](#terminals)): the
|
package/docs/reliability.md
CHANGED
|
@@ -146,14 +146,17 @@ $`J \cdot v`$.
|
|
|
146
146
|
A steady solve replaces one redundant equation per conserved combination with the combination's
|
|
147
147
|
amount (see [numerics](numerics.md#the-steady-system)). A combination left out, or weighed wrongly,
|
|
148
148
|
gives a state that converges and is steady but holds the wrong amounts, which only a transient
|
|
149
|
-
from the same start shows. `npm run lawsweep` builds
|
|
149
|
+
from the same start shows. `npm run lawsweep` builds 870 small devices in two families. The first, 810, combine the features at the
|
|
150
150
|
edges of that bookkeeping: a floating, resistive or held electrode, with or without a
|
|
151
151
|
capacitance, with adsorbates or an immobile species that adsorbs; a second floating electrode
|
|
152
152
|
or a membrane capacitor; a capacitive or ion-passing contact at no current; a face that blocks
|
|
153
153
|
an ion or makes an immobile species. Each is solved directly and run as a transient for 1e6 s,
|
|
154
154
|
and where the transient converged, finished and moves no further over 100× as long, the two
|
|
155
155
|
must agree on every species' amount, every surface's sites and every floating terminal's
|
|
156
|
-
voltage.
|
|
156
|
+
voltage. The second, 60, put an immobile S⁺ beside what carries it (a face that passes it, a port
|
|
157
|
+
at I = 0 that links it, an electrode it adsorbs on) with a capacitive contact passing K⁺ (at
|
|
158
|
+
I = 0 or behind a resistance) and K⁺'s complex with a closed L⁻, each where a cold review of
|
|
159
|
+
`src/laws.js` found a law kept wrongly.
|
|
157
160
|
|
|
158
161
|
Every reaction starts at its equilibrium. On 1.2.1, 675 devices are built (135 combinations
|
|
159
162
|
repeat others); 579 agree, and 83 transients fail to converge. In the other 13 the transient
|
|
@@ -161,8 +164,16 @@ is the one that's off: it loses a conserved K⁺ total in proportion to its tole
|
|
|
161
164
|
at tol 1e-9) when a capacitive contact, a membrane capacitor and a held electrode share the
|
|
162
165
|
device, where the steady solve keeps it to round-off. The sweep found its first law the day it
|
|
163
166
|
was written (an immobile species adsorbing on an electrode over part of its region); Gemini 4
|
|
164
|
-
Argon's review found nine more by hand (one through a device it suggested),
|
|
165
|
-
`
|
|
167
|
+
Argon's review found nine more by hand (one through a device it suggested), and a cold review
|
|
168
|
+
of `src/laws.js` against its contract alone five more (an immobile species linked across a face,
|
|
169
|
+
or by a port; two node-by-node laws sharing a row; a capacitive contact's charge in a second law,
|
|
170
|
+
and in a time step's reading of its current), none of them in the sweep's devices. Each is now in
|
|
171
|
+
`test/laws.test.js`. On the code before those fixes the second family has 14 devices that
|
|
172
|
+
disagree (a port's voltage at 0 for −1.42 V, θ 0.05 for 0.015, a contact 24 mV off); after, none,
|
|
173
|
+
26 agree and 34 transients fail (see the roadmap). The first family's laws come out unchanged by
|
|
174
|
+
the fixes, device by device, so its results above stand. On 1.2.2 the whole sweep, 870 devices, gives the
|
|
175
|
+
same: 605 agree, 117 transients fail, and 13 differ, each a capacitive contact beside a membrane
|
|
176
|
+
capacitor where the transient loses K⁺.
|
|
166
177
|
|
|
167
178
|
## Hard cases
|
|
168
179
|
|
package/llms.txt
CHANGED
|
@@ -30,9 +30,9 @@ materials, insertion hosts), data.md and reliability.md (what's validated, and t
|
|
|
30
30
|
From a CDN, pin the version you tested against:
|
|
31
31
|
|
|
32
32
|
```js nocheck
|
|
33
|
-
import { Device, units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
34
|
-
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
35
|
-
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
33
|
+
import { Device, units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/index.js';
|
|
34
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/kit.js';
|
|
35
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/plot.js';
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
From npm: `driftlet`, `driftlet/kit`, `driftlet/plot`. Every export is listed at the end.
|
|
@@ -219,9 +219,9 @@ a CDN needs a long virtual-time budget, 30 s.)
|
|
|
219
219
|
<span id="readout"></span>
|
|
220
220
|
<div id="figure"></div>
|
|
221
221
|
<script type="module">
|
|
222
|
-
import { units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
223
|
-
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
224
|
-
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.
|
|
222
|
+
import { units } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/index.js';
|
|
223
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/kit.js';
|
|
224
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.2.2/src/plot.js';
|
|
225
225
|
|
|
226
226
|
const def = build({
|
|
227
227
|
T: 300,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driftlet",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Dependency-free 1D drift–diffusion–reaction (Poisson–Nernst–Planck) solver for semiconductors, electrochemistry and nerves, fast enough for live web demos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|