Source code for pycif.plugins.transforms.complex.diagmet.utils.checkcfl
import numpy as np
from ......utils.geometry.dist_matrix import dist_matrix
[docs]
def checkcfl(transf, inout_datastore, ddi, mapper):
"""Check and enforce CFL stability: limit wind speeds so that the Courant number stays below 1.
Sets the number of CHIMERE physical time steps per hour (``nphourm``) so
that the horizontal Courant number stays below 0.5 everywhere in the
domain, given the grid-cell sizes (great-circle distance between corners)
and the strongest horizontal winds. The vertical CFL contribution is not
implemented (no explicit vertical wind is available from ECMWF). See
:doc:`/documentation/doc-models/chimere/diagmet` (section 8) for the full
derivation.
Args:
transf (Plugin): diagmet transform instance.
inout_datastore (dict): mutable datastore.
ddi (datetime): current sub-simulation date.
mapper (dict): transform mapper.
"""
# Compute size of grid cells
rearth = 6371.03e3
domain = mapper["inputs"][("meteo", "winz")]["domain"]
zlon = domain.zlon
zlat = domain.zlat
zlonc = domain.zlonc
zlatc = domain.zlatc
# Size in meridional direction
radlat = np.radians(0.5 * (zlatc[:, 1:] + zlatc[:, :-1]))
dradlon = np.diff(np.radians(0.5 * (zlonc[:, 1:] + zlonc[:, :-1])), axis=0)
ysize = rearth * 2 * np.arcsin(np.sqrt(
0.5 * (1 - np.sin(radlat[1:]) * np.sin(radlat[:-1])
- np.cos(radlat[1:]) * np.cos(radlat[:-1]) * np.cos(dradlon)
)
))
# Size in meridional direction
radlat = np.radians(0.5 * (zlatc[1:] + zlatc[:-1]))
dradlon = np.diff(np.radians(0.5 * (zlonc[1:] + zlonc[:-1])), axis=1)
xsize = rearth * 2 * np.arcsin(np.sqrt(
0.5 * (1 - np.sin(radlat[:, 1:]) * np.sin(radlat[:, :-1])
- np.cos(radlat[:, 1:]) * np.cos(radlat[:, :-1]) * np.cos(
dradlon)
)
))
# estimation of time step using CFL criteria
# conversion from entrainment flux to vertical speed
# = *1.3 for air density in kg/m3
cflmax = 0.5
# Variables from other parameters
alti = transf.diag_misc["alti"]
winm = inout_datastore["outputs"][("meteo", "winm")][ddi]["spec"]
winz = inout_datastore["outputs"][("meteo", "winz")][ddi]["spec"]
# missing: winw = inout_datastore["outputs"][("meteo", "winw")][ddi]["spec"]
# but where does it come from when DP is read?
layerdepth = np.diff(alti, axis=1)
dxoveru = cflmax * np.abs(xsize / winz).min(axis=(1, 2, 3))
dxoverv = cflmax * np.abs(ysize / winm).min(axis=(1, 2, 3))
# missing: dxoverw = cflmax * np.abs(layerdepth / winw).min(axis=(1, 2, 3))
nphourm = (3600. / np.minimum(dxoveru, dxoverv)).round() # + dxoverw
inout_datastore["outputs"][("meteo", "nphourm")][ddi]["spec"] = nphourm