From b4009bce45a21753a683814f64bc689e365edfdc Mon Sep 17 00:00:00 2001 From: karpob Date: Fri, 6 Jun 2025 16:40:20 -0400 Subject: [PATCH 1/6] safety fixes and speedup for gsi --- src/eva/data/gsi_obs_space.py | 148 +++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 38 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index 2c29d203..5d45d38b 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -20,25 +20,6 @@ # -------------------------------------------------------------------------------------------------- -def all_equal(iterable): - - """ - Check if all elements in an iterable are equal. - - Args: - iterable: An iterable object to check. - - Returns: - bool: True if all elements are equal, False otherwise. - """ - - g = groupby(iterable) - return next(g, True) and not next(g, False) - - -# -------------------------------------------------------------------------------------------------- - - def uv(group_vars): """ @@ -112,30 +93,119 @@ def subset_channels(ds, channels, logger, add_channels_variable=False): # -------------------------------------------------------------------------------------------------- -def satellite_dataset(ds): +def satellite_dataset(ds, group_vars): """ Build a new dataset to reshape satellite data. Args: ds (Dataset): The input xarray Dataset. - + group_vars: all or use selected group_vars Returns: Dataset: Reshaped xarray Dataset. """ - nchans = ds.dims['nchans'] - iters = int(ds.dims['nobs']/nchans) + nchans = ds.sizes['nchans'] + iters = int(ds.sizes['nobs']/nchans) coords = { 'nchans': (('nchans'), ds['sensor_chan'].data), 'nobs': (('nobs'), np.arange(0, iters)), } - data_vars = {} + keep_v = ['Observation_Class', + 'Water_Fraction', + 'Land_Fraction', + 'Ice_Fraction', + 'Snow_Fraction', + 'Water_Temperature', + 'Soil_Temperature', + 'Soil_Moisture', + 'Land_Type_Index', + 'sstcu', + 'sstph', + 'sstnv', + 'dta', + 'dqa', + 'dtp_avh', + 'tsavg5', + 'Vegetation_Fraction', + 'tpwc_amsua', + 'tpwc', + 'tpwc_guess', + 'clw_guess_retrieval', + 'clwp_amsua', + 'scat_amsua', + 'Cloud_Frac', + 'CTP', + 'CLW', + 'TPWC', + 'clw_obs', + 'clw_guess', + 'ciw_guess', + 'rain_guess', + 'snow_guess', + 'tropopause_pressure', + 'SST_Warm_layer_dt', + 'SST_Cool_layer_tdrop', + 'SST_dTz_dTfound', + 'Vegetation_Type', + 'Lai', + 'Soil_Type', + 'Sfc_Wind_Direction'] + + + reshape_v = ['Latitude', + 'Longitude', + 'Elevation', + 'Foundation_Temperature', + 'Ice_Temperature', + 'Land_Temperature', + 'Obs_Time', + 'Sfc_Wind_Speed', + 'Snow_Depth', + 'Snow_Temperature', + 'dTb_dTs', + 'Channel_Index', + 'Observation', + 'Obs_Minus_Forecast_adjusted', + 'Obs_Minus_Forecast_unadjusted', + 'Forecast_adjusted_clear', + 'Forecast_unadjusted', + 'Forecast_unadjusted_clear', + 'Sol_Zenith_Angle', + 'Sol_Azimuth_Angle', + 'Sat_Azimuth_Angle', + 'Sun_Glint_Angle', + 'Scan_Position', + 'Scan_Angle', + 'Sat_Zenith_Angle', + 'BC_Scan_Angle', + 'BC_Cloud_Liquid_Water', + 'BC_Cosine_Latitude_times_Node', + 'BC_Sine_Latitude', + 'Forecast_adjusted', + 'Bias_Correction', + 'Bias_Correction_Constant', + 'Bias_Correction_ScanAngle', + 'Inverse_Observation_Error', + 'Input_Observation_Error', + 'QC_Flag', + 'Emissivity', + 'Weighted_Lapse_Rate', + 'dTb_dTs', + 'BC_Constant', + 'BC_Lapse_Rate_Squared', + 'BC_Lapse_Rate', + 'BC_Emissivity', + 'BC_Fixed_Scan_Position'] # Loop through each variable + required_variables = group_vars[:] + required_variables.append('sensor_chan') for var in ds.variables: - + # Ignore everything that the user didn't ask for + if var not in required_variables: + continue # Ignore geovals data if var in ['air_temperature', 'air_pressure', 'air_pressure_levels', 'atmosphere_absorber_01', 'atmosphere_absorber_02', 'atmosphere_absorber_03']: @@ -156,20 +226,23 @@ def satellite_dataset(ds): data_vars[out_var] = (('nobs', 'nchans'), data[:, :, ipred]) # Deals with how to handle nobs data - else: - # Check if values repeat over nchans - condition = all_equal(ds[var].data[0:nchans]) + # If values are repeating over nchan iterations (known previously on safe), keep as nobs + elif var in keep_v: + data_vars[var] = (('nobs'), ds[var].thin(nobs=nchans).data) - # If values are repeating over nchan iterations, keep as nobs - if condition: - data = ds[var].data[0::nchans] - data_vars[var] = (('nobs'), data) + # reshape to be a 2d array + elif var in reshape_v: + data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters,nchans)) - # Else, reshape to be a 2d array + # Only as a last resort check data for repeats to determine reshape for unknown data + # not a particularly safe method if you have nchan values that repeat in a fluke dataset, + # but the dimensions are (nprofile, nchan) you're going to get this wrong + else: + is_1d = (ds[var].isel(nobs=slice(0,nchans)) == ds[var].isel(nobs=0)).all() + if(is_1d): + data_vars[var] = (('nobs'), ds[var].thin(nobs=nchans).data) else: - data = np.reshape(ds[var].data, (iters, nchans)) - data_vars[var] = (('nobs', 'nchans'), data) - + data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters,nchans)) # create dataset_config new_ds = Dataset(data_vars=data_vars, coords=coords, @@ -251,9 +324,8 @@ def execute(self, dataset_config, data_collections, timeing): # Reshape variables if satellite diag if 'nchans' in ds.dims: - ds = satellite_dataset(ds) + ds = satellite_dataset(ds, group_vars) ds = subset_channels(ds, channels, self.logger) - # Adjust variable names if uv if 'variable' in locals(): if variable == 'uv': From 7778cf36bc59d14e65b5ebcb358b934b47f8658f Mon Sep 17 00:00:00 2001 From: karpob Date: Fri, 6 Jun 2025 17:06:01 -0400 Subject: [PATCH 2/6] norms. --- src/eva/data/gsi_obs_space.py | 99 +++++++++++++++++------------------ 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index 5d45d38b..aef96b33 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -113,7 +113,7 @@ def satellite_dataset(ds, group_vars): 'nobs': (('nobs'), np.arange(0, iters)), } data_vars = {} - keep_v = ['Observation_Class', + keep_v = ['Observation_Class', 'Water_Fraction', 'Land_Fraction', 'Ice_Fraction', @@ -154,51 +154,50 @@ def satellite_dataset(ds, group_vars): 'Soil_Type', 'Sfc_Wind_Direction'] - reshape_v = ['Latitude', - 'Longitude', - 'Elevation', - 'Foundation_Temperature', - 'Ice_Temperature', - 'Land_Temperature', - 'Obs_Time', - 'Sfc_Wind_Speed', - 'Snow_Depth', - 'Snow_Temperature', - 'dTb_dTs', - 'Channel_Index', - 'Observation', - 'Obs_Minus_Forecast_adjusted', - 'Obs_Minus_Forecast_unadjusted', - 'Forecast_adjusted_clear', - 'Forecast_unadjusted', - 'Forecast_unadjusted_clear', - 'Sol_Zenith_Angle', - 'Sol_Azimuth_Angle', - 'Sat_Azimuth_Angle', - 'Sun_Glint_Angle', - 'Scan_Position', - 'Scan_Angle', - 'Sat_Zenith_Angle', - 'BC_Scan_Angle', - 'BC_Cloud_Liquid_Water', - 'BC_Cosine_Latitude_times_Node', - 'BC_Sine_Latitude', - 'Forecast_adjusted', - 'Bias_Correction', - 'Bias_Correction_Constant', - 'Bias_Correction_ScanAngle', - 'Inverse_Observation_Error', - 'Input_Observation_Error', - 'QC_Flag', - 'Emissivity', - 'Weighted_Lapse_Rate', - 'dTb_dTs', - 'BC_Constant', - 'BC_Lapse_Rate_Squared', - 'BC_Lapse_Rate', - 'BC_Emissivity', - 'BC_Fixed_Scan_Position'] + 'Longitude', + 'Elevation', + 'Foundation_Temperature', + 'Ice_Temperature', + 'Land_Temperature', + 'Obs_Time', + 'Sfc_Wind_Speed', + 'Snow_Depth', + 'Snow_Temperature', + 'dTb_dTs', + 'Channel_Index', + 'Observation', + 'Obs_Minus_Forecast_adjusted', + 'Obs_Minus_Forecast_unadjusted', + 'Forecast_adjusted_clear', + 'Forecast_unadjusted', + 'Forecast_unadjusted_clear', + 'Sol_Zenith_Angle', + 'Sol_Azimuth_Angle', + 'Sat_Azimuth_Angle', + 'Sun_Glint_Angle', + 'Scan_Position', + 'Scan_Angle', + 'Sat_Zenith_Angle', + 'BC_Scan_Angle', + 'BC_Cloud_Liquid_Water', + 'BC_Cosine_Latitude_times_Node', + 'BC_Sine_Latitude', + 'Forecast_adjusted', + 'Bias_Correction', + 'Bias_Correction_Constant', + 'Bias_Correction_ScanAngle', + 'Inverse_Observation_Error', + 'Input_Observation_Error', + 'QC_Flag', + 'Emissivity', + 'Weighted_Lapse_Rate', + 'dTb_dTs', + 'BC_Constant', + 'BC_Lapse_Rate_Squared', + 'BC_Lapse_Rate', + 'BC_Emissivity', + 'BC_Fixed_Scan_Position'] # Loop through each variable required_variables = group_vars[:] required_variables.append('sensor_chan') @@ -232,17 +231,17 @@ def satellite_dataset(ds, group_vars): # reshape to be a 2d array elif var in reshape_v: - data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters,nchans)) + data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters, nchans)) # Only as a last resort check data for repeats to determine reshape for unknown data - # not a particularly safe method if you have nchan values that repeat in a fluke dataset, + # not a particularly safe method if you have nchan values that repeat in a fluke dataset, # but the dimensions are (nprofile, nchan) you're going to get this wrong else: - is_1d = (ds[var].isel(nobs=slice(0,nchans)) == ds[var].isel(nobs=0)).all() + is_1d = (ds[var].isel(nobs=slice(0, nchans)) == ds[var].isel(nobs=0)).all() if(is_1d): - data_vars[var] = (('nobs'), ds[var].thin(nobs=nchans).data) + data_vars[var] = (('nobs'), ds[var].thin(nobs = nchans).data) else: - data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters,nchans)) + data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters, nchans)) # create dataset_config new_ds = Dataset(data_vars=data_vars, coords=coords, From 05fc4e9785b057cd995bf4702fb43146b2f50af5 Mon Sep 17 00:00:00 2001 From: karpob Date: Fri, 6 Jun 2025 17:09:28 -0400 Subject: [PATCH 3/6] norms. --- src/eva/data/gsi_obs_space.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index aef96b33..37c1eee0 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -113,7 +113,7 @@ def satellite_dataset(ds, group_vars): 'nobs': (('nobs'), np.arange(0, iters)), } data_vars = {} - keep_v = ['Observation_Class', + keep_v = [ 'Observation_Class', 'Water_Fraction', 'Land_Fraction', 'Ice_Fraction', @@ -237,9 +237,9 @@ def satellite_dataset(ds, group_vars): # not a particularly safe method if you have nchan values that repeat in a fluke dataset, # but the dimensions are (nprofile, nchan) you're going to get this wrong else: - is_1d = (ds[var].isel(nobs=slice(0, nchans)) == ds[var].isel(nobs=0)).all() + is_1d = (ds[var].isel(nobs= slice(0, nchans)) == ds[var].isel(nobs=0)).all() if(is_1d): - data_vars[var] = (('nobs'), ds[var].thin(nobs = nchans).data) + data_vars[var] = (('nobs'), ds[var].thin(nobs= nchans).data) else: data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters, nchans)) # create dataset_config From 2ca1bdeb70c1e7e050821903b4801d0b2a1783ec Mon Sep 17 00:00:00 2001 From: karpob Date: Fri, 6 Jun 2025 17:15:50 -0400 Subject: [PATCH 4/6] norms. --- src/eva/data/gsi_obs_space.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index 37c1eee0..bd12dfb7 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -237,9 +237,9 @@ def satellite_dataset(ds, group_vars): # not a particularly safe method if you have nchan values that repeat in a fluke dataset, # but the dimensions are (nprofile, nchan) you're going to get this wrong else: - is_1d = (ds[var].isel(nobs= slice(0, nchans)) == ds[var].isel(nobs=0)).all() - if(is_1d): - data_vars[var] = (('nobs'), ds[var].thin(nobs= nchans).data) + is_1d = (ds[var].isel(nobs=slice(0, nchans)) == ds[var].isel(nobs=0)).all() + if (is_1d): + data_vars[var] = (('nobs'), ds[var].thin(nobs=nchans).data) else: data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters, nchans)) # create dataset_config From d44aa88942348ca5846466ac1f8999ad48e06bcb Mon Sep 17 00:00:00 2001 From: karpob Date: Fri, 6 Jun 2025 17:18:20 -0400 Subject: [PATCH 5/6] norms. --- src/eva/data/gsi_obs_space.py | 80 +++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index bd12dfb7..2625dd37 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -113,46 +113,46 @@ def satellite_dataset(ds, group_vars): 'nobs': (('nobs'), np.arange(0, iters)), } data_vars = {} - keep_v = [ 'Observation_Class', - 'Water_Fraction', - 'Land_Fraction', - 'Ice_Fraction', - 'Snow_Fraction', - 'Water_Temperature', - 'Soil_Temperature', - 'Soil_Moisture', - 'Land_Type_Index', - 'sstcu', - 'sstph', - 'sstnv', - 'dta', - 'dqa', - 'dtp_avh', - 'tsavg5', - 'Vegetation_Fraction', - 'tpwc_amsua', - 'tpwc', - 'tpwc_guess', - 'clw_guess_retrieval', - 'clwp_amsua', - 'scat_amsua', - 'Cloud_Frac', - 'CTP', - 'CLW', - 'TPWC', - 'clw_obs', - 'clw_guess', - 'ciw_guess', - 'rain_guess', - 'snow_guess', - 'tropopause_pressure', - 'SST_Warm_layer_dt', - 'SST_Cool_layer_tdrop', - 'SST_dTz_dTfound', - 'Vegetation_Type', - 'Lai', - 'Soil_Type', - 'Sfc_Wind_Direction'] + keep_v = ['Observation_Class', + 'Water_Fraction', + 'Land_Fraction', + 'Ice_Fraction', + 'Snow_Fraction', + 'Water_Temperature', + 'Soil_Temperature', + 'Soil_Moisture', + 'Land_Type_Index', + 'sstcu', + 'sstph', + 'sstnv', + 'dta', + 'dqa', + 'dtp_avh', + 'tsavg5', + 'Vegetation_Fraction', + 'tpwc_amsua', + 'tpwc', + 'tpwc_guess', + 'clw_guess_retrieval', + 'clwp_amsua', + 'scat_amsua', + 'Cloud_Frac', + 'CTP', + 'CLW', + 'TPWC', + 'clw_obs', + 'clw_guess', + 'ciw_guess', + 'rain_guess', + 'snow_guess', + 'tropopause_pressure', + 'SST_Warm_layer_dt', + 'SST_Cool_layer_tdrop', + 'SST_dTz_dTfound', + 'Vegetation_Type', + 'Lai', + 'Soil_Type', + 'Sfc_Wind_Direction'] reshape_v = ['Latitude', 'Longitude', From df1e8c1f9b9411fe4059c55aca378dde4a0421e2 Mon Sep 17 00:00:00 2001 From: karpob Date: Tue, 10 Jun 2025 11:28:39 -0400 Subject: [PATCH 6/6] address potential problems with ancillary data for microwave with switch gsi_obs_space_reshape_all the user can turn on. --- src/eva/data/gsi_obs_space.py | 113 ++++++++++------------------------ 1 file changed, 32 insertions(+), 81 deletions(-) diff --git a/src/eva/data/gsi_obs_space.py b/src/eva/data/gsi_obs_space.py index 2625dd37..6bebdac0 100644 --- a/src/eva/data/gsi_obs_space.py +++ b/src/eva/data/gsi_obs_space.py @@ -93,7 +93,7 @@ def subset_channels(ds, channels, logger, add_channels_variable=False): # -------------------------------------------------------------------------------------------------- -def satellite_dataset(ds, group_vars): +def satellite_dataset(ds, group_vars, force_reshape_all): """ Build a new dataset to reshape satellite data. @@ -101,6 +101,8 @@ def satellite_dataset(ds, group_vars): Args: ds (Dataset): The input xarray Dataset. group_vars: all or use selected group_vars + force_reshape_all: bool that allows user to force all variables to be reshaped + vs. checking whether to thin or reshape Returns: Dataset: Reshaped xarray Dataset. """ @@ -113,94 +115,37 @@ def satellite_dataset(ds, group_vars): 'nobs': (('nobs'), np.arange(0, iters)), } data_vars = {} - keep_v = ['Observation_Class', - 'Water_Fraction', - 'Land_Fraction', - 'Ice_Fraction', - 'Snow_Fraction', - 'Water_Temperature', - 'Soil_Temperature', - 'Soil_Moisture', - 'Land_Type_Index', - 'sstcu', - 'sstph', - 'sstnv', - 'dta', - 'dqa', - 'dtp_avh', - 'tsavg5', - 'Vegetation_Fraction', - 'tpwc_amsua', - 'tpwc', - 'tpwc_guess', - 'clw_guess_retrieval', - 'clwp_amsua', - 'scat_amsua', - 'Cloud_Frac', - 'CTP', - 'CLW', - 'TPWC', - 'clw_obs', - 'clw_guess', - 'ciw_guess', - 'rain_guess', - 'snow_guess', - 'tropopause_pressure', - 'SST_Warm_layer_dt', - 'SST_Cool_layer_tdrop', - 'SST_dTz_dTfound', - 'Vegetation_Type', - 'Lai', - 'Soil_Type', - 'Sfc_Wind_Direction'] - - reshape_v = ['Latitude', - 'Longitude', - 'Elevation', - 'Foundation_Temperature', - 'Ice_Temperature', - 'Land_Temperature', - 'Obs_Time', - 'Sfc_Wind_Speed', - 'Snow_Depth', - 'Snow_Temperature', - 'dTb_dTs', - 'Channel_Index', + + required_variables = group_vars[:] + if ('sensor_chan' not in required_variables): + required_variables.append('sensor_chan') + + lat_lon = ['Latitude', 'Longitude'] + # these inherently have a spectral dimension. + always_2d = ['QC_Flag', 'Observation', 'Obs_Minus_Forecast_adjusted', 'Obs_Minus_Forecast_unadjusted', 'Forecast_adjusted_clear', - 'Forecast_unadjusted', 'Forecast_unadjusted_clear', - 'Sol_Zenith_Angle', - 'Sol_Azimuth_Angle', - 'Sat_Azimuth_Angle', - 'Sun_Glint_Angle', - 'Scan_Position', - 'Scan_Angle', - 'Sat_Zenith_Angle', + 'Forecast_adjusted', + 'Forecast_unadjusted', + 'Emissivity', 'BC_Scan_Angle', 'BC_Cloud_Liquid_Water', 'BC_Cosine_Latitude_times_Node', 'BC_Sine_Latitude', - 'Forecast_adjusted', 'Bias_Correction', 'Bias_Correction_Constant', 'Bias_Correction_ScanAngle', 'Inverse_Observation_Error', 'Input_Observation_Error', - 'QC_Flag', - 'Emissivity', - 'Weighted_Lapse_Rate', - 'dTb_dTs', 'BC_Constant', 'BC_Lapse_Rate_Squared', 'BC_Lapse_Rate', 'BC_Emissivity', 'BC_Fixed_Scan_Position'] # Loop through each variable - required_variables = group_vars[:] - required_variables.append('sensor_chan') for var in ds.variables: # Ignore everything that the user didn't ask for if var not in required_variables: @@ -224,18 +169,21 @@ def satellite_dataset(ds, group_vars): out_var = var+pred data_vars[out_var] = (('nobs', 'nchans'), data[:, :, ipred]) - # Deals with how to handle nobs data - # If values are repeating over nchan iterations (known previously on safe), keep as nobs - elif var in keep_v: - data_vars[var] = (('nobs'), ds[var].thin(nobs=nchans).data) - - # reshape to be a 2d array - elif var in reshape_v: + # Force all variables 2d except lat/lon (optional), or if it inherently has + # spectral dependence. Don't touch lat/lon because if they're 2d it makes it + # harder to make map. You'd need to do a per channel `accept_where` which + # would be cumbersome for hyperspectral, and annoying for any microwave that + # isn't AMSU. + # + # Recommend `gsi_obs_space_reshape_all` for any ancillary data for microwave sensors, + # because footprint size is used and calculated on a per channel basis (e.g., Land_Fraction, + # Water_Fraction, etc become inherently spectral). + elif (force_reshape_all and (var not in lat_lon)) or (var in always_2d): data_vars[var] = (('nobs', 'nchans'), ds[var].data.reshape(iters, nchans)) - # Only as a last resort check data for repeats to determine reshape for unknown data - # not a particularly safe method if you have nchan values that repeat in a fluke dataset, - # but the dimensions are (nprofile, nchan) you're going to get this wrong + # either thin or reshape based on whether or not all repeat values for the first + # slice of nchan out of the array. Always use for lat/lon because it will thin + # for most sensors except AMSU, which makes it easier to plot a map. else: is_1d = (ds[var].isel(nobs=slice(0, nchans)) == ds[var].isel(nobs=0)).all() if (is_1d): @@ -299,7 +247,10 @@ def execute(self, dataset_config, data_collections, timeing): # Get the groups to be read # ------------------------- groups = get(dataset_config, self.logger, 'groups') - + if ('gsi_obs_space_reshape_all' in dataset_config.keys()): + force_reshape_all = bool(get(dataset_config, self.logger, 'gsi_obs_space_reshape_all')) + else: + force_reshape_all = False # Loop over filenames # ------------------- for filename in filenames: @@ -323,7 +274,7 @@ def execute(self, dataset_config, data_collections, timeing): # Reshape variables if satellite diag if 'nchans' in ds.dims: - ds = satellite_dataset(ds, group_vars) + ds = satellite_dataset(ds, group_vars, force_reshape_all) ds = subset_channels(ds, channels, self.logger) # Adjust variable names if uv if 'variable' in locals():