Masking and selection#
Selecting variables#
Xarray itself provides powerful ways to explore, index, subset and aggregate
datasets. Here again hyoga is only adding a thin layer of functionality. As
soon as hyoga (or any submodule) has been imported, this new functionality will
be available in a special .hyoga
attribute called “dataset accessor”:
import hyoga
ds = hyoga.open.example('pism.alps.out.2d.nc')
ds.hyoga
One thing to note in particular, is that hyoga never accesses model variables
by their “short names”. For instance, while thk
, refers to the ice
thickness in PISM, it may refer to a different quantity, or to nothing at all,
in another ice-sheet model. This is where CF standard names come into play.
To access ice thickness by its standard name you may use:
var = ds.hyoga.getvar('land_ice_thickness')
If a particular variable is missing, hyoga will additionally try to reconstruct it from others, such as the sum of bedrock altitude and ice thickness for surface altitude, or the norm of velocity components for its magnitude.
ds.hyoga.getvar('surface_altitude')
ds.hyoga.getvar('magnitude_of_land_ice_surface_velocity')
This mechanism can be disabled using infer=False
. Because surface altitude
is not actually present in the example dataset, the following would raise an
exception:
ds.hyoga.getvar('surface_altitude', infer=False)
Because CF standard names for land ice variables are relatively recent, older ice sheet models may not include them in output metadata. For PISM, a mechanism has been implemented to fill (some of) these missing standard names during initialization.
Note
While hyoga has only been tested with PISM so far, I hope it will become compatible with some other glacier and ice sheet models in the future. If you want to make your glacier model compatible with hyoga, please consider implementing CF standard names.
Adding new variables#
New variables can be added using using xarray’s dictionary interface or
methods such as xarray.Dataset.assign()
. Besides, hyoga provides a
dataset method to assign new variables by their standard name.
bedrock = ds.hyoga.getvar('bedrock_altitude')
thickness = ds.hyoga.getvar('land_ice_thickness')
surface = bedrock + thickness
new = ds.hyoga.assign(surface_altitude=surface)
This returns a new dataset including the surface altitude variable. Some
control on the variable (short) name can be achieved by preceding the
assign
call with xarray.DataArray.rename()
.
surface = surface.rename('surface')
ds = ds.hyoga.assign(surface_altitude=surface)
assert 'surface' in ds
However, this only works if the data does not already contain a variable with
the standard name surface_altitude
. In that case, that variable’s data is
quietly replaced, and the variable is not renamed.
surface = surface.rename('name_to_ignore')
ds = ds.hyoga.assign(surface_altitude=surface)
assert 'name_to_ignore' not in ds
Masking variables#
Hyoga’s plot methods use an ice mask to determine which grid cells are
glacierized and which are not. According to CF conventions, this is defined by
the standard variable land_ice_area_fraction
. There are several ways to
affect the ice mask. The easiest way is to use the (currently single) parametre
in hyoga.config
:
hyoga.config.glacier_masking_point
If the land_ice_area_fraction
variable is missing from the dataset, hyoga
falls back to compute and ice mask from land_ice_thickness
, using this
parametre as an ice thickness threshold. The default value is 1 (metre). For
PISM output files, a non-zero threshold may be advisable in case winter output
files contain a thin cover of “seasonal ice” outside the glacier margin, as is
the case in the demo files.
with hyoga.open.example('pism.alps.out.2d.nc') as ds:
ds.hyoga.plot.bedrock_altitude(center=False)
for i, value in enumerate([0.1, 1, 500]):
hyoga.config.glacier_masking_point = value
ds.hyoga.plot.ice_margin(edgecolor=f'C{i}', linewidths=1)
# restore the default of 1 m
hyoga.config.glacier_masking_point = 1
For more control, on can set the land_ice_area_fraction
variable using
assign_icemask()
. Suppose that we define glaciers as grid
cells filled with ice at least a metre thick, and moving at least ten metres
per year:
with hyoga.open.example('pism.alps.out.2d.nc') as ds:
ds = ds.hyoga.assign_icemask(
(ds.hyoga.getvar('land_ice_thickness') > 1) &
(ds.hyoga.getvar('magnitude_of_land_ice_surface_velocity') > 10))
ds.hyoga.plot.bedrock_altitude(center=False)
ds.hyoga.plot.ice_margin(facecolor='tab:blue')
Note that the assign_icemask()
method edits (or add) a
land_ice_area_fraction
variable without affecting the rest of the dataset.
Such lossless masking is should be enough for internal use within Hyoga.
However in some situations, a lossy (destructive) ice mask may be more useful.
This includes exporting data to a compressed netCDF file for the web, where
having homogeneous values outside the glacier mask can greatly reduce file
size. This can be achieved with Dataset.hyoga.where()
,
where_icemask()
, and
where_thicker()
.
These methods behave like xarray.Dataset.where()
: they replace data
values with np.nan outside the where condition. However, they are meant to
only affect “glacier variables” (currently any variable whose standard name
does not start with bedrock_altitude
).