This example is interactive. Click the play button on any cell to execute it, or run all cells in sequence.

Checkpoints

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.

Checkpoints also enable rollback — returning to a saved state and exploring different what-if scenarios by changing parameters.

Checkpoints use a split format: a JSON file for metadata and structure, and an NPZ file for numerical data (block states, solver histories, etc.).

Building the System

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.

First let's import the Simulation and Connection classes and the required blocks:

Python
Loading...

Define the system parameters:

Python
Loading...

Define the differential equations for each oscillator using ODE blocks and the coupling force using a Function block:

Python
Loading...

Create the Simulation and run for 60 seconds:

Python
Loading...
12:43:21 - INFO - LOGGING (log: True)
12:43:21 - INFO - BLOCKS (total: 4, dynamic: 2, static: 2, eventful: 0)
12:43:21 - INFO - GRAPH (nodes: 4, edges: 6, alg. depth: 2, loop depth: 0, runtime: 0.051ms)
12:43:21 - INFO - STARTING -> TRANSIENT (Duration: 60.00s)
12:43:21 - INFO - --------------------   1% | 0.0s<0.7s | 8075.5 it/s
12:43:21 - INFO - ####----------------  20% | 0.2s<0.5s | 10468.5 it/s
12:43:22 - INFO - ########------------  40% | 0.3s<0.3s | 10936.4 it/s
12:43:22 - INFO - ############--------  60% | 0.4s<0.1s | 16323.0 it/s
12:43:22 - INFO - ################----  80% | 0.5s<0.1s | 8633.1 it/s
12:43:22 - INFO - #################### 100% | 0.7s<--:-- | 9600.5 it/s
12:43:22 - INFO - FINISHED -> TRANSIENT (total steps: 6001, successful: 6001, runtime: 674.86 ms)
Output

The two oscillators exchange energy through the coupling spring, producing a characteristic beat pattern.

Saving a Checkpoint

Now let's save the simulation state at t=60s. This creates two files: coupled.json (metadata) and coupled.npz (numerical data).

Python
Loading...
Checkpoint saved at t = 60.0s

We can inspect the JSON file to see what was saved:

Python
Loading...
PathSim version: 0.22.2
Simulation time: 60.0s
Solver: SSPRK22
Blocks saved:
  ODE_0 (ODE)
  ODE_1 (ODE)
  Function_0 (Function)
  Scope_0 (Scope)

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.

Rollback: What-If Scenarios

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.

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.

Python
Loading...
12:43:22 - INFO - LOGGING (log: True)
12:43:22 - INFO - BLOCKS (total: 4, dynamic: 2, static: 2, eventful: 0)
12:43:22 - INFO - GRAPH (nodes: 4, edges: 6, alg. depth: 2, loop depth: 0, runtime: 0.054ms)
12:43:22 - INFO - STARTING -> TRANSIENT (Duration: 60.00s)
12:43:22 - INFO - --------------------   1% | 0.0s<0.7s | 8548.7 it/s
12:43:22 - INFO - ####----------------  20% | 0.1s<0.5s | 9474.4 it/s
12:43:23 - INFO - ########------------  40% | 0.3s<0.4s | 8662.1 it/s
12:43:23 - INFO - ############--------  60% | 0.4s<0.3s | 9471.9 it/s
12:43:23 - INFO - ################----  80% | 0.6s<0.1s | 8234.2 it/s
12:43:23 - INFO - #################### 100% | 0.7s<--:-- | 7965.0 it/s
12:43:23 - INFO - FINISHED -> TRANSIENT (total steps: 6000, successful: 6000, runtime: 708.08 ms)
12:43:23 - INFO - LOGGING (log: True)
12:43:23 - INFO - BLOCKS (total: 4, dynamic: 2, static: 2, eventful: 0)
12:43:23 - INFO - GRAPH (nodes: 4, edges: 6, alg. depth: 2, loop depth: 0, runtime: 0.051ms)
12:43:23 - INFO - STARTING -> TRANSIENT (Duration: 60.00s)
12:43:23 - INFO - --------------------   1% | 0.0s<0.9s | 6822.7 it/s
12:43:23 - INFO - ####----------------  20% | 0.2s<0.6s | 8125.2 it/s
12:43:23 - INFO - ########------------  40% | 0.3s<0.4s | 9719.0 it/s
12:43:23 - INFO - ############--------  60% | 0.4s<0.3s | 9490.3 it/s
12:43:24 - INFO - ################----  80% | 0.6s<0.1s | 8614.8 it/s
12:43:24 - INFO - #################### 100% | 0.7s<--:-- | 8571.0 it/s
12:43:24 - INFO - FINISHED -> TRANSIENT (total steps: 6000, successful: 6000, runtime: 712.54 ms)
12:43:24 - INFO - LOGGING (log: True)
12:43:24 - INFO - BLOCKS (total: 4, dynamic: 2, static: 2, eventful: 0)
12:43:24 - INFO - GRAPH (nodes: 4, edges: 6, alg. depth: 2, loop depth: 0, runtime: 0.045ms)
12:43:24 - INFO - STARTING -> TRANSIENT (Duration: 60.00s)
12:43:24 - INFO - --------------------   1% | 0.0s<0.6s | 9266.1 it/s
12:43:24 - INFO - ####----------------  20% | 0.1s<0.6s | 8704.6 it/s
12:43:24 - INFO - ########------------  40% | 0.3s<0.4s | 10016.6 it/s
12:43:24 - INFO - ############--------  60% | 0.4s<0.2s | 10011.1 it/s
12:43:24 - INFO - ################----  80% | 0.6s<0.1s | 8865.0 it/s
12:43:24 - INFO - #################### 100% | 0.7s<--:-- | 8632.7 it/s
12:43:24 - INFO - FINISHED -> TRANSIENT (total steps: 6000, successful: 6000, runtime: 698.47 ms)

Comparing the Scenarios

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.

Python
Loading...
Output

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.