1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! dualis: physics for simulated worlds, in one dependency.
//!
//! A facade over the workspace. Nothing is implemented here — the point is that a
//! consumer writes `dualis = "0.1"` rather than naming seven crates, and that the
//! integration tests which need two domains at once have somewhere to live.
//!
//! ```
//! use dualis::prelude::*;
//!
//! // A lamp, a filter, and the question that needs both.
//! let lamp = SpectralPower::new(
//! Spectrum::blackbody(3200.0),
//! Power::w(1.0),
//! VISIBLE_RANGE,
//! );
//! let green = Spectrum::bands(vec![[500.0, 560.0]], 0.95, 0.0);
//! let through = lamp.through(&green);
//! assert!(through < lamp.total());
//! ```
//!
//! # Start here
//!
//! Three ideas carry the whole library.
//!
//! 1. **Units are types.** Dimensions live in the type, so `Length + Time` does not compile.
//! One place may hold a factor of a thousand — a unit-bearing constructor — and `to_si()`
//! is the only way back to a bare `f64`.
//! 2. **A domain is anything that steps.** [`Domain`](dualis_core::Domain) requires
//! `name` and `step`; everything else has a default. Override `ledger` so the audit has
//! something to check.
//! 3. **Domains never call each other.** They meet on
//! [`Exchange`](dualis_core::Exchange), a bus of named channels carrying SI *amounts* —
//! joules, not watts. A ledger says what you are holding, not what has passed through you.
//!
//! And the reason to pick this over a general-purpose engine: conservation is audited every
//! step, so a wrong model does not run quietly. `advance` returns a
//! [`Violation`](dualis_core::Violation) naming the quantity, the site, and the before and
//! after — a correctness signal you can act on without a human noticing first.
//!
//! ```text
//! energy destroyed at simulation: 5.000000e2 became 4.995000e2,
//! a relative change of 1.000e-3 against a tolerance of 1.000e-9
//! ```
//!
//! Be clear about the limit: the audit catches quantities appearing or vanishing, amounts
//! left unclaimed on the bus, and fluxes disagreeing face by face across a shared boundary.
//! It does *not* catch a model that is internally consistent and physically wrong — publish
//! a power where a joule was wanted and both sides agree perfectly about a number off by
//! `1/dt`. For that, check against something the code did not compute: a closed form, an
//! exact limit, or a convergence rate.
//!
//! `cargo run --example agents_quickstart` is all of the above as a running program,
//! including a deliberate leak so the failure is visible. `AGENTS.md` in the repository is
//! the one-page version.
//!
//! # The dependency rule
//!
//! ```text
//! dualis-units no dependencies but glam and serde
//! dualis-core depends on units
//! dualis-optics depends on core
//! dualis-thermal depends on core
//! dualis-mechanics depends on core
//! dualis-acoustic depends on core
//! dualis-molecular depends on core
//! dualis depends on all of them
//! ```
//!
//! None of the five domains knows about any of the others. They meet on the kernel's
//! [`Exchange`](dualis_core::Exchange), and each one that arrived left the others
//! untouched — which is the claim the split was made to test, now held five times.
// Every public item carries a doc comment. Denied rather than warned: a public physics API
// whose `Length::mm` shows a blank summary in rustdoc is documented in the sense that a
// paragraph exists somewhere, and not in the sense a reader needs.
pub use dualis_acoustic as acoustic;
pub use dualis_core as core;
pub use dualis_mechanics as mechanics;
pub use dualis_molecular as molecular;
pub use dualis_optics as optics;
pub use dualis_thermal as thermal;
pub use dualis_units as units;
/// Everything most simulations need, in one `use`.