Skip to main content

SimWeave Integration Plan

This document maps the SimWeave public API onto EdgeWeave nodes, lays out a phased implementation order, and lists the demos we want to reproduce as .weave files.

Goals​

  1. Make every SimWeave subsystem usable from the EdgeWeave canvas without writing Python.
  2. Keep the node naming predictable: simweave_<class> for constructors, simweave_<class>_<verb> for methods, simweave_plot_* for viz.
  3. Produce a small set of .weave exemplars that mirror the most instructive demos in the SimWeave repo, so users can open them as starting points.
  4. Preserve a clean Python export path — every node carries a codegen.python hint so an exported .weave is a runnable Python script.

Mental model​

SimWeave classes fall into a few execution roles. EdgeWeave's port graph maps onto these roles cleanly:

SimWeave roleEdgeWeave node shape
Construction (MassSpringDamper(...), Service(...))"Construct" node — fields → instance output
Action (sw.simulate(...), env.run())"Action" node — inputs + fields → result
Result (SimulationResult, MCResult)Pass-through with summary preview
Visualisation (sw.plot_*)"Viz" node — always_preview=True, returns Plotly figure
Recorder (QueueLengthRecorder)Side-channel attached to an entity

A SimEnvironment is a special case: it's a context, not a value, so we treat it as the central hub node — entities register against it.

Subsystem coverage​

simweave.continuous​

Highest-priority subsystem for the first slice — visually compelling, single-entity demos, no factory functions, no recorders. Map:

Class / functionNodeInputsFieldsOutput
MassSpringDampersimweave_msd—m, c, ksystem
SimplePendulumsimweave_pendulum—g, Lsystem
SeriesRLCsimweave_rlc—R, L, Csystem
ThermalRCsimweave_thermal_rc—R, C, T_infsystem
TwoMassThermalsimweave_thermal_two_mass—paramssystem
QuarterCarModelsimweave_quarter_car—paramssystem
simulate(...)simweave_simulate_continuoussystem, optional x0t_start, t_end, dt, x0_string, methodSimulationResult
ContinuousProcesssimweave_continuous_processsystemmethod, n_substepsprocess (registers with env)

Constructor signatures for SimplePendulum, SeriesRLC, ThermalRC, TwoMassThermal, QuarterCarModel need to be confirmed against SIMWEAVE_API.md before implementation — the SKILL only confirms MassSpringDamper(m, c, k). Until verified, ship simweave_msd only and stub the others as TODOs.

simweave.discrete​

ClassNodeNotes
Queue(maxlen, name)simweave_queuesink-style buffer
PriorityQueue(...)simweave_priority_queuepriority sort key — text field with allow-list
Service(capacity, buffer_size, next_q, default_service_time, rng, name)simweave_servicenext_q is a port (Queue input)
ArrivalGenerator(interarrival, factory, target, rng, name)simweave_arrival_generatorinterarrival and factory are callables — see "callable inputs" below
Resource, ResourcePoolsimweave_resource, simweave_resource_poolinputs to Service-like nodes
EntityPropertiesinline / hiddenusually built inside the factory function

Callable inputs (open design issue). ArrivalGenerator.factory and .interarrival are arbitrary Python callables. Three options ranked by user friction:

  1. Distribution presets node: a simweave_distribution node with fields kind={exponential|uniform|normal|deterministic} and params={rate=...}. Output is the distribution callable. Used for interarrival and for EntityProperties.service_time.
  2. Factory builder node: simweave_entity_factory with fields for common per-entity properties (service_time distribution, priority, etc.). Output is a factory(env) -> Entity.
  3. Code-string escape hatch: a simweave_python_callable node with a multi-line text field. Compiled once via exec. Used only when the preset nodes don't cover the case.

Recommended initial cut: ship (1) + (2). Add (3) only if users hit walls.

simweave.agents & simweave.spatial​

Class / functionNodeNotes
grid_graph(rows, cols, diagonal)simweave_grid_graphsmall, atomic
Graph(...) (general)defer to v2 — needs node/edge editor
Agent(graph, start_node, tasks, speed, heuristic, name)simweave_agentheuristic is a preset dropdown (manhattan/euclidean/chebyshev)
a_star, dijkstra (function-only paths)simweave_pathinputs: graph, start, goal

tasks is a list of node identifiers. Use a text field of comma- separated tuples ("7,11; 3,4") and parse client-side, or accept a JSON list via an input port.

simweave.supplychain​

ClassNode
InventoryItems(...)simweave_inventory_items — fields are parallel lists
Warehouse(inventory, name)simweave_warehouse — input port for InventoryItems

