"""Real config loading and pure structural update for the frozen development case.""" import importlib.util import math from pathlib import Path import numpy as np import pytest path = Path(__file__).resolve().parents[2] / 'research/steady_pinball/run.py' spec = importlib.util.spec_from_file_location('steady_pinball_run', path) runner = importlib.util.module_from_spec(spec) spec.loader.exec_module(runner) def test_actual_loader_and_paper_geometry(tmp_path): import json from CelerisLab.config import load_lbm_config p = tmp_path/'config.json' p.write_text(json.dumps(runner.config())) cfg = load_lbm_config(str(p)) cfg.validate() assert (cfg.nx, cfg.ny, cfg.viscosity, cfg.velocity) == (1280, 512, .004, .01) assert cfg.collision == 'MRT' and cfg.y_wall_bc == 'bounce_back' assert runner.U * runner.D / runner.NU == 50. assert runner.CENTRES == ((600., 255.5), (626., 270.5), (626., 240.5)) def test_structure_rest_equilibrium_and_energy_decay(): s = runner.Structure() assert s.candidate(0.) == (0., 0.) assert s.mass == pytest.approx(10*3*math.pi*100) s.y, s.v = .2, 0. # One damped period: independent continuous solution at total elapsed time. for _ in range(runner.PERIOD): s.y, s.v = s.candidate(0.) t = runner.PERIOD a = s.zeta*s.omega w = s.omega*math.sqrt(1-s.zeta**2) expected = .2*math.exp(-a*t)*(math.cos(w*t)+a/w*math.sin(w*t)) assert s.y == pytest.approx(expected, abs=2e-12) assert .5*s.mass*s.v*s.v+.5*s.stiffness*s.y*s.y < .5*s.stiffness*.2**2 s.y, s.v = .3, 0. assert s.candidate(s.stiffness*.3) == pytest.approx((.3, 0.), abs=1e-15) def test_report_uses_completed_rows_and_net_force(): rows = np.zeros((4, 15)) rows[:, 0] = np.arange(1, 5) rows[:, 1] = 2. rows[:, 3:9] = 1. got = runner.summarize(rows) assert got[0]['end_step'] == 4 assert got[0]['y_rms_D'] == .1 assert got[0]['fy_rms'] == 6.