Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c8e5f5e
Restore 3dvar capability to run_jedi_exe.py (#223)
RussTreadon-NOAA Jan 17, 2023
3d3298a
Correct python norm errors (#223)
RussTreadon-NOAA Jan 17, 2023
30a4f79
Merge branch 'develop' into feature/runjedi
RussTreadon-NOAA Jan 17, 2023
4528030
Remove yaml_template from 3dvar_orion.yaml (#223)
RussTreadon-NOAA Jan 17, 2023
92d21cc
add genYAML.py; clean up run_jedi_exe.py (#223)
RussTreadon-NOAA Jan 18, 2023
d306591
Update genYAML to use ufsda genYAML.py (#223)
RussTreadon-NOAA Jan 18, 2023
55f4217
add ufsda.genYAML to __init__.py (#223)
RussTreadon-NOAA Jan 19, 2023
2cda39e
remove genYAML from yamltools.py (#223)
RussTreadon-NOAA Jan 19, 2023
3d2f3c0
remove __main__ from genYAML.py
RussTreadon-NOAA Jan 19, 2023
c78f588
revert changes to ush/genYAML
RussTreadon-NOAA Jan 19, 2023
d06c497
remove stage from init, simplify genYAML
RussTreadon-NOAA Jan 19, 2023
f2fc7ef
import specific ufsda.stage functions in run_jedi_exe.py
RussTreadon-NOAA Jan 19, 2023
d669348
replace exclusive with mem=0 in sbatch jobcard
RussTreadon-NOAA Jan 20, 2023
0277523
Merge branch 'develop' into feature/runjedi
RussTreadon-NOAA Jan 20, 2023
112c4cd
add ufsda.stage import to marine_analysis_prep script
RussTreadon-NOAA Jan 20, 2023
3c828e2
use RussTreadon-NOAA/global-workflow feature/ufsda_stage in automated…
RussTreadon-NOAA Jan 21, 2023
e896c06
Revert "use RussTreadon-NOAA/global-workflow feature/ufsda_stage in a…
RussTreadon-NOAA Jan 23, 2023
331e5c5
update keywords in hera 3dvar yaml
RussTreadon-NOAA Jan 23, 2023
32796f7
provide default for NMEM_ENKF in run_jedi_exe.py
RussTreadon-NOAA Jan 23, 2023
319ed40
Merge branch 'develop' into feature/runjedi
RussTreadon-NOAA Jan 23, 2023
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
6 changes: 3 additions & 3 deletions parm/atm/berror/staticb_gsibec.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
covariance model: SABER
full inverse: true
saber blocks:
- saber block name: gsi covariance
saber central block: true
saber central block:
saber block name: gsi covariance
input variables: &control_vars [eastward_wind,northward_wind,air_temperature,surface_pressure,
specific_humidity,cloud_liquid_ice,cloud_liquid_water,
mole_fraction_of_ozone_in_air]
Expand All @@ -14,6 +13,7 @@ saber blocks:
processor layout x direction: &layout_gsib_x 3
processor layout y direction: &layout_gsib_y 2
debugging mode: false
saber outer blocks:
- saber block name: gsi interpolation to model grid
input variables: *control_vars
output variables: *control_vars
Expand Down
1 change: 1 addition & 0 deletions scripts/exgdas_global_marine_analysis_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

# import UFSDA utilities
import ufsda
from ufsda.stage import obs, soca_fix


def agg_seaice(fname_in, fname_out):
Expand Down
10 changes: 8 additions & 2 deletions ush/examples/run_jedi_exe/3dvar_hera.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
working directory: /scratch2/NCEPDEV/stmp1/Cory.R.Martin/gdas_single_test_3dvar
GDASApp home: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp
GDASApp mode: variational
executable options:
template: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/parm/atm/variational/3dvar_dripcg.yaml
config:
berror_yaml: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/parm/atm/berror/staticb_gsibec.yaml
obs_dir: obs
diag_dig: diags
crtm_coeff_dir: crtm
bias_in_dir: obs
bias_out_dir: bc
obs_yaml_dir: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/parm/atm/obs/config
yaml_template: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/parm/atm/variational/3dvar_dripcg.yaml
executable: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/build/bin/fv3jedi_var.x
obs_list: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/work/GDASApp/parm/atm/obs/lists/gdas_prototype.yaml
gdas_fix_root: /scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/fix
Expand All @@ -19,6 +24,7 @@ executable options:
staticb_type: gsibec
dohybvar: false
levs: 128
nmem: 10
interp_method: barycentric
job options:
machine: hera
Expand Down
10 changes: 8 additions & 2 deletions ush/examples/run_jedi_exe/3dvar_orion.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
working directory: /work2/noaa/stmp/cmartin/gdas_single_test_3dvar
GDASApp home: /work2/noaa/da/cmartin/GDASApp/work/GDASApp
GDASApp mode: variational
executable options:
template: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/parm/atm/variational/3dvar_dripcg.yaml
config:
berror_yaml: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/parm/atm/berror/staticb_gsibec.yaml
obs_dir: obs
diag_dig: diags
crtm_coeff_dir: crtm
bias_in_dir: obs
bias_out_dir: bc
obs_yaml_dir: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/parm/atm/obs/config
yaml_template: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/parm/atm/variational/3dvar_dripcg.yaml
executable: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/build/bin/fv3jedi_var.x
obs_list: /work2/noaa/da/cmartin/GDASApp/work/GDASApp/parm/atm/obs/lists/gdas_prototype.yaml
gdas_fix_root: /work2/noaa/da/cmartin/GDASApp/fix
Expand All @@ -19,6 +24,7 @@ executable options:
staticb_type: gsibec
dohybvar: false
levs: 128
nmem: 10
interp_method: barycentric
job options:
machine: orion
Expand Down
85 changes: 1 addition & 84 deletions ush/genYAML
Original file line number Diff line number Diff line change
Expand Up @@ -8,91 +8,8 @@ import logging
import os
import re
import yaml
from pygw.template import Template, TemplateConstants
from pygw.yaml_file import YAMLFile

def genYAML(yamlconfig):
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
# open YAML file to get config
try:
with open(yamlconfig, 'r') as yamlconfig_opened:
all_config_dict = yaml.safe_load(yamlconfig_opened)
logging.info(f'Loading configuration from {yamlconfig}')
except Exception as e:
logging.error(f'Error occurred when attempting to load: {yamlconfig}, error: {e}')
output_file = all_config_dict['output']
template = all_config_dict['template']
config_dict = all_config_dict['config']
# what if the config_dict has environment variables that need substituted?
pattern = re.compile(r'.*?\${(\w+)}.*?')
for key, value in config_dict.items():
if type(value) == str:
match = pattern.findall(value)
if match:
fullvalue = value
for g in match:
config_dict[key] = fullvalue.replace(
f'${{{g}}}', os.environ.get(g, f'${{{g}}}')
)
# NOTE the following is a hack until YAMLFile can take in an input config dict
# if something in the template is expected to be an env var
# but it is not defined in the env, problems will arise
# so we set the env var in this subprocess for the substitution to occur
for key, value in config_dict.items():
os.environ[key] = str(value)
# next we need to compute a few things
runtime_config = get_runtime_config(dict(os.environ, **config_dict))
# now run the global-workflow parser
outconfig = YAMLFile(path=template)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOUBLE_CURLY_BRACES, config_dict.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOLLAR_PARENTHESES, config_dict.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOUBLE_CURLY_BRACES, runtime_config.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOLLAR_PARENTHESES, runtime_config.get)
outconfig.save(output_file)


def get_runtime_config(config_dict):
# compute some runtime variables
# this will probably need pulled out somewhere else eventually
# a temporary hack to get UFO evaluation stuff and ATM VAR going again
valid_time = dt.datetime.strptime(config_dict['CDATE'], '%Y%m%d%H')
assim_freq = int(config_dict.get('assim_freq', 6))
window_begin = valid_time - dt.timedelta(hours=assim_freq/2)
window_end = valid_time + dt.timedelta(hours=assim_freq/2)
component_dict = {
'atmos': 'ATM',
'chem': 'AERO',
'ocean': 'SOCA',
'land': 'land',
}
win_begin_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_BEGIN'
win_end_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_END'
win_len_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_LENGTH'
bkg_string_var = 'BKG_YYYYmmddHHMMSS'
bkg_isotime_var = 'BKG_ISOTIME'
npx_ges_var = 'npx_ges'
npy_ges_var = 'npy_ges'
npz_ges_var = 'npz_ges'
npx_anl_var = 'npx_anl'
npy_anl_var = 'npy_anl'
npz_anl_var = 'npz_anl'

runtime_config = {
win_begin_var: f"{window_begin.strftime('%Y-%m-%dT%H:%M:%SZ')}",
win_end_var: f"{window_end.strftime('%Y-%m-%dT%H:%M:%SZ')}",
win_len_var: f"PT{assim_freq}H",
bkg_string_var: f"{valid_time.strftime('%Y%m%d.%H%M%S')}",
bkg_isotime_var: f"{valid_time.strftime('%Y-%m-%dT%H:%M:%SZ')}",
npx_ges_var : f"{int(os.environ['CASE'][1:]) + 1}",
npy_ges_var : f"{int(os.environ['CASE'][1:]) + 1}",
npz_ges_var : f"{int(os.environ['LEVS']) - 1}",
npx_anl_var : f"{int(os.environ['CASE_ENKF'][1:]) + 1}",
npy_anl_var : f"{int(os.environ['CASE_ENKF'][1:]) + 1}",
npz_anl_var : f"{int(os.environ['LEVS']) - 1}",
}

return runtime_config

from ufsda.genYAML import genYAML

if __name__ == "__main__":
parser = argparse.ArgumentParser()
Expand Down
29 changes: 23 additions & 6 deletions ush/run_jedi_exe.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import sys
import yaml

from ufsda.genYAML import genYAML


def export_envar(yamlfile, bashout):

Expand Down Expand Up @@ -56,10 +58,12 @@ def run_jedi_exe(yamlconfig):
sys.path.append(ufsda_path)
import ufsda
from ufsda.misc_utils import calc_fcst_steps
from ufsda.stage import gdas_single_cycle, gdas_fix

# compute config for YAML for executable
executable_subconfig = all_config_dict['executable options']
executable_subconfig = all_config_dict['config']
valid_time = executable_subconfig['valid_time']
assim_freq = int(executable_subconfig.get('assim_freq', 6))
h = re.findall('PT(\\d+)H', executable_subconfig['atm_window_length'])[0]
prev_cycle = valid_time - dt.timedelta(hours=int(h))
window_begin = valid_time - dt.timedelta(hours=int(h)/2)
Expand All @@ -68,10 +72,19 @@ def run_jedi_exe(yamlconfig):
gcyc = prev_cycle.strftime("%H")
gdate = prev_cycle.strftime("%Y%m%d%H")
pdy = valid_time.strftime("%Y%m%d")
os.environ['PDY'] = str(pdy)
os.environ['cyc'] = str(cyc)
os.environ['assim_freq'] = str(assim_freq)
oprefix = executable_subconfig['dump'] + ".t" + str(cyc) + "z."
gprefix = executable_subconfig['dump'] + ".t" + str(gcyc) + "z."

if app_mode in ['hofx', 'variational']:
single_exec = True
var_config = {
'DATA': os.path.join(workdir),
'APREFIX': str(oprefix),
'OPREFIX': str(oprefix),
'GPREFIX': str(gprefix),
'BERROR_YAML': executable_subconfig.get('berror_yaml', './'),
'STATICB_TYPE': executable_subconfig.get('staticb_type', 'gsibec'),
'OBS_YAML_DIR': executable_subconfig['obs_yaml_dir'],
Expand All @@ -80,8 +93,8 @@ def run_jedi_exe(yamlconfig):
'layout_x': str(executable_subconfig['layout_x']),
'layout_y': str(executable_subconfig['layout_y']),
'BKG_DIR': os.path.join(workdir, 'bkg'),
'fv3jedi_fix_dir': os.path.join(workdir, 'Data', 'fv3files'),
'fv3jedi_fieldmetadata_dir': os.path.join(workdir, 'Data', 'fieldmetadata'),
'fv3jedi_fix_dir': os.path.join(workdir, 'fv3jedi'),
'fv3jedi_fieldmetadata_dir': os.path.join(workdir, 'fv3jedi'),
'ANL_DIR': os.path.join(workdir, 'anl'),
'fv3jedi_staticb_dir': os.path.join(workdir, 'berror'),
'BIAS_IN_DIR': os.path.join(workdir, 'obs'),
Expand All @@ -93,6 +106,8 @@ def run_jedi_exe(yamlconfig):
'OBS_DIR': os.path.join(workdir, 'obs'),
'OBS_PREFIX': f"{executable_subconfig['dump']}.t{cyc}z.",
'OBS_DATE': f"{cdate}",
'CDATE': f"{cdate}",
'GDATE': f"{gdate}",
'valid_time': f"{valid_time.strftime('%Y-%m-%dT%H:%M:%SZ')}",
'window_begin': f"{window_begin.strftime('%Y-%m-%dT%H:%M:%SZ')}",
'prev_valid_time': f"{prev_cycle.strftime('%Y-%m-%dT%H:%M:%SZ')}",
Expand All @@ -102,18 +117,20 @@ def run_jedi_exe(yamlconfig):
'CASE_ENKF': executable_subconfig.get('case_enkf', executable_subconfig['case']),
'DOHYBVAR': executable_subconfig.get('dohybvar', False),
'LEVS': str(executable_subconfig['levs']),
'NMEM_ENKF': executable_subconfig.get('nmem', 0),
'forecast_steps': calc_fcst_steps(executable_subconfig.get('forecast_step', 'PT6H'),
executable_subconfig['atm_window_length']),
'BKG_TSTEP': executable_subconfig.get('forecast_step', 'PT6H'),
'INTERP_METHOD': executable_subconfig.get('interp_method', 'barycentric'),
}
template = executable_subconfig['yaml_template']
output_file = os.path.join(workdir, f"gdas_{app_mode}.yaml")
# set some environment variables
os.environ['PARMgfs'] = os.path.join(all_config_dict['GDASApp home'], 'parm')
for key, value in var_config.items():
os.environ[key] = str(value)
# generate YAML for executable based on input config
logging.info(f'Using YAML template {template}')
ufsda.yamltools.genYAML(var_config, template=template, output=output_file)
logging.info(f'Using yamlconfig {yamlconfig}')
genYAML(yamlconfig, output=output_file)
logging.info(f'Wrote YAML file to {output_file}')
# use R2D2 to stage backgrounds, obs, bias correction files, etc.
ufsda.stage.gdas_single_cycle(var_config)
Expand Down
2 changes: 1 addition & 1 deletion ush/ufsda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .disk_utils import mkdir, symlink
from .ufs_yaml import gen_yaml, parse_config
import ufsda.stage
import ufsda.archive
import ufsda.r2d2
import ufsda.post
import ufsda.yamltools
import ufsda.genYAML
from .misc_utils import isTrue, create_batch_job, submit_batch_job
100 changes: 100 additions & 0 deletions ush/ufsda/genYAML.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# genYAML
# generate YAML using ufsda python module,
# current runtime env, and optional input YAML
import argparse
import datetime as dt
import logging
import os
import re
import yaml
from pygw.template import Template, TemplateConstants
from pygw.yaml_file import YAMLFile


def genYAML(yamlconfig, output=None):
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
# open YAML file to get config
try:
with open(yamlconfig, 'r') as yamlconfig_opened:
all_config_dict = yaml.safe_load(yamlconfig_opened)
logging.info(f'Loading configuration from {yamlconfig}')
except Exception as e:
logging.error(f'Error occurred when attempting to load: {yamlconfig}, error: {e}')

if not output:
output_file = all_config_dict['output']
else:
output_file = output

template = all_config_dict['template']
config_dict = all_config_dict['config']
# what if the config_dict has environment variables that need substituted?
pattern = re.compile(r'.*?\${(\w+)}.*?')
for key, value in config_dict.items():
if type(value) == str:
match = pattern.findall(value)
if match:
fullvalue = value
for g in match:
config_dict[key] = fullvalue.replace(
f'${{{g}}}', os.environ.get(g, f'${{{g}}}')
)
# NOTE the following is a hack until YAMLFile can take in an input config dict
# if something in the template is expected to be an env var
# but it is not defined in the env, problems will arise
# so we set the env var in this subprocess for the substitution to occur
for key, value in config_dict.items():
os.environ[key] = str(value)
# next we need to compute a few things
runtime_config = get_runtime_config(dict(os.environ, **config_dict))
# now run the global-workflow parser
outconfig = YAMLFile(path=template)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOUBLE_CURLY_BRACES, config_dict.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOLLAR_PARENTHESES, config_dict.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOUBLE_CURLY_BRACES, runtime_config.get)
outconfig = Template.substitute_structure(outconfig, TemplateConstants.DOLLAR_PARENTHESES, runtime_config.get)
outconfig.save(output_file)


def get_runtime_config(config_dict):
# compute some runtime variables
# this will probably need pulled out somewhere else eventually
# a temporary hack to get UFO evaluation stuff and ATM VAR going again
valid_time = dt.datetime.strptime(config_dict['CDATE'], '%Y%m%d%H')
assim_freq = int(config_dict.get('assim_freq', 6))
window_begin = valid_time - dt.timedelta(hours=assim_freq/2)
window_end = valid_time + dt.timedelta(hours=assim_freq/2)
component_dict = {
'atmos': 'ATM',
'chem': 'AERO',
'ocean': 'SOCA',
'land': 'land',
}
win_begin_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_BEGIN'
win_end_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_END'
win_len_var = component_dict[config_dict.get('COMPONENT', 'atmos')] + '_WINDOW_LENGTH'
bkg_string_var = 'BKG_YYYYmmddHHMMSS'
bkg_isotime_var = 'BKG_ISOTIME'
npx_ges_var = 'npx_ges'
npy_ges_var = 'npy_ges'
npz_ges_var = 'npz_ges'
npx_anl_var = 'npx_anl'
npy_anl_var = 'npy_anl'
npz_anl_var = 'npz_anl'

runtime_config = {
win_begin_var: f"{window_begin.strftime('%Y-%m-%dT%H:%M:%SZ')}",
win_end_var: f"{window_end.strftime('%Y-%m-%dT%H:%M:%SZ')}",
win_len_var: f"PT{assim_freq}H",
bkg_string_var: f"{valid_time.strftime('%Y%m%d.%H%M%S')}",
bkg_isotime_var: f"{valid_time.strftime('%Y-%m-%dT%H:%M:%SZ')}",
npx_ges_var: f"{int(os.environ['CASE'][1:]) + 1}",
npy_ges_var: f"{int(os.environ['CASE'][1:]) + 1}",
npz_ges_var: f"{int(os.environ['LEVS']) - 1}",
npx_anl_var: f"{int(os.environ['CASE_ENKF'][1:]) + 1}",
npy_anl_var: f"{int(os.environ['CASE_ENKF'][1:]) + 1}",
npz_anl_var: f"{int(os.environ['LEVS']) - 1}",
}

return runtime_config
2 changes: 1 addition & 1 deletion ush/ufsda/misc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def create_batch_job(job_config, working_dir, executable, yaml_path, single_exec
#SBATCH --ntasks={job_config['ntasks']}
#SBATCH --ntasks-per-node={taskspernode}
#SBATCH --cpus-per-task=1
#SBATCH --exclusive
#SBATCH --mem=0
#SBATCH -t {job_config['walltime']}"""
f.write(sbatch)
commands = f"""
Expand Down
Loading