Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Version NEXTVERSION

**2025-??-??**

* Update CF aggregation keywords
(https://github.com/NCAS-CMS/cfdm/issues/341)
* Set new minimum version of `dask`: ``2025.5.1``
(https://github.com/NCAS-CMS/cfdm/issues/339)
* Changed dependency: ``dask>=2025.5.1``
Expand Down
96 changes: 46 additions & 50 deletions cfdm/data/aggregatedarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __new__(cls, *args, **kwargs):
"""
instance = super().__new__(cls)
instance._FragmentArray = {
"location": FragmentFileArray,
"uri": FragmentFileArray,
"unique_value": FragmentUniqueValueArray,
}
return instance
Expand Down Expand Up @@ -66,16 +66,16 @@ def __init__(

fragment_array: `dict`
A dictionary representation of the fragment array, in
"location" form::
either "uri" form::

{'map': <'map' fragment array variable data>,
'location': <'location' fragment array variable data>,
'variable': <'variable' fragment array variable data>,}
{'map': <'map' fragment variable data>,
'uris': <'uris' fragment variable data>,
'identifiers': <'identifiers' fragment variable data>}

or "unique_value" form:
or else in "unique_value" form::

{'map': <'map' fragment array variable data>,
'unique_value': <'unique_value' fragment array data>}
{'map': <'map' fragment variable data>,
'unique_values': <'unique_values' fragment variable data>}

storage_options: `dict` or `None`, optional
Key/value pairs to be passed on to the creation of
Expand Down Expand Up @@ -208,24 +208,24 @@ def _parse_fragment_array(self, aggregated_filename, fragment_array):

fragment_array: `dict`
A dictionary representation of the fragment array, in
"location" form::
either "uri" form::

{'map': <'map' fragment array variable data>,
'location': <'location' fragment array variable data>,
'variable': <'variable' fragment array variable data>}
{'map': <'map' fragment variable data>,
'uris': <'uris' fragment variable data>,
'identifiers': <'identifiers' fragment variable data>}

or "unique_value" form::
or else "unique_value" form::

{'map': <'map' fragment array variable data>,
'unique_value': <'unique_value' fragment array data>}
{'map': <'map' fragment variable data>,
'unique_values': <'unique_values' fragment variable data>}

:Returns:

4-`tuple`
1. The shape of the aggregated data.
2. The shape of the array of fragments.
3. The type of the fragments (either
``'unique_value'`` or ``'location'``).
3. The type of the fragments (either ``'uri'`` or
``'unique_value'``).
4. The parsed aggregation instructions.

"""
Expand All @@ -243,45 +243,43 @@ def _parse_fragment_array(self, aggregated_filename, fragment_array):
fragment_array_indices = chunk_positions(chunks)
fragment_shapes = chunk_locations(chunks)

if "location" in fragment_array:
if "uris" in fragment_array:
# --------------------------------------------------------
# Each fragment is in a file, rather than given by a
# unique value.
# Each fragment is in an external dataset, rather than
# given by a unique value.
# --------------------------------------------------------
fragment_type = "location"
fa_variable = fragment_array["variable"]
fa_location = fragment_array["location"]
fragment_array_shape = fa_location.shape
fragment_type = "uri"
fa_identifiers = fragment_array["identifiers"]
fa_uris = fragment_array["uris"]
fragment_array_shape = fa_uris.shape

if not fa_variable.ndim:
fa_variable = fa_variable.item()
if not fa_identifiers.ndim:
identifier = fa_identifiers.item()
scalar = True
else:
scalar = False

for index, shape in zip(fragment_array_indices, fragment_shapes):
if scalar:
variable = fa_variable
else:
variable = fa_variable[index].item()
if not scalar:
identifier = fa_identifiers[index].item()

parsed_fragment_array[index] = {
"map": shape,
"location": fa_location[index].item(),
"variable": variable,
"uri": fa_uris[index].item(),
"identifier": identifier,
}
else:
# --------------------------------------------------------
# Each fragment comprises a unique value, rather than
# being in a file.
# --------------------------------------------------------
fragment_type = "unique_value"
fa_unique_value = fragment_array["unique_value"]
fragment_array_shape = fa_unique_value.shape
fa_unique_values = fragment_array["unique_values"]
fragment_array_shape = fa_unique_values.shape
parsed_fragment_array = {
index: {
"map": shape,
"unique_value": fa_unique_value[index].item(),
"unique_value": fa_unique_values[index].item(),
}
for index, shape in zip(
fragment_array_indices, fragment_shapes
Expand Down Expand Up @@ -334,15 +332,13 @@ def get_fragment_array(self, copy=True):
(2, 1, 1, 1)
>>> a.get_fragment_array()
{(0, 0, 0, 0): {
'file': ('January-June.nc',),
'variable': ('temp',),
'format': 'nc',
'location': [(0, 6), (0, 1), (0, 73), (0, 144)]},
'uri': 'January-March.nc',
'identifier': 'temp',
'map': [3, 1, 73, 144]}
(1, 0, 0, 0): {
'file': ('July-December.nc',),
'variable': ('temp',),
'format': 'nc',
'location': [(6, 12), (0, 1), (0, 73), (0, 144)]}}
'uri': 'April-December.nc',
'identifier': 'temp',
'map': [9, 1, 72, 144]}}

"""
fragment_array = self._get_component("fragment_array")
Expand Down Expand Up @@ -374,9 +370,9 @@ def get_fragment_array_shape(self):
def get_fragment_type(self):
"""The type of fragments in the fragment array.

Either ``'location'`` to indicate that the fragments are
files, or else ``'unique_value'`` to indicate that they
are represented by their unique data values.
Either ``'uri'`` to indicate that the fragments are files, or
else ``'unique_value'`` to indicate that they are represented
by their unique data values.

.. versionadded:: (cfdm) 1.12.0.0

Expand Down Expand Up @@ -718,7 +714,7 @@ def to_dask_array(self, chunks="auto"):
aggregated_attributes = self.get_attributes()
unpack = self.get_unpack()

if fragment_type == "location":
if fragment_type == "uri":
# Get the directory of the aggregation file as an absolute
# URI
aggregation_file_directory = dirname(self.get_filename())
Expand Down Expand Up @@ -752,9 +748,9 @@ def to_dask_array(self, chunks="auto"):
kwargs = fragment_array[fragment_index].copy()
kwargs.pop("map", None)

if fragment_type == "location":
kwargs["filename"] = kwargs.pop("location")
kwargs["address"] = kwargs.pop("variable")
if fragment_type == "uri":
kwargs["filename"] = kwargs.pop("uri")
kwargs["address"] = kwargs.pop("identifier")
kwargs["storage_options"] = storage_options
kwargs["aggregation_file_directory"] = (
aggregation_file_directory
Expand Down
101 changes: 51 additions & 50 deletions cfdm/mixin/netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4617,7 +4617,7 @@ def nc_del_aggregated_data(self):
"""Remove the netCDF aggregated_data terms.

The aggregated data terms define the names of the fragment
array variables, and are stored in a netCDF file in an
array variables, as would be stored in a netCDF file in an
"aggregated_data" attribute.

.. versionadded:: (cfdm) 1.12.0.0
Expand All @@ -4636,28 +4636,28 @@ def nc_del_aggregated_data(self):
**Examples**

>>> f.nc_set_aggregated_data(
... {'shape': 'shape',
... 'location': 'location',
... 'address': 'address'}
... {'map': 'fragment_map',
... 'uris': 'fragment_uris',
... 'identifiers': 'fragment_identifiers'}
... )
>>> f.nc_has_aggregated_data()
True
>>> f.nc_get_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_del_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_has_aggregated_data()
False
>>> f.nc_del_aggregated_data()
{}
>>> f.nc_get_aggregated_data()
{}
>>> f.nc_set_aggregated_data(
... 'shape: shape, location: location address: address'
... 'map: fragment_map, uris: fragment_uris identifiers: fragment_idenfiers'
... )

"""
Expand Down Expand Up @@ -4690,28 +4690,28 @@ def nc_get_aggregated_data(self):
**Examples**

>>> f.nc_set_aggregated_data(
... {'shape': 'shape',
... 'location': 'location',
... 'address': 'address'}
... {'map': 'fragment_map',
... 'uris': 'fragment_uris',
... 'identifiers': 'fragment_identifiers'}
... )
>>> f.nc_has_aggregated_data()
True
>>> f.nc_get_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_del_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_has_aggregated_data()
False
>>> f.nc_del_aggregated_data()
{}
>>> f.nc_get_aggregated_data()
{}
>>> f.nc_set_aggregated_data(
... 'shape: shape, location: location address: address'
... 'map: fragment_map, uris: fragment_uris identifiers: fragment_idenfiers'
... )

"""
Expand Down Expand Up @@ -4743,28 +4743,28 @@ def nc_has_aggregated_data(self):
**Examples**

>>> f.nc_set_aggregated_data(
... {'shape': 'shape',
... 'location': 'location',
... 'address': 'address'}
... {'map': 'fragment_map',
... 'uris': 'fragment_uris',
... 'identifiers': 'fragment_identifiers'}
... )
>>> f.nc_has_aggregated_data()
True
>>> f.nc_get_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_del_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_has_aggregated_data()
False
>>> f.nc_del_aggregated_data()
{}
>>> f.nc_get_aggregated_data()
{}
>>> f.nc_set_aggregated_data(
... 'shape: shape, location: location address: address'
... 'map: fragment_map, uris: fragment_uris identifiers: fragment_idenfiers'
... )

"""
Expand Down Expand Up @@ -4805,28 +4805,28 @@ def nc_set_aggregated_data(self, value):
**Examples**

>>> f.nc_set_aggregated_data(
... {'shape': 'shape',
... 'location': 'location',
... 'address': 'address'}
... {'map': 'fragment_map',
... 'uris': 'fragment_uris',
... 'identifiers': 'fragment_identifiers'}
... )
>>> f.nc_has_aggregated_data()
True
>>> f.nc_get_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_del_aggregated_data()
{'shape': 'shape',
'location': 'location',
'address': 'address'}
{'map': 'fragment_map',
'uris': 'fragment_uris',
'identifiers': 'fragment_identifiers'}
>>> f.nc_has_aggregated_data()
False
>>> f.nc_del_aggregated_data()
{}
>>> f.nc_get_aggregated_data()
{}
>>> f.nc_set_aggregated_data(
... 'shape: shape, location: location address: address'
... 'map: fragment_map, uris: fragment_uris identifiers: fragment_idenfiers'
... )

"""
Expand All @@ -4850,9 +4850,9 @@ def _nc_del_aggregation_fragment_type(self):
:Returns:

`str` or `None`
The removed fragment type, either ``'location'`` for
fragment files, or ``'value'`` for fragment unique
values, or `None` if no fragment type was set.
The removed fragment type, either ``'uri'`` for
fragment datasets, or ``'unique_value'`` for fragment
unique values, or `None` if no fragment type was set.

"""
return self._nc_del("aggregation_fragment_type", None)
Expand All @@ -4864,9 +4864,10 @@ def nc_get_aggregation_fragment_type(self):

:Returns:

`str`
The fragment type, either ``'location'`` for fragment
files, or ``'value'`` for fragment unique values.
`str` or `None`
The fragment type, either ``'uri'`` for fragment
datsets, or ``'unique_value'`` for fragment unique
values, or `None` for an unspecified fragment type.

"""
return self._nc_get("aggregation_fragment_type", None)
Expand All @@ -4879,9 +4880,9 @@ def _nc_set_aggregation_fragment_type(self, value):
:Parameters:

value: `str` or `None`
The fragment type, either ``'location'`` for fragment
files, ``'value'`` for fragment unique values, or
`None` for an unspecified fragment type.
The fragment type, either ``'uri'`` for fragment
files, ``'unique_value'`` for fragment unique values,
or `None` for an unspecified fragment type.

:Returns:

Expand Down
Loading
Loading