"""Collect steady cloak (open-loop constant rotation). Usage: conda run -n pycuda_3_10 python scripts/collect_steady_cloak.py --device 2 Output: data/steady_cloak/steady_cloak/ """ from __future__ import annotations import argparse import json import os import sys import time import numpy as np _REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) if _REPO not in sys.path: sys.path.insert(0, _REPO) _SRC = os.path.join(_REPO, "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from LegacyCelerisLab import FlowField from CCD_analysis.configs import get_scene, data_dir_for_scene, LEGACY_CFG_DIR, L0, CENTER_Y from CCD_analysis.utils.cfd_interface import ( load_legacy_configs, get_velocity_field, save_vorticity_png, vorticity_from_ddf, ) print("Steady cloak collection starting...", flush=True) def collect(): ap = argparse.ArgumentParser() ap.add_argument("--device", type=int, default=2) ap.add_argument("--tune", action="store_true", help="scan rear omega") args = ap.parse_args() cfg = get_scene("steady_cloak") out_dir = data_dir_for_scene("steady_cloak") print(f"Output dir: {out_dir}", flush=True) cuda_cfg, field_cfg = load_legacy_configs(LEGACY_CFG_DIR) field_cfg = field_cfg._replace(viscosity=float(cfg["nu"])) print(f"Configs loaded. Viscosity={cfg['nu']}", flush=True) print(f"Creating FlowField on device {args.device}...", flush=True) ff = FlowField(field_cfg, cuda_cfg, device_id=args.device) print("FlowField created.", flush=True) l0 = L0 for y_off in [2.0, 0.0, -2.0]: ff.add_sensor((40.0 * l0, CENTER_Y + y_off * l0, 0.0), l0 / 4.0) ff.add_cylinder((30.0 * l0, CENTER_Y, 0.0), l0 / 2.0) ff.add_cylinder((31.3 * l0, CENTER_Y + 0.75 * l0, 0.0), l0 / 2.0) ff.add_cylinder((31.3 * l0, CENTER_Y - 0.75 * l0, 0.0), l0 / 2.0) print("Objects added.", flush=True) n_obj = 6 stabilize = int(4 * 1280 / cfg["u0"]) print(f"Initial stabilization ({stabilize} steps)...", flush=True) ff.run(stabilize, np.zeros(n_obj, dtype=np.float32)) print("Initial stabilization done.", flush=True) rear_scale = cfg["omega_rear_scale"] if args.tune: candidates = [4.7, 4.9, 5.1, 5.3, 5.5] else: candidates = [rear_scale] for scale in candidates: rear_val = scale * cfg["u0"] temp = np.zeros(n_obj, dtype=np.float32) temp[3] = cfg["omega_front"] temp[4] = rear_val temp[5] = -rear_val print(f"Stabilizing with rear={scale:.1f}xU0 ({stabilize} steps)...", flush=True) ff.run(stabilize, temp) sens_list = [] for _ in range(30): ff.run(cfg["sample_interval"], temp) sens_list.append(ff.obs.copy()[0:6]) std = float(np.std(np.array(sens_list), axis=0).mean()) print(f" rear={scale:.1f}xU0 -> sensor std={std:.6f}", flush=True) # Save with best (or single) value rear_val = candidates[-1] * cfg["u0"] temp = np.zeros(n_obj, dtype=np.float32) temp[3] = cfg["omega_front"] temp[4] = rear_val temp[5] = -rear_val print(f"Saving final data (rear={candidates[-1]:.1f}xU0)...", flush=True) sens_list, forc_list, ux_list, uy_list = [], [], [], [] for _ in range(30): ff.run(cfg["sample_interval"], temp) obs = ff.obs.copy() sens_list.append(obs[0:6]) forc_list.append(obs[6:12]) ux, uy = get_velocity_field(ff, u0=cfg["u0"]) ux_list.append(ux) uy_list.append(uy) np.savez_compressed(os.path.join(out_dir, "fields.npz"), ux=np.stack(ux_list), uy=np.stack(uy_list)) np.savez(os.path.join(out_dir, "sensors.npz"), sensors=np.array(sens_list, dtype=np.float32), forces=np.array(forc_list, dtype=np.float32)) omega = vorticity_from_ddf(ff, u0=cfg["u0"]) save_vorticity_png(os.path.join(out_dir, "vorticity.png"), omega, title="Steady Cloak Re=100") del ff meta = dict(cfg, rear_scale=candidates[-1], n_samples=30) with open(os.path.join(out_dir, "meta.json"), "w") as f: json.dump(meta, f, indent=2) print(f"Done, saved to {out_dir}", flush=True) if __name__ == "__main__": t0 = time.time() collect() print(f"Time: {time.time() - t0:.1f}s", flush=True)