Keep InventoryItems simple in v1: parallel comma-separated text fields per attribute (part_names, unit_cost, stock_level, etc.), parsed in the handler. v2: a richer table-editor node.

simweave.mc​

FunctionNodeNotes
run_monte_carlo(replicate, n_runs, executor)simweave_run_monte_carloreplicate is a callable — needs the same design as discrete factories
run_batched_mc(batched, n_runs, seed)simweave_run_batched_mcbatched likewise
MCResult.mean / std / quantilesimweave_mc_summaryinput: MCResult, fields: which statistic + q for quantile

Practical first cut: implement simweave_run_batched_mc paired with a simweave_python_callable node so we can demo a 1-line rng.normal(0, 1, size=(n, 100)) step without designing the full factory builder.

simweave.viz​

FunctionNodealways_preview
plot_state_trajectories(result)simweave_plot_state_trajectoriesyes
plot_phase_portrait(result, x_idx, y_idx)simweave_plot_phase_portraityes
plot_mc_fan(mc, times, percentiles)simweave_plot_mc_fanyes
plot_queue_length(qrec)simweave_plot_queue_lengthyes
plot_service_utilisation(urec)simweave_plot_service_utilisationyes
plot_warehouse_stock(wrec)simweave_plot_warehouse_stockyes
plot_agent_path(agent, graph)simweave_plot_agent_pathyes
theme registry (set_default_theme, register_theme)settings panel, not a node

Recorders (QueueLengthRecorder, ServiceUtilisationRecorder, WarehouseStockRecorder) are side-channel entities. Each viz node that consumes a recorder gets a sibling simweave_record_<thing> constructor node.

simweave.units & simweave.currency​

Out of scope for v1. These are value types used inside other nodes; they don't need their own canvas presence except possibly a simweave_money constructor for demo workflows that show FX conversion. Defer.

Implementation phasing​

Phase 1 — Continuous physics vertical slice ✅​

Goal: a user can drag four nodes onto the canvas, wire them, hit Run, and see the mass-spring-damper response. Files:

projects/demo_project/modules/user_blocks/
simweave_continuous.py # simweave_msd, simweave_simulate_continuous,
# simweave_plot_state_trajectories,
# simweave_plot_phase_portrait
projects/demo_project/
simweave_msd.weave # canvas-ready exemplar

Demos reproduced:

  • demos/01_mass_spring_damper.py — direct mapping (simweave_msd.weave)
  • (stretch) demos/14_viz_tour.py — once more viz nodes exist

Phase 2 — Continuous fan-out ✅​

Constructor signatures confirmed against simweave/continuous/systems/*.py (uploaded source). Nodes added in backend/core/nodes/simweave/simweave_continuous.py:

  • simweave_pendulum — SimplePendulum(length, mass, gravity, damping)
  • simweave_rlc — SeriesRLC(resistance, inductance, capacitance)
  • simweave_thermal_rc — ThermalRC(thermal_resistance, thermal_capacitance, ambient_temperature, initial_temperature)
  • simweave_thermal_two_mass — TwoMassThermal(C_core, C_sink, k_core_to_sink, R_sink_to_ambient, ambient_temperature, initial_core, initial_sink)
  • simweave_quarter_car — QuarterCarModel(sprung_mass, unsprung_mass, suspension_stiffness, damping, tyre_stiffness)

Also fixed in this phase: the original simweave_msd used m/c/k based on the SKILL example, but the source uses mass/damping/ stiffness. Field names and codegen hints updated; the simweave_msd.weave exemplar was migrated to the new keys.

.weave exemplars (in projects/demo_project/):

  • simweave_msd.weave
  • simweave_pendulum.weave
  • simweave_rlc.weave
  • simweave_thermal_rc.weave
  • simweave_quarter_car.weave

TwoMassThermal doesn't have a dedicated exemplar yet — its larger parameter set is better introduced via a documented walkthrough rather than a bare canvas.

Phase 3 — Discrete vertical slice ✅ (initial)​

Ships simweave_arrival_generator + simweave_service + simweave_sink + simweave_discrete_run + simweave_plot_queue_length + simweave_plot_service_utilisation.

Files:

projects/demo_project/modules/user_blocks/
simweave_discrete.py # all six nodes plus DiscreteSpec / DiscreteRunResult
projects/demo_project/
simweave_mm1.weave # canvas-ready M/M/1 exemplar

Recorders are folded into the producer block as boolean flags (record_queue_length, record_utilisation) instead of separate simweave_record_* nodes — keeps the canvas readable for the common case. Standalone recorder nodes can be added later if a use case needs them at non-default sample points.

Edge semantics: edges between discrete blocks denote entity flow, not data — see the cross-cutting "Edge semantics" section below. This deliberately mirrors the torch nodes' upstream port pattern (backend/core/nodes/torch_nodes.py, the LayerSpec graph).

Demos reproduced:

  • the M/M/1-ish queue from the SimWeave SKILL examples (simweave_mm1.weave)

Deferred to Phase 3.x:

  • simweave_priority_queue, simweave_resource, simweave_resource_pool
  • Branching / fan-out routing (a router block, or a Service subclass with a per-entity next_q selector)

Phase 4 — Agents on a graph​

simweave_grid_graph, simweave_agent, simweave_path, simweave_plot_agent_path. Demo: pick the simplest A*/dijkstra showcase from demos/.

