driftlet 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +285 -0
  3. package/llms.txt +299 -0
  4. package/package.json +54 -0
  5. package/src/bernoulli.js +29 -0
  6. package/src/blockTridiagonal.js +492 -0
  7. package/src/constants.js +36 -0
  8. package/src/data.js +127 -0
  9. package/src/describe.js +190 -0
  10. package/src/device.js +954 -0
  11. package/src/equation.js +188 -0
  12. package/src/errors.js +8 -0
  13. package/src/fermi.js +106 -0
  14. package/src/grid.js +111 -0
  15. package/src/index.js +207 -0
  16. package/src/kit.js +76 -0
  17. package/src/live.js +217 -0
  18. package/src/merge.js +11 -0
  19. package/src/plot.js +267 -0
  20. package/src/solution.js +246 -0
  21. package/src/solver.js +2884 -0
  22. package/src/stack.js +172 -0
  23. package/src/statistics.js +593 -0
  24. package/src/traces.js +125 -0
  25. package/src/types.js +221 -0
  26. package/src/worker.js +24 -0
  27. package/types/bernoulli.d.ts +4 -0
  28. package/types/blockTridiagonal.d.ts +92 -0
  29. package/types/constants.d.ts +33 -0
  30. package/types/data.d.ts +73 -0
  31. package/types/describe.d.ts +15 -0
  32. package/types/device.d.ts +20 -0
  33. package/types/equation.d.ts +102 -0
  34. package/types/errors.d.ts +3 -0
  35. package/types/fermi.d.ts +3 -0
  36. package/types/grid.d.ts +28 -0
  37. package/types/index.d.ts +103 -0
  38. package/types/kit.d.ts +61 -0
  39. package/types/live.d.ts +101 -0
  40. package/types/merge.d.ts +1 -0
  41. package/types/plot.d.ts +60 -0
  42. package/types/solution.d.ts +18 -0
  43. package/types/solver.d.ts +342 -0
  44. package/types/stack.d.ts +59 -0
  45. package/types/statistics.d.ts +10 -0
  46. package/types/traces.d.ts +118 -0
  47. package/types/types.d.ts +574 -0
