{
"cells": [
{
"cell_type": "markdown",
"id": "f13f2351",
"metadata": {},
"source": [
"# Validation case status dashboard\n",
"\n",
"At-a-glance state of every `validation/**/*.nassu.yaml` case, derived from the\n",
"**notebook and config files themselves** - no simulation results are read. It tells\n",
"you which cases still need (re)running here, when each notebook was last run, and\n",
"the nassu version/commit it was executed at, so you do not have to open notebooks\n",
"one by one.\n",
"\n",
"**Verdict legend** - `RUN` the notebook has no outputs (never executed here),\n",
"`RERUN` the config changed after the notebook was last run, `BEHIND` the notebook\n",
"was executed on an older commit than HEAD, `NO_NB` the case has no analysis\n",
"notebook, `OK` up to date. The `local_results` column is a hint only (is there a\n",
"`results/` folder on this machine) and never affects the verdict - results are\n",
"gitignored and local-only.\n",
"\n",
"Run all cells. Re-run after you execute/save a case notebook to refresh its row."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "bd5f90fd",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-29T17:47:20.741414Z",
"iopub.status.busy": "2026-06-29T17:47:20.741326Z",
"iopub.status.idle": "2026-06-29T17:47:20.942380Z",
"shell.execute_reply": "2026-06-29T17:47:20.942073Z"
}
},
"outputs": [],
"source": [
"import datetime as dt\n",
"import json\n",
"import pathlib\n",
"import re\n",
"import subprocess\n",
"from dataclasses import dataclass, field\n",
"\n",
"import pandas as pd\n",
"\n",
"UTC = dt.timezone.utc\n",
"\n",
"# Repo root = nearest ancestor containing pyproject.toml; validation/ beneath it.\n",
"REPO = pathlib.Path.cwd()\n",
"while REPO != REPO.parent and not (REPO / \"pyproject.toml\").exists():\n",
" REPO = REPO.parent\n",
"VALIDATION = REPO / \"validation\"\n",
"\n",
"_COMMIT_RE = re.compile(r\"Commit hash:\\s*([0-9a-f]{7,40})\")\n",
"_VERSION_RE = re.compile(r\"Version:\\s*([0-9][^\\s]*)\")\n",
"\n",
"# Each verdict keyword -> (background, text) hex pair. The text colour is set\n",
"# explicitly (dark on light pastels) so the table reads correctly under VS Code's\n",
"# dark or light notebook theme, not just whatever the theme default text is.\n",
"VERDICT_STYLE = {\n",
" \"RUN\": (\"#fca5a5\", \"#3f0a0a\"), # red: notebook has no outputs (never run here)\n",
" \"RERUN\": (\"#fcd34d\", \"#3a2c00\"), # amber: config changed since last run\n",
" \"BEHIND\": (\"#fde68a\", \"#3a2c00\"), # light amber: ran on an older commit\n",
" \"NO_NB\": (\"#e5e7eb\", \"#1f2937\"), # grey: no analysis notebook\n",
" \"OK\": (\"#86efac\", \"#06351a\"), # green\n",
"}\n",
"\n",
"\n",
"def sh(*args):\n",
" out = subprocess.run(args, cwd=REPO, capture_output=True, text=True, check=False)\n",
" return out.stdout.strip()\n",
"\n",
"\n",
"def git_head():\n",
" return sh(\"git\", \"rev-parse\", \"HEAD\")\n",
"\n",
"\n",
"def git_last_commit(path):\n",
" \"\"\"(short hash, committer datetime UTC, subject) of the last commit on path.\"\"\"\n",
" rel = path.relative_to(REPO).as_posix()\n",
" line = sh(\"git\", \"log\", \"-1\", \"--format=%h\\t%cI\\t%s\", \"--\", rel)\n",
" if not line:\n",
" return (\"untracked\", None, \"\")\n",
" h, iso, *subj = line.split(\"\\t\")\n",
" return (h, dt.datetime.fromisoformat(iso).astimezone(UTC), subj[0] if subj else \"\")\n",
"\n",
"\n",
"def commits_behind(run_commit, head):\n",
" \"\"\"How many commits HEAD is ahead of run_commit (0 at HEAD, None if unknown).\"\"\"\n",
" if not run_commit:\n",
" return None\n",
" if head.startswith(run_commit) or run_commit.startswith(head):\n",
" return 0\n",
" reachable = subprocess.run(\n",
" [\"git\", \"merge-base\", \"--is-ancestor\", run_commit, head],\n",
" cwd=REPO,\n",
" capture_output=True,\n",
" )\n",
" if reachable.returncode != 0:\n",
" return None\n",
" n = sh(\"git\", \"rev-list\", \"--count\", f\"{run_commit}..{head}\")\n",
" return int(n) if n.isdigit() else None\n",
"\n",
"\n",
"def mtime_utc(path):\n",
" return dt.datetime.fromtimestamp(path.stat().st_mtime, tz=UTC)\n",
"\n",
"\n",
"def fmt_dt(d):\n",
" return d.astimezone().strftime(\"%Y-%m-%d %H:%M\") if d else \"-\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "60356c52",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-29T17:47:20.943704Z",
"iopub.status.busy": "2026-06-29T17:47:20.943561Z",
"iopub.status.idle": "2026-06-29T17:47:21.154922Z",
"shell.execute_reply": "2026-06-29T17:47:21.154623Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"HEAD 9762cc51b8 - 26 validation cases\n"
]
}
],
"source": [
"@dataclass\n",
"class NotebookState:\n",
" path: pathlib.Path\n",
" has_outputs: bool\n",
" mtime: dt.datetime\n",
" ran_version: str | None # parsed from the notebook's own Version cell output\n",
" ran_commit: str | None\n",
"\n",
"\n",
"@dataclass\n",
"class CaseState:\n",
" folder: pathlib.Path\n",
" yamls: list = field(default_factory=list)\n",
" yaml_commits: dict = field(default_factory=dict)\n",
" notebooks: list = field(default_factory=list)\n",
" has_local_results: bool = False # hint only; never drives the verdict\n",
"\n",
" @property\n",
" def newest_config_change(self):\n",
" cand = []\n",
" for y in self.yamls:\n",
" cand.append(mtime_utc(y))\n",
" _, when, _ = self.yaml_commits.get(y, (None, None, None))\n",
" if when:\n",
" cand.append(when)\n",
" return max(cand) if cand else None\n",
"\n",
" @property\n",
" def newest_notebook_save(self):\n",
" saved = [nb.mtime for nb in self.notebooks if nb.has_outputs]\n",
" return max(saved) if saved else None\n",
"\n",
"\n",
"def parse_notebook(path):\n",
" \"\"\"Executed state + the version/commit the notebook printed from read_info().\"\"\"\n",
" has_outputs = False\n",
" out_text = []\n",
" try:\n",
" with path.open() as fh:\n",
" nb = json.load(fh)\n",
" for cell in nb.get(\"cells\", []):\n",
" if cell.get(\"cell_type\") != \"code\":\n",
" continue\n",
" for o in cell.get(\"outputs\", []):\n",
" has_outputs = True\n",
" txt = o.get(\"text\") or (o.get(\"data\", {}) or {}).get(\"text/plain\", \"\")\n",
" out_text.append(\"\".join(txt) if isinstance(txt, list) else str(txt))\n",
" except (json.JSONDecodeError, OSError):\n",
" pass\n",
" joined = \"\".join(out_text)\n",
" cm = _COMMIT_RE.search(joined)\n",
" vm = _VERSION_RE.search(joined)\n",
" return NotebookState(\n",
" path,\n",
" has_outputs,\n",
" mtime_utc(path),\n",
" vm.group(1) if vm else None,\n",
" cm.group(1)[:10] if cm else None,\n",
" )\n",
"\n",
"\n",
"def collect():\n",
" \"\"\"One CaseState per leaf folder containing at least one .nassu.yaml.\"\"\"\n",
" folders = sorted({p.parent for p in VALIDATION.rglob(\"*.nassu.yaml\")})\n",
" cases = []\n",
" for folder in folders:\n",
" cs = CaseState(folder=folder)\n",
" for y in sorted(folder.glob(\"*.nassu.yaml\")):\n",
" cs.yamls.append(y)\n",
" cs.yaml_commits[y] = git_last_commit(y)\n",
" cs.notebooks = [parse_notebook(nb) for nb in sorted(folder.glob(\"*.ipynb\"))]\n",
" results = folder / \"results\"\n",
" cs.has_local_results = results.is_dir() and any(results.rglob(\"info.yaml\"))\n",
" cases.append(cs)\n",
" return cases\n",
"\n",
"\n",
"def verdict(cs, head):\n",
" \"\"\"Status from the notebook + config only (no results dependency).\"\"\"\n",
" if not cs.notebooks:\n",
" return [\"NO_NB\"]\n",
" executed = [nb for nb in cs.notebooks if nb.has_outputs]\n",
" if not executed:\n",
" return [\"RUN\"]\n",
" flags = []\n",
" if (\n",
" cs.newest_config_change\n",
" and cs.newest_notebook_save\n",
" and cs.newest_config_change > cs.newest_notebook_save\n",
" ):\n",
" flags.append(\"RERUN\")\n",
" behind = [b for nb in executed if (b := commits_behind(nb.ran_commit, head)) not in (None, 0)]\n",
" if behind and \"RERUN\" not in flags:\n",
" flags.append(\"BEHIND\")\n",
" return flags or [\"OK\"]\n",
"\n",
"\n",
"def case_rows(cases, head):\n",
" \"\"\"One flat dict per case for the table.\"\"\"\n",
" rows = []\n",
" for cs in cases:\n",
" flags = verdict(cs, head)\n",
" executed = [nb for nb in cs.notebooks if nb.has_outputs]\n",
" versions = sorted({nb.ran_version for nb in executed if nb.ran_version})\n",
" commits = [nb.ran_commit for nb in executed if nb.ran_commit]\n",
" behind = [b for c in commits if (b := commits_behind(c, head)) is not None]\n",
" cfg_dates = sorted(w for y in cs.yamls if (w := cs.yaml_commits[y][1]))\n",
" rows.append(\n",
" {\n",
" \"category\": cs.folder.relative_to(VALIDATION).parts[0],\n",
" \"case\": cs.folder.relative_to(VALIDATION).as_posix(),\n",
" \"verdict\": \" \".join(flags),\n",
" \"needs_action\": flags != [\"OK\"],\n",
" \"config_changed\": fmt_dt(cfg_dates[-1]) if cfg_dates else \"-\",\n",
" \"notebooks_executed\": f\"{len(executed)}/{len(cs.notebooks)}\"\n",
" if cs.notebooks\n",
" else \"-\",\n",
" \"notebook_saved\": fmt_dt(cs.newest_notebook_save),\n",
" \"ran_version\": \",\".join(versions) if versions else \"-\",\n",
" \"ran_commit\": commits[0] if commits else \"-\",\n",
" \"commits_behind_head\": max(behind) if behind else 0,\n",
" \"local_results\": \"yes\" if cs.has_local_results else \"no\",\n",
" }\n",
" )\n",
" return rows\n",
"\n",
"\n",
"cases = collect()\n",
"head = git_head()\n",
"print(f\"HEAD {head[:10]} - {len(cases)} validation cases\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f6ed01f5",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-29T17:47:21.155740Z",
"iopub.status.busy": "2026-06-29T17:47:21.155666Z",
"iopub.status.idle": "2026-06-29T17:47:21.632676Z",
"shell.execute_reply": "2026-06-29T17:47:21.632405Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
\n",
" \n",
" \n",
" | category | \n",
" case | \n",
" verdict | \n",
" needs_action | \n",
" config_changed | \n",
" notebooks_executed | \n",
" notebook_saved | \n",
" ran_version | \n",
" ran_commit | \n",
" commits_behind_head | \n",
" local_results | \n",
"
\n",
" \n",
" \n",
" \n",
" | analytical | \n",
" analytical/01_couette_flow | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" ab62e54213 | \n",
" 4 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/02_poiseuille_channel_flow | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 4/4 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" 20647b9f11 | \n",
" 240 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/03_poiseuille_pipe_flow | \n",
" RERUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-24 09:13 | \n",
" 2.0.1a0 | \n",
" 393bce29ec | \n",
" 239 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/04_taylor_green_vortex_2d | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" 7c0d788ec9 | \n",
" 235 | \n",
" yes | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/01_lowmach_bubble | \n",
" RUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/02_sandia_helium_plume | \n",
" NO_NB | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/03_steckler_room_fire | \n",
" RUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/04_sandia_methane_pool_fire | \n",
" NO_NB | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | calibration | \n",
" calibration/neq_wm_pressure_filter/reference | \n",
" NO_NB | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/01_flow_over_sphere | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 19:23 | \n",
" 3/3 | \n",
" 2026-06-25 02:48 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/02_flow_over_cylinder | \n",
" RERUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-24 13:00 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/03_flat_plate_boundary_layer | \n",
" RUN | \n",
" True | \n",
" 2026-06-24 19:35 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | porous_media | \n",
" porous_media/01_flow_through_trees | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | porous_media | \n",
" porous_media/02_porous_pipe_flow | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | scalar_transport | \n",
" scalar_transport/01_passive_scalar_transport | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | scalar_transport | \n",
" scalar_transport/02_turb_channel_passive_scalar | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-25 01:49 | \n",
" 1/1 | \n",
" 2026-06-25 11:53 | \n",
" 2.0.1a0 | \n",
" ce9eac31c9 | \n",
" 100 | \n",
" yes | \n",
"
\n",
" \n",
" | thermal | \n",
" thermal/01_differentially_heated_cavity | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:44 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | thermal | \n",
" thermal/02_heated_cylinder | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:44 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/01_taylor_green_vortex_3d | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:45 | \n",
" 2.0.1a0 | \n",
" 50dc5112a0 | \n",
" 199 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/02_turbulent_channel_flow | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 3/3 | \n",
" 2026-06-29 14:45 | \n",
" 2.0.1a0 | \n",
" 50dc5112a0 | \n",
" 199 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/03_turbulent_pipe_flow | \n",
" BEHIND | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-29 14:45 | \n",
" 1.6.17,2.0.1a0 | \n",
" 09c9337ca1 | \n",
" 2546 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/04_backward_step_flow | \n",
" RERUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-17 11:14 | \n",
" 1.6.8 | \n",
" 9b7208ede6 | \n",
" 2772 | \n",
" no | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/01_atmospheric_boundary_layer | \n",
" RERUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-17 02:09 | \n",
" - | \n",
" - | \n",
" 0 | \n",
" yes | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/02_flow_over_wall_mounted_cube | \n",
" RERUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-22 15:31 | \n",
" 1.6.51 | \n",
" 8df9b1583e | \n",
" 1305 | \n",
" yes | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/03_cedval_building | \n",
" RUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/04_codasc_canyon | \n",
" RUN | \n",
" True | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
"
\n"
],
"text/plain": [
""
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def verdict_css(val):\n",
" pair = VERDICT_STYLE.get(str(val).split(\" \")[0])\n",
" if not pair:\n",
" return \"\"\n",
" bg, fg = pair\n",
" return f\"background-color: {bg}; color: {fg}; font-weight: 600\"\n",
"\n",
"\n",
"def style_table(df):\n",
" return (\n",
" df.style.map(verdict_css, subset=[\"verdict\"])\n",
" .set_properties(**{\"text-align\": \"left\"})\n",
" .hide(axis=\"index\")\n",
" )\n",
"\n",
"\n",
"df = pd.DataFrame(case_rows(cases, head))\n",
"style_table(df)"
]
},
{
"cell_type": "markdown",
"id": "26a16660",
"metadata": {},
"source": [
"## Cases needing action\n",
"\n",
"Everything that is not plain `OK` - your worklist."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b479c7db",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-29T17:47:21.633576Z",
"iopub.status.busy": "2026-06-29T17:47:21.633486Z",
"iopub.status.idle": "2026-06-29T17:47:21.640870Z",
"shell.execute_reply": "2026-06-29T17:47:21.640634Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"26 of 26 cases need action\n"
]
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
" \n",
" | category | \n",
" case | \n",
" verdict | \n",
" config_changed | \n",
" notebooks_executed | \n",
" notebook_saved | \n",
" ran_version | \n",
" ran_commit | \n",
" commits_behind_head | \n",
" local_results | \n",
"
\n",
" \n",
" \n",
" \n",
" | analytical | \n",
" analytical/01_couette_flow | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" ab62e54213 | \n",
" 4 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/02_poiseuille_channel_flow | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 4/4 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" 20647b9f11 | \n",
" 240 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/03_poiseuille_pipe_flow | \n",
" RERUN | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-24 09:13 | \n",
" 2.0.1a0 | \n",
" 393bce29ec | \n",
" 239 | \n",
" yes | \n",
"
\n",
" \n",
" | analytical | \n",
" analytical/04_taylor_green_vortex_2d | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:41 | \n",
" 2.0.1a0 | \n",
" 7c0d788ec9 | \n",
" 235 | \n",
" yes | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/01_lowmach_bubble | \n",
" RUN | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/02_sandia_helium_plume | \n",
" NO_NB | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/03_steckler_room_fire | \n",
" RUN | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | buoyant_flows | \n",
" buoyant_flows/04_sandia_methane_pool_fire | \n",
" NO_NB | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | calibration | \n",
" calibration/neq_wm_pressure_filter/reference | \n",
" NO_NB | \n",
" 2026-06-24 17:08 | \n",
" - | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/01_flow_over_sphere | \n",
" BEHIND | \n",
" 2026-06-24 19:23 | \n",
" 3/3 | \n",
" 2026-06-25 02:48 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/02_flow_over_cylinder | \n",
" RERUN | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-24 13:00 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | external_aero | \n",
" external_aero/03_flat_plate_boundary_layer | \n",
" RUN | \n",
" 2026-06-24 19:35 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | porous_media | \n",
" porous_media/01_flow_through_trees | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 74086fcfe9 | \n",
" 218 | \n",
" yes | \n",
"
\n",
" \n",
" | porous_media | \n",
" porous_media/02_porous_pipe_flow | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | scalar_transport | \n",
" scalar_transport/01_passive_scalar_transport | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-29 14:43 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | scalar_transport | \n",
" scalar_transport/02_turb_channel_passive_scalar | \n",
" BEHIND | \n",
" 2026-06-25 01:49 | \n",
" 1/1 | \n",
" 2026-06-25 11:53 | \n",
" 2.0.1a0 | \n",
" ce9eac31c9 | \n",
" 100 | \n",
" yes | \n",
"
\n",
" \n",
" | thermal | \n",
" thermal/01_differentially_heated_cavity | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:44 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | thermal | \n",
" thermal/02_heated_cylinder | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:44 | \n",
" 2.0.1a0 | \n",
" 80a1135356 | \n",
" 213 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/01_taylor_green_vortex_3d | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-29 14:45 | \n",
" 2.0.1a0 | \n",
" 50dc5112a0 | \n",
" 199 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/02_turbulent_channel_flow | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 3/3 | \n",
" 2026-06-29 14:45 | \n",
" 2.0.1a0 | \n",
" 50dc5112a0 | \n",
" 199 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/03_turbulent_pipe_flow | \n",
" BEHIND | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-29 14:45 | \n",
" 1.6.17,2.0.1a0 | \n",
" 09c9337ca1 | \n",
" 2546 | \n",
" yes | \n",
"
\n",
" \n",
" | turbulence | \n",
" turbulence/04_backward_step_flow | \n",
" RERUN | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-17 11:14 | \n",
" 1.6.8 | \n",
" 9b7208ede6 | \n",
" 2772 | \n",
" no | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/01_atmospheric_boundary_layer | \n",
" RERUN | \n",
" 2026-06-24 17:08 | \n",
" 1/1 | \n",
" 2026-06-17 02:09 | \n",
" - | \n",
" - | \n",
" 0 | \n",
" yes | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/02_flow_over_wall_mounted_cube | \n",
" RERUN | \n",
" 2026-06-24 17:08 | \n",
" 2/2 | \n",
" 2026-06-22 15:31 | \n",
" 1.6.51 | \n",
" 8df9b1583e | \n",
" 1305 | \n",
" yes | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/03_cedval_building | \n",
" RUN | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
" | wind_engineering | \n",
" wind_engineering/04_codasc_canyon | \n",
" RUN | \n",
" 2026-06-24 17:08 | \n",
" 0/1 | \n",
" - | \n",
" - | \n",
" - | \n",
" 0 | \n",
" no | \n",
"
\n",
" \n",
"
\n"
],
"text/plain": [
""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"todo = df[df[\"needs_action\"]].drop(columns=[\"needs_action\"])\n",
"print(f\"{len(todo)} of {len(df)} cases need action\")\n",
"style_table(todo) if len(todo) else print(\"All cases OK \\U0001f389\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "6bd2dbc8",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-29T17:47:21.641629Z",
"iopub.status.busy": "2026-06-29T17:47:21.641562Z",
"iopub.status.idle": "2026-06-29T17:47:21.645288Z",
"shell.execute_reply": "2026-06-29T17:47:21.645037Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" cases | \n",
"
\n",
" \n",
" \n",
" \n",
" | BEHIND | \n",
" 13 | \n",
"
\n",
" \n",
" | RERUN | \n",
" 5 | \n",
"
\n",
" \n",
" | RUN | \n",
" 5 | \n",
"
\n",
" \n",
" | NO_NB | \n",
" 3 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" cases\n",
"BEHIND 13\n",
"RERUN 5\n",
"RUN 5\n",
"NO_NB 3"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Verdict tally across all cases.\n",
"from collections import Counter\n",
"\n",
"tally = Counter(f for v in df[\"verdict\"] for f in v.split(\" \"))\n",
"pd.Series(tally, name=\"cases\").sort_values(ascending=False).to_frame()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "nassu",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.13"
},
"nbsphinx": {
"orphan": true
}
},
"nbformat": 4,
"nbformat_minor": 5
}