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
166
167
168
169
170
171
172
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
|