diff --git a/cdds/cdds/common/constants.py b/cdds/cdds/common/constants.py index 5230c32e5..4eb05cae9 100644 --- a/cdds/cdds/common/constants.py +++ b/cdds/cdds/common/constants.py @@ -59,6 +59,8 @@ 'so_0', # ocean_zostoga 'rho_0_mean', # ocean_zostoga 'deptho_0_mean', # ocean_zostoga + 'u_coord_reference', # nemo_eORCA1_grid-U.nc + 'v_coord_reference', # nemo_eORCA1_grid-V.nc ] APPROVED_VARS_PREFIX = 'approved_variables' APPROVED_VARS_FILENAME_TEMPLATE = APPROVED_VARS_PREFIX + '_{dt}.txt' diff --git a/cdds/cdds/common/plugins/cmip7/data/model/UKCM2-0-LL.json b/cdds/cdds/common/plugins/cmip7/data/model/UKCM2-0-LL.json index 1ca162ea1..a0ef1dad6 100644 --- a/cdds/cdds/common/plugins/cmip7/data/model/UKCM2-0-LL.json +++ b/cdds/cdds/common/plugins/cmip7/data/model/UKCM2-0-LL.json @@ -201,7 +201,8 @@ "ancil_filenames": [ "qrparm.landfrac.pp", "qrparm.soil.pp", - "qrparm.orog.pp" + "qrparm.orog.pp", + "slthick_rootd_sftgif.pp" ], "hybrid_heights_files": [ "atmosphere_theta_levels_85.txt", @@ -216,12 +217,18 @@ "levels": 75, "replacement_coordinates_file": "", "ancil_filenames": [ + "nemo_eORCA1_grid-V.nc", + "nemo_eORCA1_grid-U.nc", "ocean_constants.nc", "ocean_byte_masks.nc", "ocean_basin.nc", "diaptr_basin_masks.nc", "ocean_zostoga.nc" ], + "ancil_variables": [ + "u_coord_reference", + "v_coord_reference" + ], "bounds_coordinates": { "onm-grid-T": [ "bounds_nav_lon", diff --git a/cdds/cdds/common/plugins/cmip7/data/streams/streams_config.json b/cdds/cdds/common/plugins/cmip7/data/streams/streams_config.json index b5b23c582..b89ccef5f 100644 --- a/cdds/cdds/common/plugins/cmip7/data/streams/streams_config.json +++ b/cdds/cdds/common/plugins/cmip7/data/streams/streams_config.json @@ -585,19 +585,19 @@ "mrsll_tavg-sl-hxy-lnd@mon": "ap5", "mrso_tavg-u-hxy-lnd@day": "ap6", "mrso_tavg-u-hxy-lnd@mon": "ap5", - "mrsofc_ti-u-hxy-lnd@fx": "fx", + "mrsofc_ti-u-hxy-lnd@fx": "afx", "mrsol_tavg-d100cm-hxy-lnd@3hr": "ap8", "mrsol_tavg-d10cm-hxy-lnd@day": "ap6", "mrsol_tavg-d10cm-hxy-lnd@mon": "ap5", "mrsol_tavg-sl-hxy-lnd@day": "ap6", "mrsol_tavg-sl-hxy-lnd@mon": "ap5", "mrsol_tpt-d10cm-hxy-lnd@3hr": "ap8", - "orog_ti-u-hxy-u@fx": "fx", - "rootd_ti-u-hxy-lnd@fx": "fx", + "orog_ti-u-hxy-u@fx": "afx", + "rootd_ti-u-hxy-lnd@fx": "afx", "sftgif_tavg-u-hxy-u@mon": "ap5", - "sftgif_ti-u-hxy-u@fx": "fx", - "sftlaf_ti-u-hxy-u@fx": "fx", - "slthick_ti-sl-hxy-lnd@fx": "fx", + "sftgif_ti-u-hxy-u@fx": "afx", + "sftlaf_ti-u-hxy-u@fx": "afx", + "slthick_ti-sl-hxy-lnd@fx": "afx", "srfrad_tavg-u-hxy-u@3hr": "UNKNOWN", "sweLut_tavg-u-hxy-multi@mon": "ap5", "tran_tavg-u-hxy-lnd@mon": "ap5", diff --git a/mip_convert/mip_convert/load/__init__.py b/mip_convert/mip_convert/load/__init__.py index 4766fded0..e0f2d7de1 100644 --- a/mip_convert/mip_convert/load/__init__.py +++ b/mip_convert/mip_convert/load/__init__.py @@ -53,11 +53,13 @@ def load(filenames, variable_metadata): input_variables.update({loadable.constraint: cube}) # Ensure all 'input variables' are on the same grid. + # (Excludes ancillary variables from the grid check.) for axis in ['Y', 'X']: if cube.coords(axis=axis): try: var_names = [cube.coord(axis=axis).var_name - for cube in list(input_variables.values())] + for cube in input_variables.values() + if cube.var_name not in variable_metadata.ancil_variables] except iris.exceptions.CoordinateNotFoundError: # crude hack to account for ancils that don't have all coordinates var_names = [] diff --git a/mip_convert/mip_convert/load/iris_load_util.py b/mip_convert/mip_convert/load/iris_load_util.py index 8782bf9c8..ad0557da6 100644 --- a/mip_convert/mip_convert/load/iris_load_util.py +++ b/mip_convert/mip_convert/load/iris_load_util.py @@ -198,10 +198,13 @@ def load_cubes(all_input_data, run_bounds, loadable, ancil_variables): logger.debug('Loading cube using Iris constraints') load_constraints = constraint_constructor.load_constraints(loadable) + # Static coord-reference ancils have a fixed timestamp that won't match + # later cycle run_bounds, so skip time filtering for them. + effective_run_bounds = None if loadable.name.endswith('_coord_reference') else run_bounds if loadable.is_pp(): merged_cubes = load_cubes_from_pp(all_input_data, load_constraints, run_bounds, ancil_variables) else: - merged_cubes = load_cubes_from_nc(all_input_data, load_constraints, run_bounds) + merged_cubes = load_cubes_from_nc(all_input_data, load_constraints, effective_run_bounds) if not merged_cubes: error_msg = 'No cubes found using constraints "{}" within "{}"' @@ -391,8 +394,8 @@ def load_cubes_from_nc(all_input_data, load_constraints, run_bounds): cubes = iris.cube.CubeList() if merged_cubes: - # Apply the time constraint. - time_constraint = setup_time_constraint(run_bounds) + # Apply the time constraint (skipped if run_bounds is None, e.g. for static ancils). + time_constraint = setup_time_constraint(run_bounds) if run_bounds is not None else None for merged_cube in merged_cubes: promote_aux_time_coord_to_dim(merged_cube) # Add the fill_value as an attribute on the cube to workaround the @@ -400,9 +403,12 @@ def load_cubes_from_nc(all_input_data, load_constraints, run_bounds): if hasattr(merged_cube.lazy_data(), 'fill_value'): merged_cube.attributes['fill_value'] = merged_cube.lazy_data().fill_value - cube = apply_time_constraint(merged_cube, time_constraint) - if cube is not None: - cubes.append(cube) + if time_constraint is None: + cubes.append(merged_cube) + else: + cube = apply_time_constraint(merged_cube, time_constraint) + if cube is not None: + cubes.append(cube) return cubes diff --git a/mip_convert/mip_convert/plugins/base/data/processors.py b/mip_convert/mip_convert/plugins/base/data/processors.py index 33775f47b..3fb01d03d 100644 --- a/mip_convert/mip_convert/plugins/base/data/processors.py +++ b/mip_convert/mip_convert/plugins/base/data/processors.py @@ -2497,3 +2497,23 @@ def calc_rootd(soil_cube, frac_cube, ice_class=None): ) return rootd_cube + + +def apply_ocean_coordinates(cube, coordinate_cube): + """Update the X and Y coordinate points of a cube from a reference cube. + + Parameters + ---------- + cube: :class:`iris.cube.Cube` + The cube whose coordinates will be updated. + coordinate_cube: :class:`iris.cube.Cube` + The cube containing the reference X and Y coordinates to copy. + + Returns + ------- + :class:`iris.cube.Cube` + The input cube with updated X and Y coordinate points. + """ + cube.coord(axis='Y').points = coordinate_cube.coord(axis='Y').points.copy() + cube.coord(axis='X').points = coordinate_cube.coord(axis='X').points.copy() + return cube diff --git a/mip_convert/mip_convert/plugins/ukcm2/data/UKCM2_seaIce_mappings.cfg b/mip_convert/mip_convert/plugins/ukcm2/data/UKCM2_seaIce_mappings.cfg index 31565d9c0..e64b96bce 100644 --- a/mip_convert/mip_convert/plugins/ukcm2/data/UKCM2_seaIce_mappings.cfg +++ b/mip_convert/mip_convert/plugins/ukcm2/data/UKCM2_seaIce_mappings.cfg @@ -767,7 +767,7 @@ units = 1 comment = component = seaIce dimension = longitude latitude time -expression = apply_SI3_U_coordinates(siu) +expression = apply_ocean_coordinates(siu, u_coord_reference) mip_table_id = seaIce positive = reviewer = none @@ -778,7 +778,7 @@ units = m s-1 comment = component = seaIce dimension = longitude latitude time -expression = apply_SI3_V_coordinates(siv) +expression = apply_ocean_coordinates(siv, v_coord_reference) mip_table_id = seaIce positive = reviewer = none