driftlet 1.2.4 → 1.3.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 +1 -1
- package/docs/conventions.md +8 -3
- package/docs/device.md +38 -9
- package/docs/internals.md +33 -14
- package/docs/numerics.md +65 -15
- package/docs/reliability.md +25 -16
- package/docs/statistics.md +13 -0
- package/llms.txt +11 -8
- package/package.json +1 -1
- package/src/assemble.js +206 -0
- package/src/bernoulli.js +32 -0
- package/src/boundaries.js +795 -0
- package/src/describe.js +2 -0
- package/src/device.js +32 -6
- package/src/impedance.js +857 -0
- package/src/index.js +25 -1
- package/src/newton.js +605 -0
- package/src/regions.js +1009 -0
- package/src/solution.js +24 -2
- package/src/solver.js +178 -3643
- package/src/start.js +508 -0
- package/src/steady.js +817 -0
- package/src/timeSteps.js +17 -2
- package/src/types.js +3 -0
- package/types/assemble.d.ts +18 -0
- package/types/bernoulli.d.ts +9 -0
- package/types/boundaries.d.ts +24 -0
- package/types/impedance.d.ts +53 -0
- package/types/newton.d.ts +7 -0
- package/types/regions.d.ts +61 -0
- package/types/solver.d.ts +14 -220
- package/types/start.d.ts +14 -0
- package/types/steady.d.ts +81 -0
- package/types/types.d.ts +6 -0
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.3.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
|
|
package/docs/conventions.md
CHANGED
|
@@ -94,13 +94,18 @@ Two optional transport terms describe a moving fluid:
|
|
|
94
94
|
|
|
95
95
|
- **Advection** by an imposed velocity $`v`$ adds $`v \cdot c_i`$ to every mobile species' flux.
|
|
96
96
|
- **Eddy mixing** adds $`-(D_{\mathrm{mix}}/RT) P \nabla\bar\mu`$, with
|
|
97
|
-
$`P =
|
|
97
|
+
$`P = K - K z z^\mathsf{T} K / (z^\mathsf{T} K z)`$ and $`K = \partial c/\partial\zeta`$ (the
|
|
98
|
+
statistics' $`K`$; $`\mathrm{diag}(c)`$ when dilute).
|
|
98
99
|
- It's an Onsager term: symmetric, positive semi-definite, and zero at equilibrium (flat
|
|
99
100
|
$`\bar\mu`$), so double layers at rest are untouched.
|
|
100
101
|
- The projection removes any current ($`z^\mathsf{T} P = 0`$). Eddies move neutral fluid, so
|
|
101
102
|
they mix salt and stir concentration gradients but conduct nothing.
|
|
102
|
-
-
|
|
103
|
-
|
|
103
|
+
- Since $`K\nabla\zeta = \nabla c`$, it's $`-D_{\mathrm{mix}} \nabla c`$ for every species in a
|
|
104
|
+
neutral solution, under any statistics: eddies carry parcels of fluid, so they mix
|
|
105
|
+
concentration, not activity. (Molecular diffusion differs on purpose: a molecule's own random
|
|
106
|
+
walk is driven by the thermodynamic force, so its mobility is $`D c`$, above.) For a neutral
|
|
107
|
+
dilute species it's $`-D_{\mathrm{mix}} \nabla c`$ anywhere. For a binary salt it adds
|
|
108
|
+
$`D_{\mathrm{mix}}`$ to the salt's ambipolar diffusivity and leaves diffusion potentials
|
|
104
109
|
unchanged.
|
|
105
110
|
|
|
106
111
|
## Materials, regions, interfaces
|
package/docs/device.md
CHANGED
|
@@ -112,9 +112,11 @@ regions: [
|
|
|
112
112
|
material. In a strictly neutral region, $`\sum z \cdot c_0`$ must be zero.
|
|
113
113
|
Where two strictly neutral regions meet at a face that holds ions in equilibrium and their
|
|
114
114
|
`c0` leaves it out of equilibrium (a different fixed charge or immobile ion on each side), the
|
|
115
|
-
two nodes at the face start in it, as the face reaches it at $`t = 0^+`$: the ions
|
|
116
|
-
shared between them so their $`\bar\mu`$ is equal across it and each side neutral,
|
|
117
|
-
one's amount over the two nodes kept, everything else as `c0` has it.
|
|
115
|
+
two nodes at the face start a transient in it, as the face reaches it at $`t = 0^+`$: the ions
|
|
116
|
+
it passes shared between them so their $`\bar\mu`$ is equal across it and each side neutral,
|
|
117
|
+
each one's amount over the two nodes kept, everything else as `c0` has it. (The first time
|
|
118
|
+
step from a cold start does this; a steady solve starts from `c0` as given, which it reaches
|
|
119
|
+
better from.)
|
|
118
120
|
An entry can instead be a profile, `{ x: [...], values: [...] }`, against the device's x (m, not
|
|
119
121
|
the region's own), piecewise linear between its points and constant beyond its ends, so a
|
|
120
122
|
transient can start from a state it didn't simulate reaching: a packet already injected, a
|
|
@@ -541,6 +543,12 @@ levels are that material's, and the end node takes them through a Donnan step.
|
|
|
541
543
|
means. For example, a Schottky barrier $`\phi_B`$ on n-type material with
|
|
542
544
|
$`\mu^\circ_{\mathrm{e}^-} = 0`$ is `zeroCharge: φ_B`.
|
|
543
545
|
|
|
546
|
+
An ideally blocking electrode (inert: no reactions, nothing crossing) is a contact with no
|
|
547
|
+
terminal species and no links, `{ V, phi: 'pinned', zeroCharge }`: the solution's diffuse layer
|
|
548
|
+
is its whole double layer (the validation suite's Macdonald impedance is this cell), and a Stern layer in
|
|
549
|
+
series is `phi: { type: 'capacitive', C }` in place of `'pinned'`. (A metal layer behind a
|
|
550
|
+
capacitive face of large C does the same at more cost, and its C is warned of as a unit slip.)
|
|
551
|
+
|
|
544
552
|
A Schottky contact is that pinned barrier with its carriers passing at their emission velocities:
|
|
545
553
|
`{ V, terminal: 'e-', species: { 'e-': { type: 'velocity', v }, 'h+': { type: 'velocity', v: vp, offset: 0 } }, phi: 'pinned', zeroCharge: φ_B }`,
|
|
546
554
|
with Bethe's thermionic-emission velocity $`v = A^* T^2/(F N_c)`$ ($`N_c`$ in mol/m³, the data
|
|
@@ -589,6 +597,11 @@ ports: [{
|
|
|
589
597
|
}]
|
|
590
598
|
```
|
|
591
599
|
|
|
600
|
+
The window holds exactly `to − from` of exchange, electrode and capacitance however the grid
|
|
601
|
+
falls: each node whose box reaches into it takes the part of its box inside. A held level
|
|
602
|
+
(`'equilibrium'`) holds the nodes inside the window, so it needs one there. A window of no width
|
|
603
|
+
is the node at that point, its whole box.
|
|
604
|
+
|
|
592
605
|
On a [conductor region](#materials), a port is a wire to the whole conductor: it takes no window,
|
|
593
606
|
only the conductor's carrier, and a conductance link's `G` is per area (S/m², a resistance
|
|
594
607
|
$`R \cdot A`$ to the port's voltage as $`G = 1/(R \cdot A)`$). That's how a floating electrode is tied
|
|
@@ -748,8 +761,12 @@ as for reactions, and the window holds the opposite. Each node's charge balance
|
|
|
748
761
|
Gauss's law averaged across the section, $`-\partial_x(\varepsilon\,\partial_x\phi) = \rho + a\sigma`$,
|
|
749
762
|
and at $`\varepsilon = 0`$ neutrality $`\rho + a\sigma = 0`$, the mobile species rearranging to supply
|
|
750
763
|
it (counter-ions in, co-ions out). The port passes the charging current, $`d(\int a\sigma\,dV)/dt`$,
|
|
751
|
-
none in a steady state.
|
|
752
|
-
|
|
764
|
+
none in a steady state. `zeroCharge` is in the frame a contact's is (see the capacitive contact
|
|
765
|
+
above): $`V - \phi`$ at zero charge, with $`\phi`$ the device's own, which a bath or contact sets
|
|
766
|
+
(beside a bath referenced to Cl⁻, with the library's μ°, it sits near −1.4 V). Omitted, it's 0, so
|
|
767
|
+
a port held at V = 0 there starts charged by 1.4 V; to start it uncharged, give `zeroCharge` = V −
|
|
768
|
+
φ at its window (`sol.phi` after a solve). Capacitances on overlapping windows add (a channel's
|
|
769
|
+
top and bottom gates). A capacitance alone, driven by a current, only charges: it starts uncharged, `advance()`
|
|
753
770
|
follows it, and `solve()` refuses (there's no steady state, as for a gate contact driven by one).
|
|
754
771
|
|
|
755
772
|
```js nocheck
|
|
@@ -999,21 +1016,33 @@ const now = dev.solution(); // snapshot of the current state
|
|
|
999
1016
|
as a transient slows, so give `dtMax` to resolve a signal in time.
|
|
1000
1017
|
- `impedance(frequencies, { terminal, profiles })` solves the steady state, then linearises about
|
|
1001
1018
|
it: $`Z(f) = \delta V/\delta I`$ in Ω·m² at one terminal (`'right'` by default), with I into the
|
|
1002
|
-
device
|
|
1019
|
+
device, its input impedance whether it's held or driven by a current (for a driven one, about
|
|
1020
|
+
the state its drive holds: solve() it held, then set its I, solve(), then impedance()); the other terminals
|
|
1003
1021
|
keep their drives (held ones at AC ground, driven ones open), so at least one other must be held
|
|
1004
1022
|
for the current to return (with every other one driven, the impedance is infinite, and an error
|
|
1005
1023
|
says to measure at a driven one). A terminal behind a resistance can't be the one measured: the
|
|
1006
1024
|
resistance belongs to the external circuit. The grid must resolve how far the signal reaches,
|
|
1007
1025
|
and nothing warns of it: in a porous electrode, $`\sqrt{\kappa/(\omega C a)}`$ (cells of 2 µm
|
|
1008
1026
|
against a 3.4 µm depth put Z 7% off, falling as the cells do; at an electrode, the diffusion
|
|
1009
|
-
length $`\sqrt{2D/\omega}`$ at the highest frequency
|
|
1010
|
-
|
|
1027
|
+
length $`\sqrt{2D/\omega}`$ at the highest frequency, and coarser than that, a port's window
|
|
1028
|
+
edges falling mid-cell moved a faradaic C 17%); and a double layer's capacitance wants cells
|
|
1029
|
+
well inside its Debye length (0.1 M NaCl: hmin λ_D/2.4 read C_dl 2.1% low, 0.1 nm 0.5%, 0.05 nm
|
|
1030
|
+
to 5e-4; the warning about unresolved double layers starts only at λ_D). `relError` is the
|
|
1031
|
+
round-off's, not the grid's. The result is `{ f, Z: { re, im },
|
|
1032
|
+
relError, warnings }`: `relError[k]`, each Z's estimated error relative to $`|Z|`$ (from
|
|
1033
|
+
round-off in the current and the solves' residuals, and cautious: it can exceed the true error
|
|
1034
|
+
many times), and the warnings, the steady state's and one where a Z isn't resolved (an error
|
|
1035
|
+
over 1%: far below a capacitive device's relaxation, almost no current flows, and what does is
|
|
1036
|
+
lost to round-off) or Re Z is under a tenth of its error, relError·|Z|. With `profiles: true`, `profiles[k]` holds
|
|
1011
1037
|
frequency k's complex profiles, `{ phi, mu: {name}, c: {name} }`, each `{ re, im }` at the
|
|
1012
1038
|
nodes (`NaN` where a quantity doesn't exist), per unit excitation: a volt, or an A/m² where the
|
|
1013
1039
|
terminal is driven by a current.
|
|
1014
1040
|
- `set(patch)` merges plain objects deeply (arrays are replaced), except that a contact given `V`
|
|
1015
1041
|
or `I` drops the other (and given `I`, any series `R`): `{ contacts: { right: { I: 0 } } }`
|
|
1016
|
-
switches it to open circuit, whether it was held or behind a resistance.
|
|
1042
|
+
switches it to open circuit, whether it was held or behind a resistance. Given `V` alone, a terminal
|
|
1043
|
+
behind a resistance stays behind it: `{ V, R: undefined }` takes the resistance off (`R: 0` and
|
|
1044
|
+
`null` are refused, and `undefined` doesn't survive JSON, so a worker's patch says it this way in
|
|
1045
|
+
JavaScript). A patch that changes only the
|
|
1017
1046
|
terminals' drives (`V`, `I`, `R`) updates them in place, cheaply, keeping everything else.
|
|
1018
1047
|
Otherwise the device is rebuilt: the current state carries over while the species, regions
|
|
1019
1048
|
and terminals are unchanged (on a new grid, interpolated onto its nodes, so a floating gate
|
package/docs/internals.md
CHANGED
|
@@ -16,10 +16,17 @@ them is wrong: fix both, with a test, in the same commit.
|
|
|
16
16
|
| `src/laws.js` | yes: the steady system's conservation laws |
|
|
17
17
|
| `src/lawRows.js` | yes: the laws written into the Newton system, and the bordered solve |
|
|
18
18
|
| `src/timeSteps.js` | yes: one step (backward Euler, BDF2), advance's halving, integrate's error control |
|
|
19
|
+
| `src/start.js` | yes: the cold start, from each region's c0 and the contacts' and ports' levels |
|
|
20
|
+
| `src/newton.js` | yes: Newton's iteration, its scaled rows, GMRES-refined solves and the islands' round |
|
|
21
|
+
| `src/assemble.js` | yes: the equations assembled, the conventions of every row, and the order of the terms |
|
|
22
|
+
| `src/regions.js` | yes: statistics, storage, fluxes along segments, bulk reactions, the neutral nodes' charge rows |
|
|
23
|
+
| `src/boundaries.js` | yes: faces (φ laws, links, reactions, gates), ports, contacts, circuit rows |
|
|
24
|
+
| `src/steady.js` | yes: steady solves, their stages and continuations, the pseudo-transient ramp |
|
|
25
|
+
| `src/impedance.js` | yes: the small-signal impedance, its readouts and profiles |
|
|
19
26
|
| `src/difference.js` | header: the dilute kernels in difference form |
|
|
20
27
|
| `src/blockTridiagonal.js` | header: the block Thomas solver |
|
|
21
28
|
| `src/statistics.js` | header: each model's `evaluate` / `invert` |
|
|
22
|
-
| `src/solver.js` |
|
|
29
|
+
| `src/solver.js` | no: setup, bookkeeping and drivers, described below |
|
|
23
30
|
|
|
24
31
|
## From a definition to a solution
|
|
25
32
|
|
|
@@ -46,7 +53,9 @@ them is wrong: fix both, with a test, in the same commit.
|
|
|
46
53
|
its block (−1: inactive), `rix[o]` its index in compact vectors, with `rix = N` (the sink) for
|
|
47
54
|
inactive ones. `res`, `delta` and the Jacobian (`sys`, a `BlockTridiagonal`) are compact.
|
|
48
55
|
- **Unknowns** are `u + uLo`, a double-double, so that differences of η keep their digits; the
|
|
49
|
-
Jacobian and solves are plain doubles.
|
|
56
|
+
Jacobian and solves are plain doubles. (η is read as `u + uLo` wherever a difference of levels
|
|
57
|
+
is taken; φ̂ in the statistics and the displacement, a face's own slots and the gates as `u`
|
|
58
|
+
alone.) Concentrations `c[g·n + i]` and coverages
|
|
50
59
|
`th[g·nSurf + q]` follow from `u` (`computeConcentrations`).
|
|
51
60
|
- **Terminals:** 0 the left contact, 1 the right, `2 + k` port k. A floating terminal (driven by
|
|
52
61
|
a current, or behind a resistance) has its voltage as an extra unknown and its circuit row
|
|
@@ -61,24 +70,34 @@ By method, in file order:
|
|
|
61
70
|
|
|
62
71
|
- **Setup:** the constructor (layout, per-node data, φ-undefined regions, terminals),
|
|
63
72
|
`_activeSlots`, `_findStretches` (stretches, flat stretches), `_laws` and `_structure` (the
|
|
64
|
-
laws, from `src/laws.js`)
|
|
73
|
+
laws, from `src/laws.js`).
|
|
65
74
|
- **Applying the laws** is `src/lawRows.js` (`captureLaws`, `captureLocal`, `snapFlat`,
|
|
66
|
-
`applyLaws`, `solveBordered`), called from `assemble`,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- **
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
`applyLaws`, `solveBordered`), called from `assemble`, Newton and the steady solves.
|
|
76
|
+
- **Starting state** is `src/start.js` (`initFromComposition`, which the Solver's own calls);
|
|
77
|
+
`_surfaceStart`, which the API uses too, stays in the solver.
|
|
78
|
+
- **Assembly** is `src/assemble.js` (`assemble`, `_assembleDifference` with `src/difference.js`,
|
|
79
|
+
`_assembleBookkeeping`, and `_j` and `_replaceRow`, which write the Jacobian), with its kernels
|
|
80
|
+
in `src/regions.js` (`computeConcentrations` and the statistics, nodes, segments, bulk
|
|
81
|
+
reactions and their rates, `_chargeRows` and `_untransform`) and `src/boundaries.js` (faces,
|
|
82
|
+
`_terminals`: ports, contacts, circuit rows). Their methods are the Solver's own (installed on
|
|
83
|
+
its prototype), since every term reads and writes its arrays.
|
|
84
|
+
- **Newton** is `src/newton.js` (the iteration, its row scaling, the GMRES-refined solves, the
|
|
85
|
+
islands' second round), which the Solver's own `newton` calls; `_factor` stays in the solver.
|
|
73
86
|
- **Time steps** are `src/timeSteps.js` (`step`, `advance`, `integrate`, the predictor and the
|
|
74
87
|
error norm), which the Solver's own `step`, `advance` and `integrate` call; `_snapshot` /
|
|
75
88
|
`_restore`, `_warmFrom` and `_probes`, which the API uses too, stay in the solver.
|
|
76
|
-
- **Small signal
|
|
89
|
+
- **Small signal** is `src/impedance.js` (`impedance` and its helpers: the current cuts, the
|
|
90
|
+
regions' uniform shift, the profiles; the complex GMRES and small dense solve), methods of the
|
|
91
|
+
Solver like the assembly's.
|
|
77
92
|
- **Bookkeeping:** `segmentFluxes`, `faceOneWay`, `_assembleBookkeeping`, the contacts' intake.
|
|
78
|
-
- **Steady solves
|
|
79
|
-
a
|
|
93
|
+
- **Steady solves** are `src/steady.js` (`solveSteady`, `_steadyFromHere`, the continuations:
|
|
94
|
+
floating, generation, a contact's voltage; `_steadySteps`, the pseudo-transient ramp, and
|
|
95
|
+
`_renormalizeSpectators`; `_directSteady`, `slowestTime`, `fastestTime`, `stuckReactions`),
|
|
96
|
+
methods of the Solver like the assembly's.
|
|
80
97
|
|
|
81
|
-
|
|
98
|
+
What stays in `src/solver.js` is the setup, the bookkeeping, the probes and snapshots, and the thin
|
|
99
|
+
drivers that call the modules: about a thousand lines, with no contract of its own beyond this
|
|
100
|
+
section.
|
|
82
101
|
|
|
83
102
|
## Reviewing one module
|
|
84
103
|
|
package/docs/numerics.md
CHANGED
|
@@ -171,13 +171,25 @@ potential and the quasi-Fermi difference are both shifted by the cell Péclet nu
|
|
|
171
171
|
$`\mathrm{Pe} = v h/D`$, $`\Delta \to \Delta - \mathrm{Pe}`$, $`\Delta\eta \to \Delta\eta - \mathrm{Pe}`$.
|
|
172
172
|
That's the exact constant-flux solution of $`N = v c - D(c' + c \psi')`$ across the cell, so pure
|
|
173
173
|
convection–diffusion is exact on any grid, upwinding itself automatically at large $`\mathrm{Pe}`$.
|
|
174
|
+
Past a drift of 300 thermal units across a cell (flow toward −x past a cell Péclet number of 300),
|
|
175
|
+
the flux is taken from its downstream end, $`g B(-\Delta) c_R \operatorname{expm1}(-\Delta\eta)`$, the
|
|
176
|
+
same flux, since there $`B(\Delta)`$ underflows and $`\operatorname{expm1}(\Delta\eta)`$ overflows.
|
|
174
177
|
|
|
175
178
|
**Eddy mixing** is discretised on each segment as
|
|
176
|
-
$`N_i = -(D_{\mathrm{mix}}/h) \sum_j \bar P_{ij} \Delta\eta_j`$, where $`\bar P`$ is built from
|
|
177
|
-
|
|
178
|
-
species' mixing flux exactly $`-D_{\mathrm{mix}} \Delta c/h`$.
|
|
179
|
+
$`N_i = -(D_{\mathrm{mix}}/h) \sum_j \bar P_{ij} \Delta\eta_j`$, where $`\bar P`$ is built from a
|
|
180
|
+
segment mobility $`\bar K`$. Between ideal ends $`\bar K`$ is the logarithmic mean of each
|
|
181
|
+
concentration, which makes a neutral species' mixing flux exactly $`-D_{\mathrm{mix}} \Delta c/h`$.
|
|
182
|
+
Between crowded ends it's $`\bar K = \tfrac12(R_L K_L R_L + R_R K_R R_R)`$ with
|
|
183
|
+
$`R = \mathrm{diag}(\sqrt{\bar c/c})`$ at each end: each end's $`K`$ rescaled to the logarithmic
|
|
184
|
+
means and averaged. That's the dilute form where statistics are ideal, positive definite, and
|
|
185
|
+
smooth in the state, and in a neutral solution it gives $`-D_{\mathrm{mix}} \Delta c/h`$ to second
|
|
186
|
+
order in the cells. (A secant $`\bar K`$, exact in $`\Delta c`$, can be built from the ends' $`c`$ and
|
|
187
|
+
$`\zeta`$ alone, but not smoothly: where a solution is uniform its correction depends on the
|
|
188
|
+
direction of $`\Delta\zeta`$, a kink right where a salt carries current.) The flux is exactly zero at
|
|
179
189
|
equilibrium, and exactly current-free ($`z^\mathsf{T}\bar P = 0`$ for any $`\bar P`$ built this way). Its
|
|
180
|
-
full Jacobian, including the dependence of $`\bar P`$ on both ends, keeps Newton quadratic.
|
|
190
|
+
full Jacobian, including the dependence of $`\bar P`$ on both ends, keeps Newton quadratic. Through
|
|
191
|
+
$`K_L`$ and $`K_R`$ that needs $`\partial K/\partial\zeta`$, which the statistics models don't give, so
|
|
192
|
+
it's taken by central differences of the model (a step of 1e-5, accurate to about 1e-10).
|
|
181
193
|
|
|
182
194
|
With non-ideal statistics, $`\ln c = \zeta - \mathrm{ex}`$, where the excess
|
|
183
195
|
$`\mathrm{ex} = \zeta - \ln(c/c_{\mathrm{ref}})`$ is zero for ideal statistics. The excess enters
|
|
@@ -508,8 +520,9 @@ which cost four assemblies per product.
|
|
|
508
520
|
GMRES on $`J\delta = r`$, the factorised system as the preconditioner and $`J \cdot v`$ exact, each
|
|
509
521
|
flux on its $`\eta`$ difference (see [exact J·v](#exact-jv)), as the impedance does. A lost mode
|
|
510
522
|
takes GMRES a couple of iterations. The refined update is kept where GMRES converged and it
|
|
511
|
-
moves no level by a thermal unit or more; otherwise refining stops for the rest of the solve
|
|
512
|
-
|
|
523
|
+
moves no level by a thermal unit or more; otherwise refining stops for the rest of the solve
|
|
524
|
+
(on a device marked as below, for two iterations, three times at most).
|
|
525
|
+
A MOS capacitor's gate step now reaches the low-frequency charge in ~30 steps (it took 80,000,
|
|
513
526
|
and ended 1.5% off). Once refined, a solve stays refined: a GaAs junction without
|
|
514
527
|
recombination, whose minority carriers reach it only from the contacts, passes ~1.5e-7 A/m²,
|
|
515
528
|
which plain solves past the first refinement left to round-off (cold and warm solves 1e-3
|
|
@@ -541,9 +554,32 @@ which cost four assemblies per product.
|
|
|
541
554
|
conductances, loses about $`\log_{10}(g/G)`$ and fails near 15. It's not reported for solves that
|
|
542
555
|
converge, since digits can be lost harmlessly too (a saturated species whose $`\eta`$ nothing
|
|
543
556
|
depends on).
|
|
544
|
-
- The last iteration usually only confirms convergence (updates go like
|
|
545
|
-
|
|
546
|
-
|
|
557
|
+
- The last iteration usually only confirms convergence (updates go like 1e-2, 1e-9, 1e-15). A
|
|
558
|
+
time step stops one iteration early where the convergence rate says the next update would be
|
|
559
|
+
under the tolerance: with $`\theta`$ the last update over the one before (under 0.1, the update
|
|
560
|
+
under 1e-6), $`\theta/(1-\theta)`$ of the update (Hairer and Wanner's estimate of the error
|
|
561
|
+
left) under 1e-10. That saves a third of a typical step's work. Steady solves don't, since
|
|
562
|
+
their acceptance is checked against the residual anyway.
|
|
563
|
+
- **A mode the plain solves never see, in time steps.** Neither a plain update nor the residual
|
|
564
|
+
shows a lost mode. Newton accepted a MOS capacitor's gate step at updates of 1e-10 while the
|
|
565
|
+
inversion layer's level sat 2.4e-4 thermal units off, the residual 7e-15 there and at the
|
|
566
|
+
solution alike, and the gate's charge ended 3.4e-5 off the steady one (a cold review of
|
|
567
|
+
`src/newton.js` found it: a tight re-solve from each accepted state was the instrument). So a
|
|
568
|
+
refinement that corrects a time step's nearly converged plain update (under 1e-4) by more than
|
|
569
|
+
its own size and 1e-6, by a correction the next update keeps, marks the device: from then on
|
|
570
|
+
its time steps are refined throughout, converge only on a refined update, and don't stop
|
|
571
|
+
early on the rate. The gate step then ends 4e-10 off, in fewer factorisations (121 against
|
|
572
|
+
222; 2 steps rejected, not 5). A correction the next update undoes is $`J \cdot v`$'s own
|
|
573
|
+
round-off in a row of huge entries (one ulp's worth, $`2^{-13}`$ or $`2^{-19}`$ exactly, on
|
|
574
|
+
healthy devices), and marks nothing. A lost mode can also never stall Newton at all: a floating
|
|
575
|
+
gate at rest in inversion converged quadratically at every step while the extrapolated guess's
|
|
576
|
+
error in the layer's level grew, 8.5e-5 V by 1e5 s, and by 1e7 s took 63,000 steps. So a time
|
|
577
|
+
step's 1st, 2nd, 4th, 8th… and every 32nd convergence on a device not yet marked is probed
|
|
578
|
+
with one refined update at the accepted state; one over 1e-8 thermal units (about a dozen over
|
|
579
|
+
the suite, which the next update undid on healthy devices) sends Newton on, refining, and the
|
|
580
|
+
next update decides as above. The gate then stays within 1e-14 V, in 7 steps to 1e7 s. The
|
|
581
|
+
probes and the refining cost the suite 2.5% more assemblies and 1.3% more factorisations;
|
|
582
|
+
strictly neutral transients, marked, came out up to 1.7e-5 off and now 1.9e-6.
|
|
547
583
|
- Tried and rejected for large jumps (the retry at 3 thermal units per iteration takes 30–50
|
|
548
584
|
iterations for a 1.4 V jump on a pn diode): per-component logarithmic damping, as in
|
|
549
585
|
SPICE junction limiting, and an adaptive limit that grows while updates shrink. Both
|
|
@@ -625,10 +661,16 @@ About a steady state, a small sinusoidal excitation at one terminal gives, to fi
|
|
|
625
661
|
$`v \cdot K`$, displacement), and likewise $`C`$ and $`C'`$ for the terminal currents. The
|
|
626
662
|
time-derivative parts are read off two assemblies, $`J(dt) = J + M/dt`$, with a tiny $`dt`$ so that
|
|
627
663
|
the subtraction loses nothing.
|
|
628
|
-
- **At the measured terminal $`T`$,**
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
664
|
+
- **At the measured terminal $`T`$,** its voltage is perturbed ($`\delta V_T = 1`$) and
|
|
665
|
+
$`\delta I_T`$ read, held or driven by a current alike: $`Z = \delta V_T/\delta I_T`$ is its input
|
|
666
|
+
impedance either way, with $`I`$ into the device. (Solving a driven one's $`\delta V_T`$ for a unit
|
|
667
|
+
current instead read its current through its own circuit row, at the contact alone: a reverse
|
|
668
|
+
diode's 4.3% off $`dV/dI`$.) The other terminals keep their drives: held ones at AC ground,
|
|
669
|
+
driven ones open. A floating one's circuit row reads its current where that reading's error is
|
|
670
|
+
least: at its contact, or at any cut between it and a port's window, the same current (through a
|
|
671
|
+
metal it's the round-off of $`G\,\Delta\eta`$); and where every terminal but $`T`$ floats, the
|
|
672
|
+
response to $`T`$ with them held (nearly every level shifted alike, its current a difference of
|
|
673
|
+
large terms) is read through the identity that shifting every terminal alike passes nothing.
|
|
632
674
|
|
|
633
675
|
Each frequency costs one complex block-Thomas factorisation (rows scaled by their largest
|
|
634
676
|
entry), plus one back-substitution per floating terminal for the bordering. Conserved (blocked)
|
|
@@ -776,7 +818,11 @@ the terms that cancel:
|
|
|
776
818
|
next: there, backward-Euler steps at a huge $`dt`$ (10⁶ × the slowest diffusion time) keep
|
|
777
819
|
the storage term, growing ×10 (capped) while the state still moves, each spectator's level
|
|
778
820
|
first shifted uniformly to restore its amount exactly (round-off creeps through the vanishing
|
|
779
|
-
storage term), and one last step at the base huge $`dt`$, where pinning is tight.
|
|
821
|
+
storage term), and one last step at the base huge $`dt`$, where pinning is tight. The amounts
|
|
822
|
+
that only those steps keep are checked after each, and a step that moved one fails: where the
|
|
823
|
+
slowest species is very slow (D ~ 1e-22 m²/s), the storage term falls below round-off against a
|
|
824
|
+
fast species' conduction, and a closed Y + Z, Y still in part of its stretch, had converged 1e8
|
|
825
|
+
times too large. The slowest diffusion time counts only materials some region is made of.
|
|
780
826
|
- **A flat level.** A species that no reaction or port touches, reached by one contact at most,
|
|
781
827
|
carries no flux in a steady state: every sum of its balance rows from its closed end is the
|
|
782
828
|
flux through one segment, zero. In an ideal region without flow or mixing that's a level flat
|
|
@@ -916,7 +962,11 @@ A solve that fails falls back stage by stage:
|
|
|
916
962
|
the direct solve. After a failed step it grows ×2, then ×1.5 more each success, back to ×10.
|
|
917
963
|
Once steps reach the slowest diffusion time, the direct solve is tried from there, once a
|
|
918
964
|
decade of $`dt`$ (the state kept if it fails): with immobile traps and ions of D = 1e-17 m²/s,
|
|
919
|
-
steps from 1e8 to 1e9 s kept failing on the way to the end.
|
|
965
|
+
steps from 1e8 to 1e9 s kept failing on the way to the end. The ramp reaches about 1e12 times
|
|
966
|
+
the slowest diffusion time, so a reaction slower than that, which alone settles a closed
|
|
967
|
+
combination (no row of the steady system holds a reaction at equilibrium node by node), isn't
|
|
968
|
+
waited out: the solve fails, or converges where it began, and its warnings name the reaction
|
|
969
|
+
either way. A transient gets there (Y = Z at $`k_f`$ = 1e-12 s⁻¹ in 1610 steps).
|
|
920
970
|
|
|
921
971
|
Each of these carries cases that nothing else does (switched off one at a time, each fails
|
|
922
972
|
tests or stress cases the others pass).
|
package/docs/reliability.md
CHANGED
|
@@ -76,9 +76,9 @@ recipes.
|
|
|
76
76
|
| 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 |
|
|
77
77
|
| Transients | RC charging of a gated island; water relaxation rate | 1%; 2e-3 |
|
|
78
78
|
| Time integration | BE first order, BDF2 second order; adaptive error control | ratios 2, 4 |
|
|
79
|
-
| Advection and eddy mixing | exact convection–diffusion profile; `D + D_mix`; junction EMF unchanged by mixing | 1e-12; 1e-12; 1e-5 |
|
|
79
|
+
| Advection and eddy mixing | exact convection–diffusion profile; `D + D_mix`; junction EMF unchanged by mixing, ideal and crowded; crowded mixing −D_mix Δc (a neutral species on a lattice, second order; ions, lattice and Debye–Hückel, on 20 cells) | 1e-12; 1e-12; 1e-5; ratios 4, 1e-4 |
|
|
80
80
|
| 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 |
|
|
81
|
-
| 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
|
+
| Internal ports | transmission line σV tanh(L/λ)/λ (O(h²)); a window's exchange and electrode exactly `to − from` on any grid; 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; 1e-9; 2e-3; 1e-6; 1e-3 |
|
|
82
82
|
| 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 |
|
|
83
83
|
| 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 |
|
|
84
84
|
| 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 |
|
|
@@ -97,7 +97,7 @@ recipes.
|
|
|
97
97
|
|
|
98
98
|
The validation suite shows driftlet is right where it converges. Whether it converges, on
|
|
99
99
|
devices nobody tuned it for, is what `npm run stress` tests. It builds random but plausible
|
|
100
|
-
devices in
|
|
100
|
+
devices in six families:
|
|
101
101
|
|
|
102
102
|
| Family | What's drawn |
|
|
103
103
|
|---|---|
|
|
@@ -105,6 +105,7 @@ devices in five families:
|
|
|
105
105
|
| MOS capacitors | Si, Ge or GaAs, doped 1e15–1e18 cm⁻³; oxide 1–20 nm; Au, Al or Pt gate at any flat-band offset; with or without generation |
|
|
106
106
|
| Electrolyte cells | one to three salts from the data library's ions, 0.1 mM–1 M, 1 µm–1 mm; double layers resolved or strictly neutral; bath, reversible or blocking electrodes |
|
|
107
107
|
| Electrodes | Ag, Cu or Zn deposition, or Fe³⁺/Fe²⁺ on platinum, with Butler–Volmer kinetics over four decades of k₀; with or without a supporting salt; against a second electrode or a bath |
|
|
108
|
+
| Reacting solutions | complexations (Na⁺ + A⁻ = NaA, B + A⁻ = BA⁻, …) in 2–3 regions whose faces block some species, so conserved combinations overlap; against a bath, another bath, closed or a K⁺ electrode; sometimes an electrode at no current reducing a closed M⁺ |
|
|
108
109
|
| Liquid junctions | two mixed solutions, 1 µM–1 M, sharp or graded, at open circuit |
|
|
109
110
|
|
|
110
111
|
Each device goes through what a user would do with it: a cold solve at no bias, and one at a
|
|
@@ -114,26 +115,30 @@ after a voltage step; and the impedance about equilibrium, from far below its sl
|
|
|
114
115
|
to 1 GHz. Every result is judged by `check()`, and by what must hold whatever the device:
|
|
115
116
|
levels flat at equilibrium with no current, warm and cold solves agreeing, the current driven
|
|
116
117
|
being the one passed, the impedance passive (to 1e-3 of |Z|) and, at low frequency, equal to
|
|
117
|
-
the steady dI/dV, a MOS capacitance never above its oxide's, and its gate charge with the back
|
|
118
|
+
the steady dI/dV where it says it's resolved (`relError` under 1%), a MOS capacitance never above its oxide's, and its gate charge with the back
|
|
118
119
|
contact moved what it is with the gate moved the other way. Each case is seeded from its family and index, so
|
|
119
120
|
any failure reruns alone (`npm run stress -- semi 79`).
|
|
120
121
|
|
|
121
|
-
At 500 devices per family (about
|
|
122
|
+
At 500 devices per family (about five minutes on a desktop), 16,379 of 16,381 scenarios pass:
|
|
122
123
|
|
|
123
|
-
| Family | Cold at 0 V | Cold at bias | Warm sweep | Open circuit, current or back contact | Transient | Impedance |
|
|
124
|
-
|
|
125
|
-
| Semiconductor stacks | 500/500 | 500/500 | 500/500 | 105/105 | 500/500 | 395/395 |
|
|
126
|
-
| MOS capacitors | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 |
|
|
127
|
-
| Electrolyte cells | 500/500 | 500/500 | 500/500 | | 500/500 | 381/381 |
|
|
128
|
-
| Electrodes | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 |
|
|
129
|
-
|
|
|
124
|
+
| Family | Cold at 0 V | Cold at bias | Species reversed | Warm sweep | Open circuit, current or back contact | Transient | Impedance |
|
|
125
|
+
|---|---|---|---|---|---|---|---|
|
|
126
|
+
| Semiconductor stacks | 500/500 | 500/500 | 500/500 | 499/500 | 105/105 | 500/500 | 395/395 |
|
|
127
|
+
| MOS capacitors | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 |
|
|
128
|
+
| Electrolyte cells | 500/500 | 500/500 | 500/500 | 500/500 | | 500/500 | 381/381 |
|
|
129
|
+
| Electrodes | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 | 500/500 |
|
|
130
|
+
| Reacting solutions | 500/500 | 500/500 | 500/500 | 500/500 | | 500/500 | |
|
|
131
|
+
| Liquid junctions | | | | | 500/500 | 499/500 | |
|
|
130
132
|
|
|
131
133
|
(Impedance only where the device has an equilibrium to linearise about; open circuit only for
|
|
132
134
|
lit cells.) The MOS back contact came last: before steady solves held a group of reacting
|
|
133
135
|
carriers flat where it reaches one contact alone, 43 of the 500 failed it, 5 of them silently
|
|
134
|
-
(a gate charge up to 2× off).
|
|
135
|
-
coarse for its double layers
|
|
136
|
-
|
|
136
|
+
(a gate charge up to 2× off). Of the two that fail, one is among the known limits below, a junction on a grid far too
|
|
137
|
+
coarse for its double layers; the other is a diode whose current, a small difference of large fluxes, a warm sweep
|
|
138
|
+
reaches to 1.8e-6 of what a cold solve does, against the 1e-6 the check asks: Newton's tolerance, not the physics
|
|
139
|
+
(its own current varies 2.8e-6 along the device, its balances converged).
|
|
140
|
+
`bench/stress.json` keeps the summary, and CI runs `npm run stress -- --check` against it: any
|
|
141
|
+
scenario failing that passes there fails the build.
|
|
137
142
|
|
|
138
143
|
Most of what driftlet's numerics do differently began as a failure here: the impedance's
|
|
139
144
|
current read across the quietest cut of the device rather than at the contact, GMRES judged by
|
|
@@ -174,7 +179,11 @@ disagree (a port's voltage at 0 for −1.42 V, θ 0.05 for 0.015, a contact 24 m
|
|
|
174
179
|
the fixes, device by device, so its results above stand. On 1.2.2 the whole sweep, 870 devices,
|
|
175
180
|
gives the same: 605 agree, 117 transients fail, and 13 differ, each a capacitive contact beside a
|
|
176
181
|
membrane capacitor where the transient loses K⁺. Since a face out of equilibrium in `c0` starts
|
|
177
|
-
in it (1.2.3), 607 agree and 115 fail, the same 13 differing.
|
|
182
|
+
in it (1.2.3), 607 agree and 115 fail, the same 13 differing. Those 13 were a missing derivative:
|
|
183
|
+
a contact's current, read from its end box, didn't take the membrane port's voltage that reaches
|
|
184
|
+
it (a cold review of `src/boundaries.js`), so Newton stopped short in a way the tolerance set. With
|
|
185
|
+
it in (1.2.7), the transient settles on the steady state to 1e-14 V, in a third of the steps, and
|
|
186
|
+
648 agree, 87 transients fail, none differ.
|
|
178
187
|
|
|
179
188
|
## Consistent: the derivatives
|
|
180
189
|
|
package/docs/statistics.md
CHANGED
|
@@ -73,6 +73,19 @@ of exponentially. With one species it's Langmuir filling of sites. The dilute re
|
|
|
73
73
|
$`c_{\mathrm{ref}}`$ is kept. To use the half-filling convention of site models
|
|
74
74
|
($`e^\zeta = x/(1-x)`$), set `cRef` equal to `cMax`.
|
|
75
75
|
|
|
76
|
+
`cMax` from an ion's size: a cube of side $`a`$ per site gives $`c_{\mathrm{max}} = 1/(N_A a^3)`$, so
|
|
77
|
+
$`a`$ = 0.66 nm (a hydrated ion, about) is 5.8 M, and 0.5 nm is 13 M. The model counts sites only:
|
|
78
|
+
D falling with concentration, solvent counterflow and ion pairing are left out. On a material from
|
|
79
|
+
`aqueous()`, add it to the material it returns:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import { AVOGADRO } from 'driftlet';
|
|
83
|
+
import { aqueous } from 'driftlet/kit';
|
|
84
|
+
|
|
85
|
+
const aq = aqueous(['Cu2+', 'SO42-', 'H+']);
|
|
86
|
+
aq.materials.water.statistics = [{ type: 'lattice', species: ['Cu2+', 'SO42-', 'H+'], cMax: 1 / (AVOGADRO * 0.66e-9 ** 3) }];
|
|
87
|
+
```
|
|
88
|
+
|
|
76
89
|
### Redlich–Kister
|
|
77
90
|
|
|
78
91
|
One species on its own $`c_{\mathrm{max}}`$ sites, with the excess Gibbs energy of mixing per site
|
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.3.2/src/index.js';
|
|
34
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.3.2/src/kit.js';
|
|
35
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.3.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.3.2/src/index.js';
|
|
223
|
+
import { build, layer, ohmic, semiconductor, live } from 'https://cdn.jsdelivr.net/npm/driftlet@1.3.2/src/kit.js';
|
|
224
|
+
import { bandDiagram } from 'https://cdn.jsdelivr.net/npm/driftlet@1.3.2/src/plot.js';
|
|
225
225
|
|
|
226
226
|
const def = build({
|
|
227
227
|
T: 300,
|
|
@@ -317,6 +317,8 @@ const mos = (Vg, zeroCharge) =>
|
|
|
317
317
|
layer('Si', units.um(1), { name: 'Si', acceptors: NA }),
|
|
318
318
|
ohmic(0),
|
|
319
319
|
],
|
|
320
|
+
// The inversion layer's supply: with it, the low-frequency C–V at every frequency, even
|
|
321
|
+
// impedance at 1 MHz. Leave it out for the high-frequency curve (see below the code).
|
|
320
322
|
ports: [{ name: 'channel', region: 'Si', from: 0, to: units.nm(1), V: 0, terminal: 'e-', species: { 'e-': 'equilibrium' } }],
|
|
321
323
|
grid: { hmin: units.nm(0.25), hmax: units.nm(50), ratio: 1.15 },
|
|
322
324
|
});
|
|
@@ -383,8 +385,9 @@ electrons' V minus φ in the bulk (with the library's μ°), and a Cl⁻ termina
|
|
|
383
385
|
that, so an electrode held at 0 V beside it is strongly anodic: `describe(def)` says where SHE
|
|
384
386
|
sits, and an offset of that much (`bath(c, 'Cl-', 0, { offset })`) makes the terminal read SHE.
|
|
385
387
|
The working electrode's current is positive when oxidising. The layer's resistance is in series
|
|
386
|
-
(uncompensated IR: keep it thin, or the supporting salt strong); a 100:1 excess of salt
|
|
387
|
-
leaves the limiting current about 1% off pure diffusion
|
|
388
|
+
(uncompensated IR: keep it thin, or the supporting salt strong); a 100:1 excess of a 1:1 salt
|
|
389
|
+
still leaves the limiting current about 1% off pure diffusion, and migration counts for more with
|
|
390
|
+
more charge: Cu²⁺ in 1 M H₂SO₄ (20:1) reads 3% above it, with no acid 2×.
|
|
388
391
|
|
|
389
392
|
```js
|
|
390
393
|
import { Device, GAS_CONSTANT, FARADAY } from 'driftlet';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driftlet",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.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": {
|