aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_environment.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_environment.py')
-rw-r--r--tests/test_environment.py173
1 files changed, 173 insertions, 0 deletions
diff --git a/tests/test_environment.py b/tests/test_environment.py
new file mode 100644
index 0000000..0f4c5c6
--- /dev/null
+++ b/tests/test_environment.py
@@ -0,0 +1,173 @@
+import unittest
+
+import numpy as np
+
+from mapgen import environment as EN
+from tests.helpers import make_ctx
+
+
+def name(bio, p, tmin):
+ z, _ = EN.holdridge(np.array([bio]), np.array([p]), np.array([tmin]))
+ return EN.HOLDRIDGE_NAMES[z[0]]
+
+
+class HoldridgeTest(unittest.TestCase):
+ def test_count(self):
+ self.assertEqual(len(EN.HOLDRIDGE_NAMES), 38)
+
+ def test_table(self):
+ self.assertEqual(name(26, 3000, 20), "tropical moist forest")
+ self.assertEqual(name(26, 9000, 20), "tropical rain forest")
+ self.assertEqual(name(26, 100, 20), "tropical desert")
+ self.assertEqual(name(0.5, 100, -30), "polar desert")
+ self.assertEqual(name(8, 300, -10), "cool temperate steppe")
+ self.assertEqual(name(15, 1500, -5), "warm temperate moist forest")
+ self.assertEqual(name(15, 1500, 5), "subtropical moist forest")
+ self.assertEqual(name(4, 700, -20), "boreal wet forest")
+ self.assertEqual(name(2, 200, -25), "subpolar moist tundra")
+
+ def test_polar_desert_with_summer_monsoon(self):
+ tag = EN.seasonality(np.array([180.0, 20.0]), np.array([20.0, 180.0]), np.array([80.0, 80.0]),
+ np.array([-30.0, -30.0]), np.array([100.0, 100.0]), EN.DEFAULTS)
+ self.assertEqual(tag[0], EN.SEAS_W) # NH: wet June (summer), dry December
+ self.assertEqual(tag[1], EN.SEAS_S) # NH: dry summer
+ sh = EN.seasonality(np.array([20.0]), np.array([180.0]), np.array([-80.0]), np.array([-30.0]),
+ np.array([100.0]), EN.DEFAULTS)
+ self.assertEqual(sh[0], EN.SEAS_W) # SH summer is December
+
+ def test_tropical_monsoon(self):
+ tag = EN.seasonality(np.array([3000.0]), np.array([200.0]), np.array([15.0]), np.array([20.0]),
+ np.array([1800.0]), EN.DEFAULTS)
+ self.assertEqual(tag[0], EN.SEAS_M)
+
+
+class StageTest(unittest.TestCase):
+ def test_run(self):
+ ctx = make_ctx(3)
+ g = ctx.grid
+ n = g.n
+ land = (np.abs(g.lat) < 40) & (np.abs(g.lon) < 60)
+ z = np.where(land, 300.0, -4000.0)
+ z = np.where(land & (np.abs(g.lon) < 4), 4000.0, z)
+ zeros = np.zeros(n)
+ ctx.data.update({
+ "elevation_eroded_m": z, "continental": land, "age_class": np.where(land, 1, 0).astype(np.int8),
+ "d_over_km": np.full(n, np.inf), "T_mean": np.where(np.abs(g.lat) > 30, -5.0, 20.0),
+ "T_min": np.full(n, -10.0), "T_range": np.full(n, 20.0), "P_ann": np.full(n, 800.0),
+ "P_jun": np.full(n, 800.0), "P_dec": np.full(n, 800.0), "biotemp": np.full(n, 10.0),
+ "dist_ocean_km": np.where(land, 1000.0, 0.0), "river": np.zeros(n, bool),
+ "strahler": np.zeros(n, np.int8), "discharge_km3_yr": zeros, "lake": np.zeros(n, bool),
+ "salt_flat": np.zeros(n, bool)})
+ out = EN.run(ctx)
+ self.assertEqual(out["landform"][g.cell_index(0.0, 0.0)], EN.LF_MOUNTAINS)
+ self.assertEqual(out["landform"][g.cell_index(0.0, 150.0)], EN.LF_OCEAN)
+ self.assertEqual(out["ground"][g.cell_index(35.0, 40.0)], EN.GR_PERMAFROST)
+ for k in ("holdridge", "hold_region", "seasonality", "landform", "relief_m", "lithology", "ground",
+ "coal_potential", "iron_potential"):
+ self.assertEqual(len(out[k]), n)
+
+
+class OceanMaskEnvTest(unittest.TestCase):
+ def test_inland_basin_not_ocean_landform(self):
+ ctx = make_ctx(3)
+ g = ctx.grid
+ n = g.n
+ land = (np.abs(g.lat) < 40) & (np.abs(g.lon) < 60)
+ z = np.where(land, 300.0, -4000.0)
+ basin = (np.abs(g.lat) < 6) & (np.abs(g.lon) < 6)
+ z[basin] = -30.0
+ zeros = np.zeros(n)
+ ctx.data.update({
+ "elevation_eroded_m": z, "ocean": ~land, "continental": land, "age_class": np.where(land, 1, 0).astype(np.int8),
+ "d_over_km": np.full(n, np.inf), "T_mean": np.full(n, 20.0), "T_min": np.full(n, 10.0),
+ "T_range": np.full(n, 10.0), "P_ann": np.full(n, 800.0), "P_jun": np.full(n, 800.0),
+ "P_dec": np.full(n, 800.0), "biotemp": np.full(n, 20.0), "dist_ocean_km": np.where(land, 1000.0, 0.0),
+ "river": np.zeros(n, bool), "strahler": np.zeros(n, np.int8), "discharge_km3_yr": zeros,
+ "lake": np.zeros(n, bool), "salt_flat": np.zeros(n, bool)})
+ out = EN.run(ctx)
+ self.assertFalse(np.any(out["landform"][basin] == EN.LF_OCEAN))
+
+
+class ReliefResolutionTest(unittest.TestCase):
+ def test_relief_window_is_in_km(self):
+ from tests.helpers import small_grid
+ med = []
+ for res in (2, 3):
+ g = small_grid(res)
+ z = 1000.0 * np.sin(10.0 * g.xyz[:, 0]) + 800.0 * np.cos(9.0 * g.xyz[:, 1])
+ _, relief = EN.landform(g, z, np.ones(g.n, np.int8), np.ones(g.n, np.int8), np.full(g.n, np.inf),
+ np.full(g.n, 800.0), np.zeros(g.n, bool), relief_km=700.0)
+ med.append(np.median(relief))
+ self.assertAlmostEqual(med[0] / med[1], 1.0, delta=0.3)
+
+
+class WetlandRuleTest(unittest.TestCase):
+ def test_wetlands_need_real_rivers_on_real_flats(self):
+ from types import SimpleNamespace
+ from mapgen import graph as G
+ g = small_grid_env()
+ n = g.n
+ z = np.where(g.lat > -30, 100.0 + 0.0001 * g.lat, -3000.0) # a very flat humid plain
+ common = dict(lit=np.full(n, EN.LI_GRANITE, np.int8), t_mean=np.full(n, 15.0), t_min=np.full(n, 5.0),
+ p_ann=np.full(n, 1200.0), dist_ocean=np.full(n, 2000.0), river=np.zeros(n, bool),
+ strahler=np.zeros(n, np.int8), lake=np.zeros(n, bool), salt_flat=np.zeros(n, bool))
+ def wet(q):
+ gr = EN.ground(g, z, common["lit"], common["t_mean"], common["t_min"], common["p_ann"], common["dist_ocean"],
+ common["river"], common["strahler"], np.full(n, q), common["lake"], common["salt_flat"],
+ EN.DEFAULTS, g.lat <= -30)
+ return np.mean(gr[g.lat > 0] == EN.GR_WETLAND)
+ self.assertLess(wet(3.0), 0.05) # small streams don't make a wetland
+ self.assertGreater(wet(8.0), 0.9) # a big river across a flat does
+
+
+def small_grid_env():
+ from tests.helpers import small_grid
+ return small_grid(3)
+
+
+class DepositsTest(unittest.TestCase):
+ def test_shares_rules_and_main(self):
+ from mapgen import minerals as MN
+ from mapgen import environment as EN
+ from mapgen.crust import CRATON, POST_OROGEN
+ g = make_ctx(4).grid
+ n = g.n
+ land = np.abs(g.lat) < 60
+ mountain = land & (g.lon > 0) & (g.lon < 60)
+ f = {"land": land, "z": np.where(mountain, 3500.0, 200.0), "lit": np.where(mountain, EN.LI_METAMORPHIC, EN.LI_SANDSTONE).astype(np.int8),
+ "age": np.where(mountain, POST_OROGEN, CRATON), "lf": np.where(mountain, EN.LF_MOUNTAINS, EN.LF_PLAIN),
+ "relief": np.where(mountain, 2500.0, 50.0), "t_mean": np.full(n, 15.0), "p_ann": np.full(n, 900.0),
+ "d_coll": np.full(n, np.inf), "salt": np.zeros(n, bool), "endo": np.zeros(n, bool),
+ "dist_ocean": np.full(n, 500.0), "coal": land & ~mountain}
+ bits, main = MN.place(g, f, 7)
+ self.assertEqual(len(MN.DEPOSITS), 32)
+ self.assertFalse(bits[~land].any())
+ for name in ("ironstone", "oil_gas", "iron_high", "tungsten"):
+ has = (bits & np.uint32(MN.BIT[name])) > 0
+ share = dict((d[0], d[2]) for d in MN.DEPOSITS)[name]
+ self.assertLessEqual(has.sum(), round(share * land.sum()) + 1, name)
+ self.assertTrue(has.any(), name)
+ for name in MN.DEEP[:2]: # the good stuff: in the mountains only here
+ has = (bits & np.uint32(MN.BIT[name])) > 0
+ self.assertTrue(np.all(mountain[has]), name)
+ some = bits > 0
+ self.assertTrue(np.all(main[some] > 0) and np.all(main[~some] == 0))
+ k = np.flatnonzero(some)[0]
+ self.assertTrue(bits[k] & np.uint32(1 << (int(main[k]) - 1)))
+
+ def test_deposits_are_sprinkled_not_one_hotspot(self):
+ from mapgen import minerals as MN
+ g = make_ctx(4).grid
+ land = np.abs(g.lat) < 60
+ hot = land & (g.lat > 0) & (g.lat < 30) & (g.lon > 0) & (g.lon < 40) # a world-class district
+ score = np.where(hot, 3.0, np.where(land, 1.0, 0.0))
+ prov = MN.provinces(g, 7)
+ sel = MN._rank_select(score, 0.02, land, prov)
+ self.assertEqual(sel.sum(), round(0.02 * land.sum()))
+ self.assertFalse(sel[~land].any())
+ self.assertGreater((sel & hot).sum(), 0.3 * sel.sum()) # the district keeps the most
+ self.assertGreater((sel & ~hot).sum(), 0.3 * sel.sum()) # the rest is sprinkled
+ self.assertGreater(len(np.unique(prov[sel & ~hot])), 0.3 * len(np.unique(prov[land & ~hot])))
+ self.assertFalse(MN._rank_select(np.where(hot, 1.0, 0.0), 0.02, land, prov)[~hot].any()) # rules still rule
+ poor = MN._rank_select(np.where(hot, 10.0, np.where(land, 1.0, 0.0)), 0.02, land, prov)
+ self.assertFalse(poor[~hot].any()) # far poorer ground: no local mines