From d2d6ec6afb630636db4b2fed4f0918e17320c490 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Thu, 12 Aug 2021 20:58:36 -0400 Subject: [PATCH 01/17] Updates for MOM vertical and horizontal grids - Added MOM6 35-level z-coordinate - Renamed to mom6_z_coord - MOM6 rho2 coordinate - Moved construct_rect_grid() to horizontal submodule - Added horizontal submodule - Added tripolar grid generator - Added MOM6 35-level z-coordinate - Renamed to mom6_z_coord --- mdtf_test_data/resources/ocean_static_5deg.nc | Bin 0 -> 233984 bytes mdtf_test_data/synthetic/__init__.py | 1 + .../synthetic/horizontal/__init__.py | 2 + .../horizontal/construct_rect_grid.py | 145 ++++++++++++++++++ .../horizontal/construct_tripolar_grid.py | 87 +++++++++++ mdtf_test_data/synthetic/synthetic_data.py | 2 +- mdtf_test_data/synthetic/vertical/__init__.py | 2 + .../vertical/gfdl_plev19_vertical_coord.py | 1 - .../synthetic/vertical/gfdl_vertical_coord.py | 1 - .../synthetic/vertical/mom6_rho2_coord.py | 126 +++++++++++++++ .../synthetic/vertical/mom6_z_coord.py | 126 +++++++++++++++ .../synthetic/vertical/ncar_hybrid_coord.py | 1 - mdtf_test_data/tests/test_synthetic_data.py | 17 ++ mdtf_test_data/util/__init__.py | 4 +- mdtf_test_data/util/rectilinear.py | 136 +--------------- setup.cfg | 1 + 16 files changed, 512 insertions(+), 140 deletions(-) create mode 100644 mdtf_test_data/resources/ocean_static_5deg.nc create mode 100644 mdtf_test_data/synthetic/horizontal/__init__.py create mode 100755 mdtf_test_data/synthetic/horizontal/construct_rect_grid.py create mode 100755 mdtf_test_data/synthetic/horizontal/construct_tripolar_grid.py create mode 100755 mdtf_test_data/synthetic/vertical/mom6_rho2_coord.py create mode 100755 mdtf_test_data/synthetic/vertical/mom6_z_coord.py diff --git a/mdtf_test_data/resources/ocean_static_5deg.nc b/mdtf_test_data/resources/ocean_static_5deg.nc new file mode 100644 index 0000000000000000000000000000000000000000..3d05dc8eecd2615a23001bac2f4b0a4224ebe500 GIT binary patch literal 233984 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
N)|S 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 Date: Sun, 10 Oct 2021 16:09:48 -0400 Subject: [PATCH 02/17] NCAR tripolar ocean lat/lon bounds --- .../horizontal/construct_tripolar_grid.py | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/mdtf_test_data/synthetic/horizontal/construct_tripolar_grid.py b/mdtf_test_data/synthetic/horizontal/construct_tripolar_grid.py index ab6c757..ea5a956 100755 --- a/mdtf_test_data/synthetic/horizontal/construct_tripolar_grid.py +++ b/mdtf_test_data/synthetic/horizontal/construct_tripolar_grid.py @@ -64,7 +64,7 @@ def construct_tripolar_grid( if retain_coords is True: dset[lat.name] = lat - dset[lon.name] = lat + dset[lon.name] = lon dset[wet.name] = wet if add_attrs is True: @@ -73,8 +73,24 @@ def construct_tripolar_grid( dset[lon.name].attrs = {} dset[wet.name].attrs = {} elif attr_fmt == "ncar": - dset[lat.name].attrs = {} - dset[lon.name].attrs = {} + dset[lat.name].attrs = { + "axis": "Y", + "standard_name": "latitude", + "title": "Latitude", + "type": "double", + "units": "degrees_north", + "valid_max": 90.0, + "valid_min": -90.0, + } + dset[lon.name].attrs = { + "axis": "X", + "standard_name": "longitude", + "title": "Longitude", + "type": "double", + "units": "degrees_east", + "valid_max": 360.0, + "valid_min": 0.0, + } dset[wet.name].attrs = {} else: raise ValueError("Unknown attribute format") @@ -84,4 +100,23 @@ def construct_tripolar_grid( dset[lon.name].attrs = {} dset[wet.name].attrs = {} + if attr_fmt == "ncar": + dset = dset.rename({"xh": "nlon", "yh": "nlat"}) + + lat_range = np.array(np.arange(1, len(dset["nlat"]) + 1, dtype=np.intc)) + dset["nlat"] = xr.DataArray(lat_range, dims=("nlat")) + dset["nlat"].attrs = { + "long_name": "cell index along second dimension", + "units": "1", + } + + lon_range = np.array(np.arange(1, len(dset["nlon"]) + 1, dtype=np.intc)) + dset["nlon"] = xr.DataArray(lon_range, dims=("nlon")) + dset["nlon"].attrs = { + "long_name": "cell index along first dimension", + "units": "1", + } + + dset = dset.rename({lat.name: "lat", lon.name: "lon"}) + return dset From f0b9de7273df04ca4dc5f913eb77227dec523809 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Sun, 10 Oct 2021 16:12:07 -0400 Subject: [PATCH 03/17] Specify accompanying static file for synthetic data --- mdtf_test_data/synthetic/synthetic_setup.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index 1693c0f..fda6aac 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +import xarray as xr + __all__ = ["create_output_dirs", "synthetic_main"] """ Script to generate synthetic GFDL CM4 output """ import os @@ -67,6 +69,11 @@ def synthetic_main( # -- Create Data print("Generating data with time resolution of ", TIME_RES) for v in var_names: + static = ( + yaml_dict[v + ".static"] + if str(v + ".static") in list(yaml_dict.keys()) + else False + ) stats = ( yaml_dict[v + ".stats"] if str(v + ".stats") in list(yaml_dict.keys()) @@ -91,6 +98,12 @@ def synthetic_main( else None ) + if str(v + ".source") in list(yaml_dict.keys()): + _ds = xr.open_dataset(yaml_dict[v + ".source.filename"]) + data = _ds[yaml_dict[v + ".source.variable"]].values + else: + data = None + dset_out = generate_synthetic_dataset( DLON, DLAT, @@ -102,7 +115,9 @@ def synthetic_main( fmt=DATA_FORMAT, generator=generator, stats=stats, + static=static, coords=coords, + data=data, generator_kwargs=generator_kwargs, ) From 88e362883be41ac9c66a29abec277cdf0d1c02f1 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Mon, 11 Oct 2021 11:35:19 -0400 Subject: [PATCH 04/17] Use default static file source - If not specified in the yaml file, use the default ocean static file and the areacello variable --- mdtf_test_data/synthetic/synthetic_setup.py | 26 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index fda6aac..5d27618 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -1,6 +1,8 @@ #!/usr/bin/env python +import warnings import xarray as xr +import pkg_resources as pkgr __all__ = ["create_output_dirs", "synthetic_main"] """ Script to generate synthetic GFDL CM4 output """ @@ -98,9 +100,27 @@ def synthetic_main( else None ) - if str(v + ".source") in list(yaml_dict.keys()): - _ds = xr.open_dataset(yaml_dict[v + ".source.filename"]) - data = _ds[yaml_dict[v + ".source.variable"]].values + def _load_default_static(): + """Function to read packaged static file""" + _ds = pkgr.resource_filename( + "mdtf_test_data", f"resources/ocean_static_5deg.nc" + ) + return xr.open_dataset(_ds)["areacello"].values + + # Load the ocean static file + if static: + if str(v + ".source") in list(yaml_dict.keys()): + staticfilepath = yaml_dict[v + ".source.filename"] + if os.path.exists(staticfilepath): + _ds = xr.open_dataset(staticfilepath) + data = _ds[yaml_dict[v + ".source.variable"]].values + else: + raise ValueError( + f"Specified ocean static file does not exist: {staticfilepath}" + ) + else: + warnings.warn("Using default 5-degree ocean static file for grid") + data = _load_default_static() else: data = None From 6a18bb308e6839afb4f2e087fefac1ec07252be1 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Mon, 11 Oct 2021 12:32:28 -0400 Subject: [PATCH 05/17] Monthly ocean data in CMIP format - used by tropical sea level POD --- mdtf_test_data/config/cmip_mon.yml | 110 +++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 mdtf_test_data/config/cmip_mon.yml diff --git a/mdtf_test_data/config/cmip_mon.yml b/mdtf_test_data/config/cmip_mon.yml new file mode 100644 index 0000000..a7dca8f --- /dev/null +++ b/mdtf_test_data/config/cmip_mon.yml @@ -0,0 +1,110 @@ +variables : + name : + - "areacello" + - "zos" + - "tauuo" + - "tauvo" + +areacello : + atts : + cell_methods : "area: sum" + comment : "TAREA" + coordinates : "lat lon" + description : "Cell areas for any grid used to report ocean variables and variables which are requested as used on the model ocean grid (e.g. hfsso, which is a downward heat flux from the atmosphere interpolated onto the ocean grid). These cell areas should be defined to enable exact calculation of global integrals (e.g., of vertical fluxes of energy at the surface and top of the atmosphere)." + frequency : "fx" + id : "areacello" + long_name : "Grid-Cell Area for Ocean Variables" + mipTable : "Ofx" + missing_value : 1.e+20 + out_name : "areacello" + prov : "Ofx ((isd.003))" + realm : "ocean" + standard_name : "cell_area" + time_label : "None" + time_title : "No temporal dimensions ... fixed field" + title : "Grid-Cell Area for Ocean Variables" + type : "real" + units : "m2" + variable_id : "areacello" + grid : "tripolar" + static : true + +zos : + atts : + cell_measures : 'area: areacello' + cell_methods : 'area: mean where sea time: mean' + comment : 'SSH' + coordinates : "lat lon" + description : 'This is the dynamic sea level, so should have zero global area mean. It should not include inverse barometer depressions from sea ice.' + frequency : 'mon' + id : 'zos' + long_name : 'Sea Surface Height Above Geoid' + mipTable : 'Omon' + out_name : 'zos' + prov : 'Omon ((isd.003))' + realm : 'ocean' + standard_name : 'sea_surface_height_above_geoid' + time : 'time' + time_label : 'time-mean' + time_title : 'Temporal mean' + title : 'Sea Surface Height Above Geoid' + type : 'real' + units : 'm' + variable_id : 'zos' + grid : "tripolar" + stats : + - [-0.09352626651525497, 0.7232627272605896] + +tauuo : + atts : + cell_measures : '--OPT' + cell_methods : 'time: mean' + comment : 'TAUX' + coordinates : "lat lon" + description : 'This is the stress on the liquid ocean from overlying atmosphere, sea ice, ice shelf, etc.' + frequency : 'mon' + id : 'tauuo' + long_name : 'Surface Downward X Stress' + mipTable : 'Omon' + out_name : 'tauuo' + positive : 'down' + prov : 'Omon ((isd.003))' + realm : 'ocean' + standard_name : 'surface_downward_x_stress' + time : 'time' + time_label : 'time-mean' + time_title : 'Temporal mean' + title : 'Surface Downward X Stress' + type : 'real' + units : 'N m-2' + variable_id : 'tauuo' + grid : "tripolar" + stats : + - [0.001569337327964604, 0.07642165571451187] + +tauvo : + atts : + cell_measures : '--OPT' + cell_methods : 'time: mean' + comment : 'TAUY' + coordinates : "lat lon" + description : 'This is the stress on the liquid ocean from overlying atmosphere, sea ice, ice shelf, etc.' + frequency : 'mon' + id : 'tauuo' + long_name : 'Surface Downward Y Stress' + mipTable : 'Omon' + out_name : 'tauvo' + positive : 'down' + prov : 'Omon ((isd.003))' + realm : 'ocean' + standard_name : 'surface_downward_y_stress' + time : 'time' + time_label : 'time-mean' + time_title : 'Temporal mean' + title : 'Surface Downward Y Stress' + type : 'real' + units : 'N m-2' + variable_id : 'tauuo' + grid : "tripolar" + stats : + - [0.0020488635636866093, 0.04154461994767189] From bec696f52b81c201a37e0cd7d2dbb035d71a83bd Mon Sep 17 00:00:00 2001 From: John Krasting Date: Mon, 11 Oct 2021 12:37:06 -0400 Subject: [PATCH 06/17] Generate tripolar synthetic data in CMIP format - Generates data on a 5-degree tripolar grid - Sample entries in the `cmip_mon.yml` file - Use the `CMIP` convention to generate sample data for the tropical sea level MDTF POD --- mdtf_test_data/synthetic/synthetic_data.py | 113 +++++++++++++++----- mdtf_test_data/synthetic/synthetic_setup.py | 23 +++- scripts/mdtf_synthetic.py | 30 +++--- 3 files changed, 123 insertions(+), 43 deletions(-) diff --git a/mdtf_test_data/synthetic/synthetic_data.py b/mdtf_test_data/synthetic/synthetic_data.py index 3f853c0..3823c24 100755 --- a/mdtf_test_data/synthetic/synthetic_data.py +++ b/mdtf_test_data/synthetic/synthetic_data.py @@ -12,6 +12,7 @@ import xarray as xr import numpy as np from mdtf_test_data.synthetic.horizontal import construct_rect_grid +from mdtf_test_data.synthetic.horizontal import construct_tripolar_grid import mdtf_test_data.generators as generators from mdtf_test_data.synthetic.time import generate_monthly_time_axis @@ -68,6 +69,9 @@ def generate_synthetic_dataset( generator="normal", generator_kwargs=None, stats=None, + static=False, + data=None, + grid="standard", ): """Generates xarray dataset of syntheic data in NCAR format @@ -91,6 +95,11 @@ def generate_synthetic_dataset( Variable attributes, by default None stats : tuple or list of tuples Array statistics in the format of [(mean,stddev)] + static : bool + Flag denoting if variable is static + grid : str + Type of output grid, either "standard" or "tripolar", + by default "standard" Returns ------- @@ -100,32 +109,46 @@ def generate_synthetic_dataset( attrs = {} if attrs is None else attrs + # some logical control flags do_bounds = True if fmt == "cmip" else False - dset = construct_rect_grid( - dlon, dlat, add_attrs=True, attr_fmt=fmt, bounds=do_bounds - ) - lat = dset.lat - lon = dset.lon - xyshape = (len(dset["lat"]), len(dset["lon"])) - - if timeres == "mon": - ds_time = generate_monthly_time_axis(startyear, nyears, timefmt=fmt) - elif timeres == "day": - ds_time = generate_daily_time_axis(startyear, nyears, timefmt=fmt) - elif timeres == "3hr": - ds_time = generate_hourly_time_axis(startyear, nyears, 3, timefmt=fmt) - elif timeres == "1hr": - ds_time = generate_hourly_time_axis(startyear, nyears, 1, timefmt=fmt) + # Step 1: set up the horizontal grid + if grid == "tripolar": + dset = construct_tripolar_grid( + attr_fmt="ncar", retain_coords=True, add_attrs=True + ) + xyshape = dset["mask"].shape + lat = dset.nlat + lon = dset.nlon else: - print(timeres) - raise ValueError("Unknown time resolution requested") - - dset = ds_time.merge(dset) - time = dset["time"] + dset = construct_rect_grid( + dlon, dlat, add_attrs=True, attr_fmt=fmt, bounds=do_bounds + ) + lat = dset.lat + lon = dset.lon + xyshape = (len(dset["lat"]), len(dset["lon"])) + + # Step 2: set up the time axis + if static is False: + if timeres == "mon": + ds_time = generate_monthly_time_axis(startyear, nyears, timefmt=fmt) + elif timeres == "day": + ds_time = generate_daily_time_axis(startyear, nyears, timefmt=fmt) + elif timeres == "3hr": + ds_time = generate_hourly_time_axis(startyear, nyears, 3, timefmt=fmt) + elif timeres == "1hr": + ds_time = generate_hourly_time_axis(startyear, nyears, 1, timefmt=fmt) + else: + print(timeres) + raise ValueError("Unknown time resolution requested") - generator_kwargs = {} if generator_kwargs is None else generator_kwargs + dset = ds_time.merge(dset) + time = dset["time"] + ntimes = len(time) + else: + ntimes = 1 + # Step 3: generate the vertical coordinate if stats is not None: stats = [stats] if not isinstance(stats, list) else stats if len(stats) > 1: @@ -139,6 +162,10 @@ def generate_synthetic_dataset( else: dset = dset.merge(gfdl_vertical_coord()) lev = dset.pfull + + # Step 4: define the synthetic data generator kernel + generator_kwargs = {} if generator_kwargs is None else generator_kwargs + if stats is not None: generator_kwargs["stats"] = stats assert generator in list( @@ -146,25 +173,51 @@ def generate_synthetic_dataset( ), f"Unknown generator method: {generator}" generator = generators.__dict__[generator] - data = generators.generate_random_array( - xyshape, len(time), generator=generator, generator_kwargs=generator_kwargs + # Step 5: generate the synthetic data array + data = ( + generators.generate_random_array( + xyshape, ntimes, generator=generator, generator_kwargs=generator_kwargs + ) + if data is None + else data ) data = data.squeeze() - if len(data.shape) == 4: - assert data.shape[1] == len(lev), "Length of stats must match number of levels" - dset[varname] = xr.DataArray(data, coords=(time, lev, lat, lon), attrs=attrs) + # Step 6: convert to Xarray DataArray by assigning coords + mask = dset["mask"].values if "mask" in dset.variables else 1.0 + data = np.array(data * mask, dtype=np.float32) + + if static is True: + if len(data.shape) == 4: + assert data.shape[1] == len( + lev + ), "Length of stats must match number of levels" + dset[varname] = xr.DataArray(data, coords=(lev, lat, lon), attrs=attrs) + else: + dset[varname] = xr.DataArray(data, coords=(lat, lon), attrs=attrs) else: - dset[varname] = xr.DataArray(data, coords=(time, lat, lon), attrs=attrs) + if len(data.shape) == 4: + print(varname) + assert data.shape[1] == len( + lev + ), "Length of stats must match number of levels" + dset[varname] = xr.DataArray( + data, coords=(time, lev, lat, lon), attrs=attrs + ) + else: + dset[varname] = xr.DataArray(data, coords=(time, lat, lon), attrs=attrs) + dset.set_coords(("lat", "lon")) if coords is not None: + print(coords) dset[coords["name"]] = xr.DataArray(coords["value"], attrs=coords["atts"]) dset[varname].attrs = {**dset[varname].attrs, "coordinates": coords["name"]} dset.attrs["convention"] = fmt if fmt == "cmip": - dset["bnds"].attrs = {"long_name": "vertex number"} + if "bnds" in dset.variables: + dset["bnds"].attrs = {"long_name": "vertex number"} cmip_global_atts = [ "external_variables", "history", @@ -215,6 +268,10 @@ def generate_synthetic_dataset( cmip_global_atts = {x: "" for x in cmip_global_atts} dset.attrs = {**dset.attrs, **cmip_global_atts} + # remove unused fields + if grid == "tripolar": + dset = dset.drop(["mask", "wet", "depth"]) + return dset diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index 5d27618..190a6e1 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -44,6 +44,16 @@ def create_output_dirs(CASENAME="", ) out_dir_root= f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" + if not os.path.exists(f"{CASENAME}/day"): + os.makedirs(f"{CASENAME}/day") + if not os.path.exists(f"{CASENAME}/mon"): + os.makedirs(f"{CASENAME}/mon") + if "NCAR" in CASENAME: + if not os.path.exists(f"{CASENAME}/3hr"): + os.makedirs(f"{CASENAME}/3hr") + if not os.path.exists(f"{CASENAME}/1hr"): + os.makedirs(f"{CASENAME}/1hr") + if not os.path.exists(f"{out_dir_root}/day"): os.makedirs(f"{out_dir_root}/day") if "NCAR" in CASENAME: @@ -91,8 +101,16 @@ def synthetic_main( if str(v + ".generator.args") in list(yaml_dict.keys()) else {} ) - # vinfo = yaml_dict[v] - # print(vinfo) + grid = ( + yaml_dict[v + ".grid"] + if str(v + ".grid") in list(yaml_dict.keys()) + else "standard" + ) + + assert grid in [ + "tripolar", + "standard", + ], f"Unknown grid `{grid}` specified for variable `{v}`" coords = ( yaml_dict[v]["coordinates"] @@ -139,6 +157,7 @@ def _load_default_static(): coords=coords, data=data, generator_kwargs=generator_kwargs, + grid=grid, ) if DATA_FORMAT == "cmip": diff --git a/scripts/mdtf_synthetic.py b/scripts/mdtf_synthetic.py index 3a3cb55..ee1fa70 100755 --- a/scripts/mdtf_synthetic.py +++ b/scripts/mdtf_synthetic.py @@ -143,20 +143,24 @@ def main(): ) if cli_info.convention == "CMIP": print("Importing CMIP variable information") - input_data = pkgr.resource_filename("mdtf_test_data", "config/cmip_day.yml") - input_data = read_yaml(input_data) + time_res = ["mon", "day"] + for t in time_res: + input_data = pkgr.resource_filename( + "mdtf_test_data", f"config/cmip_{t}.yml" + ) + input_data = read_yaml(input_data) - print("Calling Synthetic Data Generator for CMIP data") - synthetic_main( - input_data, - DLAT=cli_info.dlat, - DLON=cli_info.dlon, - STARTYEAR=cli_info.startyear, - NYEARS=cli_info.nyears, - CASENAME="CMIP.Synthetic", - TIME_RES="day", - DATA_FORMAT="cmip", - ) + print("Calling Synthetic Data Generator for CMIP data") + synthetic_main( + input_data, + DLAT=cli_info.dlat, + DLON=cli_info.dlon, + STARTYEAR=cli_info.startyear, + NYEARS=cli_info.nyears, + CASENAME="CMIP.Synthetic", + TIME_RES=t, + DATA_FORMAT="cmip", + ) if __name__ == "__main__": From 6274d5b9e0c515cee1c5cb7203a9aa35a273fe23 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Mon, 11 Oct 2021 13:04:28 -0400 Subject: [PATCH 07/17] Issue with CMIP date string bug - Current implementation only worked with daily data - More flexible solution works with monthly and daily --- mdtf_test_data/synthetic/synthetic_data.py | 1 - mdtf_test_data/synthetic/synthetic_setup.py | 33 +++++++-------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/mdtf_test_data/synthetic/synthetic_data.py b/mdtf_test_data/synthetic/synthetic_data.py index 3823c24..ecc5ef3 100755 --- a/mdtf_test_data/synthetic/synthetic_data.py +++ b/mdtf_test_data/synthetic/synthetic_data.py @@ -209,7 +209,6 @@ def generate_synthetic_dataset( dset.set_coords(("lat", "lon")) if coords is not None: - print(coords) dset[coords["name"]] = xr.DataArray(coords["value"], attrs=coords["atts"]) dset[varname].attrs = {**dset[varname].attrs, "coordinates": coords["name"]} diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index 190a6e1..5a7f8df 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -11,11 +11,8 @@ from .synthetic_data import write_to_netcdf -def generate_date_string(STARTYEAR=1, - NYEARS=1, - TIME_RES="" - ): - """ formulate the date string in the file name""" +def generate_date_string(STARTYEAR=1, NYEARS=1, TIME_RES=""): + """formulate the date string in the file name""" date_string = ( str(STARTYEAR).zfill(4), str(STARTYEAR + NYEARS - 1).zfill(4), @@ -24,25 +21,16 @@ def generate_date_string(STARTYEAR=1, date_string = (date_string[0] + "01", date_string[1] + "12") elif TIME_RES == "day": date_string = (date_string[0] + "0101", date_string[1] + "1231") - date_string = ("-").join(list(date_string)) + date_string = ("-").join(list(date_string)) return date_string -def create_output_dirs(CASENAME="", - STARTYEAR=1, - NYEARS=10, - TIME_RES="day" - ): + +def create_output_dirs(CASENAME="", STARTYEAR=1, NYEARS=10, TIME_RES="day"): """Create output data directories""" out_dir_root = CASENAME print("Creating output data directories") - if "CMIP" in CASENAME: - date_string = generate_date_string(STARTYEAR=STARTYEAR, - NYEARS=NYEARS, - TIME_RES=TIME_RES - ) - out_dir_root= f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" if not os.path.exists(f"{CASENAME}/day"): os.makedirs(f"{CASENAME}/day") @@ -64,6 +52,7 @@ def create_output_dirs(CASENAME="", if not os.path.exists(f"{out_dir_root}/1hr"): os.makedirs(f"{out_dir_root}/1hr") + def synthetic_main( yaml_dict={}, DLAT=20.0, @@ -162,14 +151,14 @@ def _load_default_static(): if DATA_FORMAT == "cmip": # formulate the date string in the file name - date_string = generate_date_string(STARTYEAR=STARTYEAR, - NYEARS=NYEARS, - TIME_RES=TIME_RES - ) + date_string = generate_date_string( + STARTYEAR=STARTYEAR, NYEARS=NYEARS, TIME_RES=TIME_RES + ) outname = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}.{v}.{TIME_RES}.nc" # output root directory and file name base must match - out_dir_root = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" + # out_dir_root = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" + out_dir_root = CASENAME else: outname = f"{CASENAME}.{v}.{TIME_RES}.nc" out_dir_root = CASENAME From ada08cbe11e32764d8a17a7fd72c084fa88cf37e Mon Sep 17 00:00:00 2001 From: Jess <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Tue, 12 Oct 2021 13:19:27 -0400 Subject: [PATCH 08/17] Update README.md to reflect PyPI installation --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 399b890..b5aae73 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,13 @@ cftime * xESMF ## Getting the code -``` -git clone https://github.com/jkrasting/mdtf_test_data.git -``` + +The mdtf_test_data package is available on [PyPI](https://pypi.org/project/mdtf-test-data/). ## Installation ``` -cd mdtf_test_data -pip install . +mkdir mdtf_test_data && cd mdtf_test_data +pip install mdtf-test-data ``` ## Usage @@ -31,7 +30,7 @@ This package can be used to generate fully-synthetic datasets based on NCAR's CESM2 and GFDL's CM4 model output that can be used to test the MDTF-Diagnostics package. ``` -usage: ./scripts/mdtf_synthetic.py [-h] [-c CONVENTION] [--startyear year] [--nyears years] +usage: mdtf_synthetic.py [-h] [-c CONVENTION] [--startyear year] [--nyears years] [--dlat latitude resolution in degrees] [--dlon longitude resolution in degrees] [--unittest] Required arguments: @@ -48,23 +47,24 @@ Optional arguments: To generate NCAR CESM output in a directory called `NCAR.Synthetic`: ``` -./scripts/mdtf_synthetic.py -c [ NCAR | CESM ] --nyears 7 +mdtf_synthetic.py -c [ NCAR | CESM ] --nyears 7 ``` Note that "NCAR" and "CESM" are both valid input values that generate output files appended with "NCAR_Synthetic". To generate GFDL CM4 output in a directory called `GFDL.Synthetic`: ``` -./scripts/mdtf_synthetic.py -c GFDL --nyears 10 +mdtf_synthetic.py -c GFDL --nyears 10 ``` To generate CMIP output in a directory called `CMIP.Synthetic`: ``` -./scripts/mdtf_synthetic.py -c CMIP --nyears 10 +mdtf_synthetic.py -c CMIP --nyears 10 ``` To coarsen an existing NetCDF file: ``` -mdtf_synthetic/util/mdtf-coarsen.py +git clone https://github.com/jkrasting/mdtf_test_data.git +cd mdtf_test_data usage: mdtf_synthetic/util/mdtf-coarsen.py [-h] [-r REGRID_METHOD] [-o OUTFILE] [-O] infile Coarsen a NetCDF file. From 526183e02207f19f5c21fa1efb94f53acbe2b1c0 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Fri, 15 Oct 2021 17:56:08 -0400 Subject: [PATCH 09/17] added thetao and so fields to cmip_mon.yml --- mdtf_test_data/config/cmip_mon.yml | 168 +++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/mdtf_test_data/config/cmip_mon.yml b/mdtf_test_data/config/cmip_mon.yml index a7dca8f..12cdfd7 100644 --- a/mdtf_test_data/config/cmip_mon.yml +++ b/mdtf_test_data/config/cmip_mon.yml @@ -4,6 +4,8 @@ variables : - "zos" - "tauuo" - "tauvo" + - "so" + - "thetao" areacello : atts : @@ -108,3 +110,169 @@ tauvo : grid : "tripolar" stats : - [0.0020488635636866093, 0.04154461994767189] + so: + atts: + cell_measures: '--OPT' + cell_methods: 'time: mean' + coordinates: "lat lon" + description: 'sea water salinity' + frequency: 'mon' + id: 'so' + long_name: 'sea water salinity' + out_name: 'so' + positive: 'down' + realm: 'ocean' + standard_name: 'sea_water_salinity' + time: 'time' + time_label: 'time-mean' + time_title: 'Temporal mean' + title: 'Sea Water Salinity' + type: 'real' + units: 'psu' + variable_id: 'so' + grid: "tripolar" + stats: + - [ 37.091705, 37.169098 ] + - [ 37.090427, 37.16815 ] + - [ 37.086548, 37.163586 ] + - [ 37.054672, 37.13838 ] + - [ 36.948807, 37.03889 ] + - [ 36.872665, 36.96365 ] + - [ 36.825863, 36.92407 ] + - [ 36.793346, 36.899162 ] + - [ 36.767387, 36.878147 ] + - [ 36.744244, 36.85687 ] + - [ 36.721256, 36.8339 ] + - [ 36.698128, 36.80898 ] + - [ 36.673008, 36.782074 ] + - [ 36.644700, 36.753128 ] + - [ 36.61258, 36.72212 ] + - [ 36.576668, 36.689003 ] + - [ 36.537296, 36.65366 ] + - [ 36.495052, 36.615482 ] + - [ 36.45111, 36.574173 ] + - [ 36.406807, 36.529804 ] + - [ 36.36356, 36.483067 ] + - [ 36.322468, 36.43506 ] + - [ 36.28378, 36.387257 ] + - [ 36.247128, 36.340984 ] + - [ 36.21161, 36.297478 ] + - [ 36.176785, 36.257397 ] + - [ 36.14187, 36.22075 ] + - [ 36.106518, 36.186737 ] + - [ 36.07005, 36.154045 ] + - [ 36.03362, 36.121113 ] + - [ 36.000076, 36.087433 ] + - [ 35.974377, 36.05332 ] + - [ 35.96569, 36.020916 ] + - [ 35.982994, 35.994526 ] + - [ 36.03397, 35.97875 ] + - [ 36.11469, 35.44826 ] + - [ 36.207542, 35.27616 ] + - [ 36.277607, 35.158367 ] + - [ 36.288494, 35.102154 ] + - [ 36.21689, 35.088215 ] + - [ 36.06323, 35.089135 ] + - [ 35.852272, 35.091137 ] + - [ 35.620174, 35.089703 ] + - [ 35.406887, 35.08471 ] + - [ 35.24506, 35.078117 ] + - [ 35.145275, 35.071728 ] + - [ 35.097454, 35.06888 ] + - [ 35.080467, 35.06888 ] + - [ 35.077976, 35.072856 ] + - [ 35.079536, 35.078241 ] + - [ 35.081894, 35.0827532 ] + - [ 35.084568, 35.083969 ] + - [ 35.087505, 35.086932 ] + - [ 35.09258, 35.092854 ] + - [ 35.09881, 35.099214 ] + - [ 35.10581, 35.11024 ] + - [ 35.11267, 35.11221 ] + - [ 35.11715, 35.116893 ] + - [ 35.118637, 35.117593] + - [ 35.1188980, 45.118823 ] + + thetao: + atts: + cell_measures: '--OPT' + cell_methods: 'time: mean' + coordinates: "lat lon" + description: 'sea water potential temperature' + frequency: 'mon' + id: 'thetao' + long_name: 'sea water potential temperature' + out_name: 'thetao' + positive: 'down' + realm: 'ocean' + standard_name: 'sea_water_potential_temperature' + time: 'time' + time_label: 'time-mean' + time_title: 'Temporal mean' + title: 'Sea Water Potential Temperature' + type: 'real' + units: 'degC' + variable_id: 'thetao' + grid: "tripolar" + stats: + - [ 23.438206, 23.501827 ] + - [ 23.393858, 23.457973 ] + - [ 23.332743, 23.399124 ] + - [ 23.145956, 23.248714 ] + - [ 22.192282, 22.395288 ] + - [ 21.162468, 21.3618 ] + - [ 20.480297, 20.662258 ] + - [ 20.052425, 20.224604 ] + - [ 19.769545, 19.938387 ] + - [ 19.559475, 19.728683 ] + - [ 19.38311, 19.554249 ] + - [ 19.219982, 19.393152 ] + - [ 19.060669, 19.235872 ] + - [ 18.89744, 19.073635 ] + - [ 18.726213, 18.900887 ] + - [ 18.545187, 18.714998 ] + - [ 18.352053, 18.513506 ] + - [ 18.143354, 18.293566 ] + - [ 17.917408, 18.054794 ] + - [ 17.674921, 17.799715 ] + - [ 17.41897, 17.532667 ] + - [ 17.154121, 17.25874 ] + - [ 16.885664, 16.982744 ] + - [ 16.61834, 16.708748 ] + - [ 16.356245, 16.439966 ] + - [ 16.10178, 16.178211 ] + - [ 15.854486, 15.922733 ] + - [ 15.610342, 15.66957 ] + - [ 15.362126, 15.412205 ] + - [ 15.101099, 15.14296 ] + - [ 14.81898, 14.854497 ] + - [ 14.509788, 14.541605 ] + - [ 14.171214, 14.202383 ] + - [ 13.806399, 13.840008 ] + - [ 13.422476, 13.461085 ] + - [ 13.022508, 13.067213 ] + - [ 12.592179, 12.6415415 ] + - [ 12.087998, 12.138475 ] + - [ 11.438775, 11.487448 ] + - [ 10.568429, 10.617012 ] + - [ 9.438543, 9.494733 ] + - [ 8.101263, 8.172442 ] + - [ 6.705785, 6.790224 ] + - [ 5.4549055, 5.536766 ] + - [ 4.5219636, 4.5789433 ] + - [ 3.9483228, 3.969062 ] + - [ 3.6311398, 3.623945 ] + - [ 3.431602, 3.4139373 ] + - [ 3.279515, 3.261523 ] + - [ 3.1576657, 3.1415262 ] + - [ 3.0560117, 3.044555 ] + - [ 2.9734735, 2.9688902 ] + - [ 2.9163926, 2.9166791 ] + - [ 2.8895073, 2.890756 ] + - [ 2.885688, 2.8863788 ] + - [ 2.886533, 2.8871552 ] + - [ 2.88546, 2.8871353 ] + - [ 2.8842352, 2.886324] + - [ 2.883122, 2.885201 ] + - [ 2.8819532, 2.886345] + From 088246b5e859b44482f514694a1ee9e66b9e88a0 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Mon, 18 Oct 2021 12:00:43 -0400 Subject: [PATCH 10/17] changed lengths of so and thetao stats to match mom6 z_l size --- mdtf_test_data/config/cmip_mon.yml | 329 +++++++++++++++-------------- 1 file changed, 165 insertions(+), 164 deletions(-) diff --git a/mdtf_test_data/config/cmip_mon.yml b/mdtf_test_data/config/cmip_mon.yml index 12cdfd7..51ec3b8 100644 --- a/mdtf_test_data/config/cmip_mon.yml +++ b/mdtf_test_data/config/cmip_mon.yml @@ -110,169 +110,170 @@ tauvo : grid : "tripolar" stats : - [0.0020488635636866093, 0.04154461994767189] - so: - atts: - cell_measures: '--OPT' - cell_methods: 'time: mean' - coordinates: "lat lon" - description: 'sea water salinity' - frequency: 'mon' - id: 'so' - long_name: 'sea water salinity' - out_name: 'so' - positive: 'down' - realm: 'ocean' - standard_name: 'sea_water_salinity' - time: 'time' - time_label: 'time-mean' - time_title: 'Temporal mean' - title: 'Sea Water Salinity' - type: 'real' - units: 'psu' - variable_id: 'so' - grid: "tripolar" - stats: - - [ 37.091705, 37.169098 ] - - [ 37.090427, 37.16815 ] - - [ 37.086548, 37.163586 ] - - [ 37.054672, 37.13838 ] - - [ 36.948807, 37.03889 ] - - [ 36.872665, 36.96365 ] - - [ 36.825863, 36.92407 ] - - [ 36.793346, 36.899162 ] - - [ 36.767387, 36.878147 ] - - [ 36.744244, 36.85687 ] - - [ 36.721256, 36.8339 ] - - [ 36.698128, 36.80898 ] - - [ 36.673008, 36.782074 ] - - [ 36.644700, 36.753128 ] - - [ 36.61258, 36.72212 ] - - [ 36.576668, 36.689003 ] - - [ 36.537296, 36.65366 ] - - [ 36.495052, 36.615482 ] - - [ 36.45111, 36.574173 ] - - [ 36.406807, 36.529804 ] - - [ 36.36356, 36.483067 ] - - [ 36.322468, 36.43506 ] - - [ 36.28378, 36.387257 ] - - [ 36.247128, 36.340984 ] - - [ 36.21161, 36.297478 ] - - [ 36.176785, 36.257397 ] - - [ 36.14187, 36.22075 ] - - [ 36.106518, 36.186737 ] - - [ 36.07005, 36.154045 ] - - [ 36.03362, 36.121113 ] - - [ 36.000076, 36.087433 ] - - [ 35.974377, 36.05332 ] - - [ 35.96569, 36.020916 ] - - [ 35.982994, 35.994526 ] - - [ 36.03397, 35.97875 ] - - [ 36.11469, 35.44826 ] - - [ 36.207542, 35.27616 ] - - [ 36.277607, 35.158367 ] - - [ 36.288494, 35.102154 ] - - [ 36.21689, 35.088215 ] - - [ 36.06323, 35.089135 ] - - [ 35.852272, 35.091137 ] - - [ 35.620174, 35.089703 ] - - [ 35.406887, 35.08471 ] - - [ 35.24506, 35.078117 ] - - [ 35.145275, 35.071728 ] - - [ 35.097454, 35.06888 ] - - [ 35.080467, 35.06888 ] - - [ 35.077976, 35.072856 ] - - [ 35.079536, 35.078241 ] - - [ 35.081894, 35.0827532 ] - - [ 35.084568, 35.083969 ] - - [ 35.087505, 35.086932 ] - - [ 35.09258, 35.092854 ] - - [ 35.09881, 35.099214 ] - - [ 35.10581, 35.11024 ] - - [ 35.11267, 35.11221 ] - - [ 35.11715, 35.116893 ] - - [ 35.118637, 35.117593] - - [ 35.1188980, 45.118823 ] - thetao: - atts: - cell_measures: '--OPT' - cell_methods: 'time: mean' - coordinates: "lat lon" - description: 'sea water potential temperature' - frequency: 'mon' - id: 'thetao' - long_name: 'sea water potential temperature' - out_name: 'thetao' - positive: 'down' - realm: 'ocean' - standard_name: 'sea_water_potential_temperature' - time: 'time' - time_label: 'time-mean' - time_title: 'Temporal mean' - title: 'Sea Water Potential Temperature' - type: 'real' - units: 'degC' - variable_id: 'thetao' - grid: "tripolar" - stats: - - [ 23.438206, 23.501827 ] - - [ 23.393858, 23.457973 ] - - [ 23.332743, 23.399124 ] - - [ 23.145956, 23.248714 ] - - [ 22.192282, 22.395288 ] - - [ 21.162468, 21.3618 ] - - [ 20.480297, 20.662258 ] - - [ 20.052425, 20.224604 ] - - [ 19.769545, 19.938387 ] - - [ 19.559475, 19.728683 ] - - [ 19.38311, 19.554249 ] - - [ 19.219982, 19.393152 ] - - [ 19.060669, 19.235872 ] - - [ 18.89744, 19.073635 ] - - [ 18.726213, 18.900887 ] - - [ 18.545187, 18.714998 ] - - [ 18.352053, 18.513506 ] - - [ 18.143354, 18.293566 ] - - [ 17.917408, 18.054794 ] - - [ 17.674921, 17.799715 ] - - [ 17.41897, 17.532667 ] - - [ 17.154121, 17.25874 ] - - [ 16.885664, 16.982744 ] - - [ 16.61834, 16.708748 ] - - [ 16.356245, 16.439966 ] - - [ 16.10178, 16.178211 ] - - [ 15.854486, 15.922733 ] - - [ 15.610342, 15.66957 ] - - [ 15.362126, 15.412205 ] - - [ 15.101099, 15.14296 ] - - [ 14.81898, 14.854497 ] - - [ 14.509788, 14.541605 ] - - [ 14.171214, 14.202383 ] - - [ 13.806399, 13.840008 ] - - [ 13.422476, 13.461085 ] - - [ 13.022508, 13.067213 ] - - [ 12.592179, 12.6415415 ] - - [ 12.087998, 12.138475 ] - - [ 11.438775, 11.487448 ] - - [ 10.568429, 10.617012 ] - - [ 9.438543, 9.494733 ] - - [ 8.101263, 8.172442 ] - - [ 6.705785, 6.790224 ] - - [ 5.4549055, 5.536766 ] - - [ 4.5219636, 4.5789433 ] - - [ 3.9483228, 3.969062 ] - - [ 3.6311398, 3.623945 ] - - [ 3.431602, 3.4139373 ] - - [ 3.279515, 3.261523 ] - - [ 3.1576657, 3.1415262 ] - - [ 3.0560117, 3.044555 ] - - [ 2.9734735, 2.9688902 ] - - [ 2.9163926, 2.9166791 ] - - [ 2.8895073, 2.890756 ] - - [ 2.885688, 2.8863788 ] - - [ 2.886533, 2.8871552 ] - - [ 2.88546, 2.8871353 ] - - [ 2.8842352, 2.886324] - - [ 2.883122, 2.885201 ] - - [ 2.8819532, 2.886345] +so : + atts : + cell_measures : '--OPT' + cell_methods : 'time: mean' + coordinates : "lat lon" + description : 'sea water salinity' + frequency : 'mon' + id : 'so' + long_name : 'sea water salinity' + out_name : 'so' + positive : 'down' + realm : 'ocean' + standard_name : 'sea_water_salinity' + time : 'time' + time_label : 'time-mean' + time_title : 'Temporal mean' + title : 'Sea Water Salinity' + type : 'real' + units : 'psu' + variable_id : 'so' + grid : "tripolar" + stats : + - [ 37.091705, 37.169098 ] + - [ 37.090427, 37.16815 ] + - [ 37.086548, 37.163586 ] + - [ 37.054672, 37.13838 ] + - [ 36.948807, 37.03889 ] + - [ 36.872665, 36.96365 ] + - [ 36.825863, 36.92407 ] + - [ 36.793346, 36.899162 ] + - [ 36.767387, 36.878147 ] + - [ 36.744244, 36.85687 ] + - [ 36.721256, 36.8339 ] + - [ 36.698128, 36.80898 ] + - [ 36.673008, 36.782074 ] + - [ 36.644700, 36.753128 ] + - [ 36.61258, 36.72212 ] + - [ 36.576668, 36.689003 ] + - [ 36.537296, 36.65366 ] + - [ 36.495052, 36.615482 ] + - [ 36.45111, 36.574173 ] + - [ 36.406807, 36.529804 ] + - [ 36.36356, 36.483067 ] + - [ 36.322468, 36.43506 ] + - [ 36.28378, 36.387257 ] + - [ 36.247128, 36.340984 ] + - [ 36.21161, 36.297478 ] + - [ 36.176785, 36.257397 ] + - [ 36.14187, 36.22075 ] + - [ 36.106518, 36.186737 ] + - [ 36.07005, 36.154045 ] + - [ 36.03362, 36.121113 ] + - [ 36.000076, 36.087433 ] + - [ 35.974377, 36.05332 ] + - [ 35.96569, 36.020916 ] + - [ 35.982994, 35.994526 ] + - [ 36.03397, 35.97875 ] +# - [ 36.11469, 35.44826 ] +# - [ 36.207542, 35.27616 ] +# - [ 36.277607, 35.158367 ] +# - [ 36.288494, 35.102154 ] +# - [ 36.21689, 35.088215 ] +# - [ 36.06323, 35.089135 ] +# - [ 35.852272, 35.091137 ] +# - [ 35.620174, 35.089703 ] +# - [ 35.406887, 35.08471 ] +# - [ 35.24506, 35.078117 ] +# - [ 35.145275, 35.071728 ] +# - [ 35.097454, 35.06888 ] +# - [ 35.080467, 35.06888 ] +# - [ 35.077976, 35.072856 ] +# - [ 35.079536, 35.078241 ] +# - [ 35.081894, 35.0827532 ] +# - [ 35.084568, 35.083969 ] +# - [ 35.087505, 35.086932 ] +# - [ 35.09258, 35.092854 ] +# - [ 35.09881, 35.099214 ] +# - [ 35.10581, 35.11024 ] +# - [ 35.11267, 35.11221 ] +# - [ 35.11715, 35.116893 ] +# - [ 35.118637, 35.117593] +# - [ 35.1188980, 45.118823 ] + +thetao: + atts: + cell_measures: '--OPT' + cell_methods: 'time: mean' + coordinates: "lat lon" + description: 'sea water potential temperature' + frequency: 'mon' + id: 'thetao' + long_name: 'sea water potential temperature' + out_name: 'thetao' + positive: 'down' + realm: 'ocean' + standard_name: 'sea_water_potential_temperature' + time: 'time' + time_label: 'time-mean' + time_title: 'Temporal mean' + title: 'Sea Water Potential Temperature' + type: 'real' + units: 'degC' + variable_id: 'thetao' + grid: "tripolar" + stats: + - [ 23.438206, 23.501827 ] + - [ 23.393858, 23.457973 ] + - [ 23.332743, 23.399124 ] + - [ 23.145956, 23.248714 ] + - [ 22.192282, 22.395288 ] + - [ 21.162468, 21.3618 ] + - [ 20.480297, 20.662258 ] + - [ 20.052425, 20.224604 ] + - [ 19.769545, 19.938387 ] + - [ 19.559475, 19.728683 ] + - [ 19.38311, 19.554249 ] + - [ 19.219982, 19.393152 ] + - [ 19.060669, 19.235872 ] + - [ 18.89744, 19.073635 ] + - [ 18.726213, 18.900887 ] + - [ 18.545187, 18.714998 ] + - [ 18.352053, 18.513506 ] + - [ 18.143354, 18.293566 ] + - [ 17.917408, 18.054794 ] + - [ 17.674921, 17.799715 ] + - [ 17.41897, 17.532667 ] + - [ 17.154121, 17.25874 ] + - [ 16.885664, 16.982744 ] + - [ 16.61834, 16.708748 ] + - [ 16.356245, 16.439966 ] + - [ 16.10178, 16.178211 ] + - [ 15.854486, 15.922733 ] + - [ 15.610342, 15.66957 ] + - [ 15.362126, 15.412205 ] + - [ 15.101099, 15.14296 ] + - [ 14.81898, 14.854497 ] + - [ 14.509788, 14.541605 ] + - [ 14.171214, 14.202383 ] + - [ 13.806399, 13.840008 ] + - [ 13.422476, 13.461085 ] +# - [ 13.022508, 13.067213 ] +# - [ 12.592179, 12.6415415 ] +# - [ 12.087998, 12.138475 ] +# - [ 11.438775, 11.487448 ] +# - [ 10.568429, 10.617012 ] +# - [ 9.438543, 9.494733 ] +# - [ 8.101263, 8.172442 ] +# - [ 6.705785, 6.790224 ] +# - [ 5.4549055, 5.536766 ] +# - [ 4.5219636, 4.5789433 ] +# - [ 3.9483228, 3.969062 ] +# - [ 3.6311398, 3.623945 ] +# - [ 3.431602, 3.4139373 ] +# - [ 3.279515, 3.261523 ] +# - [ 3.1576657, 3.1415262 ] +# - [ 3.0560117, 3.044555 ] +# - [ 2.9734735, 2.9688902 ] +# - [ 2.9163926, 2.9166791 ] +# - [ 2.8895073, 2.890756 ] +# - [ 2.885688, 2.8863788 ] +# - [ 2.886533, 2.8871552 ] +# - [ 2.88546, 2.8871353 ] +# - [ 2.8842352, 2.886324] +# - [ 2.883122, 2.885201 ] +# - [ 2.8819532, 2.886345 ] From 533c969b321d295495d0c4963d174ee52fa2c284 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Mon, 18 Oct 2021 12:02:28 -0400 Subject: [PATCH 11/17] added vertical level generation for cmip tripolar variables that uses mom6 vertical coordinates --- mdtf_test_data/synthetic/synthetic_data.py | 15 +++++++++++---- scripts/mdtf_synthetic.py | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mdtf_test_data/synthetic/synthetic_data.py b/mdtf_test_data/synthetic/synthetic_data.py index ecc5ef3..a26e2a2 100755 --- a/mdtf_test_data/synthetic/synthetic_data.py +++ b/mdtf_test_data/synthetic/synthetic_data.py @@ -22,6 +22,7 @@ from mdtf_test_data.synthetic.vertical import gfdl_plev19_vertical_coord from mdtf_test_data.synthetic.vertical import gfdl_vertical_coord from mdtf_test_data.synthetic.vertical import ncar_hybrid_coord +from mdtf_test_data.synthetic.vertical import mom6_z_coord def dataset_stats(filename, var=None, limit=None): @@ -155,14 +156,20 @@ def generate_synthetic_dataset( if fmt == "ncar": dset = dset.merge(ncar_hybrid_coord()) lev = dset.lev - elif fmt == "gfdl": + elif fmt == "gfdl" : if len(stats) == 19: dset = dset.merge(gfdl_plev19_vertical_coord()) lev = dset.plev19 else: dset = dset.merge(gfdl_vertical_coord()) lev = dset.pfull - + elif fmt == "cmip" and grid == "tripolar": + if len(stats) == 35: + dset = dset.merge(mom6_z_coord()) + lev = dset.z_l + elif len(stats) == 36: + dset = dset.merge(mom6_z_coord()) + lev = dset.z_i # Step 4: define the synthetic data generator kernel generator_kwargs = {} if generator_kwargs is None else generator_kwargs if stats is not None: @@ -191,7 +198,7 @@ def generate_synthetic_dataset( if len(data.shape) == 4: assert data.shape[1] == len( lev - ), "Length of stats must match number of levels" + ), f' Length of stats {data.shape[1]} must match number of levels {len(lev)}.' dset[varname] = xr.DataArray(data, coords=(lev, lat, lon), attrs=attrs) else: dset[varname] = xr.DataArray(data, coords=(lat, lon), attrs=attrs) @@ -200,7 +207,7 @@ def generate_synthetic_dataset( print(varname) assert data.shape[1] == len( lev - ), "Length of stats must match number of levels" + ), f' Length of stats {data.shape[1]} must match number of levels {len(lev)}.' dset[varname] = xr.DataArray( data, coords=(time, lev, lat, lon), attrs=attrs ) diff --git a/scripts/mdtf_synthetic.py b/scripts/mdtf_synthetic.py index ee1fa70..a317fd2 100755 --- a/scripts/mdtf_synthetic.py +++ b/scripts/mdtf_synthetic.py @@ -1,6 +1,8 @@ #!/usr/bin/env python """ mdtf_test_data driver program """ import sys +sys.path.insert(0, '../mdtf_test_data') +import sys import mdtf_test_data from mdtf_test_data.synthetic.synthetic_setup import synthetic_main from mdtf_test_data.util.cli import cli_holder @@ -10,7 +12,6 @@ MDTF_PACKAGE_PATH = mdtf_test_data.__path__[0] - def read_yaml(file_name): """A function to read YAML files""" config = EnvYAML(file_name) From 0f1bedf644100b462bef74bb62be71c0fbb95236 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Mon, 18 Oct 2021 12:56:01 -0400 Subject: [PATCH 12/17] changed the root directory name for the cmip output to match the output data file base name and the file file base names to have the format yyyymmdd for framework functionality --- mdtf_test_data/synthetic/synthetic_setup.py | 31 ++++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index 5a7f8df..ac4c220 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -28,25 +28,23 @@ def generate_date_string(STARTYEAR=1, NYEARS=1, TIME_RES=""): def create_output_dirs(CASENAME="", STARTYEAR=1, NYEARS=10, TIME_RES="day"): """Create output data directories""" + if "cmip" in str.lower(CASENAME): + # formulate the date string in the file name + date_string = generate_date_string( + STARTYEAR=STARTYEAR, NYEARS=NYEARS, TIME_RES="day" + ) + # output root directory and file name base must match + out_dir_root = f"{CASENAME.replace('.', '_')}_r1i1p1f1_gr1_{date_string}" + else: + out_dir_root = CASENAME - out_dir_root = CASENAME print("Creating output data directories") - if not os.path.exists(f"{CASENAME}/day"): - os.makedirs(f"{CASENAME}/day") - if not os.path.exists(f"{CASENAME}/mon"): - os.makedirs(f"{CASENAME}/mon") - if "NCAR" in CASENAME: - if not os.path.exists(f"{CASENAME}/3hr"): - os.makedirs(f"{CASENAME}/3hr") - if not os.path.exists(f"{CASENAME}/1hr"): - os.makedirs(f"{CASENAME}/1hr") - if not os.path.exists(f"{out_dir_root}/day"): os.makedirs(f"{out_dir_root}/day") - if "NCAR" in CASENAME: - if not os.path.exists(f"{out_dir_root}/mon"): - os.makedirs(f"{out_dir_root}/mon") + if not os.path.exists(f"{out_dir_root}/mon"): + os.makedirs(f"{out_dir_root}/mon") + if "ncar" in str.lower(out_dir_root): if not os.path.exists(f"{out_dir_root}/3hr"): os.makedirs(f"{out_dir_root}/3hr") if not os.path.exists(f"{out_dir_root}/1hr"): @@ -152,13 +150,12 @@ def _load_default_static(): if DATA_FORMAT == "cmip": # formulate the date string in the file name date_string = generate_date_string( - STARTYEAR=STARTYEAR, NYEARS=NYEARS, TIME_RES=TIME_RES + STARTYEAR=STARTYEAR, NYEARS=NYEARS, TIME_RES="day" ) outname = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}.{v}.{TIME_RES}.nc" # output root directory and file name base must match - # out_dir_root = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" - out_dir_root = CASENAME + out_dir_root = f"{CASENAME.replace('.','_')}_r1i1p1f1_gr1_{date_string}" else: outname = f"{CASENAME}.{v}.{TIME_RES}.nc" out_dir_root = CASENAME From af54d026c42b27be4dc62e4ed41c404436cb18c2 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Mon, 18 Oct 2021 15:48:49 -0400 Subject: [PATCH 13/17] added STARTYEAR and NYEARS arguments to create_output_dirs call in synthetic_setup --- mdtf_test_data/synthetic/synthetic_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdtf_test_data/synthetic/synthetic_setup.py b/mdtf_test_data/synthetic/synthetic_setup.py index ac4c220..8512b50 100755 --- a/mdtf_test_data/synthetic/synthetic_setup.py +++ b/mdtf_test_data/synthetic/synthetic_setup.py @@ -62,7 +62,7 @@ def synthetic_main( DATA_FORMAT="", ): """Main script to generate synthetic data using GFDL naming conventions""" - create_output_dirs(CASENAME) + create_output_dirs(CASENAME, STARTYEAR=STARTYEAR, NYEARS=NYEARS) # parse the yaml dictionary var_names = yaml_dict["variables.name"] # -- Create Data From 23cca37cf5124a02e16b984505eba2b1861eb966 Mon Sep 17 00:00:00 2001 From: wrongkindofdoctor <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Mon, 18 Oct 2021 16:34:32 -0400 Subject: [PATCH 14/17] cleaned up cmip_mon.yml added cmip standard lev variable to mom6_z_coord.py changed data check procedure and lev definition for cmip tripolar level generation --- mdtf_test_data/config/cmip_mon.yml | 51 +------------------ mdtf_test_data/synthetic/synthetic_data.py | 11 ++-- .../synthetic/vertical/mom6_z_coord.py | 14 +++++ 3 files changed, 20 insertions(+), 56 deletions(-) mode change 100644 => 100755 mdtf_test_data/config/cmip_mon.yml diff --git a/mdtf_test_data/config/cmip_mon.yml b/mdtf_test_data/config/cmip_mon.yml old mode 100644 new mode 100755 index 51ec3b8..aa3f278 --- a/mdtf_test_data/config/cmip_mon.yml +++ b/mdtf_test_data/config/cmip_mon.yml @@ -168,31 +168,6 @@ so : - [ 35.96569, 36.020916 ] - [ 35.982994, 35.994526 ] - [ 36.03397, 35.97875 ] -# - [ 36.11469, 35.44826 ] -# - [ 36.207542, 35.27616 ] -# - [ 36.277607, 35.158367 ] -# - [ 36.288494, 35.102154 ] -# - [ 36.21689, 35.088215 ] -# - [ 36.06323, 35.089135 ] -# - [ 35.852272, 35.091137 ] -# - [ 35.620174, 35.089703 ] -# - [ 35.406887, 35.08471 ] -# - [ 35.24506, 35.078117 ] -# - [ 35.145275, 35.071728 ] -# - [ 35.097454, 35.06888 ] -# - [ 35.080467, 35.06888 ] -# - [ 35.077976, 35.072856 ] -# - [ 35.079536, 35.078241 ] -# - [ 35.081894, 35.0827532 ] -# - [ 35.084568, 35.083969 ] -# - [ 35.087505, 35.086932 ] -# - [ 35.09258, 35.092854 ] -# - [ 35.09881, 35.099214 ] -# - [ 35.10581, 35.11024 ] -# - [ 35.11267, 35.11221 ] -# - [ 35.11715, 35.116893 ] -# - [ 35.118637, 35.117593] -# - [ 35.1188980, 45.118823 ] thetao: atts: @@ -251,29 +226,5 @@ thetao: - [ 14.171214, 14.202383 ] - [ 13.806399, 13.840008 ] - [ 13.422476, 13.461085 ] -# - [ 13.022508, 13.067213 ] -# - [ 12.592179, 12.6415415 ] -# - [ 12.087998, 12.138475 ] -# - [ 11.438775, 11.487448 ] -# - [ 10.568429, 10.617012 ] -# - [ 9.438543, 9.494733 ] -# - [ 8.101263, 8.172442 ] -# - [ 6.705785, 6.790224 ] -# - [ 5.4549055, 5.536766 ] -# - [ 4.5219636, 4.5789433 ] -# - [ 3.9483228, 3.969062 ] -# - [ 3.6311398, 3.623945 ] -# - [ 3.431602, 3.4139373 ] -# - [ 3.279515, 3.261523 ] -# - [ 3.1576657, 3.1415262 ] -# - [ 3.0560117, 3.044555 ] -# - [ 2.9734735, 2.9688902 ] -# - [ 2.9163926, 2.9166791 ] -# - [ 2.8895073, 2.890756 ] -# - [ 2.885688, 2.8863788 ] -# - [ 2.886533, 2.8871552 ] -# - [ 2.88546, 2.8871353 ] -# - [ 2.8842352, 2.886324] -# - [ 2.883122, 2.885201 ] -# - [ 2.8819532, 2.886345 ] + diff --git a/mdtf_test_data/synthetic/synthetic_data.py b/mdtf_test_data/synthetic/synthetic_data.py index a26e2a2..6f99bad 100755 --- a/mdtf_test_data/synthetic/synthetic_data.py +++ b/mdtf_test_data/synthetic/synthetic_data.py @@ -164,12 +164,11 @@ def generate_synthetic_dataset( dset = dset.merge(gfdl_vertical_coord()) lev = dset.pfull elif fmt == "cmip" and grid == "tripolar": - if len(stats) == 35: - dset = dset.merge(mom6_z_coord()) - lev = dset.z_l - elif len(stats) == 36: - dset = dset.merge(mom6_z_coord()) - lev = dset.z_i + dset = dset.merge(mom6_z_coord()) + lev = dset.lev + assert len(stats) == len(lev),\ + f' Length of stats {data.shape[1]} must match number of levels {len(lev)}.' + # Step 4: define the synthetic data generator kernel generator_kwargs = {} if generator_kwargs is None else generator_kwargs if stats is not None: diff --git a/mdtf_test_data/synthetic/vertical/mom6_z_coord.py b/mdtf_test_data/synthetic/vertical/mom6_z_coord.py index 34d7cd4..846b762 100755 --- a/mdtf_test_data/synthetic/vertical/mom6_z_coord.py +++ b/mdtf_test_data/synthetic/vertical/mom6_z_coord.py @@ -101,6 +101,7 @@ def mom6_z_coord(): ] z_i_attrs = { + "standard_name": "depth_at_cell_interface", "long_name": "Depth at interface", "units": "meters", "axis": "Z", @@ -108,6 +109,7 @@ def mom6_z_coord(): } z_l_attrs = { + "standard_name": "depth_at_cell_center", "long_name": "Depth at cell center", "units": "meters", "axis": "Z", @@ -115,6 +117,15 @@ def mom6_z_coord(): "edges": "z_i", } + # duplicate of z_l with CMIP standard attributes + lev_attrs = { + "standard_name": "depth", + "long_name": "depth", + "units": "meters", + "axis": "Z", + "positive": "down" + } + dset_out = xr.Dataset() dset_out["z_l"] = xr.DataArray( z_l, dims={"z_l": z_l}, coords={"z_l": z_l}, attrs=z_l_attrs @@ -122,5 +133,8 @@ def mom6_z_coord(): dset_out["z_i"] = xr.DataArray( z_i, dims={"z_i": z_i}, coords={"z_i": z_i}, attrs=z_i_attrs ) + dset_out["lev"] = xr.DataArray( + z_l, dims={"lev": z_l}, coords={"lev": z_l}, attrs=lev_attrs + ) return dset_out From d6a7ff86e14381bfabdb9851874d06a7b5a7ebad Mon Sep 17 00:00:00 2001 From: Jess <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Wed, 20 Oct 2021 09:36:32 -0400 Subject: [PATCH 15/17] revert mdtf_synthetic.py mods --- scripts/mdtf_synthetic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/mdtf_synthetic.py b/scripts/mdtf_synthetic.py index a317fd2..be2ceb6 100755 --- a/scripts/mdtf_synthetic.py +++ b/scripts/mdtf_synthetic.py @@ -1,8 +1,6 @@ #!/usr/bin/env python """ mdtf_test_data driver program """ import sys -sys.path.insert(0, '../mdtf_test_data') -import sys import mdtf_test_data from mdtf_test_data.synthetic.synthetic_setup import synthetic_main from mdtf_test_data.util.cli import cli_holder From 5654440ba2b277c94e8ad0eabcc84797fdfea454 Mon Sep 17 00:00:00 2001 From: Jess <20195932+wrongkindofdoctor@users.noreply.github.com> Date: Wed, 20 Oct 2021 09:37:48 -0400 Subject: [PATCH 16/17] return space to mdtf_synthetic.py --- scripts/mdtf_synthetic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/mdtf_synthetic.py b/scripts/mdtf_synthetic.py index be2ceb6..ee1fa70 100755 --- a/scripts/mdtf_synthetic.py +++ b/scripts/mdtf_synthetic.py @@ -10,6 +10,7 @@ MDTF_PACKAGE_PATH = mdtf_test_data.__path__[0] + def read_yaml(file_name): """A function to read YAML files""" config = EnvYAML(file_name) From 63676f367f1e8e51657f7d8809041f5d35b085f4 Mon Sep 17 00:00:00 2001 From: John Krasting Date: Thu, 21 Oct 2021 10:26:15 -0400 Subject: [PATCH 17/17] Bumped version number to v1.0.4rc1 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 195c291..d8e5a51 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = mdtf_test_data -version = 1.0.3.post2 +version = 1.0.4.rc1 description = Tools for working with MDTF Diagnostics test data sets url = https://github.com/jkrasting/mdtf_test_data author = MDTF Development Team