Skip to main content

Winding

Struct Winding 

Source
pub struct Winding { /* private fields */ }
Expand description

A length of conductor carrying current, dissipating I²R onto the heat channel.

The resistance is computed from the conductor’s geometry and its temperature rather than stated, so it is a model rather than a number: get the length or the cross-section wrong and the dissipation is wrong in a way a closed form can catch.

Implementations§

Source§

impl Winding

Source

pub fn of_copper( name: impl Into<String>, length: Length, cross_section_m2: f64, at: Temperature, ) -> Winding

A winding of copper: length of wire with a given cross-section, at a temperature.

R = ρ(T)·L/A. The cross-section is in square metres — 0.35 mm² of magnet wire is 0.35e-6, which is roughly AWG 22.

Source

pub fn of_resistance( name: impl Into<String>, at_20c: Resistance, alpha: f64, at: Temperature, ) -> Winding

A winding of stated resistance at 20 °C, for a conductor this crate has no geometry for.

alpha is the temperature coefficient per kelvin; pass 0.0 for a resistor whose coefficient you do not want to model, which is the honest choice for a wirewound part whose datasheet quotes a tolerance band rather than a number.

Source

pub fn driven_at(self, current: Current) -> Winding

Drive it at a constant current. Dissipation is I²R and rises as it warms.

Source

pub fn driven_from(self, voltage: Voltage) -> Winding

Drive it from a constant voltage. Dissipation is V²/R and falls as it warms.

The opposite sign of feedback from driven_at, and the difference decides whether a runaway is possible at all: a constant-current winding can run away, a constant-voltage one cannot.

Source

pub fn with_reserve(self, joules: f64) -> Winding

Joules it may dissipate before it goes quiet.

Not optional in practice. Without it the reserve is infinite, and step refuses — see the comment there for why the refusal is the domain’s job rather than the audit’s. Saying where the energy comes from is the point.

Source

pub fn at_temperature(&mut self, t: Temperature)

Tell it what temperature it is now.

The manual half of a coupling this crate cannot close by itself. A domain cannot read another’s temperature inside the step loop — see the module documentation — so a caller wanting the electro-thermal feedback reads the thermal domain’s temperature between steps and passes it here.

Source

pub fn resistance(&self) -> Resistance

Resistance at its current temperature: R₂₀(1 + α(T − 20 °C)).

Source

pub fn dissipation(&self) -> Power

Power it is dissipating right now.

Source

pub fn resistance_at(&self, at: Temperature) -> Resistance

Resistance at an arbitrary temperature, without changing the winding.

Source

pub fn dissipation_at(&self, at: Temperature) -> Power

What it would dissipate at a temperature, without changing the winding.

A pure function, and that is the point. Everything else here is a Domain method, which means it can only be reached by stepping — and the electro-thermal feedback needs P(T) evaluated at a temperature the electrical domain has no way to learn, since Exchange carries amounts and not state.

As a plain function it is composable by whoever does hold both sides. A caller with a thermal network and a winding can write coil.dissipation_at(net.temperature(node)) between frames — which is what pantometry-world’s scene 13 does — and any future in-loop coupling needs this same function rather than a different one. So it is correct under every answer to that design question, which is why it exists before the question is settled.

Source

pub fn runaway_current(&self, path: Conductance) -> Option<Current>

The current at which this winding’s feedback overcomes a heat path of conductance g.

Thermal runaway is dP/dT > dQ_out/dT. For a constant-current winding P = I²R₂₀(1+αΔT) so dP/dT = I²R₂₀α, and against a path that sheds g watts per kelvin the threshold is exact:

    I_crit = √( g / (R₂₀ α) )

g is the conductance of the whole path to ambient, which for anything with joints in it is not the surface’s. A motor whose winding reaches air through 0.9 W/K and 2.4 W/K of joints and then 0.294 W/K of convection has a series g of 0.203 W/K, not 0.294 — and the threshold falls from 4.95 A to 4.11 A, a 17% margin a lumped model reports as present when it is not. That difference is the argument for ThermalNetwork over one body.

