Skip to content

Commit 83306a8

Browse files
committed
Merge branch 'main' of github.com:NCAS-CMS/cf-python
2 parents b72de95 + 981fcd0 commit 83306a8

28 files changed

+1649
-1176
lines changed

Changelog.rst

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

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

6+
* New keyword parameter to `cf.Field.regrids` and `cf.Field.regridc`:
7+
``return_esmpy_regrid_operator``
8+
(https://github.com/NCAS-CMS/cf-python/issues/766)
9+
* Allow a halo to be added by `cf.Field.indices` and
10+
`cf.Field.subspace`
11+
(https://github.com/NCAS-CMS/cf-python/issues/759)
612
* Added spherical regridding to discrete sampling geometry destination
713
grids (https://github.com/NCAS-CMS/cf-python/issues/716)
814
* Added 3-d spherical regridding to `cf.Field.regrids`, and the option
@@ -24,9 +30,25 @@ version 3.16.2
2430
* Fix bug in `cf.read` when reading UM files that caused LBPROC value
2531
131072 (Mean over an ensemble of parallel runs) to be ignored
2632
(https://github.com/NCAS-CMS/cf-python/issues/737)
33+
* New keyword parameters to `cf.wi`: ``open_lower`` and ``open_upper``
34+
(https://github.com/NCAS-CMS/cf-python/issues/740)
2735
* Fix bug in `cf.aggregate` that sometimes put a null transpose
2836
operation into the Dask graph when one was not needed
2937
(https://github.com/NCAS-CMS/cf-python/issues/754)
38+
* Fix bug in `cf.aggregate` that caused a failure when property values
39+
were `numpy` arrays with two or more elements
40+
(https://github.com/NCAS-CMS/cf-python/issues/764)
41+
* Fix bug in `cf.aggregate` that didn't correctly handle the
42+
"actual_range" CF attribute
43+
(https://github.com/NCAS-CMS/cf-python/issues/764)
44+
* Fix bug whereby `Field.cyclic` is not updated after a
45+
`Field.del_construct` operation
46+
(https://github.com/NCAS-CMS/cf-python/issues/758)
47+
* Fix bug that meant `cyclic()` always returned an empty
48+
set for domains produced by `cf.Field.domain`
49+
(https://github.com/NCAS-CMS/cf-python/issues/762)
50+
* Changed dependency: ``cfunits>=3.3.7``
51+
* Changed dependency: ``netCDF4>=1.6.5``
3052

3153
----
3254

RELEASE.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
heeding the Versioning Strategy (see
33
https://ncas-cms.github.io/cf-python/releases.html#versioning-strategy).
44

5+
* Set the `NEXTVERSION` version marker across the codebase (added in PRs
6+
to mark the next version where the exact number/name is not yet decided)
7+
by recursively finding all occurences within the `cf` directory and replacing
8+
them with the upcoming version name `X.Y.Z` (replace `X`, `Y` and `Z` with
9+
appropriate numbers), via running this command in `cf-python` repo root
10+
directory (don't run it repo-wide or it will e.g. edit this script!):
11+
12+
```console
13+
$ find cf/ -type f | xargs sed -i 's/NEXTVERSION/X.Y.Z/g'
14+
```
15+
516
* Change the version and date in `cf/__init__.py` (`__version__` and
617
`__date__` variables)
718

cf/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
)
168168

169169
# Check the version of netCDF4
170-
_minimum_vn = "1.5.4"
170+
_minimum_vn = "1.6.5"
171171
if Version(netCDF4.__version__) < Version(_minimum_vn):
172172
raise RuntimeError(
173173
f"Bad netCDF4 version: cf requires netCDF4>={_minimum_vn}. "
@@ -191,7 +191,7 @@
191191
)
192192

193193
# Check the version of cfunits
194-
_minimum_vn = "3.3.6"
194+
_minimum_vn = "3.3.7"
195195
if Version(cfunits.__version__) < Version(_minimum_vn):
196196
raise RuntimeError(
197197
f"Bad cfunits version: cf requires cfunits>={_minimum_vn}. "

cf/aggregate.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4852,20 +4852,36 @@ def _aggregate_2_fields(
48524852
value0 = parent0.get_property(prop, None)
48534853
value1 = parent1.get_property(prop, None)
48544854

4855+
if prop in ("_FillValue", "missing_value"):
4856+
continue
4857+
48554858
if prop in ("valid_min", "valid_max", "valid_range"):
48564859
if not m0.respect_valid:
48574860
parent0.del_property(prop, None)
48584861

48594862
continue
48604863

4861-
if prop in ("_FillValue", "missing_value"):
4864+
if prop == "actual_range":
4865+
try:
4866+
# Try to extend the actual range to encompass both
4867+
# value0 and value1
4868+
actual_range = (
4869+
min(value0[0], value1[0]),
4870+
max(value0[1], value1[1]),
4871+
)
4872+
except (TypeError, IndexError, KeyError):
4873+
# value0 and/or value1 is not set, or is
4874+
# non-CF-compliant.
4875+
parent0.del_property(prop, None)
4876+
else:
4877+
parent0.set_property(prop, actual_range)
4878+
48624879
continue
48634880

48644881
# Still here?
4865-
if isinstance(value0, str) or isinstance(value1, str):
4866-
if value0 == value1:
4867-
continue
4868-
elif parent0._equals(value0, value1):
4882+
if parent0._equals(value0, value1):
4883+
# Both values are equal, so no need to update the
4884+
# property.
48694885
continue
48704886

48714887
if concatenate:
@@ -4876,9 +4892,30 @@ def _aggregate_2_fields(
48764892
)
48774893
else:
48784894
parent0.set_property(prop, f" :AGGREGATED: {value1}")
4879-
else:
4880-
if value0 is not None:
4881-
parent0.del_property(prop)
4895+
elif value0 is not None:
4896+
parent0.del_property(prop)
4897+
4898+
# Check that actual_range is within the bounds of valid_range, and
4899+
# delete it if it isn't.
4900+
actual_range = parent0.get_property("actual_range", None)
4901+
if actual_range is not None:
4902+
valid_range = parent0.get_property("valid_range", None)
4903+
if valid_range is not None:
4904+
try:
4905+
if (
4906+
actual_range[0] < valid_range[0]
4907+
or actual_range[1] > valid_range[1]
4908+
):
4909+
actual_range = parent0.del_property("actual_range", None)
4910+
if actual_range is not None and is_log_level_info(logger):
4911+
logger.info(
4912+
"Deleted 'actual_range' attribute due to being "
4913+
"outside of 'valid_range' attribute limits."
4914+
)
4915+
4916+
except (TypeError, IndexError):
4917+
# valid_range is non-CF-compliant
4918+
pass
48824919

48834920
# Make a note that the parent construct in this _Meta object has
48844921
# already been aggregated

cf/docstring/docstring.py

Lines changed: 88 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,42 @@
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+
============== ======================================""",
654+
# return_esmpy_regrid_operator
655+
"{{return_esmpy_regrid_operator: `bool`, optional}}": """return_esmpy_regrid_operator: `bool`, optional
656+
If True then do not perform the regridding, rather
657+
return the `esmpy.Regrid` instance that defines the
658+
regridding operation.""",
616659
# ----------------------------------------------------------------
617660
# Method description substitutions (4 levels of indentation)
618661
# ----------------------------------------------------------------
@@ -663,4 +706,49 @@
663706
The removed CFA-netCDF file name substitution. If the
664707
substitution was not defined then an empty dictionary
665708
is returned.""",
709+
# subspace valid modes Field
710+
"{{subspace valid modes Field}}": """Valid modes are:
711+
712+
* ``'compress'`` This is the default.
713+
Unselected locations are removed to create the
714+
subspace. If the result is not hyperrectangular
715+
then the minimum amount of unselected locations
716+
required to make it so will also be specially
717+
selected. Missing data is inserted at the
718+
specially selected locations, unless a halo has
719+
been defined (of any size, including 0).
720+
721+
* ``'envelope'``
722+
The subspace is the smallest hyperrectangular
723+
subspace that contains all of the selected
724+
locations. Missing data is inserted at unselected
725+
locations within the envelope, unless a halo has
726+
been defined (of any size, including 0).
727+
728+
* ``'full'``
729+
The subspace has the same domain as the original
730+
construct. Missing data is inserted at unselected
731+
locations, unless a halo has been defined (of any
732+
size, including 0).
733+
734+
.. note:: Setting a halo size of `0` differs from not
735+
not defining a halo at all. The shape of the
736+
returned field will always be the same, but
737+
in the former case missing data will not be
738+
inserted at unselected locations (if any)
739+
within the output domain.""",
740+
# subspace valid modes Domain
741+
"{{subspace valid modes Domain}}": """Valid modes are:
742+
743+
* ``'compress'`` This is the default.
744+
Unselected locations are removed to create the
745+
subspace. If the result is not hyperrectangular
746+
then the minimum amount of unselected locations
747+
required to make it so will also be specially
748+
selected.
749+
750+
* ``'envelope'``
751+
The subspace is the smallest hyperrectangular
752+
subspace that contains all of the selected
753+
locations.""",
666754
}

0 commit comments

Comments
 (0)