driftlet 0.7.0 → 0.9.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/src/stack.js CHANGED
@@ -100,7 +100,7 @@ export function bath(c, reference, d = 0, { offset } = {}) {
100
100
  return { ...drive(d, 'bath'), bath: { c: { ...c }, reference, ...(offset === undefined ? {} : { offset }) } };
101
101
  }
102
102
 
103
- const STACK_FIELDS = ['T', 'library', 'species', 'materials', 'stack', 'bulkReactions', 'ports', 'grid'];
103
+ const STACK_FIELDS = ['T', 'library', 'species', 'materials', 'stack', 'bulkReactions', 'ports', 'grid', 'geometry'];
104
104
 
105
105
  /**
106
106
  * Build a plain device definition from a stack:
@@ -112,7 +112,7 @@ const STACK_FIELDS = ['T', 'library', 'species', 'materials', 'stack', 'bulkReac
112
112
  * between them get the default face. Nested lists are flattened.
113
113
  * - Layers may give doping as `donors` and `acceptors` (mol/m³) in place of `fixedCharge`.
114
114
  * - `library`: pieces with species and materials, merged with `species` and `materials`.
115
- * - `T`, `grid`, `ports` pass through.
115
+ * - `T`, `grid`, `ports`, `geometry` pass through.
116
116
  * @param {object} def
117
117
  * @returns {object} a device definition for `new Device`
118
118
  */
@@ -168,5 +168,6 @@ export function build(def) {
168
168
  if (def.bulkReactions !== undefined) out.bulkReactions = def.bulkReactions;
169
169
  if (def.ports !== undefined) out.ports = def.ports;
170
170
  if (def.grid !== undefined) out.grid = def.grid;
171
+ if (def.geometry !== undefined) out.geometry = def.geometry;
171
172
  return out;
172
173
  }
package/src/types.js CHANGED
@@ -154,9 +154,20 @@
154
154
  * @property {Source} [V] held terminal voltage, V (default 0, if no I)
155
155
  * @property {Source} [I] or: the current driven into the device, A/m² (e.g. 0 for a reference electrode)
156
156
  * @property {number} [R] with V: a series resistance, Ω·m²
157
- * @property {string} [terminal] species whose offset defaults to 0
157
+ * @property {string} [terminal] species whose offset defaults to 0; with reactions, the electrode's carrier
158
158
  * @property {Record<string, 'blocked' | 'equilibrium' | { type: 'equilibrium', offset?: number, mu?: number }
159
- * | { type: 'conductance', G: number, offset?: number } | { type: 'exchange', k: number, mu: number }>} species
159
+ * | { type: 'conductance', G: number, offset?: number } | { type: 'exchange', k: number, mu: number }>} [species]
160
+ * @property {{ equation: string, fixed?: Record<string, number>, k0: number, alpha: number, bare?: boolean }[]} [reactions] an
161
+ * electrode spread through the window: Butler–Volmer per area (k0 in mol/(m²·s)) between the region's
162
+ * species and the terminal species at the port's level
163
+ * @property {Record<string, { mu0: number, capacity: number, theta0?: number }>} [surface] with reactions: species
164
+ * on the electrode's sites, Langmuir (μ = μ° + RT ln(θ/θ₀)), capacity in mol of sites per m² of electrode,
165
+ * shared; a reaction with `bare: true` runs only on the bare fraction θ₀ = 1 − Σθ
166
+ * @property {{ C: number, zeroCharge?: number }} [capacitance] a capacitance spread through the window (F/m² of
167
+ * electrode): σ = C (V − zeroCharge − φ) on the port's side, aσ in each node's charge balance; a gate
168
+ * along a channel, a double layer
169
+ * @property {number | { x: number[], values: number[] }} [area] with reactions or a capacitance: electrode area per volume,
170
+ * m²/m³ (1/h for a film of thickness h), or a profile against the device's x
160
171
  */
161
172
 
