Skip to content

Commit a868143

Browse files
authored
Merge pull request #760 from davidhassell/indices-halo-backup
Allow a halo to be added by `indices` and `subspace`
2 parents 2ac3353 + 7d9c32f commit a868143

15 files changed

+945
-1135
lines changed

Changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ version 3.16.2
33

44
**2024-04-??**
55

6+
* Allow a halo to be added by `cf.Field.indices` and
7+
`cf.Field.subspace`
8+
(https://github.com/NCAS-CMS/cf-python/issues/759)
69
* Added spherical regridding to discrete sampling geometry destination
710
grids (https://github.com/NCAS-CMS/cf-python/issues/716)
811
* Added 3-d spherical regridding to `cf.Field.regrids`, and the option

cf/docstring/docstring.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
Whether `esmpy` logging is enabled or not is determined by
8181
`cf.regrid_logging`. If it is enabled then logging takes place
8282
after every call. By default logging is disabled.""",
83+
# subspace halos
84+
"{{subspace halos}}": """If a halo is defined via a positional argument, then each
85+
subspaced axis will be extended to include that many extra
86+
elements at each "side" of the axis. The number of extra
87+
elements will be automatically reduced if including the full
88+
amount defined by the halo would extend the subspace beyond
89+
the axis limits.""",
8390
# ----------------------------------------------------------------
8491
# Method description substitutions (3 levels of indentation)
8592
# ----------------------------------------------------------------
@@ -613,6 +620,37 @@
613620
"{{to_size: `int`, optional}}": """to_size: `int`, optional
614621
Pad the axis after so that the new axis has the given
615622
size.""",
623+
# subspace config options
624+
"{{config: optional}}": """config: optional
625+
Configure the subspace by specifying the mode of
626+
operation (``mode``) and any halo to be added to the
627+
subspaced axes (``halo``), with positional arguments
628+
in the format ``mode``, or ``halo``, or ``mode,
629+
halo``, or with no positional arguments at all.
630+
631+
A mode of operation is given as a `str`, and a halo as
632+
a non-negative `int` (or any object that can be
633+
converted to one):
634+
635+
============== ======================================
636+
*mode* Description
637+
============== ======================================
638+
Not provided If no positional arguments are
639+
provided then assume the
640+
``'compress'`` mode of operation with
641+
no halo added to the subspaced axes.
642+
643+
``mode`` Define the mode of operation with no
644+
halo added to the subspaced axes.
645+
646+
``mode, halo`` Define a mode of operation, as well as
647+
a halo to be added to the subspaced
648+
axes.
649+
650+
``halo`` Assume the ``'compress'`` mode of
651+
operation and define a halo to be
652+
added to the subspaced axes.
653+
============== ======================================""",
616654
# ----------------------------------------------------------------
617655
# Method description substitutions (4 levels of indentation)
618656
# ----------------------------------------------------------------
@@ -663,4 +701,49 @@
663701
The removed CFA-netCDF file name substitution. If the
664702
substitution was not defined then an empty dictionary
665703
is returned.""",
704+
# subspace valid modes Field
705+
"{{subspace valid modes Field}}": """Valid modes are:
706+
707+
* ``'compress'`` This is the default.
708+
Unselected locations are removed to create the
709+
subspace. If the result is not hyperrectangular
710+
then the minimum amount of unselected locations
711+
required to make it so will also be specially
712+
selected. Missing data is inserted at the
713+
specially selected locations, unless a halo has
714+
been defined (of any size, including 0).
715+
716+
* ``'envelope'``
717+
The subspace is the smallest hyperrectangular
718+
subspace that contains all of the selected
719+
locations. Missing data is inserted at unselected
720+
locations within the envelope, unless a halo has
721+
been defined (of any size, including 0).
722+
723+
* ``'full'``
724+
The subspace has the same domain as the original
725+
construct. Missing data is inserted at unselected
726+
locations, unless a halo has been defined (of any
727+
size, including 0).
728+
729+
.. note:: Setting a halo size of `0` differs from not
730+
not defining a halo at all. The shape of the
731+
returned field will always be the same, but
732+
in the former case missing data will not be
733+
inserted at unselected locations (if any)
734+
within the output domain.""",
735+
# subspace valid modes Domain
736+
"{{subspace valid modes Domain}}": """Valid modes are:
737+
738+
* ``'compress'`` This is the default.
739+
Unselected locations are removed to create the
740+
subspace. If the result is not hyperrectangular
741+
then the minimum amount of unselected locations
742+
required to make it so will also be specially
743+
selected.
744+
745+
* ``'envelope'``
746+
The subspace is the smallest hyperrectangular
747+
subspace that contains all of the selected
748+
locations.""",
666749
}

cf/domain.py

Lines changed: 36 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -739,11 +739,11 @@ def identities(self):
739739

740740
return out
741741

742-
def indices(self, *mode, **kwargs):
742+
def indices(self, *config, **kwargs):
743743
"""Create indices that define a subspace of the domain
744744
construct.
745745
746-
The indices returned by this method be used to create the
746+
The indices returned by this method may be used to create the
747747
subspace by passing them to the `subspace` method of the
748748
original domain construct.
749749
@@ -778,34 +778,22 @@ def indices(self, *mode, **kwargs):
778778
may still need to be inserted into the field construct's
779779
data.
780780
781+
**Halos**
782+
783+
{{subspace halos}}
784+
781785
.. versionadded:: 3.11.0
782786
783787
.. seealso:: `subspace`, `where`, `__getitem__`,
784788
`__setitem__`, `cf.Field.indices`
785789
786790
:Parameters:
787791
788-
mode: `str`, *optional*
789-
There are two modes of operation, each of which provides
790-
indices for a different type of subspace:
791-
792-
============== ======================================
793-
*mode* Description
794-
============== ======================================
795-
``'compress'`` This is the default mode. Unselected
796-
locations are removed to create the
797-
returned subspace. Note that if a
798-
multi-dimensional metadata construct
799-
is being used to define the indices
800-
then some missing data may still be
801-
inserted at unselected locations.
802-
803-
``'envelope'`` The returned subspace is the smallest
804-
that contains all of the selected
805-
indices.
806-
============== ======================================
807-
808-
kwargs: *optional*
792+
{{config: optional}}
793+
794+
{{subspace valid modes Domain}}
795+
796+
kwargs: optional
809797
A keyword name is an identity of a metadata construct,
810798
and the keyword value provides a condition for
811799
inferring indices that apply to the dimension (or
@@ -857,22 +845,9 @@ def indices(self, *mode, **kwargs):
857845
: time(1) = [2019-01-01 00:00:00]
858846
859847
"""
860-
if len(mode) > 1:
861-
raise ValueError(
862-
"Can't provide more than one positional argument. "
863-
f"Got: {', '.join(repr(x) for x in mode)}"
864-
)
865-
866-
if not mode or "compress" in mode:
867-
mode = "compress"
868-
elif "envelope" in mode:
869-
mode = "envelope"
870-
else:
871-
raise ValueError(f"Invalid value for 'mode' argument: {mode[0]!r}")
872-
873848
# Get the indices for every domain axis in the domain, without
874849
# any auxiliary masks.
875-
domain_indices = self._indices(mode, None, False, kwargs)
850+
domain_indices = self._indices(config, None, False, kwargs)
876851

877852
return domain_indices["indices"]
878853

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

11201095
return d
11211096

1122-
def subspace(self, *mode, **kwargs):
1123-
"""Create indices that define a subspace of the domain
1124-
construct.
1097+
def subspace(self, *config, **kwargs):
1098+
"""Create a subspace of the field construct.
11251099
1126-
The indices returned by this method be used to create the subspace
1127-
by passing them to the `subspace` method of the original domain
1128-
construct.
1100+
Creation of a new domain construct which spans a subspace of
1101+
the domain of an existing domain construct is achieved by
1102+
identifying indices based on the metadata constructs
1103+
(subspacing by metadata). The new domain construct is created
1104+
with the same properties as the original domain construct.
11291105
1130-
The subspace is defined by identifying indices based on the
1131-
metadata constructs.
1106+
**Subspacing by metadata**
11321107
1133-
Metadata constructs are selected conditions are specified on their
1134-
data. Indices for subspacing are then automatically inferred from
1135-
where the conditions are met.
1108+
Subspacing by metadata selects metadata constructs and
1109+
specifies conditions on their data. Indices for subspacing are
1110+
then automatically inferred from where the conditions are met.
11361111
11371112
Metadata constructs and the conditions on their data are defined
11381113
by keyword parameters.
@@ -1156,41 +1131,21 @@ def subspace(self, *mode, **kwargs):
11561131
acting along orthogonal dimensions, some missing data may still
11571132
need to be inserted into the field construct's data.
11581133
1159-
.. versionadded:: 3.11.0
1160-
1161-
.. seealso:: `indices`
1162-
1163-
:Parameters:
1134+
**Halos**
11641135
1165-
mode: `str`, *optional*
1166-
There are two modes of operation, each of which provides
1167-
indices for a different type of subspace:
1136+
{{subspace halos}}
11681137
1169-
============== ==========================================
1170-
*mode* Description
1171-
============== ==========================================
1172-
``'compress'`` Return indices that identify only the
1173-
requested locations.
1138+
.. versionadded:: 3.11.0
11741139
1175-
This is the default mode.
1140+
.. seealso:: `indices`, `cf.Field.subspace`
11761141
1177-
Note that if a multi-dimensional metadata
1178-
construct is being used to define the
1179-
indices then some unrequested locations
1180-
may also be selected.
1142+
:Parameters:
11811143
1182-
``'envelope'`` The returned subspace is the smallest that
1183-
contains all of the requested locations.
1144+
{{config: optional}}
11841145
1185-
``'test'`` May be used on its own or in addition to
1186-
one of the other positional arguments. Do
1187-
not create a subspace, but return `True`
1188-
or `False` depending on whether or not it
1189-
is possible to create the specified
1190-
subspace.
1191-
============== ==========================================
1146+
{{subspace valid modes Domain}}
11921147
1193-
kwargs: *optional*
1148+
kwargs: optional
11941149
A keyword name is an identity of a metadata construct, and
11951150
the keyword value provides a condition for inferring
11961151
indices that apply to the dimension (or dimensions)
@@ -1231,19 +1186,19 @@ def subspace(self, *mode, **kwargs):
12311186
12321187
"""
12331188
test = False
1234-
if "test" in mode:
1235-
mode = list(mode)
1236-
mode.remove("test")
1189+
if "test" in config:
1190+
config = list(config)
1191+
config.remove("test")
12371192
test = True
12381193

1239-
if not mode and not kwargs:
1194+
if not config and not kwargs:
12401195
if test:
12411196
return True
12421197

12431198
return self.copy()
12441199

12451200
try:
1246-
indices = self.indices(*mode, **kwargs)
1201+
indices = self.indices(*config, **kwargs)
12471202
except ValueError as error:
12481203
if test:
12491204
return False

0 commit comments

Comments
 (0)