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 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.
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.