Everything is a component
A component has:- Ports: typed slots that data flows through (inputs or outputs)
- Parameters: static configuration values that do not change during a simulation run
- A solver: the function that turns inputs into outputs
solve() directly during simulation. The engine does that for you. You can call solve() directly for unit testing.
Types
Types define the semantic meaning of signals.DCVoltage and Current are both floats at runtime, but Hardwave treats them as incompatible. You cannot accidentally wire a voltage output into a current input.
Built-in types
Import the standard library to register 30 hardware signal types:Custom types
HardwaveType and override validate().
Ports
Ports are declared with helper functions:TypeRegistry), and a description. Input ports can be marked optional with a default value.
Single vs aggregating inputs
Fan-out (one output → many inputs) works on any port. Fan-in (many outputs → one input) requires
aggregating_input_port() on the target.
Components and the registry
Component classes are registered in the globalComponentRegistry singleton. The @component decorator attaches metadata and registers the class automatically:
Solvers
Every component has a solver that computes outputs from inputs. The default solver is the component’s ownsolve() method, but you can inject a more sophisticated one at any time:
See Solvers for examples of each.
Simulation domains
Hardwave supports two simulation domains:
Declare the domain in
SimulationConfig. Components with internal state (motors, capacitors, batteries) participate automatically when using ODESolver.
Component health and faults
Components can report runtime health during simulation:
Fault-aware stdlib components (
DCMotor, LiPoCell) expose health and fault_code output ports and emit structured Diagnostic objects into SimulationResult. Override post_solve_diagnostics() and apply_fault_outputs() on custom components to add your own limits.
Component Faults and Health
Full guide to fault codes, FaultMode, and authoring fault rules
Design principles
- Everything is a component. A resistor, an Arduino, a robot arm: the abstraction never breaks.
- Components are defined in code. Python classes are the source of truth.
- Solvers are pluggable. Swap a formula for a lookup table or ML model without touching wiring.
- Strict typing at the boundary. Type errors are caught at graph construction time.
- Composability is first-class. A composite component is indistinguishable from a primitive at its interface.
- Data drives fidelity. Components start as formulas and grow into empirically-calibrated ML models.