162
173
  /**
@@ -170,6 +181,10 @@
170
181
  * @property {BulkReactionDef[]} [bulkReactions]
171
182
  * @property {{ left?: ContactDef, right?: ContactDef }} [contacts]
172
183
  * @property {PortDef[]} [ports] internal ports
184
+ * @property {'planar' | 'spherical' | 'cylindrical' | { type: 'planar' | 'spherical' | 'cylindrical', r0?: number }
185
+ * | { area: { x: number[], values: number[] } }} [geometry] the cross-section A(x): planar (A = 1 m², the
186
+ * default: currents read per m²), shells about a centre at r = r0 (m) left of x = 0 (cylindrical: per
187
+ * metre of length), or A in m² against x. Currents, fluxes, amounts and charges are then totals through A.
173
188
  * @property {GridOptions} [grid]
174
189
  */
175
190
 
@@ -199,8 +214,11 @@
199
214
  * then the ports by name): its voltage, and its current into the device (A/m²)
200
215
  * @property {{ left: ContactResult, right: ContactResult }} contacts
201
216
  * @property {{ left?: { V: number, D: number, charge: number }, right?: { V: number, D: number, charge: number } }} gates
202
- * @property {{ name: string, V: number, flux: Record<string, number>, current: number }[]} ports what each
203
- * internal port brings into the device
217
+ * @property {{ name: string, V: number, flux: Record<string, number>, current: number, x?: Float64Array,
218
+ * area?: Float64Array, rates?: Float64Array[], coverage?: Record<string, Float64Array>, bare?: Float64Array,
219
+ * sigma?: Float64Array, charge?: number }[]} ports what each internal port brings into the device;
220
+ * with reactions, also its window's nodes (x, m), area per volume there, and each reaction's rate per area
221
+ * at each node (mol/(m²·s), forward)
204
222
  * @property {{ left: string, right: string, dipole: number, sheetCharge: number, D: number, N: Record<string, number>, rates: number[] }[]} interfaces
205
223
  * @property {{ rate: Float64Array, regions: number[], total: number }[]} bulkReactions each bulk reaction's
