Skip to main content

dualis for Python

Coupled physics whose conservation audit is an exception you can catch.

pip install dualis        # once it is on PyPI; until then, see Building below
import dualis

sim = dualis.Simulation(schedule="multirate", conservation_tolerance=1e-9)
sim.add_heater("element", watts=2.0, reserve_j=6.0)
sim.add_bar("bar", length_m=0.020, cells=41, area_m2=1e-4)

for _ in range(8):
    sim.advance(0.5)          # raises dualis.Violation if the books do not close

print(sim.temperature("bar"))     # 294.39 K
print(sim.profile("bar")[:3])     # warmer at the fed end

Several bodies and the drop between them

add_lump gives you one temperature for a whole assembly. The number that decides whether a motor survives is the winding, which is hotter than the case by however much the joint between them resists — so for that, a network:

sim = dualis.Simulation(schedule="staggered")
sim.add_heater("losses", watts=6.0, reserve_j=5_400.0)
sim.add_network("motor",
    nodes=[{"name": "winding", "material": "copper", "volume_m3": 18e-6,
            "thickness_m": 2e-3, "initial_k": 298.15},
           {"name": "case", "material": "aluminium", "volume_m3": 220e-6,
            "thickness_m": 4e-3, "initial_k": 298.15,
            "ambient_k": 298.15, "area_m2": 0.042}],   # both keys, or neither
    links=[{"from": "winding", "to": "case", "w_per_k": 0.9}],
    absorbing="winding")

for _ in range(900):
    sim.advance(1.0)

dict(sim.node_temperatures("motor"))     # {'winding': 311.2, 'case': 304.9}
sim.heat_flow_w("motor", "winding", "case")   # 5.585 W crossing the joint

Materials: copper, aluminium, electrical_steel, fr4, pla. A node with ambient_k and area_m2 loses heat to still air; a node with neither is interior. Giving one without the other is refused rather than quietly producing a node that looks like it is cooling and is not.

Names are resolved once, when the network is built, so a link to a node that does not exist raises before any stepping happens and lists the nodes that do. That matters more here than it looks: a link adds +q to one node and −q to another in the same sum, so a missing or misdirected link passes the conservation audit at machine precision and simply reports the wrong temperature forever.

temperature() refuses a network rather than averaging it — the reason to build one is that its nodes differ, so a mean would describe no part of it.

Why this rather than numpy

Write physics in numpy and a wrong model runs happily, producing plausible output. There is no signal; you find out when somebody notices the answer is silly.

Every advance here either moves the clock or raises, and the exception is addressable rather than a sentence to parse:

try:
    sim.advance(0.5)
except dualis.Violation as v:
    v.quantity     # 'energy'
    v.site         # 'bus (published but not consumed)'
    v.before, v.after, v.scale, v.tolerance
    sim.time       # unchanged — a refused step does not advance the clock

Be clear about what it catches. Quantities appearing or vanishing, amounts left unclaimed on the bus, and a domain refusing a step it cannot take. It does not catch a model that is internally consistent and physically wrong. For that, check against something the library did not compute — which is what aluminium_heat_capacity_j_per_k is exported for, and what tests/test_dualis.py does throughout.

What crosses the boundary, and what does not

Values cross as SI floats with the unit in the parameter name: length_m, watts, initial_k. dualis's dimensional types are a compile-time thing — Length + Time does not compile in Rust — and Python cannot have that. A runtime Quantity wrapper would cost an allocation and a check per operation to catch, at run time, a class of error a Python caller mostly does not make, because it passes values in and reads them out rather than doing algebra on them. So the types stay on the Rust side and the units stay in the names.

What crosses instead is the audit, which is the part worth having.

What this cannot do

You cannot write a domain in Python. That means calling back into the interpreter from inside the step loop, holding the GIL across it, and deciding what an exception raised mid-sweep does to a half-advanced simulation. All three are answerable and none cheaply, so this binds the physics the library has and says so rather than shipping a subtly wrong version of that.

So: enough to run and audit coupled physics from Python, not enough to extend it. The extending lives in Rust, and the workspace is where.

A Simulation is also bound to the thread that created it. Domain is not Send, and the binding says so rather than asking the kernel for a bound it does not offer.

Building

Its own cargo workspace, deliberately: pyo3 brings about fifteen crates and links libpython, and the library's lockfile, licence allow-list and WebAssembly jobs are promises that should not have to accommodate a Python extension.

cd bindings/python
pip install "maturin>=1.7,<2.0"
maturin build --release          # an abi3 wheel: one build serves 3.10 upward
pip install target/wheels/dualis-*.whl
python tests/test_dualis.py

maturin develop works too, in a virtualenv. CI builds the wheel, installs it and runs those tests on every commit — because "it compiles" proves neither half of "pip install then import".

Metadata

Release files for dualis 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dualis 0.3.0
File Size Uploaded
dualis-0.3.0.tar.gz 530.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for dualis 0.3.0
File
dualis-0.3.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
dualis-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
dualis-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
dualis-0.3.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
dualis-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 2.1 MB

Release files / dualis-0.3.0.tar.gz

Download URL dualis-0.3.0.tar.gz
Size 530.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9cb5c3857cb2e454654ec94eac583072f1826c58df704035c54798c25608d16a
BLAKE2b-256 checksum
How to use checksums
f2a3d0e77be67f8d78a3e7e7ba489c291eb4533c87dff42eff59a7d7b5f2e85d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / dualis-0.3.0-cp310-abi3-win_amd64.whl

Download URL dualis-0.3.0-cp310-abi3-win_amd64.whl
Size 220.4 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
cae1d0e5bf62cdf6150db7171e98722ceecdd700b9a182dab34a92340a90788c
BLAKE2b-256 checksum
How to use checksums
8255c47277139778b2f2f9399627391baa32845be6eb8e17730598e499d603cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / dualis-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL dualis-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 368.6 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
7b974d10c3f0f05e44cb0a11a4007e1c80d617f810e0dcaa9b80aaa143931fdb
BLAKE2b-256 checksum
How to use checksums
0156205ccfc6bbf36ab35f1ef6c6614127c7ea63893cf288ee14d4c99110e9ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / dualis-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL dualis-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 359.1 kB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
9d00294122ad31d4e539955aa382f46733e651a573871df61f8f9c66fc683085
BLAKE2b-256 checksum
How to use checksums
a431525b13fe6303319cdce07074d8d507079b87d16527360b3713cf31508290
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / dualis-0.3.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL dualis-0.3.0-cp310-abi3-macosx_11_0_arm64.whl
Size 326.9 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
2d88d506508de4f89423ec12ce7b549e73d0c1ad3181c91f2d99fc8bc1b34fba
BLAKE2b-256 checksum
How to use checksums
b66000b196a5c9c36c0a1261f3ea0770d057d64d780fe9418c44de1ec36b315f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / dualis-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL dualis-0.3.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 328.4 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
39aeb85ffb9dc5cc347bd667d4c65d5c8d0daf84dfecca80e5850181ed7af471
BLAKE2b-256 checksum
How to use checksums
2c4e9aed05507e17b47de984a545dfc063e5f6cd58c0b08496d23a04f477cc2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release history Release notifications | RSS feed

0.15.0

6 release files

0.14.0

6 release files

0.13.0

6 release files

0.12.0

6 release files

0.11.0

6 release files

0.10.0

6 release files

0.9.0

6 release files

0.8.0

6 release files

0.7.0

6 release files

0.6.0

6 release files

0.5.0

6 release files

0.4.0

6 release files

This release

0.3.0 This release

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page