driftlet 1.2.4 → 1.3.2

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/describe.js CHANGED
@@ -114,6 +114,8 @@ export function unitWarnings(def) {
114
114
  if (!isObject(p)) return;
115
115
  capacitance(`ports[${k}].capacitance.C`, p.capacitance?.C);
116
116
  kinetics(`ports[${k}]`, p.reactions);
117
+ // (an area per volume typed in cm²/cm³ is 100 times too small: a mesh at 10 cm²/cm³ went by)
118
+ if (typeof p.area === 'number' && p.area > 0 && p.area < 100) warn(`ports[${k}].area`, `${num(p.area)} m²/m³ is under 1 cm² of electrode per cm³; area is m²/m³ (1 cm²/cm³ = 100 m²/m³)`);
117
119
  });
118
120
  for (const side of ['left', 'right']) {
119
121
  const c = def.contacts?.[side];
package/src/device.js CHANGED
@@ -415,16 +415,42 @@ export function normalizeDevice(def) {
415
415
  'geometry: the cross-section vanishes inside the device; only an end may have A = 0',
416
416
  );
417
417
 
418
- // Port windows: the nodes of the port's region within [from, to] of its left end.
418
+ // Port windows, [from, to] of the region's left end: the nodes of the port's region whose boxes
419
+ // reach into it, each with the part of its box inside (fill, by length) and that part's ends
420
+ // (lo, hi), so a window holds to − from of electrode, capacitance or exchange however the grid
421
+ // falls. A held level holds the nodes inside the window (inside). A window of no width is the
422
+ // node there, its whole box.
419
423
  ports.forEach((port, k) => {
420
- const nodes = [];
421
- const x0 = grid.x[grid.regionStart[port.region]];
422
- for (let g = grid.regionStart[port.region]; g <= grid.regionEnd[port.region]; g++) {
423
- const d = grid.x[g] - x0;
424
- if (d >= port.from - 1e-15 * port.span && d <= port.to + 1e-15 * port.span) nodes.push(g);
424
+ const nodes = [], fill = [], lo = [], hi = [], inside = [];
425
+ const r0 = grid.regionStart[port.region], r1 = grid.regionEnd[port.region], x0 = grid.x[r0];
426
+ const tol = 1e-15 * port.span, a = x0 + port.from, b = x0 + port.to;
427
+ for (let g = r0; g <= r1; g++) {
428
+ const x = grid.x[g], bl = g > r0 ? x - grid.segLength[g - 1] / 2 : x, br = g < r1 ? x + grid.segLength[g] / 2 : x;
429
+ const isIn = x >= a - tol && x <= b + tol;
430
+ const l = Math.max(bl, a), h = Math.min(br, b);
431
+ if (b - a <= tol ? !isIn : !(h - l > tol)) continue;
432
+ nodes.push(g);
433
+ inside.push(isIn ? 1 : 0);
434
+ if (b - a <= tol) {
435
+ fill.push(1);
436
+ lo.push(bl);
437
+ hi.push(br);
438
+ } else {
439
+ fill.push(br > bl ? (h - l) / (br - bl) : 1);
440
+ lo.push(l);
441
+ hi.push(h);
442
+ }
425
443
  }
426
444
  need(nodes.length > 0, `ports[${k}]: the window [${port.from}, ${port.to}] m holds no grid node; widen it or refine the grid`);
427
445
  port.nodes = Int32Array.from(nodes);
446
+ port.fill = Float64Array.from(fill);
447
+ port.lo = Float64Array.from(lo);
448
+ port.hi = Float64Array.from(hi);
449
+ port.inside = Uint8Array.from(inside);
450
+ need(
451
+ !port.species.some((l) => l.type === 'equilibrium') || inside.some((v) => v),
452
+ `ports[${k}]: the window [${port.from}, ${port.to}] m holds no grid node for its held level; widen it or refine the grid`,
453
+ );
428
454
  });
429
455
  // A node's surface belongs to one electrode, and its gates to one membrane.
430
456
  const surfaced = new Int32Array(grid.nNodes).fill(-1);