Skip to main content
Hardwave treats hardware as a directed acyclic graph of typed components. Understanding a few core concepts is enough to build anything from a resistor network to a robot drivetrain.

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
Components connect into a graph. The simulation engine walks the graph in topological order, calls each component’s solver with its current inputs, and propagates results downstream. You never call 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

For composite or bus types (CAN frames, I2C payloads), subclass HardwaveType and override validate().

Ports

Ports are declared with helper functions:
Each port has a name, a type name (looked up in 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.
See Aggregating Input Ports for built-in modes, custom callables, and connection limits.

Components and the registry

Component classes are registered in the global ComponentRegistry singleton. The @component decorator attaches metadata and registers the class automatically:
See Defining Components for the full authoring guide.

Solvers

Every component has a solver that computes outputs from inputs. The default solver is the component’s own solve() 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

  1. Everything is a component. A resistor, an Arduino, a robot arm: the abstraction never breaks.
  2. Components are defined in code. Python classes are the source of truth.
  3. Solvers are pluggable. Swap a formula for a lookup table or ML model without touching wiring.
  4. Strict typing at the boundary. Type errors are caught at graph construction time.
  5. Composability is first-class. A composite component is indistinguishable from a primitive at its interface.
  6. Data drives fidelity. Components start as formulas and grow into empirically-calibrated ML models.