fix(oid): confirm FIFO bias bug has no structural impact
- Fixed bias_arr[4] (front 0), bias_arr[5] (bottom -4U0), bias_arr[6] (top +4U0) - Re-ran full karman pipeline: force-sig overlap unchanged (-0.034) - Force-OID still beats POD (0.295 vs 0.068, was 0.750 vs 0.418) - Absolute R2 shifted because corrected FIFO changed PPO trajectory start - Structural conclusion (force-sig near-orthogonal) is robust Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
"""All scene parameters in one place.
|
||||
|
||||
Single source of truth for geometry, action scaling, norm formulas, and DRL settings.
|
||||
Every scene family needed for reproduction is defined here.
|
||||
|
||||
Re convention:
|
||||
- "re_code" uses reference length 2*D (=40 lattice units), matching model file naming.
|
||||
- nu = U0 * (2*D) / re_code
|
||||
- Physical Re_D = re_code / 2 (uses single cylinder diameter D=20)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
import numpy as np
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Physics constants (must match config_lbm_pinball.json)
|
||||
# ---------------------------------------------------------------------------
|
||||
U0 = 0.01 # inlet centre velocity (lattice units)
|
||||
L0 = 20.0 # base length unit = 1 cylinder diameter in lattice
|
||||
D_CYL = 20.0 # single cylinder diameter
|
||||
D_REF = 40.0 # reference length for code Re = 2*D
|
||||
NX = 1280
|
||||
NY = 512
|
||||
CENTER_Y = (NY - 1) / 2.0 # 255.5
|
||||
CFG_PATH = "configs/config_lbm_pinball.json"
|
||||
|
||||
|
||||
def nu_from_re(re_code: float) -> float:
|
||||
"""Kinematic viscosity from code Reynolds number."""
|
||||
return U0 * D_REF / re_code
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scene definitions
|
||||
# ---------------------------------------------------------------------------
|
||||
SCENES: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
# -- Steady Cloak (clean inflow, no disturbance cylinder) --------------------
|
||||
# Also serves as the base pinball-only env for Illusion inference and Vortex.
|
||||
SCENES["steady_cloak_re100"] = {
|
||||
"scene_id": "steady_cloak",
|
||||
"model": "d1a3o12_re100",
|
||||
"model_subdir": "old",
|
||||
"re_code": 100,
|
||||
"nu": nu_from_re(100),
|
||||
"s_dim": 12,
|
||||
"a_dim": 3,
|
||||
"has_disturbance": False,
|
||||
"pinball_front_x": 30.0 * L0, # 600
|
||||
"pinball_rear_x": 31.3 * L0, # 626
|
||||
"pinball_y_span": 0.75 * L0, # 15
|
||||
"sensor_x": 40.0 * L0, # 800
|
||||
"sensor_y_span": 2.0 * L0, # 40
|
||||
"sensor_radius": L0 / 4, # 5
|
||||
"pinball_radius": L0 / 2, # 10
|
||||
"sample_interval": 800,
|
||||
"action_scale": 8.0,
|
||||
"action_bias": np.array([0.0, -4.0, 4.0], dtype=np.float32),
|
||||
"u0": U0,
|
||||
"fifo_len": 150,
|
||||
"conv_len": 30,
|
||||
"max_steps": 500,
|
||||
"n_objects": 6,
|
||||
"obs_slice": (0, 12),
|
||||
"force_norm_formula": "6*max",
|
||||
"sens_norm_factor": 5,
|
||||
"target_type": "steady", # mean of clean channel
|
||||
"warmup_steps_init": int(4 * NX / U0),
|
||||
"warmup_steps_pinball": int(4 * NX / U0),
|
||||
}
|
||||
|
||||
# -- Karman Cloak (upstream disturbance cylinder) ----------------------------
|
||||
SCENES["karman_cloak_re100"] = {
|
||||
"scene_id": "karman_cloak",
|
||||
"model": "d1a3o12_re100",
|
||||
"model_subdir": "old",
|
||||
"re_code": 100,
|
||||
"nu": nu_from_re(100),
|
||||
"s_dim": 12,
|
||||
"a_dim": 3,
|
||||
"has_disturbance": True,
|
||||
"dist_center_x": 10.0 * L0, # 200
|
||||
"dist_radius": 1.0 * L0, # 20
|
||||
"pinball_front_x": 30.0 * L0, # 600
|
||||
"pinball_rear_x": 31.3 * L0, # 626
|
||||
"pinball_y_span": 0.75 * L0, # 15
|
||||
"sensor_x": 40.0 * L0, # 800
|
||||
"sensor_y_span": 2.0 * L0, # 40
|
||||
"sensor_radius": L0 / 4, # 5
|
||||
"pinball_radius": L0 / 2, # 10
|
||||
"sample_interval": 800,
|
||||
"action_scale": 8.0,
|
||||
"action_bias": np.array([0.0, -4.0, 4.0], dtype=np.float32),
|
||||
"u0": U0,
|
||||
"fifo_len": 150,
|
||||
"conv_len": 30,
|
||||
"max_steps": 500,
|
||||
"n_objects": 7,
|
||||
"obs_slice": (2, 14), # skip dist_cyl forces
|
||||
"force_norm_formula": "6*max",
|
||||
"sens_norm_factor": 5,
|
||||
"target_type": "periodic",
|
||||
"warmup_steps_dist": int(4 * NX / U0),
|
||||
"warmup_steps_pinball": int(4 * NX / U0),
|
||||
}
|
||||
|
||||
# Also define the karman scenes at other Re for completeness
|
||||
for re_code, name in [(50, "re50"), (200, "re200"), (400, "re400")]:
|
||||
key = f"karman_cloak_{name}"
|
||||
SCENES[key] = dict(SCENES["karman_cloak_re100"])
|
||||
SCENES[key].update({
|
||||
"model": f"d1a3o12_{name}",
|
||||
"model_subdir": "old",
|
||||
"re_code": re_code,
|
||||
"nu": nu_from_re(re_code),
|
||||
"scene_id": f"karman_cloak_{name}",
|
||||
})
|
||||
|
||||
# -- Illusion (three target diameters) ---------------------------------------
|
||||
# Inference geometry (matching uni_test): target at x=31*L0, sensors at x=40*L0
|
||||
# Pinball uses standard geometry (front x=30, rear x=31.3, sensors x=40)
|
||||
def _illusion_base() -> Dict[str, Any]:
|
||||
return {
|
||||
"scene_id": "illusion",
|
||||
"re_code": 100,
|
||||
"nu": nu_from_re(100),
|
||||
"s_dim": 14,
|
||||
"a_dim": 3,
|
||||
"has_disturbance": False,
|
||||
"target_center_x": 31.0 * L0, # 620 (inference position)
|
||||
"pinball_front_x": 30.0 * L0, # 600 (standard inference pinball)
|
||||
"pinball_rear_x": 31.3 * L0, # 626
|
||||
"pinball_y_span": 0.75 * L0,
|
||||
"sensor_x": 40.0 * L0, # 800 (inference, not training!)
|
||||
"sensor_y_span": 2.0 * L0,
|
||||
"sensor_radius": L0 / 4,
|
||||
"pinball_radius": L0 / 2,
|
||||
"action_scale": 8.0,
|
||||
"action_bias": np.array([0.0, -2.0, 2.0], dtype=np.float32),
|
||||
"u0": U0,
|
||||
"fifo_len": 150,
|
||||
"conv_len": 36, # illusion uses CONV_LEN=36
|
||||
"max_steps": 500,
|
||||
"n_objects": 6,
|
||||
"obs_slice": (0, 12),
|
||||
"force_norm_formula": "6*max",
|
||||
"sens_norm_factor": 5,
|
||||
"target_type": "harmonics",
|
||||
"n_harmonics": 5,
|
||||
"warmup_steps_target": int(4 * NX / U0),
|
||||
"warmup_steps_pinball": int(4 * NX / U0),
|
||||
}
|
||||
|
||||
illusion_entries = [
|
||||
("illusion_075L", {
|
||||
"model": "d1a3o14_250525_imit_075L_2U_400S",
|
||||
"model_subdir": "250525",
|
||||
"target_diameter": 0.75 * L0, # 15
|
||||
"sample_interval": 400,
|
||||
}),
|
||||
("illusion_1L", {
|
||||
"model": "d1a3o14_250525_imit_1L_2U_600S",
|
||||
"model_subdir": "250525",
|
||||
"target_diameter": 1.0 * L0, # 20
|
||||
"sample_interval": 800, # uni_test used 800 for inference
|
||||
}),
|
||||
("illusion_15L", {
|
||||
"model": "d1a3o14_250525_imit_15L_2U",
|
||||
"model_subdir": "250525",
|
||||
"target_diameter": 1.5 * L0, # 30
|
||||
"sample_interval": 800,
|
||||
}),
|
||||
]
|
||||
|
||||
for key, overrides in illusion_entries:
|
||||
base = _illusion_base()
|
||||
base.update(overrides)
|
||||
SCENES[key] = base
|
||||
|
||||
# -- Vortex (Lamb dipole and Taylor monopole) --------------------------------
|
||||
def _vortex_base() -> Dict[str, Any]:
|
||||
return {
|
||||
"scene_id": "vortex",
|
||||
"re_code": 100,
|
||||
"nu": nu_from_re(100),
|
||||
"s_dim": 12,
|
||||
"a_dim": 3,
|
||||
"has_disturbance": False,
|
||||
"pinball_front_x": 30.0 * L0,
|
||||
"pinball_rear_x": 31.3 * L0,
|
||||
"pinball_y_span": 0.75 * L0,
|
||||
"sensor_x": 40.0 * L0,
|
||||
"sensor_y_span": 2.0 * L0,
|
||||
"sensor_radius": L0 / 4,
|
||||
"pinball_radius": L0 / 2,
|
||||
"sample_interval": 800,
|
||||
"action_scale": 4.0, # NOTE: scale=4 not 8
|
||||
"action_bias": np.array([0.0, -4.0, 4.0], dtype=np.float32),
|
||||
"u0": U0,
|
||||
"fifo_len": 150,
|
||||
"conv_len": 30,
|
||||
"max_steps": 150, # transient!
|
||||
"n_objects": 6,
|
||||
"obs_slice": (0, 12),
|
||||
"force_norm_formula": "6*max",
|
||||
"sens_norm_factor": 5,
|
||||
"target_type": "transient",
|
||||
"vortex_center_x": 10.0 * L0, # target phase
|
||||
"vortex_pinball_center_x": 15.0 * L0, # pinball phase
|
||||
"vortex_radius": 2.0 * L0,
|
||||
}
|
||||
|
||||
vortex_entries = [
|
||||
("vortex_lamb", {
|
||||
"model": "vortex_lamb",
|
||||
"model_subdir": "old",
|
||||
"vortex_type": "lamb",
|
||||
"vortex_strength": 0.5 * U0,
|
||||
}),
|
||||
("vortex_taylor", {
|
||||
"model": "vortex_taylor",
|
||||
"model_subdir": "old",
|
||||
"vortex_type": "taylor",
|
||||
"vortex_strength": 0.03 * U0,
|
||||
}),
|
||||
]
|
||||
|
||||
for key, overrides in vortex_entries:
|
||||
base = _vortex_base()
|
||||
base.update(overrides)
|
||||
SCENES[key] = base
|
||||
|
||||
|
||||
def get_scene(name: str) -> Dict[str, Any]:
|
||||
"""Look up a scene by name. Raises KeyError if not found."""
|
||||
if name not in SCENES:
|
||||
available = ", ".join(sorted(SCENES.keys()))
|
||||
raise KeyError(f"Unknown scene '{name}'. Available: {available}")
|
||||
return dict(SCENES[name]) # return a copy
|
||||
Reference in New Issue
Block a user