import logging
import numpy as np
import pygetm.core
import pygetm.airsea
from pygetm.constants import FILL_VALUE, CellType
[docs]
class Base:
"""Base class for ice models."""
[docs]
def initialize(self, grid: pygetm.core.Grid, logger: logging.Logger):
self.logger = logger
self.grid = grid
self.ice = grid.array(
name="ice",
long_name="ice cover",
units="1",
fill_value=FILL_VALUE,
attrs=dict(
standard_name="sea_ice_area_fraction", _valid_at=(CellType.BOUNDARY,)
),
fabm_standard_name="ice_area_fraction",
)
self.ice.fill(0.0)
[docs]
class Prescribed(Base):
[docs]
def initialize(self, grid: pygetm.core.Grid, logger: logging.Logger):
super().initialize(grid, logger)
self.has_ice = False
self.ice_free = np.full(grid.H.all_values.shape, 1.0)
def __call__(
self,
macro: bool,
ct_sf: pygetm.core.Array,
sa_sf: pygetm.core.Array,
airsea: pygetm.airsea.Fluxes,
):
if macro:
f = np.where(self.ice.grid._water, self.ice.all_values, 0.0)
self.has_ice = f.any()
# 1st order freezing point approximation based on
# gsw_mod_freezing_poly_coefficients
ct_freezing = 0.017947064327968736 - 0.06076099099929818 * sa_sf.all_values
if self.has_ice:
self.ice_free[...] = 1.0 - f
# Set temperature in ice-covered fraction to freezing temperature
ct_sf.all_values += f * (ct_freezing - ct_sf.all_values)
# Allow outward surface heat flux [cooling] only in ice-free area
cooling = airsea.shf.all_values < 0
airsea.shf.all_values[cooling] *= self.ice_free[cooling]
np.maximum(
ct_sf.all_values,
ct_freezing,
out=ct_sf.all_values,
where=self.ice.grid._water,
)
if self.has_ice:
airsea.taux.all_values *= self.ice_free
airsea.tauy.all_values *= self.ice_free
[docs]
class Ice(Base):
"""Simple ice model that assumes a cell is completely ice covered when its
surface temperature drops below freezing. At that point, surface heat
fluxes in that cell are clipped to positive values (= no further cooling).
Surface momentum fluxes for that same cell are switched off altogether."""
[docs]
def initialize(self, grid: pygetm.core.Grid, logger: logging.Logger):
super().initialize(grid, logger)
self.has_ice = False
self.covered = np.full(grid.H.all_values.shape, False)
def __call__(
self,
macro: bool,
ct_sf: pygetm.core.Array,
sa_sf: pygetm.core.Array,
airsea: pygetm.airsea.Fluxes,
):
if macro:
# 1st order freezing point approximation based on
# gsw_mod_freezing_poly_coefficients
ct_freezing = 0.017947064327968736 - 0.06076099099929818 * sa_sf.all_values
np.less_equal(
ct_sf.all_values,
ct_freezing,
out=self.covered,
where=self.ice.grid._water,
)
self.has_ice = self.covered.any()
np.putmask(self.ice.all_values, self.ice.grid._water, self.covered)
if self.has_ice:
np.putmask(ct_sf.all_values, self.covered, ct_freezing)
cooling = airsea.shf.all_values < 0
airsea.shf.all_values[self.covered & cooling] = 0.0
if self.has_ice:
airsea.taux.all_values[self.covered] = 0.0
airsea.tauy.all_values[self.covered] = 0.0