velxio/test/test_circuit/src/components/passive.js

158 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Passive linear components: Resistor, VoltageSource, CurrentSource, Capacitor.
// Each component exposes:
// name, nodes() → [a, b, ...], stampDC(G, b, ctx)
// flags: isVoltageSource, isNonlinear
export class Resistor {
constructor(name, a, b, resistance) {
this.name = name;
this.a = a;
this.b = b;
this.R = resistance;
}
nodes() { return [this.a, this.b]; }
stampDC(G, b, ctx) {
const g = 1 / this.R;
const ia = ctx.nodeIndex(this.a);
const ib = ctx.nodeIndex(this.b);
if (ia >= 0) G[ia][ia] += g;
if (ib >= 0) G[ib][ib] += g;
if (ia >= 0 && ib >= 0) { G[ia][ib] -= g; G[ib][ia] -= g; }
}
}
export class VoltageSource {
constructor(name, plus, minus, voltage) {
this.name = name;
this.plus = plus;
this.minus = minus;
this.V = voltage;
this.isVoltageSource = true;
}
nodes() { return [this.plus, this.minus]; }
setVoltage(v) { this.V = v; }
stampDC(G, b, ctx) {
const ip = ctx.nodeIndex(this.plus);
const im = ctx.nodeIndex(this.minus);
const iv = ctx.vsourceIndex(this.name);
if (ip >= 0) { G[ip][iv] += 1; G[iv][ip] += 1; }
if (im >= 0) { G[im][iv] -= 1; G[iv][im] -= 1; }
b[iv] += this.V;
}
}
export class CurrentSource {
constructor(name, from, to, current) {
this.name = name;
this.from = from; // current flows from → to inside the source (into 'to')
this.to = to;
this.I = current;
}
nodes() { return [this.from, this.to]; }
stampDC(G, b, ctx) {
const ifrom = ctx.nodeIndex(this.from);
const ito = ctx.nodeIndex(this.to);
if (ifrom >= 0) b[ifrom] -= this.I;
if (ito >= 0) b[ito] += this.I;
}
}
/**
* Capacitor (backward Euler companion model).
* G_eq = C / dt
* I_eq = (C / dt) * V_prev
* Stamped as a conductance + current source between a and b.
*/
export class Capacitor {
constructor(name, a, b, capacitance, initialV = 0) {
this.name = name;
this.a = a;
this.b = b;
this.C = capacitance;
this.Vinit = initialV;
}
nodes() { return [this.a, this.b]; }
stampDC(G, b, ctx) {
const ia = ctx.nodeIndex(this.a);
const ib = ctx.nodeIndex(this.b);
const dt = ctx.dt;
if (!dt) {
// Pure DC: treat as open circuit (very large R to keep matrix non-singular)
const g = 1e-12;
if (ia >= 0) G[ia][ia] += g;
if (ib >= 0) G[ib][ib] += g;
if (ia >= 0 && ib >= 0) { G[ia][ib] -= g; G[ib][ia] -= g; }
return;
}
const g = this.C / dt;
const Vprev = ctx.prev
? ((ctx.prev.nodeVoltages[this.a] ?? 0) - (ctx.prev.nodeVoltages[this.b] ?? 0))
: this.Vinit;
const Ieq = g * Vprev;
if (ia >= 0) G[ia][ia] += g;
if (ib >= 0) G[ib][ib] += g;
if (ia >= 0 && ib >= 0) { G[ia][ib] -= g; G[ib][ia] -= g; }
if (ia >= 0) b[ia] += Ieq;
if (ib >= 0) b[ib] -= Ieq;
}
}
/** Potentiometer: two resistors in series with a middle wiper node. */
export class Potentiometer {
constructor(name, top, wiper, bottom, totalR, wiperPos = 0.5) {
this.name = name;
this.top = top;
this.wiper = wiper;
this.bottom = bottom;
this.totalR = totalR;
this.wiperPos = wiperPos; // 0..1, 0 = wiper at bottom, 1 = wiper at top
}
nodes() { return [this.top, this.wiper, this.bottom]; }
setWiper(pos) { this.wiperPos = Math.max(0, Math.min(1, pos)); }
stampDC(G, b, ctx) {
const minR = 1; // avoid 0Ω
const Rtop = Math.max(minR, (1 - this.wiperPos) * this.totalR);
const Rbot = Math.max(minR, this.wiperPos * this.totalR);
new Resistor(this.name + '_top', this.top, this.wiper, Rtop).stampDC(G, b, ctx);
new Resistor(this.name + '_bot', this.wiper, this.bottom, Rbot).stampDC(G, b, ctx);
}
}
/** NTC thermistor. R(T) = R0 · exp(β · (1/T − 1/T0)), T in Kelvin. */
export class NTCThermistor {
constructor(name, a, b, { R0 = 10000, T0 = 298.15, beta = 3950 } = {}) {
this.name = name;
this.a = a;
this.b = b;
this.R0 = R0;
this.T0 = T0;
this.beta = beta;
this.TCelsius = 25;
}
nodes() { return [this.a, this.b]; }
setTemperatureC(c) { this.TCelsius = c; }
resistance() {
const T = this.TCelsius + 273.15;
return this.R0 * Math.exp(this.beta * (1 / T - 1 / this.T0));
}
stampDC(G, b, ctx) {
new Resistor(this.name + '_R', this.a, this.b, this.resistance()).stampDC(G, b, ctx);
}
}
/** Simple switch: R=0.001 closed, R=1e9 open. */
export class Switch {
constructor(name, a, b, closed = false) {
this.name = name;
this.a = a;
this.b = b;
this.closed = closed;
}
nodes() { return [this.a, this.b]; }
set(state) { this.closed = state; }
stampDC(G, b, ctx) {
const R = this.closed ? 1e-3 : 1e9;
new Resistor(this.name + '_R', this.a, this.b, R).stampDC(G, b, ctx);
}
}