Skip to content

CMORgasbord: yaml parsing #368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ea62768
init commit, smth small, to start draft PR
ilaflott Feb 25, 2025
d742d5f
Create constructors.py
ilaflott Feb 25, 2025
f58f0fd
Update frecmor.py
ilaflott Feb 25, 2025
bb5b284
Update cmor_yamler.py
ilaflott Feb 25, 2025
c8a4c9c
Update cmor_yamler.py
ilaflott Feb 25, 2025
2d0cb3e
Update test_cmor_run_subtool.py
ilaflott Feb 25, 2025
d0a1233
Update frecmor.py
ilaflott Feb 25, 2025
8fed0fa
Update test_cmor_run_subtool.py
ilaflott Feb 25, 2025
dad33f8
Update test_cmor_run_subtool_ppan_only.py
ilaflott Feb 25, 2025
bac4bfe
new file, fre/yamltools/helpers.py to hold widely used functions. adj…
ilaflott Feb 26, 2025
fbbdbbf
centralize how the yaml module is imported, put this in __init__ so e…
ilaflott Feb 26, 2025
ded54b5
quick tweak
ilaflott Feb 26, 2025
68c1195
update cmor test calls. make debug run one mode an input argument as …
ilaflott Feb 26, 2025
7ae3360
fix for syntax in function call, missing comma
ilaflott Feb 26, 2025
9e1d85b
add initial test, add initial success conditions. Goal is now, turn t…
ilaflott Feb 26, 2025
c5e28ce
add new script for quickly making a vanilla variable list with the eg…
ilaflott Feb 27, 2025
ef70afa
small logger info edits
ilaflott Feb 27, 2025
4f417a2
adjust calls to set run_one_mode for testings sake
ilaflott Feb 27, 2025
5fd5143
add back in old cmor_info_parser, it wont work right away- but it is …
ilaflott Feb 27, 2025
f130d6e
annnnnnd we are back! we now have a cmor info parser that produces a …
ilaflott Feb 27, 2025
da995eb
new combine-yamls cli call test, also bring base fre yamltools combin…
ilaflott Feb 27, 2025
84eca3c
add yaml file containing expected output given the fre yamltools comb…
ilaflott Feb 27, 2025
b2120ce
put back the xfail under test cli fre cmor yaml case1, its not the sp…
ilaflott Feb 27, 2025
da12556
emacs whitespace-cleanup for every file touched
ilaflott Feb 27, 2025
cb25640
pylint edits, some print to logging changes in generate-time-averages
ilaflott Feb 28, 2025
cbdf1d4
remove unused output/error variables in regrid xy test. switch print …
ilaflott Feb 28, 2025
733dd21
remove more of those unused out and err variables in tests scripts fo…
ilaflott Feb 28, 2025
553d97d
trivial pylint edits
ilaflott Feb 28, 2025
cbfea55
pylint the last few minutes of the week away... why not
ilaflott Feb 28, 2025
3554258
remove prototype bash script for local variable list resolving- not r…
ilaflott Feb 28, 2025
3ecdd58
better comments describing import magic
ilaflott Feb 28, 2025
e496cd2
silly syntax error
ilaflott Feb 28, 2025
ddff672
remove some old commented out cruft- add in a doc string for my unit …
ilaflott Feb 28, 2025
c89bfe7
remove an old DEBUG flag thats been worked into a command line argument
ilaflott Feb 28, 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
2 changes: 2 additions & 0 deletions fre/analysis/subtools.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
""" holds subtool commands and related modules for 'fre analysis' routines"""

from pathlib import Path
from subprocess import run
from tempfile import TemporaryDirectory
Expand Down
8 changes: 4 additions & 4 deletions fre/analysis/tests/test_subtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def test_install_analysis_package():
url = "github.com/noaa-gfdl/analysis-scripts"
name = "freanalysis_clouds"
with TemporaryDirectory() as tmp:
install_analysis_package(url, name, tmp)
plugins = list_plugins(tmp)
assert name in list_plugins(tmp)
install_analysis_package(url, name, tmp)
plugins = list_plugins(tmp)
assert name in list_plugins(tmp)


