* test(physics): add TMS Dynamics golden validation suite
Cross-engine validation of Ymir force models against the TMS Dynamics 3.0
golden dataset (vessel1 VLCC). Strategy is state-in -> force-out: the golden
motion trajectory is fed row by row into the matching Ymir force model and the
result is compared against the golden per-force decomposition. This is
integrator-independent, so it validates the force formula, not the time step.
- GoldenCsv.h: header-only CSV reader (column access by name)
- Vessel1Params.h: force configs mapped from vessel1.json, with the tonne->SI
x1000 unit conversion documented (verified empirically to ~3e-5 on Fr_z)
- TestGolden08Restoring.cpp: restoring (Fr_z, Mr_y) + heave damping (Fd_z)
- Downsampled golden fixtures under tests/fixtures/golden/08_restoring/
- ymir_golden_tests target wired into tests/CMakeLists.txt
Tolerances are set empirically and the residual causes are documented: the
restoring divergence (up to ~6.5% Fr_z / ~13.5% Mr_y as pitch grows) is the
off-diagonal hydrostatic coupling Ymir omits by design. Findings on squat
(diverges ~4 orders — model gap) and the body-frame-flow forces (need TMS
heading-convention mapping) are captured under docs/specs/features/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* build(tests): run C++ suite through the turbo monorepo
Make core/ a pnpm workspace package so `pnpm test` (turbo run test) runs the
C++ ctest suite alongside the JS vitest suites — one command for everything.
- Add "core" to pnpm-workspace.yaml so turbo discovers @ymir/core
- core/package.json: add a `build` script (cmake configure + compile)
- core/turbo.json: override `test` to dependsOn ["build"] instead of the root's
build:wasm, so native ctest does not require Emscripten; cache off (native)
- scripts/run-all-tests.sh: standalone runner (cpp/js/e2e flags) for running
the C++ suite without touching the wasm/emcc toolchain
@ymir/core#build -> @ymir/core#test: 270/270 tests pass via turbo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(physics): add wind golden validation + reconstruct flow convention
Extends the cross-engine golden suite to wind drag (scenario 02_wind) and adds
the earth->body flow conversion needed by flow-dependent forces.
- GoldenFrame.h: nautToBodyFrame mirroring NavalDomain — rotates the scenario
environment (speed + nautical direction) into the body frame using the golden
heading, so wind/current vectors can be reconstructed from controlDict + the
golden trajectory
- Vessel1Params.h: vessel1Wind() config with the full Cd(angle) table
- TestGolden02Wind.cpp: Fwd_x validated to ~4% across the trajectory (residual
is the rho_air constant: Ymir 1.225 vs reference ~1.27); Fwd_y/Mwd_z ~0 bow-on
- Fixtures for 02_wind
Documents the remaining divergences with measured numbers: current OBOKATA
disagrees in sign and magnitude (Fc_x -83kN vs golden +172kN — sectional-area
approximation + sign convention), squat off by ~4 orders. See
docs/specs/features/golden-validation/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(graphify): refresh knowledge graph after golden validation work
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(physics): align rho_air, squat sinkage, and damping norm to reference
Cross-engine alignment against the TMS Dynamics 3.0 golden dataset (see
docs/planning/force-model-alignment/). Verified against the reference engine
sources in dynamics/nashville/src/*Forces/.
- rho_air 1.225 -> 1.275 (PhysicalConstants.h): matches PhysicalProps.cpp of the
reference and closes the ~4% wind gap. Wind golden test tightened to <3%.
- SquatForces: fix double division by (rho*g) — the sinkage numerator is now
volumetricWeight/(rho*g*L^2) (single division, matching the reference), not
nabla_/(rho*g*L^2). Also use heave relative to equilibrium (z + draft) for the
depth/seabed clamp, since the reference q[2] is waterline-referenced. Squat
goes from ~4 orders of magnitude off to the correct order (partial: <=5% still
blocked by the artificial 07 scenario depth dynamics).
- DampingForces: the exponential-decay velocity norm now uses all 6 DOF (matching
dampingForces/DampingForces.cpp); the surge/sway sign was already correct given
Ymir's speedToWater convention.
Restoring needs no change: the reference restoring is also diagonal-only, so the
residual is a zero-crossing artifact, not a missing off-diagonal term.
Full C++ suite: 271/271 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(physics): add force-model-alignment spec/design/tasks + findings
TLC spec-driven planning for aligning all force models to the golden dataset:
- spec.md: 11 requirements (FMA-01..11), tolerances, decisions
- design.md: reference formula per model cross-checked with Ymir; root causes
- tasks.md: atomic tasks + execution status of the first pass
Records the key research corrections: restoring reference is diagonal (no
off-diagonal), squat is a double-division bug, current alignment conflicts with
self-propelled physics (reverted, needs speedToWater-convention resolution).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(graphify): refresh graph after force-model alignment
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan to move physics from the browser (WASM Web Worker) to the server.
Client becomes a thin remote terminal; single engine; foundation for
persistence. Includes 6 ADRs and a 9-task breakdown.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1 of the Figma "[INSTRUTOR]" port plus hardening found during testing.
Design system
- theme/tokens.ts + global.css: Figma palette, type scale, spacing, radii,
shadows; injected as CSS vars at boot.
- ui/: 11 themed primitives (Button, IconButton, TopBar, Panel, Tabs, Card,
Slider, Field, Modal/AlertDialog, SimulationControl, MapActions), each tested.
App shell (Layout do instrutor)
- features/shell/AppShell.tsx: TopBar (logo + breadcrumb, gated center actions,
Sair/Salvar), full-bleed map, bottom-center Build/Play/Stop pill, bottom-right
zoom, no-ownship AlertDialog. Replaces ScenarioCreatorPage.
- stores/mapStore.ts exposes the Leaflet instance so the zoom pill can drive it;
AreaMapView registers it and drops Leaflet's default zoom control.
Physics truth
- Worker reports which engine loaded (real WASM vs JS mock); surfaced as a
status badge/warning so the instructor never mistakes the mock for real
dynamics. SimulationControls is now a status-only strip (actions moved to the
pill).
Tests
- 229 passing, coverage gate green. New: tokens, all 11 primitives, AppShell,
VesselPanel, vesselPanelStore. Fixed GeralTab to read draft by instanceId.
Docs
- docs/specs/features/instructor-ui/: extracted screen specs, reference maps,
phased PLAN.md, and DYNAMICS-GAP.md (MATLAB↔Ymir physics parity).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full spec (7 requirements, P1–P3) and architecture design for the
instructor scenario creation flow: area selection, map view, vessel
placement with drag, heading input, and simulation start/stop.
Key decisions: Leaflet + OSM tiles, equirectangular projection for
lat/lng↔meters, separate useScenarioStore from useSimulationStore,
POST /scenarios before starting the worker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 6 supplementary SQLite tables (physical_props, current, connection_points,
rudders, thrusters, waves) to hold all hydrodynamic data for the VLCC
VLCC_L350_B63_T230 (L=350m, B=63m, T=23m).
- Auto-seed at startup: reads vessel1.json into all tables when DB is empty
- GET /vessels/:id/config endpoint returns full physics config for simulation
- Refactored vessels route to use service + repository layer pattern
- Expanded @ymir/types with VesselConfigDTO and all sub-component schemas
- Simulation worker fetches vessel config on init, passes to WASM loadConfig()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move files from flat core/naval/adapters layout into libs/{common,physics,simulation,vessel,world,persistence} with updated namespaces, CMakeLists, tests, README, AGENTS.md, and Doxyfile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CMake project structure with ymir_core, ymir_naval, and ymir_adapters
static library targets. Catch2 and nlohmann/json via FetchContent;
SUNDIALS and CGAL probed as optional system deps (required from M1/M2).
Smoke tests pass on macOS ARM64. Docker multi-stage image for Linux
x86_64. GitHub Actions CI matrix: Linux x86_64, macOS ARM64, Windows
x86_64. Code quality tooling: .clang-format, .clang-tidy, Doxyfile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>