Do not assemble g by hand. Those three numbers are a convection-only path, and the real one on that motor is 0.220 W/K because the housing also radiates at its operating temperature — so the hand-computed 4.11 A understates the true 4.28 A by 4%. Ask the network: ThermalNetwork::path_conductance(node, at) takes the slope of its own solved balance and therefore includes every path out, radiative terms and interior environments alike. The hand formula was found to be wrong by a sizing tool written against 0.6.0, which is what FRICTION.md 20 is about.

Returns None for a voltage-driven winding, which cannot run away: P = V²/R falls as it warms, so the feedback has the opposite sign and there is no threshold to report.

Source

pub fn current(&self) -> Current

The current through it, whichever way it is driven.

Source

pub fn voltage(&self) -> Voltage

The voltage across it, whichever way it is driven.

Source

pub fn dissipated_energy(&self) -> Energy

Joules it has put onto the bus over the run.

Source

pub fn reserve(&self) -> Energy

Joules it has left to spend.

Trait Implementations§

Source§

impl Domain for Winding

Source§

fn kind(&self) -> Kind

Nothing here is integrated: the dissipation is a closed-form function of the state, so there is no state to march and no stability limit to respect.

Source§

fn ledger(&self) -> Ledger

What is left to spend, not what has passed through.

The joules published are gone from here and are being reported by whoever took them. Adding them back would make the total grow by the heat that moved, which this workspace has written down more than once as the most common way to author a domain that audits green and is wrong.

Source§

fn readings(&self) -> Vec<Reading>

What it is dissipating, at what resistance, and what it has spent.

The resistance is here because it is the reason the dissipation moves: at fixed current the power ratio is the resistance ratio, and a table with both columns shows that in a way a table with one cannot.

Source§

fn books_balance(&self) -> bool

Whether this domain’s books are exact: its ledger changes by precisely what it takes from the bus minus what it publishes, every step. Read more
Source§

fn name(&self) -> &str

What this domain is called. Used to look it up and to name it in a violation. Read more
Source§

fn step( &mut self, _t: Time, dt: Time, bus: &mut Exchange, ) -> Result<(), Violation>

Advance by dt from t, reading inputs from bus and publishing outputs to it. A quasi-static domain ignores dt. Read more
Source§

fn as_any(&self) -> Option<&dyn Any>

This domain as Any, so a caller can get the concrete type back out of a Simulation — see Simulation::domain_as. Read more
Source§

fn as_any_mut(&mut self) -> Option<&mut dyn Any>

The same, mutably, so a caller can write to a domain between steps. Read more
Source§

fn max_stable_dt( &self, now: Qty<0, 0, 1, 0, 0, 0, 0>, ) -> Qty<0, 0, 1, 0, 0, 0, 0>

The largest step this domain can take from now and stay stable — a CFL condition, a diffusion limit, a contact penetration budget. Read more
Source§

fn residual(&self) -> f64

How far this domain still is from agreeing with its neighbours, for Schedule::Iterative. Zero means converged.
Source§

fn checkpoint(&mut self)

Save state so an iterative sweep can be re-run from the same starting point. A domain that does not implement this cannot take part in Schedule::Iterative, and Simulation::advance says so rather than silently iterating from the wrong state.
Source§

fn restore(&mut self)

Restore the last Domain::checkpoint.
Source§

fn supports_restore(&self) -> bool

Whether Domain::checkpoint and Domain::restore actually do something. Read more
Source§

fn as_field(&self) -> Option<&dyn ScalarField>

This domain as a ScalarField, if it has one to show. Read more
Source§

fn as_bodies(&self) -> Option<&dyn Bodies>

This domain as a countable set of bodies, if that is what it is. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.