feat(simulation): lakes from settled hydrology (D-227, T-1184)
Productionizes the T-1177 equilibrium solver: run_layer1 now solves hydrology once per body (~24ms, mirrors drainage::analyze) and carries it as TerrainAnalysis.hydrology; run_layer1_with_moisture threads the real body moisture ceiling (extracted derive_moisture_ceiling_q), with the T-1177 population-survey default as fallback. The resident rung-0 global tier does not exist yet (T-1181's scope) — hydrology rides TerrainAnalysis and lands in that tier for free when it is built (deviation recorded on the ticket). MorphologyZone::Lake is now sourced from the settled solver at derive time: a gridunit is Lake when bilinear-sampled filled surface exceeds bilinear-sampled original elevation at the sample's own (px, py) — the continuous comparison, so lake edges refine with rung like coastlines; never a discrete basin-cell projection. The gate sits strictly between OpenOcean (>= 80) and the old ocean_fraction heuristic (>= 60), which survives as the derive-fresh fallback when no solve is attached — byte-identical to pre-T-1184 output in that case. Static classification, distinct from the sim-state flooded plane; no endorheic bit (the drains-vs-closed cue is T-1185's outlet-course presence, per the D-227 amendment (4) sequencing). Zero new wire bytes. Acceptance: lake_classification_cache_hit_equals_cache_miss (solve twice independently, byte-identical zones, non-vacuous Lake hit) plus hydrology determinism tests. Golden fidelity: the window golden fixture now builds TerrainAnalysis through the production entry point (run_layer1_with_moisture, per-body), and a dedicated lake_bowl golden body pins the hydrology-sourced Lake path (morphology 1 at ocean_fraction_q 0 — provably not the heuristic); the 108 pre-existing golden rows are byte-identical (pure append). believability.json moved by one lake-shaped line (GJ338Bd voxel_relief_m 27->28, a correctly reclassified lake district leaving the dry-relief sample set). river_course and derivation-harness goldens unchanged. Full suite: 2114 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1554,9 +1554,23 @@ mod tests {
|
||||
);
|
||||
|
||||
// Ground truth: derive_at_metres at the SAME settlement world metres,
|
||||
// via the terrain cache's own re-derive path (run_layer1) so the
|
||||
// TerrainAnalysis is byte-identical to what the resolver used.
|
||||
let (_l1, ta) = crate::atlas::layer1::run_layer1(&heightmap);
|
||||
// via the terrain cache's own re-derive path so the TerrainAnalysis is
|
||||
// byte-identical to what the resolver used — INCLUDING which hydrology
|
||||
// moisture ceiling gets solved with. `resolve_settlement_morphology_zone`
|
||||
// threads `Some(&body_params)` into `get_or_derive` (T-1184), which
|
||||
// derives the body's real moisture ceiling rather than falling back to
|
||||
// `run_layer1`'s body-agnostic default; this ground truth must use the
|
||||
// SAME `run_layer1_with_moisture` path (not bare `run_layer1`) or the
|
||||
// two `TerrainAnalysis`es solve hydrology at different moisture inputs
|
||||
// — moot for this fixture's LAKE EXTENT (moisture-independent, see
|
||||
// `run_layer1_with_moisture_changes_endorheic_split_not_lake_extent`),
|
||||
// but the two paths must agree by construction, not by coincidence of
|
||||
// this specific body having no moisture-sensitive basin near the
|
||||
// sampled position.
|
||||
let (_l1, ta) = crate::atlas::layer1::run_layer1_with_moisture(
|
||||
&heightmap,
|
||||
district_profile::derive_moisture_ceiling_q(&body_params),
|
||||
);
|
||||
let expected = district_profile::derive_at_metres(
|
||||
body_seed,
|
||||
"TestBody",
|
||||
|
||||
Reference in New Issue
Block a user