def test_run_analysis():
Expand All @@ -44,7 +44,7 @@ def test_run_analysis():
install_analysis_package(url, name, library_directory)
with pytest.raises(CalledProcessError) as err:
run_analysis(name, str(catalog), ".", "output.yaml", experiment_yaml,
library_directory)
library_directory)
for line in err._excinfo[1].output.decode("utf-8").split("\n"):
if f"No such file or directory: '{str(catalog)}'" in line:
return
Expand Down
26 changes: 15 additions & 11 deletions fre/app/generate_time_averages/generate_time_averages.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
''' tools for generating time averages from various packages '''
import logging
fre_logger = logging.getLogger(__name__)

def generate_time_average(infile=None, outfile=None,
pkg=None, var=None,
unwgt=False, avg_type=None, stddev_type=None):
def generate_time_average(infile = None, outfile = None,
pkg = None, var = None, unwgt = False,
avg_type = None, stddev_type = None):
''' steering function to various averaging functions above'''
if __debug__:
print(locals()) #input argument details
fre_logger.info(locals()) #input argument details
exitstatus=1

#needs a case statement. better yet, smarter way to do this? (TODO)
Expand All @@ -29,31 +31,33 @@ def generate_time_average(infile=None, outfile=None,
avg_type = avg_type, stddev_type = stddev_type)

else :
print('requested package unknown. exit.')
fre_logger.info('requested package unknown. exit.')
return exitstatus

if __debug__:
print(f'myavger.__repr__={myavger.__repr__}')
fre_logger.info(f'myavger.__repr__={myavger.__repr__}')
if myavger is not None:
exitstatus=myavger.generate_timavg(infile=infile, outfile=outfile)
else:
print('ERROR: averager is None, check generate_time_average in generate_time_averages.py!')
fre_logger.info('ERROR: averager is None, check generate_time_average in generate_time_averages.py!')

return exitstatus

def generate(inf, outf, pkg, var, unwgt, avg_type, stddev_type):
def generate(inf = None, outf = None,
pkg = None, var = None, unwgt = False,
avg_type = None, stddev_type = None):
''' click entrypoint to time averaging routine '''
exitstatus=generate_time_average( inf, outf,
pkg, var,
unwgt,
avg_type, stddev_type)
if exitstatus!=0:
print(f'WARNING: exitstatus={exitstatus} != 0. Something exited poorly!')
fre_logger.info(f'WARNING: exitstatus={exitstatus} != 0. Something exited poorly!')
else:
print('time averaging finished successfully')
fre_logger.info('time averaging finished successfully')

