import unittest
import numpy as np
from mapgen import fields as FI
from mapgen import ice as IC
from tests.helpers import make_ctx
class IceTest(unittest.TestCase):
def test_sheet_glacier_sea_ice(self):
ctx = make_ctx(3)
g = ctx.grid
polar_land = g.lat > 70
z = np.where(polar_land, 500.0, -3000.0)
peak = g.cell_index(0.0, 0.0)
z[peak] = 6000.0
t_mean = np.where(g.lat > 60, -25.0, 15.0)
t_mean[peak] = -9.0 # too warm for a sheet, summer below zero → glacier
t_sum = t_mean + 8
t_win = t_mean - 8
ctx.data.update({"elevation_eroded_m": z, "T_mean": t_mean, "T_jun": t_sum, "T_dec": t_win,
"P_ann": np.full(g.n, 300.0)})
out = IC.run(ctx)
self.assertEqual(out["ice"][g.cell_index(85.0, 0.0)], IC.ICE_SHEET)
self.assertEqual(out["ice"][peak], IC.ICE_GLACIER)
self.assertEqual(out["ice"][g.cell_index(65.0, 0.0)], IC.ICE_SEA_PERENNIAL)
self.assertEqual(out["ice"][g.cell_index(0.0, 150.0)], IC.ICE_NONE)
i = g.cell_index(85.0, 0.0)
self.assertGreater(out["z_surface_m"][i], z[i] + 200)
class FieldsTest(unittest.TestCase):
def test_o2_and_gravity(self):
ctx = make_ctx(1)
n = ctx.grid.n
zs = np.zeros(n)
zs[1] = 8000.0
m_o2 = np.zeros(n)
m_o2[2] = 1.0
m_g = np.zeros(n)
m_g[3] = -1.0
ctx.data.update({"z_surface_m": zs, "m_o2_zones": m_o2, "m_gravity_zones": m_g})
out = FI.run(ctx)
self.assertAlmostEqual(out["po2_bar"][0], 0.21)
self.assertAlmostEqual(out["po2_bar"][1], 0.21 * np.exp(-1), places=6)
self.assertAlmostEqual(out["po2_bar"][2], 0.21 * 1.5)
self.assertAlmostEqual(out["gravity_g"][0], 1.05)
self.assertAlmostEqual(out["gravity_g"][3], 1.05 * 0.3)
def test_pressure_o2_fraction_and_fire(self):
ctx = make_ctx(1)
n = ctx.grid.n
zs = np.zeros(n)
zs[1] = 8000.0
m_o2 = np.zeros(n)
m_o2[2] = 1.0
ctx.data.update({"z_surface_m": zs, "m_o2_zones": m_o2, "m_gravity_zones": np.zeros(n)})
out = FI.run(ctx)
self.assertAlmostEqual(out["pressure_bar"][0], 1.0)
self.assertAlmostEqual(out["pressure_bar"][1], np.exp(-1))
self.assertAlmostEqual(out["o2_fraction"][2], 0.21 * 1.5)
np.testing.assert_allclose(out["po2_bar"], out["o2_fraction"] * out["pressure_bar"])
self.assertTrue(np.all(out["fire_reactivity"] == 1.0))
np.testing.assert_allclose(out["plant_height_x"], ctx.cfg["planet"]["gravity_g"] / out["gravity_g"])
np.testing.assert_allclose(FI.pressure(2.0, np.array([-100.0, 8000.0]), 8000.0), [2.0, 2.0 * np.exp(-1)])
class IceOceanMaskTest(unittest.TestCase):
def test_cold_inland_basin_is_not_sea_ice(self):
ctx = make_ctx(3)
g = ctx.grid
land = g.lat > 50
z = np.where(land, 300.0, -3000.0)
basin = (g.lat > 60) & (g.lat < 68) & (np.abs(g.lon) < 20)
z[basin] = -20.0
t = np.where(g.lat > 50, -5.0, 10.0)
ctx.data.update({"elevation_eroded_m": z, "ocean": ~land, "T_mean": t, "T_jun": t + 8, "T_dec": t - 8,
"P_ann": np.full(g.n, 300.0)})
out = IC.run(ctx)
self.assertFalse(np.isin(out["ice"][basin], [IC.ICE_SEA_SEASONAL, IC.ICE_SEA_PERENNIAL]).any())
class SummerMeltRuleTest(unittest.TestCase):
def test_ice_sheets_need_summers_below_freezing(self):
ctx = make_ctx(3)
g = ctx.grid
land = g.lat > 55
z = np.where(land, 300.0, -3000.0)
t_mean = np.where(g.lat > 75, -25.0, -12.0) # 55–75°: cold on average, but summers melt
t_sum = np.where(g.lat > 75, -8.0, 4.0)
peak = g.cell_index(0.0, 0.0)
z[peak], t_mean[peak], t_sum[peak] = 6000.0, -9.0, -1.0
ctx.data.update({"elevation_eroded_m": z, "ocean": ~(land | (np.arange(g.n) == peak)), "T_mean": t_mean,
"T_jun": t_sum, "T_dec": t_mean - 10, "P_ann": np.full(g.n, 300.0)})
out = IC.run(ctx)
self.assertEqual(out["ice"][g.cell_index(65.0, 0.0)], IC.ICE_NONE) # tundra, not ice
self.assertEqual(out["ice"][g.cell_index(85.0, 0.0)], IC.ICE_SHEET)
self.assertEqual(out["ice"][peak], IC.ICE_GLACIER) # small cold spot = glacier