206
224
  * forward rate per node (mol/(m³·s), NaN where it doesn't run), and per region and in all (mol/(m²·s))
package/types/grid.d.ts CHANGED
@@ -26,3 +26,32 @@ export function buildGrid(regions: {
26
26
  regionEnd: Int32Array<ArrayBuffer>;
27
27
  length: number;
28
28
  };
29
+ /**
30
+ * Give a grid its cross-section: `area` (A at each node, m²), `segArea` (each segment's
31
+ * effective area, its length over ∫dx/A, so that a flux D·segArea·Δc/h is exact for steady
32
+ * diffusion between the nodes; A at its middle where A vanishes at an end, as at a sphere's
33
+ * centre) and `vol` (each node's box, ∫A dx). Planar (no geometry): A = 1, and nothing changes.
34
+ * @param {ReturnType<typeof buildGrid>} grid
35
+ * @param {{ type: 'planar' } | { type: 'spherical' | 'cylindrical', r0: number } | { type: 'profile', x: ArrayLike<number>, values: ArrayLike<number> }} geometry
36
+ */
37
+ export function applyGeometry(grid: ReturnType<typeof buildGrid>, geometry: {
38
+ type: "planar";
39
+ } | {
40
+ type: "spherical" | "cylindrical";
41
+ r0: number;
42
+ } | {
43
+ type: "profile";
44
+ x: ArrayLike<number>;
45
+ values: ArrayLike<number>;
46
+ }): {
47
+ nNodes: number;
48
+ x: Float64Array<ArrayBuffer>;
49
+ nodeRegion: Int32Array<ArrayBuffer>;
50
+ segLength: Float64Array<ArrayBuffer>;
51
+ segRegion: Int32Array<ArrayBuffer>;
52
+ segFace: Int32Array<ArrayBuffer>;
53
+ vol: Float64Array<ArrayBuffer>;
54
+ regionStart: Int32Array<ArrayBuffer>;
55
+ regionEnd: Int32Array<ArrayBuffer>;
56
+ length: number;
57
+ };
package/types/kit.d.ts CHANGED
@@ -36,6 +36,7 @@ export function vacuumZeroCharge(def: object, workFunction: number, inside: Vacu
36
36
  export { live } from "./live.js";
37
37
  export { recorder } from "./recorder.js";
38
38
  export { check } from "./checks.js";
39
+ export { polarization } from "./polarization.js";
39
40
  /**
40
41
  * One material seen from vacuum: a level inside it (the anchor) and how far above that level,
41
42
  * in electron energy, the vacuum level just outside lies (the offset, V). So
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Each electrode reaction's rate at a face (a metal's) or at one spot of an electrode port, with
3
+ * the composition beside it as in `sol` and the metal's carrier at each level in `V` (in the
4
+ * terminals' convention, V = μ̄/(zF); against SHE, subtract φ for the usual tables' ions). The
5
+ * rates are the solver's own law, so at the electrode's actual level they are
6
+ * `sol.interfaces[f].rates` (or `sol.ports[k].rates` at that node).
7
+ * @param {import('./index.js').Device} device
8
+ * @param {object} sol a solution of it
9
+ * @param {{ face: number } | { port: string | number, x?: number }} where a face's index, or a port
10
+ * (name or index) and the position in its window (m, the device's x; default its first node)
11
+ * @param {number | ArrayLike<number>} V the metal's level(s), V
12
+ * @returns {{ V: Float64Array, current: Float64Array, reactions: { equation: string, level: number,
13
+ * rate: Float64Array, current: Float64Array }[] }} per reaction: its equilibrium level here (where
14
+ * its rate is zero), its rate (mol/(m²·s) of electrode, forward) and its current (A/m², anodic
15
+ * positive: into the solution); and their sum
16
+ */
17
+ export function polarization(device: import("./index.js").Device, sol: object, where: {
18
+ face: number;
19
+ } | {
20
+ port: string | number;
21
+ x?: number;
22
+ }, V: number | ArrayLike<number>): {
23
+ V: Float64Array;
24
+ current: Float64Array;
25
+ reactions: {
26
+ equation: string;
27
+ level: number;
28
+ rate: Float64Array;
29
+ current: Float64Array;
30
+ }[];
31
+ };
package/types/solver.d.ts CHANGED
@@ -8,12 +8,19 @@ export class Solver {
8
8
  n: any;
9
9
  M: any;
10
10
  nRx: any;
11
+ nSurf: any;
11
12
  nNodes: any;
12
13
  nFaces: number;
13
14
  nB: any;
14
15
  VT: number;
15
16
  blockOfNode: Int32Array<any>;
16
17
  blockOfFace: Int32Array<ArrayBuffer>;
18
+ surfPort: Int32Array<any>;
19
+ surfW: Int32Array<any>;
20
+ th: Float64Array<ArrayBuffer>;
21
+ thOld: Float64Array<ArrayBuffer>;
22
+ th0: Float64Array<any>;
23
+ th0Old: Float64Array<any>;
17
24
  present: Uint8Array<ArrayBuffer>;
18
25
  cRef: Float64Array<ArrayBuffer>;
19
26
  mu0hat: Float64Array<ArrayBuffer>;
@@ -54,6 +61,10 @@ export class Solver {
54
61
  left: number;
55
62
  right: number;
56
63
  };
64
+ portQ: Float64Array<any>;
65
+ portQStart: Float64Array<any>;
66
+ portQOld: Float64Array<any>;
67
+ portQEnd: any;
57
68
  contactDOld: {
58
69
  left: number;
59
70
  right: number;
@@ -77,6 +88,8 @@ export class Solver {
77
88
  history: any[];
78
89
  dtNext: any;
79
90
  rxs: any;
91
+ portArea: any;
92
+ portRates: any;
80
93
  strictlyNeutral: any;
81
94
  generationScale: number;
82
95
  hasGeneration: any;
@@ -119,7 +132,7 @@ export class Solver {
119
132
  redrive(): void;
120
133
  deltaV: Float64Array<any>;
121
134
  _refreshSources(): void;
122
- /** η_i (μ̄/RT) of a port's outside level for species i. */
135
+ /** η_i (μ̄/RT) of a port's outside level for species i (its electrode's carrier: at V itself). */
123
136
  portEta(port: any, i: any): number;
124
137
  /** η_i/RT that a fixed contact link imposes (or NaN if the link isn't fixed). */
125
138
  contactEta(side: any, i: any): number;
@@ -129,7 +142,9 @@ export class Solver {
129
142
  * profile varies), or continued across the interface dipole if nothing there responds.
130
143
  */
131
144
  initFromComposition(): void;
145
+ _portRate(port: any, rx: any, g: any): number;
132
146
  _materialAt(mat: any, background: any, eta: any, mode: any, cFix: any, ph: any, zeta: any, cc: any): void;
147
+ _coverages(): void;
133
148
  computeConcentrations(): void;
134
149
  _work(md: any): any;
135
150
  _nodeStatistics(g: any, b: any, phiHat: any): void;
@@ -165,11 +180,14 @@ export class Solver {
165
180
  Cp: number;
166
181
  Ca: number;
167
182
  };
168
- _faceReaction(rx: any, k: any, f: any, bf: any, bL: any, bR: any): void;
183
+ _faceReaction(rx: any, k: any, f: any, bf: any, bL: any, bR: any, Af: any): void;
169
184
  _terminals(dt: any): void;
170
185
  _circuit(k: any): void;
171
186
  _captureRow(b: any, s: any, w: any, into: any): number;
172
- _port(port: any, flux: any, k: any): void;
187
+ _port(port: any, flux: any, k: any, dt: any): void;
188
+ _portReaction(port: any, rx: any, area: any, rates: any, flux: any, k: any): void;
189
+ _portCapacitance(port: any, k: any, dt: any): void;
190
+ _surfaceStorage(port: any, dt: any): void;
173
191
  _contact(side: any, dt: any): void;
174
192
  _replaceRow(b: any, rs: any): void;
175
193
  _equilibrate(): void;
@@ -253,6 +271,8 @@ export class Solver {
253
271
  u: Float64Array<ArrayBuffer>;
254
272
  uLo: Float64Array<ArrayBuffer>;
255
273
  cOld: Float64Array<ArrayBuffer>;
274
+ thOld: Float64Array<ArrayBuffer>;
275
+ th0Old: Float64Array<ArrayBuffer>;
256
276
  time: number;
257
277
  lastDt: number;
258
278
  atSteady: boolean;
@@ -265,6 +285,9 @@ export class Solver {
265
285
  right: number;
266
286
  };
267
287
  contactDEnd: any;
288
+ portQStart: Float64Array<any>;
289
+ portQOld: Float64Array<any>;
290
+ portQEnd: any;
268
291
  boundaryIntake: Float64Array<ArrayBuffer>;
269
292
  history: any[];
270
293
  termV: Float64Array<ArrayBuffer>;
package/types/stack.d.ts CHANGED
@@ -53,7 +53,7 @@ export function bath(c: Record<string, number>, reference: string, d?: number, {
53
53
  * between them get the default face. Nested lists are flattened.
54
54
  * - Layers may give doping as `donors` and `acceptors` (mol/m³) in place of `fixedCharge`.
55
55
  * - `library`: pieces with species and materials, merged with `species` and `materials`.
56
- * - `T`, `grid`, `ports` pass through.
56
+ * - `T`, `grid`, `ports`, `geometry` pass through.
57
57
  * @param {object} def
58
58
  * @returns {object} a device definition for `new Device`
59
59
  */
package/types/types.d.ts CHANGED
@@ -354,10 +354,10 @@ export type PortDef = {
354
354
  */
355
355
  R?: number;
356
356
  /**
357
- * species whose offset defaults to 0
357
+ * species whose offset defaults to 0; with reactions, the electrode's carrier
358
358
  */
359
359
  terminal?: string;
360
- species: Record<string, "blocked" | "equilibrium" | {
360
+ species?: Record<string, "blocked" | "equilibrium" | {
361
361
  type: "equilibrium";
362
362
  offset?: number;
363
363
  mu?: number;
@@ -370,6 +370,45 @@ export type PortDef = {
370
370
  k: number;
371
371
  mu: number;
372
372
  }>;
373
+ /**
374
+ * an
375
+ * electrode spread through the window: Butler–Volmer per area (k0 in mol/(m²·s)) between the region's
376
+ * species and the terminal species at the port's level
377
+ */
378
+ reactions?: {
379
+ equation: string;
380
+ fixed?: Record<string, number>;
381
+ k0: number;
382
+ alpha: number;
383
+ bare?: boolean;
384
+ }[];
385
+ /**
386
+ * with reactions: species
387
+ * on the electrode's sites, Langmuir (μ = μ° + RT ln(θ/θ₀)), capacity in mol of sites per m² of electrode,
388
+ * shared; a reaction with `bare: true` runs only on the bare fraction θ₀ = 1 − Σθ
389
+ */
390
+ surface?: Record<string, {
391
+ mu0: number;
392
+ capacity: number;
393
+ theta0?: number;
394
+ }>;
395
+ /**
396
+ * a capacitance spread through the window (F/m² of
397
+ * electrode): σ = C (V − zeroCharge − φ) on the port's side, aσ in each node's charge balance; a gate
398
+ * along a channel, a double layer
399
+ */
400
+ capacitance?: {
401
+ C: number;
402
+ zeroCharge?: number;
403
+ };
404
+ /**
405
+ * with reactions or a capacitance: electrode area per volume,
406
+ * m²/m³ (1/h for a film of thickness h), or a profile against the device's x
407
+ */
408
+ area?: number | {
409
+ x: number[];
410
+ values: number[];
411
+ };
373
412
  };
374
413
  /**
375
414
  * A device definition: plain, serialisable data.
@@ -398,6 +437,20 @@ export type DeviceDefinition = {
398
437
  * internal ports
399
438
  */
400
439
  ports?: PortDef[];
440
+ /**
441
+ * the cross-section A(x): planar (A = 1 m², the
442
+ * default: currents read per m²), shells about a centre at r = r0 (m) left of x = 0 (cylindrical: per
443
+ * metre of length), or A in m² against x. Currents, fluxes, amounts and charges are then totals through A.
444
+ */
445
+ geometry?: "planar" | "spherical" | "cylindrical" | {
446
+ type: "planar" | "spherical" | "cylindrical";
447
+ r0?: number;
448
+ } | {
449
+ area: {
450
+ x: number[];
451
+ values: number[];
452
+ };
453
+ };
401
454
  grid?: GridOptions;
402
455
  };
403
456
  export type ContactResult = {
@@ -500,14 +553,22 @@ export type Solution = {
500
553
  };
501
554
  };
502
555
  /**
503
- * what each
504
- * internal port brings into the device
556
+ * what each internal port brings into the device;
557
+ * with reactions, also its window's nodes (x, m), area per volume there, and each reaction's rate per area
558
+ * at each node (mol/(m²·s), forward)
505
559
  */
506
560
  ports: {
507
561
  name: string;
508
562
  V: number;
509
563
  flux: Record<string, number>;
510
564
  current: number;
565
+ x?: Float64Array;
566
+ area?: Float64Array;
567
+ rates?: Float64Array[];
568
+ coverage?: Record<string, Float64Array>;
569
+ bare?: Float64Array;
570
+ sigma?: Float64Array;
571
+ charge?: number;
511
572
  }[];
512
573
  interfaces: {
513
574
  left: string;