Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
25 changes: 22 additions & 3 deletions esmvalcore/_data_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ def _replace_tags(path, variable):
"""Replace tags in the config-developer's file with actual values."""
path = path.strip('/')
tlist = re.findall(r'{([^}]*)}', path)
paths = [path]
if 'startdate' in variable:
paths = list(set([
re.sub(r'(\b{ensemble}\b)', r'{startdate}-\1', path),
re.sub(r'({ensemble})', r'{startdate}-\1', path)
]))
tlist.append('startdate')
else:
paths = [path]
for tag in tlist:
original_tag = tag
tag, _, _ = _get_caps_options(tag)
Expand All @@ -105,7 +112,6 @@ def _replace_tags(path, variable):
else:
raise KeyError("Dataset key {} must be specified for {}, check "
"your recipe entry".format(tag, variable))

paths = _replace_tag(paths, original_tag, replacewith)
return paths

Expand Down Expand Up @@ -218,6 +224,17 @@ def _get_filenames_glob(variable, drs):
return filenames_glob


def _update_output_file(variable, files):
intervals = [get_start_end_year(name) for name in files]
variable.update({'start_year': min(intervals)[0]})
variable.update({'end_year': max(intervals)[1]})
filename = variable['filename'].replace(
'.nc', '_{start_year}-{end_year}.nc'.format(**variable)
)
Comment thread
zklaus marked this conversation as resolved.
Outdated
variable['filename'] = filename
return variable


def _find_input_files(variable, rootpath, drs):
short_name = variable['short_name']
variable['short_name'] = variable['original_short_name']
Expand All @@ -237,6 +254,8 @@ def get_input_filelist(variable, rootpath, drs):
(files, dirnames, filenames) = _find_input_files(variable, rootpath, drs)
# do time gating only for non-fx variables
if variable['frequency'] != 'fx':
if 'startdate' in variable:
variable = _update_output_file(variable, files)
files = select_files(files, variable['start_year'],
variable['end_year'])
return (files, dirnames, filenames)
Expand All @@ -257,7 +276,7 @@ def get_output_file(variable, preproc_dir):
variable['variable_group'],
_replace_tags(cfg['output_file'], variable)[0],
)
if variable['frequency'] != 'fx':
if variable['frequency'] != 'fx' and 'startdate' not in variable:
outfile += '_{start_year}-{end_year}'.format(**variable)
outfile += '.nc'
return outfile
Expand Down
41 changes: 26 additions & 15 deletions esmvalcore/_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,38 +968,38 @@ def _initialize_datasets(raw_datasets):
return datasets

@staticmethod
def _expand_ensemble(variables):
def _expand_tag(variables, input_tag):
"""
Expand ensemble members to multiple datasets.
Expand tags such as ensemble members or stardates to multiple datasets.

Expansion only supports ensembles defined as strings, not lists.
"""
expanded = []
regex = re.compile(r'\(\d+:\d+\)')

def expand_ensemble(variable):
ens = variable.get('ensemble', "")
match = regex.search(ens)
def expand_tag(variable, input_tag):
tag = variable.get(input_tag, "")
match = regex.search(tag)
if match:
start, end = match.group(0)[1:-1].split(':')
for i in range(int(start), int(end) + 1):
expand = deepcopy(variable)
expand['ensemble'] = regex.sub(str(i), ens, 1)
expand_ensemble(expand)
expand[input_tag] = regex.sub(str(i), tag, 1)
expand_tag(expand, input_tag)
else:
expanded.append(variable)

for variable in variables:
ensemble = variable.get('ensemble', "")
if isinstance(ensemble, (list, tuple)):
for elem in ensemble:
tag = variable.get(input_tag, "")
if isinstance(tag, (list, tuple)):
for elem in tag:
if regex.search(elem):
raise RecipeError(
f"In variable {variable}: ensemble expansion "
"cannot be combined with ensemble lists")
f"In variable {variable}: {input_tag} expansion "
f"cannot be combined with {input_tag} lists")
expanded.append(variable)
else:
expand_ensemble(variable)
expand_tag(variable, input_tag)

return expanded

Expand Down Expand Up @@ -1042,8 +1042,19 @@ def _initialize_variables(self, raw_variable, raw_datasets):
activity = get_activity(variable)
if activity:
variable['activity'] = activity
check.variable(variable, required_keys)
variables = self._expand_ensemble(variables)
if 'startdate' in variable:
startdate_keys = deepcopy(required_keys)
startdate_keys.update({'startdate'})
try:
startdate_keys.remove('start_year')
startdate_keys.remove('end_year')
except KeyError:
continue
check.variable(variable, startdate_keys)
else:
check.variable(variable, required_keys)
variables = self._expand_tag(variables, 'ensemble')
variables = self._expand_tag(variables, 'startdate')
return variables

