Skip to content
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

Add LETKF suite and initial tasks. #232

Merged
merged 5 commits into from
Sep 19, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ clean_patterns:
- '*.txt'
- logfile.*.out

ensemble_num_members:
default_value: 10

geos_background_restart_offset:
default_value: TODO

Expand Down Expand Up @@ -153,6 +156,9 @@ observations:
- omi_aura
- ompsnm_npp

path_to_ensemble:
default_value: /discover/nobackup/drholdaw/SwellTestData/letk/ensemble/Y%Y/M%m/D%d/H%H

path_to_geos_adas_background:
default_value: /discover/nobackup/drholdaw/SwellTestData/geosadas/bkg/*bkg_clcv_rst*

Expand Down
1 change: 1 addition & 0 deletions src/swell/deployment/prep_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def prepare_cylc_suite_jinja2(logger, swell_suite_path, exp_suite_path, experime
'BuildGeos',
'GenerateBClimatology',
'RunJediHofxExecutable',
'RunJediLetkfExecutable',
'RunJediVariationalExecutable',
'RunJediUfoTestsExecutable',
'RunGeosExecutable',
Expand Down
155 changes: 155 additions & 0 deletions src/swell/suites/letkf/flow.cylc
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# (C) Copyright 2021- United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.

# --------------------------------------------------------------------------------------------------

# Cylc suite for executing JEDI-based h(x)

# --------------------------------------------------------------------------------------------------

[scheduler]
UTC mode = True
allow implicit tasks = False

# --------------------------------------------------------------------------------------------------

[scheduling]

initial cycle point = {{start_cycle_point}}
final cycle point = {{final_cycle_point}}
runahead limit = {{runahead_limit}}

[[graph]]
R1 = """
# Triggers for non cycle time dependent tasks
# -------------------------------------------
# Clone JEDI source code
CloneJedi

# Build JEDI source code by linking
CloneJedi => BuildJediByLinking?

# If not able to link to build create the build
BuildJediByLinking:fail? => BuildJedi

{% for model_component in model_components %}
# Stage JEDI static files
CloneJedi => StageJedi-{{model_component}}
{% endfor %}
"""

{% for cycle_time in cycle_times %}
{{cycle_time.cycle_time}} = """
{% for model_component in model_components %}
{% if cycle_time[model_component] %}
# Task triggers for: {{model_component}}
# ------------------
# Get ensemble
GetEnsemble-{{model_component}}

# Get observations
GetObservations-{{model_component}}

# Perform staging that is cycle dependent
StageJediCycle-{{model_component}}

# Run Jedi hofx executable
BuildJediByLinking[^]? | BuildJedi[^] => RunJediLetkfExecutable-{{model_component}}
StageJedi-{{model_component}}[^] => RunJediLetkfExecutable-{{model_component}}
StageJediCycle-{{model_component}} => RunJediLetkfExecutable-{{model_component}}
GetEnsemble-{{model_component}} => RunJediLetkfExecutable-{{model_component}}
GetObservations-{{model_component}} => RunJediLetkfExecutable-{{model_component}}

# EvaObservations
RunJediLetkfExecutable-{{model_component}} => EvaObservations-{{model_component}}

# Save observations
RunJediLetkfExecutable-{{model_component}} => SaveObsDiags-{{model_component}}

# Clean up large files
EvaObservations-{{model_component}} & SaveObsDiags-{{model_component}} =>
CleanCycle-{{model_component}}

{% endif %}
{% endfor %}
"""
{% endfor %}

# --------------------------------------------------------------------------------------------------

[runtime]

# Task defaults
# -------------
[[root]]
pre-script = "source $CYLC_SUITE_DEF_PATH/modules"

[[[environment]]]
datetime = $CYLC_TASK_CYCLE_POINT
config = $CYLC_SUITE_DEF_PATH/experiment.yaml

# Tasks
# -----
[[CloneJedi]]
script = "swell_task CloneJedi $config"

[[BuildJediByLinking]]
script = "swell_task BuildJediByLinking $config"

[[BuildJedi]]
script = "swell_task BuildJedi $config"
platform = {{platform}}
execution time limit = {{scheduling["BuildJedi"]["execution_time_limit"]}}
[[[directives]]]
--account = {{scheduling["BuildJedi"]["account"]}}
--qos = {{scheduling["BuildJedi"]["qos"]}}
--job-name = BuildJedi
--nodes={{scheduling["BuildJedi"]["nodes"]}}
--ntasks-per-node={{scheduling["BuildJedi"]["ntasks_per_node"]}}
--constraint={{scheduling["BuildJedi"]["constraint"]}}
{% if scheduling["BuildJedi"]["partition"] %}
--partition={{scheduling["BuildJedi"]["partition"]}}
{% endif %}

{% for model_component in model_components %}
[[StageJedi-{{model_component}}]]
script = "swell_task StageJedi $config -m {{model_component}}"

[[StageJediCycle-{{model_component}}]]
script = "swell_task StageJedi $config -d $datetime -m {{model_component}}"

[[ GetEnsemble-{{model_component}} ]]
script = "swell_task GetEnsemble $config -d $datetime -m {{model_component}}"

[[GetObservations-{{model_component}}]]
script = "swell_task GetObservations $config -d $datetime -m {{model_component}}"

[[RunJediLetkfExecutable-{{model_component}}]]
script = "swell_task RunJediLetkfExecutable $config -d $datetime -m {{model_component}}"
platform = {{platform}}
execution time limit = {{scheduling["RunJediLetkfExecutable"]["execution_time_limit"]}}
[[[directives]]]
--account = {{scheduling["RunJediLetkfExecutable"]["account"]}}
--qos = {{scheduling["RunJediLetkfExecutable"]["qos"]}}
--job-name = RunJediLetkfExecutable
--nodes={{scheduling["RunJediLetkfExecutable"]["nodes"]}}
--ntasks-per-node={{scheduling["RunJediLetkfExecutable"]["ntasks_per_node"]}}
--constraint={{scheduling["RunJediLetkfExecutable"]["constraint"]}}
{% if scheduling["RunJediLetkfExecutable"]["partition"] %}
--partition={{scheduling["RunJediLetkfExecutable"]["partition"]}}
{% endif %}

[[EvaObservations-{{model_component}}]]
script = "swell_task EvaObservations $config -d $datetime -m {{model_component}}"

[[SaveObsDiags-{{model_component}}]]
script = "swell_task SaveObsDiags $config -d $datetime -m {{model_component}}"

[[CleanCycle-{{model_component}}]]
script = "swell_task CleanCycle $config -d $datetime -m {{model_component}}"
{% endfor %}

# --------------------------------------------------------------------------------------------------
26 changes: 26 additions & 0 deletions src/swell/suites/letkf/suite_questions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
start_cycle_point:
ask_question: True
default_value: '2021-12-12T00:00:00Z'
prompt: What is the time of the first cycle (middle of the window)?
type: iso-datetime

final_cycle_point:
ask_question: True
default_value: '2021-12-12T06:00:00Z'
prompt: What is the time of the final cycle (middle of the window)?
type: iso-datetime

runahead_limit:
ask_question: True
default_value: 'P4'
prompt: Since this suite is non-cycling choose how many hours the workflow can run ahead?
type: string

cycle_times:
ask_question: True
default_value: defer_to_model
options: defer_to_model
models:
- all
prompt: Enter the cycle times for this model.
type: string-check-list
2 changes: 2 additions & 0 deletions src/swell/tasks/base/task_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'GenerateBClimatology',
'GetBackgroundGeosExperiment',
'GetBackground',
'GetEnsemble',
'GetGeosAdasBackground',
'GetGeosRestart',
'GetGeovals',
Expand All @@ -35,6 +36,7 @@
'RemoveForecastDir',
'RunGeosExecutable',
'RunJediHofxExecutable',
'RunJediLetkfExecutable',
'RunJediUfoTestsExecutable',
'RunJediVariationalExecutable',
'SaveObsDiags',
Expand Down
69 changes: 69 additions & 0 deletions src/swell/tasks/get_ensemble.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# (C) Copyright 2021- United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.


# --------------------------------------------------------------------------------------------------


import datetime
import glob
import os
import re

from swell.tasks.base.task_base import taskBase


# --------------------------------------------------------------------------------------------------


class GetEnsemble(taskBase):

def execute(self):

# Get the path and pattern for the background files
# -------------------------------------------------
ensemble_path = self.config.path_to_ensemble()

# Fetch list of ensemble members
# --------------------------------
ensemble_members = glob.glob(ensemble_path)

# Assert at least one ensemble member was found
# -----------------------------------------------
self.logger.assert_abort(len(ensemble_members) != 0, f'No ensemble member ' +
f'files found in the source directory ' +
f'\'{ensemble_path}\'')

# Loop over all the ensemble members found
# -----------------------------------------
for ensemble_member in ensemble_members:

# Get filename from full path
member_file = os.path.basename(ensemble_member)

# Extract the datetime part from the string
datetime_part = re.search(r"\d{8}_\d{4}\w", member_file).group()

# Get datetime for the file from the filename
member_file_datetime = datetime.datetime.strptime(datetime_part, '%Y%m%d_%H%Mz')

# Create target filename using the datetime format
member_file_target = member_file_datetime.strftime('geos.mem001.%Y%m%d_%H%M%Sz.nc4')

# Target path and filename
ensemble_path_file_target = os.path.join(self.cycle_dir(), member_file_target)

# Remove target file if it exists (might be a link)
if os.path.exists(ensemble_path_file_target):
os.remove(ensemble_path_file_target)

# Create symlink from target to source
self.logger.info(f'Creating sym link from {ensemble_member} to '
f'{ensemble_path_file_target}')
os.symlink(ensemble_member, ensemble_path_file_target)


# --------------------------------------------------------------------------------------------------
Loading