-
Notifications
You must be signed in to change notification settings - Fork 12
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
Feat/echam streams #1240
Open
pgierz
wants to merge
15
commits into
release
Choose a base branch
from
feat/echam-streams
base: release
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Feat/echam streams #1240
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
fa0a524
root: start of tidy streams
pgierz bd39ec3
chore: cleanup imports in tidy
pgierz 0242091
wip
pgierz c9d467f
feat: auto-create streams from mvtags
pgierz c293aaa
minor tweaks for globbing of new tags
pgierz 5bec486
doc(esm_runscripts/echam.py): add chat-gpt generated documentation
pgierz 08e8207
Apply suggestions from code review
pgierz d6aab91
fix: include JSBACH streams in ignore tags for ECHAM
pgierz 509ac21
feat: jsbach streams
pgierz 8897815
fix: typo
pgierz d384c50
fix: test had a bad quote
pgierz cdba0c7
test: allows doctest to use actual namelist (maybe)
pgierz 55130b9
test: allows doctest to use actual namelists also for jsbach part
pgierz 7c0d0aa
fix: bad check if comment character was missing
pgierz 82f649d
chore: cleanup of logger statements
pgierz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
""" | ||
This module provides functionality specific to the ECHAM component. | ||
|
||
The functions included in this module are: | ||
|
||
- ``append_namelist_dependent_sources``: Appends namelist dependent sources to the ECHAM | ||
configuration. | ||
|
||
These functions are used to manage and update the configuration settings for ECHAM, | ||
particularly focusing on handling namelist files and updating output data sources | ||
based on the configuration parameters. | ||
""" | ||
|
||
import f90nml | ||
from loguru import logger | ||
|
||
|
||
def _get_mvstream_tags_from_namelist(namelist): | ||
""" | ||
Extracts mvstream tags from a given namelist. | ||
|
||
Parameters | ||
---------- | ||
namelist : str or f90nml.namelist.Namelist | ||
The path to the namelist file or an already parsed namelist object. | ||
|
||
Returns | ||
------- | ||
list of str | ||
A list of mvstream tags found in the namelist. | ||
|
||
Raises | ||
------ | ||
FileNotFoundError | ||
If the namelist file specified by the path cannot be found. | ||
TypeError | ||
If the provided namelist cannot be converted to an f90nml.namelist.Namelist | ||
object. | ||
|
||
Examples | ||
-------- | ||
Assuming you have a namelist file at ``tests/namelists/echam/paul_custom_namelist.echam`` with the | ||
following contents: | ||
|
||
.. code-block:: fortran | ||
|
||
! This is the "no output" variant of namelist.echam. It contains absolutely no | ||
! output, and can be used as a template for testing. | ||
! | ||
! Extended by mvstreamctl namelist block, as an example for the mvstream tags. | ||
! | ||
! P. Gierz | ||
! Alfred Wegener Institute, Helmholtz Centre for Polar and Marine Research | ||
! July 2021 | ||
! | ||
! P. Gierz | ||
! October 2024 | ||
|
||
&runctl | ||
dt_start = 2285, 12, 31, 23, 52, 30 | ||
dt_stop = 6699, 12, 31, 23, 52, 30 | ||
putrerun = 12, 'months', 'last', 0 | ||
lfractional_mask = .false. | ||
lresume = .true. | ||
out_datapath = './' | ||
out_expname = 'E280' | ||
rerun_filetype = 4 | ||
delta_time = 450 | ||
putdata = 1, 'months', 'last', 0 | ||
nproma = 8 | ||
lcouple = .true. | ||
getocean = 1, 'days', 'last', 0 | ||
putocean = 1, 'days', 'last', 0 | ||
lcouple_co2 = .true. | ||
default_output = .false. | ||
/ | ||
|
||
&parctl | ||
nproca = 24 | ||
nprocb = 24 | ||
/ | ||
|
||
&submodelctl | ||
lmethox = .true. | ||
/ | ||
|
||
&submdiagctl | ||
vphysc_lpost = .false. | ||
/ | ||
|
||
&radctl | ||
iaero = 3 | ||
io3 = 4 | ||
isolrad = 6 | ||
ich4 = 3 | ||
in2o = 3 | ||
co2vmr = 284.3169860840e-06 | ||
ch4vmr = 808.2490234375e-09 | ||
n2ovmr = 273.0210571289e-09 | ||
yr_perp = 1850 | ||
/ | ||
&mvstreamctl | ||
filetag = 'paul_custom' | ||
source = 'g3b' | ||
variables = 'temp2:mean>temp2=167' | ||
interval = 1, 'months', 'last', 0 | ||
/ | ||
|
||
The following code will extract the mvstream tags from the namelist: | ||
|
||
>>> namelist_path = "tests/namelists/echam/paul_custom_namelist.echam" | ||
>>> tags = _get_mvstream_tags_from_namelist(namelist_path) | ||
>>> print(tags) | ||
['paul_custom'] | ||
""" | ||
mvstream_tags = [] | ||
if not isinstance(namelist, f90nml.namelist.Namelist): | ||
try: | ||
namelist = f90nml.read(namelist) | ||
except FileNotFoundError: | ||
logger.error(f"Namelist specified by {namelist} could not be found") | ||
except TypeError as e: | ||
logger.error( | ||
f"Could not convert {namelist} to f90nml.namelist.Namelist object." | ||
) | ||
raise e | ||
|
||
for chapter, contents in namelist.items(): | ||
if chapter == "mvstreamctl": | ||
tag = contents.get("filetag") | ||
if tag is not None: | ||
mvstream_tags.append(tag) | ||
return mvstream_tags | ||
|
||
|
||
def append_namelist_dependent_sources(config): | ||
""" | ||
Append namelist dependent sources to the ECHAM configuration. | ||
|
||
This function updates the `outdata_sources` in the ECHAM configuration | ||
based on the namelist objects and other configuration parameters. | ||
|
||
Parameters | ||
---------- | ||
config : dict | ||
The configuration dictionary containing general, ECHAM, and JSBACH settings. | ||
|
||
Notes | ||
----- | ||
- The function reads the namelist from the specified directory if not | ||
already loaded. | ||
- It filters out tags that are to be ignored based on the JSBACH streams | ||
or specified ignore tags. | ||
- The output file type is checked, and if it is NetCDF (indicated by | ||
``out_filetype`` == 2), the file extension `.nc` is appended to the tags. | ||
- The function logs the updates made to the ``outdata_sources``. | ||
""" | ||
expid = config["general"]["expid"] | ||
econfig = config["echam"] | ||
try: | ||
namelist = econfig["namelist_objs"] | ||
except KeyError: # Namelists not yet loaded... | ||
namelist = f90nml.read(f"{econfig['namelist_dir']}/namelist.echam") | ||
mvstream_tags = _get_mvstream_tags_from_namelist(namelist) | ||
jsbach_streams = config["jsbach"].get("streams", []) | ||
ignore_these_tags = econfig.get("ignore_tags", []) | ||
if econfig.get("ignore_tags_include_jsbach_tags", True): | ||
ignore_these_tags.extend(jsbach_streams) | ||
mvstream_tags = [tag for tag in mvstream_tags if tag not in ignore_these_tags] | ||
mvstream_dict = {tag: f"{expid}*{tag}" for tag in mvstream_tags} | ||
if namelist["runctl"].get("out_filetype") == 2: | ||
# Using NetCDF Outputs: | ||
mvstream_dict = {k: v + ".nc" for k, v in mvstream_dict.items()} | ||
logger.debug("Updating outdata_sources...") | ||
for k, v in mvstream_dict.items(): | ||
logger.debug(f"{k}: {v}") | ||
econfig["outdata_sources"].update(mvstream_dict) | ||
logger.debug("...done!") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
""" | ||
Functionality specific for the JSBACH sub-component. | ||
""" | ||
|
||
import f90nml | ||
from loguru import logger | ||
|
||
from .echam import _get_mvstream_tags_from_namelist | ||
|
||
|
||
def _get_comments_for_streams(namelist, mvstream_tags, flag="ESM_TOOLS_JSBACH_STREAM"): | ||
""" | ||
Extracts tags from a namelist file that have a specific comment flag. | ||
|
||
Parameters | ||
---------- | ||
namelist : str | ||
Path to the namelist file. | ||
mvstream_tags : list of str | ||
List of tags to search for in the namelist file. | ||
flag : str, optional | ||
The flag to search for in the comments (default is "ESM_TOOLS_JSBACH_STREAM"). | ||
|
||
Returns | ||
------- | ||
list of str | ||
List of tags that have the specified comment flag. | ||
|
||
Examples | ||
-------- | ||
Assuming you have a namelist file with the following contents: | ||
|
||
.. code-block:: fortran | ||
|
||
&mvstreamctl | ||
filetag = 'plants' ! ESM_TOOLS_JSBACH_STREAM | ||
source = 'g3b' | ||
variables = 'soilwet', 'soiltemp', 'lai', 'gpp', 'npp' | ||
interval = 1, 'months', 'last', 0 | ||
/ | ||
&mvstreamctl | ||
filetag = 'soil' ! ESM_TOOLS_JSBACH_STREAM | ||
source = 'g3b' | ||
variables = 'soilwet', 'soiltemp' | ||
interval = 1, 'months', 'last', 0 | ||
/ | ||
&mvstreamctl | ||
filetag = 'something' | ||
source = 'g3b' | ||
variables = 'temp2' | ||
interval = 1, 'months', 'last', 0 | ||
/ | ||
The following will extract ['plants', 'soil']:: | ||
|
||
>>> namelist = "tests/namelists/echam/jsbach_tags_namelist.echam" | ||
>>> mvstream_tags = ["plants", "soil", "something"] | ||
>>> _get_comments_for_streams(namelist, mvstream_tags) | ||
['plants', 'soil'] | ||
""" | ||
with open(namelist, "r") as f: | ||
lines = f.readlines() | ||
jsbach_tags = [] | ||
for tag in mvstream_tags: | ||
# Find the tag in the namlist | ||
matching_lines = [line for line in lines if tag in line] | ||
if not matching_lines: | ||
continue | ||
# Find the comment | ||
for line in matching_lines: | ||
if "!" not in line: | ||
continue | ||
comment = line.split("!")[1].strip() | ||
if flag in comment: | ||
jsbach_tags.append(tag) | ||
return jsbach_tags | ||
|
||
|
||
def append_namelist_dependent_sources(config): | ||
""" | ||
Append namelist dependent sources of JSBACH configuration. | ||
|
||
Parameters | ||
---------- | ||
config : dict | ||
|
||
Notes | ||
----- | ||
- The function reads the namelist and will filter mvstream tags that have | ||
the comment "ESM_TOOLS_JSBACH_STREAM". | ||
""" | ||
expid = config["general"]["expid"] | ||
jconfig = config["jsbach"] | ||
namelist_file = f"{jconfig['namelist_dir']}/namelist.echam" | ||
try: | ||
namelist = jconfig["namelist_objs"] | ||
except KeyError: | ||
# NOTE(PG): This is one of the reasons ECHAM/JSBACH is unfriendly... | ||
# JSBACH is controlled by the ECHAM namelist, this makes | ||
# it difficult to separate the two components. | ||
namelist = f90nml.read(namelist_file) | ||
mvstream_tags = _get_mvstream_tags_from_namelist(namelist) | ||
# Check if any of the JSBACH streams identified in the namelist | ||
# have a comment attached to them: | ||
flagged_tags = _get_comments_for_streams(namelist.file, mvstream_tags) | ||
mvstream_dict = {tag: f"{expid}*{tag}" for tag in flagged_tags} | ||
if namelist["runctl"].get("out_filetype") == 2: | ||
# Using NetCDF Outputs: | ||
mvstream_dict = {k: v + ".nc" for k, v in mvstream_dict.items()} | ||
logger.debug("Updating outdata_sources...") | ||
for k, v in mvstream_dict.items(): | ||
logger.debug(f"{k}: {v}") | ||
jconfig["outdata_sources"].update(mvstream_dict) | ||
logger.debug("...done!") | ||
|
||
return config |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Shouldn't this happen for
echam
but alsojsbach
? Doesn'tjsbach
also have its own streams?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.
This is a little bit convoluted: jsbach and echam are controlled by the same namelist, so I would need some way to filter out which tags go to which model. In principle this would just be defined by which model config they come from.
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.
See 509ac21.
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.
Do you mean that they can be controlled from the same namelist? If that is the case, then I understand the rest of your reply and the new
jsbach.py
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.
Yes, the output generated from
jsbach
is defined inmvstreamctl
chapters ofnamelist.echam
(since JSBACH technically speaking is just a submodule inside of ECHAM), and does not have any output control in it's own namelist.