Capture the few case values that identify what a run actually solved.
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