Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
__init__.py
1import glob
2import json
3import os
4
5import shamrock.sys
6
7
8def helper_purge_old_dumps(dump_prefix, keep_first=1, keep_last=3, ext=".sham") -> None:
9 """
10 Purge old dump files.
11 """
12 if shamrock.sys.world_rank() == 0:
13 res = glob.glob(dump_prefix + "*" + ext)
14 res.sort()
15
16 # The list of dumps to remove (keep the first and last 3 dumps)
17 to_remove = res[keep_first:-keep_last]
18
19 for f in to_remove:
20 os.remove(f)
21
22
23def helper_get_last_dump(dump_prefix, ext=".sham") -> int | None:
24 """
25 Get the last dump number.
26 """
27 res = glob.glob(dump_prefix + "*" + ext)
28
29 num_max = -1
30
31 for f in res:
32 try:
33 dump_num = int(f[len(dump_prefix) : -len(ext)])
34 num_max = max(num_max, dump_num)
35 except ValueError:
36 pass
37
38 if num_max == -1:
39 return None
40 else:
41 return num_max
42
43
45 """
46 Helper class to handle Shamrock checkpoint dump files.
47
48 When ``metadata`` is enabled at construction, a JSON companion file is written
49 and read alongside each checkpoint to store simulation metadata.
50 """
51
52 def __init__(self, model, dump_prefix, ext=".sham", metadata=False):
53 """
54 Parameters
55 ----------
56 model
57 The Shamrock model instance used to write and load dumps.
58 dump_prefix : str
59 Path prefix for dump files; the dump index is appended as a
60 zero-padded seven-digit number (e.g. ``prefix0000042``).
61 ext : str, optional
62 File extension for checkpoint dumps (default is ``".sham"``).
63 metadata : bool, optional
64 If ``True``, also write/read a ``.json`` companion with per-checkpoint
65 metadata (default is ``False``).
66 """
67 self.model = model
68 self.dump_prefix = dump_prefix
69 self.ext = ext
70 os.makedirs(os.path.dirname(self.dump_prefix), exist_ok=True)
71 self.metadata = metadata
72
73 def get_dump_name_extension(self, idump, ext) -> str:
74 """Get the name of the dump file with the extension"""
75 return self.dump_prefix + f"{idump:07}" + ext
76
77 def get_dump_name(self, idump) -> str:
78 """Get the name of the dump file (extension from self.ext)"""
79 return self.get_dump_name_extension(idump, self.ext)
80
81 def get_last_dump(self) -> int | None:
82 """Find the last dump number.
83
84 When metadata mode is enabled, validate that checkpoint dumps and JSON
85 companion files agree on the latest checkpoint index.
86 """
87 last_dump = helper_get_last_dump(self.dump_prefix, self.ext)
88 if not self.metadata:
89 return last_dump
90
91 last_metadata_dump = helper_get_last_dump(self.dump_prefix, ".json")
92 if last_dump != last_metadata_dump:
93 raise ValueError(
94 "Detected inconsistent checkpoint files: "
95 f"last {self.ext} dump is {last_dump}, "
96 f"last .json dump is {last_metadata_dump}. "
97 "This may indicate a botched checkpoint."
98 )
99 return last_dump
100
101 def purge_old_dumps(self, keep_first=1, keep_last=3) -> None:
102 """
103 Purge old dump files.
104
105 When metadata mode is enabled, also purge old JSON companion files.
106
107 Parameters
108 ----------
109 keep_first : int, optional
110 Number of oldest dump files to keep (default is 1, i.e. keep the first dump).
111 keep_last : int, optional
112 Number of newest dump files to keep (default is 3, i.e. keep the last 3 dumps).
113
114 Returns
115 -------
116 None
117 This method does not return a value.
118 """
119 helper_purge_old_dumps(self.dump_prefix, keep_first, keep_last, self.ext)
120
121 if self.metadata:
122 helper_purge_old_dumps(self.dump_prefix, keep_first, keep_last, ".json")
123
124 def load_dump(self, idump) -> dict | None:
125 """
126 Load a dump file.
127
128 Parameters
129 ----------
130 idump : int
131 The dump identifier to load.
132
133 Returns
134 -------
135 dict or None
136 If ``metadata`` was enabled at construction, the JSON metadata
137 loaded from the companion file; otherwise ``None``.
138 """
139 dump_name = self.get_dump_name(idump)
140 if shamrock.sys.world_rank() == 0:
141 print(f"Loading dump: {dump_name} i={idump}")
142 self.model.load_from_dump(dump_name)
143 if self.metadata:
144 dump_name = self.get_dump_name_extension(idump, ".json")
145 with open(dump_name, "r") as f:
146 return json.load(f)
147 else:
148 return None
149
151 self, idump, metadata=None, purge_old_dumps=False, keep_first=1, keep_last=3
152 ) -> None:
153 """
154 Write a dump file.
155
156 Parameters
157 ----------
158 idump : int
159 The dump identifier to write.
160 metadata : object, optional
161 JSON-serializable metadata stored in a ``.json`` companion next to the
162 checkpoint. Required when ``metadata`` was enabled at construction.
163 purge_old_dumps : bool, optional
164 Whether to purge old dumps (default is False).
165 keep_first : int, optional
166 Number of oldest dump files to keep (default is 1, i.e. keep the first dump).
167 keep_last : int, optional
168 Number of newest dump files to keep (default is 3, i.e. keep the last 3 dumps).
169
170 Returns
171 -------
172 None
173 This method does not return a value.
174 """
175 dump_name = self.get_dump_name(idump)
176 self.model.dump(dump_name)
177
178 if self.metadata:
179 if metadata is None:
180 raise ValueError("metadata is required when metadata is enabled")
181
182 if shamrock.sys.world_rank() == 0:
183 with open(self.get_dump_name_extension(idump, ".json"), "w") as f:
184 json.dump(metadata, f)
185
186 if purge_old_dumps:
187 self.purge_old_dumps(keep_first, keep_last)
188
189 def load_last_dump_or(self, functor_no_last_dump) -> dict | None:
190 """
191 Load the last dump or call a function if no dump is found.
192
193 Parameters
194 ----------
195 functor_no_last_dump : callable
196 Setup function invoked when no dump exists. Must not return a value.
197
198 Returns
199 -------
200 dict or None
201 If a dump was loaded and ``metadata`` was enabled at construction,
202 the JSON metadata from the companion; otherwise ``None``.
203 """
204 idump = self.get_last_dump()
205 if idump is None:
206 result = functor_no_last_dump()
207 if result is not None:
208 raise ValueError("functor_no_last_dump must not return a value")
209 return None
210 else:
211 return self.load_dump(idump)
str get_dump_name_extension(self, idump, ext)
Definition __init__.py:73
None write_dump(self, idump, metadata=None, purge_old_dumps=False, keep_first=1, keep_last=3)
Definition __init__.py:152
__init__(self, model, dump_prefix, ext=".sham", metadata=False)
Definition __init__.py:52
None purge_old_dumps(self, keep_first=1, keep_last=3)
Definition __init__.py:101
dict|None load_last_dump_or(self, functor_no_last_dump)
Definition __init__.py:189