Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
146 commits
Select commit Hold shift + click to select a range
dd48ab1
Start for climo wrapper (fre app gen-time-averages-wrapper)
Jun 6, 2025
9c2e202
Merge remote-tracking branch 'origin/main' into add-climo-wrapper
Jul 10, 2025
f74645d
Updated click parsing for fre app gen-time-averages-wrapper
Jul 11, 2025
c7c7f1d
Add the wrapper.py this time
Jul 11, 2025
8886a26
Convert pathlib objects to strings during cdo call creation
Aug 11, 2025
dfa40e5
Needed updates to climo wrapper
Aug 11, 2025
dd06814
Start of climo wrapper test
Aug 11, 2025
c4825a1
Generate netcdf test input files from ascii CDF files
Aug 12, 2025
5467c55
simplify directory setup
Aug 12, 2025
3b29798
Completed test for monthly history to annual climo
Aug 12, 2025
e7f4367
type adjustments
Aug 12, 2025
542a0f3
add annual-av-from-annual-history test
Aug 12, 2025
e0a230d
fix typo
Aug 12, 2025
6a081be
Fix output filename for monthly climatologies
Aug 13, 2025
fc5ce14
Save monthly output in the same user-specified directory
Aug 13, 2025
fa2d0df
Added monthly climo from monthly timeseries test
Aug 13, 2025
691418f
add fre app combine-time-averages
Aug 20, 2025
7e3172a
docstrings for fre app gen-time-averages-wrapper
Aug 20, 2025
a5a9afc
finished the climo combiner
Aug 20, 2025
b34b538
add docstrings for fre app combine-timeavgs
Aug 20, 2025
4ee12af
remove do_timeavgs pp yaml switch (now controlled in 'climatology' se…
Aug 25, 2025
7773a83
Added test for combining annual climatologies
Aug 25, 2025
7e3bca6
fre app combine-time-averages good to go
Aug 25, 2025
5318365
Add chdir to return to original working directory
Aug 26, 2025
6fce01e
Add test for monthly average combining
Aug 26, 2025
868f02a
Remove test files erroneously included in the repository.
Sep 9, 2025
7126721
Python f-strings are the superior method for string concatenation
Sep 9, 2025
892cd67
Simplify imports for metomi date parsers
Sep 9, 2025
22d193e
Merge remote-tracking branch 'origin/main' into add-climo-wrapper
Sep 9, 2025
dab3775
Move poorly-formatted filepaths out of doc-strings
Sep 12, 2025
2f00fff
Fixed docstrings
Sep 15, 2025
e0c9dd5
Add docstrings for exceptions
Sep 15, 2025
3fd53f5
Cleaned up imports in pytests
Sep 15, 2025
c592969
Merge remote-tracking branch 'origin/main' into add-climo-wrapper
Sep 15, 2025
1c60dbd
add combine.py module to fre.app.generate_time_averages module
Sep 15, 2025
d785566
Removed do_timeavgs switch from test yamls
Sep 15, 2025
3bc0e87
clean up fre/app/freapp.py
Sep 15, 2025
7f629bf
Update fre-cli click tests to look for exit 0 when running non-error …
Sep 15, 2025
8bbd1e8
one more removal of do_timeavgs option from example yamls
Sep 15, 2025
9508f4b
Set 'fre pp generate-time-averages-wrapper' tests to xfail
Sep 15, 2025
a310e5b
fre app combine-time-averages begin and end should be integars not st…
Sep 16, 2025
f79da97
remap bugfix
Sep 16, 2025
a188316
remap bugfix- string boolean is odd
Sep 17, 2025
1154893
Revert "Update fre-cli click tests to look for exit 0 when running no…
Sep 17, 2025
d4ffcd3
Replace string to boolean conversion with a click option
Sep 18, 2025
0b2f833
Merge branch 'main' into add-climo-wrapper
ilaflott Sep 22, 2025
93937b2
Initial plan
Copilot Sep 22, 2025
1359293
Fix remap_pp_components file naming issue causing test failures
Copilot Sep 22, 2025
49fa0a6
Enable workflow to run for this specific PR branch
Copilot Sep 22, 2025
dd10e4b
Add pkg parameter to generate time averages wrapper and CDO tests
Copilot Sep 22, 2025
76cbf57
Fix Path vs string typing issues and timavg.csh availability check in…
Copilot Sep 23, 2025
632a372
Fix CDO monthly test expectations and mark timavg.csh tests as xfail
Copilot Sep 23, 2025
b0fc58d
print --> logging
ilaflott Sep 23, 2025
04f0b0d
more specific error message
ilaflott Sep 23, 2025
6ce3dfe
print --> logging
ilaflott Sep 23, 2025
709deb6
better output handling, reduce annoying and useless locals output
ilaflott Sep 23, 2025
c504126
remove worthless output printing thats redundant with logging functio…
ilaflott Sep 23, 2025
c54cc7f
break up a long line
ilaflott Sep 23, 2025
fd7f999
clean up and proper TODO
ilaflott Sep 23, 2025
142513d
clarify tests
ilaflott Sep 23, 2025
129d2dc
reduce input test data files to minimum, to help with debugging by-ey…
ilaflott Sep 23, 2025
7b0bb2b
use unweighted stats for averaging
ilaflott Sep 23, 2025
8a9dbb0
Merge pull request #655 from NOAA-GFDL/copilot/fix-8ed54e3a-c382-4fe1…
ilaflott Sep 23, 2025
4cf6da6
Change pytest log level from INFO to DEBUG
ilaflott Sep 23, 2025
423699d
Merge branch 'main' into add-climo-wrapper
ilaflott Sep 24, 2025
c9eae1d
Adjust cdoTimeAverage to save out per-month files like fre-nctools ti…
Sep 26, 2025
bc52ad8
fre app gen-time-averages-wrapper to use cdo backend for now
Sep 26, 2025
3a183fc
remove xfails from climo tests
Sep 26, 2025
d00329b
Merge branch 'add-climo-wrapper-chris' into add-climo-wrapper
Sep 26, 2025
6cf201b
disable timavg.csh test
Sep 27, 2025
b5c9dcf
updated cdo climo test
Sep 27, 2025
a63a058
disable fre-nctools tests
Sep 27, 2025
58d08f9
adjust cdo climo monthly tests
Sep 27, 2025
ff659f7
Merge branch 'main' into add-climo-wrapper
ilaflott Oct 2, 2025
926ae4c
Add branches filter for pull request in spell check
ilaflott Oct 2, 2025
7f5fe52
Remove branch filter for copilot and update pylint failure threshold …
ilaflott Oct 3, 2025
4006b4e
for github codespace
ilaflott Oct 3, 2025
65c2905
Refactor combine function to use context manager for directory change…
ilaflott Oct 3, 2025
b3db6c4
move mask atmos plevel tests
ilaflott Oct 3, 2025
20c3c67
tweak reqs and alphabetize dep/req lists. remove host requirements an…
ilaflott Oct 3, 2025
9b1cf2e
pin cftime to 1.6 something
ilaflott Oct 3, 2025
b347735
remove jinja template pointing to... a host installed python, i think?
ilaflott Oct 3, 2025
2ae2dee
ok fine random jinja variable definitions for PYTHON it is...
ilaflott Oct 3, 2025
39b521c
curious experiment: test reqs for pytest pytest-cov and pylint, what …
ilaflott Oct 3, 2025
edb5f17
tweak pyproject
ilaflott Oct 3, 2025
137f2b1
not clear why pylint cant be found at the testing step
ilaflott Oct 3, 2025
fc88604
is it a slight race condition? is the test step executing slightly be…
ilaflott Oct 3, 2025
bb4c298
stop installing a non-existent conda-forge::conda-verify
ilaflott Oct 3, 2025
3934af0
OH the test key under requirements doesnt exist. testing reqs go unde…
ilaflott Oct 3, 2025
e722c91
heavily update test_generate_time_averages
ilaflott Oct 7, 2025
1d93dd5
init for mask_atmost_plevel tests
ilaflott Oct 7, 2025
29ffd69
update the other bits too
ilaflott Oct 7, 2025
013088b
readability edits, remove a redundant test case or two, condtional xs…
ilaflott Oct 7, 2025
95bae4f
some module import changes, comment out a case i prefer xfail, but se…
ilaflott Oct 7, 2025
f6b221c
increase clarity of testing constant file names. try weird shutil,whi…
ilaflott Oct 7, 2025
65d4a62
actually add the timeaverager edit this time
ilaflott Oct 7, 2025
8886291
verbose flag to timavg.csh, and comment out the fre-nctools calls, si…
ilaflott Oct 7, 2025
4b405e4
explicitly list packages and their versions from the environment. als…
ilaflott Oct 8, 2025
be974fc
remove a bad assertion i used for local debugging, aadd some extra c…
ilaflott Oct 8, 2025
9d58b14
omg environment please just solve
ilaflott Oct 8, 2025
86e5a63
why is this happening to me
ilaflott Oct 8, 2025
d153832
Update numpy and xarray version constraints
ilaflott Oct 8, 2025
9d65240
Remove netcdf4 from environment.yml
ilaflott Oct 8, 2025
e0fe628
Update meta.yaml
ilaflott Oct 8, 2025
e3d61c2
Update dependencies and Python version in meta.yaml
ilaflott Oct 8, 2025
b76136a
Modify environment.yml for dependency updates
ilaflott Oct 8, 2025
beab0a8
more explicitly configure conda in create_test_conda_env workflow, re…
ilaflott Oct 8, 2025
04adfbe
conda build has a debug flag, not a verbose flag
ilaflott Oct 8, 2025
1fd9686
ok now remove pylint pytest-cov and pytest from the environment yaml …
ilaflott Oct 8, 2025
5ff7586
remove pip as a dep, its a std module of python for awhile now. remov…
ilaflott Oct 8, 2025
fa1070f
another attempt at env resolution, mark a comparison test xfail
ilaflott Oct 8, 2025
2d2840f
xfail the right test
ilaflott Oct 8, 2025
e4b8066
conda update conda in workflows, change channel order as it affects e…
ilaflott Oct 9, 2025
dc16413
make conda confguration very explcit, show config before final calls,…
ilaflott Oct 9, 2025
9a04a13
make conda confguration very explcit, show config before final calls,…
ilaflott Oct 9, 2025
e0f754e
Merge remote-tracking branch 'origin/add-climo-wrapper' into add-clim…
ilaflott Oct 9, 2025
b2f6ac0
fine, lets try a conda init
ilaflott Oct 9, 2025
2472d3d
try normal conda-verify and anaconda-client. also directly source /pa…
ilaflott Oct 9, 2025
16ff1f1
forget the root/bashrc sourcing. add some extra steps
ilaflott Oct 9, 2025
cc18f08
move the conda sourcing and conda init to where the env activation is
ilaflott Oct 9, 2025
9e509f8
newer condabuilds seem unhappy with a None version specifier in the m…
ilaflott Oct 9, 2025
87dee76
syntax error...
ilaflott Oct 9, 2025
a751f29
missing parenthesis in meta yaml. activate the fre-cli environment ex…
ilaflott Oct 9, 2025
c779ae6
remove now unused and always-not-working load_setup_py_data from meta…
ilaflott Oct 9, 2025
2ea1a62
better echos, line spacing, workflow file formatting, propagate the c…
ilaflott Oct 9, 2025
30a024d
ok so the options key is not negotiable, i understand
ilaflott Oct 9, 2025
8280676
pylint feedback, env tweak, put the testing stuff back where it was, …
ilaflott Oct 10, 2025
2d97382
increase num of allowed positional args to 6 from 5, inch up the thre…
ilaflott Oct 10, 2025
a752650
break up a long line and implement way of avoiding redefining a pytho…
ilaflott Oct 10, 2025
2440bcc
largely pylint feedback, and some review feedback
ilaflott Oct 10, 2025
fb1ddd6
update cdoTimeAverager according to some feedbacl
ilaflott Oct 10, 2025
958cf45
better comments for workflow files
ilaflott Oct 10, 2025
008cf24
some review feedback
ilaflott Oct 10, 2025
cc42140
fixing a bug i introduced
ilaflott Oct 10, 2025
2a962f6
pylintrc file for explicit pylint call configuration. add new --rcfil…
ilaflott Oct 10, 2025
0c07ebc
remove time use from click entrypoint file and put with generate_time…
ilaflott Oct 10, 2025
1211192
add some TODO items now, dont be shy! pylint wont ding you for trying…
ilaflott Oct 10, 2025
7f650b6
spacing mostly to address feedback
ilaflott Oct 10, 2025
4010ce3
fix an oops in the workflow file
ilaflott Oct 10, 2025
ed18d34
spacing / review fedback
ilaflott Oct 10, 2025
0e0b752
removed unreachable else statement, since the error condition is chec…
ilaflott Oct 10, 2025
7dd313c
clean up how the value error for an unsupported frequency gets raised…
ilaflott Oct 14, 2025
4bdcff9
touch up when/how value error is raised in generate_time_Averages, ad…
ilaflott Oct 14, 2025
6117d24
last tweaks and last env experiment, let cmor be >=3.11
ilaflott Oct 14, 2025
dbef853
remove unneeded file
ilaflott Oct 14, 2025
5dabfd0
prototype CLI tests for the time-averager, printouts for the non-cli …
ilaflott Oct 15, 2025
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
74 changes: 74 additions & 0 deletions fre/app/freapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

from .mask_atmos_plevel import mask_atmos_plevel_subtool
from .generate_time_averages.generate_time_averages import generate
from .generate_time_averages.wrapper import generate_wrapper
from .regrid_xy.regrid_xy import regrid_xy
from .generate_time_averages.combine import combine

@click.group(help=click.style(" - app subcommands", fg=(250,154,90)))
def app_cli():
Expand Down Expand Up @@ -111,5 +113,77 @@ def gen_time_averages(inf, outf, pkg, var, unwgt, avg_type):
generate(inf, outf, pkg, var, unwgt, avg_type)
click.echo(f'Finished in total time {round(time.perf_counter() - start_time , 2)} second(s)')

@app_cli.command()
@click.option("--cycle-point",
type = str,
required = True,
help = "Beginning cycle-point in ISO8601")
@click.option("--dir",
type = str,
required = True,
help = "Root directory containing the shards")
@click.option("--sources",
type = str,
required = True,
help = "Sources (history file) input file, comma-separated")
@click.option("--output-interval",
type = str,
required = True,
help = "ISO interval of the desired climatology")
@click.option("--input-interval",
type = str,
required = True,
help = "ISO interval of the input timeseries")
@click.option("--grid",
type = str,
required = True,
help = "Grid label corresponding to the shards directory (e.g. 'native' and 'regrid-xy/180_288.conserve_order2'")
@click.option("--frequency",
type = str,
required = True,
help = "Frequency of desired climatology: 'mon' or 'yr'")
def gen_time_averages_wrapper(cycle_point, dir, sources, output_interval, input_interval, grid, frequency):
"""
Wrapper for climatology tool.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better description needed

Timeaverages all variables for a desired cycle point, source, and grid.
"""
sources_list = sources.split(',')
generate_wrapper(cycle_point, dir, sources_list, output_interval, input_interval, grid, frequency)

@app_cli.command()
@click.option("--in-dir",
type = str,
required = True,
help = "Input directory")
@click.option("--out-dir",
type = str,
required = True,
help = "Output directory")
@click.option("--component",
type = str,
required = True,
help = "Component name to combine")
@click.option("--begin",
type = str,
required = True,
help = "Beginning year")
@click.option("--end",
type = str,
required = True,
help = "Ending year")
@click.option("--frequency",
type = str,
required = True,
help = "Climatology frequency; 'mon' or 'yr'")
@click.option("--interval",
type = str,
required = True,
help = "Climatology interval in ISO8601")
def combine_time_averages(in_dir, out_dir, component, begin, end, frequency, interval):
"""
Combine per-variable climatologies into one file
"""
combine(in_dir, out_dir, component, begin, end, frequency, interval)

if __name__ == "__main__":
app_cli()
2 changes: 1 addition & 1 deletion fre/app/generate_time_averages/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
'''required for generate_time_averages module import functionality'''
__all__ = ['generate_time_averages', 'timeAverager',
__all__ = ['generate_time_averages', 'timeAverager', 'wrapper',
'frenctoolsTimeAverager', 'cdoTimeAverager', 'frepytoolsTimeAverager']
110 changes: 110 additions & 0 deletions fre/app/generate_time_averages/combine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import logging
from pathlib import Path
import glob
import os
import subprocess
import metomi.isodatetime.parsers

fre_logger = logging.getLogger(__name__)
duration_parser = metomi.isodatetime.parsers.DurationParser()

def form_bronx_directory_name(frequency: str, interval: str) -> str:
"""
Form the legacy Bronx timeaverage directory name
given a frequency and interval.
:param frequency: Frequency of the climatology
:type frequency: 'mon' or 'yr'
:param interval: Interval of the climatology
:type interval: ISO8601 duration
:return: Corresponding Bronx directory name
:rtype: str
"""

if frequency == "mon":
frequency_label = "monthly"
elif frequency == "yr":
frequency_label = "annual"
else:
raise ValueError(f"Frequency '{frequency}' not recognized")
interval_object = duration_parser.parse(interval)
return frequency_label + '_' + str(interval_object.years) + 'yr'


def check_glob(target: str) -> None:
"""
Verify that at least one file is resolved by the glob.
Raises FileNotFoundError if no files are found.
:param target: Glob target to resolve
:type target: str
:rtype: None
"""
files = glob.glob(target)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic can be de-nested

if not (condition):
    raise
logger.debug

if len(files) >= 1:
fre_logger.debug(f"{target} has {len(files)} files")
else:
raise FileNotFoundError(f"{target} resolves to no files")


def combine(root_in_dir: str, root_out_dir: str, component: str, begin: str, end: str, frequency: str, interval: str) -> None:
"""
Combine per-variable climatologies into one file.
:param root_in_dir: Root timeaverage shards directory, up to the "av"
:param type: str
:param root_out_dir: Root output postprocess directory, up to the "pp"
:param type: str
:param component: Component to process
:param type: str
:param begin: Beginning of the climatology
:param type: YYYY
:param end: Ending of the climatology
:param type: YYYY
:param frequency: Sampling type of the climatology
:param type: 'mon' or 'yr'
:param interval: Length of the climatology
:param type: ISO8601 duration
:rtype: None
"""
if frequency == "yr":
frequency_iso = "P1Y"
elif frequency == "mon":
frequency_iso = "P1M"
else:
raise ValueError(f"Frequency '{frequency}' not known")
outdir = Path(root_out_dir) / component / "av" / form_bronx_directory_name(frequency, interval)
fre_logger.debug(f"Output dir = '{outdir}'")
outdir.mkdir(exist_ok=True, parents=True)

if begin == end:
date_string = f"{begin:04d}"
else:
date_string = f"{begin:04d}-{end:04d}"

indir = Path(root_in_dir) / frequency_iso / interval
fre_logger.debug(f"Input dir = '{indir}'")
gotta_go_back_here = os.getcwd()
os.chdir(indir)

if frequency == 'yr':
source = component + '.' + date_string + '.*.nc'
target = component + '.' + date_string + '.nc'
check_glob(source)
subprocess.run(['cdo', '-O', 'merge', source, target], check=True)
fre_logger.debug(f"Output file created: {target}")
fre_logger.debug(f"Copying to {outdir}")
subprocess.run(['cp', '-v', target, outdir], check=True)
elif frequency == 'mon':
for MM in range(1,13):
source = f"{component}.{date_string}.*.{MM:02d}.nc"
target = f"{component}.{date_string}.{MM:02d}.nc"
check_glob(source)
subprocess.run(['cdo', '-O', 'merge', source, target], check=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should use the cdo module if possible

fre_logger.debug(f"Output file created: {target}")
fre_logger.debug(f"Copying to {outdir}")
subprocess.run(['cp', '-v', target, outdir], check=True)
else:
raise ValueError(f"Frequency '{frequency}' not known")

os.chdir(gotta_go_back_here)
5 changes: 3 additions & 2 deletions fre/app/generate_time_averages/frenctoolsTimeAverager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging
import subprocess
import shutil
from pathlib import Path

fre_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -50,7 +51,7 @@ def generate_timavg(self, infile=None, outfile=None):
#Recursive call if month is selcted for climatology. by Avery Kiihne
if self.avg_type == 'month':
monthly_nc_dir = f"monthly_nc_files" #Folder that new monthly input files are put
output_dir = f"monthly_output_files" #Folder for the results, split by month
output_dir = Path(outfile).parent #Save output in the user-specified location
os.makedirs(monthly_nc_dir, exist_ok=True) #create directory if it does not exist
os.makedirs(output_dir, exist_ok=True)
#Extract unique months from the infile
Expand All @@ -59,7 +60,7 @@ def generate_timavg(self, infile=None, outfile=None):

#Dictionary to store output filenames by month
nc_month_file_paths = {month_index: os.path.join(monthly_nc_dir, f"all_years.{month_index}.nc") for month_index in month_indices}
month_output_file_paths = {month_index: os.path.join(output_dir, f"{outfile}_.{month_index}.nc") for month_index in month_indices}
month_output_file_paths = {month_index: os.path.join(output_dir, f"{Path(outfile).stem}.{month_index:02d}.nc") for month_index in month_indices}

cdo = Cdo()
#Loop through each month and select the corresponding data
Expand Down
3 changes: 2 additions & 1 deletion fre/app/generate_time_averages/generate_time_averages.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ def generate_time_average(infile = None, outfile = None,
#Use cdo to merge multiple files if present
merged = False
if type(infile).__name__=='list' and len(infile)> 1: #multiple files case. Generates one combined file
infile_str = [str(item) for item in infile]
from cdo import Cdo
_cdo=Cdo()
merged_file = "merged_output.nc"
_cdo.mergetime(input=' '.join(infile), output=merged_file)
_cdo.mergetime(input=' '.join(infile_str), output=merged_file)
multi_file = infile #preserve the original file names for later
infile = merged_file
merged = True
Expand Down
98 changes: 98 additions & 0 deletions fre/app/generate_time_averages/tests/test_combine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import pytest
import subprocess
from pathlib import Path
import xarray as xr
#import metomi.isodatetimeparsers.parsers

from .. import combine

@pytest.fixture()
def create_annual_per_variable_climatologies(tmp_path):
"""
Create per-variable climatologies.
in/atmos/P1Y/P2Y/alb_sfc.nc
in/atmos/P1Y/P2Y/aliq.nc
"""
# path to input files
input_file_dir = Path('fre/tests/test_files/climatology/outputs/annual')
input_files = [
input_file_dir / 'atmos.1980-1981.alb_sfc.cdl',
input_file_dir / 'atmos.1980-1981.aliq.cdl'
]
for file_ in input_files:
assert file_.exists()

# write netcdf files
output_dir = Path(tmp_path, 'in', 'atmos', 'P1Y', 'P2Y')
output_dir.mkdir(parents=True)

# write netcdfs from the cdfs
for file_ in input_files:
output_file = output_dir / file_.stem
output_file = Path(str(output_file) + '.nc')
command = ['ncgen', '-o', output_file, file_]
sp = subprocess.run(command, check=True)
assert sp.returncode == 0
assert output_file.exists()

yield tmp_path

@pytest.fixture()
def create_monthly_per_variable_climatologies(tmp_path):
"""
Create per-variable climatologies.
in/atmos/P1M/P2Y/alb_sfc.[01-12].nc
in/atmos/P1M/P2Y/aliq.[01-12].nc
"""
# path to input CDL files
input_dir = Path('fre/tests/test_files/climatology/outputs/monthly')

# two variables
input_basenames = [
'atmos.1980-1981.alb_sfc',
'atmos.1980-1981.aliq'
]

# path to test output NC files
output_dir = Path(tmp_path, 'in', 'atmos', 'P1M', 'P2Y')
output_dir.mkdir(parents=True)

# write netcdf files
for i in range(1,13):
for name in input_basenames:
input_file = input_dir / f"{name}.{i:02d}.cdl"
assert input_file.exists()

output_file = output_dir / f"{name}.{i:02d}.nc"
command = ['ncgen', '-o', output_file, input_file]
sp = subprocess.run(command, check=True)
assert sp.returncode == 0
assert output_file.exists()

yield tmp_path

def test_combine_annual_av(create_annual_per_variable_climatologies):
"""
Combine per-variable annual climatologies into combined annual climatology file
"""

combine.combine(create_annual_per_variable_climatologies / 'in' / 'atmos', create_annual_per_variable_climatologies / 'out', 'atmos', 1980, 1981, 'yr', 'P2Y')

output_dir = Path(create_annual_per_variable_climatologies, 'out', 'atmos', 'av', 'annual_2yr')
output_file = output_dir / 'atmos.1980-1981.nc'

assert output_file.exists()

def test_combine_monthly_av(create_monthly_per_variable_climatologies):
"""
Combine per-variable monthly climatologies into combined monthly climatology file
"""

combine.combine(create_monthly_per_variable_climatologies / 'in' / 'atmos', create_monthly_per_variable_climatologies / 'out', 'atmos', 1980, 1981, 'mon', 'P2Y')

output_dir = Path(create_monthly_per_variable_climatologies, 'out', 'atmos', 'av', 'monthly_2yr')
for i in range(1,13):
output_file = output_dir / f'atmos.1980-1981.{i:02d}.nc'
assert output_file.exists()
Loading