Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
cb9c947
write out exceptions
Dooruk Oct 3, 2025
c1f7119
minor changes, make TUI message clearer
Dooruk Oct 3, 2025
3ca20c4
changes in moving files between forecast cycles
Dooruk Oct 3, 2025
10ff584
take out the linking task
Dooruk Oct 3, 2025
79e6153
introduce simplified coupled tasks assuming direct execution of GEOS …
Dooruk Oct 7, 2025
b573f10
forecast suite changes
Dooruk Oct 7, 2025
a1fd030
revamp files, more changes underway
Dooruk Oct 7, 2025
55aeb11
define new coupled model only suite
Dooruk Oct 7, 2025
4c8b9a3
change forecast location and make forecast a static folder
Dooruk Dec 1, 2025
2f8ed47
format and code suggestions
Dooruk Dec 18, 2025
f44ec97
document and codestring
Dooruk Dec 18, 2025
c02bf4d
dont except exception, send exception type to logger
Dooruk Jan 26, 2026
e872e38
background error new form
Dooruk Jan 26, 2026
2aab229
get rid off deprecated keys
Dooruk Feb 2, 2026
a35440d
let go off catching exceptions
Dooruk Feb 2, 2026
4454c92
Merge branch 'develop' into feature/exec_geos_direct_part1
Dooruk Feb 2, 2026
1008a81
revert genB to original
Dooruk Feb 3, 2026
d6e5b99
get rid of unused keys, create alternative method (R2D2, albeit inact…
Dooruk Feb 3, 2026
d081529
minor changes to keep up with PRs
Dooruk Feb 3, 2026
14de66a
changes related to initial restarts
Dooruk Feb 3, 2026
c898ab1
first model specific task
Dooruk Feb 3, 2026
4facad7
Merge branch 'develop' into feature/exec_geos_direct_part1
Dooruk Feb 3, 2026
3ad2722
code style fixes
Dooruk Feb 3, 2026
1d71e1e
adapt to new obs handling
Dooruk Feb 4, 2026
eb4383a
skip save_restart while figuring R2D2 out
Dooruk Feb 4, 2026
6dc3825
deprecated keys related changes and few fixes
Dooruk Feb 4, 2026
23577cf
some changes for geosv12
Dooruk Feb 5, 2026
bad345f
changes in geos class utility for geos v12
Dooruk Feb 5, 2026
c03d9ac
changes for 3dvar_cycle to test get, prep and run
Dooruk Feb 5, 2026
68cea55
make forecast_geos use new method
Dooruk Feb 10, 2026
e6b5643
few fixes, stop using adjacent cycle
Dooruk Feb 10, 2026
cfd44be
clarify homdir expdir logic
Dooruk Feb 10, 2026
3adf0d7
netcdf specify
Dooruk Feb 10, 2026
b9a9a47
Merge branch 'develop' into feature/exec_geos_direct_part1
Dooruk Feb 10, 2026
c2317a3
better statement
Dooruk Feb 12, 2026
ba29005
take out model specific approach from this PR
Dooruk Feb 12, 2026
33a968c
apply minor code improvements
Dooruk Feb 12, 2026
23bc8a5
Merge branch 'develop' into feature/exec_geos_direct_part1
Dooruk Feb 12, 2026
f2e787d
Merge branch 'feature/exec_geos_direct_part1' into feature/exec_geos_…
Dooruk Feb 12, 2026
60b13c5
rename suites, take out obsolete parts and tasks
Dooruk Feb 12, 2026
2cffb0f
add docstrings and proper naming for tasks as forecast directory is n…
Dooruk Feb 13, 2026
b5b61da
version bump
Dooruk Feb 13, 2026
3149f05
suite changes with new task names
Dooruk Feb 18, 2026
dbe0a3f
code improvements
Dooruk Feb 18, 2026
7a2ad5f
make mom6_iau model dependent
Dooruk Feb 20, 2026
f24cd0a
cycle times hack
Dooruk Feb 20, 2026
5094cda
make experiment.yaml non-alphabetical again by using default ruamel
Dooruk Feb 20, 2026
022287b
relevant for experiment.yaml
Dooruk Feb 20, 2026
53ede0e
minor fix for MOM6 IAU
Dooruk Feb 20, 2026
714ecfe
cycle times and overrride fixes
Dooruk Feb 20, 2026
c88b448
add tier2 cycling run
Dooruk Feb 20, 2026
01905d5
Merge branch 'develop' into feature/exec_geos_direct_part2
Dooruk Feb 20, 2026
8127f55
observation changes for new gdas_marine obs
Dooruk Feb 23, 2026
6ec5a39
take csh out, add more observers for tier2
Dooruk Feb 23, 2026
fa72657
experiment.yaml original order
Dooruk Feb 23, 2026
d5b6221
observation yaml, calculate obserror in timeseries
Dooruk Feb 23, 2026
01b13b7
tier2 in 3dvar_marine_cycle
Dooruk Feb 23, 2026
afa338e
update documentation and add another example
Dooruk Feb 23, 2026
2063efa
erase obsolete tasks
Dooruk Feb 23, 2026
0436b08
marine suite configs homdir location
Dooruk Feb 23, 2026
3f82f8e
include hotstart option for model runs, take out unnecessary questions
Dooruk Feb 23, 2026
25ca9ee
fix platform defaults
Dooruk Feb 23, 2026
5396d0b
Merge branch 'feature/exec_geos_direct_part2' into feature/exec_geos_…
Dooruk Feb 23, 2026
ef63eed
erase obsolete methods, add docstrings
Dooruk Feb 23, 2026
b0e948d
pycodestyle
Dooruk Feb 23, 2026
0fdc3db
erase window_offset
Dooruk Feb 23, 2026
5d59260
last changes
Dooruk Feb 23, 2026
70d2c9b
minor fixes
Dooruk Feb 25, 2026
6057a4b
another minot fix
Dooruk Feb 25, 2026
4949606
save forecast with r2d2 option only
Dooruk Feb 26, 2026
488f5d8
save forecast peripheries
Dooruk Feb 26, 2026
0fb4c86
add store for cf and nwp
Dooruk Mar 2, 2026
c65900e
fix forecast conflicts
Dooruk Mar 9, 2026
e84c08d
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Mar 9, 2026
a17a044
conflict fix
Dooruk Mar 11, 2026
4c3bf14
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Mar 11, 2026
13134b0
fix save forecast to work with develop
Dooruk May 18, 2026
7b842b4
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Jun 10, 2026
105d4f6
suite config and flow changes to save forecast
Dooruk Jun 12, 2026
43b072b
use r2d2_experiment_id for store
Dooruk Jun 24, 2026
ce240ed
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Jun 24, 2026
a8c61af
use tar.gz to store, take one
Dooruk Jun 25, 2026
6a2862e
Add compression utilities #823
ftgoktas Jul 17, 2026
16a46ef
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Jul 21, 2026
65d9b66
Enable compression for ingest
ftgoktas Jul 29, 2026
0b767b0
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Aug 3, 2026
f916b00
add Furkan's utility
Dooruk Aug 6, 2026
c71e5a8
initial test for using pigz works, except existing files
Dooruk Aug 6, 2026
d286148
migjt need to thinkl further about calling background
Dooruk Aug 6, 2026
3201806
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Aug 6, 2026
5d0a895
create two separate tasks for compressing and storing, as R2D2 doesn'…
Dooruk Aug 10, 2026
61f4c4d
compress utility update
Dooruk Aug 10, 2026
e2396ae
Merge branch 'develop' into feature/save_tasks/save_forecast
Dooruk Aug 10, 2026
63c5521
handle geos_marine background archives separately
Dooruk Aug 10, 2026
3f174f0
workflow and codestyle fixes
Dooruk Aug 10, 2026
7859ad9
final changes, addressing code test issues
Dooruk Aug 10, 2026
029ce47
change pyproject version
Dooruk Aug 10, 2026
4412049
Merge remote-tracking branch 'origin' into feature/r2d2-file-compression
ftgoktas Aug 14, 2026
f5c9689
Merge remote-tracking branch 'origin/feature/save_tasks/save_forecast…
ftgoktas Aug 14, 2026
c38b685
Remove question
ftgoktas Aug 14, 2026
f980acd
Revert obs compression and implement for restart
ftgoktas Aug 14, 2026
7dce07c
Merge branch 'develop' into feature/r2d2-file-compression
ftgoktas Aug 21, 2026
7e4d2f3
trimming after pr merge
ftgoktas Aug 21, 2026
dea89bc
comment back
ftgoktas Aug 21, 2026
5731fe2
Merge branch 'develop' into feature/r2d2-file-compression
Dooruk Aug 24, 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
19 changes: 16 additions & 3 deletions src/swell/tasks/get_background.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


from swell.tasks.base.task_base import taskBase
from swell.utilities.compress import compressed_extension, decompress_if_needed
from swell.utilities.r2d2 import load_r2d2_credentials, get_r2d2_model_name

from datetime import timedelta
Expand Down Expand Up @@ -189,6 +190,9 @@ def execute(self) -> None:
fetch_step = bkg_step
fetch_date = forecast_start_time.strftime('%Y-%m-%dT%H:%M:%SZ')

fetch_target = target_file
compress_output = False

if use_geos_cf_oper_background:
geos_cf_forecast_start_time = self.geos_cf_oper_forecast_start(background_time)
fetch_step = self.geos_cf_oper_step(
Expand All @@ -203,9 +207,16 @@ def execute(self) -> None:
f'date={fetch_date}, step={fetch_step}'
)

# SaveBackground may have compressed this record before storing it
# (compress_output). R2D2 query must match the stored extension.
compress_output = self.config.compress_output(False)
if compress_output:
file_extension = compressed_extension(file_extension)
fetch_target = target_file + '.gz'

fetch_kwargs = dict(
item='forecast',
target_file=target_file,
target_file=fetch_target,
model=r2d2_model,
experiment=background_experiment,
file_extension=file_extension,
Expand All @@ -218,7 +229,9 @@ def execute(self) -> None:
fetch_kwargs['data_store'] = r2d2_datastore
r2d2.fetch(**fetch_kwargs)

# Change permission
os.chmod(target_file, 0o644)
# Decompress if SaveBackground stored this record compressed, then
# change permission on the file that actually ends up at target_file.
actual_file = decompress_if_needed(fetch_target)
os.chmod(actual_file, 0o644)

# --------------------------------------------------------------------------------------------------
57 changes: 55 additions & 2 deletions src/swell/tasks/save_background.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from datetime import datetime as dt
from datetime import timedelta
import os
import shutil
from r2d2 import store

from swell.tasks.base.task_base import taskBase
from swell.utilities.compress import compress_file, compressed_extension
from swell.utilities.datetime_util import datetime_formats
from swell.utilities.r2d2 import load_r2d2_credentials

Expand Down Expand Up @@ -45,6 +47,13 @@ def execute(self) -> None:
- ``horizontal_resolution``: R2D2 resolution string (default ``c360``)
- ``store_as_symlink``: if ``True`` (default), register files as symlinks
in R2D2 rather than copying them
- ``compress_output``: if ``True`` (default ``False``), gzip/pigz-compress
each file before storing to save R2D2 disk space. Incompatible with
``store_as_symlink`` (the compressed copy is not the original file), so
``store_as_symlink`` is forced to ``False`` when compression is enabled.
- ``compress_algorithm``: ``gzip`` (default) or ``pigz`` (parallel, requires
the ``pigz`` binary on ``PATH``)
- ``compress_pigz_threads``: thread count for ``pigz`` (default 4)

The Cylc cycle point must be the forecast initialization time,
e.g. ``2025-10-02T09:00:00Z``.
Expand Down Expand Up @@ -75,6 +84,21 @@ def execute(self) -> None:
resolution = self.config.horizontal_resolution('c360')
store_as_symlink = self.config.store_as_symlink(True)

compress_output = self.config.compress_output(False)
compress_algorithm = self.config.compress_algorithm('gzip')
compress_pigz_threads = self.config.compress_pigz_threads(4)

if compress_output and store_as_symlink:
self.logger.warning(
'compress_output=True and store_as_symlink=True are incompatible '
'(a compressed copy is not the original file). '
'Forcing store_as_symlink=False.'
)
store_as_symlink = False

if compress_output:
os.makedirs(self.cycle_dir(), 0o755, exist_ok=True)

stored = 0
skipped = 0

Expand All @@ -98,6 +122,32 @@ def execute(self) -> None:

self.logger.info(f' Storing step={step}: {os.path.basename(source_file)}')

store_source = source_file
store_extension = 'nc4'
staged_compressed_file = None

if compress_output:
# Source files live on a shared, often read-only NRT filesystem —
# stage a local copy in the cycle directory before compressing
# rather than writing next to the original.
staged_file = os.path.join(self.cycle_dir(), os.path.basename(source_file))
try:
shutil.copy(source_file, staged_file)
staged_compressed_file = compress_file(
staged_file,
algorithm=compress_algorithm,
num_threads=compress_pigz_threads,
)
except Exception as exc:
self.logger.abort(
f'Failed to compress background file {source_file}: {exc}')
finally:
if os.path.exists(staged_file):
os.remove(staged_file)

store_source = staged_compressed_file
store_extension = compressed_extension('nc4')

try:
store(
model=model,
Expand All @@ -106,8 +156,8 @@ def execute(self) -> None:
experiment=experiment,
resolution=resolution,
date=forecast_start.strftime('%Y%m%d_%H%Mz'),
source_file=source_file,
file_extension='nc4',
source_file=store_source,
file_extension=store_extension,
file_type='bkg',
store_as_symlink=store_as_symlink,
)
Expand All @@ -129,6 +179,9 @@ def execute(self) -> None:
f'{os.path.basename(source_file)}')
else:
raise
finally:
if staged_compressed_file and os.path.exists(staged_compressed_file):
os.remove(staged_compressed_file)
stored += 1

verb = 'Would store' if dry_run else 'Stored'
Expand Down
4 changes: 4 additions & 0 deletions src/swell/tasks/task_questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ class TaskQuestions(QuestionContainer, Enum):
qd.background_frequency(),
qd.horizontal_resolution(),
qd.marine_models(),
qd.compress_output(),
]
)

