1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
import unittest
import numpy as np
from mapgen import check as CK
from tests.helpers import small_grid
class CheckTest(unittest.TestCase):
def setUp(self):
self.g = small_grid(2)
def test_land_fraction(self):
a = np.ones(100)
z = np.where(np.arange(100) < 22, 100.0, -100.0)
self.assertIsNone(CK.check_land_fraction(z, a, 0.22))
self.assertIn("land fraction", CK.check_land_fraction(z, a, 0.40))
def test_hypsometry(self):
rng = np.random.default_rng(0)
z = np.concatenate([rng.normal(-4000, 600, 7000), rng.normal(500, 400, 3000)])
self.assertIsNone(CK.check_hypsometry(z, np.ones(len(z))))
self.assertIn("bimodal", CK.check_hypsometry(rng.normal(0, 3000, 10000), np.ones(10000)))
def test_max_elevation(self):
self.assertIsNone(CK.check_max_elevation(np.array([11000.0]), np.array([1.0])))
self.assertIsNone(CK.check_max_elevation(np.array([20000.0]), np.array([0.5])))
self.assertIn("12000", CK.check_max_elevation(np.array([13000.0]), np.array([1.0])))
def test_drainage_and_uphill(self):
recv = np.array([0, 0, 1, 3])
z = np.array([-10.0, 5.0, 8.0, 20.0])
endo = np.array([False, False, False, False])
self.assertIn("sink", CK.check_drainage(recv, z, endo))
endo[3] = True
self.assertIsNone(CK.check_drainage(recv, z, endo))
self.assertIsNone(CK.check_no_uphill(z, recv, z, endo))
self.assertIn("uphill", CK.check_no_uphill(np.array([-10.0, 9.0, 8.0, 20.0]), recv, z, endo))
def test_temperature_and_rain_bands(self):
g = self.g
self.assertIsNone(CK.check_temperature(g, 30 - 0.6 * np.abs(g.lat)))
self.assertIn("poleward", CK.check_temperature(g, 0.3 * np.abs(g.lat)))
wet = 2000 * np.exp(-(g.lat / 8) ** 2) + 900 * np.exp(-((np.abs(g.lat) - 40) / 8) ** 2) + 100
self.assertIsNone(CK.check_rain_bands(g, wet, 20.0))
self.assertIn("subtropic", CK.check_rain_bands(g, np.full(g.n, 800.0), 20.0))
def test_drainage_terminals_must_hold_water_or_salt(self):
recv = np.array([0, 0, 1, 3])
z = np.array([-10.0, 5.0, 8.0, 20.0])
endo = np.array([False, False, False, True])
dry = np.zeros(4, bool)
self.assertIn("lake", CK.check_drainage(recv, z, endo, terminal_ok=dry))
wet = dry.copy()
wet[3] = True
self.assertIsNone(CK.check_drainage(recv, z, endo, terminal_ok=wet))
def test_lake_surface_routing_is_exempt(self):
recv = np.array([0, 0, 1, 2])
zf = np.array([-10.0, 5.0, 5.02, 5.01]) # cell 3 → 2 is ε-uphill across a flat lake
z = np.array([-10.0, 5.0, 4.0, 4.5])
endo = np.zeros(4, bool)
lake = np.array([False, False, True, True])
self.assertIn("uphill", CK.check_no_uphill(zf, recv, z, endo))
self.assertIsNone(CK.check_no_uphill(zf, recv, z, endo, lake=lake))
class RevisionCheckTest(unittest.TestCase):
P1 = {"name": "east-flank", "center": [-39.9, -18.0], "area_km2": 3.0e6, "elongation": 1.9, "azimuth_deg": 30.0,
"top_m": [1500.0, 3000.0]}
P2 = {"name": "plateau-02", "center": [-0.4, 176.6], "area_km2": 1.0e6, "top_m": [1000.0, 1500.0],
"islands": True}
def world(self):
from mapgen import plateaus as PL
g = small_grid(4)
ids = PL.cell_ids(g.xyz, [self.P1, self.P2], 7, g.radius_km)
z = PL.apply(g, np.full(g.n, -6000.0), [self.P1, self.P2], ids, 7)
return g, {"elevation_eroded_m": z, "ocean": z <= 0, "plateau_id": ids}
def test_plateaus_pass_and_fail(self):
g, d = self.world()
self.assertEqual(CK.check_plateaus(g, d, [self.P1, self.P2], 7), [])
z = d["elevation_eroded_m"].copy()
z[np.flatnonzero(d["plateau_id"] == 0)[0]] = -100.0
z[z > 0] = -700.0
bad = CK.check_plateaus(g, {**d, "elevation_eroded_m": z, "ocean": z <= 0}, [self.P1, self.P2], 7)
self.assertTrue(any("east-flank" in m and "−500" in m for m in bad), bad)
self.assertTrue(any("plateau-02" in m and "island" in m for m in bad), bad)
def test_zones_gradual_rule(self):
from mapgen import zones as ZN
g = small_grid(4)
m = ZN.contribution(g.xyz, {"name": "R1", "field": "o2", "center": [0.0, 0.0], "radius_km": 3200.0,
"v": 1.0}, 7, g.radius_km)
smooth = {"o2_fraction": 0.21 * (1 + 0.5 * m), "gravity_g": np.full(g.n, 1.05)}
self.assertEqual(CK.check_zones(g, smooth), [])
step = {"o2_fraction": np.where(g.lon > 0, 0.35, 0.21), "gravity_g": np.full(g.n, 1.05)}
self.assertTrue(any("o2_fraction" in m for m in CK.check_zones(g, step)))
def test_compare_skips_o2_fields_inside_o2_zones(self):
g = small_grid(3)
tect = {"plateau": [], "land_patch": [],
"zone": [{"name": "r", "field": "o2", "center": [0.0, 0.0], "radius_km": 2000.0, "v": 1.0}]}
o2 = CK.o2_areas(g.xyz, tect, g.radius_km)
self.assertTrue(o2[g.cell_index(0.0, 0.0)])
self.assertFalse(o2[g.cell_index(0.0, 90.0)])
ex = CK.edited_areas(g.xyz, tect, g.radius_km)
self.assertFalse(ex.any(), "O₂ zones don't change relief")
old = {"po2_bar": np.zeros(g.n), "T_mean": np.zeros(g.n)}
new = {"po2_bar": np.where(o2, 0.1, 0.0), "T_mean": np.where(o2, 1.0, 0.0)}
lines = CK.compare(old, new, ex, {k: o2 for k in CK.O2_FIELDS})
self.assertIn("po2_bar: 0.0% changed, max 0, p99 0", lines)
self.assertTrue(any(s.startswith("T_mean:") and not s.startswith("T_mean: 0.0%") for s in lines), lines)
def test_compare_command_needs_a_world_cells_file(self):
import importlib.util
import tempfile
from pathlib import Path
from mapgen.testing import built_world
spec = importlib.util.spec_from_file_location("mapgen_cli", Path(CK.__file__).parents[1] / "mapgen.py")
cli = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cli)
old = Path(tempfile.mkdtemp()) / "other.npz"
np.savez(old, a=np.zeros(3))
self.assertEqual(cli.main(["compare", "--res", "2", "--old", str(old)], root=built_world()), 2)
def test_compare_ignores_edited_areas(self):
g = small_grid(3)
tect = {"plateau": [self.P1], "land_patch": [], "zone": []}
ex = CK.edited_areas(g.xyz, tect, g.radius_km)
self.assertTrue(ex[g.cell_index(-39.9, -18.0)])
self.assertFalse(ex[g.cell_index(40.0, 100.0)])
old = {"a": np.zeros(g.n), "b": np.zeros(g.n), "g_ids": g.ids}
new = {"a": np.where(ex, 5.0, 0.0), "b": np.where(ex, 0.0, 1.0), "g_ids": g.ids}
lines = CK.compare(old, new, ex)
self.assertIn("a: 0.0% changed, max 0, p99 0", lines)
self.assertTrue(any(line.startswith("b: 100.0% changed, max 1") for line in lines), lines)
def test_site_report_warns_about_land_nearby(self):
from mapgen import plateaus as PL
g, d = self.world()
land = np.abs(g.lat - (-39.9)) < 2.0
near = land & (np.abs(g.lon - (-18.0)) < 30.0) & (d["plateau_id"] < 0)
data = {**d, "ocean": ~near, "bnd_type": np.zeros(g.n, np.int8)}
msgs = PL.site_report(g, data, [self.P1, self.P2])
self.assertTrue(any("east-flank" in m and "from land" in m for m in msgs), msgs)
def test_compare_command(self):
import importlib.util
import shutil
import tempfile
from pathlib import Path
from mapgen.testing import built_world
spec = importlib.util.spec_from_file_location("mapgen_cli", Path(CK.__file__).parents[1] / "mapgen.py")
cli = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cli)
old = Path(tempfile.mkdtemp()) / "cells.npz"
shutil.copy(built_world() / "out" / "r2" / "cells.npz", old)
self.assertEqual(cli.main(["compare", "--res", "2", "--old", str(old)], root=built_world()), 0)
def test_compare_equal_infinities_are_unchanged(self):
ex = np.zeros(3, bool)
lines = CK.compare({"d": np.array([np.inf, 1.0, 2.0])}, {"d": np.array([np.inf, 1.0, 3.0])}, ex)
self.assertIn("d: 33.3% changed, max 1, p99 0.98", lines)
|