pub struct Exchange { /* private fields */ }Expand description
The channel between domains: named quantities, in SI base units.
A domain publishes what it produced and consumes what it needs. Nothing else crosses between domains, which means every transfer is in one place and can be checked in one place.
Implementations§
Source§impl Exchange
impl Exchange
Sourcepub fn publish(&mut self, channel: &'static str, si_amount: f64)
pub fn publish(&mut self, channel: &'static str, si_amount: f64)
Offer an amount on a channel. Repeated publishes accumulate, so several surfaces can each contribute to one heat load.
Sourcepub fn take(&mut self, channel: &'static str) -> f64
pub fn take(&mut self, channel: &'static str) -> f64
Take everything on a channel, recording that it was taken. The channel is left empty: an amount consumed twice would be an amount doubled.
Take the share of a channel that belongs to a substep of length dt.
For a domain that subcycles. Exchange::take empties the channel, which is right for
a domain stepping once per interval and wrong for one stepping many times: a publisher
offers a whole outer step’s worth at once, so the first substep would take all of it and
the rest would find the channel dark. Every joule of the interval then lands at its
beginning, and refining the substep stops improving the answer — see
Schedule::Multirate, where the measured error is 26% at a 300 s outer step whatever
the substep count.
The share is taken against the time remaining, not against the whole interval. That is
what makes it exact: after handing out A·dt/T and reducing both, A/T is unchanged, so
the last substep — which asks for at least what is left — receives the remainder and the
channel ends empty to the last bit. Apportioning against the whole interval instead
leaves O(n·ε·A) stranded, and Exchange::audit_transfers uses an absolute tolerance
that would eventually refuse it.
Falls back to Exchange::take when the interval is unknown, so a domain written
against this works unchanged under a bare Exchange and under
Schedule::Staggered, where it steps once and the share is the whole.
Sourcepub fn covering(&mut self, dt: Time)
pub fn covering(&mut self, dt: Time)
Tell the bus what interval the current sweep covers, so Exchange::take_share can
apportion. Called by Simulation::advance; a standalone Exchange need not.
Sourcepub fn publish_on(
&mut self,
interface: &Interface,
channel: &'static str,
flux: &Flux,
) -> Result<(), Violation>
pub fn publish_on( &mut self, interface: &Interface, channel: &'static str, flux: &Flux, ) -> Result<(), Violation>
Offer an amount that knows where on a boundary it landed.
The spatial counterpart of publish, and the reason
scene exists: a coating absorbs where the beam is, and a lumped
number cannot say that. Repeated publishes accumulate face by face, so two
mechanisms heating the same surface add up in place.
Refuses a Flux whose face count does not match the interface. Silently padding
or truncating would put energy on the wrong part of the boundary, which is worse
than losing it — losing it the audit would catch.
Sourcepub fn take_on(
&mut self,
interface: &Interface,
channel: &'static str,
) -> Result<Flux, Violation>
pub fn take_on( &mut self, interface: &Interface, channel: &'static str, ) -> Result<Flux, Violation>
Take everything offered on an interface’s channel, leaving it empty.
Returns zeros rather than an error when nothing was published, because a consumer
stepping a boundary that happens to be dark this step is not a fault. A face-count
disagreement is, and is reported: the two sides do not share a discretisation, and
the fix is Flux::resample at whichever side owns the decision.
Sourcepub fn peek_on(
&self,
interface: &Interface,
channel: &'static str,
) -> Option<&Flux>
pub fn peek_on( &self, interface: &Interface, channel: &'static str, ) -> Option<&Flux>
Look at a spatial channel without taking it.
Sourcepub fn unclaimed(&self) -> impl Iterator<Item = (String, f64)> + '_
pub fn unclaimed(&self) -> impl Iterator<Item = (String, f64)> + '_
Channels that were published to but never taken from, with what is left on them. Energy sitting here at the end of a step is energy that left one domain and arrived nowhere.
Spatial channels appear as "interface/channel", with the total left on them.
Sourcepub fn audit_transfers(&self, site: &str, abs_tol: f64) -> Result<(), Violation>
pub fn audit_transfers(&self, site: &str, abs_tol: f64) -> Result<(), Violation>
Fail if anything published was not consumed.
This is the check that catches a coupling whose two sides disagree — a surface that absorbed 3.7 mW handing it to a mesh that received 3.4 mW because the interpolation between their discretisations lost the rest.
The original design said that, and then could not check it: with one number per channel there was no discretisation to disagree about. Spatial channels close that gap, and they are audited face by face rather than on their total — a redistribution that moves heat from one side of a mirror to the other keeps the sum exactly right, so a total-only check would pass the one bug the spatial coupling exists to prevent. The failure names the face.
Sourcepub fn total_consumed(&self, channel: &str) -> f64
pub fn total_consumed(&self, channel: &str) -> f64
Total taken from a channel over the run, for reporting.
Sourcepub fn total_consumed_on(
&self,
interface: &Interface,
channel: &'static str,
) -> f64
pub fn total_consumed_on( &self, interface: &Interface, channel: &'static str, ) -> f64
Total taken from a spatial channel over the run, summed over its faces.
Sourcepub fn clear_offers(&mut self)
pub fn clear_offers(&mut self)
Empty the offers, keeping the running consumption totals.