You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"source": "# Checkpoints\n\nPathSim supports saving and loading simulation state via checkpoints. This allows you to pause a simulation, save its complete state to disk, and resume it later from exactly where you left off. \n\nCheckpoints also enable **rollback**, where you return to a saved state and explore different what-if scenarios by changing parameters.\n\nCheckpoints use a split format: a JSON file for metadata and structure, and an NPZ file for numerical data (block states, solver histories, etc.)."
6
+
"source": [
7
+
"# Checkpoints\n",
8
+
"\n",
9
+
"PathSim supports saving and loading simulation state via checkpoints. This allows you to pause a simulation, save its complete state to disk, and resume it later from exactly where you left off.\n",
10
+
"\n",
11
+
"Checkpoints also enable **rollback** — returning to a saved state and exploring different what-if scenarios by changing parameters.\n",
12
+
"\n",
13
+
"Checkpoints use a split format: a JSON file for metadata and structure, and an NPZ file for numerical data (block states, solver histories, etc.)."
14
+
]
7
15
},
8
16
{
9
17
"cell_type": "markdown",
10
18
"metadata": {},
11
-
"source": "## Setup\n\nWe'll simulate a driven harmonic oscillator — a mass-spring system excited by an external sinusoidal force. The system produces a sustained periodic response, making it easy to visually verify that checkpoints preserve continuity."
19
+
"source": [
20
+
"## Building the System\n",
21
+
"\n",
22
+
"We'll use the coupled oscillators system to demonstrate checkpoints. The energy exchange between the two oscillators produces a sustained, non-trivial response that makes it easy to visually verify checkpoint continuity."
23
+
]
24
+
},
25
+
{
26
+
"cell_type": "raw",
27
+
"metadata": {},
28
+
"source": [
29
+
"First let's import the :class:`.Simulation` and :class:`.Connection` classes and the required blocks:"
"x2_0 = np.array([0.0, 0.0]) # oscillator 2 at rest"
74
+
]
32
75
},
33
76
{
34
-
"cell_type": "markdown",
77
+
"cell_type": "raw",
35
78
"metadata": {},
36
-
"source": "## Save Checkpoint\n\nRun the simulation for 20 seconds, then save a checkpoint. The system will be in a sustained oscillation by this point."
79
+
"source": [
80
+
"Define the differential equations for each oscillator using :class:`.ODE` blocks and the coupling force using a :class:`.Function` block:"
81
+
]
37
82
},
38
83
{
39
84
"cell_type": "code",
40
85
"execution_count": null,
41
86
"metadata": {},
42
87
"outputs": [],
43
-
"source": "sim, scope = make_system()\nsim.run(20)\n\n# Save checkpoint\nsim.save_checkpoint(\"checkpoint\")\nprint(f\"Saved checkpoint at t = {sim.time:.1f}s\")"
"source": "## Resume from Checkpoint\n\nLoad the checkpoint into a fresh simulation and continue for another 20 seconds. The new simulation has completely different Python objects, yet the checkpoint restores the exact state by matching blocks by type and insertion order."
124
+
"source": [
125
+
"Create the :class:`.Simulation` and run for 60 seconds:"
126
+
]
49
127
},
50
128
{
51
129
"cell_type": "code",
52
130
"execution_count": null,
53
131
"metadata": {},
54
132
"outputs": [],
55
-
"source": "sim_resumed, scope_resumed = make_system()\nsim_resumed.load_checkpoint(\"checkpoint\")\nprint(f\"Resumed from t = {sim_resumed.time:.1f}s\")\n\nsim_resumed.run(20)"
"The two oscillators exchange energy through the coupling spring, producing a characteristic beat pattern."
147
+
]
56
148
},
57
149
{
58
150
"cell_type": "markdown",
59
151
"metadata": {},
60
-
"source": "## Rollback: What-If Scenarios\n\nThis is where checkpoints really shine. We reload the same checkpoint but with **different parameters** — increasing the damping significantly. Both branches start from the exact same state at t=20, but evolve differently."
152
+
"source": [
153
+
"## Saving a Checkpoint\n",
154
+
"\n",
155
+
"Now let's save the simulation state at t=60s. This creates two files: `coupled.json` (metadata) and `coupled.npz` (numerical data)."
"print(f\"Checkpoint saved at t = {sim.time:.1f}s\")"
166
+
]
68
167
},
69
168
{
70
169
"cell_type": "markdown",
71
170
"metadata": {},
72
-
"source": "## Compare Results\n\nThe plot shows the original simulation (0–20s), followed by three different futures branching from the same checkpoint."
171
+
"source": [
172
+
"We can inspect the JSON file to see what was saved:"
173
+
]
73
174
},
74
175
{
75
176
"cell_type": "code",
76
177
"execution_count": null,
77
178
"metadata": {},
78
179
"outputs": [],
79
-
"source": "time_orig, data_orig = scope.read()\ntime_a, data_a = scope_a.read()\ntime_b, data_b = scope_b.read()\ntime_c, data_c = scope_c.read()\n\nfig, ax = plt.subplots(figsize=(10, 4))\n\n# Original run (0-20s)\nax.plot(time_orig, data_orig[0], \"k-\", lw=1.5, label=\"original (c=0.1, k=4)\")\n\n# Three futures from checkpoint\nax.plot(time_a, data_a[0], \"C0-\", alpha=0.8, label=\"resumed (c=0.1, k=4)\")\nax.plot(time_b, data_b[0], \"C1-\", alpha=0.8, label=\"what-if: heavy damping (c=1.5)\")\nax.plot(time_c, data_c[0], \"C2-\", alpha=0.8, label=\"what-if: stiffer spring (k=9)\")\n\nax.axvline(20, color=\"gray\", ls=\":\", alpha=0.5, lw=2, label=\"checkpoint (t=20s)\")\nax.set_xlabel(\"time [s]\")\nax.set_ylabel(\"position\")\nax.set_title(\"Checkpoint Rollback: Three Futures from the Same State\")\nax.legend(loc=\"upper right\", fontsize=8)\nfig.tight_layout()\nplt.show()"
"Blocks are identified by type and insertion order (``ODE_0``, ``ODE_1``, etc.), so the checkpoint can be loaded into any simulation with the same block structure, regardless of the specific Python objects."
199
+
]
80
200
},
81
201
{
82
202
"cell_type": "markdown",
83
203
"metadata": {},
84
-
"source": "All three scenarios start from the exact same state at t=20s. The blue continuation matches the original trajectory perfectly, while the heavy damping scenario (orange) decays rapidly and the stiffer spring scenario (green) shifts to a higher natural frequency."
204
+
"source": [
205
+
"## Rollback: What-If Scenarios\n",
206
+
"\n",
207
+
"This is where checkpoints really shine. We'll load the same checkpoint three times with different coupling strengths to explore how the system evolves from the exact same state.\n",
208
+
"\n",
209
+
"Since the checkpoint restores all block states by type and insertion order, we just need to rebuild the simulation with the same block structure but different parameters."
210
+
]
211
+
},
212
+
{
213
+
"cell_type": "code",
214
+
"execution_count": null,
215
+
"metadata": {},
216
+
"outputs": [],
217
+
"source": [
218
+
"def run_scenario(k12_new, duration=60):\n",
219
+
"\"\"\"Load checkpoint and continue with a different coupling constant.\"\"\"\n",
220
+
" def coupling_new(x1, x2):\n",
221
+
" f = k12_new * (x1 - x2)\n",
222
+
" return f, -f\n",
223
+
"\n",
224
+
" o1 = ODE(osc1_func, x1_0)\n",
225
+
" o2 = ODE(osc2_func, x2_0)\n",
226
+
" f = Function(coupling_new)\n",
227
+
" s = Scope()\n",
228
+
"\n",
229
+
" sim = Simulation(\n",
230
+
" [o1, o2, f, s],\n",
231
+
" [Connection(o1[0], f[0], s[0]),\n",
232
+
" Connection(o2[0], f[1], s[1]),\n",
233
+
" Connection(f[0], o1[0]),\n",
234
+
" Connection(f[1], o2[0])],\n",
235
+
" dt=0.01\n",
236
+
" )\n",
237
+
" sim.load_checkpoint(\"coupled\")\n",
238
+
" sim.run(duration)\n",
239
+
" return s.read()\n",
240
+
"\n",
241
+
"# Original coupling (continuation)\n",
242
+
"t_a, d_a = run_scenario(k12_new=0.5)\n",
243
+
"\n",
244
+
"# Stronger coupling\n",
245
+
"t_b, d_b = run_scenario(k12_new=2.0)\n",
246
+
"\n",
247
+
"# Decoupled\n",
248
+
"t_c, d_c = run_scenario(k12_new=0.0)"
249
+
]
85
250
},
86
251
{
87
252
"cell_type": "markdown",
88
253
"metadata": {},
89
254
"source": [
90
-
"## Checkpoint File Contents\n",
255
+
"## Comparing the Scenarios\n",
91
256
"\n",
92
-
"The JSON file contains human-readable metadata about the simulation state. Let's inspect it."
257
+
"The plot shows the original run (0-60s) followed by three different futures branching from the checkpoint at t=60s. We show oscillator 1 for clarity."
"ax.set_title(\"Checkpoint Rollback: Three Futures from the Same State\")\n",
282
+
"ax.legend(loc=\"upper right\", fontsize=8)\n",
283
+
"fig.tight_layout()\n",
284
+
"plt.show()"
112
285
]
113
286
},
114
287
{
115
288
"cell_type": "markdown",
116
289
"metadata": {},
117
290
"source": [
118
-
"Blocks are matched by type and insertion order (`Integrator_0`, `Integrator_1`, etc.), which means the checkpoint can be loaded into any simulation with the same block structure, regardless of the specific Python objects."
291
+
"All three scenarios start from the exact same state at t=60s. The blue continuation matches the original trajectory perfectly, confirming checkpoint fidelity. The stronger coupling (orange) produces faster energy exchange, while the decoupled system (green) oscillates independently at its natural frequency."
0 commit comments