Skip to content

Commit

Permalink
Merge pull request #760 from davidhassell/indices-halo-backup
Browse files Browse the repository at this point in the history
Allow a halo to be added by `indices` and `subspace`
  • Loading branch information
davidhassell committed Apr 24, 2024
2 parents 2ac3353 + 7d9c32f commit a868143
Show file tree
Hide file tree
Showing 15 changed files with 945 additions and 1,135 deletions.
3 changes: 3 additions & 0 deletions Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ version 3.16.2

**2024-04-??**

* Allow a halo to be added by `cf.Field.indices` and
`cf.Field.subspace`
(https://github.com/NCAS-CMS/cf-python/issues/759)
* Added spherical regridding to discrete sampling geometry destination
grids (https://github.com/NCAS-CMS/cf-python/issues/716)
* Added 3-d spherical regridding to `cf.Field.regrids`, and the option
Expand Down
83 changes: 83 additions & 0 deletions cf/docstring/docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@
Whether `esmpy` logging is enabled or not is determined by
`cf.regrid_logging`. If it is enabled then logging takes place
after every call. By default logging is disabled.""",
# subspace halos
"{{subspace halos}}": """If a halo is defined via a positional argument, then each
subspaced axis will be extended to include that many extra
elements at each "side" of the axis. The number of extra
elements will be automatically reduced if including the full
amount defined by the halo would extend the subspace beyond
the axis limits.""",
# ----------------------------------------------------------------
# Method description substitutions (3 levels of indentation)
# ----------------------------------------------------------------
Expand Down Expand Up @@ -613,6 +620,37 @@
"{{to_size: `int`, optional}}": """to_size: `int`, optional
Pad the axis after so that the new axis has the given
size.""",
# subspace config options
"{{config: optional}}": """config: optional
Configure the subspace by specifying the mode of
operation (``mode``) and any halo to be added to the
subspaced axes (``halo``), with positional arguments
in the format ``mode``, or ``halo``, or ``mode,
halo``, or with no positional arguments at all.
A mode of operation is given as a `str`, and a halo as
a non-negative `int` (or any object that can be
converted to one):
============== ======================================
*mode* Description
============== ======================================
Not provided If no positional arguments are
provided then assume the
``'compress'`` mode of operation with
no halo added to the subspaced axes.
``mode`` Define the mode of operation with no
halo added to the subspaced axes.
``mode, halo`` Define a mode of operation, as well as
a halo to be added to the subspaced
axes.
``halo`` Assume the ``'compress'`` mode of
operation and define a halo to be
added to the subspaced axes.
============== ======================================""",
# ----------------------------------------------------------------
# Method description substitutions (4 levels of indentation)
# ----------------------------------------------------------------
Expand Down Expand Up @@ -663,4 +701,49 @@
The removed CFA-netCDF file name substitution. If the
substitution was not defined then an empty dictionary
is returned.""",
# subspace valid modes Field
"{{subspace valid modes Field}}": """Valid modes are:
* ``'compress'`` This is the default.
Unselected locations are removed to create the
subspace. If the result is not hyperrectangular
then the minimum amount of unselected locations
required to make it so will also be specially
selected. Missing data is inserted at the
specially selected locations, unless a halo has
been defined (of any size, including 0).
* ``'envelope'``
The subspace is the smallest hyperrectangular
subspace that contains all of the selected
locations. Missing data is inserted at unselected
locations within the envelope, unless a halo has
been defined (of any size, including 0).
* ``'full'``
The subspace has the same domain as the original
construct. Missing data is inserted at unselected
locations, unless a halo has been defined (of any
size, including 0).
.. note:: Setting a halo size of `0` differs from not
not defining a halo at all. The shape of the
returned field will always be the same, but
in the former case missing data will not be
inserted at unselected locations (if any)
within the output domain.""",
# subspace valid modes Domain
"{{subspace valid modes Domain}}": """Valid modes are:
* ``'compress'`` This is the default.
Unselected locations are removed to create the
subspace. If the result is not hyperrectangular
then the minimum amount of unselected locations
required to make it so will also be specially
selected.
* ``'envelope'``
The subspace is the smallest hyperrectangular
subspace that contains all of the selected
locations.""",
}
117 changes: 36 additions & 81 deletions cf/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,11 +739,11 @@ def identities(self):

return out

def indices(self, *mode, **kwargs):
def indices(self, *config, **kwargs):
"""Create indices that define a subspace of the domain
construct.
The indices returned by this method be used to create the
The indices returned by this method may be used to create the
subspace by passing them to the `subspace` method of the
original domain construct.
Expand Down Expand Up @@ -778,34 +778,22 @@ def indices(self, *mode, **kwargs):
may still need to be inserted into the field construct's
data.
**Halos**
{{subspace halos}}
.. versionadded:: 3.11.0
.. seealso:: `subspace`, `where`, `__getitem__`,
`__setitem__`, `cf.Field.indices`
:Parameters:
mode: `str`, *optional*
There are two modes of operation, each of which provides
indices for a different type of subspace:
============== ======================================
*mode* Description
============== ======================================
``'compress'`` This is the default mode. Unselected
locations are removed to create the
returned subspace. Note that if a
multi-dimensional metadata construct
is being used to define the indices
then some missing data may still be
inserted at unselected locations.
``'envelope'`` The returned subspace is the smallest
that contains all of the selected
indices.
============== ======================================
kwargs: *optional*
{{config: optional}}
{{subspace valid modes Domain}}
kwargs: optional
A keyword name is an identity of a metadata construct,
and the keyword value provides a condition for
inferring indices that apply to the dimension (or
Expand Down Expand Up @@ -857,22 +845,9 @@ def indices(self, *mode, **kwargs):
: time(1) = [2019-01-01 00:00:00]
"""
if len(mode) > 1:
raise ValueError(
"Can't provide more than one positional argument. "
f"Got: {', '.join(repr(x) for x in mode)}"
)

if not mode or "compress" in mode:
mode = "compress"
elif "envelope" in mode:
mode = "envelope"
else:
raise ValueError(f"Invalid value for 'mode' argument: {mode[0]!r}")

# Get the indices for every domain axis in the domain, without
# any auxiliary masks.
domain_indices = self._indices(mode, None, False, kwargs)
domain_indices = self._indices(config, None, False, kwargs)

return domain_indices["indices"]

Expand Down Expand Up @@ -1119,20 +1094,20 @@ def roll(self, axis, shift, inplace=False):

return d

def subspace(self, *mode, **kwargs):
"""Create indices that define a subspace of the domain
construct.
def subspace(self, *config, **kwargs):
"""Create a subspace of the field construct.
The indices returned by this method be used to create the subspace
by passing them to the `subspace` method of the original domain
construct.
Creation of a new domain construct which spans a subspace of
the domain of an existing domain construct is achieved by
identifying indices based on the metadata constructs
(subspacing by metadata). The new domain construct is created
with the same properties as the original domain construct.
The subspace is defined by identifying indices based on the
metadata constructs.
**Subspacing by metadata**
Metadata constructs are selected conditions are specified on their
data. Indices for subspacing are then automatically inferred from
where the conditions are met.
Subspacing by metadata selects metadata constructs and
specifies conditions on their data. Indices for subspacing are
then automatically inferred from where the conditions are met.
Metadata constructs and the conditions on their data are defined
by keyword parameters.
Expand All @@ -1156,41 +1131,21 @@ def subspace(self, *mode, **kwargs):
acting along orthogonal dimensions, some missing data may still
need to be inserted into the field construct's data.
.. versionadded:: 3.11.0
.. seealso:: `indices`
:Parameters:
**Halos**
mode: `str`, *optional*
There are two modes of operation, each of which provides
indices for a different type of subspace:
{{subspace halos}}
============== ==========================================
*mode* Description
============== ==========================================
``'compress'`` Return indices that identify only the
requested locations.
.. versionadded:: 3.11.0
This is the default mode.
.. seealso:: `indices`, `cf.Field.subspace`
Note that if a multi-dimensional metadata
construct is being used to define the
indices then some unrequested locations
may also be selected.
:Parameters:
``'envelope'`` The returned subspace is the smallest that
contains all of the requested locations.
{{config: optional}}
``'test'`` May be used on its own or in addition to
one of the other positional arguments. Do
not create a subspace, but return `True`
or `False` depending on whether or not it
is possible to create the specified
subspace.
============== ==========================================
{{subspace valid modes Domain}}
kwargs: *optional*
kwargs: optional
A keyword name is an identity of a metadata construct, and
the keyword value provides a condition for inferring
indices that apply to the dimension (or dimensions)
Expand Down Expand Up @@ -1231,19 +1186,19 @@ def subspace(self, *mode, **kwargs):
"""
test = False
if "test" in mode:
mode = list(mode)
mode.remove("test")
if "test" in config:
config = list(config)
config.remove("test")
test = True

if not mode and not kwargs:
if not config and not kwargs:
if test:
return True

return self.copy()

try:
indices = self.indices(*mode, **kwargs)
indices = self.indices(*config, **kwargs)
except ValueError as error:
if test:
return False
Expand Down

0 comments on commit a868143

Please sign in to comment.