A collection of diagnostic and interpolation routines for use with output from the Weather Research and Forecasting (WRF-ARW) Model.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

869 lines
38 KiB

from __future__ import (absolute_import, division, print_function)
import numpy as np
import numpy.ma as ma
from .extension import (_interpz3d, _vertcross, _interpline, _smooth2d,
_monotonic, _vintrp, _interpz3d_lev2d)
from .metadecorators import set_interp_metadata
from .util import (extract_vars, is_staggered, get_id, to_np, get_iterable,
is_moving_domain, is_latlon_pair)
from .py3compat import py3range
from .interputils import get_xy, get_xy_z_params, to_xy_coords
from .constants import Constants, default_fill, ConversionFactors
from wrf.g_terrain import get_terrain
from wrf.g_geoht import get_height
from wrf.g_temp import get_theta, get_temp, get_eth
from wrf.g_pressure import get_pressure
# Note: Extension decorator is good enough to handle left dims
@set_interp_metadata("horiz")
def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64),
squeeze=True, meta=True):
"""Return the three-dimensional field interpolated to a horizontal plane
at the specified vertical level.
Args:
field3d (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
three-dimensional field to interpolate, with the rightmost
dimensions of nz x ny x nx.
vert (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
three-dimensional array for the vertical coordinate, typically
pressure or height. This array must have the same dimensionality
as *field3d*.
desiredlev (:obj:`float`, 1D sequence, or :class:`numpy.ndarray`): The
desired vertical level(s). This can be a single value (e.g. 500),
a sequence of values (e.g. [1000, 850, 700, 500, 250]), or a
multidimensional array where the right two dimensions (ny x nx)
must match *field3d*, and any leftmost dimensions match
field3d.shape[:-3] (e.g. planetary boundary layer).
Must be in the same units as the *vert* parameter.
missing (:obj:`float`): The fill value to use for the output.
Default is :data:`wrf.default_fill(numpy.float64)`.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape
of the output. Default is True.
meta (:obj:`bool`): Set to False to disable metadata and return
:class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True.
Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`: The
interpolated variable. If xarray is enabled and
the *meta* parameter is True, then the result will be an
:class:`xarray.DataArray` object. Otherwise, the result will
be a :class:`numpy.ndarray` object with no metadata.
Example:
Interpolate Geopotential Height to 500 hPa
.. code-block:: python
from netCDF4 import Dataset
from wrf import getvar, interplevel
wrfin = Dataset("wrfout_d02_2010-06-13_21:00:00")
p = getvar(wrfin, "pressure")
ht = getvar(wrfin, "z", units="dm")
ht_500 = interplevel(ht, p, 500.0)
Interpolate Relative Humidity to Boundary Layer Heights
.. code-block:: python
from netCDF4 import Dataset
from wrf import getvar, interplevel
wrfin = Dataset("wrfout_d02_2010-06-13_21:00:00")
rh = getvar(wrfin, "rh")
z = getvar(wrfin, "z")
pblh = getvar(wrfin, "PBLH")
rh_pblh = interplevel(rh, p, pblh)
"""
_desiredlev = np.asarray(desiredlev)
if _desiredlev.ndim == 0:
_desiredlev = np.array([desiredlev], np.float64)
levsare2d = False
else:
levsare2d = _desiredlev.ndim >= 2
if not levsare2d:
result = _interpz3d(field3d, vert, _desiredlev, missing)
else:
result = _interpz3d_lev2d(field3d, vert, _desiredlev, missing)
masked = ma.masked_values (result, missing)
if not meta:
if squeeze:
return masked.squeeze()
return masked
@set_interp_metadata("cross")
def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
wrfin=None, timeidx=0, stagger=None, projection=None,
ll_point=None,
pivot_point=None, angle=None,
start_point=None, end_point=None,
latlon=False, autolevels=100, cache=None, meta=True):
"""Return the vertical cross section for a three-dimensional field.
The cross section is defined by a horizontal line through the domain.
This horizontal line is defined by either including the
*pivot_point* and *angle* parameters, or the *start_point* and
*end_point* parameters. The *pivot_point*, *start_point*, and *end_point*
coordinates can be defined in either x,y or latitude,longitude space.
If latitude,longitude coordinates are used, then a WRF input file or
map projection must also be specified.
The vertical levels for the cross section are fixed if *levels* is not
specified, and are determined by dividing the vertical coordinate in to
grid boxes of roughly 1% of the maximum vertical distance from top to
bottom. Otherwise, the *levels* argument can be used to specify specific
vertical levels. If all vertical levels are desired, use the raw
:meth:`wrf.interp2dxy` function.
See Also:
:meth:`wrf.interp2dxy`
Args:
field3d (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
three-dimensional field to interpolate, whose
rightmost dimensions are nz x ny x nx.
vert (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
three-dimensional variable for the vertical coordinate, typically
pressure or height. This array must have the same dimensionality
as *field3d*
levels (sequence, optional): A sequence of :obj:`float` for the desired
vertical levels in the output array. Must be in the same units
as *vert*. If None, a fixed set of vertical levels is provided.
Default is None.
missing (:obj:`float`): The fill value to use for the output.
Default is :data:`wrf.default_fill(numpy.float64)`.
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable, optional): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. This is used
to obtain the map projection when using latitude,longitude
coordinates. Default is None.
timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer.
Only required when *wrfin* is specified and the nest is moving.
Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs
for *start_point*, *end_point*, or *pivot_point*,
set the appropriate grid staggering type for *field3d*. By default,
the mass grid is used. The options are:
- 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension.
projection (:class:`wrf.WrfProj` subclass, optional): The map
projection object to use when working with latitude, longitude
coordinates, and must be specified if *wrfin* is None. Default
is None.
ll_point (:class:`wrf.CoordPair`, sequence of :class:`wrf.CoordPair`, \
optional): The lower left latitude, longitude point for your domain,
and must be specified
if *wrfin* is None. If the domain is a moving nest, this should be
a sequence of :class:`wrf.CoordPair`. Default is None.
pivot_point (:class:`wrf.CoordPair`, optional): A coordinate pair for
the pivot point, which indicates the location through which
the plane will pass. Must also specify *angle*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
angle (:obj:`float`, optional): Only valid for cross sections where
a plane will be plotted through
a given point on the model domain. 0.0 represents a S-N cross
section. 90.0 is a W-E cross section.
start_point (:class:`wrf.CoordPair`, optional): A coordinate pair
which indicates the start location through which the plane will
pass. Must also specify *end_point*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
end_point (:class:`wrf.CoordPair`, optional): A coordinate pair
which indicates the end location through which the plane will
pass. Must also specify *end_point*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
latlon (:obj:`bool`, optional): Set to True to also interpolate the
two-dimensional latitude and longitude coordinates along the same
horizontal line and include this information in the metadata
(if enabled). This can be helpful for plotting. Default is False.
Note:
Currently, *field3d* must be of type :class:`xarray.DataArray`
and contain coordinate information in order to generate the
latitude and longitude coordinates along the line if
*latlon* is set to True. Otherwise, a warning will be issued,
and the latitude and longitude information will not be
present.
autolevels(:obj:`int`, optional): The number of evenly spaced
automatically chosen vertical levels to use when *levels*
is None. Default is 100.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using
large sequences of files.
Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True.
Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`:
The interpolated variable. If xarray is enabled and
the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata.
"""
# Some fields like uvmet have an extra left dimension for the product
# type, we'll handle that iteration here.
multi = True if field3d.ndim - vert.ndim == 1 else False
try:
xy = cache["xy"]
var2dz = cache["var2dz"]
z_var2d = cache["z_var2d"]
except (KeyError, TypeError):
# Convert Lat/Lon points to grid points
start_point_xy = None
end_point_xy = None
pivot_point_xy = None
if (latlon is True or is_latlon_pair(start_point) or
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the line, which causes
# crashes or different line lengths.
if is_moving:
raise ValueError("Requesting all times with a moving nest "
"is not supported when using lat/lon "
"cross sections because the domain could "
"move outside of the cross section. "
"You must request each time "
"individually.")
else:
# Domain not moving, just use 0
_timeidx = 0
# If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0.
else:
_timeidx = 0
else:
if is_moving:
_timeidx = timeidx
else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None:
xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx,
stagger, projection, ll_point)
pivot_point_xy = (xy_coords.x, xy_coords.y)
else:
pivot_point_xy = (pivot_point.x, pivot_point.y)
if start_point is not None and end_point is not None:
if start_point.lat is not None and start_point.lon is not None:
xy_coords = to_xy_coords(start_point, wrfin, _timeidx,
stagger, projection, ll_point)
start_point_xy = (xy_coords.x, xy_coords.y)
else:
start_point_xy = (start_point.x, start_point.y)
if end_point.lat is not None and end_point.lon is not None:
xy_coords = to_xy_coords(end_point, wrfin, _timeidx,
stagger, projection, ll_point)
end_point_xy = (xy_coords.x, xy_coords.y)
else:
end_point_xy = (end_point.x, end_point.y)
xy, var2dz, z_var2d = get_xy_z_params(to_np(vert), pivot_point_xy,
angle, start_point_xy,
end_point_xy, levels,
autolevels)
if not multi:
result = _vertcross(field3d, xy, var2dz, z_var2d, missing)
else:
outshape = field3d.shape[0:-3] + (z_var2d.shape[0], xy.shape[0])
result = np.empty(outshape, dtype=field3d.dtype)
for i in py3range(field3d.shape[0]):
result[i,:] = _vertcross(field3d[i,:], xy, var2dz, z_var2d,
missing)[:]
return ma.masked_values(result, missing)
@set_interp_metadata("line")
def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None,
ll_point=None,
pivot_point=None, angle=None, start_point=None,
end_point=None, latlon=False,
cache=None, meta=True):
"""Return the two-dimensional field interpolated along a line.
This line is defined by either including the
*pivot_point* and *angle* parameters, or the *start_point* and
*end_point* parameters. The *pivot_point*, *start_point*, and *end_point*
coordinates can be defined in either x,y or latitude,longitude space.
If latitude,longitude coordinates are used, then a WRF input file or
map projection must also be specified.
Args:
field2d (:class:`xarray.DataArray` or :class:`numpy.ndarray`):
A two-dimensional field.
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \
iterable, optional): WRF-ARW NetCDF
data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile`
or an iterable sequence of the aforementioned types. This is used
to obtain the map projection when using latitude,longitude
coordinates. Should not be used when working with x,y
coordinates. Default is None.
timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer.
Only required when *wrfin* is specified and the nest is moving.
Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs
for *start_point*, *end_point*, or *pivot_point*,
set the appropriate grid staggering type for *field2d*. By default,
the mass grid is used. The options are:
- 'm': Use the mass grid (default).
- 'u': Use the same staggered grid as the u wind component,
which has a staggered west_east (x) dimension.
- 'v': Use the same staggered grid as the v wind component,
which has a staggered south_north (y) dimension.
projection (:class:`wrf.WrfProj`, optional): The map
projection object to use when working with latitude, longitude
coordinates, and must be specified if *wrfin* is None. Should
not be used when working with x,y coordinates. Default
is None.
ll_point (:class:`wrf.CoordPair`, sequence of :class:`wrf.CoordPair`, \
optional): The lower left latitude, longitude point for your domain,
and must be specified
if *wrfin* is None. If the domain is a moving nest, this should be
a sequence of :class:`wrf.CoordPair`. Default is None.
pivot_point (:class:`wrf.CoordPair`, optional): A coordinate pair for
the pivot point, which indicates the location through which
the plane will pass. Must also specify *angle*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
angle (:obj:`float`, optional): Only valid for cross sections where
a plane will be plotted through
a given point on the model domain. 0.0 represents a S-N cross
section. 90.0 is a W-E cross section.
start_point (:class:`wrf.CoordPair`, optional): A coordinate pair
which indicates the start location through which the plane will
pass. Must also specify *end_point*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
end_point (:class:`wrf.CoordPair`, optional): A coordinate pair
which indicates the end location through which the plane will
pass. Must also specify *end_point*. The coordinate
pair can be in x,y grid coordinates or latitude, longitude
coordinates. If using latitude, longitude coordinates, then
either *wrfin* or *projection* must be specified to obtain the
map projection. Default is None.
latlon (:obj:`bool`, optional): Set to True to also interpolate the
two-dimensional latitude and longitude coordinates along the same
horizontal line and include this information in the metadata
(if enabled). This can be helpful for plotting. Default is False.
Note:
Currently, *field2d* must be of type :class:`xarray.DataArray`
and contain coordinate information in order to generate the
latitude and longitude coordinates along the line if
*latlon* is set to True. Otherwise, a warning will be issued,
and the latitude and longitude information will not be
present.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using
large sequences of files.
Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True.
Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`:
The interpolated variable. If xarray is enabled and
the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata.
"""
try:
xy = cache["xy"]
except (KeyError, TypeError):
start_point_xy = None
end_point_xy = None
pivot_point_xy = None
if (latlon is True or is_latlon_pair(start_point) or
is_latlon_pair(pivot_point)):
if wrfin is not None:
is_moving = is_moving_domain(wrfin)
else:
is_moving = False
if timeidx is None:
if wrfin is not None:
# Moving nests aren't supported with ALL_TIMES because the
# domain could move outside of the line, which causes
# crashes or different line lengths.
if is_moving:
raise ValueError("Requesting all times with a moving nest "
"is not supported when using a lat/lon "
"line because the domain could "
"move outside of line. "
"You must request each time "
"individually.")
else:
# Domain not moving, just use 0
_timeidx = 0
# If using grid coordinates, then don't care about lat/lon
# coordinates. Just use 0.
else:
_timeidx = 0
else:
if is_moving:
_timeidx = timeidx
else:
# When using non-moving nests, set the time to 0
# to avoid problems downstream
_timeidx = 0
if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None:
xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx,
stagger, projection, ll_point)
pivot_point_xy = (xy_coords.x, xy_coords.y)
else:
pivot_point_xy = (pivot_point.x, pivot_point.y)
if start_point is not None and end_point is not None:
if start_point.lat is not None and start_point.lon is not None:
xy_coords = to_xy_coords(start_point, wrfin, _timeidx,
stagger, projection, ll_point)
start_point_xy = (xy_coords.x, xy_coords.y)
else:
start_point_xy = (start_point.x, start_point.y)
if end_point.lat is not None and end_point.lon is not None:
xy_coords = to_xy_coords(end_point, wrfin, _timeidx,
stagger, projection, ll_point)
end_point_xy = (xy_coords.x, xy_coords.y)
else:
end_point_xy = (end_point.x, end_point.y)
xy = get_xy(field2d, pivot_point_xy, angle, start_point_xy,
end_point_xy)
return _interpline(field2d, xy)
@set_interp_metadata("vinterp")
def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
field_type=None, log_p=False, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True):
"""Return the field vertically interpolated to the given the type of
surface and a set of new levels.
Args:
wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`,\
or an iterable):
WRF-ARW NetCDF data as a :class:`netCDF4.Dataset`,
:class:`Nio.NioFile` or an iterable sequence of the
aforementioned types.
field (:class:`xarray.DataArray` or :class:`numpy.ndarray`): A
three-dimensional field.
vert_coord (:obj:`str`): A string indicating the vertical coordinate
type to interpolate to.
Valid strings are:
* 'pressure', 'pres', 'p': pressure [hPa]
* 'ght_msl': grid point height msl [km]
* 'ght_agl': grid point height agl [km]
* 'theta', 'th': potential temperature [K]
* 'theta-e', 'thetae', 'eth': equivalent potential temperature \
[K]
interp_levels (sequence): A 1D sequence of vertical levels to
interpolate to. Values must be in the same units as specified
above for the *vert_coord* parameter.
extrapolate (:obj:`bool`, optional): Set to True to extrapolate
values below ground. This is only performed when *vert_coord* is
a pressure or height type, and the *field_type* is a pressure type
(with height vertical coordinate), a height type (with pressure as
the vertical coordinate), or a temperature type (with either height
or pressure as the vertical coordinate). If those conditions are
not met, or *field_type* is None, then the lowest model level
will be used. Extrapolation is performed using standard atmosphere.
Default is False.
field_type (:obj:`str`, optional):
The type of field. Default is None.
Valid strings are:
* 'none': None
* 'pressure', 'pres', 'p': pressure [Pa]
* 'pressure_hpa', 'pres_hpa', 'p_hpa': pressure [hPa]
* 'z', 'ght': geopotential height [m]
* 'z_km', 'ght_km': geopotential height [km]
* 'tc': temperature [degC]
* 'tk': temperature [K]
* 'theta', 'th': potential temperature [K]
* 'theta-e', 'thetae', 'eth': equivalent potential temperature
log_p (:obj:`bool`, optional): Set to True to use the log of the
vertical coordinate for interpolation. This is mainly intended
for pressure vertical coordinate types, but note that the log
will still be taken for any vertical coordinate type when
this is set to True. Default is False.
timeidx (:obj:`int`, optional):
The time index to use when extracting auxiallary variables used in
the interpolation. This value must be set to match the same value
used when the `field` variable was extracted. Default is 0.
method (:obj:`str`, optional): The aggregation method to use for
sequences. Must be either 'cat' or 'join'.
'cat' combines the data along the Time dimension.
'join' creates a new dimension for the file index.
The default is 'cat'.
squeeze (:obj:`bool`, optional): Set to False to prevent dimensions
with a size of 1 from being automatically removed from the shape
of the output. Default is True.
cache (:obj:`dict`, optional): A dictionary of (varname, ndarray)
that can be used to supply pre-extracted NetCDF variables to the
computational routines. It is primarily used for internal
purposes, but can also be used to improve performance by
eliminating the need to repeatedly extract the same variables
used in multiple diagnostics calculations, particularly when using
large sequences of files.
Default is None.
meta (:obj:`bool`, optional): Set to False to disable metadata and
return :class:`numpy.ndarray` instead of
:class:`xarray.DataArray`. Default is True.
Returns:
:class:`xarray.DataArray` or :class:`numpy.ndarray`:
The interpolated variable. If xarray is enabled and
the *meta* parameter is True, then the result will be a
:class:`xarray.DataArray` object. Otherwise, the result will be a
:class:`numpy.ndarray` object with no metadata.
"""
_key = get_id(wrfin)
_wrfin = get_iterable(wrfin)
# Remove case sensitivity
field_type = field_type.lower() if field_type is not None else "none"
vert_coord = vert_coord.lower() if vert_coord is not None else "none"
valid_coords = ("pressure", "pres", "p", "ght_msl",
"ght_agl", "theta", "th", "theta-e", "thetae", "eth")
valid_field_types = ("none", "pressure", "pres", "p",
'pressure_hpa', 'pres_hpa', 'p_hpa', "z",
"tc", "tk", "theta", "th", "theta-e", "thetae",
"eth", "ght", 'z_km', 'ght_km')
icase_lookup = {"none" : 0,
"p" : 1,
"pres" : 1,
"pressure" : 1,
"p_hpa" : 1,
"pres_hpa" : 1,
"pressure_hpa" : 1,
"z" : 2,
"ght" : 2,
"z_km" : 2,
"ght_km" : 2,
"tc" : 3,
"tk" : 4,
"theta" : 5,
"th" : 5,
"theta-e" : 6,
"thetae" : 6,
"eth" : 6}
in_unitmap = {"p_hpa" : 1.0/ConversionFactors.PA_TO_HPA,
"pres_hpa" : 1.0/ConversionFactors.PA_TO_HPA,
"pressure_hpa" : 1.0/ConversionFactors.PA_TO_HPA,
"z_km" : 1.0/ConversionFactors.M_TO_KM,
"ght_km" : 1.0/ConversionFactors.M_TO_KM,
}
out_unitmap = {"p_hpa" : ConversionFactors.PA_TO_HPA,
"pres_hpa" : ConversionFactors.PA_TO_HPA,
"pressure_hpa" : ConversionFactors.PA_TO_HPA,
"z_km" : ConversionFactors.M_TO_KM,
"ght_km" : ConversionFactors.M_TO_KM,
}
# These constants match what's in the fortran code.
rgas = Constants.RD
ussalr = Constants.USSALR
sclht = Constants.SCLHT
# interp_levels might be a list or tuple, make a numpy array
if not isinstance(interp_levels, np.ndarray):
interp_levels = np.asarray(interp_levels, np.float64)
if len(interp_levels) == 0:
raise ValueError("'interp_levels' contains no values")
# Check if field is staggered
if is_staggered(_wrfin, field):
raise ValueError("Please unstagger field in the vertical")
# Check for valid coord
if vert_coord not in valid_coords:
raise ValueError("'%s' is not a valid vertical "
"coordinate type" % vert_coord)
# Check for valid field type
if field_type not in valid_field_types:
raise ValueError("'%s' is not a valid field type" % field_type)
log_p_int = 1 if log_p else 0
icase = 0
extrap = 0
if extrapolate:
extrap = 1
icase = icase_lookup[field_type]
# Extract variables
ncvars = extract_vars(_wrfin, timeidx, ("PSFC", "QVAPOR"),
method, squeeze, cache, meta=False, _key=_key)
sfp = ncvars["PSFC"] * ConversionFactors.PA_TO_HPA
qv = ncvars["QVAPOR"]
terht = get_terrain(_wrfin, timeidx, units="m",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
tk = get_temp(_wrfin, timeidx, units="k",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
p = get_pressure(_wrfin, timeidx, units="pa",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
ght = get_height(_wrfin, timeidx, msl=True, units="m",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
smsfp = _smooth2d(sfp, 3, 2.0)
vcor = 0
if vert_coord in ("pressure", "pres", "p"):
vcor = 1
vcord_array = p * ConversionFactors.PA_TO_HPA
elif vert_coord == "ght_msl":
vcor = 2
vcord_array = np.exp(-ght/sclht)
elif vert_coord == "ght_agl":
ht_agl = get_height(_wrfin, timeidx, msl=False, units="m",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
vcor = 3
vcord_array = np.exp(-ht_agl/sclht)
elif vert_coord in ("theta", "th"):
t = get_theta(_wrfin, timeidx, units="k",
method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
coriolis = extract_vars(_wrfin, timeidx, "F",
method, squeeze, cache, meta=False,
_key=_key)["F"]
vcor = 4
idir = 1
icorsw = 0
delta = 0.01
p_hpa = p * ConversionFactors.PA_TO_HPA
vcord_array = _monotonic(t, p_hpa, coriolis, idir, delta, icorsw)
# We only extrapolate temperature fields below ground
# if we are interpolating to pressure or height vertical surfaces.
icase = 0
elif vert_coord in ("theta-e", "thetae", "eth"):
vcor = 5
icorsw = 0
idir = 1
delta = 0.01
eth = get_eth(_wrfin, timeidx, method=method, squeeze=squeeze,
cache=cache, meta=False, _key=_key)
coriolis = extract_vars(_wrfin, timeidx, "F",
method, squeeze, cache, meta=False,
_key=_key)["F"]
p_hpa = p * ConversionFactors.PA_TO_HPA
vcord_array = _monotonic(eth, p_hpa, coriolis, idir, delta, icorsw)
# We only extrapolate temperature fields below ground if we are
# interpolating to pressure or height vertical surfaces
icase = 0
# Set the missing value
if isinstance(field, ma.MaskedArray):
missing = field.fill_value
else:
missing = default_fill(np.float64)
if (field.shape != p.shape):
raise ValueError("'field' shape does not match other variable shapes. "
"Verify that the 'timeidx' parameter matches the "
"same value used when extracting the 'field' "
"variable.")
# Some field types are in different units than the Fortran routine
# expects
conv_factor = in_unitmap.get(field_type)
if conv_factor is not None:
field_ = field * conv_factor
else:
field_ = field
res = _vintrp(field_, p, tk, qv, ght, terht, sfp, smsfp,
vcord_array, interp_levels,
icase, extrap, vcor, log_p_int, missing)
conv_factor = out_unitmap.get(field_type)
if conv_factor is not None:
res_ = res * conv_factor
else:
res_ = res
return ma.masked_values(res_, missing)