PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions
picurv_cli.storage.compatibility Namespace Reference

Functions

list _capture_study_context (dict target)
 Embed small study control-plane files with an individually archived member.
 
dict _capture_parameter_summary (str root)
 Capture the few case values that identify what a run actually solved.
 
list _capture_workspace_assets (dict target)
 List the asset ids a workspace archive carries.
 
list _capture_run_assets (str root)
 Record reusable asset references carried by run-local input snapshots.
 
dict _config_fingerprints (str root)
 Hash small canonical YAML inputs stored under an artifact.
 
dict _git_provenance (str root)
 Record best-effort current source revision and dirty state.
 
None _restore_study_context (dict manifest, str case_destination)
 Recreate missing study control-plane files around a restored member.
 
list _rebase_restored_text_paths (str root, list replacements)
 Rebase known generated text artifacts after an explicit relocated restore.
 

Function Documentation

◆ _capture_study_context()

list picurv_cli.storage.compatibility._capture_study_context ( dict  target)
protected

Embed small study control-plane files with an individually archived member.

Parameters
[in]targetValue supplied through the target argument.
Returns
Result produced by this operation.

Definition at line 33 of file compatibility.py.

33def _capture_study_context(target: dict) -> list:
34 """!
35 @brief Embed small study control-plane files with an individually archived member.
36 @param[in] target Value supplied through the `target` argument.
37 @return Result produced by this operation.
38 """
39 if target["artifact_type"] != "study-case":
40 return []
41 study_root = Path(target["study_path"])
42 candidates = [
43 study_root / "study.yml",
44 study_root / "cluster.yml",
45 study_root / "study_manifest.json",
46 study_root / "scheduler" / "case_index.tsv",
47 study_root / "scheduler" / "submission.json",
48 ]
49 captured = []
50 for path in candidates:
51 if not path.is_file() or path.stat().st_size > 5 * 1024 * 1024:
52 continue
53 captured.append({
54 "path": path.relative_to(study_root).as_posix(),
55 "mode": int(path.stat().st_mode & 0o7777),
56 "content_base64": base64.b64encode(path.read_bytes()).decode("ascii"),
57 })
58 return captured
59
60

◆ _capture_parameter_summary()

dict picurv_cli.storage.compatibility._capture_parameter_summary ( str  root)
protected

Capture the few case values that identify what a run actually solved.

storage show has to answer "which run was this?" months later without restoring anything, and an id plus a label does not. These are read from the run's own configuration snapshot, so they describe the run rather than whatever the editable workspace says now.

Parameters
[in]rootArtifact root directory.
Returns
Parameter summary, empty when no readable case snapshot exists.

Definition at line 61 of file compatibility.py.

61def _capture_parameter_summary(root: str) -> dict:
62 """!
63 @brief Capture the few case values that identify what a run actually solved.
64
65 @details `storage show` has to answer "which run was this?" months later without
66 restoring anything, and an id plus a label does not. These are read from
67 the run's own configuration snapshot, so they describe the run rather than
68 whatever the editable workspace says now.
69 @param[in] root Artifact root directory.
70 @return Parameter summary, empty when no readable case snapshot exists.
71 """
72 for relative in (("config", "case.yml"), ("cases", "case_0000", "config", "case.yml")):
73 case_path = os.path.join(root, *relative)
74 if os.path.isfile(case_path):
75 break
76 else:
77 return {}
78 try:
79 with open(case_path, "r", encoding="utf-8") as stream:
80 case = yaml.safe_load(stream) or {}
81 except (OSError, ValueError):
82 return {}
83 if not isinstance(case, dict):
84 return {}
85 properties = case.get("properties") or {}
86 scaling = properties.get("scaling") or {}
87 fluid = properties.get("fluid") or {}
88 grid = case.get("grid") or {}
89 run_control = case.get("run_control") or {}
90 summary = {
91 "title": case.get("title"),
92 "grid_mode": grid.get("mode"),
93 "grid_dimensions": None,
94 "blocks": ((case.get("models") or {}).get("domain") or {}).get("blocks"),
95 "start_step": run_control.get("start_step"),
96 "total_steps": run_control.get("total_steps"),
97 "dt_physical": run_control.get("dt_physical"),
98 "length_ref": scaling.get("length_ref"),
99 "velocity_ref": scaling.get("velocity_ref"),
100 "density": fluid.get("density"),
101 "viscosity": fluid.get("viscosity"),
102 "reynolds": None,
103 }
104 programmatic = grid.get("programmatic_settings")
105 if isinstance(programmatic, dict):
106 dims = [programmatic.get(key) for key in ("im", "jm", "km")]
107 if all(isinstance(value, int) for value in dims):
108 summary["grid_dimensions"] = dims
109 try:
110 summary["reynolds"] = (
111 float(fluid["density"]) * float(scaling["velocity_ref"])
112 * float(scaling["length_ref"]) / float(fluid["viscosity"])
113 )
114 except (KeyError, TypeError, ValueError, ZeroDivisionError):
115 summary["reynolds"] = None
116 return {key: value for key, value in summary.items() if value is not None}
117
118

