Skip to content

Commit

Permalink
Prepare snow depth observations for JEDI-based Land DA. (#1609)
Browse files Browse the repository at this point in the history
This PR:
- adds a job to prepare IMS snow depth observations as a task in the workflow.  This task depends on the `prep.sh` job to bring IMS data.  To test this type of data `DMPDIR` in `config.base` needed to be pointed to `"/scratch1/NCEPDEV/global/Jiarui.Dong/JEDI/GlobalWorkflow/para_gfs/glopara_dump"`
- `land_analysis.py` introduces a method `prepare_IMS` for this type of data.
- Updates are necessary in the `GDASApp` repo.  See companion PR NOAA-EMC/GDASApp#472

This job only runs at the `18z` cycle in the workflow.  This is controlled in the XML and not in the script.
Consequently, updates are in the `workflow/` scripts to make this happen.

Other updates included:
- Adds a method (and associated test) to provide a datetime in the form of Julian day of the year.  The method is `to_julian` in `timetools.py`
- `executable.py` was updated to accept a `list` of arguments (in addition to `str`).

**Note:**
- `calcfIMS.exe` is a serial executable and yet requires to be run with `srun`.  I recommend compiling this executable without MPI. @jiaruidong2017 @CoryMartin-NOAA 
- There are several deprecation warnings from the python IODA converter that will need to be addressed.  I recommend creating an issue to be resolved later. @jiaruidong2017 @CoryMartin-NOAA 

Part of Land DA work for snow depth assimilation, for which there is no issue open.
  • Loading branch information
aerorahul committed May 16, 2023
1 parent 48b2a71 commit 579a44f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 10 deletions.
9 changes: 6 additions & 3 deletions ush/python/pygw/src/pygw/executable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import shlex
import subprocess
import sys
from typing import Any, Optional
from typing import Any, Optional, Union, List

__all__ = ["Executable", "which", "CommandNotFoundError"]

Expand Down Expand Up @@ -47,15 +47,18 @@ def __init__(self, name: str):
if not self.exe:
raise ProcessError(f"Cannot construct executable for '{name}'")

def add_default_arg(self, arg: str) -> None:
def add_default_arg(self, arg: Union[str, List]) -> None:
"""
Add a default argument to the command.
Parameters
----------
arg : str
argument to the executable
"""
self.exe.append(arg)
if isinstance(arg, list):
self.exe.extend(arg)
else:
self.exe.append(arg)

def add_default_env(self, key: str, value: Any) -> None:
"""
Expand Down
3 changes: 2 additions & 1 deletion ush/python/pygw/src/pygw/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pathlib import Path
from typing import Dict

from .timetools import strftime, to_YMDH, to_YMD, to_fv3time, to_isotime
from .timetools import strftime, to_YMDH, to_YMD, to_fv3time, to_isotime, to_julian

__all__ = ['Jinja']

Expand Down Expand Up @@ -114,6 +114,7 @@ def get_set_env(self, loader: jinja2.BaseLoader) -> jinja2.Environment:
env.filters["to_fv3time"] = lambda dt: to_fv3time(dt) if not isinstance(dt, SilentUndefined) else dt
env.filters["to_YMDH"] = lambda dt: to_YMDH(dt) if not isinstance(dt, SilentUndefined) else dt
env.filters["to_YMD"] = lambda dt: to_YMD(dt) if not isinstance(dt, SilentUndefined) else dt
env.filters["to_julian"] = lambda dt: to_julian(dt) if not isinstance(dt, SilentUndefined) else dt
return env

@staticmethod
Expand Down
29 changes: 27 additions & 2 deletions ush/python/pygw/src/pygw/timetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@


__all__ = ["to_datetime", "to_timedelta",
"datetime_to_YMDH", "datetime_to_YMD",
"datetime_to_YMDH", "datetime_to_YMD", "datetime_to_JDAY",
"timedelta_to_HMS",
"strftime", "strptime",
"to_YMDH", "to_YMD",
"to_YMDH", "to_YMD", "to_JDAY", "to_julian",
"to_isotime", "to_fv3time",
"add_to_datetime", "add_to_timedelta"]

Expand Down Expand Up @@ -151,6 +151,29 @@ def datetime_to_YMD(dt: datetime.datetime) -> str:
raise Exception(f"Bad datetime: '{dt}'")


def datetime_to_JDAY(dt: datetime.datetime) -> str:
"""
Description
-----------
Translate a datetime object to 'YYYYDOY' format.
Parameters
----------
dt : datetime.datetime
Datetime object to translate
Returns
-------
str: str
Formatted string in 'YYYYDOY' format.
"""
try:
return dt.strftime('%Y%j')
except Exception:
raise Exception(f"Bad datetime: '{dt}'")


def timedelta_to_HMS(td: datetime.timedelta) -> str:
"""
Description
Expand Down Expand Up @@ -289,3 +312,5 @@ def add_to_timedelta(td1, td2):

to_YMDH = datetime_to_YMDH
to_YMD = datetime_to_YMD
to_JDAY = datetime_to_JDAY
to_julian = datetime_to_JDAY
12 changes: 8 additions & 4 deletions ush/python/pygw/src/tests/test_timetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_strftime():

def test_strptime():
assert strptime(current_date.strftime('%Y%m%d'), '%Y%m%d') == \
datetime.strptime(current_date.strftime('%Y%m%d'), '%Y%m%d')
datetime.strptime(current_date.strftime('%Y%m%d'), '%Y%m%d')


def test_to_isotime():
Expand All @@ -56,13 +56,17 @@ def test_to_fv3time():
assert to_fv3time(current_date) == current_date.strftime('%Y%m%d.%H%M%S')


def test_to_julian():
assert to_julian(current_date) == current_date.strftime('%Y%j')


def test_add_to_timedelta():
assert add_to_timedelta(timedelta(days=1), timedelta(hours=3)) == \
timedelta(days=1, hours=3)
timedelta(days=1, hours=3)
assert add_to_timedelta(timedelta(hours=5, minutes=30), timedelta(minutes=15)) == \
timedelta(hours=5, minutes=45)
timedelta(hours=5, minutes=45)
assert add_to_timedelta(timedelta(seconds=45), timedelta(milliseconds=500)) == \
timedelta(seconds=45, milliseconds=500)
timedelta(seconds=45, milliseconds=500)


def test_add_to_datetime():
Expand Down

0 comments on commit 579a44f

Please sign in to comment.