{ "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
categorycaseverdictneeds_actionconfig_changednotebooks_executednotebook_savedran_versionran_commitcommits_behind_headlocal_results
analyticalanalytical/01_couette_flowBEHINDTrue2026-06-24 17:081/12026-06-29 14:412.0.1a0ab62e542134yes
analyticalanalytical/02_poiseuille_channel_flowBEHINDTrue2026-06-24 17:084/42026-06-29 14:412.0.1a020647b9f11240yes
analyticalanalytical/03_poiseuille_pipe_flowRERUNTrue2026-06-24 17:082/22026-06-24 09:132.0.1a0393bce29ec239yes
analyticalanalytical/04_taylor_green_vortex_2dBEHINDTrue2026-06-24 17:081/12026-06-29 14:412.0.1a07c0d788ec9235yes
buoyant_flowsbuoyant_flows/01_lowmach_bubbleRUNTrue2026-06-24 17:080/1---0no
buoyant_flowsbuoyant_flows/02_sandia_helium_plumeNO_NBTrue2026-06-24 17:08----0no
buoyant_flowsbuoyant_flows/03_steckler_room_fireRUNTrue2026-06-24 17:080/1---0no
buoyant_flowsbuoyant_flows/04_sandia_methane_pool_fireNO_NBTrue2026-06-24 17:08----0no
calibrationcalibration/neq_wm_pressure_filter/referenceNO_NBTrue2026-06-24 17:08----0no
external_aeroexternal_aero/01_flow_over_sphereBEHINDTrue2026-06-24 19:233/32026-06-25 02:482.0.1a074086fcfe9218yes
external_aeroexternal_aero/02_flow_over_cylinderRERUNTrue2026-06-24 17:082/22026-06-24 13:002.0.1a074086fcfe9218yes
external_aeroexternal_aero/03_flat_plate_boundary_layerRUNTrue2026-06-24 19:350/1---0no
porous_mediaporous_media/01_flow_through_treesBEHINDTrue2026-06-24 17:081/12026-06-29 14:432.0.1a074086fcfe9218yes
porous_mediaporous_media/02_porous_pipe_flowBEHINDTrue2026-06-24 17:081/12026-06-29 14:432.0.1a080a1135356213yes
scalar_transportscalar_transport/01_passive_scalar_transportBEHINDTrue2026-06-24 17:082/22026-06-29 14:432.0.1a080a1135356213yes
scalar_transportscalar_transport/02_turb_channel_passive_scalarBEHINDTrue2026-06-25 01:491/12026-06-25 11:532.0.1a0ce9eac31c9100yes
thermalthermal/01_differentially_heated_cavityBEHINDTrue2026-06-24 17:081/12026-06-29 14:442.0.1a080a1135356213yes
thermalthermal/02_heated_cylinderBEHINDTrue2026-06-24 17:081/12026-06-29 14:442.0.1a080a1135356213yes
turbulenceturbulence/01_taylor_green_vortex_3dBEHINDTrue2026-06-24 17:081/12026-06-29 14:452.0.1a050dc5112a0199yes
turbulenceturbulence/02_turbulent_channel_flowBEHINDTrue2026-06-24 17:083/32026-06-29 14:452.0.1a050dc5112a0199yes
turbulenceturbulence/03_turbulent_pipe_flowBEHINDTrue2026-06-24 17:082/22026-06-29 14:451.6.17,2.0.1a009c9337ca12546yes
turbulenceturbulence/04_backward_step_flowRERUNTrue2026-06-24 17:081/12026-06-17 11:141.6.89b7208ede62772no
wind_engineeringwind_engineering/01_atmospheric_boundary_layerRERUNTrue2026-06-24 17:081/12026-06-17 02:09--0yes
wind_engineeringwind_engineering/02_flow_over_wall_mounted_cubeRERUNTrue2026-06-24 17:082/22026-06-22 15:311.6.518df9b1583e1305yes
wind_engineeringwind_engineering/03_cedval_buildingRUNTrue2026-06-24 17:080/1---0no
wind_engineeringwind_engineering/04_codasc_canyonRUNTrue2026-06-24 17:080/1---0no
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
categorycaseverdictconfig_changednotebooks_executednotebook_savedran_versionran_commitcommits_behind_headlocal_results
analyticalanalytical/01_couette_flowBEHIND2026-06-24 17:081/12026-06-29 14:412.0.1a0ab62e542134yes
analyticalanalytical/02_poiseuille_channel_flowBEHIND2026-06-24 17:084/42026-06-29 14:412.0.1a020647b9f11240yes
analyticalanalytical/03_poiseuille_pipe_flowRERUN2026-06-24 17:082/22026-06-24 09:132.0.1a0393bce29ec239yes
analyticalanalytical/04_taylor_green_vortex_2dBEHIND2026-06-24 17:081/12026-06-29 14:412.0.1a07c0d788ec9235yes
buoyant_flowsbuoyant_flows/01_lowmach_bubbleRUN2026-06-24 17:080/1---0no
buoyant_flowsbuoyant_flows/02_sandia_helium_plumeNO_NB2026-06-24 17:08----0no
buoyant_flowsbuoyant_flows/03_steckler_room_fireRUN2026-06-24 17:080/1---0no
buoyant_flowsbuoyant_flows/04_sandia_methane_pool_fireNO_NB2026-06-24 17:08----0no
calibrationcalibration/neq_wm_pressure_filter/referenceNO_NB2026-06-24 17:08----0no
external_aeroexternal_aero/01_flow_over_sphereBEHIND2026-06-24 19:233/32026-06-25 02:482.0.1a074086fcfe9218yes
external_aeroexternal_aero/02_flow_over_cylinderRERUN2026-06-24 17:082/22026-06-24 13:002.0.1a074086fcfe9218yes
external_aeroexternal_aero/03_flat_plate_boundary_layerRUN2026-06-24 19:350/1---0no
porous_mediaporous_media/01_flow_through_treesBEHIND2026-06-24 17:081/12026-06-29 14:432.0.1a074086fcfe9218yes
porous_mediaporous_media/02_porous_pipe_flowBEHIND2026-06-24 17:081/12026-06-29 14:432.0.1a080a1135356213yes
scalar_transportscalar_transport/01_passive_scalar_transportBEHIND2026-06-24 17:082/22026-06-29 14:432.0.1a080a1135356213yes
scalar_transportscalar_transport/02_turb_channel_passive_scalarBEHIND2026-06-25 01:491/12026-06-25 11:532.0.1a0ce9eac31c9100yes
thermalthermal/01_differentially_heated_cavityBEHIND2026-06-24 17:081/12026-06-29 14:442.0.1a080a1135356213yes
thermalthermal/02_heated_cylinderBEHIND2026-06-24 17:081/12026-06-29 14:442.0.1a080a1135356213yes
turbulenceturbulence/01_taylor_green_vortex_3dBEHIND2026-06-24 17:081/12026-06-29 14:452.0.1a050dc5112a0199yes
turbulenceturbulence/02_turbulent_channel_flowBEHIND2026-06-24 17:083/32026-06-29 14:452.0.1a050dc5112a0199yes
turbulenceturbulence/03_turbulent_pipe_flowBEHIND2026-06-24 17:082/22026-06-29 14:451.6.17,2.0.1a009c9337ca12546yes
turbulenceturbulence/04_backward_step_flowRERUN2026-06-24 17:081/12026-06-17 11:141.6.89b7208ede62772no
wind_engineeringwind_engineering/01_atmospheric_boundary_layerRERUN2026-06-24 17:081/12026-06-17 02:09--0yes
wind_engineeringwind_engineering/02_flow_over_wall_mounted_cubeRERUN2026-06-24 17:082/22026-06-22 15:311.6.518df9b1583e1305yes
wind_engineeringwind_engineering/03_cedval_buildingRUN2026-06-24 17:080/1---0no
wind_engineeringwind_engineering/04_codasc_canyonRUN2026-06-24 17:080/1---0no
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
cases
BEHIND13
RERUN5
RUN5
NO_NB3
\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 }