driftlet 0.12.0 → 0.14.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 +48 -76
- package/docs/device.md +61 -26
- package/docs/kit.md +71 -4
- package/docs/logo-dark.svg +3 -2
- package/docs/logo.svg +3 -2
- package/docs/reliability.md +60 -3
- package/docs/statistics.md +10 -0
- package/llms.txt +622 -391
- package/package.json +1 -1
- package/src/channels.js +27 -4
- package/src/checks.js +5 -1
- package/src/describe.js +1 -1
- package/src/device.js +112 -14
- package/src/index.js +6 -1
- package/src/kit.js +1 -0
- package/src/particles.js +331 -0
- package/src/solution.js +42 -4
- package/src/solver.js +181 -50
- package/src/stack.js +6 -4
- package/src/types.js +2 -1
- package/types/channels.d.ts +14 -3
- package/types/checks.d.ts +2 -1
- package/types/kit.d.ts +1 -0
- package/types/particles.d.ts +42 -0
- package/types/solution.d.ts +1 -0
- package/types/solver.d.ts +8 -2
- package/types/stack.d.ts +6 -5
- package/types/types.d.ts +5 -0
package/src/solver.js
CHANGED
|
@@ -261,12 +261,18 @@ export class SolverError extends Error {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
// Lagrange weights at x through the points s0, s1, s2.
|
|
265
|
+
function lagrange3(x, s0, s1, s2) {
|
|
266
|
+
return [((x - s1) * (x - s2)) / ((s0 - s1) * (s0 - s2)), ((x - s0) * (x - s2)) / ((s1 - s0) * (s1 - s2)), ((x - s0) * (x - s1)) / ((s2 - s0) * (s2 - s1))];
|
|
267
|
+
}
|
|
268
|
+
|
|
264
269
|
// A gate's rate law (see normalizeGates in device.js) at voltage V: [rate, ∂rate/∂V], 1/s and 1/(s·V).
|
|
265
270
|
function gateRate(law, V) {
|
|
266
271
|
const { rate, midpoint, scale } = law;
|
|
267
272
|
if (law.type === 'exp') {
|
|
268
|
-
|
|
269
|
-
|
|
273
|
+
// (held finite where Newton's iterates stray to absurd voltages: β_m at −13 V would overflow)
|
|
274
|
+
const y = (V - midpoint) / scale, v = rate * Math.exp(Math.min(y, 700));
|
|
275
|
+
return [v, y > 700 ? 0 : v / scale];
|
|
270
276
|
}
|
|
271
277
|
if (law.type === 'sigmoid') {
|
|
272
278
|
const e = Math.exp((midpoint - V) / scale), d = 1 + e;
|
|
@@ -1214,7 +1220,7 @@ export class Solver {
|
|
|
1214
1220
|
/** η_i/RT that a fixed contact link imposes (or NaN if the link isn't fixed). */
|
|
1215
1221
|
contactEta(side, i) {
|
|
1216
1222
|
const link = this.model.contacts[side].species[i];
|
|
1217
|
-
if (link.type !== 'equilibrium') return NaN;
|
|
1223
|
+
if (link.type !== 'equilibrium' && link.type !== 'velocity') return NaN;
|
|
1218
1224
|
const z = this.z[i];
|
|
1219
1225
|
return z === 0 ? link.mu / this.model.RT : (z * (this.termV[side === 'left' ? 0 : 1] + link.offset)) / this.VT;
|
|
1220
1226
|
}
|
|
@@ -2341,6 +2347,21 @@ export class Solver {
|
|
|
2341
2347
|
return P * -Math.expm1(-(aHi + aLo));
|
|
2342
2348
|
}
|
|
2343
2349
|
|
|
2350
|
+
// A bulk reaction's forward one-way rate at node g (mol/(m³·s)); its backward one is that
|
|
2351
|
+
// times e^{−a}, so their difference is bulkRate's.
|
|
2352
|
+
bulkForward(rx, g) {
|
|
2353
|
+
const { n, M, u, uLo, c } = this, b = this.blockOfNode[g];
|
|
2354
|
+
const law = rx.srh?.[this.nodeMaterial[g]];
|
|
2355
|
+
if (law) {
|
|
2356
|
+
let a = rx.fixedA;
|
|
2357
|
+
for (let p = 0; p < rx.sp.length; p++) a += rx.nu[p] * (u[b * M + 1 + rx.sp[p]] + uLo[b * M + 1 + rx.sp[p]]);
|
|
2358
|
+
return this._srhRate(law, c[g * n + law.n.i], c[g * n + law.p.i], a).forward;
|
|
2359
|
+
}
|
|
2360
|
+
let P = rx.generation ? rx.kfNode[g] * this.generationScale : rx.kfNode[g];
|
|
2361
|
+
for (let p = 0; p < rx.sp.length; p++) if (rx.nu[p] > 0) P *= rx.nu[p] === 1 ? c[g * n + rx.sp[p]] : powi(c[g * n + rx.sp[p]], rx.nu[p]);
|
|
2362
|
+
return P;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2344
2365
|
// The larger of a bulk reaction's two one-way rates at node g (mol/(m³·s)): the scale its net
|
|
2345
2366
|
// rate is read against (in equilibrium the net rate is their round-off).
|
|
2346
2367
|
bulkOneWay(rx, g) {
|
|
@@ -2370,7 +2391,7 @@ export class Solver {
|
|
|
2370
2391
|
const dDn = tp * cn + tp * n1 * kN + tn * p1 * (1 - kN);
|
|
2371
2392
|
const dDp = tp * n1 * kP + tn * cp + tn * p1 * (1 - kP);
|
|
2372
2393
|
const dDa = tp * n1 * kA + tn * p1 * (-1 - kA);
|
|
2373
|
-
return { rate, oneWay: (np / den) * Math.max(1, Math.exp(-a)), Cn: rate * (1 - dDn / den), Cp: rate * (1 - dDp / den), Ca: (np / den) * (1 - f) - (rate * dDa) / den };
|
|
2394
|
+
return { rate, forward: np / den, oneWay: (np / den) * Math.max(1, Math.exp(-a)), Cn: rate * (1 - dDn / den), Cp: rate * (1 - dDp / den), Ca: (np / den) * (1 - f) - (rate * dDa) / den };
|
|
2374
2395
|
}
|
|
2375
2396
|
|
|
2376
2397
|
// A face reaction (see normalizeFaceReactions). Its rate r_k is an unknown of the face block,
|
|
@@ -2519,6 +2540,7 @@ export class Solver {
|
|
|
2519
2540
|
flux[i] += res[R[o]];
|
|
2520
2541
|
this._captureRow(b, r, zF, C);
|
|
2521
2542
|
this._replaceRow(b, r);
|
|
2543
|
+
for (const Bk of this.termB) Bk[R[o]] = 0; // (other ports' terms in the row replaced)
|
|
2522
2544
|
this._j(b, r, b, r, 1);
|
|
2523
2545
|
res[R[o]] = -deta;
|
|
2524
2546
|
B[R[o]] += -z[i] / VT;
|
|
@@ -2746,7 +2768,7 @@ export class Solver {
|
|
|
2746
2768
|
}
|
|
2747
2769
|
|
|
2748
2770
|
// Conductance links: J (toward the device) = G (V_out − V_i), V_out = V_t + offset; exchange
|
|
2749
|
-
// links (neutral species): N_in = k (μ_out − μ)/RT.
|
|
2771
|
+
// links (neutral species): N_in = k (μ_out − μ)/RT; velocity links, v (c_eq − c).
|
|
2750
2772
|
for (let i = 0; i < n; i++) {
|
|
2751
2773
|
const link = ct.species[i];
|
|
2752
2774
|
const o = b * M + 1 + i;
|
|
@@ -2758,6 +2780,16 @@ export class Solver {
|
|
|
2758
2780
|
res[R[o]] -= (Ac * link.G * (Vt + link.offset - Vi)) / (z[i] * F);
|
|
2759
2781
|
this._j(b, 1 + i, b, 1 + i, (Ac * link.G * VT) / (z[i] * z[i] * F));
|
|
2760
2782
|
B[R[o]] += (-Ac * link.G) / (z[i] * F);
|
|
2783
|
+
} else if (link.type === 'velocity') {
|
|
2784
|
+
// N_in = v c (e^(η_out − η) − 1): v (c_eq − c), c_eq in equilibrium with the outside.
|
|
2785
|
+
const ci = c[g * n + i], E = Math.exp(Math.min(this.contactEta(side, i) - (u[o] + uLo[o]), 700));
|
|
2786
|
+
const N = Ac * link.v * ci * (E - 1), d = this.dA;
|
|
2787
|
+
res[R[o]] -= N;
|
|
2788
|
+
d.fill(0);
|
|
2789
|
+
this._dlnc(g, i, 1, d);
|
|
2790
|
+
for (let s = 0; s <= n; s++) if (d[s] !== 0) this._j(b, 1 + i, b, s, -N * d[s]);
|
|
2791
|
+
this._j(b, 1 + i, b, 1 + i, Ac * link.v * ci * E);
|
|
2792
|
+
if (z[i] !== 0) B[R[o]] += (-Ac * link.v * ci * E * z[i]) / VT;
|
|
2761
2793
|
}
|
|
2762
2794
|
}
|
|
2763
2795
|
|
|
@@ -3135,7 +3167,13 @@ export class Solver {
|
|
|
3135
3167
|
const alpha = own > maxStep ? maxStep / own : 1;
|
|
3136
3168
|
this._addToState(delta, -alpha);
|
|
3137
3169
|
this.floating.forEach((k, a) => (this.termV[k] -= alpha * deltaV[a]));
|
|
3138
|
-
|
|
3170
|
+
// A time step's Newton contracts fast from its predictor (1e-2, 1e-9, 1e-15 thermal
|
|
3171
|
+
// units). The next update would be about θ = step/(the last one) times this one, so where
|
|
3172
|
+
// θ/(1 − θ) of it (Hairer and Wanner's estimate) is under tol, this update converged it:
|
|
3173
|
+
// the iteration that would only show that is skipped, a third of a typical step's work.
|
|
3174
|
+
const last = history[history.length - 2], theta = step / last;
|
|
3175
|
+
const contracted = dt !== Infinity && it >= 2 && step < 1e-6 && theta < 0.1 && (theta / (1 - theta)) * step < tol;
|
|
3176
|
+
if (alpha === 1 && (step < tol || contracted)) {
|
|
3139
3177
|
const done = { converged: true, iterations: it, history, residual: rmax };
|
|
3140
3178
|
if (islandsOff(it, done)) continue;
|
|
3141
3179
|
this.computeConcentrations();
|
|
@@ -3408,7 +3446,7 @@ export class Solver {
|
|
|
3408
3446
|
}
|
|
3409
3447
|
} else {
|
|
3410
3448
|
// (Order 2 once three consistent states can check it.)
|
|
3411
|
-
const r = this.step(h, { method: this.history.length >= 2 ? method : 'be', guess: this._extrapolate(guess,
|
|
3449
|
+
const r = this.step(h, { method: this.history.length >= 2 ? method : 'be', guess: this._extrapolate(guess, h) });
|
|
3412
3450
|
iterations += r.iterations;
|
|
3413
3451
|
if (!r.converged) {
|
|
3414
3452
|
rejected++;
|
|
@@ -3454,24 +3492,28 @@ export class Solver {
|
|
|
3454
3492
|
}
|
|
3455
3493
|
this.dtNext = dt;
|
|
3456
3494
|
this.landing = undefined;
|
|
3457
|
-
|
|
3495
|
+
// Stopped short, other than on the caller's wall-time budget: say where, and why.
|
|
3496
|
+
let stopped;
|
|
3497
|
+
const at = `advance stopped at t = ${this.time.toPrecision(6)} s of ${tEnd.toPrecision(6)}`;
|
|
3498
|
+
if (failed) stopped = `${at}: a step didn't converge even when shortened to ${dt.toExponential(1)} s. A drive's jump too big for one step (ramp it), a driven current the device can't keep passing (a diode's reverse recovery past its storage time: stop there), or a device the steady solves also find hard.`;
|
|
3499
|
+
else if (this.time < tEnd && steps + rejected >= maxSteps) stopped = `${at}: maxSteps (${maxSteps}) used up (${rejected} of them rejected). Call again to go on, or loosen tol.`;
|
|
3500
|
+
return { converged: !failed, done: this.time >= tEnd, steps, rejected, iterations, trace, ...(stopped ? { stopped } : {}) };
|
|
3458
3501
|
}
|
|
3459
3502
|
|
|
3460
|
-
// Quadratic (or linear) extrapolation of the state
|
|
3461
|
-
// two most recent history entries: a starting guess for Newton.
|
|
3462
|
-
|
|
3503
|
+
// Quadratic (or linear) extrapolation of the state a time h ahead, from the current state and
|
|
3504
|
+
// the two most recent history entries: a starting guess for Newton. (In offsets built from the
|
|
3505
|
+
// steps' own lengths, never differences of absolute times: a second into a run, after a jump
|
|
3506
|
+
// that a fine grid resolves in steps of 1e-13 s, those differences keep only 3 digits.)
|
|
3507
|
+
_extrapolate(out, h) {
|
|
3463
3508
|
const [e0, e1] = this.history;
|
|
3464
|
-
const
|
|
3509
|
+
const u = this.u;
|
|
3465
3510
|
if (!e0) return undefined;
|
|
3466
3511
|
if (!e1) {
|
|
3467
|
-
const l =
|
|
3512
|
+
const l = h / e0.dt;
|
|
3468
3513
|
for (let k = 0; k < out.length; k++) out[k] = u[k] + l * (u[k] - e0.u[k]);
|
|
3469
3514
|
return out;
|
|
3470
3515
|
}
|
|
3471
|
-
const
|
|
3472
|
-
const l0 = ((t - t1) * (t - t2)) / ((t0 - t1) * (t0 - t2));
|
|
3473
|
-
const l1 = ((t - t0) * (t - t2)) / ((t1 - t0) * (t1 - t2));
|
|
3474
|
-
const l2 = ((t - t0) * (t - t1)) / ((t2 - t0) * (t2 - t1));
|
|
3516
|
+
const [l0, l1, l2] = lagrange3(h, 0, -e0.dt, -(e0.dt + e1.dt));
|
|
3475
3517
|
for (let k = 0; k < out.length; k++) out[k] = l0 * u[k] + l1 * e0.u[k] + l2 * e1.u[k];
|
|
3476
3518
|
return out;
|
|
3477
3519
|
}
|
|
@@ -3480,7 +3522,7 @@ export class Solver {
|
|
|
3480
3522
|
// linear after backward Euler), and the factor turning |u − pred| into the local error.
|
|
3481
3523
|
_predict(pred, bdf) {
|
|
3482
3524
|
const [e0, e1, e2] = this.history;
|
|
3483
|
-
const
|
|
3525
|
+
const h = e0.dt;
|
|
3484
3526
|
if (!e1) {
|
|
3485
3527
|
pred.set(e0.u);
|
|
3486
3528
|
return { scale: 1, order: 1 };
|
|
@@ -3488,18 +3530,15 @@ export class Solver {
|
|
|
3488
3530
|
const hp = e1.dt;
|
|
3489
3531
|
if (bdf && e2) {
|
|
3490
3532
|
const hpp = e2.dt;
|
|
3491
|
-
// Lagrange through
|
|
3492
|
-
const
|
|
3493
|
-
const l0 = ((t - t1) * (t - t2)) / ((t0 - t1) * (t0 - t2));
|
|
3494
|
-
const l1 = ((t - t0) * (t - t2)) / ((t1 - t0) * (t1 - t2));
|
|
3495
|
-
const l2 = ((t - t0) * (t - t1)) / ((t2 - t0) * (t2 - t1));
|
|
3533
|
+
// Lagrange through the three states before at the new time (offsets as in _extrapolate).
|
|
3534
|
+
const [l0, l1, l2] = lagrange3(0, -h, -(h + hp), -(h + hp + hpp));
|
|
3496
3535
|
for (let k = 0; k < pred.length; k++) pred[k] = l0 * e0.u[k] + l1 * e1.u[k] + l2 * e2.u[k];
|
|
3497
3536
|
const w = h / hp;
|
|
3498
3537
|
const Cc = (h * h * h * (1 + w) * (1 + w)) / (w * (1 + 2 * w));
|
|
3499
3538
|
const Cp = h * (h + hp) * (h + hp + hpp);
|
|
3500
3539
|
return { scale: Cc / (Cc + Cp), order: 2 };
|
|
3501
3540
|
}
|
|
3502
|
-
const l0 = (
|
|
3541
|
+
const l0 = (h + hp) / hp, l1 = 1 - l0;
|
|
3503
3542
|
for (let k = 0; k < pred.length; k++) pred[k] = l0 * e0.u[k] + l1 * e1.u[k];
|
|
3504
3543
|
// Backward Euler's error against a linear predictor; after a BDF2 step without enough
|
|
3505
3544
|
// history this overestimates, which is safe.
|
|
@@ -3720,7 +3759,15 @@ export class Solver {
|
|
|
3720
3759
|
for (const X of ['A', 'B', 'C']) S[X] = sys[X].map((v, k) => (v - J[X][k]) * dts);
|
|
3721
3760
|
const Cs = this.termC.map((v, k) => v.map((x, j) => (x - Ct[k][j]) * dts));
|
|
3722
3761
|
const DIs = this.termDI.map((x, k) => (x - DI[k]) * dts);
|
|
3723
|
-
|
|
3762
|
+
// Where the measured contact's current may be read across a segment: anywhere without
|
|
3763
|
+
// ports; with them, only between it and the nearest window (a port's exchange takes current
|
|
3764
|
+
// in or out along it). Never for a port's own terminal.
|
|
3765
|
+
let cuts = kT < 2 ? this._currentCuts() : null;
|
|
3766
|
+
if (cuts && this.model.ports.length > 0) {
|
|
3767
|
+
const windows = this.model.ports.flatMap((p) => Array.from(p.nodes));
|
|
3768
|
+
const lo = Math.min(...windows), hi = Math.max(...windows);
|
|
3769
|
+
cuts = cuts.filter((cut) => (kT === 0 ? cut.s + 1 <= lo : cut.s >= hi));
|
|
3770
|
+
}
|
|
3724
3771
|
// J·v for GMRES, exact: the assembled J holds a flux's dependence on η_L and η_R as two
|
|
3725
3772
|
// entries, and where both are huge (an inversion layer) and v nearly uniform, J·v loses the
|
|
3726
3773
|
// flux to round-off. So the dilute kernels' terms are kept in difference form (see
|
|
@@ -3889,7 +3936,7 @@ export class Solver {
|
|
|
3889
3936
|
Ii += im;
|
|
3890
3937
|
for (let j = 0; j < Nc; j++) err += (Math.abs(c[j]) + w * Math.abs(cs[j])) * (eps * Math.hypot(p.r[j], p.i[j]) + p.floor);
|
|
3891
3938
|
}
|
|
3892
|
-
if (!cuts
|
|
3939
|
+
if (!cuts) return { Ir, Ii, err };
|
|
3893
3940
|
const sgn = kT === 0 ? 1 : -1;
|
|
3894
3941
|
for (const cut of cuts) {
|
|
3895
3942
|
let re = 0, im = 0, e = 0;
|
|
@@ -3929,7 +3976,8 @@ export class Solver {
|
|
|
3929
3976
|
// taken at its edges; and what's left, small, solved for to GMRES's floor, its error now
|
|
3930
3977
|
// relative to itself. An electrolyte at its open circuit needs it: the response to its
|
|
3931
3978
|
// terminal is nearly such a shift, and its current the slope of levels uniform to 1e-14,
|
|
3932
|
-
// more than a double can hold. (
|
|
3979
|
+
// more than a double can hold. (A port's exchange sees the shift: its window's rows are
|
|
3980
|
+
// kept with the edges'.) So does a MOS capacitor's back contact, its channel held by a port.
|
|
3933
3981
|
if (K === 0 && cuts && err > 1e-2 * Math.hypot(Ir, Ii)) {
|
|
3934
3982
|
const [er, ei] = this._regionShift(yr, yi);
|
|
3935
3983
|
const tr = new Float64Array(Nc), ti = new Float64Array(Nc);
|
|
@@ -4019,8 +4067,9 @@ export class Solver {
|
|
|
4019
4067
|
return [er, ei];
|
|
4020
4068
|
}
|
|
4021
4069
|
|
|
4022
|
-
// Compact rows a region's uniform shift can touch: faces, the nodes beside them,
|
|
4023
|
-
//
|
|
4070
|
+
// Compact rows a region's uniform shift can touch: faces, the nodes beside them, the two ends
|
|
4071
|
+
// and the ports' windows (whatever holds a level from outside). Elsewhere inside a region,
|
|
4072
|
+
// every row is unchanged by it.
|
|
4024
4073
|
_regionEdgeRows() {
|
|
4025
4074
|
const { M, loc, nB } = this, R = this.rix;
|
|
4026
4075
|
const edge = new Uint8Array(this.sys.size);
|
|
@@ -4036,16 +4085,17 @@ export class Solver {
|
|
|
4036
4085
|
mark(b);
|
|
4037
4086
|
mark(b + 1);
|
|
4038
4087
|
}
|
|
4088
|
+
for (const port of this.model.ports) for (const g of port.nodes) mark(this.blockOfNode[g]);
|
|
4039
4089
|
return edge;
|
|
4040
4090
|
}
|
|
4041
4091
|
|
|
4042
|
-
// The cuts where the impedance may read a small-signal current
|
|
4043
|
-
//
|
|
4044
|
-
//
|
|
4045
|
-
//
|
|
4092
|
+
// The cuts where the impedance may read a small-signal current. Each is the linearised total
|
|
4093
|
+
// current toward +x through one segment s of an ideal region, Σ (g + iω d)·δu over the
|
|
4094
|
+
// unknowns idx: the Scharfetter–Gummel fluxes' derivatives (as in _segmentsDilute) and the
|
|
4095
|
+
// displacement's. Without ports it's the same through every cut; with them, only between a
|
|
4096
|
+
// contact and the nearest port's window (see _impedance).
|
|
4046
4097
|
_currentCuts() {
|
|
4047
4098
|
const { model, n, M, u, uLo, c, z } = this, F = FARADAY;
|
|
4048
|
-
if (model.ports.length > 0) return null;
|
|
4049
4099
|
const { grid, materials, regions } = model;
|
|
4050
4100
|
const cuts = [];
|
|
4051
4101
|
for (let r = 0; r < regions.length; r++) {
|
|
@@ -4085,12 +4135,78 @@ export class Solver {
|
|
|
4085
4135
|
}
|
|
4086
4136
|
if (terms.size === 0) continue;
|
|
4087
4137
|
const idx = [...terms.keys()];
|
|
4088
|
-
cuts.push({ idx, g: idx.map((x) => terms.get(x)[0]), d: idx.map((x) => terms.get(x)[1]) });
|
|
4138
|
+
cuts.push({ s, idx, g: idx.map((x) => terms.get(x)[0]), d: idx.map((x) => terms.get(x)[1]) });
|
|
4089
4139
|
}
|
|
4090
4140
|
}
|
|
4091
4141
|
return cuts;
|
|
4092
4142
|
}
|
|
4093
4143
|
|
|
4144
|
+
// Each species' flux toward +x across every segment, at the current state (concentrations
|
|
4145
|
+
// computed): totals through the cross-section, mol/s (mol/(m²·s) for a planar device). A face's
|
|
4146
|
+
// segment, between its pair of nodes, carries the face's flux; a conductor's, its carrier's.
|
|
4147
|
+
// These are the fluxes the balances add up (displacement aside), from the same formulas.
|
|
4148
|
+
segmentFluxes() {
|
|
4149
|
+
const { model, n, M, u, uLo, c, z, ex } = this;
|
|
4150
|
+
const { grid, materials, regions, interfaces } = model;
|
|
4151
|
+
const out = Array.from({ length: n }, () => new Float64Array(grid.nNodes - 1));
|
|
4152
|
+
for (let r = 0; r < regions.length; r++) {
|
|
4153
|
+
const reg = regions[r], mat = materials[reg.material], vel = reg.velocity;
|
|
4154
|
+
for (let s = grid.regionStart[r]; s < grid.regionEnd[r]; s++) {
|
|
4155
|
+
const bL = s + r, bR = bL + 1, h = grid.segLength[s] / grid.segArea[s];
|
|
4156
|
+
if (mat.conductor) {
|
|
4157
|
+
out[mat.conductor.i][s] = u[bL * M];
|
|
4158
|
+
continue;
|
|
4159
|
+
}
|
|
4160
|
+
for (let i = 0; i < n; i++) {
|
|
4161
|
+
if (!mat.present[i] || mat.D[i] === 0) continue;
|
|
4162
|
+
const pe = (vel * h) / mat.D[i];
|
|
4163
|
+
const d = z[i] * (u[bR * M] - u[bL * M]) + ex[(s + 1) * n + i] - ex[s * n + i] - pe;
|
|
4164
|
+
const deta = u[bR * M + 1 + i] - u[bL * M + 1 + i] + (uLo[bR * M + 1 + i] - uLo[bL * M + 1 + i]) - pe;
|
|
4165
|
+
out[i][s] = -(mat.D[i] / h) * bernoulli(d) * c[s * n + i] * Math.expm1(deta);
|
|
4166
|
+
}
|
|
4167
|
+
if (reg.mixing > 0) {
|
|
4168
|
+
// Eddy mixing (see _segmentMixing): −(D_mix/h) c̄_i (Δη_i − z_i q), c̄ the logarithmic mean.
|
|
4169
|
+
const on = [...Array(n).keys()].filter((i) => mat.present[i] && mat.D[i] > 0);
|
|
4170
|
+
const cb = [], de = [];
|
|
4171
|
+
let S = 0, Q = 0;
|
|
4172
|
+
for (const k of on) {
|
|
4173
|
+
const a = c[s * n + k], b = c[(s + 1) * n + k];
|
|
4174
|
+
cb[k] = a === b ? a : (b - a) / Math.log(b / a);
|
|
4175
|
+
de[k] = u[bR * M + 1 + k] - u[bL * M + 1 + k] + (uLo[bR * M + 1 + k] - uLo[bL * M + 1 + k]);
|
|
4176
|
+
S += z[k] * z[k] * cb[k];
|
|
4177
|
+
Q += z[k] * cb[k] * de[k];
|
|
4178
|
+
}
|
|
4179
|
+
const q = S > 0 ? Q / S : 0;
|
|
4180
|
+
for (const i of on) out[i][s] -= (reg.mixing / h) * cb[i] * (de[i] - z[i] * q);
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
interfaces.forEach((itf, f) => {
|
|
4185
|
+
const gL = grid.regionEnd[f], b = this.blockOfFace[f];
|
|
4186
|
+
for (let i = 0; i < n; i++) if (itf.links[i].type !== 'blocked') out[i][gL] = grid.area[gL] * u[b * M + 1 + i];
|
|
4187
|
+
});
|
|
4188
|
+
return out;
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
// A face's one-way fluxes for each species crossing it by permeability, per area: toward +x
|
|
4192
|
+
// and toward −x, whose difference is its net flux (Ussing's unidirectional fluxes; GHK's two
|
|
4193
|
+
// terms between like solutions). null for a species crossing any other way.
|
|
4194
|
+
faceOneWay(f) {
|
|
4195
|
+
const { model, n, M, u, uLo, z } = this, itf = model.interfaces[f], grid = model.grid;
|
|
4196
|
+
const bL = this.blockOfFace[f] - 1, bR = bL + 2, gl = grid.regionEnd[f], gr = gl + 1;
|
|
4197
|
+
return itf.links.map((link, i) => {
|
|
4198
|
+
if (link.type !== 'permeability') return null;
|
|
4199
|
+
const kl = gl * n + i, kr = gr * n + i;
|
|
4200
|
+
const shift = this.mu0hat[kr] - this.mu0hat[kl] - Math.log(this.cRef[kr] / this.cRef[kl]);
|
|
4201
|
+
const d = z[i] * (u[bR * M] - u[bL * M] + (uLo[bR * M] - uLo[bL * M])) + shift;
|
|
4202
|
+
const deta = u[bR * M + 1 + i] - u[bL * M + 1 + i] + (uLo[bR * M + 1 + i] - uLo[bL * M + 1 + i]);
|
|
4203
|
+
const gated = link.gates ?? [];
|
|
4204
|
+
const P = link.P * (gated.length ? this._open(gated, (q) => u[this._gateSlot(f, q)]) : 1);
|
|
4205
|
+
const right = P * bernoulli(d) * this.c[kl];
|
|
4206
|
+
return [right, right * Math.exp(deta)];
|
|
4207
|
+
});
|
|
4208
|
+
}
|
|
4209
|
+
|
|
4094
4210
|
// Complex profiles of δφ (V), δμ̄ (J/mol) and δc (mol/m³) from a small-signal solution.
|
|
4095
4211
|
_smallSignalProfiles(xr, xi) {
|
|
4096
4212
|
const { n, M, z, VT, model } = this;
|
|
@@ -4264,7 +4380,7 @@ export class Solver {
|
|
|
4264
4380
|
// pseudo-transient ramp: one direct attempt first.
|
|
4265
4381
|
const quick = (canContinue || this.hasGeneration) && direct && opts.continuation !== false;
|
|
4266
4382
|
let r = this._solveSteady(quick ? { ...opts, maxSteps: 1 } : opts);
|
|
4267
|
-
if (r.converged) this.solvedV = target;
|
|
4383
|
+
if (r.converged) this.solvedV = [level, target];
|
|
4268
4384
|
if (r.converged) return r;
|
|
4269
4385
|
// Generation (e.g. light) holding the device far from equilibrium: ramp it up from nearly
|
|
4270
4386
|
// nothing, each solve warm from the last.
|
|
@@ -4278,7 +4394,7 @@ export class Solver {
|
|
|
4278
4394
|
const g = this._generationContinuation(opts, r);
|
|
4279
4395
|
dimmer = this.dimmer;
|
|
4280
4396
|
if (g.converged) {
|
|
4281
|
-
this.solvedV = target;
|
|
4397
|
+
this.solvedV = [level, target];
|
|
4282
4398
|
return g;
|
|
4283
4399
|
}
|
|
4284
4400
|
r = g;
|
|
@@ -4319,17 +4435,23 @@ export class Solver {
|
|
|
4319
4435
|
this.computeConcentrations();
|
|
4320
4436
|
};
|
|
4321
4437
|
restart();
|
|
4322
|
-
// Ramp from the last converged
|
|
4323
|
-
//
|
|
4324
|
-
|
|
4325
|
-
const
|
|
4438
|
+
// Ramp from the contacts' voltages at the last converged solve when the state is that
|
|
4439
|
+
// solution (a warm start): the left one first where it moved, the right held where it was,
|
|
4440
|
+
// then the right. Otherwise from level terminals.
|
|
4441
|
+
const solved = this.solvedV;
|
|
4442
|
+
let c;
|
|
4443
|
+
if (solved) {
|
|
4444
|
+
c = solved[0] === level ? { ...r, converged: true, ramped: false } : this._continuation(opts, 0, solved[0], level, r, true, solved[1]);
|
|
4445
|
+
// (Ramping the right from level terminals, solve there first: a sweep's start.)
|
|
4446
|
+
if (c.converged) c = this._continuation(opts, 1, solved[1], target, c, c.ramped || solved[1] !== level);
|
|
4447
|
+
} else c = this._continuation(opts, 1, level, target, r, false);
|
|
4326
4448
|
if (c.converged) {
|
|
4327
|
-
this.solvedV = target;
|
|
4449
|
+
this.solvedV = [level, target];
|
|
4328
4450
|
return c;
|
|
4329
4451
|
}
|
|
4330
4452
|
restart();
|
|
4331
4453
|
r = this._solveSteady(opts); // the full pseudo-transient ramp
|
|
4332
|
-
if (r.converged) this.solvedV = target;
|
|
4454
|
+
if (r.converged) this.solvedV = [level, target];
|
|
4333
4455
|
return { ...r, steps: r.steps + c.steps, iterations: r.iterations + c.iterations };
|
|
4334
4456
|
}
|
|
4335
4457
|
|
|
@@ -4410,13 +4532,15 @@ export class Solver {
|
|
|
4410
4532
|
// Floated, the system can lose what held it doesn't (a closed redox cell driven near its
|
|
4411
4533
|
// limit: its bordered row 33 digits short), but a current-driven steady state is the held
|
|
4412
4534
|
// one at the voltage that passes the target: found by held solves, by regula falsi
|
|
4413
|
-
// (Illinois) within the bracket, to 1e-10 of the target
|
|
4535
|
+
// (Illinois) within the bracket, to 1e-10 of the target, or at open circuit of the largest
|
|
4536
|
+
// current the march saw (a target of 0 is never met exactly).
|
|
4537
|
+
const scale = Math.max(Math.abs(target), ...seen.map(([, I]) => Math.abs(I)));
|
|
4414
4538
|
try {
|
|
4415
4539
|
let side = 0;
|
|
4416
4540
|
for (let it = 0; it < 60; it++) {
|
|
4417
4541
|
const Vm = Va - (fa * (Vb - Va)) / (fb - fa), fm = hold(Vm);
|
|
4418
4542
|
if (!Number.isFinite(fm)) return fail();
|
|
4419
|
-
if (Math.abs(fm) <= 1e-10 *
|
|
4543
|
+
if (Math.abs(fm) <= 1e-10 * scale) return { converged: true, steps, iterations, history, residual: Math.abs(fm) };
|
|
4420
4544
|
if (Math.sign(fm) === Math.sign(fb)) {
|
|
4421
4545
|
[Vb, fb] = [Vm, fm];
|
|
4422
4546
|
if (side === -1) fa /= 2;
|
|
@@ -4474,12 +4598,18 @@ export class Solver {
|
|
|
4474
4598
|
}
|
|
4475
4599
|
}
|
|
4476
4600
|
|
|
4477
|
-
|
|
4601
|
+
// Terminal k (a contact) ramped from `level` to `target` through steady solves, each warm from
|
|
4602
|
+
// the last (`hold`: the other contact's voltage meanwhile, if not its own). A warm ramp that has
|
|
4603
|
+
// nowhere to go converges only if what it started from did (`ramped`): the state it carries
|
|
4604
|
+
// must be some ramp's solution, never just the one a failed solve left.
|
|
4605
|
+
_continuation(opts, k, level, target, r, warm, hold) {
|
|
4478
4606
|
const sub = this._directSteady() ? { ...opts, maxSteps: 1 } : opts;
|
|
4479
4607
|
let V = level, dV = (target - level) / 8, steps = r.steps, iterations = r.iterations;
|
|
4480
4608
|
const history = r.history.slice();
|
|
4609
|
+
if (warm && V === target) return { ...r, converged: r.ramped === true, steps, iterations, history, ramped: r.ramped };
|
|
4481
4610
|
try {
|
|
4482
|
-
this.sourceOverride.set(
|
|
4611
|
+
this.sourceOverride.set(k, V);
|
|
4612
|
+
if (hold !== undefined) this.sourceOverride.set(1 - k, hold);
|
|
4483
4613
|
let q;
|
|
4484
4614
|
if (!warm) {
|
|
4485
4615
|
q = this._solveSteady(opts);
|
|
@@ -4501,7 +4631,7 @@ export class Solver {
|
|
|
4501
4631
|
while (V !== target) {
|
|
4502
4632
|
const next = Math.abs(target - V) <= Math.abs(dV) ? target : V + dV;
|
|
4503
4633
|
const u1 = Float64Array.from(this.u), u1Lo = Float64Array.from(this.uLo), v1 = Float64Array.from(this.termV);
|
|
4504
|
-
this.sourceOverride.set(
|
|
4634
|
+
this.sourceOverride.set(k, next);
|
|
4505
4635
|
q = this._solveSteady(sub);
|
|
4506
4636
|
steps += q.steps;
|
|
4507
4637
|
iterations += q.iterations;
|
|
@@ -4518,9 +4648,10 @@ export class Solver {
|
|
|
4518
4648
|
if (Math.abs(dV) < 1e-6 * Math.abs(target - level)) return { converged: false, steps, iterations, history };
|
|
4519
4649
|
}
|
|
4520
4650
|
}
|
|
4521
|
-
return { converged: true, steps, iterations, history };
|
|
4651
|
+
return { converged: true, steps, iterations, history, ramped: true };
|
|
4522
4652
|
} finally {
|
|
4523
|
-
this.sourceOverride.delete(
|
|
4653
|
+
this.sourceOverride.delete(k);
|
|
4654
|
+
if (hold !== undefined) this.sourceOverride.delete(1 - k);
|
|
4524
4655
|
this._refreshSources();
|
|
4525
4656
|
}
|
|
4526
4657
|
}
|
package/src/stack.js
CHANGED
|
@@ -87,16 +87,18 @@ export function ohmic(d = 0, carriers = ['e-', 'h+']) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* A bath: a neutral composition `c` (mol/m³) held at the contact
|
|
91
|
-
*
|
|
92
|
-
*
|
|
90
|
+
* A bath: a neutral composition `c` (mol/m³) held at the contact. With a `reference`, the charged
|
|
91
|
+
* species a reference electrode would sense (Cl⁻ for Ag/AgCl), its levels are referred to that
|
|
92
|
+
* species, which is the terminal. Without one, `bath(c, drive)`, the terminal voltage is the
|
|
93
|
+
* bath's φ: an ideal salt bridge, the convention of membrane potentials.
|
|
93
94
|
* @param {Record<string, number>} c
|
|
94
|
-
* @param {string} reference
|
|
95
|
+
* @param {string | number | object} [reference] the reference species, or the drive
|
|
95
96
|
* @param {number | object} [drive]
|
|
96
97
|
* @param {{ offset?: number }} [opts] where the reference species sits relative to the terminal
|
|
97
98
|
* voltage, V_ref = V + offset (V)
|
|
98
99
|
*/
|
|
99
100
|
export function bath(c, reference, d = 0, { offset } = {}) {
|
|
101
|
+
if (typeof reference !== 'string') return { ...drive(reference ?? 0, 'bath'), bath: { c: { ...c } } };
|
|
100
102
|
return { ...drive(d, 'bath'), bath: { c: { ...c }, reference, ...(offset === undefined ? {} : { offset }) } };
|
|
101
103
|
}
|
|
102
104
|
|
package/src/types.js
CHANGED
|
@@ -114,7 +114,8 @@
|
|
|
114
114
|
* @typedef {'blocked' | 'equilibrium'
|
|
115
115
|
* | { type: 'equilibrium', offset?: number, mu?: number }
|
|
116
116
|
* | { type: 'conductance', G: number, offset?: number }
|
|
117
|
-
* | { type: 'exchange', k: number, mu: number }
|
|
117
|
+
* | { type: 'exchange', k: number, mu: number }
|
|
118
|
+
* | { type: 'velocity', v: number, offset?: number, mu?: number }} ContactSpeciesLink
|
|
118
119
|
*/
|
|
119
120
|
/**
|
|
120
121
|
* 'bulk': the end node is plain bulk (locally neutral, no double layer); 'neutral': no charge at
|
package/types/channels.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function ghkCurrent(z: any, inside: any, outside: any, V: any, T: any): number;
|
|
2
2
|
/**
|
|
3
|
-
* Hodgkin and Huxley's squid-axon channels
|
|
3
|
+
* Hodgkin and Huxley's squid-axon channels: for a membrane face with the outside on its left
|
|
4
4
|
* (so the gates' voltage, φ_right − φ_left, is the membrane potential): the face's `gates`, and
|
|
5
5
|
* `species` links for Na⁺ (P·m³h), K⁺ (P·n⁴) and the leak (ungated), to spread into the face
|
|
6
6
|
* beside its φ law, `{ phi: { type: 'capacitive', C: 0.01 }, ...hh }`, adding links (or blocks)
|
|
@@ -12,12 +12,21 @@ export function ghkCurrent(z: any, inside: any, outside: any, V: any, T: any): n
|
|
|
12
12
|
* (`leak`, of charge `leakZ`: Cl⁻ by default, whose Nernst level in squid is near their leak
|
|
13
13
|
* reversal, −54 mV). The
|
|
14
14
|
* rates are theirs at 6.3 °C, scaled to `T` by Q₁₀ = 3.
|
|
15
|
+
*
|
|
16
|
+
* Or with `linear: true`, Hodgkin and Huxley's own linear channels on the face: conductance links,
|
|
17
|
+
* G = g, gated the same, whose currents are g(V − E) with E each ion's Nernst level at the
|
|
18
|
+
* concentrations on either side (so `inside`, `outside` and `at` aren't needed).
|
|
19
|
+
*
|
|
20
|
+
* Or, given `area` (membrane per volume of the region, 2/a for an axon of radius a), for a membrane
|
|
21
|
+
* port along a region: links that are their linear conductances, G = g × area per volume, gated the
|
|
22
|
+
* same, each ion's reversal potential following from the port's bath and the concentrations inside
|
|
23
|
+
* (so `inside`, `outside` and `at` aren't needed).
|
|
15
24
|
* @param {{ inside: object, outside: object, T?: number, g?: { Na?: number, K?: number, leak?: number },
|
|
16
|
-
* leak?: string, leakZ?: number, at?: number, names?: { Na?: string, K?: string } }} opts concentrations in
|
|
25
|
+
* leak?: string, leakZ?: number, at?: number, names?: { Na?: string, K?: string }, linear?: boolean, area?: number }} opts concentrations in
|
|
17
26
|
* mol/m³ by species name, K, S/m², V
|
|
18
27
|
* @returns {{ gates: object, species: object }}
|
|
19
28
|
*/
|
|
20
|
-
export function hodgkinHuxley({ inside, outside, T, g, leak, leakZ, at, names }?: {
|
|
29
|
+
export function hodgkinHuxley({ inside, outside, T, g, leak, leakZ, at, names, linear, area }?: {
|
|
21
30
|
inside: object;
|
|
22
31
|
outside: object;
|
|
23
32
|
T?: number;
|
|
@@ -33,6 +42,8 @@ export function hodgkinHuxley({ inside, outside, T, g, leak, leakZ, at, names }?
|
|
|
33
42
|
Na?: string;
|
|
34
43
|
K?: string;
|
|
35
44
|
};
|
|
45
|
+
linear?: boolean;
|
|
46
|
+
area?: number;
|
|
36
47
|
}): {
|
|
37
48
|
gates: object;
|
|
38
49
|
species: object;
|
package/types/checks.d.ts
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* zero; under light it's J = F(G − R), and it says where every carrier went.
|
|
19
19
|
* - **conservation** (a transient): every closed, unreacting stretch keeps what it held plus
|
|
20
20
|
* what came in.
|
|
21
|
-
* - **grid** (
|
|
21
|
+
* - **grid** (with `refine`, the default; in a transient, `ok: null` and a reminder, since the
|
|
22
|
+
* answer depends on the whole run): in a steady state, the device solved again on a grid
|
|
22
23
|
* twice as fine everywhere, from this solution interpolated onto it. The current, each floating terminal's voltage and each
|
|
23
24
|
* region's charge must agree to `tol` (relative, default 1e-2, a plot's accuracy; a benchmark
|
|
24
25
|
* wants less). The discretisation is second order, so the change estimates this grid's error
|
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 { particles } from "./particles.js";
|
|
39
40
|
export { polarization } from "./polarization.js";
|
|
40
41
|
/**
|
|
41
42
|
* One material seen from vacuum: a level inside it (the anchor) and how far above that level,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A swarm of dots sampling a solution's concentrations and fluxes. Amounts are per area (a planar
|
|
3
|
+
* device's). `step(dt)` moves them on in the device's time, at the rates of the solution last
|
|
4
|
+
* given; `update(sol)` gives a new one.
|
|
5
|
+
* @param {object} sol a solution (from solve(), advance(), live(), …)
|
|
6
|
+
* @param {object} [opts]
|
|
7
|
+
* @param {string[]} [opts.species] which species (default: every species with a concentration)
|
|
8
|
+
* @param {number} [opts.dots] about how many dots per species (default 200), for the default weight
|
|
9
|
+
* @param {number|object} [opts.weight] amount per dot, mol/m²: one number for every species (so
|
|
10
|
+
* their dots compare), or { name: weight }; default, each species' total over `dots`
|
|
11
|
+
* @param {number} [opts.cap] the most dots a cell holds on average before it's a sea (default ∞)
|
|
12
|
+
* @param {number} [opts.cells] about how many cells across the device (default 120)
|
|
13
|
+
* @param {() => number} [opts.random] a uniform random source in [0, 1) (default Math.random)
|
|
14
|
+
*/
|
|
15
|
+
export function particles(sol: object, { species, dots, weight, cap, cells, random }?: {
|
|
16
|
+
species?: string[];
|
|
17
|
+
dots?: number;
|
|
18
|
+
weight?: number | object;
|
|
19
|
+
cap?: number;
|
|
20
|
+
cells?: number;
|
|
21
|
+
random?: () => number;
|
|
22
|
+
}): {
|
|
23
|
+
cells: {
|
|
24
|
+
x0: any;
|
|
25
|
+
x1: any;
|
|
26
|
+
region: any;
|
|
27
|
+
}[];
|
|
28
|
+
species: any;
|
|
29
|
+
weight: {};
|
|
30
|
+
sea: {};
|
|
31
|
+
dots: any[];
|
|
32
|
+
events: any[];
|
|
33
|
+
crossed: {};
|
|
34
|
+
time: number;
|
|
35
|
+
update: (next: any, { resample }?: {
|
|
36
|
+
resample?: boolean;
|
|
37
|
+
}) => /*elided*/ any;
|
|
38
|
+
step: (dt: any, { maxHops }?: {
|
|
39
|
+
maxHops?: number;
|
|
40
|
+
}) => /*elided*/ any;
|
|
41
|
+
expected: (nm: any) => Float64Array<ArrayBuffer>;
|
|
42
|
+
};
|
package/types/solution.d.ts
CHANGED
package/types/solver.d.ts
CHANGED
|
@@ -209,9 +209,11 @@ export class Solver {
|
|
|
209
209
|
mixWork: Float64Array<any>[];
|
|
210
210
|
_dc(g: any, k: any, d: any): void;
|
|
211
211
|
bulkRate(rx: any, g: any): number;
|
|
212
|
+
bulkForward(rx: any, g: any): any;
|
|
212
213
|
bulkOneWay(rx: any, g: any): number;
|
|
213
214
|
_srhRate(law: any, cn: any, cp: any, a: any): {
|
|
214
215
|
rate: number;
|
|
216
|
+
forward: number;
|
|
215
217
|
oneWay: number;
|
|
216
218
|
Cn: number;
|
|
217
219
|
Cp: number;
|
|
@@ -294,6 +296,7 @@ export class Solver {
|
|
|
294
296
|
* animation can call it once per frame; the step size carries over between calls.
|
|
295
297
|
*/
|
|
296
298
|
integrate(tEnd: any, opts?: {}): {
|
|
299
|
+
stopped?: string;
|
|
297
300
|
converged: boolean;
|
|
298
301
|
done: boolean;
|
|
299
302
|
steps: number;
|
|
@@ -307,7 +310,7 @@ export class Solver {
|
|
|
307
310
|
};
|
|
308
311
|
};
|
|
309
312
|
landing: any;
|
|
310
|
-
_extrapolate(out: any,
|
|
313
|
+
_extrapolate(out: any, h: any): any;
|
|
311
314
|
_predict(pred: any, bdf: any): {
|
|
312
315
|
scale: number;
|
|
313
316
|
order: number;
|
|
@@ -381,10 +384,13 @@ export class Solver {
|
|
|
381
384
|
_regionShift(yr: any, yi: any): Float64Array<any>[];
|
|
382
385
|
_regionEdgeRows(): Uint8Array<any>;
|
|
383
386
|
_currentCuts(): {
|
|
387
|
+
s: any;
|
|
384
388
|
idx: any[];
|
|
385
389
|
g: any[];
|
|
386
390
|
d: any[];
|
|
387
391
|
}[];
|
|
392
|
+
segmentFluxes(): Float64Array<ArrayBuffer>[];
|
|
393
|
+
faceOneWay(f: any): any;
|
|
388
394
|
_smallSignalProfiles(xr: any, xi: any): {
|
|
389
395
|
phi: {
|
|
390
396
|
re: Float64Array<any>;
|
|
@@ -424,7 +430,7 @@ export class Solver {
|
|
|
424
430
|
iterations: any;
|
|
425
431
|
history: any;
|
|
426
432
|
};
|
|
427
|
-
_continuation(opts: any, level: any, target: any, r: any, warm: any): any;
|
|
433
|
+
_continuation(opts: any, k: any, level: any, target: any, r: any, warm: any, hold: any): any;
|
|
428
434
|
_solveSteady(opts?: {}): {
|
|
429
435
|
converged: boolean;
|
|
430
436
|
steps: number;
|