87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
package check
|
|
|
|
import (
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"salty/terrain/internal/fluvial"
|
|
"salty/terrain/internal/manifest"
|
|
"salty/terrain/internal/thermal"
|
|
"salty/terrain/internal/uplift"
|
|
)
|
|
|
|
// TestTalusHoldsInThePipeline is the gap between a unit test and a system. thermal.Apply demonstrably cuts a
|
|
// cone to the angle of repose, and yet a full run with the angle set to 22 degrees reports a median land slope
|
|
// of 37. One of those two things is wrong and the difference matters: the angle of repose is the only direct
|
|
// lever on how steep hill country looks.
|
|
func TestTalusHoldsInThePipeline(t *testing.T) {
|
|
const (
|
|
size = 300
|
|
deg = 22.0
|
|
steps = 300
|
|
)
|
|
passesUnderTest := 3
|
|
if v := os.Getenv("TALUS_PASSES"); v != "" {
|
|
passesUnderTest, _ = strconv.Atoi(v)
|
|
}
|
|
m := manifest.Defaults()
|
|
m.Source.Seed = 7
|
|
cellM := m.SideM() / float64(size-1)
|
|
up := uplift.Build(size, cellM, m)
|
|
h := up.Height.Clone()
|
|
|
|
g := fluvial.NewGrid(size, size, cellM, up.Base)
|
|
g.SetElevationRange(-2000, 4000)
|
|
talus := thermal.TalusFromDegrees(deg)
|
|
g.Run(h.Data, up.Rate.Data, up.K.Data, fluvial.Params{
|
|
K: m.Pipeline.Fluvial.K, M: 0.5, N: 1, DtYr: 1500, Steps: steps,
|
|
Diffusion: 0.02, FillEvery: 1,
|
|
TalusSlope: talus, ThermalEvery: 1, ThermalPasses: passesUnderTest,
|
|
}, nil)
|
|
|
|
if os.Getenv("CLAMP_AFTER") != "" {
|
|
t.Logf("removed %.2f m mean by a final clamp", g.ClampToRepose(h.Data, talus))
|
|
}
|
|
|
|
// The steepest neighbour drop anywhere on land, in the same units the angle of repose is written in.
|
|
worst, count := 0.0, 0
|
|
wx, wy := 0, 0
|
|
worstOther := float32(0)
|
|
worstOtherBase := false
|
|
for y := 1; y < size-1; y++ {
|
|
for x := 1; x < size-1; x++ {
|
|
i := y*size + x
|
|
if up.Base[i] {
|
|
continue
|
|
}
|
|
for _, d := range [][2]int{{1, 0}, {0, 1}, {1, 1}, {1, -1}} {
|
|
ni := (y+d[1])*size + (x + d[0])
|
|
if up.Base[ni] {
|
|
continue
|
|
}
|
|
dist := cellM
|
|
if d[0] != 0 && d[1] != 0 {
|
|
dist = cellM * math.Sqrt2
|
|
}
|
|
if s := math.Abs(float64(h.Data[i]-h.Data[ni])) / dist; s > worst {
|
|
worst = s
|
|
wx, wy = x, y
|
|
worstOther = h.Data[ni]
|
|
worstOtherBase = up.Base[ni]
|
|
}
|
|
}
|
|
if count++; false {
|
|
_ = count
|
|
}
|
|
}
|
|
}
|
|
t.Logf("worst pair at (%d,%d): h=%.1f vs h=%.1f, base=%v/%v", wx, wy, h.Data[wy*size+wx], worstOther, up.Base[wy*size+wx], worstOtherBase)
|
|
gotDeg := math.Atan(worst) * 180 / math.Pi
|
|
t.Logf("steepest land slope after %d steps: %.1f deg (repose %.1f, cell %.0f m)", steps, gotDeg, deg, cellM)
|
|
if gotDeg > deg+5 {
|
|
t.Errorf("steepest land slope is %.1f deg with the angle of repose at %.1f: landsliding is not binding", gotDeg, deg)
|
|
}
|
|
}
|