Phase 5 — Supply chain & Monte Carlo​

simweave_inventory_items, simweave_warehouse, simweave_record_warehouse_stock, simweave_plot_warehouse_stock. Add simweave_run_batched_mc + simweave_plot_mc_fan for the ensemble fan-chart demo.

Phase 6 — Polish​

  • Theme presets exposed in EdgeWeave Settings, mapped to sw.set_default_theme(...).
  • Money / FX nodes if a demo asks for them.
  • Code-export tests: ensure .weave → .py round-trip runs without EdgeWeave installed.

Cross-cutting decisions​

Optional dependency. SimWeave is registered as simweave[viz] in backend/requirements.txt. Each node module top-level imports SimWeave inside a try/except ImportError so the EdgeWeave backend boots even when the package is absent — handlers fail with a clear pip install simweave[viz] message at execution time.

Plotly previews. Viz nodes return the figure as the output value and a self-contained HTML preview via fig.to_html(include_plotlyjs="cdn", full_html=False). This matches the style of backend/core/nodes/plotly_nodes.py and works in the existing preview div without any frontend changes. Codegen for viz nodes should emit import simweave as sw + the corresponding sw.plot_* call.

Stateful instances on the wire. Service, Warehouse, etc. carry mutable state. EdgeWeave already passes Python objects between nodes by reference — we rely on the engine's existing copy/cache semantics. A worth-flagging risk: re-running a graph re-instantiates each construction node, so previously-recorded state is dropped between runs. This is the desired behaviour but should be called out in user docs.

Categories. All SimWeave nodes use the SimWeave top-level category in the sidebar, with sub-categories per subsystem rendered via the category field as "SimWeave / Continuous", "SimWeave / Discrete", etc.

Edge semantics — three flavours of connection. EdgeWeave today treats every wire as "data passes from upstream output to downstream input". As we add SimWeave (and revisit control-flow nodes for loops/conditionals), at least three semantically-distinct edge kinds emerge:

KindCarriesExamplesTopo direction
Data flow (default)A value (DataFrame, tensor, scalar)Most existing nodesUpstream produces, downstream consumes.
Execution / control flowNothing — signals "run this block before that block"Torch LayerSpec upstream port; future for/while/if nodesUpstream completes before downstream starts.
Entity flowConceptually "tokens / entities move from A to B"SimWeave discrete blocks; future SimEvents-style nodesVisual direction = entity direction; construction may run in reverse.

Concrete handling today:

  • Torch and SimWeave-discrete edges both pass a lightweight handle (LayerSpec / DiscreteSpec). The handle is technically a "data" payload, but the user-visible meaning is execution-order / entity-flow respectively.
  • SimWeave-discrete blocks defer SimWeave-object construction to the terminal Run node, because SimWeave constructors expect downstream refs (Service(next_q=…), ArrivalGenerator(target=…)). The Run node topo-sorts sinks-first and constructs in dependency order.

Open follow-ups (not blocking either Phase 1 or Phase 3):

  1. Adopt a port-colour convention so users can tell the three edge kinds apart at a glance — e.g. data = grey, execution = orange, entity = blue.
  2. Audit the existing torch upstream port and the future for-loop / if-statement nodes to use a single shared EXEC_PORT_KIND constant.
  3. Decide whether to surface a stricter "edge kind" annotation in the node-meta dict (e.g. inputs: [{"name": "upstream", "kind": "exec"}]) so the validator can refuse mixed-kind connections.

