-
Notifications
You must be signed in to change notification settings - Fork 2
Make streams configurable at dataset level #510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0f85e1c
6718e1e
a1d1e0b
58d88b8
3f13e88
19c4f29
b88d721
994139d
76556b6
b967d17
5465543
c6f14f5
9ec7719
677f635
5cb0541
a339f0f
cc1245b
2d2a744
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently this is just a single function that's imported by the two files in this directory so that I didn't write the code twice (and tested it only once), so it doesn't have main(). As it applies only to these two highly related files, I put it here rather than in |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| #!/usr/bin/env python | ||
| # (C) Crown Copyright 2026, Met Office. | ||
| # The LICENSE.md file contains full licensing details. | ||
| """ | ||
| Determines the stream config used to create the request and variables files. | ||
| """ | ||
| import os | ||
| import sys | ||
| import yaml | ||
| import logging | ||
|
|
||
| logging.basicConfig(level=logging.INFO, stream=sys.stdout) | ||
| filename = os.path.basename(__file__) | ||
| logger = logging.getLogger(filename) | ||
|
|
||
|
|
||
| def determine_stream_config_fp(): | ||
| """ | ||
| Provide a path to pp file stream configuration data. | ||
|
|
||
| Uses the value of key `use_custom_data_streams` in the dataset information, | ||
| and returns either `path_to_custom_streams_config` or a default value. | ||
| The dataset is determined by variable `CYLC_TASK_PARAM_dataset`. | ||
|
|
||
| Returns | ||
| ------- | ||
| str | ||
| Filepath to stream information for the dataset. | ||
| """ | ||
| # Load the dataset information from model_runs YAML | ||
| dataset = os.environ["CYLC_TASK_PARAM_dataset"].strip() | ||
| with open(os.environ["MODEL_RUNS_CONFIG"]) as f: | ||
| content = yaml.safe_load(f) | ||
| dataset_dict = content[dataset] | ||
| logger.debug("Dataset information:\n%s", dataset_dict) | ||
|
|
||
| # Check whether a custom stream map is set to be used | ||
| if dataset_dict.get("use_custom_data_streams"): | ||
| streams_config = dataset_dict["path_to_custom_streams_config"] | ||
| else: | ||
| streams_config = os.environ["DEFAULT_STREAM_CONFIG_PATH"] | ||
| logger.info( | ||
| "Stream info for dataset % will use: %s", dataset, streams_config | ||
| ) | ||
|
|
||
| return streams_config |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # (C) Crown Copyright 2026, Met Office. | ||
| # The LICENSE.md file contains full licensing details. | ||
| """ | ||
| Unit tests for determine_streams_config.py | ||
|
|
||
| Test data files: | ||
| /app/unittest/mock_data/model_run_stream_config.yml | ||
| input for test_create_request | ||
| """ | ||
| import pytest | ||
| from pathlib import Path | ||
| from determine_streams_config import determine_stream_config_fp | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_env_vars(monkeypatch): | ||
| monkeypatch.setenv( | ||
| "MODEL_RUNS_CONFIG", | ||
| str( | ||
| Path(__file__).parent.parent.parent | ||
| / "unittest" | ||
| / "mock_data" | ||
| / "model_run_stream_config.yml" | ||
| ), | ||
| ) | ||
| monkeypatch.setenv( | ||
| "DEFAULT_STREAM_CONFIG_PATH", "/path/to/default_streams.yml" | ||
| ) | ||
|
|
||
|
|
||
| def test_determine_stream_config_fp_custom(mock_env_vars, monkeypatch): | ||
| monkeypatch.setenv("CYLC_TASK_PARAM_dataset", "run-1") | ||
| expected = "/path/to/custom_streams.yml" | ||
| actual = determine_stream_config_fp() | ||
| assert actual == expected | ||
|
|
||
|
|
||
| def test_determine_stream_config_fp_default(mock_env_vars, monkeypatch): | ||
| monkeypatch.setenv("CYLC_TASK_PARAM_dataset", "run-2") | ||
| expected = "/path/to/default_streams.yml" | ||
| actual = determine_stream_config_fp() | ||
| assert actual == expected |
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should discuss which runs count as "standard" for writing the streams to this file. I've written the MJO variables' streams as they are in the not for CMIP model run ( |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,21 @@ | ||
| # (C) Crown Copyright 2026, Met Office. | ||
| # The LICENSE.md file contains full licensing details. | ||
| apa: | ||
| - day/pr | ||
| ape: | ||
| - day/ua | ||
| apm: | ||
| - Amon/hfls | ||
| - Amon/hfss | ||
| - Amon/rlds | ||
| - Amon/rlut | ||
| - Amon/rlutcs | ||
| - Amon/rsds | ||
| - Amon/rsdt | ||
| - Amon/rsut | ||
| - Amon/rsutcs | ||
| - Amon/tas | ||
| - Emon/rls | ||
| - Emon/rss | ||
| - Amon/hfls | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The spacing worked before, but I think this way is standard. |
||
| - Amon/hfss | ||
| - Amon/rlds | ||
| - Amon/rlut | ||
| - Amon/rlutcs | ||
| - Amon/rsds | ||
| - Amon/rsdt | ||
| - Amon/rsut | ||
| - Amon/rsutcs | ||
| - Amon/tas | ||
| - Emon/rls | ||
| - Emon/rss | ||
| inm: | ||
| - SImon/siconc | ||
| - SImon/siconc | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -311,6 +311,32 @@ def add_reference_key(filepath): | |
| yaml.dump(dataset_dict, f) | ||
|
|
||
|
|
||
| def format_as_bools(filepath): | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Rose to set the type to either of "boolean" or "python_boolean" resulted in a quoted string ('true' or 'True'), so this is just to make querying the file easier later (and it makes the file neater to look at, and to reproduce a different way in future if we moved away from the whole namelist thing). |
||
| """ | ||
| Format second level key true or false strings as booleans. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| filepath: str | ||
| The location of the YAML file to be edited. | ||
| """ | ||
| # Read the yaml as a dictionary without the extra key | ||
| with open(filepath, "r") as f: | ||
| dataset_dict = yaml.safe_load(f) | ||
|
|
||
| # Reformat true and false keys | ||
| for dataset in dataset_dict: | ||
| for key in dataset_dict[dataset]: | ||
| if str(dataset_dict[dataset][key]).lower() == "true": | ||
| dataset_dict[dataset][key] = True | ||
| elif str(dataset_dict[dataset][key]).lower() == "false": | ||
| dataset_dict[dataset][key] = False | ||
|
|
||
| # Re-save the file | ||
| with open(filepath, "w") as f: | ||
| yaml.dump(dataset_dict, f) | ||
|
|
||
|
|
||
| def main(): | ||
| """Copy dataset information from configuration to the share directory.""" | ||
| # Read the target (shared) directory from the environment | ||
|
|
@@ -346,14 +372,17 @@ def main(): | |
|
|
||
| # Reformat the YAML files to use unique identifiers as keys | ||
| logger.info("Reformatting YAML files to use suite IDs as keys") | ||
| model_runs_yaml = f"{target_dir}/model_runs.yml" | ||
| model_runs_yaml = os.environ["MODEL_RUNS_CONFIG"] | ||
| use_facet_as_key(model_runs_yaml, "suite_id") | ||
| use_facet_as_key(f"{target_dir}/cmip6_datasets.yml", "model_id") | ||
|
|
||
| # Add the reference identifier | ||
| logger.info("Adding benchmarking key to model runs YAML") | ||
| add_reference_key(model_runs_yaml) | ||
|
|
||
| # Reformat Cylc or Rose booleans as python booleans | ||
| format_as_bools(model_runs_yaml) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # (C) Crown Copyright 2026, Met Office. | ||
| # The LICENSE.md file contains full licensing details. | ||
| run-1: | ||
| path_to_custom_streams_config: /path/to/custom_streams.yml | ||
| use_custom_data_streams: true | ||
| other_key: something | ||
| run-2: | ||
| use_custom_data_streams: false | ||
| other_key: something else |
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests before were using the actual file, so they started failing. They should have used a mock to begin with. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # (C) Crown Copyright 2026, Met Office. | ||
| # The LICENSE.md file contains full licensing details. | ||
| apm: | ||
| - Amon/hfls | ||
| - Amon/hfss | ||
| - Amon/rlds | ||
| - Amon/rlut | ||
| - Amon/rlutcs | ||
| - Amon/rsds | ||
| - Amon/rsdt | ||
| - Amon/rsut | ||
| - Amon/rsutcs | ||
| - Amon/tas | ||
| - Emon/rls | ||
| - Emon/rss | ||
| inm: | ||
| - SImon/siconc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,7 +85,8 @@ | |
| RAW_DATA_DIR = {{ RAW_DATA_DIR | default("") }} | ||
| RAW_DATA_DIR_MODE = {{ RAW_DATA_DIR_MODE }} | ||
| REQUEST_DEFAULTS_PATH = ${CYLC_WORKFLOW_RUN_DIR}/app/configure_standardise/etc/request_defaults.yml | ||
| STREAM_CONFIG_PATH = ${CYLC_WORKFLOW_RUN_DIR}/app/configure_standardise/etc/streams.yml | ||
| ROOT_RESTRUCTURED_DIR=${CYLC_WORKFLOW_SHARE_DIR}/work/ | ||
| DEFAULT_STREAM_CONFIG_PATH = ${CYLC_WORKFLOW_RUN_DIR}/app/configure_standardise/etc/streams.yml | ||
| # Workaround for bug in CDDS: ROOT_SOFTWARE_DIR: unbound variable. | ||
| ROOT_SOFTWARE_DIR = ${CDDS_SOFTWARE_DIR} | ||
| CDDS_VERSION = {{ CDDS_VERSION }} | ||
|
|
@@ -99,7 +100,9 @@ | |
| [[MODEL_RUNS]] | ||
| [[[environment]]] | ||
| # Directory containing dataset lists as YAML files | ||
| DATASETS_LIST_DIR = "${CYLC_WORKFLOW_SHARE_DIR}/dataset_lists" | ||
| DATASETS_LIST_DIR = ${CYLC_WORKFLOW_SHARE_DIR}/dataset_lists | ||
| # This particular file controls behaviours throughout the workflow | ||
| MODEL_RUNS_CONFIG = ${DATASETS_LIST_DIR}/model_runs.yml | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the path I found referenced a few times so I thought it belonged here. |
||
| START_YEAR = {{ START_YEAR }} | ||
| NUMBER_OF_YEARS = {{ NUMBER_OF_YEARS }} | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this referenced in a few places.