Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2488693
updates from HFIP MPAS 2025 parallel
SamuelTrahanNOAA Nov 17, 2025
0fc78c2
simplify scrubbing dependencies
SamuelTrahanNOAA Nov 17, 2025
fafeb06
move tracker archiving to its own yaml
SamuelTrahanNOAA Nov 17, 2025
ba832e0
remove tracker from this branch
SamuelTrahanNOAA Nov 17, 2025
1211180
a few changes missing from prior commit
SamuelTrahanNOAA Nov 21, 2025
c0bd156
add gfs path on ursa
SamuelTrahanNOAA Nov 21, 2025
61cae6f
corrections taken from real-time runs
SamuelTrahanNOAA Nov 22, 2025
6d9c3a7
default 12 hour length of hfip 2025 forecast
SamuelTrahanNOAA Nov 22, 2025
a9fe72f
make archive target dir if it does not exist yet
SamuelTrahanNOAA Nov 22, 2025
ea11764
make archive target dir if it does not exist yet
SamuelTrahanNOAA Nov 22, 2025
9b862a6
do not override forecast length in hfip_2025.yaml
SamuelTrahanNOAA Nov 22, 2025
892a1e3
only archive mpassit on days with data to archive
SamuelTrahanNOAA Nov 22, 2025
a46f6cc
do not modify driver validation blocks in hfip parallel
SamuelTrahanNOAA Nov 24, 2025
60462e1
calculate ungrib start, step, stop at runtime
SamuelTrahanNOAA Nov 24, 2025
785ebc7
document archiving and scrubbing
SamuelTrahanNOAA Nov 24, 2025
c3e5b94
revert ungrib fix
SamuelTrahanNOAA Nov 24, 2025
d0a58a3
merge PR #98 with ungrib fix
SamuelTrahanNOAA Nov 24, 2025
1e3a42d
revert accidental change
SamuelTrahanNOAA Nov 24, 2025
cc57707
delete unneeded comment
SamuelTrahanNOAA Nov 24, 2025
b2c5303
better documentation of disabling scrubber & archiver steps
SamuelTrahanNOAA Nov 24, 2025
bb3cb11
New default cycles of hfip_2025: two days of Hurricane Erin
SamuelTrahanNOAA Nov 25, 2025
b6ad085
remove unneeded sleeps
SamuelTrahanNOAA Nov 25, 2025
146dff7
remove some environment variables
SamuelTrahanNOAA Nov 25, 2025
26b09fd
merge main
SamuelTrahanNOAA Nov 25, 2025
083da3e
put the tracker scripts back in
SamuelTrahanNOAA Nov 25, 2025
3a56ef8
Use hafs-community for tracker, branch feature/hafsv2p2_baseline
SamuelTrahanNOAA Nov 25, 2025
6225783
remove envars from tracker job in yaml
SamuelTrahanNOAA Nov 25, 2025
dd92078
build.sh builds the tracker
SamuelTrahanNOAA Nov 25, 2025
9367c66
address reviewer comments
SamuelTrahanNOAA Nov 25, 2025
ae66f5a
revert changes to output_is_available_for.sh
SamuelTrahanNOAA Nov 25, 2025
3e6eb3c
merge feature/archiving-scrubbing (PR 96)
SamuelTrahanNOAA Nov 25, 2025
351f83a
build.sh: install_tracker failed making an existing directory
SamuelTrahanNOAA Nov 25, 2025
795c78f
get it to a quasi-working state after removing mpiserial
SamuelTrahanNOAA Dec 2, 2025
c707e6c
make it configurable
SamuelTrahanNOAA Dec 8, 2025
de21a03
Adding driver files from HFIP_2024
christinaholtNOAA Dec 15, 2025
92e0f11
rename some directories and add configurability
SamuelTrahanNOAA Dec 15, 2025
1be2a19
merge main
SamuelTrahanNOAA Dec 15, 2025
9b20695
First pass at adding uwtools tracker driver components
christinaholtNOAA Dec 15, 2025
abf1f7a
it works again
SamuelTrahanNOAA Dec 16, 2025
f57c060
WIP
christinaholtNOAA Dec 18, 2025
1badc00
Add new files.
christinaholtNOAA Dec 18, 2025
37742ae
Finished tracker unittests.
christinaholtNOAA Dec 18, 2025
1a71c61
Updates from testing.
christinaholtNOAA Jan 8, 2026
f71ec46
Clean up.
christinaholtNOAA Jan 8, 2026
dd35a10
Merge remote-tracking branch 'sam/feature/archiving-scrubbing-tracker…
christinaholtNOAA Jan 8, 2026
48a1898
Changes needed for successful test.
christinaholtNOAA Jan 8, 2026
6c5349f
Merge pull request #1 from christinaholtNOAA/tracker_update
SamuelTrahanNOAA Jun 1, 2026
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
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@
[submodule "ush/pygraf"]
path = ush/pygraf
url = https://github.com/NOAA-GSL/pygraf.git
[submodule "src/GFDL-VortexTracker"]
path = src/GFDL-VortexTracker
url = https://github.com/hafs-community/gfdl-tracker
branch = feature/hafsv2p2_baseline
16 changes: 16 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,21 @@ install_upp () {
)
}

install_tracker () {
echo "=> Building the GFDL Vortex Tracker"
(
cd "$MPAS_APP_DIR/src/GFDL-VortexTracker/src/"
if [[ "$DEBUG" == false ]] ; then
export BUILD_TYPE=Release
else
export BUILD_TYPE=Debug
fi
./build_all_cmake.sh
test -d "$MPAS_APP_DIR/exec" || mkdir "$MPAS_APP_DIR/exec"
cp -v ../exec/*.x "$MPAS_APP_DIR/exec/."
)
}

prepare_conda () {
install_conda
test $CONDA_ONLY == true && exit 0
Expand Down Expand Up @@ -304,4 +319,5 @@ install_mpas init_atmosphere
install_mpas atmosphere
install_mpassit
install_upp
install_tracker
echo "=> Ready"
Empty file added drivers/__init__.py
Empty file.
83 changes: 83 additions & 0 deletions drivers/tracker.jsonschema
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"properties": {
"gfdltracker": {
"additionalProperaties": false,
"properties": {
"basins": {
"type": "string",
"pattern": "^[ABCELPQSW]+$"
},
"execution": {
"$ref": "urn:uwtools:execution-serial"
},
"input_files": {
"additionalProperties": false,
"properties": {
"endhour": {
"type": "number"
},
"filefreq": {
"type": "number"
},
"filepath": {
"type": "string"
}
},
"type": "object"
},
"namelist": {
"additionalProperties": false,
"anyOf": [
{
"required": [
"base_file"
]
},
{
"required": [
"update_values"
]
}
],
"properties": {
"base_file": {
"type": "string"
},
"update_values": {
"type": "object"
},
"validate": {
"type": "boolean"
}
},
"type": "object"
},
"rundir": {
"type": "string"
},
"tcvitals": {
"type": "string"
}
},
"required": [
"basins",
"execution",
"input_files",
"namelist",
"rundir",
"tcvitals"
],
"type": "object"
}
},
"required": [
"gfdltracker"
],
"type": "object"
}






201 changes: 201 additions & 0 deletions drivers/tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
"""
A driver for GFDL's Cyclone Tracker
"""

import re
from datetime import timedelta
from pathlib import Path

from iotaa import asset, task, tasks
from uwtools.config.formats.nml import NMLConfig
from uwtools.config.formats.yaml import YAMLConfig
from uwtools.config.validator import validate_internal
from uwtools.drivers.driver import Assets, DriverCycleBased
from uwtools.strings import STR
from uwtools.utils.file import writable
from uwtools.utils.processing import run_shell_cmd
from uwtools.utils.tasks import file, symlink

from ush.validation import TrackerNamelist


class GFDLTracker(DriverCycleBased):
"""
A driver for the GFDL Tracker
"""

# Workflow tasks

@task
def input_fcst_minutes(self):
"""
The input.fcst_minutes (fort.15) file.
"""
fn = "fort.15"
yield self.taskname("input fcst minutes file")
path = self.rundir / fn
yield asset(path, path.is_file)
yield [self.input_files(), self.input_index_files()]
content_list = [f"{i + 1:4d} {fhr * 60:5d}" for i, fhr in enumerate(self._input_file_map())]
with writable(path) as f:
f.write("\n".join(content_list))

@tasks
def input_files(self):
"""
The UPP-processed, combined forecast files.
"""
yield self.taskname("input files")
upp_files = self._input_file_map()
symlinks = {}
for fhr, target in upp_files.items():
linkname = f"mpas.trak.all.{self.cycle.strftime('%Y%m%d%H')}.f{fhr * 60:05d}"
symlinks[target] = linkname
yield [
symlink(target=Path(target), linkname=self.rundir / link)
for target, link in symlinks.items()
]

@task
def input_index_files(self):
"""
The Grib2 index files
"""
yield self.taskname("input index files")
index_files = {}
for fhr in self._input_file_map():
infile = str(
self.rundir / f"mpas.trak.all.{self.cycle.strftime('%Y%m%d%H')}.f{fhr * 60:05d}"
)
indexfile = f"{infile}.ix"
index_files[infile] = str(self.rundir / indexfile)
yield [asset(Path(idx), Path(idx).is_file) for idx in index_files.values()]
yield [self.input_files()]
self.rundir.mkdir(exist_ok=True)
envcmds = self.config["execution"]["envcmds"]
for infile, idx in index_files.items():
run_shell_cmd(
cmd=f"{' && '.join(envcmds)} && grb2index {infile} {idx}",
cwd=self.rundir,
log_output=True,
)

@task
def input_vitals(self):
"""
The TC vitals input data
"""
fn = "allvit"
yield self.taskname(f"TC vitals input file {fn}")
path = self.rundir / fn
yield asset(path, path.is_file)
tcvitals = Path(self.config["tcvitals"])
yield file(tcvitals)
datestr = self.cycle.strftime("%Y%m%d %H")
basins = self.config["basins"]
storms = []
with tcvitals.open() as f:
for line in f:
if datestr in line:
# Get all the storms in the required basins
# Matches: NHC 13L MELISSA 20251021 1200 143N 0713W 280 062.....
pattern = r"^.{5}[0-49][0-9][%s].{11}%s.*$" % (basins, datestr)
match = re.match(pattern, line)
if match:
storms.append(match.group(0))
with writable(path) as f:
f.write("\n".join(storms))

@task
def input_vitals_other_names(self):
"""
TC Vitals file named differently.
"""
fns = ["tcvit_rsmc_storms.txt", "fort.12"]
yield self.taskname(f"TC vitals input file {' & '.join(fns)}")
paths = [self.rundir / fn for fn in fns]
yield [asset(path, path.is_file) for path in paths]
yield self.input_vitals()
for path in paths:
symlink(target=self.rundir / "allvit", linkname=path)

@task
def namelist_file(self):
"""
The namelist file
"""
fn = "namelist.gettrk"
yield self.taskname(f"namelist file {fn}")
path = self.rundir / fn
yield asset(path, path.is_file)
input_files = []
namelist = self.config[STR.namelist]
if base_file := namelist.get(STR.basefile):
input_files.append(base_file)
yield [file(Path(input_file)) for input_file in input_files]
self.create_user_updated_config(
config_class=NMLConfig,
config_values=namelist,
path=path,
)

@tasks
def provisioned_rundir(self):
"""
Run directory provisioned with all required content.
"""
yield self.taskname(f"provisioned run directory: {self.rundir}")
self.rundir.mkdir(exist_ok=True)
yield [
self.input_fcst_minutes(),
self.input_files(),
self.input_index_files(),
self.input_vitals(),
self.input_vitals_other_names(),
self.namelist_file(),
self.runscript(),
]

# Private helper methods

@classmethod
def driver_name(cls) -> str:
"""
Returns the name of this driver.
"""
return "gfdltracker"

def _input_file_map(self) -> dict:
"""
Return a map of forecast hour to UPP file name.
"""
infiles = self.config["input_files"]
endhour = infiles["endhour"]
filefreq = infiles["filefreq"]
filemap = {}
for fhr in range(0, endhour + 1, filefreq):
leadtime = timedelta(hours=fhr)
configobj = YAMLConfig(infiles)
configobj.dereference(
context={
"cycle": self.cycle,
"leadtime": leadtime,
**self.config_full,
}
)
filemap[fhr] = configobj["filepath"]
return filemap

def _validate(self) -> None:
"""
Perform all necessary schema and pydantic validation.

:raises: UWConfigError if validation fails.
"""
Assets._validate(self) # noqa: SLF001
validate_internal(
schema_name=STR.platform,
desc="platform config",
config_data=self._config_intermediate,
)
TrackerNamelist(**self.config["namelist"]["update_values"])
2 changes: 1 addition & 1 deletion format
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash -eu

dirs=( docs scripts tests ush )
dirs=( docs drivers scripts tests ush )

echo "=> Formatting code"
ruff format ${dirs[*]}
Expand Down
1 change: 1 addition & 0 deletions parm/machines/hera.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ data:
platform:
crtm_dir: /contrib/spack-stack/spack-stack-1.6.0/envs/unified-env-rocky8/install/intel/2021.5.0/crtm-fix-2.4.0.1_emc-zvwtu3t/fix
scheduler: slurm
mpicmd: "srun -l"
hrrr_fix: /scratch3/BMC/gsd-fv3-dev/mpas_dev/fix
forecast:
mpas:
Expand Down
1 change: 1 addition & 0 deletions parm/machines/hercules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ data:
mesh_files: /work/noaa/gsd-hpcs/charrop/mpas/mpas-dev/test-mesh
platform:
scheduler: slurm
mpicmd: "srun -l"
forecast:
mpas:
execution:
Expand Down
3 changes: 3 additions & 0 deletions parm/machines/jet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ data:
mesh_files: "{{ platform.hrrr_fix }}"
platform:
scheduler: slurm
mpicmd: "srun -l"
crtm_dir: /misc/contrib/spack-stack/spack-stack-1.6.0/envs/unified-env-rocky8/install/intel/2021.5.0/crtm-fix-2.4.0.1_emc-bscege4/fix
geog_data:
global: /lfs5/BMC/wrfruc/Michael.Barlage/mpas/data/mpas_static
Expand All @@ -11,6 +12,8 @@ platform:
gfs:
path: /public/data/grids/gfs/0p25deg/grib2
file_templates: '@y@j@H00{{ "%04d" % fcst_hr }}'
syndat_tcvitals: /lfs5/HFIP/hwrf-data/hwrf-input/SYNDAT-PLUS

forecast:
mpas:
execution:
Expand Down
8 changes: 8 additions & 0 deletions parm/machines/ursa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ platform:
crtm_dir: /contrib/spack-stack/spack-stack-1.9.1/envs/ue-oneapi-2024.2.1/install/oneapi/2024.2.1/crtm-fix-2.4.0.1_emc-t5jym6r/fix
hrrr_fix: /scratch3/BMC/wrfruc/cholt/MPAS-Model/fix
scheduler: slurm
mpicmd: "srun -l"
geog_data:
global: /scratch2/BMC/wrfruc/talcott/mpas/global/static/geog/
service_partition: u1-service
data:
gfs:
path: /public/data/grids/gfs/0p25deg/grib2
file_templates: '@y@j@H00{{ "%04d" % fcst_hr }}'
syndat_tcvitals: /scratch3/HFIP/hwrfv3/noscrub/input/SYNDAT-PLUS
create_ics:
mpas_init:
execution:
Expand Down Expand Up @@ -47,3 +49,9 @@ graphics:
execution:
batchargs:
partition: u1-compute
tracker:
gfdltracker:
execution:
batchargs:
partition: u1-compute

Loading
Loading