Open questions for the user​

  1. Constructor signatures. ✅ Resolved — confirmed from the uploaded simweave/continuous/systems/*.py source. Phase 2 nodes landed.

  2. Factory function UX. Distribution-preset node + factory-builder node (recommended) versus a raw "compile this Python source" node (escape hatch). User leaves this to recommendation; provisional plan is the preset + builder pair, with the raw-Python node held in reserve for power users when a real demo demands it. Phase 3.x.

  3. Theme integration. ✅ Resolved — Plotly-only theme. Wire it through EdgeWeave Settings (panel control → backend config → sw.set_default_theme(...) called once during backend startup, re-applied on settings save). No per-graph theme node for now.

  4. Demo .weave location. ✅ Resolved — projects/demo_project/ for now; revisit when the example set grows large enough to warrant a dedicated examples/ folder or an in-app picker.

Newly raised​

  1. Worker cold-start cost. Each graph run spins a fresh subprocess that imports every node module — incl. heavyweights like torch that the user's graph may not touch. Selective per-graph module loading via a build-time manifest is captured separately in Selective Module Loading. Out of scope for the SimWeave work itself, but the worst case (cold-start on a simple simweave[viz] graph alongside a torch-loaded backend) is the kind of thing it would fix.

SimWeave skill / doc discrepancies​

While building the Phase 1–3 nodes we had to verify several constructor and method signatures against the uploaded simweave/**/*.py source. A few details in the local SimWeave skill (~/.claude/skills/simweave/) disagree with the source of truth. Recording them here so the skill can be patched in one pass:

SurfaceSkill saysActual source saysNotes
MassSpringDamper.__init__m, c, kmass, damping, stiffness, x0Long-form names; verified against simweave/continuous/systems/mass_spring_damper.py. The short-form m/c/k are not aliases — they raise TypeError.
SimEnvironment.__init__skip_idle_gaps=False acceptedonly start, dt, end, graph acceptedskip_idle_gaps is a parameter on env.run(until, skip_idle_gaps=False), not the constructor. Passing it to __init__ raises TypeError: __init__() got an unexpected keyword argument 'skip_idle_gaps'. Verified against simweave/core/environment.py.
SimplePendulum.__init__(varies)length, mass=1.0, gravity=9.81, damping=0.0, x0=NoneNote gravity (full word), not g.
SeriesRLC.__init__(varies)resistance, inductance, capacitance, x0=NoneLong-form names.
ThermalRC.__init__(varies)thermal_resistance, thermal_capacitance, ambient_temperature, initial_temperatureAll long-form.
Service.__init__(varies)capacity=1, buffer_size=10, next_q="terminus", resources=None, default_service_time=1.0, rng=None, name=Nonenext_q defaults to the sentinel string "terminus".
Queue.__init__(varies)maxlen=10, name=None, next_q="terminus"Note maxlen, not capacity.
ArrivalGenerator.__init__(varies)interarrival, factory, target, rng=None, name=Noneinterarrival is a callable taking an np.random.Generator; factory takes a SimEnvironment.

When updating the SKILL.md, the two breaking discrepancies are the first two rows — those crashed our nodes outright. The rest are flagged for completeness so the skill matches the canonical source.

Codegen / export reliability (2026-08-12)​

A dedicated pass (feature/codegen-export-coverage) actually exported every shipped .weave demo through the real pipeline and executed the generated script, not just eyeballed it or compiled it — several bugs were syntactically valid Python that only failed at runtime. See backend/tests/test_demo_exports.py for the permanent regression test (parametrized over every file in projects/demo_project/*.weave).

Fixed, affecting SimWeave nodes directly:

  • simweave_arrival_generator/simweave_service/simweave_sink's "imports" baked in the import /from keyword (same bug class as the numpy_io/image_read fix from the layers slice) — produced import import simweave as sw.
  • Those same three nodes' expr used {name!r} — the generator's substitution already applies repr() itself and never matches the literal substring {name} inside {name!r}, so the placeholder was left completely unsubstituted (a real SyntaxError, not just broken output).
  • simweave_simulate_continuous's expr wrote method='{method}' (manually quoted) — combined with the generator's own repr(), this produced double-quoted method=''rk4''. Also fixed x0_string (a CSV field like "1.0,0.0") being substituted as a single quoted-string array element instead of being parsed into actual numbers.

Known, deliberately unfixed (tracked as xfail in test_demo_exports.py, each flagged as its own follow-up task rather than patched speculatively):

  • Discrete SimWeave can't export at all. simweave_arrival_generator needs a downstream Queue as its target= argument, which per-node codegen has no way to supply — this is exactly why the runtime handlers defer real object construction to simweave_discrete_run's own sink-first traversal (see simweave_discrete.py's module docstring). Fixing this needs the same deferred-construction trick reproduced in generated code, not a template patch.