- Shift analysis from raw-field q_ctl to correction-field dq_ctl = q_ctl - q_blk - Force/action/signature CCD for illusion 0.75L, 1.0L, 1.5L - Zone-restricted CCD (near_body/body_wake/sensor_zone) with spatial separation evidence - 1.5L identified as special mechanism (low action coupling, phase drift) - Karman reference data collected (q_in, q_blk) - Snapshot POD speedup (96x96 instead of 1310720x96) - Comprehensive report: docs/ccd_correction_field_report.md (412 lines) - Handover document: docs/ccd_handover.md Co-authored-by: Cursor <cursoragent@cursor.com>
127 lines
4.3 KiB
Python
127 lines
4.3 KiB
Python
"""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)
|