def _initialize_preprocessor_output(self, diagnostic_name, raw_variables,
Expand Down
2 changes: 1 addition & 1 deletion esmvalcore/_recipe_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def data_availability(input_files, var, dirnames, filenames):
raise RecipeError("Missing data")

# check time avail only for non-fx variables
if var['frequency'] == 'fx':
if var['frequency'] == 'fx' or 'startdate' in var:
return

required_years = set(range(var['start_year'], var['end_year'] + 1))
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/data_finder/test_replace_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Unit tests for :func:`esmvalcore._data_finder.regrid._stock_cube`"""
from esmvalcore._data_finder import _replace_tags

VARIABLE = {
'project': 'CMIP6',
'dataset': 'ACCURATE-MODEL',
'activity': 'act',
'exp': 'experiment',
'institute': 'HMA',
'ensemble': 'r1i1p1f1',
'mip': 'Amon',
'short_name': 'tas',
'grid': 'gr',
}


def test_replace_tags():
"""Tests for get_start_end_year function."""
path = _replace_tags(
'{activity}/{institute}/{dataset}/{exp}/{ensemble}/{mip}/{short_name}/'
'{grid}/{latestversion}', VARIABLE)
input_file = _replace_tags(
'{short_name}_{mip}_{dataset}_{exp}_{ensemble}_{grid}*.nc', VARIABLE)
output_file = _replace_tags(
'{project}_{dataset}_{mip}_{exp}_{ensemble}_{short_name}', VARIABLE)
assert path == [
'act/HMA/ACCURATE-MODEL/experiment/r1i1p1f1/Amon/tas/gr/'
'{latestversion}'
]
assert input_file == ['tas_Amon_ACCURATE-MODEL_experiment_r1i1p1f1_gr*.nc']
assert output_file == ['CMIP6_ACCURATE-MODEL_Amon_experiment_r1i1p1f1_tas']


def test_replace_tags_with_startdate():
"""Tests for get_start_end_year function."""
variable = {'startdate': '199411', **VARIABLE}
path = _replace_tags(
'{activity}/{institute}/{dataset}/{exp}/{ensemble}/{mip}/{short_name}/'
'{grid}/{latestversion}', variable)
input_file = _replace_tags(
'{short_name}_{mip}_{dataset}_{exp}_{ensemble}_{grid}*.nc', variable)
output_file = _replace_tags(
'{project}_{dataset}_{mip}_{exp}_{ensemble}_{short_name}', variable)
assert path == [
'act/HMA/ACCURATE-MODEL/experiment/r1i1p1f1/Amon/tas/gr/'
'{latestversion}',
'act/HMA/ACCURATE-MODEL/experiment/199411-r1i1p1f1/Amon/tas/gr/'
'{latestversion}'
]
assert input_file == [
'tas_Amon_ACCURATE-MODEL_experiment_199411-r1i1p1f1_gr*.nc'
]
assert output_file == [
'CMIP6_ACCURATE-MODEL_Amon_experiment_199411-r1i1p1f1_tas'
]
28 changes: 26 additions & 2 deletions tests/unit/test_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_expand_ensemble(self):
},
]

expanded = Recipe._expand_ensemble(datasets)
expanded = Recipe._expand_tag(datasets, 'ensemble')

ensembles = [
'r1i2p3',
Expand All @@ -29,6 +29,30 @@ def test_expand_ensemble(self):
for i, ensemble in enumerate(ensembles):
assert expanded[i] == {'dataset': 'XYZ', 'ensemble': ensemble}

def test_expand_startdate(self):

datasets = [
{
'dataset': 'XYZ',
'startdate': 's(1998:2005)',
},
]

expanded = Recipe._expand_tag(datasets, 'startdate')

startdates = [
's1998',
's1999',
's2000',
's2001',
's2002',
's2003',
's2004',
's2005',
]
for i, startdate in enumerate(startdates):
assert expanded[i] == {'dataset': 'XYZ', 'startdate': startdate}

def test_expand_ensemble_nolist(self):

datasets = [
Expand All @@ -39,4 +63,4 @@ def test_expand_ensemble_nolist(self):
]

with pytest.raises(RecipeError):
Recipe._expand_ensemble(datasets)
Recipe._expand_tag(datasets, 'ensemble')