@@ -0,0 +1,342 @@
1
+ export class SolverError extends Error {
2
+ constructor(message: any, details: any);
3
+ details: any;
4
+ }
5
+ export class Solver {
6
+ constructor(model: any);
7
+ model: any;
8
+ n: any;
9
+ M: any;
10
+ nRx: any;
11
+ nNodes: any;
12
+ nFaces: number;
13
+ nB: any;
14
+ VT: number;
15
+ blockOfNode: Int32Array<any>;
16
+ blockOfFace: Int32Array<ArrayBuffer>;
17
+ present: Uint8Array<ArrayBuffer>;
18
+ cRef: Float64Array<ArrayBuffer>;
19
+ mu0hat: Float64Array<ArrayBuffer>;
20
+ rhoFixed: Float64Array<any>;
21
+ nodeMaterial: Int32Array<any>;
22
+ z: Int32Array<ArrayBuffer>;
23
+ phiUndefined: Uint8Array<any>;
24
+ u: Float64Array<ArrayBuffer>;
25
+ uLo: Float64Array<ArrayBuffer>;
26
+ uPrev: Float64Array<ArrayBuffer>;
27
+ uPrevLo: Float64Array<ArrayBuffer>;
28
+ c: Float64Array<ArrayBuffer>;
29
+ cOld: Float64Array<ArrayBuffer>;
30
+ nodeIdeal: Uint8Array<any>;
31
+ background: Float64Array<any>;
32
+ anyNonIdeal: boolean;
33
+ nodeConductor: Int32Array<any>;
34
+ sheetFace: Int32Array<any>;
35
+ sheetSign: Int8Array<any>;
36
+ conductorLast: Uint8Array<any>;
37
+ K: Float64Array<ArrayBuffer>;
38
+ ex: Float64Array<ArrayBuffer>;
39
+ zeta: Float64Array<any>;
40
+ scratch: Map<any, any>;
41
+ dA: Float64Array<any>;
42
+ dB: Float64Array<any>;
43
+ jL: Float64Array<any>;
44
+ jR: Float64Array<any>;
45
+ time: number;
46
+ lastDt: number;
47
+ contactFlux: {
48
+ left: Float64Array<any>;
49
+ right: Float64Array<any>;
50
+ };
51
+ portFlux: any;
52
+ contactD: {
53
+ left: number;
54
+ right: number;
55
+ };
56
+ contactDOld: {
57
+ left: number;
58
+ right: number;
59
+ };
60
+ contactDStart: {
61
+ left: number;
62
+ right: number;
63
+ };
64
+ terms: any;
65
+ termV: Float64Array<any>;
66
+ termVPrev: Float64Array<any>;
67
+ termI: Float64Array<any>;
68
+ sourceTime: number;
69
+ sourceOverride: Map<any, any>;
70
+ steady: boolean;
71
+ floating: any;
72
+ termB: any;
73
+ termC: any;
74
+ termDI: Float64Array<any>;
75
+ termRes: Float64Array<any>;
76
+ history: any[];
77
+ dtNext: any;
78
+ rxs: any;
79
+ strictlyNeutral: any;
80
+ generationScale: number;
81
+ hasGeneration: any;
82
+ rxsIn: any;
83
+ chargeNode: Uint8Array<any>;
84
+ chargeNodes: Int32Array<ArrayBuffer>;
85
+ combining: boolean;
86
+ bookkeeping: any;
87
+ bookkeepingFaces: number[];
88
+ referenceAmounts: number[];
89
+ boundaryIntake: Float64Array<ArrayBuffer>;
90
+ _activeSlots(): void;
91
+ active: Uint8Array<ArrayBuffer>;
92
+ loc: Int32Array<ArrayBuffer>;
93
+ sys: BlockTridiagonal;
94
+ SINK: number;
95
+ rix: Int32Array<ArrayBuffer>;
96
+ fullOf: Int32Array<ArrayBuffer>;
97
+ res: Float64Array<ArrayBuffer>;
98
+ delta: Float64Array<ArrayBuffer>;
99
+ _factor(): void;
100
+ _solveLinear(rhs: any, delta: any): void;
101
+ _findStretches(): void;
102
+ stretches: any[];
103
+ stretchOf: Int32Array<ArrayBuffer>;
104
+ moieties: Float64Array<ArrayBuffer>[];
105
+ constraints: any[];
106
+ constrained: boolean;
107
+ _conservedMoieties(): Float64Array<ArrayBuffer>[];
108
+ _directSteady(): boolean;
109
+ _applyConstraints(): void;
110
+ _solveBordered(rhs: any, delta: any, deltaV: any, pins: any): void;
111
+ dWork: Float64Array<ArrayBuffer>;
112
+ borderCols: any[];
113
+ /** Total amount (mol per unit area) of a stretch's species in the current state. */
114
+ amount(stretch: any): number;
115
+ redrive(): void;
116
+ deltaV: Float64Array<any>;
117
+ _refreshSources(): void;
118
+ /** η_i (μ̄/RT) of a port's outside level for species i. */
119
+ portEta(port: any, i: any): number;
120
+ /** η_i/RT that a fixed contact link imposes (or NaN if the link isn't fixed). */
121
+ contactEta(side: any, i: any): number;
122
+ /**
123
+ * Cold start. Species connected to a contact take that contact's level; spectators take
124
+ * their region's c0 (which fixes their conserved amount). φ in each region is then chosen
125
+ * for local neutrality, or continued across the interface dipole if nothing there responds.
126
+ */
127
+ initFromComposition(): void;
128
+ _materialAt(mat: any, background: any, eta: any, mode: any, cFix: any, ph: any, zeta: any, cc: any): void;
129
+ computeConcentrations(): void;
130
+ _work(md: any): any;
131
+ _nodeStatistics(g: any, b: any, phiHat: any): void;
132
+ _Kz(g: any, i: any): number;
133
+ /** zᵀKz at node g (per RT): the charge response that sets the screening length. */
134
+ screening(g: any): number;
135
+ _dlnc(g: any, i: any, w: any, d: any): void;
136
+ _j(rb: any, rs: any, cb: any, cs: any, v: any): void;
137
+ /** Assemble residual and Jacobian for a backward-Euler step of size dt. */
138
+ assemble(dt: any): void;
139
+ transformed: boolean;
140
+ _nodeConductor(g: any, dt: any): void;
141
+ _nodesDilute(g0: any, g1: any, dt: any): void;
142
+ _nodeConcentrated(g: any, dt: any): void;
143
+ _storage(g: any, i: any, dt: any, Kz?: number): void;
144
+ _chargeRows(dt: any): void;
145
+ _untransform(delta: any): void;
146
+ _bulkReactions(g: any, rxs: any): void;
147
+ _segmentDisplacement(s: any, bL: any, bR: any, mat: any, h: any): void;
148
+ _segmentsDilute(g0: any, g1: any, region: any, mat: any, vel: any): void;
149
+ _face(f: any): void;
150
+ _segmentConcentrated(s: any, bL: any, bR: any, mat: any, h: any, vel: any): void;
151
+ _segmentConductor(s: any, bL: any, bR: any, mat: any, h: any): void;
152
+ _segmentMixing(s: any, bL: any, bR: any, Dm: any, h: any, mat: any): void;
153
+ mixWork: Float64Array<any>[];
154
+ _dc(g: any, k: any, d: any): void;
155
+ _faceReaction(rx: any, k: any, f: any, bf: any, bL: any, bR: any): void;
156
+ _terminals(dt: any): void;
157
+ _circuit(k: any): void;
158
+ _captureRow(b: any, s: any, w: any, into: any): number;
159
+ _port(port: any, flux: any, k: any): void;
160
+ _contact(side: any, dt: any): void;
161
+ _replaceRow(b: any, rs: any): void;
162
+ _equilibrate(): void;
163
+ _maxPotentialStep(delta: any, deltaV?: Float64Array<any>): number;
164
+ /**
165
+ * Newton iteration for one backward-Euler step from the current cOld.
166
+ * @returns {{converged: boolean, iterations: number, history: number[]}}
167
+ */
168
+ newton(dt: any, { maxIter, tol, maxStep }?: {
169
+ maxIter?: number;
170
+ tol?: number;
171
+ maxStep?: number;
172
+ }): {
173
+ converged: boolean;
174
+ iterations: number;
175
+ history: number[];
176
+ };
177
+ _noteConditioning(): void;
178
+ conditioning: {
179
+ digits: number;
180
+ x: number;
181
+ where: string;
182
+ };
183
+ /**
184
+ * Advance by dt, splitting the interval into halves (recursively) wherever Newton fails.
185
+ * The end time is always t + dt; the result reports the substeps taken.
186
+ */
187
+ advance(dt: any, opts: any, depth?: number): any;
188
+ _addToState(d: any, scale: any): void;
189
+ /**
190
+ * One time step of size dt. Backward Euler by default; with `method: 'bdf2'`, variable-step
191
+ * BDF2 once a previous step exists (and the step ratio is at most 2, for stability). On
192
+ * failure the state is restored.
193
+ *
194
+ * BDF2's storage term (a0 c − (1+ω) c_n + ω²/(1+ω) c_{n−1})/dt is written in backward-Euler
195
+ * form (c − c*)/(dt/a0), so assembly is shared: c* and the displacement histories replace
196
+ * the start-of-step values, and dt/a0 replaces dt.
197
+ */
198
+ step(dt: any, opts?: {}): {
199
+ converged: boolean;
200
+ iterations: number;
201
+ history: number[];
202
+ };
203
+ contactDEnd: any;
204
+ sourceBefore: boolean;
205
+ /**
206
+ * Adaptive time stepping to tEnd: variable-step BDF2 (or backward Euler), with the local error
207
+ * estimated against an explicit predictor through the previous states, and controlled to
208
+ * `tol` thermal units per step in every potential (φ̂, each η, a floating terminal). The first
209
+ * step is checked by step doubling. Stops early when `budgetMs` of wall time is used, so an
210
+ * animation can call it once per frame; the step size carries over between calls.
211
+ */
212
+ integrate(tEnd: any, opts?: {}): {
213
+ converged: boolean;
214
+ done: boolean;
215
+ steps: number;
216
+ rejected: number;
217
+ iterations: number;
218
+ trace: {
219
+ t: any[];
220
+ current: any[];
221
+ voltage: any[];
222
+ };
223
+ };
224
+ landing: any;
225
+ _extrapolate(out: any, t: any): any;
226
+ _predict(pred: any, bdf: any): {
227
+ scale: number;
228
+ order: number;
229
+ };
230
+ _errorNorm(ref: any): number;
231
+ _snapshot(): {
232
+ u: Float64Array<ArrayBuffer>;
233
+ uLo: Float64Array<ArrayBuffer>;
234
+ cOld: Float64Array<ArrayBuffer>;
235
+ time: number;
236
+ lastDt: number;
237
+ contactDStart: {
238
+ left: number;
239
+ right: number;
240
+ };
241
+ contactDOld: {
242
+ left: number;
243
+ right: number;
244
+ };
245
+ contactDEnd: any;
246
+ boundaryIntake: Float64Array<ArrayBuffer>;
247
+ history: any[];
248
+ termV: Float64Array<ArrayBuffer>;
249
+ sourceTime: number;
250
+ };
251
+ _restore(s: any): void;
252
+ _terminalCurrent(): number;
253
+ /**
254
+ * Small-signal impedance about the current (steady) state, seen at one terminal. Linearising
255
+ * gives (J + iωM)·δx + Σ B_k δV_k = rhs for the grid's rows, and (C_k + iωC′_k)·δx +
256
+ * (∂I_k/∂V_k)·δV_k = δI_set,k for each floating terminal's circuit row, with J the steady
257
+ * Jacobian and M the storage part (from J(dt) = J + M/dt), B_k = ∂res/∂V_k and C_k = ∂I_k/∂x.
258
+ * At the measured terminal, a held voltage is perturbed (rhs = −B δV) and its current read
259
+ * from C, or a driven current is perturbed and its voltage solved for. The other terminals
260
+ * keep their drives: held ones at AC ground, driven ones at AC open circuit.
261
+ * Z = δV/δI with I into the device (positive real part for a passive device).
262
+ * @param {ArrayLike<number>} frequencies Hz
263
+ */
264
+ impedance(frequencies: ArrayLike<number>, { profiles, terminal }?: {
265
+ profiles?: boolean;
266
+ terminal?: string;
267
+ }): {
268
+ f: Float64Array<ArrayBuffer>;
269
+ Z: {
270
+ re: Float64Array<ArrayBuffer>;
271
+ im: Float64Array<ArrayBuffer>;
272
+ };
273
+ };
274
+ _smallSignalProfiles(xr: any, xi: any): {
275
+ phi: {
276
+ re: Float64Array<any>;
277
+ im: Float64Array<any>;
278
+ };
279
+ mu: {};
280
+ c: {};
281
+ };
282
+ _assembleBookkeeping(dt: any): void;
283
+ _accumulateBoundaryIntake(dt: any, cN: any): void;
284
+ slowestTime(): number;
285
+ /**
286
+ * Steady state.
287
+ * - If every species stretch is fed by a contact, nothing is conserved on its own, so the
288
+ * true steady equations (dt = ∞, no storage term) are solved directly: no slow modes to
289
+ * wait out, however slow the physics (e.g. exponentially scarce minority carriers).
290
+ * - Otherwise backward-Euler steps at a huge dt, whose storage term pins each conserved
291
+ * amount exactly; dt keeps growing ×10 while the state still moves.
292
+ * If Newton fails, dt ramps up from a small value (pseudo-transient continuation) instead.
293
+ * The clock is not advanced, and open-system conservation bookkeeping restarts here.
294
+ */
295
+ solveSteady(opts?: {}): any;
296
+ solvedV: any;
297
+ _generationContinuation(opts: any, r: any): {
298
+ converged: boolean;
299
+ steps: any;
300
+ iterations: any;
301
+ history: any;
302
+ };
303
+ _continuation(opts: any, level: any, target: any, r: any, warm: any): any;
304
+ _solveSteady(opts?: {}): {
305
+ converged: boolean;
306
+ steps: number;
307
+ iterations: number;
308
+ history: ({
309
+ dt: number;
310
+ converged: boolean;
311
+ iterations: number;
312
+ maxStep?: undefined;
313
+ } | {
314
+ dt: number;
315
+ converged: boolean;
316
+ iterations: number;
317
+ maxStep: number;
318
+ })[];
319
+ };
320
+ _steadySteps({ maxSteps, tol }?: {
321
+ maxSteps?: number;
322
+ tol?: number;
323
+ }): {
324
+ converged: boolean;
325
+ steps: number;
326
+ iterations: number;
327
+ history: ({
328
+ dt: number;
329
+ converged: boolean;
330
+ iterations: number;
331
+ maxStep?: undefined;
332
+ } | {
333
+ dt: number;
334
+ converged: boolean;
335
+ iterations: number;
336
+ maxStep: number;
337
+ })[];
338
+ };
339
+ _renormalizeSpectators(): void;
340
+ _diff(): Float64Array<ArrayBuffer>;
341
+ }
342
+ import { BlockTridiagonal } from './blockTridiagonal.js';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pieces of a definition (species and materials, as in a library entry) merged into one. A
3
+ * species or material given twice must be given identically.
4
+ * @param {...{ species?: object[], materials?: Record<string, object> }} parts
5
+ * @returns {{ species: object[], materials: Record<string, object> }}
6
+ */
7
+ export function combine(...parts: {
8
+ species?: object[];
9
+ materials?: Record<string, object>;
10
+ }[]): {
11
+ species: object[];
12
+ materials: Record<string, object>;
13
+ };
14
+ /**
15
+ * A layer of the stack: a region of `material`, `length` m long, with any other region fields
16
+ * (name, c0, grid, …) and the kit's doping shorthand `donors`, `acceptors` (mol/m³).
17
+ * @param {string} material
18
+ * @param {number} length m
19
+ * @param {object} [more]
20
+ */
21
+ export function layer(material: string, length: number, more?: object): any;
22
+ /**
23
+ * An ohmic contact: each carrier in equilibrium with a metal at the terminal voltage, the
24
+ * first being the terminal species, and every other sharing its voltage (offset 0, as holes and
25
+ * electrons do at a metal). The default, both e⁻ and h⁺, gives n·p = n_i² at the contact
26
+ * (infinite recombination); one carrier alone is a selective contact, and `['e-']` is a
27
+ * current collector on a metal region.
28
+ * @param {number | object} [drive] a voltage, a waveform, or { V }, { I }, { V, R }
29
+ * @param {string[]} [carriers] default ['e-', 'h+']
30
+ */
31
+ export function ohmic(d?: number, carriers?: string[]): any;
32
+ /**
33
+ * A bath: a neutral composition `c` (mol/m³) held at the contact, its levels referred to the
34
+ * charged `reference` species (the ion a reference electrode would sense, e.g. Cl⁻ for
35
+ * Ag/AgCl), which is the terminal.
36
+ * @param {Record<string, number>} c
37
+ * @param {string} reference
38
+ * @param {number | object} [drive]
39
+ * @param {{ offset?: number }} [opts] where the reference species sits relative to the terminal
40
+ * voltage, V_ref = V + offset (V)
41
+ */
42
+ export function bath(c: Record<string, number>, reference: string, d?: number, { offset }?: {
43
+ offset?: number;
44
+ }): any;
45
+ /**
46
+ * Build a plain device definition from a stack:
47
+ *
48
+ * build({ library: [Si], stack: [ohmic(0), layer('Si', 1e-6, { donors }), layer(...), ohmic(0.5)] })
49
+ *
50
+ * - `stack`: a contact, then layers (regions: objects with a `material`) and faces (anything
51
+ * else: an interface's fields) left to right, then a contact. Two adjacent layers with no face
52
+ * between them get the default face. Nested lists are flattened.
53
+ * - Layers may give doping as `donors` and `acceptors` (mol/m³) in place of `fixedCharge`.
54
+ * - `library`: pieces with species and materials, merged with `species` and `materials`.
55
+ * - `T`, `grid`, `ports` pass through.
56
+ * @param {object} def
57
+ * @returns {object} a device definition for `new Device`
58
+ */
59
+ export function build(def: object): object;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Validate a material's `statistics` list and build its models.
3
+ * @returns {{ models: object[], modelOf: Int32Array, warnings: string[], phiFree: boolean }}
4
+ */
5
+ export function normalizeStatistics(mat: any, path: any, species: any, speciesIndex: any, RT: any, h: any): {
6
+ models: object[];
7
+ modelOf: Int32Array;
8
+ warnings: string[];
9
+ phiFree: boolean;
10
+ };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * One line of a level diagram.
3
+ * @typedef {object} Trace
4
+ * @property {string} id e.g. 'V:e-', 'Vstd:e-', 'phi', 'level:0'
5
+ * @property {string} label e.g. 'V_{e⁻}', 'V°_{SO₄²⁻}' (subscripts marked TeX-style, as `labelParts()` in driftlet/plot reads them)
6
+ * @property {'level' | 'standard' | 'phi' | 'redox' | 'redox-standard'} kind a species voltage, its
7
+ * standard level, φ, or a half-reaction's level or standard level
8
+ * @property {string} [species]
9
+ * @property {'electron' | 'cation' | 'anion' | 'redox' | 'phi'} role what it is, for its colour:
10
+ * electrons, cations (holes too), anions, redox levels, φ
11
+ * @property {number} slot its colour within the role: a species' place among the solution's
12
+ * species of that role, so a colour follows its species whichever lines are shown; a redox
13
+ * level's place among the distinct half-reactions asked for (a couple's level and standard
14
+ * level share one)
15
+ * @property {number} [shift] a display offset added to all of a species' lines, V
16
+ * @property {Float64Array} y V, NaN where undefined (a break in the line)
17
+ */
18
+ /**
19
+ * A charged species' colour role and slot in level diagrams: electrons (`e-`), cations (holes
20
+ * too), or anions, and its place among the solution's species of that role.
21
+ * @param {import('./types.js').Solution} sol
22
+ * @param {string} name
23
+ * @returns {{ role: 'electron' | 'cation' | 'anion', slot: number }}
24
+ */
25
+ export function speciesRole(sol: import("./types.js").Solution, name: string): {
26
+ role: "electron" | "cation" | "anion";
27
+ slot: number;
28
+ };
29
+ /**
30
+ * A species name typeset for labels, given its charge: the charge as a superscript and the counts
31
+ * in its formula as subscripts ('SO42-' with z = −2 is 'SO₄²⁻', 'Fe3+' with z = 3 is 'Fe³⁺').
32
+ * A name that doesn't end in its charge is returned as it is.
33
+ * @param {string} name
34
+ * @param {number} z
35
+ * @returns {string}
36
+ */
37
+ export function typeset(name: string, z: number): string;
38
+ /**
39
+ * The level diagram of a solution as traces: per charged species, its voltage V_i (`level`) and
40
+ * its standard level V°_i (`standard`, the band edge for e⁻ and h⁺), optionally φ and
41
+ * half-reactions' levels. Doubled interface nodes share an x, so steps draw as vertical lines.
42
+ * @param {import('./types.js').Solution} sol
43
+ * @param {{ species?: string[], standard?: boolean, phi?: boolean | string[],
44
+ * levels?: { half: { equation: string, fixed?: Record<string, number> }, label?: string, standard?: boolean }[],
45
+ * labels?: Record<string, string>, shifts?: Record<string, number> }} [opts]
46
+ * species to show (default: every charged one), whether to show standard levels (default true)
47
+ * and φ (default false; or a list of regions, by name or material, to draw it in only, say an
48
+ * insulator's), half-reaction levels, labels to use by trace id
49
+ * ({ 'V:e-': 'Fermi level' }), and display offsets per species (V), which move all of a
50
+ * species' lines together so that widely separated species can share one readable plot
51
+ * @returns {{ x: Float64Array, series: Trace[], regions: { name: string, material: string, x0: number, x1: number }[],
52
+ * faces: number[], range: [number, number] }}
53
+ */
54
+ export function traces(sol: import("./types.js").Solution, { species, standard, phi, levels, labels, shifts }?: {
55
+ species?: string[];
56
+ standard?: boolean;
57
+ phi?: boolean | string[];
58
+ levels?: {
59
+ half: {
60
+ equation: string;
61
+ fixed?: Record<string, number>;
62
+ };
63
+ label?: string;
64
+ standard?: boolean;
65
+ }[];
66
+ labels?: Record<string, string>;
67
+ shifts?: Record<string, number>;
68
+ }): {
69
+ x: Float64Array;
70
+ series: Trace[];
71
+ regions: {
72
+ name: string;
73
+ material: string;
74
+ x0: number;
75
+ x1: number;
76
+ }[];
77
+ faces: number[];
78
+ range: [number, number];
79
+ };
80
+ /**
81
+ * One line of a level diagram.
82
+ */
83
+ export type Trace = {
84
+ /**
85
+ * e.g. 'V:e-', 'Vstd:e-', 'phi', 'level:0'
86
+ */
87
+ id: string;
88
+ /**
89
+ * e.g. 'V_{e⁻}', 'V°_{SO₄²⁻}' (subscripts marked TeX-style, as `labelParts()` in driftlet/plot reads them)
90
+ */
91
+ label: string;
92
+ /**
93
+ * a species voltage, its
94
+ * standard level, φ, or a half-reaction's level or standard level
95
+ */
96
+ kind: "level" | "standard" | "phi" | "redox" | "redox-standard";
97
+ species?: string;
98
+ /**
99
+ * what it is, for its colour:
100
+ * electrons, cations (holes too), anions, redox levels, φ
101
+ */
102
+ role: "electron" | "cation" | "anion" | "redox" | "phi";
103
+ /**
104
+ * its colour within the role: a species' place among the solution's
105
+ * species of that role, so a colour follows its species whichever lines are shown; a redox
106
+ * level's place among the distinct half-reactions asked for (a couple's level and standard
107
+ * level share one)
108
+ */
109
+ slot: number;
110
+ /**
111
+ * a display offset added to all of a species' lines, V
112
+ */
113
+ shift?: number;
114
+ /**
115
+ * V, NaN where undefined (a break in the line)
116
+ */
117
+ y: Float64Array;
118
+ };