Skip to content

Commit 17058fc

Browse files
committed
Merge branch 'chardata_extra' into chardata
2 parents 93bf8a1 + 9281784 commit 17058fc

File tree

1 file changed

+77
-75
lines changed

1 file changed

+77
-75
lines changed

lib/iris/fileformats/netcdf/saver.py

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ def _create_cf_dimensions(self, cube, dimension_names, unlimited_dimensions=None
759759
# used for a different one
760760
pass
761761
else:
762-
dim_name = self._get_coord_variable_name(cube, coord)
762+
dim_name = self._get_element_variable_name(cube, coord)
763763
unlimited_dim_names.append(dim_name)
764764

765765
for dim_name in dimension_names:
@@ -1365,7 +1365,7 @@ def record_dimension(names_list, dim_name, length, matching_coords=None):
13651365
if dim_name is None:
13661366
# Not already present : create a unique dimension name
13671367
# from the coord.
1368-
dim_name = self._get_coord_variable_name(cube, coord)
1368+
dim_name = self._get_element_variable_name(cube, coord)
13691369
# Disambiguate if it has the same name as an
13701370
# existing dimension.
13711371
# OR if it matches an existing file variable name.
@@ -1541,38 +1541,14 @@ def _create_cf_bounds(self, coord, cf_var, cf_name, /, *, compression_kwargs=Non
15411541
)
15421542
self._lazy_stream_data(data=bounds, cf_var=cf_var_bounds)
15431543

1544-
def _get_cube_variable_name(self, cube):
1545-
"""Return a CF-netCDF variable name for the given cube.
1546-
1547-
Parameters
1548-
----------
1549-
cube : :class:`iris.cube.Cube`
1550-
An instance of a cube for which a CF-netCDF variable
1551-
name is required.
1552-
1553-
Returns
1554-
-------
1555-
str
1556-
A CF-netCDF variable name as a string.
1557-
1558-
"""
1559-
if cube.var_name is not None:
1560-
cf_name = cube.var_name
1561-
else:
1562-
# Convert to lower case and replace whitespace by underscores.
1563-
cf_name = "_".join(cube.name().lower().split())
1564-
1565-
cf_name = self.cf_valid_var_name(cf_name)
1566-
return cf_name
1567-
1568-
def _get_coord_variable_name(self, cube_or_mesh, coord):
1569-
"""Return a CF-netCDF variable name for a given coordinate-like element.
1544+
def _get_element_variable_name(self, cube_or_mesh, element):
1545+
"""Return a CF-netCDF variable name for a given coordinate-like element, or cube.
15701546
15711547
Parameters
15721548
----------
15731549
cube_or_mesh : :class:`iris.cube.Cube` or :class:`iris.mesh.MeshXY`
15741550
The Cube or Mesh being saved to the netCDF file.
1575-
coord : :class:`iris.coords._DimensionalMetadata`
1551+
element : :class:`iris.coords._DimensionalMetadata` | :class:``iris.cube.Cube``
15761552
An instance of a coordinate (or similar), for which a CF-netCDF
15771553
variable name is required.
15781554
@@ -1592,17 +1568,21 @@ def _get_coord_variable_name(self, cube_or_mesh, coord):
15921568
cube = None
15931569
mesh = cube_or_mesh
15941570

1595-
if coord.var_name is not None:
1596-
cf_name = coord.var_name
1571+
if element.var_name is not None:
1572+
cf_name = element.var_name
1573+
elif isinstance(element, Cube):
1574+
# Make name for a Cube without a var_name.
1575+
cf_name = "_".join(element.name().lower().split())
15971576
else:
1598-
name = coord.standard_name or coord.long_name
1577+
# Make name for a Coord-like element without a var_name
1578+
name = element.standard_name or element.long_name
15991579
if not name or set(name).intersection(string.whitespace):
16001580
# We need to invent a name, based on its associated dimensions.
1601-
if cube is not None and cube.coords(coord):
1602-
# It is a regular cube coordinate.
1581+
if cube is not None and cube.elements(element):
1582+
# It is a regular cube elementinate.
16031583
# Auto-generate a name based on the dims.
16041584
name = ""
1605-
for dim in cube.coord_dims(coord):
1585+
for dim in cube.coord_dims(element):
16061586
name += f"dim{dim}"
16071587
# Handle scalar coordinate (dims == ()).
16081588
if not name:
@@ -1616,8 +1596,8 @@ def _get_coord_variable_name(self, cube_or_mesh, coord):
16161596

16171597
# At present, a location-coord cannot be nameless, as the
16181598
# MeshXY code relies on guess_coord_axis.
1619-
assert isinstance(coord, Connectivity)
1620-
location = coord.cf_role.split("_")[0]
1599+
assert isinstance(element, Connectivity)
1600+
location = element.cf_role.split("_")[0]
16211601
location_dim_attr = f"{location}_dimension"
16221602
name = getattr(mesh, location_dim_attr)
16231603

@@ -1693,6 +1673,8 @@ def _create_mesh(self, mesh):
16931673
return cf_mesh_name
16941674

16951675
def _set_cf_var_attributes(self, cf_var, element):
1676+
from iris.cube import Cube
1677+
16961678
# Deal with CF-netCDF units, and add the name+units properties.
16971679
if isinstance(element, iris.coords.Coord):
16981680
# Fix "degree" units if needed.
@@ -1715,19 +1697,21 @@ def _set_cf_var_attributes(self, cf_var, element):
17151697
if element.units.calendar:
17161698
_setncattr(cf_var, "calendar", str(element.units.calendar))
17171699

1718-
# Add any other custom coordinate attributes.
1719-
for name in sorted(element.attributes):
1720-
value = element.attributes[name]
1700+
if not isinstance(element, Cube):
1701+
# Add any other custom coordinate attributes.
1702+
# N.B. not Cube, which has specific handling in _create_cf_data_variable
1703+
for name in sorted(element.attributes):
1704+
value = element.attributes[name]
17211705

1722-
if name == "STASH":
1723-
# Adopting provisional Metadata Conventions for representing MO
1724-
# Scientific Data encoded in NetCDF Format.
1725-
name = "um_stash_source"
1726-
value = str(value)
1706+
if name == "STASH":
1707+
# Adopting provisional Metadata Conventions for representing MO
1708+
# Scientific Data encoded in NetCDF Format.
1709+
name = "um_stash_source"
1710+
value = str(value)
17271711

1728-
# Don't clobber existing attributes.
1729-
if not hasattr(cf_var, name):
1730-
_setncattr(cf_var, name, value)
1712+
# Don't clobber existing attributes.
1713+
if not hasattr(cf_var, name):
1714+
_setncattr(cf_var, name, value)
17311715

17321716
def _create_generic_cf_array_var(
17331717
self,
@@ -1739,6 +1723,7 @@ def _create_generic_cf_array_var(
17391723
element_dims=None,
17401724
fill_value=None,
17411725
compression_kwargs=None,
1726+
is_dataless=False,
17421727
):
17431728
"""Create theCF-netCDF variable given dimensional_metadata.
17441729
@@ -1791,7 +1776,7 @@ def _create_generic_cf_array_var(
17911776

17921777
# Work out the var-name to use.
17931778
# N.B. the only part of this routine that may use a mesh _or_ a cube.
1794-
cf_name = self._get_coord_variable_name(cube_or_mesh, element)
1779+
cf_name = self._get_element_variable_name(cube_or_mesh, element)
17951780
while cf_name in self._dataset.variables:
17961781
cf_name = self._increment_name(cf_name)
17971782

@@ -1804,10 +1789,13 @@ def _create_generic_cf_array_var(
18041789
# Get the data values, in a way which works for any element type, as
18051790
# all are subclasses of _DimensionalMetadata.
18061791
# (e.g. =points if a coord, =data if an ancillary, etc)
1807-
data = element._core_values()
1792+
if isinstance(element, Cube):
1793+
data = element.core_data()
1794+
else:
1795+
data = element._core_values()
18081796

18091797
# This compression contract is *not* applicable to a mesh.
1810-
if cube and cube.shape != data.shape:
1798+
if cube is not None and data is not None and cube.shape != data.shape:
18111799
compression_kwargs = {}
18121800

18131801
if np.issubdtype(data.dtype, np.str_):
@@ -1837,11 +1825,13 @@ def _create_generic_cf_array_var(
18371825
# Convert data from an array of strings into a character array
18381826
# with an extra string-length dimension.
18391827
if len(element_dims) == 1:
1828+
# Scalar variable (only has string dimension).
18401829
data_first = data[0]
18411830
if is_lazy_data(data_first):
18421831
data_first = dask.compute(data_first)
18431832
data = list("%- *s" % (string_dimension_depth, data_first))
18441833
else:
1834+
# NOTE: at present, can't do this lazily??
18451835
orig_shape = data.shape
18461836
new_shape = orig_shape + (string_dimension_depth,)
18471837
new_data = np.zeros(new_shape, cf_var.dtype)
@@ -1850,7 +1840,7 @@ def _create_generic_cf_array_var(
18501840
new_data[index_slice] = list(
18511841
"%- *s" % (string_dimension_depth, data[index])
18521842
)
1853-
data = new_data
1843+
data = new_data
18541844
else:
18551845
# A normal (numeric) variable.
18561846
# ensure a valid datatype for the file format.
@@ -1887,7 +1877,8 @@ def _create_generic_cf_array_var(
18871877
)
18881878

18891879
# Add the data to the CF-netCDF variable.
1890-
self._lazy_stream_data(data=data, cf_var=cf_var)
1880+
if not is_dataless:
1881+
self._lazy_stream_data(data=data, cf_var=cf_var)
18911882

18921883
# Add names + units
18931884
self._set_cf_var_attributes(cf_var, element)
@@ -2238,9 +2229,9 @@ def _create_cf_grid_mapping(self, cube, cf_var_cube):
22382229
cfvar = self._name_coord_map.name(coord)
22392230
if not cfvar:
22402231
# not found - create and store it:
2241-
cfvar = self._get_coord_variable_name(cube, coord)
2232+
cfvar = self._get_element_variable_name(cube, coord)
22422233
self._name_coord_map.append(
2243-
cfvar, self._get_coord_variable_name(cube, coord)
2234+
cfvar, self._get_element_variable_name(cube, coord)
22442235
)
22452236
cfvar_names.append(cfvar)
22462237

@@ -2383,32 +2374,43 @@ def set_packing_ncattrs(cfvar):
23832374
if add_offset:
23842375
_setncattr(cfvar, "add_offset", add_offset)
23852376

2386-
cf_name = self._get_cube_variable_name(cube)
2387-
while cf_name in self._dataset.variables:
2388-
cf_name = self._increment_name(cf_name)
2389-
2377+
# cf_name = self._get_element_variable_name(cube_or_mesh=None, element=cube)
2378+
# while cf_name in self._dataset.variables:
2379+
# cf_name = self._increment_name(cf_name)
2380+
#
2381+
# cf_var = self._dataset.createVariable(
2382+
# cf_name, dtype, dimension_names, fill_value=fill_value, **kwargs
2383+
# )
23902384
# Create the cube CF-netCDF data variable with data payload.
2391-
cf_var = self._dataset.createVariable(
2392-
cf_name, dtype, dimension_names, fill_value=fill_value, **kwargs
2385+
cf_name = self._create_generic_cf_array_var(
2386+
cube,
2387+
dimension_names,
2388+
cube,
2389+
element_dims=dimension_names,
2390+
fill_value=fill_value,
2391+
compression_kwargs=kwargs,
2392+
is_dataless=is_dataless,
23932393
)
2394+
cf_var = self._dataset.variables[cf_name]
23942395

23952396
if not is_dataless:
23962397
set_packing_ncattrs(cf_var)
2397-
self._lazy_stream_data(data=data, cf_var=cf_var)
2398-
2399-
if cube.standard_name:
2400-
_setncattr(cf_var, "standard_name", cube.standard_name)
2401-
2402-
if cube.long_name:
2403-
_setncattr(cf_var, "long_name", cube.long_name)
2404-
2405-
if cube.units.is_udunits():
2406-
_setncattr(cf_var, "units", str(cube.units))
2407-
2408-
# Add the CF-netCDF calendar attribute.
2409-
if cube.units.calendar:
2410-
_setncattr(cf_var, "calendar", cube.units.calendar)
24112398

2399+
# if cube.standard_name:
2400+
# _setncattr(cf_var, "standard_name", cube.standard_name)
2401+
#
2402+
# if cube.long_name:
2403+
# _setncattr(cf_var, "long_name", cube.long_name)
2404+
#
2405+
# if cube.units.is_udunits():
2406+
# _setncattr(cf_var, "units", str(cube.units))
2407+
#
2408+
# # Add the CF-netCDF calendar attribute.
2409+
# if cube.units.calendar:
2410+
# _setncattr(cf_var, "calendar", cube.units.calendar)
2411+
2412+
# Set attributes: NB this part is cube-specific (not the same for components)
2413+
# - therefore 'set_cf_var_attributes' doesn't set attributes if element is a Cube
24122414
if iris.FUTURE.save_split_attrs:
24132415
attr_names = cube.attributes.locals.keys()
24142416
else:

0 commit comments

Comments
 (0)