Spaces:
Running
Running
| """Render the flown-rover rediscovery mass--range overlay (Fig. 5). | |
| Reproduces ``paper/figures/fig_rediscovery_overlay.png`` from the committed | |
| rediscovery artifacts under | |
| ``reports/rediscovery_loo_evaluator/`` (regenerate those with | |
| ``python scripts/run_rediscovery_loo.py --all``). For each flown rover the | |
| panel overlays its real design point and the nearest Pareto design on the | |
| optimizer's front in the (total mass, range) plane. | |
| Usage | |
| ----- | |
| :: | |
| python scripts/make_rediscovery_overlay_figure.py | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from roverdevkit.tradespace.visualize import set_paper_rcparams | |
| # Flown rovers shown in the overlay: artifact slug -> panel label. | |
| FLOWN_ROVERS: dict[str, str] = { | |
| "pragyan": "Pragyan (polar)", | |
| "yutu_2": "Yutu-2 (mare)", | |
| } | |
| _FRONT_COLOR = "#bbbbbb" | |
| _NEAREST_COLOR = "#1b7837" | |
| _ROVER_COLOR = "#d6604d" | |
| def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: | |
| p = argparse.ArgumentParser( | |
| description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter | |
| ) | |
| p.add_argument( | |
| "--rediscovery-dir", | |
| type=Path, | |
| default=Path("reports/rediscovery_loo_evaluator"), | |
| help="Directory holding <rover>.json rediscovery artifacts.", | |
| ) | |
| p.add_argument( | |
| "--out", | |
| type=Path, | |
| default=Path("paper/figures/fig_rediscovery_overlay.png"), | |
| ) | |
| return p.parse_args(argv) | |
| def main(argv: list[str] | None = None) -> int: | |
| args = _parse_args(argv) | |
| set_paper_rcparams() | |
| import matplotlib.pyplot as plt | |
| fig, axes = plt.subplots(1, 2, figsize=(10.5, 4.4)) | |
| for ax, (slug, label) in zip(axes, FLOWN_ROVERS.items()): | |
| d = json.loads((args.rediscovery_dir / f"{slug}.json").read_text()) | |
| front = d["pareto_front"] | |
| mass = [p["metrics"]["total_mass_kg"] for p in front] | |
| rng = [p["metrics"]["range_km"] for p in front] | |
| ax.scatter(mass, rng, s=14, color=_FRONT_COLOR, label="Pareto front") | |
| nm = d["nearest_pareto_metrics"] | |
| rm = d["rover_metrics_under_generic_scenario"] | |
| ax.scatter( | |
| [nm["total_mass_kg"]], [nm["range_km"]], | |
| color=_NEAREST_COLOR, s=80, marker="o", zorder=5, label="nearest design", | |
| ) | |
| ax.scatter( | |
| [rm["total_mass_kg"]], [rm["range_km"]], | |
| color=_ROVER_COLOR, s=130, marker="*", zorder=6, label="real rover", | |
| ) | |
| ax.set_title(f"{label}\ndesign-space distance = {d['design_space_distance']:.2f}") | |
| ax.set_xlabel("total mass (kg)") | |
| ax.set_ylabel("range (km)") | |
| ax.legend(loc="best") | |
| fig.suptitle("Rediscovery overlay: real rover vs nearest Pareto design") | |
| fig.tight_layout() | |
| args.out.parent.mkdir(parents=True, exist_ok=True) | |
| fig.savefig(args.out) | |
| plt.close(fig) | |
| print(f"Wrote {args.out}") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |