Skip to content

Commit

Permalink
Merge branch 'develop' into feature/stable-symlink
Browse files Browse the repository at this point in the history
  • Loading branch information
CoryMartin-NOAA committed Apr 17, 2024
2 parents badc1bd + 1389383 commit b2794c3
Show file tree
Hide file tree
Showing 21 changed files with 468 additions and 57 deletions.
1 change: 1 addition & 0 deletions modulefiles/GDAS/hera.intel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ setenv('MPIEXEC_NPROC', mpinproc)

setenv("CRTM_FIX","/scratch1/NCEPDEV/da/Cory.R.Martin/GDASApp/fix/crtm/2.4.0")
setenv("GDASAPP_TESTDATA","/scratch1/NCEPDEV/da/Cory.R.Martin/CI/GDASApp/data")
setenv("GDASAPP_UNIT_TEST_DATA_PATH", "/scratch1/NCEPDEV/da/Cory.R.Martin/CI/GDASApp/data/test")
--prepend_path("PATH","/scratch2/NCEPDEV/nwprod/hpc-stack/libs/hpc-stack/intel-18.0.5.274/prod_util/1.2.2/bin")

whatis("Name: ".. pkgName)
Expand Down
1 change: 1 addition & 0 deletions modulefiles/GDAS/hercules.intel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ setenv('MPIEXEC_NPROC', mpinproc)

setenv("CRTM_FIX","/work2/noaa/da/cmartin/GDASApp/fix/crtm/2.4.0")
setenv("GDASAPP_TESTDATA","/work2/noaa/da/cmartin/CI/GDASApp/data")
setenv("GDASAPP_UNIT_TEST_DATA_PATH", "/work2/noaa/da/cmartin/CI/GDASApp/data/test/")
prepend_path("PATH","/apps/contrib/NCEP/libs/hpc-stack/intel-2018.4/prod_util/1.2.2/bin")

execute{cmd="ulimit -s unlimited",modeA={"load"}}
Expand Down
1 change: 1 addition & 0 deletions modulefiles/GDAS/orion.intel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ setenv('MPIEXEC_NPROC', mpinproc)

setenv("CRTM_FIX","/work2/noaa/da/cmartin/GDASApp/fix/crtm/2.4.0")
setenv("GDASAPP_TESTDATA","/work2/noaa/da/cmartin/CI/GDASApp/data")
setenv("GDASAPP_UNIT_TEST_DATA_PATH", "/work2/noaa/da/cmartin/CI/GDASApp/data/test/")
prepend_path("PATH","/apps/contrib/NCEP/libs/hpc-stack/intel-2018.4/prod_util/1.2.2/bin")

