driftlet 1.3.2 → 1.4.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 +1 -1
- package/docs/device.md +79 -6
- package/docs/internals.md +5 -2
- package/docs/kit.md +7 -3
- package/docs/numerics.md +11 -0
- package/docs/statistics.md +7 -2
- package/docs/templates.md +157 -2
- package/llms.txt +19 -14
- package/package.json +1 -1
- package/src/boundaries.js +200 -14
- package/src/checks.js +13 -2
- package/src/describe.js +3 -1
- package/src/device.js +70 -5
- package/src/diffuse.js +240 -0
- package/src/impedance.js +2 -1
- package/src/kit.js +6 -2
- package/src/lawRows.js +26 -1
- package/src/newton.js +31 -5
- package/src/regions.js +6 -3
- package/src/solution.js +46 -6
- package/src/solver.js +87 -9
- package/src/start.js +37 -8
- package/src/statistics.js +1 -0
- package/src/steady.js +51 -22
- package/src/timeSteps.js +10 -6
- package/src/types.js +16 -2
- package/types/boundaries.d.ts +8 -0
- package/types/diffuse.d.ts +25 -0
- package/types/newton.d.ts +1 -1
- package/types/solver.d.ts +8 -2
- package/types/types.d.ts +36 -1
package/src/solver.js
CHANGED
|
@@ -10,8 +10,9 @@ import { Assembly } from './assemble.js';
|
|
|
10
10
|
import { BlockTridiagonal } from './blockTridiagonal.js';
|
|
11
11
|
import { bernoulli, SG_FAR, sgFlux } from './bernoulli.js';
|
|
12
12
|
import { BoundaryTerms } from './boundaries.js';
|
|
13
|
-
import { FARADAY } from './constants.js';
|
|
13
|
+
import { EPS0, FARADAY } from './constants.js';
|
|
14
14
|
import { sourceAt } from './device.js';
|
|
15
|
+
import { diffuseScratch, diffuseUnits } from './diffuse.js';
|
|
15
16
|
import { DeviceError } from './errors.js';
|
|
16
17
|
import { conservedMoieties, findLaws, noCurrent } from './laws.js';
|
|
17
18
|
import { newton as newtonFn } from './newton.js';
|
|
@@ -60,15 +61,29 @@ export class SolverError extends Error {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
// The species a face's diffuse layer on that side (0 left, 1 right) passes to its reactions:
|
|
65
|
+
// each participant there that the layer holds (charged, or on its lattice), but SRH's (refused
|
|
66
|
+
// beside a layer), in species order. A flux across the layer lowers each one's level at the plane.
|
|
67
|
+
function drawSpecies(itf, side, species) {
|
|
68
|
+
const lat = itf.diffuse.lattice[side], out = new Set();
|
|
69
|
+
for (const rx of itf.reactions) {
|
|
70
|
+
if (rx.srh) continue;
|
|
71
|
+
for (const p of rx.part) if (p.side === side && (species[p.i].z !== 0 || lat?.on[p.i])) out.add(p.i);
|
|
72
|
+
}
|
|
73
|
+
return [...out].sort((a, b) => a - b);
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
export class Solver {
|
|
64
77
|
constructor(model) {
|
|
65
78
|
this.model = model;
|
|
66
79
|
const { grid, species, materials, regions, contacts } = model;
|
|
67
80
|
const n = species.length;
|
|
68
81
|
// Slots per block: φ̂ (or D, or a conductor's segment flux J), one per species, and at a face
|
|
69
|
-
// one per face reaction (its rate)
|
|
70
|
-
//
|
|
71
|
-
|
|
82
|
+
// one per face reaction (its rate), one per gate (its fraction open) and one per diffuse
|
|
83
|
+
// layer (its drop) and one per species that layer passes to a face reaction (its level at the
|
|
84
|
+
// plane: drawSpecies), as many as the busiest face has.
|
|
85
|
+
const nDiff = (itf) => (itf.diffuse ? [0, 1].reduce((m, side) => m + ((side ? itf.diffuse.right : itf.diffuse.left) ? 1 + drawSpecies(itf, side, species).length : 0), 0) : 0);
|
|
86
|
+
const nRx = model.interfaces.reduce((m, itf) => Math.max(m, itf.reactions.length + itf.gates.length + nDiff(itf)), 0);
|
|
72
87
|
// (and at an electrode port's nodes, one per surface species: its coverage's η)
|
|
73
88
|
const nSurf = model.ports.reduce((m, port) => Math.max(m, port.surface.length), 0);
|
|
74
89
|
// (and a membrane port's gates, after them: each one's fraction open)
|
|
@@ -122,6 +137,36 @@ export class Solver {
|
|
|
122
137
|
let j = 0;
|
|
123
138
|
for (let f = 0; f < nFaces; j += model.interfaces[f].gates.length, f++) this.gateIndex[f] = j;
|
|
124
139
|
this.portGateIndex = model.ports.map((port) => ((j += port.nodes.length * port.gates.length), j - port.nodes.length * port.gates.length));
|
|
140
|
+
// Diffuse layers (src/diffuse.js): one per strictly neutral side of a face that has them, in
|
|
141
|
+
// face order, left side first. Each: its face f, side (0 left, 1 right), edge node g and its
|
|
142
|
+
// block b, its drop s = Fψ/RT in slot o of the face's block (after the gates), the layer's
|
|
143
|
+
// constants, its lattice (crowded: { cMax, on }, the species on it; or null, dilute), which
|
|
144
|
+
// species it holds an excess of (the charged, and a lattice's neutral ones, displaced), and
|
|
145
|
+
// its scratch (q, Γ and their derivatives, D, from computeConcentrations). Γ
|
|
146
|
+
// [d·n + i], the excesses, now and at the step's start; diffAt[f] = [left d, right d] (−1: none).
|
|
147
|
+
// And the species it passes to the face's reactions (the drawn: each one's level at the plane
|
|
148
|
+
// in slot drawSlot[i], after both sides' drops, −1 where not drawn; draw[i] = 1 for them), and
|
|
149
|
+
// their diffusivities there, Dm.
|
|
150
|
+
this.diffList = [];
|
|
151
|
+
this.diffAt = model.interfaces.map(() => [-1, -1]);
|
|
152
|
+
model.interfaces.forEach((itf, f) => {
|
|
153
|
+
if (!itf.diffuse) return;
|
|
154
|
+
let slot = 1 + n + itf.reactions.length + itf.gates.length;
|
|
155
|
+
const sides = [0, 1].filter((side) => (side ? itf.diffuse.right : itf.diffuse.left));
|
|
156
|
+
let level = slot + sides.length;
|
|
157
|
+
for (const side of sides) {
|
|
158
|
+
const g = side ? grid.regionStart[f + 1] : grid.regionEnd[f];
|
|
159
|
+
this.diffAt[f][side] = this.diffList.length;
|
|
160
|
+
const lat = itf.diffuse.lattice[side], drawSlot = new Int32Array(n).fill(-1);
|
|
161
|
+
for (const i of drawSpecies(itf, side, species)) drawSlot[i] = level++;
|
|
162
|
+
const draw = drawSlot.some((q) => q >= 0) ? Uint8Array.from(drawSlot, (q) => (q >= 0 ? 1 : 0)) : null;
|
|
163
|
+
const Dm = model.materials[regions[grid.nodeRegion[g]].material].D;
|
|
164
|
+
this.diffList.push({ f, side, g, b: this.blockOfNode[g], slot, o: this.blockOfFace[f] * M + slot, units: diffuseUnits(itf.diffuse.epsr * EPS0, model.RT, FARADAY), dl: diffuseScratch(n), lat, holds: Uint8Array.from(species, (sp, i) => (sp.z !== 0 || lat?.on[i] ? 1 : 0)), drawSlot, draw, Dm });
|
|
165
|
+
slot++;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
this.gam = new Float64Array(this.diffList.length * n);
|
|
169
|
+
this.gamOld = new Float64Array(this.diffList.length * n);
|
|
125
170
|
|
|
126
171
|
// Per-node material data, flattened [g·n + i].
|
|
127
172
|
this.present = new Uint8Array(nNodes * n);
|
|
@@ -177,6 +222,7 @@ export class Solver {
|
|
|
177
222
|
// A face with φ undefined on both sides holds no charge, whatever its law says.
|
|
178
223
|
model.interfaces.forEach((itf, f) => {
|
|
179
224
|
if (itf.phi.type !== 'neutral' && this.phiUndefined[grid.regionEnd[f]] && this.phiUndefined[grid.regionEnd[f] + 1]) itf.phi = { type: 'neutral' };
|
|
225
|
+
// (a diffuse side has ions, so φ is defined there: the face above never has one)
|
|
180
226
|
});
|
|
181
227
|
|
|
182
228
|
// Unknowns as compensated double-doubles, u + uLo. Only differences of η need the extra
|
|
@@ -438,6 +484,10 @@ export class Solver {
|
|
|
438
484
|
for (let i = 0; i < n; i++) active[bf * M + 1 + i] = itf.links[i].type === 'blocked' ? 0 : 1;
|
|
439
485
|
for (let k = 0; k < itf.reactions.length + itf.gates.length; k++) active[bf * M + 1 + n + k] = 1;
|
|
440
486
|
});
|
|
487
|
+
for (const d of this.diffList) {
|
|
488
|
+
active[d.o] = 1;
|
|
489
|
+
for (const q of d.drawSlot) if (q >= 0) active[this.blockOfFace[d.f] * M + q] = 1;
|
|
490
|
+
}
|
|
441
491
|
const sizes = new Int32Array(nB), loc = (this.loc = new Int32Array(nB * M).fill(-1));
|
|
442
492
|
for (let b = 0; b < nB; b++) for (let r = 0; r < M; r++) if (active[b * M + r]) loc[b * M + r] = sizes[b]++;
|
|
443
493
|
const sys = (this.sys = new BlockTridiagonal(nB, sizes));
|
|
@@ -624,6 +674,18 @@ export class Solver {
|
|
|
624
674
|
this.contactLaw = found.contactLaw;
|
|
625
675
|
this.carriers = found.carriers;
|
|
626
676
|
this.unkept = found.unkept; // (laws no row here can keep: see _directSteady)
|
|
677
|
+
// (room in each law's derivative for the diffuse layers' excesses, which src/laws.js, reading
|
|
678
|
+
// the structure alone, doesn't count: each layer's drop and its edge node's slots)
|
|
679
|
+
if (this.diffList.length > 0) {
|
|
680
|
+
const more = this.diffList.length * (this.M + 1);
|
|
681
|
+
for (const cs of [...this.constraints, ...this.timeLaws]) {
|
|
682
|
+
const idx = new Int32Array(cs.idx.length + more), w = new Float64Array(cs.w.length + more);
|
|
683
|
+
idx.set(cs.idx);
|
|
684
|
+
w.set(cs.w);
|
|
685
|
+
cs.idx = idx;
|
|
686
|
+
cs.w = w;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
627
689
|
this.contactQ = [null, null]; // (each contact's charge as _contact captures it, for contactLaw)
|
|
628
690
|
this.pinnedIslands = [];
|
|
629
691
|
this.islandsOn = false; // (newton() turns them on, see there)
|
|
@@ -652,12 +714,22 @@ export class Solver {
|
|
|
652
714
|
return port.surface[0].capacity * s;
|
|
653
715
|
}
|
|
654
716
|
|
|
655
|
-
/** Total amount (mol per unit area) of a stretch's species in the current state. */
|
|
717
|
+
/** Total amount (mol per unit area) of a stretch's species in the current state, its faces' diffuse layers' included. */
|
|
656
718
|
amount(stretch) {
|
|
657
719
|
const { n, c, model } = this;
|
|
658
720
|
const vol = model.grid.vol;
|
|
659
721
|
let s = 0;
|
|
660
722
|
for (let g = stretch.nodes[0]; g <= stretch.nodes[1]; g++) s += vol[g] * c[g * n + stretch.species];
|
|
723
|
+
return s + this._diffuseHeld(stretch, this.gam);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// What the faces' diffuse layers hold of a stretch's species (mol, per area if planar), by the
|
|
727
|
+
// excesses gam (now, or gamOld at the step's start).
|
|
728
|
+
_diffuseHeld(stretch, gam) {
|
|
729
|
+
let s = 0;
|
|
730
|
+
this.diffList.forEach((dl, k) => {
|
|
731
|
+
if (dl.g >= stretch.nodes[0] && dl.g <= stretch.nodes[1]) s += this.model.grid.area[dl.g] * gam[k * this.n + stretch.species];
|
|
732
|
+
});
|
|
661
733
|
return s;
|
|
662
734
|
}
|
|
663
735
|
|
|
@@ -848,6 +920,7 @@ export class Solver {
|
|
|
848
920
|
cOld: Float64Array.from(this.cOld),
|
|
849
921
|
thOld: Float64Array.from(this.thOld),
|
|
850
922
|
th0Old: Float64Array.from(this.th0Old),
|
|
923
|
+
gamOld: Float64Array.from(this.gamOld),
|
|
851
924
|
gateOld: Float64Array.from(this.gateOld),
|
|
852
925
|
time: this.time,
|
|
853
926
|
lastDt: this.lastDt,
|
|
@@ -872,6 +945,7 @@ export class Solver {
|
|
|
872
945
|
this.cOld.set(s.cOld);
|
|
873
946
|
this.thOld.set(s.thOld);
|
|
874
947
|
this.th0Old.set(s.th0Old);
|
|
948
|
+
this.gamOld.set(s.gamOld);
|
|
875
949
|
this.gateOld.set(s.gateOld);
|
|
876
950
|
this.time = s.time;
|
|
877
951
|
this.lastDt = s.lastDt;
|
|
@@ -972,19 +1046,22 @@ export class Solver {
|
|
|
972
1046
|
if (link.type !== 'permeability') return null;
|
|
973
1047
|
const kl = gl * n + i, kr = gr * n + i;
|
|
974
1048
|
const shift = this.mu0hat[kr] - this.mu0hat[kl] - Math.log(this.cRef[kr] / this.cRef[kl]);
|
|
975
|
-
|
|
1049
|
+
// (between the diffuse layers' planes, where the face has them: see _face)
|
|
1050
|
+
const [dL, dR] = this.diffAt[f], sL = dL < 0 ? 0 : u[this.diffList[dL].o], sR = dR < 0 ? 0 : u[this.diffList[dR].o];
|
|
1051
|
+
const cl = this.c[kl] * Math.exp(-z[i] * sL), cr = this.c[kr] * Math.exp(-z[i] * sR);
|
|
1052
|
+
const d = z[i] * (u[bR * M] - u[bL * M] + (uLo[bR * M] - uLo[bL * M]) + sR - sL) + shift;
|
|
976
1053
|
const deta = u[bR * M + 1 + i] - u[bL * M + 1 + i] + (uLo[bR * M + 1 + i] - uLo[bL * M + 1 + i]);
|
|
977
1054
|
const gated = link.gates ?? [];
|
|
978
1055
|
const P = link.P * (gated.length ? this._open(gated, (q) => u[this._gateSlot(f, q)]) : 1);
|
|
979
|
-
const right = P * bernoulli(d) *
|
|
1056
|
+
const right = P * bernoulli(d) * cl;
|
|
980
1057
|
// (far up a drift, right × e^Δη is 0·∞: the backward one is then sgFlux's, whole)
|
|
981
|
-
return [right, d < SG_FAR ? right * Math.exp(deta) : sgFlux(P, d, deta,
|
|
1058
|
+
return [right, d < SG_FAR ? right * Math.exp(deta) : sgFlux(P, d, deta, cl, cr, this.sg)[3]];
|
|
982
1059
|
});
|
|
983
1060
|
}
|
|
984
1061
|
|
|
985
1062
|
// Add this step's contact fluxes (at the converged state) to each stretch's intake.
|
|
986
1063
|
// (A BDF2 step also moves each amount by Σ v (c* − c_n), its history term.)
|
|
987
|
-
_accumulateBoundaryIntake(dt, cN) {
|
|
1064
|
+
_accumulateBoundaryIntake(dt, cN, gamN = this.gamOld) {
|
|
988
1065
|
this._assembleBookkeeping(dt);
|
|
989
1066
|
this.contactDEnd = { ...this.contactD };
|
|
990
1067
|
this.portQEnd = Float64Array.from(this.portQ);
|
|
@@ -1008,6 +1085,7 @@ export class Solver {
|
|
|
1008
1085
|
if (driven.has(k)) q = driven.get(k);
|
|
1009
1086
|
let hist = 0;
|
|
1010
1087
|
for (let g = st.nodes[0]; g <= st.nodes[1]; g++) hist += vol[g] * (cOld[g * n + st.species] - cN[g * n + st.species]);
|
|
1088
|
+
hist += this._diffuseHeld(st, this.gamOld) - this._diffuseHeld(st, gamN);
|
|
1011
1089
|
this.boundaryIntake[k] += q * dt + hist;
|
|
1012
1090
|
});
|
|
1013
1091
|
}
|
package/src/start.js
CHANGED
|
@@ -29,9 +29,11 @@
|
|
|
29
29
|
// otherwise it's the running φ̂, or where a conductor on the left or an electrode spread
|
|
30
30
|
// through the region has a reaction that can balance, the φ̂ that balances it (by bisection;
|
|
31
31
|
// a trial a non-ideal model refuses counts its ideal charge, which has the sign there). Where
|
|
32
|
-
// neutrality is out of reach (c0's amounts leave a charge no held level balances)
|
|
33
|
-
//
|
|
34
|
-
// fails from it to say why)
|
|
32
|
+
// neutrality is out of reach (c0's amounts leave a charge no held level balances): in a
|
|
33
|
+
// strictly neutral region the running φ̂, the region noted in s.unbalanced (for a transient
|
|
34
|
+
// that fails from it to say why); otherwise the φ̂ nearest the running one, toward less
|
|
35
|
+
// charge, where the charge is at most ε V_T/(F L²) (L the region's length: what it holds with
|
|
36
|
+
// a thermal voltage across it), or where it stops falling (a fixed charge). A strictly
|
|
35
37
|
// neutral region (ε = 0) with every charged species' amount given must have a neutral c0:
|
|
36
38
|
// an error otherwise. A non-ideal material's composition comes from its statistics
|
|
37
39
|
// (materialAt).
|
|
@@ -63,7 +65,8 @@
|
|
|
63
65
|
// equilibrated by step 2 (a transient's first step), its amount over the two nodes is kept.
|
|
64
66
|
// 2. A species given by its level starts at the level of what feeds it, as step 1 orders them.
|
|
65
67
|
// 3. Where a level-fixed charged species responds, each node starts neutral (Σ z c + the fixed
|
|
66
|
-
// charge = 0) to the bisection's tolerance, unless neutrality is out of reach (step 1
|
|
68
|
+
// charge = 0) to the bisection's tolerance, unless neutrality is out of reach (step 1: then
|
|
69
|
+
// its charge at most ε V_T/(F L²), or the least φ̂ gives, to the bisection's tolerance); a
|
|
67
70
|
// strictly neutral region given by amounts is neutral by its c0, checked.
|
|
68
71
|
// 4. Steps 2 and 3 change no concentration but as they say: step 3 is a gauge (φ̂ and each η by
|
|
69
72
|
// z·Δ), step 2 moves the passing species between a face's two nodes only.
|
|
@@ -81,7 +84,7 @@
|
|
|
81
84
|
// Tests: every test (each device starts here); test/transient.test.js on what a start must be.
|
|
82
85
|
|
|
83
86
|
import { gateRate } from './boundaries.js';
|
|
84
|
-
import { FARADAY } from './constants.js';
|
|
87
|
+
import { EPS0, FARADAY } from './constants.js';
|
|
85
88
|
import { sourceAt } from './device.js';
|
|
86
89
|
import { profileAt, SolverError } from './solver.js';
|
|
87
90
|
|
|
@@ -260,13 +263,39 @@ export function initFromComposition(s) {
|
|
|
260
263
|
while (charge(lo) < 0 && lo > -1e4) lo -= 2 * (hi - lo);
|
|
261
264
|
while (charge(hi) > 0 && hi < 1e4) hi += 2 * (hi - lo);
|
|
262
265
|
// Out of reach (every responding carrier has one sign and nothing balances it, as in an
|
|
263
|
-
// undoped layer that holds only holes):
|
|
266
|
+
// undoped layer that holds only holes): not run to ±∞, but where the charge is no more than
|
|
267
|
+
// the region holds with a thermal voltage across it, ε V_T/(F L²), or as little as it gets
|
|
268
|
+
// (a fixed charge left). Kept at the running φ̂ (a contact's level), a hole-only layer 100
|
|
269
|
+
// nm thick started full of the anode's 1e27 m⁻³ holes, 7 kV of space charge: with traps in
|
|
270
|
+
// it, every step failed from there.
|
|
264
271
|
if (charge(lo) < 0 || charge(hi) > 0) {
|
|
265
272
|
// In a strictly neutral region that's a start that can't be: the amounts c0 gives leave
|
|
266
273
|
// a charge no level-held species can balance. Noted, for a transient that fails from it
|
|
267
274
|
// to say so (a steady solve, its levels from the contacts, can still find the state).
|
|
268
|
-
if (mat.epsr === 0)
|
|
269
|
-
|
|
275
|
+
if (mat.epsr === 0) {
|
|
276
|
+
s.unbalanced.push(r);
|
|
277
|
+
return ph;
|
|
278
|
+
}
|
|
279
|
+
const L = grid.x[grid.regionEnd[r]] - grid.x[grid.regionStart[r]];
|
|
280
|
+
const most = (mat.epsr * EPS0 * model.RT) / (FARADAY * FARADAY * L * L);
|
|
281
|
+
const q0 = charge(ph), dir = q0 > 0 ? 1 : -1; // (the charge falls as φ̂ rises)
|
|
282
|
+
if (!(Math.abs(q0) > most)) return ph;
|
|
283
|
+
let a = ph, qa = Math.abs(q0);
|
|
284
|
+
for (let step = 1; step < 1e4; step *= 2) {
|
|
285
|
+
let b = a + dir * step;
|
|
286
|
+
const qb = Math.abs(charge(b));
|
|
287
|
+
if (qb <= most) {
|
|
288
|
+
for (let it = 0; it < 100 && Math.abs(b - a) > 1e-9; it++) {
|
|
289
|
+
const m = 0.5 * (a + b);
|
|
290
|
+
if (Math.abs(charge(m)) > most) a = m;
|
|
291
|
+
else b = m;
|
|
292
|
+
}
|
|
293
|
+
return b;
|
|
294
|
+
}
|
|
295
|
+
if (!(qb < qa * (1 - 1e-9))) return a; // (a fixed charge: as little as it gets)
|
|
296
|
+
[a, qa] = [b, qb];
|
|
297
|
+
}
|
|
298
|
+
return a;
|
|
270
299
|
}
|
|
271
300
|
for (let it = 0; it < 200 && hi - lo > 1e-12; it++) {
|
|
272
301
|
const m = 0.5 * (lo + hi);
|
package/src/statistics.js
CHANGED
|
@@ -207,6 +207,7 @@ function latticeModel(idx, cRef, cMax) {
|
|
|
207
207
|
return {
|
|
208
208
|
type: 'lattice',
|
|
209
209
|
idx,
|
|
210
|
+
cMax, // (read by a face's diffuse layer beside it, which crowds on the same sites: src/diffuse.js)
|
|
210
211
|
// c_a = c_max w_a / (1 + Σ w), w_a = (c_ref,a / c_max) e^{ζ_a}; K = diag(c) − c cᵀ/c_max.
|
|
211
212
|
evaluate(zeta, c, K) {
|
|
212
213
|
let m = 0;
|
package/src/steady.js
CHANGED
|
@@ -49,11 +49,14 @@
|
|
|
49
49
|
// (this.stepped), on a direct device with continuation not off, the same again from a cold
|
|
50
50
|
// start (initFromComposition), whose u and uLo become the stages' start (termV stays the
|
|
51
51
|
// start's: a floating terminal keeps the voltage the transient left it at).
|
|
52
|
-
// 2. Continuation: generation ramped up (_generationContinuation);
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
// driven by a current,
|
|
52
|
+
// 2. Continuation: generation ramped up (_generationContinuation); terminals behind a
|
|
53
|
+
// resistance (_resistanceContinuation): one whose current Kirchhoff doesn't give, held at
|
|
54
|
+
// voltages marched to its load line's crossing (_floatingContinuation with a line); else, or
|
|
55
|
+
// where that fails, each held (at its source voltage, or where every other terminal is
|
|
56
|
+
// driven by a current at V_src − I R, Kirchhoff's I), then the resistances put back and the
|
|
57
|
+
// steady system solved from there (and where that fails for one terminal held by
|
|
58
|
+
// Kirchhoff, its load line); a terminal driven by a current, not bound (_bound), held at
|
|
59
|
+
// voltages instead (_floatingContinuation); with
|
|
57
60
|
// canContinue, the right contact's voltage ramped to its target (_continuation): from the
|
|
58
61
|
// voltages of the last converged solve (this.solvedV), the left contact's first where it
|
|
59
62
|
// moved, else from level contacts, a cold start.
|
|
@@ -69,7 +72,8 @@
|
|
|
69
72
|
// (∞ where the device can be solved directly; else 1e6·tau): a first step at ∞ that fails is
|
|
70
73
|
// retried once with maxStep 3. A step that fails sends dt down (to tau·1e-6 from giant, then
|
|
71
74
|
// ÷4, stopping below min(1e-15·tau, 1e-2·fastestTime)); one that converges sends it back up
|
|
72
|
-
// (×2 after a failure, growing by ×1.5 to ×10 a step; past 1e6·tau, giant
|
|
75
|
+
// (×2 after a failure, growing by ×1.5 to ×10 a step; past 1e6·tau, giant, or on a direct
|
|
76
|
+
// device past 1e12·tau); on a direct device,
|
|
73
77
|
// once dt ≥ tau, a step at ∞ is tried once a decade of dt, the state kept where it fails. Past
|
|
74
78
|
// giant (not direct), dt grows ×10 a step to 1e6·giant. Converged where a step at ∞ converged,
|
|
75
79
|
// or, not direct, where a step of dt ≥ giant moved the potentials under tol (finished by one
|
|
@@ -130,7 +134,7 @@
|
|
|
130
134
|
// here is more than u, uLo, termV and time: this.solvedV, this.stepped, whether the history
|
|
131
135
|
// is empty (this.violent) and the drives, all of which choose the stages.
|
|
132
136
|
// 5. Reach: time steps reach a mode as slow as ~1e12·tau (1e6·tau a step, past the giant; on a
|
|
133
|
-
// direct device
|
|
137
|
+
// direct device by finite steps to 1e12·tau). A reaction that alone settles a
|
|
134
138
|
// closed combination, slower than that, isn't waited out: the solve fails or converges where
|
|
135
139
|
// it began, and stuckReactions names it (Solution's warnings, both ways).
|
|
136
140
|
// Tests: every Device.solve goes through here; test/steady.test.js (the stages, the laws, the
|
|
@@ -462,8 +466,10 @@ export class SteadySolves {
|
|
|
462
466
|
}
|
|
463
467
|
}
|
|
464
468
|
|
|
465
|
-
//
|
|
466
|
-
//
|
|
469
|
+
// One terminal behind a resistance: its load line's crossing by held solves. Otherwise (or where
|
|
470
|
+
// that fails), each terminal behind a resistance held at its source voltage (R taken off; at
|
|
471
|
+
// V_src − I R where Kirchhoff gives I), solved, then the resistances put back and the steady
|
|
472
|
+
// system solved from that state.
|
|
467
473
|
_resistanceContinuation(opts, r) {
|
|
468
474
|
const ks = this.floating.filter((k) => this.terms[k].drive.kind === 'V' && this.terms[k].drive.R > 0);
|
|
469
475
|
const drives = ks.map((k) => this.terms[k].drive);
|
|
@@ -479,8 +485,29 @@ export class SteadySolves {
|
|
|
479
485
|
const I = -this.terms.reduce((t, tm, j) => (j === k ? t : t + sourceAt(tm.drive.src, this.time)), 0);
|
|
480
486
|
return V - I * d.R;
|
|
481
487
|
};
|
|
482
|
-
let heldOk = false;
|
|
483
488
|
const start = [Float64Array.from(this.u), Float64Array.from(this.uLo), Float64Array.from(this.termV)];
|
|
489
|
+
const restore = () => {
|
|
490
|
+
this.u.set(start[0]);
|
|
491
|
+
this.uLo.set(start[1]);
|
|
492
|
+
this.termV.set(start[2]);
|
|
493
|
+
this.computeConcentrations();
|
|
494
|
+
};
|
|
495
|
+
// One terminal behind R, the rest not all driven by currents (so its current isn't known):
|
|
496
|
+
// its crossing with the load line, (V_src − V)/R, by held solves, as an open circuit's is
|
|
497
|
+
// with its target, first. Held at its source instead, the state is I R from the answer and
|
|
498
|
+
// the pseudo-transient relaxes it over R C at Newton's pace through that swing (a lit Schottky
|
|
499
|
+
// diode passing 0.9 A/m²: 0.05 s at 1 Ω·m², 10 s at 1e4, 105 s at 1e5, steps ∝ R).
|
|
500
|
+
const d = drives[0], Vs = sourceAt(d.src, this.time), line = (V) => (Vs - V) / d.R;
|
|
501
|
+
const byLine = ks.length === 1 && !others;
|
|
502
|
+
if (byLine) {
|
|
503
|
+
const q = this._floatingContinuation(opts, r, ks[0], line);
|
|
504
|
+
if (q.converged) return q;
|
|
505
|
+
restore(); // (from where this stage began, not where the failed one left it)
|
|
506
|
+
steps = q.steps ?? steps;
|
|
507
|
+
iterations = q.iterations ?? iterations;
|
|
508
|
+
history.splice(0, history.length, ...(q.history ?? []));
|
|
509
|
+
}
|
|
510
|
+
let heldOk = false;
|
|
484
511
|
try {
|
|
485
512
|
const held = ks.map(at);
|
|
486
513
|
ks.forEach((k, j) => (this.terms[k].drive = { ...this.terms[k].drive, src: { value: held[j] }, R: 0 }));
|
|
@@ -497,16 +524,10 @@ export class SteadySolves {
|
|
|
497
524
|
}
|
|
498
525
|
const q = heldOk ? this._steadySteps(opts) : { converged: false, steps: 0, iterations: 0, history: [] };
|
|
499
526
|
const done = { ...q, steps: steps + q.steps, iterations: iterations + q.iterations, history: [...history, ...q.history] };
|
|
500
|
-
if (q.converged || ks.length !== 1) return done;
|
|
501
|
-
//
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const d = drives[0], Vs = sourceAt(d.src, this.time);
|
|
505
|
-
this.u.set(start[0]);
|
|
506
|
-
this.uLo.set(start[1]);
|
|
507
|
-
this.termV.set(start[2]);
|
|
508
|
-
this.computeConcentrations(); // (from where this stage began, not where the failed one left it)
|
|
509
|
-
return this._floatingContinuation(opts, done, ks[0], (V) => (Vs - V) / d.R);
|
|
527
|
+
if (q.converged || ks.length !== 1 || byLine) return done;
|
|
528
|
+
// Where Kirchhoff's hold failed too, the load line.
|
|
529
|
+
restore();
|
|
530
|
+
return this._floatingContinuation(opts, done, ks[0], line);
|
|
510
531
|
}
|
|
511
532
|
|
|
512
533
|
// Steady solves with the generation reactions' rates scaled from 1e-30 up to 1, ×100 a step
|
|
@@ -687,7 +708,10 @@ export class SteadySolves {
|
|
|
687
708
|
if (dt < giant) {
|
|
688
709
|
dt *= grow; // ramping up after a failure
|
|
689
710
|
grow = Math.min(10, grow * 1.5);
|
|
690
|
-
|
|
711
|
+
// Then the giant step; or, solved directly, on to 1e12 τ, the direct solve tried once a
|
|
712
|
+
// decade (above): jumping to dt = ∞ at 1e6 τ, its failure sent dt back to 1e-6 τ, round
|
|
713
|
+
// and round below 70 s, where traps filled by 1e9 cm⁻³ electrons took 1e3 s (τ 7e-5 s).
|
|
714
|
+
if (dt > (direct ? 1e12 : 1e6) * tau) dt = giant;
|
|
691
715
|
} else if (dt < 1e6 * giant) {
|
|
692
716
|
dt *= 10; // conserved amounts present and still moving: let dt keep growing (capped)
|
|
693
717
|
}
|
|
@@ -734,7 +758,9 @@ export class SteadySolves {
|
|
|
734
758
|
// it, and there's nothing here to shift.
|
|
735
759
|
if (!st.spectator || this.model.materials[this.model.regions[st.regions[0]].material].conductor) return;
|
|
736
760
|
const want = this.referenceAmounts[k];
|
|
737
|
-
|
|
761
|
+
// (one shift of η scales an ideal stretch's amount exactly, but not what a diffuse layer
|
|
762
|
+
// at its edge holds, whose excess isn't proportional to c: iterate there too)
|
|
763
|
+
const ideal = this.nodeIdeal.subarray(st.nodes[0], st.nodes[1] + 1).every((v) => v === 1) && this._diffuseHeld(st, this.gam) === 0;
|
|
738
764
|
for (let it = 0; it < (ideal ? 1 : 30); it++) {
|
|
739
765
|
const now = this.amount(st);
|
|
740
766
|
if (!(now > 0) || now === want) return;
|
|
@@ -746,6 +772,9 @@ export class SteadySolves {
|
|
|
746
772
|
const dc = this.nodeIdeal[g] ? this.c[g * n + st.species] : this.K[g * n * n + st.species * (n + 1)];
|
|
747
773
|
dA += this.model.grid.vol[g] * dc;
|
|
748
774
|
}
|
|
775
|
+
this.diffList.forEach((dl, d) => {
|
|
776
|
+
if (dl.g >= st.nodes[0] && dl.g <= st.nodes[1]) dA += this.model.grid.area[dl.g] * dl.dl.dgdc[st.species * (n + 1)] * this.c[dl.g * n + st.species];
|
|
777
|
+
});
|
|
749
778
|
shift = (want - now) / dA;
|
|
750
779
|
if (!(Math.abs(shift) > 1e-16)) return;
|
|
751
780
|
}
|
package/src/timeSteps.js
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// in docs/internals.md. The Solver's own step, advance and integrate call them (the API, the
|
|
9
9
|
// steady solves' time stepping and the kit call those). What they use of it:
|
|
10
10
|
// s.newton(dt, opts) solves the equations at the step's end, from the state as it stands,
|
|
11
|
-
// with storage (c − c*)/dt against s.cOld, s.thOld, s.th0Old, s.
|
|
11
|
+
// with storage (c − c*)/dt against s.cOld, s.thOld, s.th0Old, s.gamOld (the
|
|
12
|
+
// diffuse layers' excesses), s.gateOld,
|
|
12
13
|
// s.contactDStart, s.portQStart; leaves the state at its last iterate.
|
|
13
14
|
// s._snapshot() / s._restore(snap) the whole time state (u, uLo, termV, the start-of-step
|
|
14
15
|
// values, time, lastDt, history, the boundary intake, sourceTime).
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
//
|
|
18
19
|
// The state: u + uLo (Newton's unknowns, η and φ̂ in thermal units), termV (terminal voltages),
|
|
19
20
|
// time. The history: s.history, the accepted steps before this one, newest first, at most 3,
|
|
20
|
-
// each { t (its start), dt, u (its start state), c, th, th0, g (gates), D (contacts' D), Q
|
|
21
|
+
// each { t (its start), dt, u (its start state), c, th, th0, gam, g (gates), D (contacts' D), Q
|
|
21
22
|
// (ports' charges) }: what BDF2 and the predictor need.
|
|
22
23
|
//
|
|
23
24
|
// step(s, dt, opts) → newton's result (+ bdf)
|
|
@@ -128,7 +129,7 @@ export function step(s, dt, opts = {}) {
|
|
|
128
129
|
// What the step overwrites before Newton runs, for a failed one to put back: the Solution read
|
|
129
130
|
// after it takes its contacts' fluxes against the start-of-step values (a host emptied by its
|
|
130
131
|
// current read its contact's Li⁺ flux 8× off at the same state).
|
|
131
|
-
const was = { cOld: Float64Array.from(s.cOld), thOld: Float64Array.from(s.thOld), th0Old: Float64Array.from(s.th0Old), gateOld: Float64Array.from(s.gateOld), contactDStart: s.contactDStart, portQStart: s.portQStart, atSteady: s.atSteady };
|
|
132
|
+
const was = { cOld: Float64Array.from(s.cOld), thOld: Float64Array.from(s.thOld), th0Old: Float64Array.from(s.th0Old), gamOld: Float64Array.from(s.gamOld), gateOld: Float64Array.from(s.gateOld), contactDStart: s.contactDStart, portQStart: s.portQStart, atSteady: s.atSteady };
|
|
132
133
|
s.atSteady = false;
|
|
133
134
|
const prev = s.history[0];
|
|
134
135
|
const w = prev ? dt / prev.dt : 0;
|
|
@@ -137,7 +138,7 @@ export function step(s, dt, opts = {}) {
|
|
|
137
138
|
s.uPrevLo.set(s.uLo);
|
|
138
139
|
s.termVPrev.set(s.termV);
|
|
139
140
|
s.computeConcentrations();
|
|
140
|
-
const cN = Float64Array.from(s.c), thN = Float64Array.from(s.th), th0N = Float64Array.from(s.th0);
|
|
141
|
+
const cN = Float64Array.from(s.c), thN = Float64Array.from(s.th), th0N = Float64Array.from(s.th0), gamN = Float64Array.from(s.gam);
|
|
141
142
|
const gN = Float64Array.from(s.gateList, (gt) => s.u[gt.o] + s.uLo[gt.o]);
|
|
142
143
|
// Contact displacement before the step: as it was at the end of the previous step (under
|
|
143
144
|
// the parameters then), so a gate-voltage change shows up as displacement current.
|
|
@@ -155,6 +156,7 @@ export function step(s, dt, opts = {}) {
|
|
|
155
156
|
for (let k = 0; k < cOld.length; k++) cOld[k] = b1 * cN[k] - b2 * prev.c[k];
|
|
156
157
|
for (let k = 0; k < thN.length; k++) s.thOld[k] = b1 * thN[k] - b2 * prev.th[k];
|
|
157
158
|
for (let k = 0; k < th0N.length; k++) s.th0Old[k] = b1 * th0N[k] - b2 * prev.th0[k];
|
|
159
|
+
for (let k = 0; k < gamN.length; k++) s.gamOld[k] = b1 * gamN[k] - b2 * prev.gam[k];
|
|
158
160
|
for (let k = 0; k < gN.length; k++) s.gateOld[k] = b1 * gN[k] - b2 * prev.g[k];
|
|
159
161
|
s.contactDStart = { left: b1 * DN.left - b2 * prev.D.left, right: b1 * DN.right - b2 * prev.D.right };
|
|
160
162
|
s.portQStart = QN.map((q, k) => b1 * q - b2 * prev.Q[k]);
|
|
@@ -163,6 +165,7 @@ export function step(s, dt, opts = {}) {
|
|
|
163
165
|
s.cOld.set(cN);
|
|
164
166
|
s.thOld.set(thN);
|
|
165
167
|
s.th0Old.set(th0N);
|
|
168
|
+
s.gamOld.set(gamN);
|
|
166
169
|
s.gateOld.set(gN);
|
|
167
170
|
s.contactDStart = DN;
|
|
168
171
|
s.portQStart = QN;
|
|
@@ -205,13 +208,13 @@ export function step(s, dt, opts = {}) {
|
|
|
205
208
|
}
|
|
206
209
|
result.bdf = bdf;
|
|
207
210
|
if (result.converged) {
|
|
208
|
-
s.history.unshift({ t: s.time, dt, u: Float64Array.from(s.uPrev), c: cN, th: thN, th0: th0N, g: gN, D: DN, Q: QN });
|
|
211
|
+
s.history.unshift({ t: s.time, dt, u: Float64Array.from(s.uPrev), c: cN, th: thN, th0: th0N, gam: gamN, g: gN, D: DN, Q: QN });
|
|
209
212
|
if (s.history.length > 3) s.history.length = 3;
|
|
210
213
|
s.time = tEnd;
|
|
211
214
|
s.lastDt = dtEff;
|
|
212
215
|
s.contactDOld = s.contactDStart;
|
|
213
216
|
s.portQOld = s.portQStart;
|
|
214
|
-
s._accumulateBoundaryIntake(dtEff, cN);
|
|
217
|
+
s._accumulateBoundaryIntake(dtEff, cN, gamN);
|
|
215
218
|
// (In time, the state is no longer the steady solve's that a continuation would start from.)
|
|
216
219
|
if (!s.steady) {
|
|
217
220
|
s.solvedV = undefined;
|
|
@@ -224,6 +227,7 @@ export function step(s, dt, opts = {}) {
|
|
|
224
227
|
s.cOld.set(was.cOld);
|
|
225
228
|
s.thOld.set(was.thOld);
|
|
226
229
|
s.th0Old.set(was.th0Old);
|
|
230
|
+
s.gamOld.set(was.gamOld);
|
|
227
231
|
s.gateOld.set(was.gateOld);
|
|
228
232
|
s.contactDStart = was.contactDStart;
|
|
229
233
|
s.portQStart = was.portQStart;
|
package/src/types.js
CHANGED
|
@@ -114,6 +114,15 @@
|
|
|
114
114
|
* @property {Record<string, number>} [K] with vmax: a half-saturation concentration (mol/m³) for
|
|
115
115
|
* every species the forward reaction consumes
|
|
116
116
|
*/
|
|
117
|
+
/**
|
|
118
|
+
* A face's diffuse layer on one side, in a solution.
|
|
119
|
+
* @typedef {object} DiffuseLayerResult
|
|
120
|
+
* @property {number} psi the drop from the side's edge node to the plane, φ_plane − φ_edge (V)
|
|
121
|
+
* @property {number} charge the layer's charge (C/m²)
|
|
122
|
+
* @property {Record<string, number>} excess each ion's surface excess, ∫ (c − c_edge) dx (mol/m²)
|
|
123
|
+
* @property {Record<string, number>} plane each ion's concentration at the plane, c e^{−zFψ/RT} (mol/m³; on a
|
|
124
|
+
* lattice, over its crowding; for what a face reaction takes or makes there, times its level's drop across the layer)
|
|
125
|
+
*/
|
|
117
126
|
/**
|
|
118
127
|
* One face between consecutive regions. For different materials under a 'pinned' or
|
|
119
128
|
* 'capacitive' law, exactly one alignment (step or dipole) is required.
|
|
@@ -124,6 +133,9 @@
|
|
|
124
133
|
* @property {InterfacePhiLaw} [phi] electrostatic law (default 'pinned'; 'neutral' between ε = 0 materials;
|
|
125
134
|
* beside a conductor 'neutral' or capacitive, required next to an ε > 0 material)
|
|
126
135
|
* @property {number} [sheetCharge] C/m²
|
|
136
|
+
* @property {{ epsr: number }} [diffuse] a Gouy–Chapman diffuse layer below the grid on each strictly
|
|
137
|
+
* neutral (ε = 0) side of a charged face, of relative permittivity epsr: the side's share of the
|
|
138
|
+
* face's charge held as its ions' surface excesses, the face's reactions at its plane
|
|
127
139
|
* @property {Record<string, InterfaceSpeciesLink>} [species] per-species laws (default equilibrium
|
|
128
140
|
* where present on both sides and mobile on one, blocked otherwise; a species reacting at the
|
|
129
141
|
* face and present on both sides has no default)
|
|
@@ -283,11 +295,13 @@
|
|
|
283
295
|
* side holds; with gates, each gate's fraction open at each node and the voltage they follow (V)
|
|
284
296
|
* @property {{ left: string, right: string, dipole: number, sheetCharge: number, D: number, N: Record<string, number>,
|
|
285
297
|
* rates: number[], links: Record<string, string>, oneWay?: Record<string, [number, number]>,
|
|
286
|
-
* gates?: Record<string, number>, V?: number
|
|
298
|
+
* gates?: Record<string, number>, V?: number,
|
|
299
|
+
* diffuse?: { left?: DiffuseLayerResult, right?: DiffuseLayerResult } }[]} interfaces each face: the regions it joins, its dipole
|
|
287
300
|
* (V) and sheet charge (C/m²), its displacement D (C/m²), each species' flux N across it and each face
|
|
288
301
|
* reaction's net rate (mol/(m²·s), toward +x and forward), how each species that crosses it does (its
|
|
289
302
|
* link type); for those crossing by permeability, the one-way fluxes [toward +x, toward −x] whose
|
|
290
|
-
* difference is N; with gates, each one's fraction open and the voltage they follow, φ_right − φ_left (V)
|
|
303
|
+
* difference is N; with gates, each one's fraction open and the voltage they follow, φ_right − φ_left (V; between the diffuse layers' planes, where it has them);
|
|
304
|
+
* with a diffuse layer, each side's
|
|
291
305
|
* @property {{ rate: Float64Array, forward: Float64Array, nu: Record<string, number>, regions: number[], total: number }[]} bulkReactions
|
|
292
306
|
* each bulk reaction's net rate per node (mol/(m³·s), forward minus backward, NaN where it doesn't
|
|
293
307
|
* run), its one-way forward rate (the backward is forward − rate), what one forward reaction makes
|
package/types/boundaries.d.ts
CHANGED
|
@@ -2,12 +2,20 @@ export function gateRate(law: any, V: any): number[];
|
|
|
2
2
|
export class BoundaryTerms {
|
|
3
3
|
_portRate(port: any, rx: any, g: any): number;
|
|
4
4
|
_face(f: any): void;
|
|
5
|
+
_diffuseDraws(d: any, bf: any): void;
|
|
6
|
+
_diffuseTerms(d: any, bf: any, Af: any): void;
|
|
7
|
+
dW: Float64Array<any>;
|
|
8
|
+
_diffuseChain(d: any): any;
|
|
9
|
+
dV2: Float64Array<any>;
|
|
10
|
+
_diffuseLayers(): void;
|
|
5
11
|
_gateSlot(f: any, q: any): any;
|
|
6
12
|
_gateVoltage(gt: any): number;
|
|
7
13
|
_portVoltage(k: any, g: any): number;
|
|
8
14
|
_faceVoltage(f: any): number;
|
|
9
15
|
_faceGates(f: any, bf: any, bL: any, bR: any): void;
|
|
10
16
|
_faceReaction(rx: any, k: any, f: any, bf: any, bL: any, bR: any, Af: any): void;
|
|
17
|
+
dWr: Float64Array<any>;
|
|
18
|
+
aWr: Float64Array<any>;
|
|
11
19
|
_liveTerms(): any;
|
|
12
20
|
_allTerms: any;
|
|
13
21
|
_terminals(dt: any): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The diffuse layer's charge, excesses and their derivatives (see the contract above). */
|
|
2
|
+
export function diffuseLayer(c: any, z: any, n: any, s: any, { A, B }: {
|
|
3
|
+
A: any;
|
|
4
|
+
B: any;
|
|
5
|
+
}, out: any, lat?: any, draw?: any): any;
|
|
6
|
+
/** A scratch object for diffuseLayer over n species. */
|
|
7
|
+
export function diffuseScratch(n: any): {
|
|
8
|
+
q: number;
|
|
9
|
+
dqds: number;
|
|
10
|
+
dqdc: Float64Array<any>;
|
|
11
|
+
gam: Float64Array<any>;
|
|
12
|
+
dgds: Float64Array<any>;
|
|
13
|
+
dgdc: Float64Array<ArrayBuffer>;
|
|
14
|
+
K: Float64Array<any>;
|
|
15
|
+
dKds: Float64Array<any>;
|
|
16
|
+
dKdc: Float64Array<ArrayBuffer>;
|
|
17
|
+
D: number;
|
|
18
|
+
dDds: number;
|
|
19
|
+
dDdc: Float64Array<any>;
|
|
20
|
+
};
|
|
21
|
+
/** The constants A = √(εRT/2)/F and B = √(2εRT) for a layer of permittivity eps (F/m) at RT (J/mol). */
|
|
22
|
+
export function diffuseUnits(eps: any, RT: any, F: any): {
|
|
23
|
+
A: number;
|
|
24
|
+
B: number;
|
|
25
|
+
};
|
package/types/newton.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function maxPotentialStep(s: any, delta: any, deltaV?: any): number;
|
|
1
|
+
export function maxPotentialStep(s: any, delta: any, deltaV?: any, layers?: boolean): number;
|
|
2
2
|
export function newton(s: any, dt: any, { maxIter, tol, maxStep, islands }?: {
|
|
3
3
|
maxIter?: number;
|
|
4
4
|
tol?: number;
|
package/types/solver.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ export class Solver {
|
|
|
32
32
|
gateOld: Float64Array<ArrayBuffer>;
|
|
33
33
|
gateIndex: Int32Array<ArrayBuffer>;
|
|
34
34
|
portGateIndex: any;
|
|
35
|
+
diffList: any[];
|
|
36
|
+
diffAt: any;
|
|
37
|
+
gam: Float64Array<ArrayBuffer>;
|
|
38
|
+
gamOld: Float64Array<ArrayBuffer>;
|
|
35
39
|
present: Uint8Array<ArrayBuffer>;
|
|
36
40
|
cRef: Float64Array<ArrayBuffer>;
|
|
37
41
|
mu0hat: Float64Array<ArrayBuffer>;
|
|
@@ -234,8 +238,9 @@ export class Solver {
|
|
|
234
238
|
_surfaceStart(g: any): void;
|
|
235
239
|
/** Amount (mol per unit area) of surface column j's species, Γ Σ v·a·θ, in the current state. */
|
|
236
240
|
surfaceAmount(j: any): number;
|
|
237
|
-
/** Total amount (mol per unit area) of a stretch's species in the current state. */
|
|
241
|
+
/** Total amount (mol per unit area) of a stretch's species in the current state, its faces' diffuse layers' included. */
|
|
238
242
|
amount(stretch: any): number;
|
|
243
|
+
_diffuseHeld(stretch: any, gam: any): number;
|
|
239
244
|
redrive(): void;
|
|
240
245
|
deltaV: any;
|
|
241
246
|
_drivenKey(): any;
|
|
@@ -280,6 +285,7 @@ export class Solver {
|
|
|
280
285
|
cOld: Float64Array<ArrayBuffer>;
|
|
281
286
|
thOld: Float64Array<ArrayBuffer>;
|
|
282
287
|
th0Old: Float64Array<ArrayBuffer>;
|
|
288
|
+
gamOld: Float64Array<ArrayBuffer>;
|
|
283
289
|
gateOld: Float64Array<ArrayBuffer>;
|
|
284
290
|
time: number;
|
|
285
291
|
lastDt: number;
|
|
@@ -308,6 +314,6 @@ export class Solver {
|
|
|
308
314
|
_terminalCurrent(): number;
|
|
309
315
|
segmentFluxes(): Float64Array<ArrayBuffer>[];
|
|
310
316
|
faceOneWay(f: any): any;
|
|
311
|
-
_accumulateBoundaryIntake(dt: any, cN: any): void;
|
|
317
|
+
_accumulateBoundaryIntake(dt: any, cN: any, gamN?: Float64Array<ArrayBuffer>): void;
|
|
312
318
|
}
|
|
313
319
|
import { BlockTridiagonal } from './blockTridiagonal.js';
|