raw · 5378 bytes
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 | import unittest import numpy as np from mapgen import climate as CL from mapgen.sphere import east_north from tests.helpers import make_ctx, small_grid class WindTest(unittest.TestCase): def test_bands(self): g = small_grid(3) w = CL.band_winds(g, 0.0, CL.DEFAULTS) e, n = east_north(g.xyz) u = np.sum(w * e, axis=1) v = np.sum(w * n, axis=1) self.assertLess(u[np.abs(g.lat - 10) < 2].mean(), -3) # trades from the east self.assertGreater(u[np.abs(g.lat - 40) < 2].mean(), 3) # westerlies self.assertLess(v[np.abs(g.lat - 10) < 2].mean(), 0) # NH trades flow equatorward self.assertGreater(v[np.abs(g.lat + 10) < 2].mean(), 0) # SH trades flow equatorward def test_monsoon_reverses_onshore_flow(self): g = small_grid(3) land = (g.lat > 10) & (g.lat < 40) & (np.abs(g.lon) < 50) z = np.where(land, 200.0, -4000.0) T, _ = CL.temperatures(g, z, land, CL.DEFAULTS, 20.0) _, n = east_north(g.xyz) south = (g.lat > 0) & (g.lat < 8) & (np.abs(g.lon) < 40) vj = np.sum(CL.monsoon_winds(g, T["jun"], land, CL.DEFAULTS) * n, axis=1)[south].mean() vd = np.sum(CL.monsoon_winds(g, T["dec"], land, CL.DEFAULTS) * n, axis=1)[south].mean() self.assertGreater(vj, 0.5) self.assertLess(vd, -0.5) class RainTest(unittest.TestCase): def test_itcz_wetter_than_subtropics_on_aquaplanet(self): g = small_grid(3) z = np.full(g.n, -4000.0) T, _ = CL.temperatures(g, z, z > 0, CL.DEFAULTS, 20.0) w = CL.band_winds(g, 0.0, CL.DEFAULTS) p = CL.precipitation(g, w, T["eq"], z, z > 0, 0.0, CL.DEFAULTS) self.assertGreater(p[np.abs(g.lat) < 5].mean(), 1.5 * p[np.abs(np.abs(g.lat) - 21) < 3].mean()) def test_windward_wetter_than_lee(self): g = small_grid(3) land = (g.lat > 30) & (g.lat < 50) & (np.abs(g.lon) < 60) z = np.where(land, 200.0, -4000.0) z = np.where(land & (np.abs(g.lon) < 5), 3000.0, z) T, _ = CL.temperatures(g, z, land, CL.DEFAULTS, 20.0) w = CL.band_winds(g, 0.0, CL.DEFAULTS) p = CL.precipitation(g, w, T["eq"], z, land, 0.0, CL.DEFAULTS) band = (g.lat > 35) & (g.lat < 45) west = p[band & (g.lon > -12) & (g.lon < -3)].mean() east = p[band & (g.lon > 3) & (g.lon < 12)].mean() self.assertGreater(west, 1.3 * east) def test_stage_outputs(self): ctx = make_ctx(2) g = ctx.grid land = (np.abs(g.lat) < 40) & (np.abs(g.lon) < 50) ctx.data["elevation_eroded_m"] = np.where(land, 300.0, -4000.0).astype(np.float32) out = CL.run(ctx) for k in ("T_jun", "T_dec", "T_eq", "T_mean", "T_range", "T_min", "P_jun", "P_dec", "P_eq", "P_ann", "wind_jun", "biotemp", "PET", "dist_ocean_km"): self.assertIn(k, out) self.assertTrue(np.all(np.isfinite(out[k])), k) mean = np.sum(out["P_ann"] * g.area_km2) / g.area_km2.sum() self.assertAlmostEqual(mean, 1000.0, delta=1.0) self.assertTrue(np.all(out["P_ann"] >= 0)) class StormTrackTest(unittest.TestCase): def test_midlatitudes_wetter_than_subtropics(self): g = small_grid(3) z = np.full(g.n, -4000.0) T, _ = CL.temperatures(g, z, z > 0, CL.DEFAULTS, 20.0) p = CL.precipitation(g, CL.band_winds(g, 0.0, CL.DEFAULTS), T["eq"], z, z > 0, 0.0, CL.DEFAULTS) a = np.abs(g.lat) h = CL.DEFAULTS["hadley_edge_deg"] # storm track ≈ h+12..h+25, dry belt ≈ h−2..h+8 self.assertGreater(p[(a > h + 12) & (a < h + 25)].mean(), 1.2 * p[(a > h - 2) & (a < h + 8)].mean()) class LandMoistureTest(unittest.TestCase): def test_equatorial_land_stays_wet(self): g = small_grid(3) land = (np.abs(g.lat) < 12) & (np.abs(g.lon) < 40) z = np.where(land, 300.0, -4000.0) T, _ = CL.temperatures(g, z, land, CL.DEFAULTS, 20.0) p = CL.precipitation(g, CL.band_winds(g, 0.0, CL.DEFAULTS), T["eq"], z, land, 0.0, CL.DEFAULTS) eq = np.abs(g.lat) < 8 self.assertGreater(p[land & eq].mean(), 0.5 * p[~land & eq].mean()) class OceanMaskUseTest(unittest.TestCase): def test_inland_basin_is_land_for_climate(self): ctx = make_ctx(3) g = ctx.grid land = (np.abs(g.lat) < 40) & (np.abs(g.lon) < 60) z = np.where(land, 300.0, -4000.0) basin = (np.abs(g.lat) < 8) & (np.abs(g.lon) < 8) z[basin] = -30.0 ctx.data.update({"elevation_eroded_m": z.astype(np.float32), "ocean": ~land}) out = CL.run(ctx) self.assertTrue(np.all(out["dist_ocean_km"][basin] > 1000)) class InteriorRainTest(unittest.TestCase): def test_continental_interior_keeps_a_third_of_coastal_rain(self): from mapgen.graph import distance_to g = small_grid(3) land = (g.lat > 10) & (g.lat < 50) & (np.abs(g.lon) < 70) z = np.where(land, 300.0, -4000.0) T, _ = CL.temperatures(g, z, land, CL.DEFAULTS, 20.0) p = CL.precipitation(g, CL.band_winds(g, 0.0, CL.DEFAULTS), T["eq"], z, land, 0.0, CL.DEFAULTS) d = distance_to(g, ~land) coast, interior = p[land & (d < 500)].mean(), p[land & (d > 1500)].mean() self.assertGreater(interior, 0.33 * coast) # was ≈0.25 before the desert retune |