driftlet 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,27 @@
1
- # driftlet
1
+ <h1>
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://markblundeberg.github.io/driftlet/docs/logo-dark.svg">
4
+ <img alt="driftlet" src="https://markblundeberg.github.io/driftlet/docs/logo.svg" width="300">
5
+ </picture>
6
+ </h1>
2
7
 
3
8
  [![test](https://github.com/markblundeberg/driftlet/actions/workflows/test.yml/badge.svg)](https://github.com/markblundeberg/driftlet/actions/workflows/test.yml)
9
+ [![npm](https://img.shields.io/npm/v/driftlet)](https://www.npmjs.com/package/driftlet)
4
10
 
5
- **driftlet solves 1D drift–diffusion–reaction problems properly**: any mix of charged and
6
- neutral species, Poisson electrostatics (or strict neutrality), bulk and interfacial reactions,
7
- heterointerfaces, metals, and external circuits, in steady state, in time, and as small-signal
8
- impedance. It's validated against analytic results, and small enough to run live in a web page:
9
- pure JavaScript, no dependencies, millisecond solves.
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. Every physics feature is validated against analytic results, and
17
+ it's small enough to run in a web page: no dependencies, millisecond solves.
10
18
 
11
- **Status: pre-release.** Not yet on npm, and the API may still change.
19
+ [Live demos](https://markblundeberg.github.io/driftlet/demos/) · [npm](https://www.npmjs.com/package/driftlet) ·
20
+ [reading level diagrams](docs/visualization.md) · [device reference](docs/device.md) ·
21
+ [agent guide](llms.txt)
22
+
23
+ **Status: 0.x.** Released on npm and used by the demos, but the API may still change between
24
+ minor versions.
12
25
 
13
26
  ## Is it for your problem?
14
27
 
@@ -17,12 +30,12 @@ The same equations go by different names in different fields. If your problem is
17
30
 
18
31
  | If you work on | you may call it | start from | checked against |
19
32
  |---|---|---|---|
20
- | Semiconductor devices | drift–diffusion, van Roosbroeck, quasi-Fermi levels, Scharfetter–Gummel; pn, Schottky, MOS, heterojunctions | [first example](#a-semiconductor-junction), [pn demo](https://markblundeberg.github.io/driftlet/demos/pn.html), [`contacts`](test/contacts.test.js), [`metal`](test/metal.test.js) tests | exact built-in potential, Shockley J–V, depletion charge, MOS C–V |
21
- | Solar cells | photogeneration, radiative and SRH recombination | [`reactions`](test/reactions.test.js) test: generation is a reaction from a photon reservoir (uniform per material), SRH runs through explicit trap species | `J_sc = qG(L_n + L_p + W)`; Shockley J–V in the dark |
22
- | Electrochemistry | Nernst–Planck, concentration polarization, limiting current, Butler–Volmer, Warburg, cyclic voltammetry | [second example](#an-electrochemical-cell), [saturation](https://markblundeberg.github.io/driftlet/demos/saturation.html) and [impedance](https://markblundeberg.github.io/driftlet/demos/impedance.html) demos, [`circuit`](test/circuit.test.js), [`kinetics`](test/kinetics.test.js), [`impedance`](test/impedance.test.js) tests | `i_lim·tanh(V/4V_T)`, Butler–Volmer closed form, finite-length Warburg |
33
+ | Semiconductor devices | drift–diffusion, van Roosbroeck, quasi-Fermi levels, Scharfetter–Gummel; pn, Schottky, MOS, heterojunctions | [first example](#a-semiconductor-junction), [pn](https://markblundeberg.github.io/driftlet/demos/pn.html) and [MOS](https://markblundeberg.github.io/driftlet/demos/mos.html) demos, [`contacts`](test/contacts.test.js), [`metal`](test/metal.test.js) tests | exact built-in potential, Shockley J–V, depletion charge, MOS C–V |
34
+ | Solar cells | photogeneration, radiative and SRH recombination | [solar demo](https://markblundeberg.github.io/driftlet/demos/solar.html), [`reactions`](test/reactions.test.js) test: generation is a reaction from a photon reservoir (uniform per material), SRH runs through explicit trap species | `J_sc = qG(L_n + L_p + W)`; Shockley J–V in the dark |
35
+ | Electrochemistry | Nernst–Planck, concentration polarization, limiting current, Butler–Volmer, Warburg, cyclic voltammetry, salt bridges and liquid junctions | [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) and [impedance](https://markblundeberg.github.io/driftlet/demos/impedance.html) demos, [`circuit`](test/circuit.test.js), [`kinetics`](test/kinetics.test.js), [`impedance`](test/impedance.test.js) tests | `i_lim·tanh(V/4V_T)`, Butler–Volmer closed form, finite-length Warburg |
23
36
  | Batteries, intercalation | OCV, insertion hosts, chemical diffusion | [insertion demo](https://markblundeberg.github.io/driftlet/demos/insertion.html), [`statistics`](test/statistics.test.js) test | composition vs OCV, π²D/4L² relaxation |
24
37
  | Double layers, colloids | Poisson–Boltzmann, Gouy–Chapman–Stern, Debye screening, crowding (Bikerman) | [double-layer demo](https://markblundeberg.github.io/driftlet/demos/double-layer.html), [`equilibrium`](test/equilibrium.test.js) test | Gouy–Chapman charge and profile, Kilic–Bazant–Ajdari |
25
- | Membranes, desalination | Donnan, ion exchange, liquid junctions, water dissociation | [`equilibrium`](test/equilibrium.test.js), [`neutral`](test/neutral.test.js) tests | Donnan partition, Planck EMF |
38
+ | Membranes, desalination | Donnan, ion exchange, liquid junctions, water dissociation | [membrane demo](https://markblundeberg.github.io/driftlet/demos/membrane.html), [`equilibrium`](test/equilibrium.test.js), [`neutral`](test/neutral.test.js) tests | Donnan partition, Planck EMF |
26
39
  | Solid-state ionics | mixed ionic–electronic conduction, defect chemistry, mobile ions | [`statistics`](test/statistics.test.js), [`reactions`](test/reactions.test.js) tests | mass action from standard potentials |
27
40
  | Biophysics | Poisson–Nernst–Planck, ion channels, membrane potentials | the electrochemistry setups, with fixed charge in the channel region | no dedicated check yet |
28
41
 
@@ -42,7 +55,7 @@ import { bandDiagram } from 'driftlet/plot'; // a level diagram as an SVG str
42
55
  ```
43
56
 
44
57
  Or straight from a CDN in a page, pinned to a version:
45
- `https://cdn.jsdelivr.net/npm/driftlet@0.1.0/src/index.js` (and `src/kit.js`, `src/plot.js`).
58
+ `https://cdn.jsdelivr.net/npm/driftlet@0.2.0/src/index.js` (and `src/kit.js`, `src/plot.js`).
46
59
  TypeScript declarations ship in the package. Writing a demo with an LLM agent? Point it at
47
60
  [`llms.txt`](llms.txt).
48
61
 
@@ -131,10 +144,10 @@ them locally, serve the repository root (e.g. `python3 -m http.server`) and open
131
144
  | **pn junction**: quasi-Fermi levels under bias; I–V against Shockley | **Solar cell**: J<sub>sc</sub> and V<sub>oc</sub> against collection theory | **MOS capacitor**: band bending; C–V against ideal theory |
132
145
  | [![cyclic voltammetry](demos/screenshots/redox.png)](https://markblundeberg.github.io/driftlet/demos/redox.html) | [![Daniell cell](demos/screenshots/daniell.png)](https://markblundeberg.github.io/driftlet/demos/daniell.html) | [![saturation](demos/screenshots/saturation.png)](https://markblundeberg.github.io/driftlet/demos/saturation.html) |
133
146
  | **Cyclic voltammetry**: a Fermi level against a redox level | **Daniell cell**: a real salt bridge, leaking, ion by ion | **Saturation**: Ag \| AgNO₃ \| Ag under a square wave, up to its limiting current |
134
- | [![impedance](demos/screenshots/impedance.png)](https://markblundeberg.github.io/driftlet/demos/impedance.html) | | |
135
- | **Impedance**: the Warburg arc, and the cell's small-signal response inside | | |
136
- | [![ion-exchange membrane](demos/screenshots/membrane.png)](https://markblundeberg.github.io/driftlet/demos/membrane.html) | [![double layer](demos/screenshots/double-layer.png)](https://markblundeberg.github.io/driftlet/demos/double-layer.html) | [![insertion host](demos/screenshots/insertion.png)](https://markblundeberg.github.io/driftlet/demos/insertion.html) |
137
- | **Ion-exchange membrane**: Donnan steps, ion by ion, against TMS theory | **Double layer**: dilute vs crowded ions, against closed forms | **Intercalation host**: cycling between cutoffs against the OCV |
147
+ | [![impedance](demos/screenshots/impedance.png)](https://markblundeberg.github.io/driftlet/demos/impedance.html) | [![ion-exchange membrane](demos/screenshots/membrane.png)](https://markblundeberg.github.io/driftlet/demos/membrane.html) | [![double layer](demos/screenshots/double-layer.png)](https://markblundeberg.github.io/driftlet/demos/double-layer.html) |
148
+ | **Impedance**: the Warburg arc, and the cell's small-signal response inside | **Ion-exchange membrane**: Donnan steps, ion by ion, against TMS theory | **Double layer**: dilute vs crowded ions, against closed forms |
149
+ | [![insertion host](demos/screenshots/insertion.png)](https://markblundeberg.github.io/driftlet/demos/insertion.html) | | |
150
+ | **Intercalation host**: cycling between cutoffs against the OCV | | |
138
151
 
139
152
  ## How to think about it
140
153
 
@@ -217,6 +230,7 @@ Every physics feature is tested against analytic results (`npm test`, node's bui
217
230
  | Floating island | Gauss's law; conserved amounts through gate sweeps | 1e-12 |
218
231
  | pn junction | exact built-in potential; depletion charge; short-diode J–V | 1e-12; 1%; 2e-3 |
219
232
  | Long pn diode with recombination | Shockley J–V incl. depletion recombination | 2e-3 |
233
+ | 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 |
220
234
  | Illuminated long pn diode | `J_sc = qG(L_n + L_p + W)` from a cold start; superposition at low injection | 5e-3; 1e-2 |
221
235
  | Schottky barrier (metal region \| n-Si) | surface density from the alignment; depletion charge | 5e-3; 2% |
222
236
  | Liquid junction, open circuit | cell EMF 2t₊(RT/F) ln(c₁/c₂); Planck diffusion potential | 1e-4 |
@@ -268,7 +282,7 @@ statistics can't be, and must be built inside the worker.)
268
282
 
269
283
  ## Development
270
284
 
271
- No dependencies. Tests use node's built-in runner (Node ≥ 20):
285
+ No dependencies. Tests use node's built-in runner (Node ≥ 22):
272
286
 
273
287
  ```sh
274
288
  npm test # the validation suite
@@ -0,0 +1,146 @@
1
+ # Band alignment from vacuum levels
2
+
3
+ driftlet never decides how levels line up across an interface. Each interface's alignment is
4
+ its own physical property, and you give it explicitly (see [conventions](conventions.md)). But
5
+ sometimes vacuum-level reasoning is the only estimate available, and it's a useful worked
6
+ reference. This page sets it out as one recipe with two ingredients, and lists what it
7
+ assumes.
8
+
9
+ **Before reaching for it,** ask whether you need an alignment at all. If only macroscopic,
10
+ bulk-neutral behaviour matters, a `phi: 'neutral'` interface makes the alignment drop out, and
11
+ none is needed.
12
+
13
+ ## The recipe: an anchor and an offset per material
14
+
15
+ Every vacuum-level number in the literature describes **one material against vacuum**. It
16
+ picks a level inside the material (the **anchor**) and says how far above that level, in
17
+ electron energy, the vacuum level just outside the surface lies (the **offset**). In
18
+ driftlet's voltage units ($`V = -E/e`$ for electrons):
19
+
20
+ ```math
21
+ V_{\mathrm{vac}} = V_{\mathrm{anchor}} - \mathtt{offset}
22
+ ```
23
+
24
+ | Quantity | Anchor | Offset |
25
+ |---|---|---|
26
+ | electron affinity $`\chi`$ | conduction band = the e⁻ standard level | $`\chi`$ |
27
+ | ionisation energy | valence band = the h⁺ standard level | $`\chi + E_g`$ |
28
+ | work function W | Fermi level | W |
29
+ | Trasatti's absolute SHE, ≈ 4.44 V | the SHE level of a solution | 4.44 V |
30
+ | surface potential $`\chi_s`$ | the inner potential $`\phi`$ | $`\chi_s`$ |
31
+
32
+ Two materials are then aligned by **assuming their vacuum levels coincide** across the
33
+ interface. That assumption is the whole heuristic: Anderson's rule, the Schottky–Mott rule, a
34
+ gate's flat-band voltage and the pzc estimate are all this one recipe applied to different
35
+ pairs. It can combine any pair, for example a semiconductor (electron affinity) against an
36
+ aqueous electrolyte (absolute SHE).
37
+
38
+ ### The anchors in driftlet
39
+
40
+ An anchor has to be a level that driftlet knows in terms of a material's $`\phi`$:
41
+
42
+ - **A charged species' standard level**, $`V^\circ_i = \phi + \mu^\circ_i/(z_i F)`$. For e⁻ and h⁺
43
+ these are the band edges, which are electronic levels. For an ion, $`V^\circ_i`$ is simply that
44
+ ion's standard level: a fixed offset from $`\phi`$, not by itself the level of any electrons.
45
+ - **The SHE level**, the electronic level of the standard hydrogen electrode in a solution:
46
+
47
+ ```math
48
+ V^\circ_{\mathrm{e}^-}(\mathrm{SHE}) = \phi + (\mu^\circ_{\mathrm{H}^+} - \tfrac{1}{2} \mu^\circ_{\mathrm{H}_2}) / F
49
+ ```
50
+
51
+ As an anchor this is `'H+'` (its standard level $`\phi + \mu^\circ_{\mathrm{H}^+}/F`$), with the H₂
52
+ term folded into the offset: Trasatti's value becomes
53
+ `{ anchor: 'H+', offset: 4.44 + μ°_H₂/(2F) }`. On the usual conventions
54
+ ($`\mu^\circ_{\mathrm{H}_2} = 0`$ for the element in its standard state, and
55
+ $`\mu^\circ_{\mathrm{H}^+} = 0`$ so that $`E^\circ(\mathrm{SHE}) = 0`$), that's just offset 4.44 V.
56
+ Trasatti's 4.44 V then works exactly like a semiconductor's electron affinity, with the SHE level
57
+ in place of the conduction band. It needs H⁺ among the species. Other half-reactions could be used
58
+ as anchors in principle, but each brings its own conventions (the neutral element's $`\mu`$,
59
+ $`E^\circ(\mathrm{SHE}) = 0`$), so they're not offered as shortcuts here.
60
+ - **The Fermi level** of a metal, the proper reference for a work function. It's taken by
61
+ `vacuumZeroCharge`, for a conductor region's face or a contact (below). A semiconductor's Fermi
62
+ level moves with doping and bias, so its work function isn't a material constant: use its
63
+ electron affinity instead.
64
+ - **$`\phi`$ itself** (`'phi'`), with a surface potential as the offset. That's discouraged, since
65
+ $`\phi`$ is bookkeeping: its value inside a material depends on how that material's $`\mu^\circ`$ were
66
+ anchored, and surface potentials are not measurable on their own. If you have settled that
67
+ bookkeeping to your own satisfaction, it's available.
68
+
69
+ ## In the API
70
+
71
+ The recipe lives in `driftlet/kit`, as helpers that read a device definition and return the
72
+ spec value to put in it. The spec itself only takes the result (a `dipole` or a `zeroCharge`),
73
+ so what a device assumes stays visible in its definition.
74
+
75
+ ```js nocheck
76
+ import { vacuumDipole, vacuumZeroCharge } from 'driftlet/kit';
77
+
78
+ // GaAs | AlGaAs: electron affinities on the e⁻ standard level (the conduction band)
79
+ def.interfaces = [{
80
+ dipole: vacuumDipole(def, { material: 'GaAs', anchor: 'e-', offset: 4.07 }, { material: 'AlGaAs', anchor: 'e-', offset: 3.8 }),
81
+ }];
82
+ ```
83
+
84
+ `vacuumDipole` gives $`\phi_R - \phi_L = (V_{\mathrm{vac}} - \phi)_L - (V_{\mathrm{vac}} - \phi)_R`$,
85
+ each side's $`V_{\mathrm{vac}} - \phi`$ coming from its anchor and offset (`vacuumLevel` gives one
86
+ side's).
87
+
88
+ Where a conductor meets a material, at a conductor region's face or at a contact's capacitive
89
+ or pinned law, the conductor's side is its work function W from its Fermi level, and the
90
+ helper gives `zeroCharge` ($`V_F - \phi_{\mathrm{edge}}`$ at zero charge):
91
+
92
+ ```js nocheck
93
+ const zc = vacuumZeroCharge(def, 4.75, { material: 'Si', anchor: 'e-', offset: 4.05 }); // Au | n-Si
94
+ def.interfaces = [{ phi: { type: 'capacitive', C: 10 }, zeroCharge: zc }]; // Au as a region
95
+ Object.assign(def.contacts.left, { phi: { type: 'capacitive', C: 10 }, zeroCharge: zc }); // or as a contact
96
+ // zeroCharge = W + (V_vac − φ)_inside = W − χ − μ°_e/F
97
+ ```
98
+
99
+ ## Pairwise examples
100
+
101
+ **Semiconductor heterojunction (Anderson's rule).** Electron affinities on both sides,
102
+ `{ material, anchor: 'e-', offset: χ }`, give $`\Delta E_c = \chi_L - \chi_R`$. The valence-band offset
103
+ then follows from the two gaps, already in your holes' $`\mu^\circ`$. Ionisation energies on `'h+'`
104
+ give the same thing from the other band.
105
+
106
+ **Metal | semiconductor (Schottky–Mott).** Work function against electron affinity gives the
107
+ n-type barrier $`\phi_B = W - \chi`$. With the metal as a contact, a pinned law
108
+ `phi: 'pinned', zeroCharge: vacuumZeroCharge(def, W, { material, anchor: 'e-', offset: χ })`
109
+ puts the semiconductor's surface electron density at $`N_c e^{-\phi_B/kT}`$. With the metal as a
110
+ region, the face is capacitive with the same `zeroCharge`; a large C approaches the pinned
111
+ barrier.
112
+
113
+ **Gate | semiconductor (flat band).** The same with a capacitive law. Its `zeroCharge` is the
114
+ anchor-level form of the textbook $`V_{\mathrm{FB}} = (W_g - W_s)/e`$, which refers to the
115
+ semiconductor's bulk Fermi level instead. Fixed charge in a real insulator shifts flat band further.
116
+
117
+ **Metal | electrolyte (potential of zero charge).** Work function against the absolute SHE gives
118
+ $`\mathrm{pzc} \approx W/e - 4.44\,\mathrm{V}`$ on the SHE scale (on the usual conventions above):
119
+ `zeroCharge: vacuumZeroCharge(def, W, { material, anchor: 'H+', offset: 4.44 })`.
120
+
121
+ **Semiconductor | electrolyte.** Electron affinity against the absolute SHE places the band edges on
122
+ the SHE scale, $`E_c \approx -(\chi - 4.44)\,\mathrm{eV}`$ relative to SHE: the usual
123
+ photoelectrochemistry estimate:
124
+ `vacuumDipole(def, { material: 'TiO2', anchor: 'e-', offset: χ }, { material: 'water', anchor: 'H+', offset: 4.44 })`.
125
+
126
+ **Solvent | solvent.** Each solvent's own absolute-SHE-type value (its Trasatti-type offset)
127
+ aligns them, exactly as Anderson's rule aligns two semiconductors.
128
+
129
+ ## What the heuristic assumes, and where it fails
130
+
131
+ 1. **The vacuum level runs continuously through the interface.** In reality, the interface
132
+ forms its own dipole (charge transfer, bonding, solvent orientation, adsorbates) beyond
133
+ what the two free surfaces imply.
134
+ 2. **Surface quantities are properties of the material.** They aren't: W, $`\chi`$ and 4.44 V are
135
+ barrier heights of particular surfaces, and change with termination, adsorbates and
136
+ preparation. The 4.44 V is itself the work function of a hydrogen electrode, not a
137
+ universal zero.
138
+ 3. **Alignments add up transitively** around any set of materials, and are uniform along each
139
+ interface.
140
+
141
+ At covalent interfaces (common semiconductors, most metal–semiconductor contacts), measured
142
+ alignments often depart substantially from these rules, and Fermi-level pinning is common.
143
+ Ionic materials tend to follow them better. At metal–solution interfaces, the neglected
144
+ metal–solvent terms (orientation, chemisorption) are often a few tenths of a volt.
145
+ Whenever a measured offset, barrier, flat-band voltage or pzc exists, use it instead:
146
+ `step`, `dipole` and `zeroCharge` take measured values directly.
@@ -0,0 +1,194 @@
1
+ # Conventions
2
+
3
+ Conventions are where users of any drift–diffusion code go wrong, so here they all are in one
4
+ place. driftlet's are chosen to be thermodynamically honest, and some of them are deliberately
5
+ stricter than you may be used to.
6
+
7
+ ## Principles
8
+
9
+ 1. **Every control and every measurable is an electrochemical potential** $`\bar\mu`$, or a
10
+ difference of them. A bias voltage sets a difference in $`\bar\mu`$ of electrons between
11
+ terminals, $`V = -\Delta\bar\mu_{\mathrm{e}^-}/F`$. A gate sets its metal's $`\bar\mu_{\mathrm{e}^-}`$.
12
+ Nothing you set is an electrostatic potential.
13
+ 2. **$`\phi`$ is bookkeeping.** Any consistent $`\phi`$ gives identical physics. Each material's standard
14
+ chemical potentials fix where its $`\phi`$ sits relative to its own levels, so $`\phi`$ jumps at
15
+ every interface between different materials, by that interface's dipole. How big a dipole is
16
+ depends on how each material was anchored, which is why it's arbitrary, correctly so. If you
17
+ do have true mean inner potentials, you may anchor to them, but nothing requires it.
18
+ Extrathermodynamic conventions are one way people settle a "real" $`\phi`$ per material. TATB,
19
+ for instance, fixes single-ion transfer energies between solvents by assuming
20
+ $`\Delta G_{\mathrm{tr}}(\mathrm{Ph_4As}^+) = \Delta G_{\mathrm{tr}}(\mathrm{Ph_4B}^-)`$. That's a
21
+ statement about bulk single-ion energies, i.e. about how each solvent's $`\mu^\circ`$ are
22
+ anchored. It says nothing about the dipole at an actual interface, which stays a separate
23
+ input.
24
+ 3. **Bulk standard potentials only mean something in neutral combinations**
25
+ ($`\mu^\circ_{\mathrm{e}^-} + \mu^\circ_{\mathrm{h}^+}`$,
26
+ $`\mu^\circ_{\mathrm{Na}^+} + \mu^\circ_{\mathrm{Cl}^-}`$,
27
+ $`\mu^\circ_{\mathrm{Li}^+} + \mu^\circ_{\mathrm{e}^-}`$). How charged levels line up across a
28
+ boundary between different materials is a property of that interface, and you must give it.
29
+ There's no Anderson rule, no Schottky–Mott rule, and no implied common vacuum level. Each
30
+ interface's alignment is independent, even when the same pair of materials meets twice
31
+ (e.g. with different adsorbates). When vacuum-level estimates are all you have, helpers in
32
+ `driftlet/kit` turn anchors and offsets into an alignment (see the
33
+ [alignment guide](alignment.md)).
34
+ 4. **A work function belongs to a surface, not a material.**
35
+
36
+ ## Electrochemical potential and its views
37
+
38
+ For species *i* with charge number $`z_i`$, in a material where its standard chemical potential
39
+ is $`\mu^\circ_i`$, the electrochemical potential splits as
40
+
41
+ ```math
42
+ \bar\mu_i = \mu^\circ_i + z_i F \phi + RT \zeta_i
43
+ ```
44
+
45
+ The reduced chemical potential $`\zeta_i`$ is what a material's **statistics** turn into a
46
+ concentration, $`c_i = c_i(\zeta)`$. The default is ideal (dilute) statistics,
47
+
48
+ ```math
49
+ c_i = c_{\mathrm{ref},i} \cdot e^{\zeta_i}, \quad\text{i.e.}\quad \bar\mu_i = \mu^\circ_i + z_i F \phi + RT \ln(c_i / c_{\mathrm{ref},i})
50
+ ```
51
+
52
+ with $`c_{\mathrm{ref},i}`$ the reference concentration that $`\mu^\circ_i`$ refers to. Non-ideal
53
+ statistics (degenerate carriers, crowding, intercalation hosts, activity coefficients) are chosen
54
+ per material; see [statistics](statistics.md). They all reduce to the ideal form in the dilute
55
+ limit, so $`\mu^\circ`$ and $`c_{\mathrm{ref}}`$ mean the same thing in every model: the dilute
56
+ (Henry's-law) reference.
57
+
58
+ The API speaks $`\bar\mu`$ in J/mol (`units.eV` converts per-particle energies). Two voltage views
59
+ are provided for charged species (what they show, and why they're drawn, is in
60
+ [reading level diagrams](visualization.md)):
61
+
62
+ - the **species voltage** $`V_i = \bar\mu_i / (z_i F)`$, which is what a voltmeter reads for
63
+ electrons, and the analogous quantity a reversible electrode reads for an ion;
64
+ - the **standard level** $`V^\circ_i = \phi + \mu^\circ_i / (z_i F)`$, which plays the part of a
65
+ band edge.
66
+
67
+ Plotted as voltages, a semiconductor's conduction-band level $`V^\circ_{\mathrm{e}^-}`$ sits
68
+ *below* its valence-band level $`V^\circ_{\mathrm{h}^+}`$. That's expected, since $`V = -E/e`$ for
69
+ electrons.
70
+
71
+ Electrons and holes are ordinary species: $`z = \mp 1`$, $`c_{\mathrm{ref}}`$ = effective density of
72
+ states, and $`\mu^\circ`$ = the band edges as molar energies (conduction band for e⁻, minus the
73
+ valence band for h⁺). With Fermi–Dirac statistics the same parameters give
74
+ $`c = N_c \mathcal{F}_{1/2}(\zeta)`$.
75
+
76
+ ## Transport
77
+
78
+ Each species moves down its own electrochemical potential:
79
+
80
+ ```math
81
+ \begin{aligned}
82
+ N_i &= -(D_i c_i / RT) \nabla\bar\mu_i && \quad\text{(particle flux, mol/(m}^2\cdot\text{s))} \\
83
+ J_i &= z_i F N_i = -\sigma_i \nabla V_i, \qquad \sigma_i = z_i^2 F^2 D_i c_i / RT
84
+ \end{aligned}
85
+ ```
86
+
87
+ Drift and diffusion are one law, and the Einstein relation is automatic. $`D`$ is the coefficient
88
+ of $`c\nabla\bar\mu/RT`$, i.e. $`RT`$ times the mobility per mole, and it's the same parameter
89
+ whatever the statistics. For ideal statistics it's the ordinary diffusivity. With non-ideal
90
+ statistics the measured chemical diffusivity is $`D`$ times the thermodynamic factor,
91
+ $`\partial \ln a/\partial \ln c`$: the generalised Einstein relation, as for degenerate electrons.
92
+
93
+ Two optional transport terms describe a moving fluid:
94
+
95
+ - **Advection** by an imposed velocity $`v`$ adds $`v \cdot c_i`$ to every mobile species' flux.
96
+ - **Eddy mixing** adds $`-(D_{\mathrm{mix}}/RT) P \nabla\bar\mu`$, with
97
+ $`P = C - C z z^\mathsf{T} C / (z^\mathsf{T} C z)`$ and $`C = \mathrm{diag}(c)`$.
98
+ - It's an Onsager term: symmetric, positive semi-definite, and zero at equilibrium (flat
99
+ $`\bar\mu`$), so double layers at rest are untouched.
100
+ - The projection removes any current ($`z^\mathsf{T} P = 0`$). Eddies move neutral fluid, so
101
+ they mix salt and stir concentration gradients but conduct nothing.
102
+ - For a neutral dilute species it's exactly $`-D_{\mathrm{mix}} \nabla c`$. For a binary salt it
103
+ adds $`D_{\mathrm{mix}}`$ to the salt's ambipolar diffusivity and leaves diffusion potentials
104
+ unchanged.
105
+
106
+ ## Materials, regions, interfaces
107
+
108
+ A **material** holds bulk properties ($`\varepsilon`$, and $`D`$, $`\mu^\circ`$, $`c_{\mathrm{ref}}`$ per
109
+ species). A **region** is a material plus a length, fixed charge and initial composition. Every
110
+ **interface** between different materials takes exactly one alignment (see principle 3), unless
111
+ its electrostatic law is `neutral`, in which case the alignment has no effect and is not allowed.
112
+ The three laws:
113
+
114
+ - `pinned`: $`\phi`$ jumps by exactly the alignment, and the double layers on either side are
115
+ resolved by the grid.
116
+ - `neutral`: the macroscopic idealisation. No charge sits at the face, and $`\phi`$ jumps by
117
+ whatever local neutrality on each side requires (a Donnan or Galvani step).
118
+ - `capacitive`: a Helmholtz layer of capacitance $`C`$, charged by any departure of the jump
119
+ from the alignment.
120
+
121
+ A **conductor** (a metal, or a fast ion conductor) is a material of its own kind: its one level
122
+ is its carrier's (a metal's Fermi level), it has no $`\phi`$, and a face beside it ties the other
123
+ side's $`\phi`$ to that level through a capacitance (see the
124
+ [device reference](device.md#faces-next-to-a-conductor)).
125
+
126
+ A face between a strictly neutral ($`\varepsilon = 0`$) material and one with $`\varepsilon > 0`$,
127
+ such as an electrode and an electrolyte, is naturally `pinned`. The neutral side holds the
128
+ surface charge, and the alignment plays the role of a potential of zero charge or work function.
129
+
130
+ ## Contacts and terminals
131
+
132
+ A contact is an interface with an **outside phase** whose levels are known, like one more region
133
+ with a fully known node. The outside phase's charged levels form a rigid ladder,
134
+ $`V_i = V + \mathtt{offset}_i`$, which the external circuit slides by the **terminal voltage** $`V`$
135
+ (the voltage of a named terminal species). The laws joining it to the device (equilibrium,
136
+ blocked, conductance; and for $`\phi`$, bulk, neutral, capacitive, dipole) are the same as at
137
+ internal faces. Reactions sit at faces between regions: an electrode is a conductor region with
138
+ reactions at its face.
139
+
140
+ - **Species in equilibrium** with the outside phase sit at $`V_i = V + \mathtt{offset}_i`$. The
141
+ offset belongs to the outside phase: it's the chemical potential, per charge, of whatever
142
+ neutral combination the species forms with the terminal species there. It's 0 for the
143
+ terminal species itself. For an ion at a reversible electrode,
144
+ $`\mathrm{M}^{n+} + n\,\mathrm{e}^- \rightleftharpoons \mathrm{M(s)}`$ gives
145
+ $`\mu_{\mathrm{M}}/(nF)`$, which is 0 on the usual table convention: $`E^\circ`$ is carried by the
146
+ ion's $`\mu^\circ`$ in the solution, not by the offset. It's never defaulted for any other
147
+ species. Neutral species are fixed by an absolute $`\bar\mu`$.
148
+ - **Ohmic contacts** to a semiconductor are just $`V_{\mathrm{e}^-}`$ and/or $`V_{\mathrm{h}^+}`$ in
149
+ equilibrium with the metal:
150
+ - both, at the same metal, gives $`V_{\mathrm{h}^+} = V_{\mathrm{e}^-}`$, an
151
+ infinite-recombination contact ($`n \cdot p = n_i^2`$ there);
152
+ - one alone gives a selective contact.
153
+ - **Baths** are a shorthand. From a neutral composition and a reference species (the ion a
154
+ reversible reference electrode would sense, e.g. Cl⁻ for Ag/AgCl), driftlet works out every
155
+ species' equilibrium level.
156
+ - **Gates and Stern layers** are capacitive $`\phi`$ laws to a conductor at the terminal voltage.
157
+ Their `zeroCharge` is the value of $`V - \phi_{\mathrm{edge}}`$ at which the interface is
158
+ uncharged. For an electrode in a solution with table $`\mu^\circ`$ (where $`V - \phi`$ reads
159
+ against SHE) that's the potential of zero charge; for a gate it sets the flat-band voltage,
160
+ $`V_{\mathrm{FB}} = \mathtt{zeroCharge} + \phi_{\mathrm{bulk}}`$, with $`\phi_{\mathrm{bulk}}`$ in
161
+ the semiconductor's own anchoring (−1.00 V in p-type silicon at 1e17 cm⁻³ with
162
+ $`\mu^\circ_{\mathrm{e}^-} = 0`$). Like every alignment, it's a property of that interface. The
163
+ `pinned` law is the $`C \to \infty`$ limit (e.g. a Schottky barrier), which is what a
164
+ "fixed $`\phi`$" boundary honestly means.
165
+ - **`bulk`** says the end node is plain bulk (locally neutral, no double layer), while
166
+ **`neutral`** says no charge sits at the face ($`D = 0`$), as for internal faces.
167
+ - **Electrode reactions** take their electrons from the conductor region at its Fermi level,
168
+ which the contact behind it holds at $`\bar\mu_{\mathrm{e}^-} = -F \cdot V`$.
169
+
170
+ The electronic level of a standard hydrogen electrode in a solution is
171
+ $`V^\circ_{\mathrm{e}^-}(\mathrm{SHE}) = \phi + (\mu^\circ_{\mathrm{H}^+} - \tfrac{1}{2}\mu^\circ_{\mathrm{H_2}})/F`$.
172
+ The usual tables set $`\mu^\circ_{\mathrm{H}^+} = 0`$ (so that $`E^\circ(\mathrm{SHE}) = 0`$) and
173
+ $`\mu^\circ_{\mathrm{H_2}} = 0`$ for the element in its standard state, and then
174
+ $`V^\circ_{\mathrm{e}^-}(\mathrm{SHE}) = \phi`$. So with ion $`\mu^\circ`$ from such tables, $`V - \phi`$
175
+ in a solution is a potential against SHE. A metal's $`V - \phi_{\mathrm{edge}}`$ at zero charge (a
176
+ `zeroCharge`, i.e. a pzc) reads directly on the SHE scale.
177
+
178
+ ## Terminals and signs
179
+
180
+ - A terminal's voltage $`V`$ is the shift of its outside phase's ladder, measured as the voltage of
181
+ its terminal species. Every voltage is absolute (no terminal is special), and at least one
182
+ terminal must be held at one.
183
+ - **A terminal's current is into the device** (conduction plus displacement), so in steady
184
+ state they sum to zero. $`V \cdot I`$ is the electrical work done through it; an outside phase
185
+ that exchanges several species also trades chemical free energy through its offsets.
186
+ - **`current` is positive toward +x** through the device (into it at the left, out at the
187
+ right). A forward-biased pn junction with its p side on the right carries negative current.
188
+ - `terminalVoltage` is $`V_{\mathrm{right}} - V_{\mathrm{left}}`$.
189
+
190
+ ## Units
191
+
192
+ SI throughout: m, mol/m³, J/mol, V, A/m², F/m² (per-area capacitance), C/m³ (fixed charge),
193
+ C/m² (sheet charge), S/m² (interface conductance), mol/(m²·s) (surface rate constants),
194
+ s. Temperature is uniform (default 298.15 K).
package/docs/data.md ADDED
@@ -0,0 +1,120 @@
1
+ # Data library
2
+
3
+ `driftlet/kit` carries a small library of bulk properties, each with its source, so a demo can
4
+ start from vetted numbers. It's kept small on purpose, because a wrong library number is worse
5
+ than a wrong demo number. The ions and semiconductors are checked in the tests against
6
+ independent tables: $`\mu^\circ`$ against the electrochemical series, D against limiting conductivities,
7
+ band data against the quoted intrinsic concentrations.
8
+
9
+ The library holds **bulk properties only**. How levels line up at an interface, a surface's
10
+ work function, a pzc and rate constants all belong to a particular interface or surface, so
11
+ you give them yourself (see [conventions](conventions.md)).
12
+
13
+ ## Aqueous ions, 25 °C
14
+
15
+ `aqueous(names, { epsr, material })` returns water holding the named ions, as a library piece
16
+ for `build()`: each ion a species with $`c_{\mathrm{ref}}`$ = 1000 mol/m³ (1 M), and the material
17
+ `water` with its D and $`\mu^\circ`$. `epsr` defaults to water's 78.30 at 25 °C (Malmberg and
18
+ Maryott 1956); `epsr: 0` makes the solution strictly neutral, for macroscopic models without
19
+ double layers. The data are in `IONS`, and $`\mu^\circ`$ of liquid water is `H2O`
20
+ (−237.129 kJ/mol), for reactions such as H₂O ⇌ H⁺ + OH⁻.
21
+
22
+ | Ion | z | D (10⁻⁹ m²/s) | $`\mu^\circ`$ (kJ/mol) |
23
+ |---|---|---|---|
24
+ | H+ | +1 | 9.311 | 0 |
25
+ | Li+ | +1 | 1.029 | −293.31 |
26
+ | Na+ | +1 | 1.334 | −261.905 |
27
+ | K+ | +1 | 1.957 | −283.27 |
28
+ | Ag+ | +1 | 1.648 | 77.107 |
29
+ | Cu2+ | +2 | 0.714 | 65.49 |
30
+ | Zn2+ | +2 | 0.703 | −147.06 |
31
+ | Fe2+ | +2 | 0.719 | −78.9 |
32
+ | Fe3+ | +3 | 0.604 | −4.7 |
33
+ | OH- | −1 | 5.273 | −157.244 |
34
+ | Cl- | −1 | 2.032 | −131.228 |
35
+ | NO3- | −1 | 1.902 | −111.25 |
36
+ | SO42- | −2 | 1.065 | −744.53 |
37
+
38
+ - **D**: diffusivities at infinite dilution, from the CRC Handbook of Chemistry and Physics,
39
+ "Ionic conductivity and diffusion at infinite dilution". They're limiting values, so at
40
+ real concentrations ions move somewhat slower.
41
+ - **$`\mu^\circ`$**: standard Gibbs energies of formation, from Wagman et al., "The NBS tables of
42
+ chemical thermodynamic properties" (J. Phys. Chem. Ref. Data 11, Suppl. 2, 1982), on the usual
43
+ table convention: $`\Delta_{\mathrm{f}} G^\circ(\mathrm{H}^+) = 0`$ and elements in their standard
44
+ states at 0. With these $`\mu^\circ`$, the electronic level of a standard hydrogen electrode in
45
+ the solution is $`\phi`$ itself (see [conventions](conventions.md)), so $`V - \phi`$ reads against
46
+ SHE.
47
+ - **Standard state**: the tables use 1 mol/kg, and the library uses 1 M. In dilute water at 25 °C
48
+ they differ by 0.3%, about 7 J/mol in $`\mu^\circ`$.
49
+ - **Iron**: Fe²⁺'s $`\Delta_{\mathrm{f}} G^\circ`$ gives $`E^\circ(\mathrm{Fe}^{2+}/\mathrm{Fe})`$ =
50
+ −0.41 V, where the CRC series lists −0.447 V; sources disagree on iron. The difference between
51
+ Fe³⁺ and Fe²⁺ gives $`E^\circ(\mathrm{Fe}^{3+}/\mathrm{Fe}^{2+})`$ = 0.769 V, against the
52
+ tabulated 0.771 V, which is what a Fe³⁺/Fe²⁺ redox demo needs.
53
+
54
+ ## Semiconductors, 300 K
55
+
56
+ `semiconductor(name, { material })` returns electrons `e-` and holes `h+` and the material:
57
+ $`c_{\mathrm{ref}} = N_c`$ and $`N_v`$, D from the mobilities by the Einstein relation at 300 K,
58
+ $`\mu^\circ_{\mathrm{e}^-} = 0`$ (so $`\phi`$ sits at the conduction band) and
59
+ $`\mu^\circ_{\mathrm{h}^+} = E_g \cdot F`$. The data are for 300 K, so give the device `T: 300`.
60
+ Statistics are ideal (Boltzmann); add Fermi–Dirac statistics to the material for degenerate
61
+ doping. The data are in `SEMICONDUCTORS`, in the source's units (cm⁻³, cm²/(V·s), eV).
62
+
63
+ | | $`\varepsilon_r`$ | $`E_g`$ (eV) | $`\chi`$ (V) | $`N_c`$ (cm⁻³) | $`N_v`$ (cm⁻³) | $`\mu_n`$ (cm²/(V·s)) | $`\mu_p`$ (cm²/(V·s)) |
64
+ |---|---|---|---|---|---|---|---|
65
+ | Si | 11.9 | 1.12 | 4.05 | 2.8 × 10¹⁹ | 1.04 × 10¹⁹ | 1500 | 450 |
66
+ | Ge | 16.0 | 0.66 | 4.0 | 1.04 × 10¹⁹ | 6.0 × 10¹⁸ | 3900 | 1900 |
67
+ | GaAs | 13.1 | 1.424 | 4.07 | 4.7 × 10¹⁷ | 7.0 × 10¹⁸ | 8500 | 400 |
68
+
69
+ - Source: Sze, *Physics of Semiconductor Devices*, 2nd ed. (1981), appendix "Properties of Ge,
70
+ Si and GaAs at 300 K". The mobilities are for lightly doped material.
71
+ - The electron affinity $`\chi`$ is for vacuum-level estimates with `vacuumDipole` (anchor `'e-'`,
72
+ see the [alignment guide](alignment.md)). It isn't used otherwise, since an alignment is never
73
+ implied.
74
+ - **Silicon's intrinsic concentration**: these $`N_c`$, $`N_v`$ and $`E_g`$ give $`n_i`$ = 6.7 × 10⁹ cm⁻³.
75
+ Sze quotes 1.45 × 10¹⁰ (an older measurement), and the accepted value is 9.65 × 10⁹. The
76
+ library keeps the band data self-consistent rather than matching a quoted $`n_i`$. For Ge and
77
+ GaAs, the data reproduce the quoted $`n_i`$ (2.4 × 10¹³ and 1.79 × 10⁶) to within 10–15%.
78
+
79
+ ## Metals, 20 °C
80
+
81
+ `metal(name, { material })` returns a conductor material carrying `e-`, with its conductivity
82
+ $`1/\rho`$ from the CRC Handbook's resistivities of pure metals at 293 K ("Electrical resistivity of
83
+ pure metals"). The data are in `METALS`, as `rho` (10⁻⁸ Ω·m) and `conductivity` (S/m). They
84
+ aren't cross-checked against a second table in the tests; a metal's conductivity only sets the
85
+ ohmic drop inside it, which is usually negligible.
86
+
87
+ | | Ag | Cu | Au | Al | Zn | Li | Pt |
88
+ |---|---|---|---|---|---|---|---|
89
+ | $`\rho`$ (10⁻⁸ Ω·m) | 1.587 | 1.678 | 2.214 | 2.65 | 5.90 | 9.28 | 10.5 |
90
+ | $`\sigma`$ (10⁷ S/m) | 6.30 | 5.96 | 4.52 | 3.77 | 1.69 | 1.08 | 0.952 |
91
+
92
+ There are no work functions: a work function belongs to a surface, not a metal, and it varies
93
+ by facet and adsorbate. Give a face's `zeroCharge` (or use `vacuumZeroCharge` with a work
94
+ function you've chosen for that surface).
95
+
96
+ ## Example
97
+
98
+ ```js
99
+ import { Device } from 'driftlet';
100
+ import { build, layer, ohmic, half, level, SHE, aqueous, metal } from 'driftlet/kit';
101
+
102
+ // A platinum electrode in Fe³⁺/Fe²⁺ chloride at equilibrium: its Fermi level sits at the
103
+ // couple's redox level, E° against SHE when the two are equal: 0.769 V from the library's NBS values (tables list 0.771 V).
104
+ const iron = half('Fe3+ + e- = Fe2+');
105
+ const def = build({
106
+ library: [aqueous(['H+', 'Fe3+', 'Fe2+', 'Cl-'], { epsr: 0 }), metal('Pt')],
107
+ stack: [
108
+ ohmic(0, ['e-']),
109
+ layer('Pt', 1e-6),
110
+ { reactions: [{ ...iron, k0: 1e-3 }] },
111
+ layer('water', 1e-6, { c0: { 'H+': 100, 'Fe3+': 10, 'Fe2+': 10, 'Cl-': 150 } }),
112
+ { reactions: [{ ...iron, k0: 1e-3 }] },
113
+ layer('Pt', 1e-6),
114
+ ohmic(0, ['e-']),
115
+ ],
116
+ });
117
+ const sol = new Device(def).solve();
118
+ const g = sol.x.length >> 1;
119
+ console.log(`V_e(Pt) − V°(SHE) = ${(sol.V['e-'][0] - level(sol, SHE, { standard: true })[g]).toFixed(4)} V`);
120
+ ```