Skip to main content

Crate dualis_core

Crate dualis_core 

Source
Expand description

dualis-core: the kernel a simulated world’s physics is built on.

This crate knows nothing about any particular physics. It knows that a quantity can vary over space and time, that a process must answer for what it conserves, that a system with no closed form has to be rolled forward, that matter has properties several domains need at once, and that several domains sharing a clock is a scheduling problem with real failure modes. What any of that is about — light, heat, contact, sound — belongs to a domain crate.

That separation is the point. dualis-optics depends on this crate; this crate must never depend on it, or anything else that models a specific physics. If a new domain needs the kernel changed, the kernel was wrong.

§The two invariants

Both survive the generalisation, and both are now enforced rather than promised:

  • Nothing is created or destroyed without being noticed. A Ledger is what a process claims to hold and audit is the check; energy crossing between domains goes through Exchange, which refuses to let a transfer silently lose some. This generalises what SurfaceOptics did for one quantity at one kind of boundary. Where a boundary is resolved into faces, the audit is per face — a redistribution that keeps the total but moves it to the wrong part of a mirror is the one bug a total-only check cannot see.
  • Nothing is random. Rng::for_index gives every piece of work its own stateless stream, so a parallel simulation is still bit-reproducible — which is when the guarantee starts to matter, and when a single shared generator would have quietly lost it.

§What is here

Module
conservedConservation as an audit: ledgers, violations, tolerances
integratorFixed-step time evolution, and why symplectic beats accurate
simSeveral domains on one clock: quasi-static, multirate, iterative coupling
sceneWhere two domains meet: shared boundaries, and flux that knows its place
fieldScalar and vector fields, with gradient, divergence, curl, Laplacian
substanceThermal, mechanical and acoustic properties of matter
motionClosed-form rigid motion and time gating
rngA deterministic generator, and the sampling built on it
transformThe discrete Fourier transform, accurate rather than fast
vectorBasis construction and reflection — the vector maths no domain owns

§A domain, and the audit that checks it

use dualis_core::conserved::quantity;
use dualis_core::{Domain, Exchange, Kind, Ledger, Schedule, Simulation, Violation};
use dualis_core::units::Time;

/// A source that pays out a watt, and says so in its books.
struct Lamp { paid: f64 }
impl Domain for Lamp {
    fn name(&self) -> &str { "lamp" }
    fn kind(&self) -> Kind { Kind::QuasiStatic }
    fn step(&mut self, _t: Time, dt: Time, bus: &mut Exchange) -> Result<(), Violation> {
        let joules = 1.0 * dt.to_si();
        bus.publish(quantity::ENERGY, joules);
        self.paid += joules;
        Ok(())
    }
    // Negative: it is holding a debt, having handed the energy away.
    fn ledger(&self) -> Ledger { Ledger::new().with(quantity::ENERGY, -self.paid) }
}

/// A sink that takes whatever is offered and keeps it.
struct Block { held: f64 }
impl Domain for Block {
    fn name(&self) -> &str { "block" }
    fn step(&mut self, _t: Time, _dt: Time, bus: &mut Exchange) -> Result<(), Violation> {
        self.held += bus.take(quantity::ENERGY);
        Ok(())
    }
    fn ledger(&self) -> Ledger { Ledger::new().with(quantity::ENERGY, self.held) }
    // Opt in to being readable from outside. Without this, `domain_as` returns `None`:
    // the coupling never needs the concrete type, so it is not given away by default.
    fn as_any(&self) -> Option<&dyn std::any::Any> { Some(self) }
    fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> { Some(self) }
}

let mut sim = Simulation::new(Schedule::Staggered)
    .with(Lamp { paid: 0.0 })
    .with(Block { held: 0.0 });
for _ in 0..10 {
    sim.advance(Time::ms(100.0)).expect("the books balance");
}

// A joule went across, and the two ledgers cancel because nothing was lost.
let block: &Block = sim.domain_as("block").unwrap();
assert!((block.held - 1.0).abs() < 1e-12);
assert!(sim.ledger().get(quantity::ENERGY).unwrap().abs() < 1e-12);

Had Block consumed only half of what was published, advance would have returned a Violation naming the channel rather than quietly losing the rest.

Units come from dualis-units and are re-exported below, so a domain crate needs one dependency rather than two.

Re-exports§

pub use conserved::audit;
pub use conserved::Conserves;
pub use conserved::Ledger;
pub use conserved::Violation;
pub use ensemble::Ensemble;
pub use ensemble::Estimate;
pub use field::ScalarField;
pub use field::VectorField;
pub use integrator::velocity_verlet;
pub use integrator::Dynamics;
pub use integrator::Integrator;
pub use integrator::Newtonian;
pub use integrator::State;
pub use motion::Motion;
pub use motion::Strobe;
pub use rng::Rng;
pub use scene::Flux;
pub use scene::Interface;
pub use sim::Domain;
pub use sim::Exchange;
pub use sim::Kind;
pub use sim::Report;
pub use sim::Schedule;
pub use sim::Simulation;
pub use substance::Substance;
pub use transform::fft;
pub use transform::fft2;
pub use transform::fftshift;
pub use transform::ifft;
pub use transform::ifft2;
pub use vector::basis_for;
pub use vector::oriented_against;
pub use vector::reflect;

Modules§

conserved
Conservation, as a thing a process must answer for rather than a property it is trusted to have.
ensemble
Many independent samples, run in parallel, with an answer that does not depend on how many threads did the work.
field
Quantities that vary over space and time.
integrator
Time evolution for systems that have no closed form.
motion
How a scene changes with time, where it changes in closed form.
rng
A deterministic pseudo-random generator, and the sampling built on it.
scene
Where two domains meet, and how a quantity crosses with its place intact.
sim
Running several domains at once.
substance
What a piece of matter is, across every domain that cares.
transform
The discrete Fourier transform, as a kernel utility.
units
Everything from dualis-units, so that use dualis_core::units::* is enough to write dimensioned physics.
vector
Vector arithmetic that no single domain owns.