From ca7b179d8fa080cb0d9064ddfa9585b139d9eb9a Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 11 May 2026 18:36:09 +0100 Subject: [PATCH 1/4] Drop range related attributes on load --- esmvalcore/preprocessor/_io.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/esmvalcore/preprocessor/_io.py b/esmvalcore/preprocessor/_io.py index 78914e0f7c..34b3838120 100644 --- a/esmvalcore/preprocessor/_io.py +++ b/esmvalcore/preprocessor/_io.py @@ -42,6 +42,28 @@ } +def _drop_range_attributes(cube: Cube) -> Cube: + """Drop range related attributes from cube and its components.""" + drop_attrs = ( + "actual_range", + "valid_max", + "valid_min", + "valid_range", + ) + cube = cube.copy() + for attr in drop_attrs: + cube.attributes.pop(attr, None) + for coord in cube.dim_coords: + coord.attributes.pop(attr, None) + for aux_coord in cube.aux_coords: + aux_coord.attributes.pop(attr, None) + for cell_measure in cube.cell_measures(): + cell_measure.attributes.pop(attr, None) + for ancillary_variable in cube.ancillary_variables(): + ancillary_variable.attributes.pop(attr, None) + return cube + + def load( file: str | Path @@ -133,7 +155,9 @@ def load( ) warnings.warn(warn_msg, ESMValCoreLoadWarning, stacklevel=2) - return cubes + # Drop range related attributes as these are likely to be + # invalidated by preprocessing the data. + return CubeList(_drop_range_attributes(cube) for cube in cubes) def _load_zarr( From 929f61e2b79b69ececcec476be7b1446c024dfba Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 11 May 2026 22:19:34 +0100 Subject: [PATCH 2/4] Add test --- tests/integration/preprocessor/_io/test_load.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/integration/preprocessor/_io/test_load.py b/tests/integration/preprocessor/_io/test_load.py index 1a9e747f4a..fec3d0b40b 100644 --- a/tests/integration/preprocessor/_io/test_load.py +++ b/tests/integration/preprocessor/_io/test_load.py @@ -39,6 +39,23 @@ def test_load(tmp_path, sample_cube): assert_array_equal(sample_cube.coord("latitude").points, [1, 2]) +def test_load_with_valid_max(tmp_path, sample_cube): + """Test loading multiple files.""" + sample_cube.attributes["valid_max"] = 1 + temp_file = tmp_path / "cube.nc" + iris.save(sample_cube, temp_file) + + cubes = load(temp_file) + + assert len(cubes) == 1 + sample_cube = cubes[0] + assert "valid_max" not in sample_cube.attributes + assert_array_equal( + sample_cube.data, + np.ma.array([1, 2], mask=[False, True]), + ) + + def test_load_grib(): """Test loading a grib file.""" grib_path = ( From 6194723ece4977bba4dd2b1d40da3a2286d019c5 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 11 May 2026 22:43:41 +0100 Subject: [PATCH 3/4] Improve test coverage --- tests/integration/preprocessor/_io/test_load.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/integration/preprocessor/_io/test_load.py b/tests/integration/preprocessor/_io/test_load.py index fec3d0b40b..06692ffd74 100644 --- a/tests/integration/preprocessor/_io/test_load.py +++ b/tests/integration/preprocessor/_io/test_load.py @@ -9,7 +9,7 @@ import numpy as np import pytest import xarray as xr -from iris.coords import DimCoord +from iris.coords import AncillaryVariable, CellMeasure, DimCoord from iris.cube import Cube, CubeList from esmvalcore.exceptions import ESMValCoreLoadWarning @@ -39,9 +39,19 @@ def test_load(tmp_path, sample_cube): assert_array_equal(sample_cube.coord("latitude").points, [1, 2]) -def test_load_with_valid_max(tmp_path, sample_cube): +def test_load_with_valid_max(tmp_path: Path, sample_cube: Cube) -> None: """Test loading multiple files.""" sample_cube.attributes["valid_max"] = 1 + ancillary_var = AncillaryVariable( + [0, 1.1], + standard_name="land_area_fraction", + units="%", + ) + ancillary_var.attributes["valid_max"] = 1 + sample_cube.add_ancillary_variable(ancillary_var, data_dims=[0]) + cell_measure = CellMeasure([1, 1], standard_name="cell_area", units="m2") + cell_measure.attributes["valid_max"] = 1 + sample_cube.add_cell_measure(cell_measure, data_dims=[0]) temp_file = tmp_path / "cube.nc" iris.save(sample_cube, temp_file) From a6897140a0b2e005fe13f0fb7821a826770ca507 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 11 May 2026 22:46:28 +0100 Subject: [PATCH 4/4] Better test --- tests/integration/preprocessor/_io/test_load.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/integration/preprocessor/_io/test_load.py b/tests/integration/preprocessor/_io/test_load.py index 06692ffd74..f672ac39c2 100644 --- a/tests/integration/preprocessor/_io/test_load.py +++ b/tests/integration/preprocessor/_io/test_load.py @@ -39,7 +39,7 @@ def test_load(tmp_path, sample_cube): assert_array_equal(sample_cube.coord("latitude").points, [1, 2]) -def test_load_with_valid_max(tmp_path: Path, sample_cube: Cube) -> None: +def test_load_with_range_attrs(tmp_path: Path, sample_cube: Cube) -> None: """Test loading multiple files.""" sample_cube.attributes["valid_max"] = 1 ancillary_var = AncillaryVariable( @@ -50,7 +50,7 @@ def test_load_with_valid_max(tmp_path: Path, sample_cube: Cube) -> None: ancillary_var.attributes["valid_max"] = 1 sample_cube.add_ancillary_variable(ancillary_var, data_dims=[0]) cell_measure = CellMeasure([1, 1], standard_name="cell_area", units="m2") - cell_measure.attributes["valid_max"] = 1 + cell_measure.attributes["valid_min"] = 1 sample_cube.add_cell_measure(cell_measure, data_dims=[0]) temp_file = tmp_path / "cube.nc" iris.save(sample_cube, temp_file) @@ -64,6 +64,12 @@ def test_load_with_valid_max(tmp_path: Path, sample_cube: Cube) -> None: sample_cube.data, np.ma.array([1, 2], mask=[False, True]), ) + assert "valid_max" not in sample_cube.ancillary_variables()[0].attributes + assert_array_equal( + sample_cube.ancillary_variables()[0].data, + np.ma.array([0, 1.1], mask=[False, True]), + ) + assert "valid_min" not in sample_cube.cell_measures()[0].attributes def test_load_grib():