◆ _capture_workspace_assets()

list picurv_cli.storage.compatibility._capture_workspace_assets ( dict  target)
protected

List the asset ids a workspace archive carries.

Parameters
[in]targetLocal artifact target.
Returns
Sorted asset ids, empty for non-workspace artifacts.

Definition at line 119 of file compatibility.py.

119def _capture_workspace_assets(target: dict) -> list:
120 """!
121 @brief List the asset ids a workspace archive carries.
122 @param[in] target Local artifact target.
123 @return Sorted asset ids, empty for non-workspace artifacts.
124 """
125 if target["artifact_type"] != "workspace":
126 return []
127 objects_root = os.path.join(target["root_path"], "assets", "objects")
128 if not os.path.isdir(objects_root):
129 return []
130 found = []
131 for kind in sorted(os.listdir(objects_root)):
132 kind_root = os.path.join(objects_root, kind)
133 if os.path.isdir(kind_root):
134 found.extend(
135 name for name in sorted(os.listdir(kind_root))
136 if os.path.isdir(os.path.join(kind_root, name))
137 )
138 return sorted(found)
139
140

◆ _capture_run_assets()

list picurv_cli.storage.compatibility._capture_run_assets ( str  root)
protected

Record reusable asset references carried by run-local input snapshots.

Parameters
[in]rootRun or study artifact root.
Returns
Portable asset references for the remote archive catalog.

Definition at line 141 of file compatibility.py.

141def _capture_run_assets(root: str) -> list:
142 """!
143 @brief Record reusable asset references carried by run-local input snapshots.
144 @param[in] root Run or study artifact root.
145 @return Portable asset references for the remote archive catalog.
146 """
147 captured = []
148 archive_root = Path(os.path.abspath(root))
149 for runtime_root_text in _artifact_runtime_roots(root):
150 runtime_root = Path(runtime_root_text)
151 lock_path = runtime_root / "inputs" / "assets.lock.yml"
152 if not lock_path.is_file():
153 continue
154 try:
155 payload = yaml.safe_load(lock_path.read_text(encoding="utf-8")) or {}
156 except (OSError, ValueError, TypeError):
157 continue
158 assets = payload.get("assets") if isinstance(payload, dict) else None
159 if not isinstance(assets, dict):
160 continue
161 runtime_prefix = runtime_root.relative_to(archive_root).as_posix()
162 for kind, reference in sorted(assets.items()):
163 if not isinstance(reference, dict):
164 continue
165 files = reference.get("files") or reference.get("exposed") or []
166 normalized_files = []
167 for item in files:
168 if not isinstance(item, dict) or not isinstance(item.get("path"), str):
169 continue
170 run_path = item["path"]
171 archived_path = (
172 f"{runtime_prefix}/{run_path}" if runtime_prefix != "." else run_path
173 )
174 normalized_files.append({
175 "path": run_path,
176 "archived_path": archived_path,
177 "bytes": item.get("bytes"),
178 "sha256": item.get("sha256"),
179 })
180 captured.append({
181 "kind": kind,
182 "asset_id": reference.get("asset_id"),
183 "provider": reference.get("provider"),
184 "provider_spec_sha256": reference.get("provider_spec_sha256"),
185 "files": normalized_files,
186 })
187 return captured
188
189

◆ _config_fingerprints()

dict picurv_cli.storage.compatibility._config_fingerprints ( str  root)
protected

Hash small canonical YAML inputs stored under an artifact.

Parameters
[in]rootValue supplied through the root argument.
Returns
Result produced by this operation.

Definition at line 190 of file compatibility.py.