Expand Down Expand Up @@ -956,6 +957,9 @@ class TaskQuestions(QuestionContainer, Enum):
qd.background_experiment(),
qd.horizontal_resolution(),
qd.store_as_symlink(),
qd.compress_output(),
qd.compress_algorithm(),
qd.compress_pigz_threads(),
]
)

Expand Down
1 change: 0 additions & 1 deletion src/swell/utilities/compress.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# (C) Copyright 2021- United States Government as represented by the Administrator of the
# National Aeronautics and Space Administration. All Rights Reserved.
#
Expand Down
42 changes: 42 additions & 0 deletions src/swell/utilities/question_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,48 @@ class store_as_symlink(TaskQuestion):

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

@dataclass
class compress_output(TaskQuestion):
default_value: bool = False
question_name: str = "compress_output"
ask_question: bool = False
models: List[str] = mutable_field([
"all_models"
])
prompt: str = "Compress output files with gzip before storing in R2D2 to save disk space"
widget_type: WType = WType.BOOLEAN

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

@dataclass
class compress_algorithm(TaskQuestion):
default_value: str = "gzip"
question_name: str = "compress_algorithm"
ask_question: bool = False
models: List[str] = mutable_field([
"all_models"
])
prompt: str = ("Compression algorithm: 'gzip' (Python stdlib, no external binary) or "
"'pigz' (parallel gzip, requires pigz binary on PATH)")
widget_type: WType = WType.STRING_DROP_LIST

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

@dataclass
class compress_pigz_threads(TaskQuestion):
default_value: int = 4
question_name: str = "compress_pigz_threads"
ask_question: bool = False
models: List[str] = mutable_field([
"all_models"
])
prompt: str = ("Number of parallel threads for pigz compression. "
"Only used when compress_algorithm is 'pigz'. "
"Should not exceed the number of CPUs allocated to the task.")
widget_type: WType = WType.INTEGER

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

@dataclass
class obs_rc_path(TaskQuestion):
default_value: str = 'GEOS_mksi/ObsClass/obsys-nccs.rc'
Expand Down
Loading