chatcad / drivaernet_data.py
Samarjithbiswas's picture
chat_cad live (Docker app + weights)
32c7c43 verified
Raw
History Blame Contribute Delete
4.72 kB
"""Real DrivAerNet++ data access — retrieval & statistics.
DrivAerNet++ (Elrefaie et al., MIT, CC-BY-NC-4.0) is a public dataset of
8,150 car designs each with high-fidelity OpenFOAM CFD results. The full
dataset is 39 TB (meshes + volume fields) hosted on Harvard Dataverse,
but the per-design aerodynamic drag coefficients ship as a small CSV that
we vendor locally under ./DrivAerNet/.
This module mirrors the *Simulation Agent* retrieval idea from the
"AI Agents in Engineering Design" paper: rather than running CFD on a new
shape, look up the closest REAL pre-computed cases and report their
measured drag. It needs no torch and no mesh download — just the CSV.
Design-ID morphology codes (first token of the ID):
F = Fastback N = Notchback (sedan) E = Estate (wagon)
Functions:
load_cd() -> dict {design_id: Cd}
dataset_stats() -> overall + per-class statistics
retrieve_by_cd(target) -> closest real designs to a target Cd
class_means() -> mean Cd per body morphology
"""
from __future__ import annotations
import csv
from pathlib import Path
from typing import Dict, List, Optional, Tuple
_DATA_DIR = Path(__file__).parent / "DrivAerNet"
_CD_CSV = _DATA_DIR / "DrivAerNetPlusPlus_Cd_8k.csv"
_CLASS_NAMES = {"F": "Fastback", "N": "Notchback (sedan)", "E": "Estate (wagon)"}
_CACHE: Optional[Dict[str, float]] = None
def available() -> bool:
return _CD_CSV.exists()
def load_cd() -> Dict[str, float]:
"""Return {design_id: Cd} from the vendored DrivAerNet++ CSV."""
global _CACHE
if _CACHE is not None:
return _CACHE
out: Dict[str, float] = {}
if not _CD_CSV.exists():
return out
with open(_CD_CSV) as f:
rd = csv.DictReader(f)
id_key = rd.fieldnames[0]
cd_key = next((k for k in rd.fieldnames
if k and ("drag" in k.lower() or k.lower() == "cd")),
rd.fieldnames[-1])
for row in rd:
try:
out[str(row[id_key]).strip()] = float(row[cd_key])
except (ValueError, KeyError, TypeError):
continue
_CACHE = out
return out
def _morphology(design_id: str) -> str:
return design_id.split("_", 1)[0].upper()
def class_means() -> Dict[str, Tuple[str, int, float, float, float]]:
"""Per-morphology (name, n, mean, min, max) of real Cd."""
cd = load_cd()
buckets: Dict[str, List[float]] = {}
for k, v in cd.items():
buckets.setdefault(_morphology(k), []).append(v)
res = {}
for c, vals in sorted(buckets.items()):
res[c] = (_CLASS_NAMES.get(c, c), len(vals),
sum(vals) / len(vals), min(vals), max(vals))
return res
def dataset_stats() -> dict:
cd = load_cd()
if not cd:
return {"available": False}
vals = list(cd.values())
n = len(vals)
mean = sum(vals) / n
var = sum((x - mean) ** 2 for x in vals) / n
return {
"available": True,
"n": n,
"cd_min": min(vals),
"cd_mean": mean,
"cd_max": max(vals),
"cd_std": var ** 0.5,
"classes": class_means(),
"source": str(_CD_CSV.name),
}
def retrieve_by_cd(target_cd: float, n: int = 5,
morphology: Optional[str] = None
) -> List[Tuple[str, float, float]]:
"""Return the n real designs whose Cd is closest to target_cd.
Each item is (design_id, cd, abs_error). Optionally restrict to a
morphology class ("F"/"N"/"E"). Mirrors the paper's "show me designs
with drag near X" retrieval, but over the REAL CFD dataset.
"""
cd = load_cd()
items = cd.items()
if morphology:
m = morphology.upper()
items = [(k, v) for k, v in items if _morphology(k) == m]
else:
items = list(items)
ranked = sorted(items, key=lambda kv: abs(kv[1] - target_cd))
return [(k, v, abs(v - target_cd)) for k, v in ranked[:n]]
if __name__ == "__main__":
s = dataset_stats()
if not s["available"]:
print("DrivAerNet++ CSV not found — run fetch_drivaernet to download it.")
raise SystemExit(1)
print(f"DrivAerNet++ real drag data ({s['source']})")
print(f" N={s['n']} Cd mean={s['cd_mean']:.3f} "
f"std={s['cd_std']:.3f} range [{s['cd_min']:.3f}, {s['cd_max']:.3f}]")
print(" per body class:")
for c, (name, cnt, mean, lo, hi) in s["classes"].items():
print(f" {c} {name:18s} n={cnt:4d} mean={mean:.3f} [{lo:.3f}, {hi:.3f}]")
print("\n retrieval demo - closest real designs to Cd=0.25:")
for did, cd, err in retrieve_by_cd(0.25, n=5):
print(f" {did:18s} Cd={cd:.4f} (d={err:.4f})")