190def _config_fingerprints(root: str) -> dict:
191 """!
192 @brief Hash small canonical YAML inputs stored under an artifact.
193 @param[in] root Value supplied through the `root` argument.
194 @return Result produced by this operation.
195 """
196 result = {}
197 root_path = Path(root)
198 candidates = set(root_path.glob("config/*.yml"))
199 candidates.update(root_path.glob("base_configs/*.yml"))
200 candidates.update(root_path.glob("cases/case_*/config/*.yml"))
201 for path in sorted(candidates):
202 result[path.relative_to(root).as_posix()] = _sha256_file(str(path))
203 for name in ("study.yml", "cluster.yml"):
204 path = root_path / name
205 if path.is_file():
206 result[name] = _sha256_file(str(path))
207 return result
208
209

◆ _git_provenance()

dict picurv_cli.storage.compatibility._git_provenance ( str  root)
protected

Record best-effort current source revision and dirty state.

Parameters
[in]rootValue supplied through the root argument.
Returns
Result produced by this operation.

Definition at line 210 of file compatibility.py.

210def _git_provenance(root: str) -> dict:
211 """!
212 @brief Record best-effort current source revision and dirty state.
213 @param[in] root Value supplied through the `root` argument.
214 @return Result produced by this operation.
215 """
216 result = {"commit": None, "dirty": None}
217 try:
218 commit = subprocess.run(
219 ["git", "rev-parse", "HEAD"], cwd=root, text=True, capture_output=True, check=False
220 )
221 status = subprocess.run(
222 ["git", "status", "--porcelain"], cwd=root, text=True, capture_output=True, check=False
223 )
224 if commit.returncode == 0:
225 result["commit"] = commit.stdout.strip()
226 if status.returncode == 0:
227 result["dirty"] = bool(status.stdout.strip())
228 except OSError:
229 pass
230 return result
231
232

◆ _restore_study_context()

None picurv_cli.storage.compatibility._restore_study_context ( dict  manifest,
str  case_destination 
)
protected

Recreate missing study control-plane files around a restored member.

Parameters
[in]manifestValue supplied through the manifest argument.
[in]case_destinationValue supplied through the case_destination argument.

Definition at line 233 of file compatibility.py.

233def _restore_study_context(manifest: dict, case_destination: str) -> None:
234 """!
235 @brief Recreate missing study control-plane files around a restored member.
236 @param[in] manifest Value supplied through the `manifest` argument.
237 @param[in] case_destination Value supplied through the `case_destination` argument.
238 """
239 if manifest.get("artifact_type") != "study-case":
240 return
241 study_root = Path(case_destination).parent.parent
242 for item in manifest.get("study_context", []):
243 relative = item.get("path")
244 if not isinstance(relative, str):
245 continue
246 destination = study_root.joinpath(*relative.split("/"))
247 if destination.exists():
248 continue
249 destination.parent.mkdir(parents=True, exist_ok=True)
250 destination.write_bytes(base64.b64decode(item.get("content_base64", "")))
251 try:
252 destination.chmod(int(item.get("mode", 0o644)))
253 except OSError:
254 pass
255
256

◆ _rebase_restored_text_paths()

list picurv_cli.storage.compatibility._rebase_restored_text_paths ( str  root,
list  replacements 
)
protected

Rebase known generated text artifacts after an explicit relocated restore.

Parameters
[in]rootValue supplied through the root argument.
[in]replacementsValue supplied through the replacements argument.
Returns
Result produced by this operation.

Definition at line 257 of file compatibility.py.

257def _rebase_restored_text_paths(root: str, replacements: list) -> list:
258 """!
259 @brief Rebase known generated text artifacts after an explicit relocated restore.
260 @param[in] root Value supplied through the `root` argument.
261 @param[in] replacements Value supplied through the `replacements` argument.
262 @return Result produced by this operation.
263 """
264 changed = []
265 allowed_suffixes = {".control", ".run", ".sbatch", ".json", ".tsv", ".yml", ".yaml"}
266 for path in Path(root).rglob("*"):
267 if not path.is_file() or path.is_symlink() or path.stat().st_size > 32 * 1024 * 1024:
268 continue
269 if path.suffix.lower() not in allowed_suffixes:
270 continue
271 try:
272 content = path.read_text(encoding="utf-8")
273 except (OSError, UnicodeDecodeError):
274 continue
275 updated = content
276 for old, new in replacements:
277 if old and new and old != new:
278 updated = updated.replace(old, new)
279 if updated != content:
280 path.write_text(updated, encoding="utf-8")
281 changed.append(str(path))
282 return changed