execute{cmd="ulimit -s unlimited",modeA={"load"}}
Expand Down
2 changes: 0 additions & 2 deletions parm/atm/obs/lists/gdas_prototype_3d.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ observers:
{% include 'atm/obs/config/ompsnp_npp.yaml.j2' %}
{% include 'atm/obs/config/ompstc_npp.yaml.j2' %}
{% include 'atm/obs/config/omi_aura.yaml.j2' %}
{% include 'atm/obs/config/seviri_m08.yaml.j2' %}
{% include 'atm/obs/config/seviri_m11.yaml.j2' %}
{% endfilter %}
2 changes: 1 addition & 1 deletion parm/snow/obs/config/adpsfc_snow.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
obsdatain:
engine:
type: H5File
obsfile: '{{ DATA }}/obs/{{ OPREFIX }}adpsfc_snow.nc4'
obsfile: '{{ DATA }}/obs/{{ OPREFIX_OUT }}adpsfc_snow.nc4'
obsdataout:
engine:
type: H5File
Expand Down
2 changes: 1 addition & 1 deletion parm/snow/obs/config/ims_snow.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
obsdatain:
engine:
type: H5File
obsfile: '{{ DATA }}/obs/{{ OPREFIX }}ims_snow.nc4'
obsfile: '{{ DATA }}/obs/{{ OPREFIX_OUT }}ims_snow.{{ CASE }}.nc4'
obsdataout:
engine:
type: H5File
Expand Down
2 changes: 1 addition & 1 deletion parm/snow/obs/config/snocvr_snow.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
obsdatain:
engine:
type: H5File
obsfile: '{{ DATA }}/obs/{{ OPREFIX }}snocvr_snow.nc4'
obsfile: '{{ DATA }}/obs/{{ OPREFIX_OUT }}snocvr_snow.nc4'
obsdataout:
engine:
type: H5File
Expand Down
14 changes: 11 additions & 3 deletions parm/snow/prep/prep_gts.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ gtsbufr:
mkdir:
- '{{ DATA }}/obs'
copy:
- ['{{ COM_OBS }}/{{ OPREFIX }}adpsfc.tm00.bufr_d', '{{ DATA }}/obs/']
- ['{{ COMIN_OBS }}/{{ OPREFIX_IN }}adpsfc.tm00.bufr_d', '{{ DATA }}/obs/']
gtsioda:
copy:
- ['{{ DATA }}/{{ OPREFIX }}adpsfc_snow.nc4', '{{ COM_OBS }}/{{ OPREFIX }}adpsfc_snow.nc4']
gfs:
copy:
- ['{{ DATA }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4', '{{ COMOUT_OBS }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4']
gdas:
copy:
- ['{{ DATA }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4', '{{ COMOUT_OBS }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4']
enkfgdas:
copy:
- ['{{ DATA }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4', '{{ COMOUT_OBS }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4']
- ['{{ COMIN_OBS }}/{{ OPREFIX_IN }}snocvr_snow.nc4', '{{ COMOUT_OBS }}/{{ OPREFIX_OUT }}snocvr_snow.nc4']
bufr2ioda:
adpsfc: '{{ HOMEgfs }}/sorc/gdas.cd/test/testinput/bufr_adpsfc_snow.yaml'
4 changes: 2 additions & 2 deletions parm/snow/prep/prep_ims.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ calcfims:
mkdir:
- '{{ DATA }}/obs'
copy:
- ['{{ COM_OBS }}/{{ OPREFIX }}ims{{ current_cycle | to_julian }}_4km_v1.3.nc', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.nc']
- ['{{ COMIN_OBS }}/{{ OPREFIX_IN }}ims{{ current_cycle | to_julian }}_4km_v1.3.nc', '{{ DATA }}/obs/ims{{ current_cycle | to_julian }}_4km_v1.3.nc']
- ['{{ FIXgfs }}/gdas/obs/ims/IMS_4km_to_{{ CASE }}.mx{{ OCNRES }}.nc', '{{ DATA }}/obs/IMS4km_to_FV3_mapping.{{ CASE }}_oro_data.nc']
ims2ioda:
copy:
- ['{{ DATA }}/ims_snow_{{ current_cycle | to_YMDH }}.nc4', '{{ COM_OBS }}/{{ OPREFIX }}ims_snow.nc4']
- ['{{ DATA }}/ims_snow_{{ current_cycle | to_YMDH }}.nc4', '{{ COMOUT_OBS }}/{{ OPREFIX_OUT }}ims_snow.{{ CASE }}.nc4']

7 changes: 7 additions & 0 deletions parm/soca/obs/obs_stats.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
time window:
begin: '1900-01-01T00:00:00Z'
end: '2035-01-01T00:00:00Z'
bound to include: begin

obs spaces:
{{ obs_spaces }}
77 changes: 74 additions & 3 deletions scripts/exgdas_global_marine_analysis_post.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
import glob
import shutil
from datetime import datetime, timedelta
from wxflow import FileHandler, Logger
from wxflow import AttrDict, FileHandler, Logger, parse_j2yaml
from multiprocessing import Process
import subprocess
import netCDF4
import re

logger = Logger()

Expand All @@ -36,8 +40,6 @@ def list_all_files(dir_in, dir_out, wc='*', fh_list=[]):
return fh_list


logger.info(f"---------------- Copy from RUNDIR to COMOUT")

com_ocean_analysis = os.getenv('COM_OCEAN_ANALYSIS')
com_ice_restart = os.getenv('COM_ICE_RESTART')
anl_dir = os.getenv('DATA')
Expand All @@ -52,6 +54,8 @@ def list_all_files(dir_in, dir_out, wc='*', fh_list=[]):
bdate = datetime.strftime(bdatedt, '%Y-%m-%dT%H:00:00Z')
mdate = datetime.strftime(datetime.strptime(cdate, '%Y%m%d%H'), '%Y-%m-%dT%H:00:00Z')

logger.info(f"---------------- Copy from RUNDIR to COMOUT")

post_file_list = []

# Make a copy the IAU increment
Expand Down Expand Up @@ -106,3 +110,70 @@ def list_all_files(dir_in, dir_out, wc='*', fh_list=[]):
os.path.join(com_ocean_analysis, 'yaml'), wc='*.yaml', fh_list=fh_list)

FileHandler({'copy': fh_list}).sync()

# obs space statistics
logger.info(f"---------------- Compute basic stats")
diags_list = glob.glob(os.path.join(os.path.join(com_ocean_analysis, 'diags', '*.nc4')))
obsstats_j2yaml = str(os.path.join(os.getenv('HOMEgfs'), 'sorc', 'gdas.cd',
'parm', 'soca', 'obs', 'obs_stats.yaml.j2'))


# function to create a minimalist ioda obs sapce
def create_obs_space(data):
os_dict = {"obs space": {
"name": data["obs_space"],
"obsdatain": {
"engine": {"type": "H5File", "obsfile": data["obsfile"]}
},
"simulated variables": [data["variable"]]
},
"variable": data["variable"],
"experiment identifier": data["pslot"],
"csv output": data["csv_output"]
}
return os_dict


