> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hardwave.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Saving and Loading Graphs

> Serialize Hardwave simulation graphs to JSON and reload them later

Hardwave graphs are serializable. Save an assembled graph to disk or the Hardwave cloud so it can be reloaded without re-running setup code. That is useful for sharing with teammates or inspecting in the dashboard.

## Serialize to JSON

```python theme={null}
import json

graph_dict = graph.to_dict()
print(json.dumps(graph_dict, indent=2))
```

Example output:

```json theme={null}
{
  "schema_version": "0.1.0",
  "components": [
    {
      "instance_id": "vent_1",
      "class_name": "GreenhouseVentAssembly",
      "class_version": "1.0.0",
      "parameters": { "t_min": 22.0, "t_max": 38.0 }
    }
  ],
  "connections": []
}
```

Composite components store only their external interface. Internal sub-graph wiring is defined by the composite's `build()` method.

## Reload from JSON

```python theme={null}
from hardwave.simulation import SimulationGraph, SimulationEngine

graph_reloaded = SimulationGraph.from_dict(graph_dict)
result = SimulationEngine(graph_reloaded).run(
    inputs={"vent_1": {"temperature": 30.0}},
)
```

`from_dict()` looks up each `class_name` in `ComponentRegistry`, instantiates the component with stored parameters, and reconnects all edges.

<Warning>
  Every component class referenced in a saved graph must be registered in `ComponentRegistry` before calling `from_dict()`. Import `hardwave.stdlib` and register any custom components first.
</Warning>

## Simulation results

Simulation results are also serializable via `result.to_dict()`:

```python theme={null}
result_dict = result.to_dict()
# Keys: domain, time_array, outputs, diagnostics
# Each diagnostic includes: level, code, message, component_id, port_name,
# suggestion, step_index, timestamp, value, threshold

import json
print(json.dumps(result_dict, indent=2)[:300])

# Filter runtime faults before saving
fault_summary = [d for d in result.get_faults()]
```

For transient runs, export to pandas:

```python theme={null}
df = result.to_dataframe()
df.to_csv("simulation_output.csv", index=False)
```

### Embedding a snapshot in saved graph data

When saving to the cloud (see below), you can attach a **simulation snapshot** so the Hardwave dashboard can display outputs and diagnostics without re-running the engine. The snapshot is stored inside `graph_data` under `simulation_snapshot`: no separate database field is required.

```python theme={null}
from hardwave.simulation import build_simulation_snapshot

graph_dict = graph.to_dict()
graph_dict["simulation_snapshot"] = build_simulation_snapshot(
    result,
    inputs={"ctrl": {"temperature": 32.0}},
    config=config,
)
```

Snapshot shape:

```json theme={null}
{
  "schema_version": "0.1.0",
  "components": [...],
  "connections": [...],
  "simulation_snapshot": {
    "result": {
      "domain": "transient",
      "time_array": [0.0, 0.01, ...],
      "outputs": { "pos": { "vent_angle_deg": [72.0, ...] } },
      "diagnostics": []
    },
    "inputs": { "ctrl": { "temperature": 32.0 } },
    "config": { "domain": "transient", "t_end": 3.0, "dt": 0.01 },
    "captured_at": "2026-07-09T11:00:00Z"
  }
}
```

`SimulationGraph.from_dict()` ignores `simulation_snapshot` when reloading. Only `components` and `connections` are used to reconstruct the graph. Re-saving from Python without passing `result=` clears any existing snapshot in the cloud copy.

## Schema version

The current schema version is exposed as `hardwave.SCHEMA_VERSION` (currently `"0.1.0"`). Hardwave raises `DeserializationError` if a saved graph uses an incompatible schema version.

## Roundtrip verification

```python theme={null}
original_result = engine.run(inputs={"vent": {"temperature": 30.0}})
reloaded_graph = SimulationGraph.from_dict(graph.to_dict())
reloaded_result = SimulationEngine(reloaded_graph).run(inputs={"vent": {"temperature": 30.0}})

assert abs(
    reloaded_result.get_output("vent", "vent_state") -
    original_result.get_output("vent", "vent_state")
) < 1e-9
```

## Save to the cloud

On Builder and Crew plans you can save graphs directly from Python. Configure your organisation credentials once (the same setup used for premium components), then call `save()`:

```python theme={null}
import hardwave
import hardwave.stdlib
import hardwave.premium

hardwave.premium.configure(
    organization_id="YOUR_ORG_ID",
    secret_key=open("hardwave_private.pem").read(),
)

graph = hardwave.SimulationGraph()
# ... add components and connections ...

engine = hardwave.SimulationEngine(graph)
result = engine.run(inputs={"vent": {"temperature": 30.0}})

# Save structure only (no dashboard results tab)
graph_id = graph.save("greenhouse-v1", description="Baseline greenhouse model")

# Save with a simulation snapshot (recommended)
graph_id = graph.save(
    "greenhouse-v1",
    description="Baseline greenhouse model",
    result=result,
    inputs={"vent": {"temperature": 30.0}},
)
```

Pass `inputs=` without `result=` to run the engine and snapshot the outcome in one step:

```python theme={null}
graph_id = graph.save(
    "greenhouse-v1",
    inputs={"vent": {"temperature": 30.0}},
)
```

To update an existing saved graph, pass its ID:

```python theme={null}
graph.add_component(...)
graph.save(
    "greenhouse-v2",
    description="Added humidity sensor",
    graph_id=graph_id,
    result=result,
    inputs={...},
)
```

You can also call `hardwave.premium.save_graph()` / `update_graph()` directly, or embed `simulation_snapshot` manually in `graph.to_dict()` before upload.

Saved graphs appear in your organisation's **Graphs** dashboard. When a snapshot is attached, the **RESULTS** tab shows diagnostics, run context (inputs and config), scalar outputs, and time-series charts for transient runs. Authentication uses the same RSA-signed requests as premium component API calls. Your private key never leaves your machine.
