diff --git a/PARITY.md b/PARITY.md index e25c8f23..3bac66f7 100644 --- a/PARITY.md +++ b/PARITY.md @@ -870,8 +870,12 @@ separate from Python's default implicit added mass. A [four-step R2025b refinement](https://github.com/cmudrc/wec-sim-python/actions/runs/38093454463) keeps the published 120 s, 2.5 m/8 s regular wave, 40 s ramp, hinge, and mesh -hydrodynamics. Disabling pressure and VTP output changes the original 0.1 s -pitch trace by exactly zero. Halving the MATLAB `ode4` and nonlinear-force +hydrodynamics. Disabling pressure and VTP output leaves all 1,201 samples of +the original 0.1 s flap pose, velocity, acceleration, excitation, radiation, +added-mass, and restoring-force histories exactly unchanged. The refined-step +gate now compares its coarse run against a compact +[published-case source fixture](tests/fixtures/matlab/README.md) before +testing Python motion. Halving the MATLAB `ode4` and nonlinear-force sample steps from `0.1` to `0.05`, `0.05` to `0.025`, and `0.025` to `0.0125` s changes pitch by at most `0.005581`, `0.001295`, and `0.000317` rad, respectively. The public `WEC.fixed_hinge` path now diff --git a/tests/fixtures/matlab/README.md b/tests/fixtures/matlab/README.md index 13c85e0d..ff544a2d 100644 --- a/tests/fixtures/matlab/README.md +++ b/tests/fixtures/matlab/README.md @@ -1,5 +1,23 @@ # Portable MATLAB reference data +## OSWEC nonlinear visualization motion + +`oswec_nonlinear_viz_published_motion.npz` keeps all 1,201 samples of the +published 120 s, 0.1 s `ode4` flap motion and its principal force channels. +It was copied without rounding from the untouched R2025b +[published-case run](https://github.com/cmudrc/wec-sim-python/actions/runs/38259283403) +(`matlab-oswec-nonlinear-viz-pressure` artifact, run commit +`1083a1f29725785cbba0cb67f8e389a9cade16fc`). The workflow pins WEC-Sim +core `0753b2e47f2457c078751dcfe5d251d1767b80ab` and Applications +`d53d4d4c9eda2581f04204f5d394a6ef84bb099e`. The source `source.mat` +hash is `f5c2944777f35741a48034e64fb4d3d7ec165466f6f2fc76f7be1f38383ad466`. +The archive contains `time`, `pose`, `velocity`, `acceleration`, +`forceExcitation`, `forceRadiationDamping`, `forceAddedMass`, and +`forceRestoring` for the six flap degrees of freedom. Its SHA-256 is +`63f3669b192ab271a92f5ea9c4650d1d183ef68f0abc4a6b0faedcd2b4948e4f`. +The refined-step source job gates its 0.1 s run against these published +channels before evaluating step sensitivity or independent Python motion. + ## RM3 regular wave `rm3_regular_wave.npz` contains selected motion and PTO channels at every diff --git a/tests/fixtures/matlab/oswec_nonlinear_viz_published_motion.npz b/tests/fixtures/matlab/oswec_nonlinear_viz_published_motion.npz new file mode 100644 index 00000000..946ec63f Binary files /dev/null and b/tests/fixtures/matlab/oswec_nonlinear_viz_published_motion.npz differ diff --git a/tests/test_oswec_nonlinear_viz_motion.py b/tests/test_oswec_nonlinear_viz_motion.py index 9b8550c7..86936d52 100644 --- a/tests/test_oswec_nonlinear_viz_motion.py +++ b/tests/test_oswec_nonlinear_viz_motion.py @@ -29,6 +29,17 @@ def test_published_nonlinear_oswec_hinge_against_refined_matlab(): assert float(source["nonlinearDt"].item()) == dt assert str(source["solver"].item()) == "ode4" assert len(source["time"].ravel()) == round(120 / dt) + 1 + # The refinement disables pressure and ParaView output. Its coarse run + # must still reproduce the untouched published case before comparisons + # with finer source steps can be interpreted as step sensitivity. + fixture = (Path(__file__).parent / "fixtures" / "matlab" + / "oswec_nonlinear_viz_published_motion.npz") + with np.load(fixture) as published: + np.testing.assert_array_equal(coarse["time"].ravel(), published["time"]) + for channel in ("pose", "velocity", "acceleration", + "forceExcitation", "forceRadiationDamping", + "forceAddedMass", "forceRestoring"): + np.testing.assert_array_equal(coarse[channel], published[channel]) np.testing.assert_allclose(coarse["time"].ravel(), medium["time"].ravel()[::2], rtol=0, atol=1e-12) np.testing.assert_allclose(medium["time"].ravel(),