# attempt to extract the experiment id from the path
pslot = os.path.normpath(com_ocean_analysis).split(os.sep)[-5]

# iterate through the obs spaces and generate the yaml for gdassoca_obsstats.x
obs_spaces = []
for obsfile in diags_list:

# define an obs space name
obs_space = re.sub(r'\.\d{10}\.nc4$', '', os.path.basename(obsfile))

# get the variable name, assume 1 variable per file
nc = netCDF4.Dataset(obsfile, 'r')
variable = next(iter(nc.groups["ObsValue"].variables))
nc.close()

# filling values for the templated yaml
data = {'obs_space': os.path.basename(obsfile),
'obsfile': obsfile,
'pslot': pslot,
'variable': variable,
'csv_output': os.path.join(com_ocean_analysis,
f"{RUN}.t{cyc}z.ocn.{obs_space}.stats.csv")}
obs_spaces.append(create_obs_space(data))

# create the yaml
data = {'obs_spaces': obs_spaces}
conf = parse_j2yaml(path=obsstats_j2yaml, data=data)
stats_yaml = 'diag_stats.yaml'
conf.save(stats_yaml)

# run the application
# TODO(GorA): this should be setup properly in the g-w once gdassoca_obsstats is in develop
gdassoca_obsstats_exec = os.path.join(os.getenv('HOMEgfs'),
'sorc', 'gdas.cd', 'build', 'bin', 'gdassoca_obsstats.x')
command = f"{os.getenv('launcher')} {gdassoca_obsstats_exec} {stats_yaml}"
logger.info(f"{command}")
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# issue a warning if the process has failed
if result.returncode != 0:
logger.warning(f"{command} has failed")
if result.stderr:
print("STDERR:", result.stderr.decode())
28 changes: 16 additions & 12 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ set(URL "https://ftp.emc.ncep.noaa.gov/static_files/public/GDASApp")
set(SHA "bda76f96666405f72abef56ad4d7d5b93ca153cd7d675853fbdb199096949f8a")
string(SUBSTRING ${SHA} 0 6 SHORTSHA)
set(TAR "gdasapp-fix-${SHORTSHA}.tgz")
# download test files
file(DOWNLOAD
${URL}/${TAR}
${CMAKE_CURRENT_BINARY_DIR}/${TAR}
INACTIVITY_TIMEOUT 30
TIMEOUT 90
SHOW_PROGRESS
STATUS status
EXPECTED_HASH SHA256=${SHA}
)
# Extract downloaded tarball.
IF(DEFINED ENV{GDASAPP_UNIT_TEST_DATA_PATH})
set(GDASAPP_UNIT_TEST_DATA_PATH "$ENV{GDASAPP_UNIT_TEST_DATA_PATH}")
file(COPY ${GDASAPP_UNIT_TEST_DATA_PATH}/${TAR} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
ELSE()
# download test files
file(DOWNLOAD
${URL}/${TAR}
${CMAKE_CURRENT_BINARY_DIR}/${TAR}
INACTIVITY_TIMEOUT 30
TIMEOUT 90
SHOW_PROGRESS
STATUS status
EXPECTED_HASH SHA256=${SHA}
)
ENDIF()
# Extract tarball.
file(ARCHIVE_EXTRACT INPUT ${CMAKE_CURRENT_BINARY_DIR}/${TAR}
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

# list of test binary/data files to install
list(APPEND test_data
${CMAKE_CURRENT_BINARY_DIR}/testdata/atminc_compress.nc4
Expand Down
5 changes: 3 additions & 2 deletions test/soca/gw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ set(jjob_list "JGLOBAL_PREP_OCEAN_OBS"
"JGDAS_GLOBAL_OCEAN_ANALYSIS_RUN"
"JGDAS_GLOBAL_OCEAN_ANALYSIS_ECEN"
"JGDAS_GLOBAL_OCEAN_ANALYSIS_CHKPT"
"JGDAS_GLOBAL_OCEAN_ANALYSIS_POST"
"JGDAS_GLOBAL_OCEAN_ANALYSIS_VRFY")
"JGDAS_GLOBAL_OCEAN_ANALYSIS_POST")
# TODO(WaterPeople) Add back to the list of tested jobs once fixed
# "JGDAS_GLOBAL_OCEAN_ANALYSIS_VRFY")

set(setup "")
foreach(jjob ${jjob_list})
Expand Down
4 changes: 2 additions & 2 deletions test/testinput/bufr_adpsfc_snow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ observations:
- obs space:
name: bufr

obsdatain: '{{ DATA }}/obs/{{ OPREFIX }}adpsfc.tm00.bufr_d'
obsdatain: '{{ DATA }}/obs/{{ OPREFIX_IN }}adpsfc.tm00.bufr_d'

exports:
variables:
Expand Down Expand Up @@ -39,7 +39,7 @@ observations:

ioda:
backend: netcdf
obsdataout: '{{ DATA }}/{{ OPREFIX }}adpsfc_snow.nc4'
obsdataout: '{{ DATA }}/{{ OPREFIX_OUT }}adpsfc_snow.nc4'

variables:

Expand Down
97 changes: 97 additions & 0 deletions ush/soca/gdassoca_obsstats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3


# creates figures of timeseries from the csv outputs computed by gdassoca_obsstats.x
import argparse
from itertools import product
import os
import glob
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates


class ObsStats:
def __init__(self):
self.data = pd.DataFrame()

def read_csv(self, filepaths):
# Iterate through the list of file paths and append their data
for filepath in filepaths:
new_data = pd.read_csv(filepath)
# Convert date to datetime for easier plotting
new_data['date'] = pd.to_datetime(new_data['date'], format='%Y%m%d%H')
self.data = pd.concat([self.data, new_data], ignore_index=True)

def plot_timeseries(self, ocean, variable):
# Filter data for the given ocean and variable
filtered_data = self.data[(self.data['Ocean'] == ocean) & (self.data['Variable'] == variable)]

if filtered_data.empty:
print("No data available for the given ocean and variable combination.")
return

# Get unique experiments
experiments = filtered_data['Exp'].unique()

# Plot settings
fig, axs = plt.subplots(3, 1, figsize=(10, 15), sharex=True)
fig.suptitle(f'{variable} statistics, {ocean} ocean', fontsize=16, fontweight='bold')

for exp in experiments:
exp_data = filtered_data[filtered_data['Exp'] == exp]

# Plot RMSE
axs[0].plot(exp_data['date'], exp_data['RMSE'], marker='o', linestyle='-', label=exp)
axs[0].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H'))
axs[0].xaxis.set_major_locator(mdates.DayLocator())
axs[0].tick_params(labelbottom=False)
axs[0].set_ylabel('RMSE')
axs[0].legend()
axs[0].grid(True)

# Plot Bias
axs[1].plot(exp_data['date'], exp_data['Bias'], marker='o', linestyle='-', label=exp)
axs[1].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H'))
axs[1].xaxis.set_major_locator(mdates.DayLocator())
axs[1].tick_params(labelbottom=False)
axs[1].set_ylabel('Bias')
axs[1].legend()
axs[1].grid(True)

# Plot Count
axs[2].plot(exp_data['date'], exp_data['Count'], marker='o', linestyle='-', label=exp)
axs[2].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H'))
axs[2].xaxis.set_major_locator(mdates.DayLocator())
axs[2].set_ylabel('Count')
axs[2].legend()
axs[2].grid(True)

# Improve layout and show plot
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.savefig(f'{ocean}_test.png')


if __name__ == "__main__":
epilog = ["Usage examples: ./gdassoca_obsstats.py --exp gdas.*.csv"]
parser = argparse.ArgumentParser(description="Observation space RMSE's and BIAS's",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=os.linesep.join(epilog))
parser.add_argument("--exps", nargs='+', required=True,
help="Path to the experiment's COMROOT")
parser.add_argument("--variable", required=True, help="ombg_qc or ombg_noqc")
parser.add_argument("--figname", required=True, help="The name of the output figure")
args = parser.parse_args()

flist = []
for exp in args.exps:
print(exp)
wc = exp + '/*.*/??/analysis/ocean/*.t??z.stats.csv'
flist.append(glob.glob(wc))

flist = sum(flist, [])
obsStats = ObsStats()
obsStats.read_csv(flist)
for var, ocean in product(['ombg_noqc', 'ombg_qc'],
['Atlantic', 'Pacific', 'Indian', 'Arctic', 'Southern']):
obsStats.plot_timeseries(ocean, var)
6 changes: 5 additions & 1 deletion ush/soca/run_jjobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,16 @@ def _conda_envs(self, jjob):
"""
Write a section that will load the machine dependent modules
"""
if self.machine != "container":
if self.machine in ["orion", "hercules"]:
if jjob in ENVS:
# set +/-u is a workaround for an apparent conda bug
self.f.write(f"set +u \n")
self.f.write(f"conda activate {ENVS[jjob]} \n")
self.f.write(f"set -u \n")
elif self.machine == "hera":
if jjob in ENVS:
self.f.write(f"module unload GDAS \n")
self.f.write(f"module load {ENVS[jjob].upper()/{self.machine}} \n")

def precom(self, com, tmpl):
cmd = f"RUN={self.RUN} YMD={self.gPDY} HH={self.gcyc} declare_from_tmpl -xr {com}:{tmpl}"
Expand Down
Loading

0 comments on commit b2794c3

Please sign in to comment.