@@ -338,11 +338,34 @@ def plot(self, *args, **kwargs):
338338
339339 # checkpoint methods ----------------------------------------------------------
340340
341+ @staticmethod
342+ def _checkpoint_key (type_name , type_counts ):
343+ """Generate a deterministic checkpoint key from block/event type
344+ and occurrence index (e.g. 'Integrator_0', 'Scope_1').
345+
346+ Parameters
347+ ----------
348+ type_name : str
349+ class name of the block or event
350+ type_counts : dict
351+ running counter per type name, mutated in place
352+
353+ Returns
354+ -------
355+ key : str
356+ deterministic checkpoint key
357+ """
358+ idx = type_counts .get (type_name , 0 )
359+ type_counts [type_name ] = idx + 1
360+ return f"{ type_name } _{ idx } "
361+
362+
341363 def save_checkpoint (self , path , recordings = False ):
342364 """Save simulation state to checkpoint files (JSON + NPZ).
343365
344366 Creates two files: {path}.json (structure/metadata) and
345- {path}.npz (numerical data).
367+ {path}.npz (numerical data). Blocks and events are keyed by
368+ type and insertion order for deterministic cross-instance matching.
346369
347370 Parameters
348371 ----------
@@ -371,22 +394,28 @@ def save_checkpoint(self, path, recordings=False):
371394 "tolerance_fpi" : self .tolerance_fpi ,
372395 "iterations_max" : self .iterations_max ,
373396 },
374- "blocks" : {} ,
375- "events" : {} ,
397+ "blocks" : [] ,
398+ "events" : [] ,
376399 }
377400
378401 npz_data = {}
379402
380- #checkpoint all blocks (keyed by UUID)
403+ #checkpoint all blocks (keyed by type + insertion index)
404+ type_counts = {}
381405 for block in self .blocks :
382- b_json , b_npz = block .to_checkpoint (recordings = recordings )
383- checkpoint ["blocks" ][block .id ] = b_json
406+ key = self ._checkpoint_key (block .__class__ .__name__ , type_counts )
407+ b_json , b_npz = block .to_checkpoint (key , recordings = recordings )
408+ b_json ["_key" ] = key
409+ checkpoint ["blocks" ].append (b_json )
384410 npz_data .update (b_npz )
385411
386- #checkpoint external events (keyed by UUID)
412+ #checkpoint external events (keyed by type + insertion index)
413+ type_counts = {}
387414 for event in self .events :
388- e_json , e_npz = event .to_checkpoint ()
389- checkpoint ["events" ][event .id ] = e_json
415+ key = self ._checkpoint_key (event .__class__ .__name__ , type_counts )
416+ e_json , e_npz = event .to_checkpoint (key )
417+ e_json ["_key" ] = key
418+ checkpoint ["events" ].append (e_json )
390419 npz_data .update (e_npz )
391420
392421 #write files
@@ -400,8 +429,9 @@ def load_checkpoint(self, path):
400429 """Load simulation state from checkpoint files (JSON + NPZ).
401430
402431 Restores simulation time and all block/event states from a
403- previously saved checkpoint. The simulation must have the same
404- blocks and events as when the checkpoint was saved.
432+ previously saved checkpoint. Matching is based on block/event
433+ type and insertion order, so the simulation must be constructed
434+ with the same block types in the same order.
405435
406436 Parameters
407437 ----------
@@ -444,26 +474,32 @@ def load_checkpoint(self, path):
444474 f"current solver '{ self .Solver .__name__ } '"
445475 )
446476
447- #restore blocks
448- block_data = checkpoint .get ("blocks" , {})
477+ #index checkpoint blocks by key
478+ block_data = {b ["_key" ]: b for b in checkpoint .get ("blocks" , [])}
479+
480+ #restore blocks by type + insertion order
481+ type_counts = {}
449482 for block in self .blocks :
450- if block .id in block_data :
451- block .load_checkpoint (block_data [block .id ], npz )
483+ key = self ._checkpoint_key (block .__class__ .__name__ , type_counts )
484+ if key in block_data :
485+ block .load_checkpoint (key , block_data [key ], npz )
452486 else :
453487 warnings .warn (
454- f"Block { block .__class__ .__name__ } (id={ block .id [:8 ]} ...) "
455- f"not found in checkpoint"
488+ f"Block '{ key } ' not found in checkpoint"
456489 )
457490
458- #restore external events
459- event_data = checkpoint .get ("events" , {})
491+ #index checkpoint events by key
492+ event_data = {e ["_key" ]: e for e in checkpoint .get ("events" , [])}
493+
494+ #restore external events by type + insertion order
495+ type_counts = {}
460496 for event in self .events :
461- if event .id in event_data :
462- event .load_checkpoint (event_data [event .id ], npz )
497+ key = self ._checkpoint_key (event .__class__ .__name__ , type_counts )
498+ if key in event_data :
499+ event .load_checkpoint (key , event_data [key ], npz )
463500 else :
464501 warnings .warn (
465- f"Event { event .__class__ .__name__ } (id={ event .id [:8 ]} ...) "
466- f"not found in checkpoint"
502+ f"Event '{ key } ' not found in checkpoint"
467503 )
468504
469505 finally :
0 commit comments