From 2e5d7b47dc4f0a5e5f8327de5d5e8c3d9707250e Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 11 Nov 2025 12:51:50 +0000 Subject: [PATCH 01/13] Add config switch for OSCAR compliant attributes --- satpy/_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/satpy/_config.py b/satpy/_config.py index fbfcb0c0d5..3883aa13de 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,6 +54,7 @@ "readers": { "clip_negative_radiances": False, }, + "oscar_compliant_attributes": False, } # Satpy main configuration object From 62e44738bb081f55aa16719e5988b52f6a118f53 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Tue, 11 Nov 2025 12:53:25 +0000 Subject: [PATCH 02/13] Make sensor name OSCAR compliant in ABI readers --- satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 18 +----------------- satpy/etc/readers/abi_l2_nc.yaml | 2 +- satpy/readers/core/abi.py | 3 +++ satpy/tests/reader_tests/conftest.py | 8 ++++++++ satpy/tests/reader_tests/test_abi_l1b.py | 13 ++++++++----- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 3f743b0e5d..7016845343 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: [abi] + sensors: ['abi', 'ABI'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index bad924223e..d9203e196a 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -5,7 +5,7 @@ reader: description: SCMI NetCDF4 Reader for ABI data status: Beta supports_fsspec: false - sensors: [] + sensors: ['abi', 'ABI'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # Typical filenames from Unidata THREDDS server: @@ -116,7 +116,6 @@ file_types: datasets: C01: name: C01 - sensor: abi wavelength: [0.450, 0.470, 0.490] resolution: 1000 calibration: @@ -127,7 +126,6 @@ datasets: C02: name: C02 - sensor: abi wavelength: [0.590, 0.640, 0.690] resolution: 500 calibration: @@ -138,7 +136,6 @@ datasets: C03: name: C03 - sensor: abi wavelength: [0.8455, 0.865, 0.8845] resolution: 1000 calibration: @@ -149,7 +146,6 @@ datasets: C04: name: C04 - sensor: abi wavelength: [1.3705, 1.378, 1.3855] resolution: 2000 calibration: @@ -160,7 +156,6 @@ datasets: C05: name: C05 - sensor: abi wavelength: [1.580, 1.610, 1.640] resolution: 1000 calibration: @@ -171,7 +166,6 @@ datasets: C06: name: C06 - sensor: abi wavelength: [2.225, 2.250, 2.275] resolution: 2000 calibration: @@ -182,7 +176,6 @@ datasets: C07: name: C07 - sensor: abi wavelength: [3.80, 3.90, 4.00] resolution: 2000 calibration: @@ -193,7 +186,6 @@ datasets: C08: name: C08 - sensor: abi wavelength: [5.770, 6.185, 6.600] resolution: 2000 calibration: @@ -204,7 +196,6 @@ datasets: C09: name: C09 - sensor: abi wavelength: [6.75, 6.95, 7.15] resolution: 2000 calibration: @@ -215,7 +206,6 @@ datasets: C10: name: C10 - sensor: abi wavelength: [7.24, 7.34, 7.44] resolution: 2000 calibration: @@ -226,7 +216,6 @@ datasets: C11: name: C11 - sensor: abi wavelength: [8.30, 8.50, 8.70] resolution: 2000 calibration: @@ -237,7 +226,6 @@ datasets: C12: name: C12 - sensor: abi wavelength: [9.42, 9.61, 9.80] resolution: 2000 calibration: @@ -248,7 +236,6 @@ datasets: C13: name: C13 - sensor: abi wavelength: [10.10, 10.35, 10.60] resolution: 2000 calibration: @@ -259,7 +246,6 @@ datasets: C14: name: C14 - sensor: abi wavelength: [10.80, 11.20, 11.60] resolution: 2000 calibration: @@ -270,7 +256,6 @@ datasets: C15: name: C15 - sensor: abi wavelength: [11.80, 12.30, 12.80] resolution: 2000 calibration: @@ -281,7 +266,6 @@ datasets: C16: name: C16 - sensor: abi wavelength: [13.00, 13.30, 13.60] resolution: 2000 calibration: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index ff4dec80d7..97a69e7fcc 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: ['abi'] + sensors: ['abi', 'ABI'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index e50cd616c4..2128109338 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -27,6 +27,7 @@ import xarray as xr from pyresample import geometry +import satpy from satpy._compat import cached_property from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename @@ -111,6 +112,8 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" + if satpy.config.get("oscar_compliant_attributes", False): + return "ABI" return "abi" def __getitem__(self, item): diff --git a/satpy/tests/reader_tests/conftest.py b/satpy/tests/reader_tests/conftest.py index 4a551cadc8..4c1fb74438 100644 --- a/satpy/tests/reader_tests/conftest.py +++ b/satpy/tests/reader_tests/conftest.py @@ -28,6 +28,7 @@ from trollsift import compose, parse from xarray import DataTree +import satpy from satpy.readers.mwr_l1b import AWS_EPS_Sterna_MWR_L1BFile from satpy.readers.mwr_l1c import AWS_MWR_L1CFile @@ -232,3 +233,10 @@ def aws_mwr_l1c_handler(aws_mwr_l1c_file): filetype_info["file_type"] = "aws1_mwr_l1c" filetype_info["feed_horn_group_name"] = None return AWS_MWR_L1CFile(aws_mwr_l1c_file, filename_info, filetype_info) + + +@pytest.fixture(params=[True, False]) +def oscar_compliant_attrs(request): + """Set config switch for OSCAR compliant attributes.""" + satpy.config.set(oscar_compliant_attributes=request.param) + return request.param diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index bcb6d35041..6ff07f204f 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -44,7 +44,6 @@ RAD_SHAPE[1000] = (RAD_SHAPE[500][0] // 2, RAD_SHAPE[500][1] // 2) RAD_SHAPE[2000] = (RAD_SHAPE[500][0] // 4, RAD_SHAPE[500][1] // 4) - def _create_fake_rad_dataarray( rad: xr.DataArray | None = None, resolution: int = 2000, @@ -146,7 +145,7 @@ def c01_refl(tmp_path) -> xr.DataArray: @pytest.fixture -def c01_rad(tmp_path) -> xr.DataArray: +def c01_rad(tmp_path, oscar_compliant_attrs) -> xr.DataArray: """Load c01 radiances.""" with _apply_dask_chunk_size(): reader = _create_reader_for_data(tmp_path, "C01", None, 1000) @@ -154,7 +153,7 @@ def c01_rad(tmp_path) -> xr.DataArray: @pytest.fixture -def c01_rad_h5netcdf(tmp_path) -> xr.DataArray: +def c01_rad_h5netcdf(tmp_path, oscar_compliant_attrs) -> xr.DataArray: """Load c01 radiances through h5netcdf.""" shape = RAD_SHAPE[1000] rad_data = (np.arange(shape[0] * shape[1]).reshape(shape) + 1.0) * 50.0 @@ -345,8 +344,12 @@ def test_file_patterns_match(channel, suffix): class Test_NC_ABI_L1B: """Test the NC_ABI_L1B reader.""" - def test_get_dataset(self, c01_data_arr): + def test_get_dataset(self, c01_data_arr, oscar_compliant_attrs): """Test the get_dataset method.""" + sensors = { + True: "ABI", + False: "abi" + } exp = { "calibration": "radiance", "instrument_ID": None, @@ -371,7 +374,7 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "sensor": sensors[oscar_compliant_attrs], "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", From b906749963e6e802df726fc5e76c4d428855f316 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 13 Nov 2025 10:26:20 +0000 Subject: [PATCH 03/13] Use only one sensor name in yaml definition --- satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 18 +++++++++++++++++- satpy/etc/readers/abi_l2_nc.yaml | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 7016845343..1d8c7ce6d3 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: ['abi', 'ABI'] + sensors: ['ABI'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index d9203e196a..f811f35125 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -5,7 +5,7 @@ reader: description: SCMI NetCDF4 Reader for ABI data status: Beta supports_fsspec: false - sensors: ['abi', 'ABI'] + sensors: [] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # Typical filenames from Unidata THREDDS server: @@ -116,6 +116,7 @@ file_types: datasets: C01: name: C01 + sensor: ABI wavelength: [0.450, 0.470, 0.490] resolution: 1000 calibration: @@ -126,6 +127,7 @@ datasets: C02: name: C02 + sensor: ABI wavelength: [0.590, 0.640, 0.690] resolution: 500 calibration: @@ -136,6 +138,7 @@ datasets: C03: name: C03 + sensor: ABI wavelength: [0.8455, 0.865, 0.8845] resolution: 1000 calibration: @@ -146,6 +149,7 @@ datasets: C04: name: C04 + sensor: ABI wavelength: [1.3705, 1.378, 1.3855] resolution: 2000 calibration: @@ -156,6 +160,7 @@ datasets: C05: name: C05 + sensor: ABI wavelength: [1.580, 1.610, 1.640] resolution: 1000 calibration: @@ -166,6 +171,7 @@ datasets: C06: name: C06 + sensor: ABI wavelength: [2.225, 2.250, 2.275] resolution: 2000 calibration: @@ -176,6 +182,7 @@ datasets: C07: name: C07 + sensor: ABI wavelength: [3.80, 3.90, 4.00] resolution: 2000 calibration: @@ -186,6 +193,7 @@ datasets: C08: name: C08 + sensor: ABI wavelength: [5.770, 6.185, 6.600] resolution: 2000 calibration: @@ -196,6 +204,7 @@ datasets: C09: name: C09 + sensor: ABI wavelength: [6.75, 6.95, 7.15] resolution: 2000 calibration: @@ -206,6 +215,7 @@ datasets: C10: name: C10 + sensor: ABI wavelength: [7.24, 7.34, 7.44] resolution: 2000 calibration: @@ -216,6 +226,7 @@ datasets: C11: name: C11 + sensor: ABI wavelength: [8.30, 8.50, 8.70] resolution: 2000 calibration: @@ -226,6 +237,7 @@ datasets: C12: name: C12 + sensor: ABI wavelength: [9.42, 9.61, 9.80] resolution: 2000 calibration: @@ -236,6 +248,7 @@ datasets: C13: name: C13 + sensor: ABI wavelength: [10.10, 10.35, 10.60] resolution: 2000 calibration: @@ -246,6 +259,7 @@ datasets: C14: name: C14 + sensor: ABI wavelength: [10.80, 11.20, 11.60] resolution: 2000 calibration: @@ -256,6 +270,7 @@ datasets: C15: name: C15 + sensor: ABI wavelength: [11.80, 12.30, 12.80] resolution: 2000 calibration: @@ -266,6 +281,7 @@ datasets: C16: name: C16 + sensor: ABI wavelength: [13.00, 13.30, 13.60] resolution: 2000 calibration: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index 97a69e7fcc..310c28a62c 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: ['abi', 'ABI'] + sensors: ['ABI'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] From 562d09c60dec579cd68c9b207393593116c64d3b Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Thu, 13 Nov 2025 10:58:56 +0000 Subject: [PATCH 04/13] Restore lowercase sensor names in yaml --- satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 32 ++++++++++++++--------------- satpy/etc/readers/abi_l2_nc.yaml | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 1d8c7ce6d3..3f743b0e5d 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: ['ABI'] + sensors: [abi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index f811f35125..bad924223e 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -116,7 +116,7 @@ file_types: datasets: C01: name: C01 - sensor: ABI + sensor: abi wavelength: [0.450, 0.470, 0.490] resolution: 1000 calibration: @@ -127,7 +127,7 @@ datasets: C02: name: C02 - sensor: ABI + sensor: abi wavelength: [0.590, 0.640, 0.690] resolution: 500 calibration: @@ -138,7 +138,7 @@ datasets: C03: name: C03 - sensor: ABI + sensor: abi wavelength: [0.8455, 0.865, 0.8845] resolution: 1000 calibration: @@ -149,7 +149,7 @@ datasets: C04: name: C04 - sensor: ABI + sensor: abi wavelength: [1.3705, 1.378, 1.3855] resolution: 2000 calibration: @@ -160,7 +160,7 @@ datasets: C05: name: C05 - sensor: ABI + sensor: abi wavelength: [1.580, 1.610, 1.640] resolution: 1000 calibration: @@ -171,7 +171,7 @@ datasets: C06: name: C06 - sensor: ABI + sensor: abi wavelength: [2.225, 2.250, 2.275] resolution: 2000 calibration: @@ -182,7 +182,7 @@ datasets: C07: name: C07 - sensor: ABI + sensor: abi wavelength: [3.80, 3.90, 4.00] resolution: 2000 calibration: @@ -193,7 +193,7 @@ datasets: C08: name: C08 - sensor: ABI + sensor: abi wavelength: [5.770, 6.185, 6.600] resolution: 2000 calibration: @@ -204,7 +204,7 @@ datasets: C09: name: C09 - sensor: ABI + sensor: abi wavelength: [6.75, 6.95, 7.15] resolution: 2000 calibration: @@ -215,7 +215,7 @@ datasets: C10: name: C10 - sensor: ABI + sensor: abi wavelength: [7.24, 7.34, 7.44] resolution: 2000 calibration: @@ -226,7 +226,7 @@ datasets: C11: name: C11 - sensor: ABI + sensor: abi wavelength: [8.30, 8.50, 8.70] resolution: 2000 calibration: @@ -237,7 +237,7 @@ datasets: C12: name: C12 - sensor: ABI + sensor: abi wavelength: [9.42, 9.61, 9.80] resolution: 2000 calibration: @@ -248,7 +248,7 @@ datasets: C13: name: C13 - sensor: ABI + sensor: abi wavelength: [10.10, 10.35, 10.60] resolution: 2000 calibration: @@ -259,7 +259,7 @@ datasets: C14: name: C14 - sensor: ABI + sensor: abi wavelength: [10.80, 11.20, 11.60] resolution: 2000 calibration: @@ -270,7 +270,7 @@ datasets: C15: name: C15 - sensor: ABI + sensor: abi wavelength: [11.80, 12.30, 12.80] resolution: 2000 calibration: @@ -281,7 +281,7 @@ datasets: C16: name: C16 - sensor: ABI + sensor: abi wavelength: [13.00, 13.30, 13.60] resolution: 2000 calibration: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index 310c28a62c..ff4dec80d7 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: ['ABI'] + sensors: ['abi'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] From 0881614a6f72b6249c27c3a059bdd6989ab18144 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 14 Nov 2025 11:42:04 +0000 Subject: [PATCH 05/13] Add decorator for getting legacy attributes back --- satpy/composites/config_loader.py | 8 +++- satpy/etc/composites/abi.yaml | 5 ++- satpy/etc/enhancements/abi.yaml | 24 +++++------ satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 32 +++++++------- satpy/etc/readers/abi_l2_nc.yaml | 2 +- satpy/modifiers/atmosphere.py | 4 +- satpy/modifiers/spectral.py | 2 +- satpy/readers/core/abi.py | 5 +-- satpy/readers/core/yaml_reader.py | 66 +++++++++++++++++++++++++++-- 10 files changed, 105 insertions(+), 45 deletions(-) diff --git a/satpy/composites/config_loader.py b/satpy/composites/config_loader.py index e5165ada01..916b94c835 100644 --- a/satpy/composites/config_loader.py +++ b/satpy/composites/config_loader.py @@ -192,7 +192,7 @@ def _load_config(composite_configs): sensor_modifiers = {} dep_id_keys = None - sensor_deps = sensor_name.split("/")[:-1] + sensor_deps = conf.get("dependencies", sensor_name.split("/")[:-1]) if sensor_deps: # get dependent for sensor_dep in sensor_deps: @@ -246,6 +246,10 @@ def _update_cached_wrapper(wrapper, cached_func): return wrapper +def sensor_to_filename(sensor_name: str) -> str: + """Get filename tag for the given sensor.""" + return sensor_name.lower().replace("-", "").replace(" ", "_").replace("/", "-") + @_lru_cache_with_config_path def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict], dict[str, dict], dict]: """Load compositor, modifier, and DataID key information from configuration files for the specified sensor. @@ -268,7 +272,7 @@ def load_compositor_configs_for_sensor(sensor_name: str) -> tuple[dict[str, dict DataID key -> key properties """ - config_filename = sensor_name + ".yaml" + config_filename = sensor_to_filename(sensor_name) + ".yaml" logger.debug("Looking for composites config file %s", config_filename) paths = get_entry_points_config_dirs("satpy.composites") composite_configs = config_search_paths( diff --git a/satpy/etc/composites/abi.yaml b/satpy/etc/composites/abi.yaml index 59b4fe28f0..f96d5b8ca6 100644 --- a/satpy/etc/composites/abi.yaml +++ b/satpy/etc/composites/abi.yaml @@ -1,5 +1,6 @@ -sensor_name: visir/abi - +sensor_name: ABI +dependencies: + - visir modifiers: rayleigh_corrected_crefl: modifier: !!python/name:satpy.modifiers.atmosphere.ReflectanceCorrector diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index 9920f842a3..7ffdf3b7a5 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - sensor: abi + sensor: ABI operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - sensor: abi + sensor: ABI operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - sensor: abi + sensor: ABI operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 3f743b0e5d..052e75695f 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: [abi] + sensors: [ABI] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index bad924223e..f811f35125 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -116,7 +116,7 @@ file_types: datasets: C01: name: C01 - sensor: abi + sensor: ABI wavelength: [0.450, 0.470, 0.490] resolution: 1000 calibration: @@ -127,7 +127,7 @@ datasets: C02: name: C02 - sensor: abi + sensor: ABI wavelength: [0.590, 0.640, 0.690] resolution: 500 calibration: @@ -138,7 +138,7 @@ datasets: C03: name: C03 - sensor: abi + sensor: ABI wavelength: [0.8455, 0.865, 0.8845] resolution: 1000 calibration: @@ -149,7 +149,7 @@ datasets: C04: name: C04 - sensor: abi + sensor: ABI wavelength: [1.3705, 1.378, 1.3855] resolution: 2000 calibration: @@ -160,7 +160,7 @@ datasets: C05: name: C05 - sensor: abi + sensor: ABI wavelength: [1.580, 1.610, 1.640] resolution: 1000 calibration: @@ -171,7 +171,7 @@ datasets: C06: name: C06 - sensor: abi + sensor: ABI wavelength: [2.225, 2.250, 2.275] resolution: 2000 calibration: @@ -182,7 +182,7 @@ datasets: C07: name: C07 - sensor: abi + sensor: ABI wavelength: [3.80, 3.90, 4.00] resolution: 2000 calibration: @@ -193,7 +193,7 @@ datasets: C08: name: C08 - sensor: abi + sensor: ABI wavelength: [5.770, 6.185, 6.600] resolution: 2000 calibration: @@ -204,7 +204,7 @@ datasets: C09: name: C09 - sensor: abi + sensor: ABI wavelength: [6.75, 6.95, 7.15] resolution: 2000 calibration: @@ -215,7 +215,7 @@ datasets: C10: name: C10 - sensor: abi + sensor: ABI wavelength: [7.24, 7.34, 7.44] resolution: 2000 calibration: @@ -226,7 +226,7 @@ datasets: C11: name: C11 - sensor: abi + sensor: ABI wavelength: [8.30, 8.50, 8.70] resolution: 2000 calibration: @@ -237,7 +237,7 @@ datasets: C12: name: C12 - sensor: abi + sensor: ABI wavelength: [9.42, 9.61, 9.80] resolution: 2000 calibration: @@ -248,7 +248,7 @@ datasets: C13: name: C13 - sensor: abi + sensor: ABI wavelength: [10.10, 10.35, 10.60] resolution: 2000 calibration: @@ -259,7 +259,7 @@ datasets: C14: name: C14 - sensor: abi + sensor: ABI wavelength: [10.80, 11.20, 11.60] resolution: 2000 calibration: @@ -270,7 +270,7 @@ datasets: C15: name: C15 - sensor: abi + sensor: ABI wavelength: [11.80, 12.30, 12.80] resolution: 2000 calibration: @@ -281,7 +281,7 @@ datasets: C16: name: C16 - sensor: abi + sensor: ABI wavelength: [13.00, 13.30, 13.60] resolution: 2000 calibration: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index ff4dec80d7..6db58e8dff 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: ['abi'] + sensors: [ABI] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index c7144c27ca..bce18180c7 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -104,7 +104,7 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["sensor"], + corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["sensor"].lower(), atmosphere=atmosphere, aerosol_type=aerosol_type) @@ -159,7 +159,7 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Correction for limb cooling") corrector = AtmosphericalCorrection(band.attrs["platform_name"], - band.attrs["sensor"]) + band.attrs["sensor"].lower()) atm_corr = da.map_blocks(_call_mapped_correction, satz, band.data, corrector=corrector, diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index 402b5606d4..ed99935f15 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -132,7 +132,7 @@ def _init_reflectance_calculator(self, metadata): logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["sensor"], metadata["name"], + reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["sensor"].lower(), metadata["name"], sunz_threshold=self.sun_zenith_threshold, masking_limit=self.masking_limit) return reflectance_3x_calculator diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index 2128109338..d51764dc38 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -27,7 +27,6 @@ import xarray as xr from pyresample import geometry -import satpy from satpy._compat import cached_property from satpy.readers.core.file_handlers import BaseFileHandler from satpy.readers.core.remote import open_file_or_filename @@ -112,9 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - if satpy.config.get("oscar_compliant_attributes", False): - return "ABI" - return "abi" + return "ABI" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 1982aac346..13e14eb74d 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -47,6 +47,7 @@ from satpy.coords import add_crs_xy_coords from satpy.dataset import DataID, DataQuery, get_key from satpy.dataset.dataid import default_co_keys_config, default_id_keys_config, get_keys_from_config +from satpy.readers.core.file_handlers import BaseFileHandler from satpy.utils import recursive_dict_update logger = logging.getLogger(__name__) @@ -483,6 +484,7 @@ def __init__(self, self.file_handlers = {} self.available_ids = {} self.register_data_files() + self.attribute_composer = AttributeComposer(self.name) @property def sensor_names(self): @@ -737,10 +739,8 @@ def _load_dataset_data(self, file_handlers, dsid, **kwargs): ds_info = self.all_ids[dsid] proj = self._load_dataset(dsid, ds_info, file_handlers, **kwargs) # FIXME: areas could be concatenated here - # Update the metadata - proj.attrs["start_time"] = file_handlers[0].start_time - proj.attrs["end_time"] = file_handlers[-1].end_time - proj.attrs["reader"] = self.name + attrs = self.attribute_composer.get_attrs(proj, file_handlers) + proj.attrs.update(attrs) return proj def _preferred_filetype(self, filetypes): @@ -985,6 +985,64 @@ def _get_coordinates_for_dataset_key(self, dsid): return cids +# 8< v1.0 +LEGACY_SENSORS = { + "ABI": "abi" +} +LEGACY_PLATFORMS: dict[str, str] = {} + +from functools import wraps # noqa: E402 + +import satpy # noqa: E402 + + +def with_legacy_attributes(func): + """Adds option to get legacy attributes back.""" + @wraps(func) + def translate_attrs(*args, **kwargs): + attrs = func(*args, **kwargs) + if not satpy.config.get("oscar_compliant_attributes", False): + _make_legacy_attrs(attrs) + return attrs + return translate_attrs + +def _make_legacy_attrs(attrs: dict) -> None: + sensor = attrs.get("sensor") + if sensor and sensor in LEGACY_SENSORS: + attrs["sensor"] = LEGACY_SENSORS[sensor] + platform = attrs.get("platform") + if platform and platform in LEGACY_PLATFORMS: + attrs["platform_name"] = LEGACY_PLATFORMS[platform] +# >8 v1.0 + +class AttributeComposer: + """Compose dataset attributes.""" + + def __init__(self, reader_name: str) -> None: + """Initialize the composer.""" + self.reader_name = reader_name + + def get_attrs(self, data: xr.DataArray, file_handlers: list[BaseFileHandler]) -> dict: + """Get dataset attributes.""" + attrs = { + "start_time": file_handlers[0].start_time, + "end_time": file_handlers[-1].end_time, + "reader": self.reader_name, + } + return attrs | self._get_platform_sensor(data) + + # 8< v1.0 + @with_legacy_attributes + # >8 v1.0 + def _get_platform_sensor(self, data: xr.DataArray) -> dict: + keys = ["platform_name", "sensor"] + return { + key: value + for key in keys + if (value := data.attrs[key]) + } + + def _load_area_def(dsid, file_handlers): """Load the area definition of *dsid*.""" area_defs = [fh.get_area_def(dsid) for fh in file_handlers] From 25182bdffe61a3f41ae322c28c1fcb4c3a95a3d0 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 14 Nov 2025 11:44:27 +0000 Subject: [PATCH 06/13] Restore tests --- satpy/tests/reader_tests/conftest.py | 12 +++++------- satpy/tests/reader_tests/test_abi_l1b.py | 13 +++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/satpy/tests/reader_tests/conftest.py b/satpy/tests/reader_tests/conftest.py index 4c1fb74438..e87905b4fb 100644 --- a/satpy/tests/reader_tests/conftest.py +++ b/satpy/tests/reader_tests/conftest.py @@ -28,7 +28,6 @@ from trollsift import compose, parse from xarray import DataTree -import satpy from satpy.readers.mwr_l1b import AWS_EPS_Sterna_MWR_L1BFile from satpy.readers.mwr_l1c import AWS_MWR_L1CFile @@ -234,9 +233,8 @@ def aws_mwr_l1c_handler(aws_mwr_l1c_file): filetype_info["feed_horn_group_name"] = None return AWS_MWR_L1CFile(aws_mwr_l1c_file, filename_info, filetype_info) - -@pytest.fixture(params=[True, False]) -def oscar_compliant_attrs(request): - """Set config switch for OSCAR compliant attributes.""" - satpy.config.set(oscar_compliant_attributes=request.param) - return request.param +@pytest.fixture(scope="module", autouse=True) +def oscar_compliant_attrs(): + """Request oscar compliant dataset attributes.""" + import satpy + satpy.config.set(oscar_compliant_attributes=True) diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index 6ff07f204f..5db4081128 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -44,6 +44,7 @@ RAD_SHAPE[1000] = (RAD_SHAPE[500][0] // 2, RAD_SHAPE[500][1] // 2) RAD_SHAPE[2000] = (RAD_SHAPE[500][0] // 4, RAD_SHAPE[500][1] // 4) + def _create_fake_rad_dataarray( rad: xr.DataArray | None = None, resolution: int = 2000, @@ -145,7 +146,7 @@ def c01_refl(tmp_path) -> xr.DataArray: @pytest.fixture -def c01_rad(tmp_path, oscar_compliant_attrs) -> xr.DataArray: +def c01_rad(tmp_path) -> xr.DataArray: """Load c01 radiances.""" with _apply_dask_chunk_size(): reader = _create_reader_for_data(tmp_path, "C01", None, 1000) @@ -153,7 +154,7 @@ def c01_rad(tmp_path, oscar_compliant_attrs) -> xr.DataArray: @pytest.fixture -def c01_rad_h5netcdf(tmp_path, oscar_compliant_attrs) -> xr.DataArray: +def c01_rad_h5netcdf(tmp_path) -> xr.DataArray: """Load c01 radiances through h5netcdf.""" shape = RAD_SHAPE[1000] rad_data = (np.arange(shape[0] * shape[1]).reshape(shape) + 1.0) * 50.0 @@ -344,12 +345,8 @@ def test_file_patterns_match(channel, suffix): class Test_NC_ABI_L1B: """Test the NC_ABI_L1B reader.""" - def test_get_dataset(self, c01_data_arr, oscar_compliant_attrs): + def test_get_dataset(self, c01_data_arr): """Test the get_dataset method.""" - sensors = { - True: "ABI", - False: "abi" - } exp = { "calibration": "radiance", "instrument_ID": None, @@ -374,7 +371,7 @@ def test_get_dataset(self, c01_data_arr, oscar_compliant_attrs): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": sensors[oscar_compliant_attrs], + "sensor": "ABI", "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", From a40e7c1473b2f451f6c34d6e03ecd67cf3cd2d54 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 17 Nov 2025 07:31:27 +0000 Subject: [PATCH 07/13] Fix getting attribute --- satpy/readers/core/yaml_reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 13e14eb74d..589984cf51 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -1039,7 +1039,7 @@ def _get_platform_sensor(self, data: xr.DataArray) -> dict: return { key: value for key in keys - if (value := data.attrs[key]) + if (value := data.attrs.get(key)) } From c711355dcf6a7c5852d71328b2d6711e3461f0b8 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Mon, 17 Nov 2025 09:16:41 +0000 Subject: [PATCH 08/13] Fix test --- satpy/tests/reader_tests/test_abi_l2_nc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/satpy/tests/reader_tests/test_abi_l2_nc.py b/satpy/tests/reader_tests/test_abi_l2_nc.py index 0f7132f9b7..53c90ac57d 100644 --- a/satpy/tests/reader_tests/test_abi_l2_nc.py +++ b/satpy/tests/reader_tests/test_abi_l2_nc.py @@ -160,7 +160,7 @@ def test_get_dataset(self, obs_type, ds_func, var_name, var_attrs, fh_kwargs): "scan_mode": "M3", "scene_abbr": "C", "scene_id": None, - "sensor": "abi", + "sensor": "ABI", "timeline_ID": None, } exp_attrs.update(var_attrs) @@ -222,7 +222,7 @@ def test_mcmip_get_dataset(self, xr_, product, exp_metadata): "scan_mode": "M6", "scene_abbr": "F", "scene_id": None, - "sensor": "abi", + "sensor": "ABI", "timeline_ID": None, "start_time": dt.datetime(2017, 9, 20, 17, 30, 40, 800000), "end_time": dt.datetime(2017, 9, 20, 17, 41, 17, 500000), From e843ee16ab85134cc9376571475f11584d534713 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 10 Apr 2026 13:27:10 +0000 Subject: [PATCH 09/13] Revert ABI changes --- satpy/etc/composites/abi.yaml | 4 +-- satpy/etc/enhancements/abi.yaml | 24 ++++++++-------- satpy/etc/readers/abi_l1b.yaml | 2 +- satpy/etc/readers/abi_l1b_scmi.yaml | 32 +++++++++++----------- satpy/etc/readers/abi_l2_nc.yaml | 2 +- satpy/readers/core/abi.py | 2 +- satpy/tests/reader_tests/test_abi_l1b.py | 2 +- satpy/tests/reader_tests/test_abi_l2_nc.py | 4 +-- 8 files changed, 35 insertions(+), 37 deletions(-) diff --git a/satpy/etc/composites/abi.yaml b/satpy/etc/composites/abi.yaml index f96d5b8ca6..982766a43b 100644 --- a/satpy/etc/composites/abi.yaml +++ b/satpy/etc/composites/abi.yaml @@ -1,6 +1,4 @@ -sensor_name: ABI -dependencies: - - visir +sensor_name: visir/abi modifiers: rayleigh_corrected_crefl: modifier: !!python/name:satpy.modifiers.atmosphere.ReflectanceCorrector diff --git a/satpy/etc/enhancements/abi.yaml b/satpy/etc/enhancements/abi.yaml index 7ffdf3b7a5..9920f842a3 100644 --- a/satpy/etc/enhancements/abi.yaml +++ b/satpy/etc/enhancements/abi.yaml @@ -1,7 +1,7 @@ enhancements: cimss_true_color: standard_name: cimss_true_color - sensor: ABI + sensor: abi operations: - name: linear_stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -35,7 +35,7 @@ enhancements: true_color_with_night_fires: standard_name: true_color_with_night_fires - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -77,7 +77,7 @@ enhancements: ash_abi: ## RGB Ash recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf standard_name: ash - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -89,7 +89,7 @@ enhancements: dust_abi: ## RGB Dust recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/Dust_RGB_Quick_Guide.pdf standard_name: dust - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -105,7 +105,7 @@ enhancements: convection_abi: ## RGB Convection recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayConvectionRGB_final.pdf standard_name: convection - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -142,7 +142,7 @@ enhancements: night_microphysics_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -154,7 +154,7 @@ enhancements: night_microphysics_tropical_abi: ## RGB Nighttime Microphysics recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_NtMicroRGB_final.pdf standard_name: night_microphysics_tropical - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -170,7 +170,7 @@ enhancements: land_cloud_fire: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_DayLandCloudFireRGB_final.pdf standard_name: land_cloud_fire - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -179,7 +179,7 @@ enhancements: land_cloud: ## RGB Day Land Cloud Fire recipe source: http://rammb.cira.colostate.edu/training/visit/quick_guides/QuickGuide_GOESR_daylandcloudRGB_final.pdf standard_name: land_cloud - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -191,7 +191,7 @@ enhancements: # IR with white clouds highlighted_brightness_temperature: standard_name: highlighted_toa_brightness_temperature - sensor: ABI + sensor: abi operations: - name: btemp_threshold method: !!python/name:satpy.enhancements.contrast.btemp_threshold @@ -308,7 +308,7 @@ enhancements: ## RGB Recipe: https://rammb2.cira.colostate.edu/wp-content/uploads/2024/11/GOES-BlowingSnowRGB1_QuickGuide_24April2024.pdf ## Modified to match recommendations from RGB Workshop 2025 standard_name: day_blowing_snow - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch @@ -323,7 +323,7 @@ enhancements: day_cloud_type: # Recipe PDF: http://cimss.ssec.wisc.edu/goes/OCLOFactSheetPDFs/ABIQuickGuide_Day_Cloud_Type_RGB.pdf standard_name: day_cloud_type - sensor: ABI + sensor: abi operations: - name: stretch method: !!python/name:satpy.enhancements.contrast.stretch diff --git a/satpy/etc/readers/abi_l1b.yaml b/satpy/etc/readers/abi_l1b.yaml index 052e75695f..3f743b0e5d 100644 --- a/satpy/etc/readers/abi_l1b.yaml +++ b/satpy/etc/readers/abi_l1b.yaml @@ -14,7 +14,7 @@ reader: `here `_. status: Nominal supports_fsspec: true - sensors: [ABI] + sensors: [abi] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/etc/readers/abi_l1b_scmi.yaml b/satpy/etc/readers/abi_l1b_scmi.yaml index f811f35125..bad924223e 100644 --- a/satpy/etc/readers/abi_l1b_scmi.yaml +++ b/satpy/etc/readers/abi_l1b_scmi.yaml @@ -116,7 +116,7 @@ file_types: datasets: C01: name: C01 - sensor: ABI + sensor: abi wavelength: [0.450, 0.470, 0.490] resolution: 1000 calibration: @@ -127,7 +127,7 @@ datasets: C02: name: C02 - sensor: ABI + sensor: abi wavelength: [0.590, 0.640, 0.690] resolution: 500 calibration: @@ -138,7 +138,7 @@ datasets: C03: name: C03 - sensor: ABI + sensor: abi wavelength: [0.8455, 0.865, 0.8845] resolution: 1000 calibration: @@ -149,7 +149,7 @@ datasets: C04: name: C04 - sensor: ABI + sensor: abi wavelength: [1.3705, 1.378, 1.3855] resolution: 2000 calibration: @@ -160,7 +160,7 @@ datasets: C05: name: C05 - sensor: ABI + sensor: abi wavelength: [1.580, 1.610, 1.640] resolution: 1000 calibration: @@ -171,7 +171,7 @@ datasets: C06: name: C06 - sensor: ABI + sensor: abi wavelength: [2.225, 2.250, 2.275] resolution: 2000 calibration: @@ -182,7 +182,7 @@ datasets: C07: name: C07 - sensor: ABI + sensor: abi wavelength: [3.80, 3.90, 4.00] resolution: 2000 calibration: @@ -193,7 +193,7 @@ datasets: C08: name: C08 - sensor: ABI + sensor: abi wavelength: [5.770, 6.185, 6.600] resolution: 2000 calibration: @@ -204,7 +204,7 @@ datasets: C09: name: C09 - sensor: ABI + sensor: abi wavelength: [6.75, 6.95, 7.15] resolution: 2000 calibration: @@ -215,7 +215,7 @@ datasets: C10: name: C10 - sensor: ABI + sensor: abi wavelength: [7.24, 7.34, 7.44] resolution: 2000 calibration: @@ -226,7 +226,7 @@ datasets: C11: name: C11 - sensor: ABI + sensor: abi wavelength: [8.30, 8.50, 8.70] resolution: 2000 calibration: @@ -237,7 +237,7 @@ datasets: C12: name: C12 - sensor: ABI + sensor: abi wavelength: [9.42, 9.61, 9.80] resolution: 2000 calibration: @@ -248,7 +248,7 @@ datasets: C13: name: C13 - sensor: ABI + sensor: abi wavelength: [10.10, 10.35, 10.60] resolution: 2000 calibration: @@ -259,7 +259,7 @@ datasets: C14: name: C14 - sensor: ABI + sensor: abi wavelength: [10.80, 11.20, 11.60] resolution: 2000 calibration: @@ -270,7 +270,7 @@ datasets: C15: name: C15 - sensor: ABI + sensor: abi wavelength: [11.80, 12.30, 12.80] resolution: 2000 calibration: @@ -281,7 +281,7 @@ datasets: C16: name: C16 - sensor: ABI + sensor: abi wavelength: [13.00, 13.30, 13.60] resolution: 2000 calibration: diff --git a/satpy/etc/readers/abi_l2_nc.yaml b/satpy/etc/readers/abi_l2_nc.yaml index 6db58e8dff..ff4dec80d7 100644 --- a/satpy/etc/readers/abi_l2_nc.yaml +++ b/satpy/etc/readers/abi_l2_nc.yaml @@ -9,7 +9,7 @@ reader: `here `_. status: Beta supports_fsspec: true - sensors: [ABI] + sensors: ['abi'] reader: !!python/name:satpy.readers.core.yaml_reader.FileYAMLReader # file pattern keys to sort files by with 'satpy.utils.group_files' group_keys: ['start_time', 'platform_shortname', 'scene_abbr'] diff --git a/satpy/readers/core/abi.py b/satpy/readers/core/abi.py index d51764dc38..e50cd616c4 100644 --- a/satpy/readers/core/abi.py +++ b/satpy/readers/core/abi.py @@ -111,7 +111,7 @@ def _rename_dims(nc): @property def sensor(self): """Get sensor name for current file handler.""" - return "ABI" + return "abi" def __getitem__(self, item): """Wrap `self.nc[item]` for better floating point precision. diff --git a/satpy/tests/reader_tests/test_abi_l1b.py b/satpy/tests/reader_tests/test_abi_l1b.py index 5db4081128..bcb6d35041 100644 --- a/satpy/tests/reader_tests/test_abi_l1b.py +++ b/satpy/tests/reader_tests/test_abi_l1b.py @@ -371,7 +371,7 @@ def test_get_dataset(self, c01_data_arr): "scan_mode": "M4", "scene_abbr": "C", "scene_id": None, - "sensor": "ABI", + "sensor": "abi", "timeline_ID": None, "suffix": "suffix", "units": "W m-2 um-1 sr-1", diff --git a/satpy/tests/reader_tests/test_abi_l2_nc.py b/satpy/tests/reader_tests/test_abi_l2_nc.py index 53c90ac57d..0f7132f9b7 100644 --- a/satpy/tests/reader_tests/test_abi_l2_nc.py +++ b/satpy/tests/reader_tests/test_abi_l2_nc.py @@ -160,7 +160,7 @@ def test_get_dataset(self, obs_type, ds_func, var_name, var_attrs, fh_kwargs): "scan_mode": "M3", "scene_abbr": "C", "scene_id": None, - "sensor": "ABI", + "sensor": "abi", "timeline_ID": None, } exp_attrs.update(var_attrs) @@ -222,7 +222,7 @@ def test_mcmip_get_dataset(self, xr_, product, exp_metadata): "scan_mode": "M6", "scene_abbr": "F", "scene_id": None, - "sensor": "ABI", + "sensor": "abi", "timeline_ID": None, "start_time": dt.datetime(2017, 9, 20, 17, 30, 40, 800000), "end_time": dt.datetime(2017, 9, 20, 17, 41, 17, 500000), From fa82f534b33091ac77c406432ecb2f7f14eb3647 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 10 Apr 2026 13:35:38 +0000 Subject: [PATCH 10/13] Add more v1.0 scissors --- satpy/_config.py | 2 ++ satpy/tests/reader_tests/conftest.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/satpy/_config.py b/satpy/_config.py index 3883aa13de..6fb1a62451 100644 --- a/satpy/_config.py +++ b/satpy/_config.py @@ -54,7 +54,9 @@ "readers": { "clip_negative_radiances": False, }, + # 8< v1.0 "oscar_compliant_attributes": False, + # >8 v1.0 } # Satpy main configuration object diff --git a/satpy/tests/reader_tests/conftest.py b/satpy/tests/reader_tests/conftest.py index e87905b4fb..49dd495d46 100644 --- a/satpy/tests/reader_tests/conftest.py +++ b/satpy/tests/reader_tests/conftest.py @@ -233,8 +233,10 @@ def aws_mwr_l1c_handler(aws_mwr_l1c_file): filetype_info["feed_horn_group_name"] = None return AWS_MWR_L1CFile(aws_mwr_l1c_file, filename_info, filetype_info) +# 8< v1.0 @pytest.fixture(scope="module", autouse=True) def oscar_compliant_attrs(): """Request oscar compliant dataset attributes.""" import satpy - satpy.config.set(oscar_compliant_attributes=True) + satpy.config.set(oscar_compliant_attributes=False) +# >8 v1.0 From 28c6778c95d1f47bb0795486d8b5ae4ac2c820a1 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 10 Apr 2026 13:55:13 +0000 Subject: [PATCH 11/13] Add method for converting sensor name --- satpy/modifiers/atmosphere.py | 7 +++++-- satpy/modifiers/base.py | 5 +++++ satpy/modifiers/spectral.py | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/satpy/modifiers/atmosphere.py b/satpy/modifiers/atmosphere.py index bce18180c7..655a489632 100644 --- a/satpy/modifiers/atmosphere.py +++ b/satpy/modifiers/atmosphere.py @@ -26,6 +26,7 @@ from satpy.modifiers import ModifierBase from satpy.modifiers._crefl import ReflectanceCorrector # noqa from satpy.modifiers.angles import compute_relative_azimuth, get_angles, get_satellite_zenith_angle +from satpy.modifiers.base import _convert_to_pyspectral logger = logging.getLogger(__name__) @@ -104,7 +105,8 @@ def __call__(self, projectables, optional_datasets=None, **info): logger.info("Removing Rayleigh scattering with atmosphere '%s' and " "aerosol type '%s' for '%s'", atmosphere, aerosol_type, vis.attrs["name"]) - corrector = Rayleigh(vis.attrs["platform_name"], vis.attrs["sensor"].lower(), + sensor = _convert_to_pyspectral(vis.attrs["sensor"]) + corrector = Rayleigh(vis.attrs["platform_name"], sensor, atmosphere=atmosphere, aerosol_type=aerosol_type) @@ -158,8 +160,9 @@ def __call__(self, projectables, optional_datasets=None, **info): satz = satz.data # get dask array underneath logger.info("Correction for limb cooling") + sensor = _convert_to_pyspectral(band.attrs["sensor"]) corrector = AtmosphericalCorrection(band.attrs["platform_name"], - band.attrs["sensor"].lower()) + sensor) atm_corr = da.map_blocks(_call_mapped_correction, satz, band.data, corrector=corrector, diff --git a/satpy/modifiers/base.py b/satpy/modifiers/base.py index 06c561db7e..8170d73460 100644 --- a/satpy/modifiers/base.py +++ b/satpy/modifiers/base.py @@ -38,3 +38,8 @@ class ModifierBase(CompositeBase): def __call__(self, datasets, optional_datasets=None, **info): """Generate a modified copy of the first provided dataset.""" raise NotImplementedError() + + +def _convert_to_pyspectral(sensor: str) -> str: + """Convert sensor name to format expected by pyspectral.""" + return sensor.lower() diff --git a/satpy/modifiers/spectral.py b/satpy/modifiers/spectral.py index ed99935f15..bf8925461d 100644 --- a/satpy/modifiers/spectral.py +++ b/satpy/modifiers/spectral.py @@ -22,6 +22,7 @@ import xarray as xr from satpy.modifiers import ModifierBase +from satpy.modifiers.base import _convert_to_pyspectral try: from pyspectral.near_infrared_reflectance import Calculator @@ -132,7 +133,8 @@ def _init_reflectance_calculator(self, metadata): logger.info("Couldn't load pyspectral") raise ImportError("No module named pyspectral.near_infrared_reflectance") - reflectance_3x_calculator = Calculator(metadata["platform_name"], metadata["sensor"].lower(), metadata["name"], + sensor = _convert_to_pyspectral(metadata["sensor"]) + reflectance_3x_calculator = Calculator(metadata["platform_name"], sensor, metadata["name"], sunz_threshold=self.sun_zenith_threshold, masking_limit=self.masking_limit) return reflectance_3x_calculator From dee2430e82bb06bac0c0be3ed94a8ab928ded070 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 10 Apr 2026 14:35:45 +0000 Subject: [PATCH 12/13] Add support for set-type sensor attributes --- satpy/readers/core/yaml_reader.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 589984cf51..0d27fac79c 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -1008,13 +1008,28 @@ def translate_attrs(*args, **kwargs): def _make_legacy_attrs(attrs: dict) -> None: sensor = attrs.get("sensor") - if sensor and sensor in LEGACY_SENSORS: - attrs["sensor"] = LEGACY_SENSORS[sensor] + if sensor: + attrs["sensor"] = _get_legacy_sensor(sensor) platform = attrs.get("platform") - if platform and platform in LEGACY_PLATFORMS: - attrs["platform_name"] = LEGACY_PLATFORMS[platform] + if platform: + attrs["platform_name"] = _get_legacy_platform(platform) + + +def _get_legacy_sensor(sensor: str|set[str]) -> str|set[str]: + if isinstance(sensor, set): + # TODO: Should all readers provide sensor as string? + return { + LEGACY_SENSORS.get(s, s) + for s in sensor + } + return LEGACY_SENSORS.get(sensor, sensor) + + +def _get_legacy_platform(platform: str) -> str: + return LEGACY_PLATFORMS.get(platform, platform) # >8 v1.0 + class AttributeComposer: """Compose dataset attributes.""" From fbf682781496cc2e00a3911cbdaa9d04d9e3c2f3 Mon Sep 17 00:00:00 2001 From: Stephan Finkensieper Date: Fri, 10 Apr 2026 14:56:45 +0000 Subject: [PATCH 13/13] Remove TODO comment --- satpy/readers/core/yaml_reader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/satpy/readers/core/yaml_reader.py b/satpy/readers/core/yaml_reader.py index 0d27fac79c..533f1f2210 100644 --- a/satpy/readers/core/yaml_reader.py +++ b/satpy/readers/core/yaml_reader.py @@ -1017,7 +1017,6 @@ def _make_legacy_attrs(attrs: dict) -> None: def _get_legacy_sensor(sensor: str|set[str]) -> str|set[str]: if isinstance(sensor, set): - # TODO: Should all readers provide sensor as string? return { LEGACY_SENSORS.get(s, s) for s in sensor