Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion mip_convert/mip_convert/new_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ def ordered_coords(self):

return self._ordered_coords

def process(self):
def process(self, saver=None, cell_measures_config=None):
"""Process the data.

The units of the data of the |MIP requested variable| are the
Expand Down Expand Up @@ -367,6 +367,19 @@ def process(self):
self._update_time_units()
if hasattr(self.model_to_mip_mapping, 'valid_min'):
self._apply_valid_min_correction()
if saver is not None and cell_measures_config is not None:
self.apply_cell_measures(saver, cell_measures_config)

def apply_cell_measures(self, saver, cell_measures_config):
"""Apply cell measures to the CMOR variable if not already applied. Parameters
----------
saver:
The CMOR outputter for this variable.
cell_measures_config: tuple
Arguments forwarded to cmor_wrapper.apply_cell_measures,
in the form (mip_era, mip_table_dir, realm, variable, frequency, region).
"""
saver.apply_cell_measures(self, *cell_measures_config)

def _remove_alevhalf_bounds(self):
"""
Expand Down
11 changes: 10 additions & 1 deletion mip_convert/mip_convert/requested_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,17 @@ def produce_mip_requested_variable(
# Process the data by performing the appropriate 'model to MIP mapping', then save the 'MIP output variable'
# to an 'output netCDF file'.
period = user_config.slicing.get(stream_id, 'year')

cell_measures_config = (
user_config.mip_era,
user_config.inpath,
mip_table.id,
variable_name,
frequency,
user_config.global_attributes.get('region', ''))

for time_slice in variable.slices_over(period):
time_slice.process()
time_slice.process(saver, cell_measures_config)
logger.debug('MIP output variable contains: {}'.format(time_slice.info))
save(time_slice, saver)

Expand Down
12 changes: 12 additions & 0 deletions mip_convert/mip_convert/save/cmor/cmor_outputter.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from mip_convert import mip_parser
import mip_convert.common
from mip_convert.common import RelativePathChecker
from mip_convert.save import create_cmor_variable
from mip_convert.save.mip_config import MipTableFactory


Expand Down Expand Up @@ -301,6 +302,17 @@ def _optional_kwargs(self, variable):
kwargs['comment'] = self._name_space.namespace_stamp(variable.comment)
return kwargs

def apply_cell_measures(self, mip_output_variable, *cell_measures_config):
"""Apply cell measures to this CMOR variable.

Ensures the CMOR variable ID exists (creating it if needed), then
sets the cell_measures attribute via the CMOR wrapper.
"""
if self.varid is None:
cmor_variable = create_cmor_variable(mip_output_variable)
self._getVarId(cmor_variable)
self.cmor.apply_cell_measures(*cell_measures_config, self.varid)

def _close_file(self):
self.cmor.close(self.varid, preserve=True)

Expand Down
37 changes: 37 additions & 0 deletions mip_convert/mip_convert/save/cmor/cmor_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# (C) British Crown Copyright 2009-2025, Met Office.
# Please see LICENSE.md for license details.
from collections import OrderedDict
import os
import json

import cmor

Expand Down Expand Up @@ -115,3 +117,38 @@ def zfactor(self, *args, **kwargs):
def set_frequency(self, frequency, **kwargs):
self._debug_on_args('frequency', [frequency], kwargs)
cmor.cmor.set_cur_dataset_attribute('frequency', frequency)

def apply_cell_measures(self, mip_era, mip_table_dir, realm, variable, frequency, region, variable_id):
"""
Set the cell_measures attribute on a CMOR variable using a lookup in
``{mip_era}_cell_measures.json`` from the MIP tables directory. Returns
silently if the file does not exist.

The lookup key has the form ``{realm}.{root_label}.{branding}.{frequency}.{region}``
(variable name split on ``_``), so entries in the JSON must follow the
convention used for CMIP7.
"""
self._debug_on_args('apply_cell_measures', [mip_era, mip_table_dir, realm, variable, frequency, region, variable_id], {})

cell_measures_file = os.path.join(mip_table_dir, f'{mip_era}_cell_measures.json')

if os.path.exists(cell_measures_file):
with open(cell_measures_file) as fh:
cell_measures = json.load(fh)
if 'cell_measures' not in cell_measures:
self.logger.debug(f'"cell_measures" key not found in {cell_measures_file}')
return
cell_measures = cell_measures['cell_measures']

root_label, branding = variable.split('_')
key = f'{realm}.{root_label}.{branding}.{frequency}.{region}'
if key in cell_measures:
retval = cmor.cmor.set_variable_attribute(
variable_id,
'cell_measures',
'c',
cell_measures[key])
if retval != 0:
self.logger.debug('cell_measures assignment failed. Check cmor log file for details')
else:
self.logger.debug(f'Cell_measures file "{cell_measures_file}" not found. Continuing')