if __name__ == '__main__':
import time
start_time=time.perf_counter()
generate(inf, outf, pkg, var, unwgt, avg_type, stddev_type)
print(f'Finished in total time {round(time.perf_counter() - start_time , 2)} second(s)')
fre_logger.info(f'Finished in total time {round(time.perf_counter() - start_time , 2)} second(s)')
79 changes: 39 additions & 40 deletions fre/app/regrid_xy/regrid_xy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
most valid input args to fregrid are valid in this script
"""

import logging
fre_logger = logging.getLogger(__name__)

import subprocess
import shutil
import os
Expand All @@ -21,24 +24,20 @@ def truncate_date(date, freq):
""" truncates iso freq to iso date time """
freq_in_date_format=freq_to_date_format(freq)

#in the shell version, this line simply gets run.
#we will simply print this command to screen for now. TO DO (maybe we can work around it?)
#not clear to me why piping to tr is necessary, doesnt seem to change output at all
#print('cylc date --template '+freq_in_date_format+' '+date+' | tr -d T')
#output =subprocess.Popen(["cylc", "date", "--template", freq_in_date_format, date,
# "|","tr","-d","T"],
# stdout=subprocess.PIPE)
output =subprocess.Popen(["cylc", "cycle-point", "--template", freq_in_date_format, date],
stdout=subprocess.PIPE)
fre_logger.debug('cylc date --template '+freq_in_date_format+' '+date)
output =subprocess.Popen(["cylc", "cycle-point", "--template",
freq_in_date_format, date],
stdout = subprocess.PIPE)

bytedate = output.communicate()[0]
date=str(bytedate.decode())
date = str(bytedate.decode())

#remove trailing newline
date=date[:(len(date)-1)]
date = date[:(len(date)-1)]

#check for and remove 'T' if present
if not date.isnumeric():
date=date[:8]+date[-2:]
date = date[:8]+date[-2:]
return date

def freq_to_date_format(iso_freq):
Expand Down Expand Up @@ -91,8 +90,8 @@ def check_interp_method( nc_variable, interp_method):
if 'interp_method' not in attr_list:
pass
elif nc_variable.interp_method != interp_method:
print(f'WARNING: interp_method={interp_method} differs from what is in target_file')
print(f'WARNING: interp_method used may not be {interp_method}!')
fre_logger.info(f'WARNING: interp_method={interp_method} differs from what is in target_file')
fre_logger.info(f'WARNING: interp_method used may not be {interp_method}!')


def check_per_component_settings(component_list, rose_app_cfg):
Expand Down Expand Up @@ -167,7 +166,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
config_name = os.getcwd() #REMOVE ME TODO
config_name += '/rose-app-run.conf'
#config_name += '/rose-app.conf'
print(f'config_name = {config_name}')
fre_logger.info(f'config_name = {config_name}')
try:
rose_app_config = rose_cfg.load(config_name)
except Exception as exc:
Expand All @@ -188,7 +187,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
begin , tmp_dir ,
remap_dir , source ,
grid_spec , def_xy_interp ]:
print( f'input_dir = { input_dir }\n' + \
fre_logger.info( f'input_dir = { input_dir }\n' + \
f'output_dir = { output_dir }\n' + \
f'begin = { begin }\n' + \
f'tmp_dir = { tmp_dir }\n' + \
Expand Down Expand Up @@ -265,16 +264,16 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
do_regridding = \
check_per_component_settings( \
component_list, rose_app_config)
print(f'component_list = {component_list}')
print(f'do_regridding = {do_regridding}')
fre_logger.info(f'component_list = {component_list}')
fre_logger.info(f'do_regridding = {do_regridding}')
else:
do_regridding=[True]

for component in component_list:
if not do_regridding[
component_list.index(component) ]:
continue
print(f'\n\nregridding \nsource={source} for \ncomponent={component}\n')
fre_logger.info(f'\n\nregridding \nsource={source} for \ncomponent={component}\n')

# mandatory per-component inputs, will error if nothing in rose config
input_realm, interp_method, input_grid = None, None, None
Expand All @@ -287,7 +286,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
f'input_grid=\n{input_grid}\n,input_realm=\n' + \
f'{input_realm}\n,/interp_method=\n{interp_method}') \
from exc
print(f'input_grid = {input_grid }\n' + \
fre_logger.info(f'input_grid = {input_grid }\n' + \
f'input_realm = {input_realm }\n' + \
f'interp_method = {interp_method }' )

Expand All @@ -299,7 +298,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
else f"/{truncate_date(begin,'P1D')}.{source}.nc"
if not Path( target_file ).exists():
raise OSError(f'regrid_xy target does not exist. \ntarget_file={target_file}')
print(f'target_file={target_file}') #DELETE
fre_logger.info(f'target_file={target_file}') #DELETE


# optional per-component inputs
Expand All @@ -316,7 +315,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
output_grid_lon = def_xy_interp[0]
output_grid_lat = def_xy_interp[1]

print( f'output_grid_type = {output_grid_type }\n' + \
fre_logger.info( f'output_grid_type = {output_grid_type }\n' + \
f'remap_file = {remap_file }\n' + \
f'more_options = {more_options }\n' + \
f'output_grid_lon = {output_grid_lon }\n' + \
Expand All @@ -336,22 +335,22 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
raise ValueError(f'input_realm={input_realm} not recognized.')

# this is just to get the grid_file name
#print(f'mosaic_type = {mosaic_type}')
#print(f'grid_spec_file = {grid_spec_file}')
#print(f'input_mosaic = get_mosaic_file_name(grid_spec_file, mosaic_type)')
fre_logger.debug(f'mosaic_type = {mosaic_type}')
fre_logger.debug(f'grid_spec_file = {grid_spec_file}')
fre_logger.debug(f'input_mosaic = get_mosaic_file_name(grid_spec_file, mosaic_type)')

# assume input_mosaic near input grid_spec, where intially specified.
input_mosaic = input_dir + get_mosaic_file_name(grid_spec_file, mosaic_type)
#print(f'input_mosaic = {input_mosaic}')
fre_logger.debug(f'input_mosaic = {input_mosaic}')

## this is to get the tile1 filename?
mosaic_grid_file = input_dir + get_mosaic_grid_file_name(input_mosaic)
#print(f'mosaic_grid_file = {mosaic_grid_file}')
fre_logger.debug(f'mosaic_grid_file = {mosaic_grid_file}')

# need source file dimenions for lat/lon
source_nx = str(int(Dataset(mosaic_grid_file).dimensions['nx'].size / 2 ))
source_ny = str(int(Dataset(mosaic_grid_file).dimensions['ny'].size / 2 ))
print(f'source_[nx,ny] = ({source_nx},{source_ny})')
fre_logger.info(f'source_[nx,ny] = ({source_nx},{source_ny})')
Dataset(mosaic_grid_file).close()


Expand All @@ -363,7 +362,7 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
raise OSError('remap_file={remap_file} could not be copied to local dir') \
from exc
else:
print('remap_file was not specified nor found. looking for default one')
fre_logger.info('remap_file was not specified nor found. looking for default one')
remap_file= f'fregrid_remap_file_{output_grid_lon}_by_{output_grid_lat}.nc' \
if \
all( [output_grid_lon is not None,
Expand All @@ -377,16 +376,16 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
f'{FREGRID_SHARED_FILES}/{input_grid}/' + \
f'{source_nx}_by_{source_ny}/{remap_file}'

print(f'remap_file = {remap_file }' + \
fre_logger.info(f'remap_file = {remap_file }' + \
f'remap_cache_file = {remap_cache_file }' + \
f'central_remap_cache_file = {central_remap_cache_file}' )

if Path( remap_cache_file ).exists():
print(f'NOTE: using cached remap file {remap_cache_file}')
fre_logger.info(f'NOTE: using cached remap file {remap_cache_file}')
shutil.copy(remap_cache_file,
work_dir+remap_cache_file.split('/').pop())
elif Path( central_remap_cache_file ).exists():
print(f'NOTE: using centrally cached remap file {remap_cache_file}')
fre_logger.info(f'NOTE: using centrally cached remap file {remap_cache_file}')
shutil.copy(central_remap_cache_file,
work_dir+central_remap_cache_file.split('/').pop())

Expand All @@ -395,12 +394,12 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
# if no variables in config, find the interesting ones to regrid
if regrid_vars is None:
regrid_vars=make_regrid_var_list( target_file , interp_method)
print(f'regrid_vars = {regrid_vars}') #DELETE
fre_logger.info(f'regrid_vars = {regrid_vars}') #DELETE

#check if there's anything worth regridding
if len(regrid_vars) < 1:
raise ValueError('make_regrid_var_list found no vars to regrid. and no vars given. exit')
print(f'regridding {len(regrid_vars)} variables: {regrid_vars}')
fre_logger.info(f'regridding {len(regrid_vars)} variables: {regrid_vars}')
regrid_vars_str=','.join(regrid_vars) # fregrid needs comma-demarcated list of vars


Expand Down Expand Up @@ -434,10 +433,10 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
if more_options is not None:
fregrid_command.append(f'{more_options}')

print(f"\n\nabout to run the following command: \n{' '.join(fregrid_command)}\n")
fre_logger.info(f"\n\nabout to run the following command: \n{' '.join(fregrid_command)}\n")
fregrid_proc = subprocess.run( fregrid_command, check = False )#i hate it
fregrid_rc =fregrid_proc.returncode
print(f'fregrid_result.returncode()={fregrid_rc}')
fre_logger.info(f'fregrid_result.returncode()={fregrid_rc}')


# output wrangling
Expand All @@ -446,8 +445,8 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
if not Path( remap_cache_file ).exists():
remap_cache_file_dir='/'.join(remap_cache_file.split('/')[0:-1])
Path( remap_cache_file_dir ).mkdir( parents = True , exist_ok = True)
print(f'copying \nremap_file={remap_file} to')
print(f'remap_cache_file_dir={remap_cache_file_dir}')
fre_logger.info(f'copying \nremap_file={remap_file} to')
fre_logger.info(f'remap_cache_file_dir={remap_cache_file_dir}')
shutil.copy(remap_file, remap_cache_file_dir)

# more output wrangling
Expand All @@ -456,12 +455,12 @@ def regrid_xy(input_dir = None, output_dir = None, begin = None, tmp_dir = None,
else output_dir + '/' + output_grid_type
Path( final_output_dir ).mkdir( exist_ok = True)

print(f'TRYING TO COPY {output_file} TO {final_output_dir}')
fre_logger.info(f'TRYING TO COPY {output_file} TO {final_output_dir}')
shutil.copy(output_file, final_output_dir)

continue # end of comp loop, exit or next one.

print('done running regrid_xy()')
fre_logger.info('done running regrid_xy()')
return 0


Expand Down
Loading