-
Notifications
You must be signed in to change notification settings - Fork 340
Enable using instantaneous outputs for GDD generation #3445
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
base: b4b-dev
Are you sure you want to change the base?
Changes from all commits
821c6d3
820f674
f71e27c
b5b9bdd
315e65e
9a9ac56
873587a
4f4cc6b
6d6c741
0c47024
f1bbda2
d620a16
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
|
|
||
| hist_avgflag_pertape(3) = 'A' | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |
| import warnings | ||
| import os | ||
| import glob | ||
| from importlib import util as importlib_util | ||
| import numpy as np | ||
| import xarray as xr | ||
|
|
||
|
|
@@ -256,9 +255,11 @@ def import_and_process_1yr( | |
| log(logger, f"netCDF year {this_year}...") | ||
|
|
||
| # Without dask, this can take a LONG time at resolutions finer than 2-deg | ||
| if importlib_util.find_spec("dask"): | ||
| if not utils.DASK_UNAVAILABLE: | ||
| log(logger, "dask available") | ||
| chunks = {"time": 1} | ||
| else: | ||
| log(logger, "dask NOT available") | ||
| chunks = None | ||
|
|
||
| # Get h1 file (list) | ||
|
|
@@ -550,6 +551,10 @@ def import_and_process_1yr( | |
| clm_gdd_var = "GDDACCUM" | ||
| my_vars = [clm_gdd_var, "GDDHARV"] | ||
| patterns = [f"*h2i.{this_year-1}-01*.nc", f"*h2i.{this_year-1}-01*.nc.base"] | ||
|
|
||
| # TODO: Undo this, replacing with just h2i or h2a | ||
| patterns += [f"*h2a.{this_year-1}-01*.nc", f"*h2a.{this_year-1}-01*.nc.base"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Look for h2i files first, falling back to h2a files if (a) h2i files not found or (b) variable not in h2i files. |
||
|
|
||
| for pat in patterns: | ||
| pattern = os.path.join(indir, pat) | ||
| h2_files = glob.glob(pattern) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| """Unit tests for grid_one_variable.py""" | ||
|
|
||
| import unittest | ||
| import numpy as np | ||
| import xarray as xr | ||
|
|
||
| from ctsm import unit_testing | ||
| from ctsm.crop_calendars import grid_one_variable as g1v | ||
|
|
||
| # Allow names that pylint doesn't like, because otherwise I find it hard | ||
| # to make readable unit test names | ||
| # pylint: disable=invalid-name | ||
|
|
||
|
|
||
| class TestGridOneVariable(unittest.TestCase): | ||
| """Tests of grid_one_variable""" | ||
|
|
||
| def setUp(self): | ||
| self.lat_da = xr.DataArray( | ||
| data=[-45.0, 45.0], | ||
| dims=["lat"], | ||
| ) | ||
| self.lon_da = xr.DataArray( | ||
| data=[-145.0, 145.0], | ||
| dims=["lon"], | ||
| ) | ||
| self.result = None | ||
| self.expected = None | ||
|
|
||
| def _compare_arrays(self): | ||
| print(f"Result:\n{self.result}") | ||
| print(f"Expected:\n{self.expected}") | ||
| self.assertTrue(np.array_equal(self.result, self.expected, equal_nan=True)) | ||
|
|
||
| def test_create_filled_array_fillnan(self): | ||
| """Test create_filled_array() with NaN fill_value""" | ||
| this_da = xr.DataArray( | ||
| data=np.array([[1, 2], [3, 4]]), | ||
| dims=["lat", "lon"], | ||
| coords={"lat": self.lat_da, "lon": self.lon_da}, | ||
| ) | ||
| dummy_ds = xr.Dataset() | ||
|
|
||
| fill_value = np.nan | ||
| self.result = g1v.create_filled_array(dummy_ds, fill_value, this_da, ["lat"]) | ||
|
|
||
| self.expected = np.full_like(self.lat_da, fill_value) | ||
| self._compare_arrays() | ||
|
|
||
| def test_create_filled_array_fill6(self): | ||
| """Test create_filled_array() with fill_value = 6.0""" | ||
| this_da = xr.DataArray( | ||
| data=np.array([[1, 2], [3, 4]]), | ||
| dims=["lat", "lon"], | ||
| coords={"lat": self.lat_da, "lon": self.lon_da}, | ||
| ) | ||
| dummy_ds = xr.Dataset() | ||
|
|
||
| fill_value = 6.0 | ||
| self.result = g1v.create_filled_array(dummy_ds, fill_value, this_da, ["lat"]) | ||
|
|
||
| self.expected = np.full_like(self.lat_da, fill_value) | ||
| self._compare_arrays() | ||
|
|
||
| def test_create_filled_array_fill6_ivtstr(self): | ||
| """Test create_filled_array() with fill_value = 6.0 and ivt_str in new_dims""" | ||
| this_da = xr.DataArray( | ||
| data=np.array([[1, 2], [3, 4]]), | ||
| dims=["lat", "lon"], | ||
| coords={"lat": self.lat_da, "lon": self.lon_da}, | ||
| ) | ||
|
|
||
| ivt_da = xr.DataArray( | ||
| data=[14, 15, 16], | ||
| dims=["ivt"], | ||
| ) | ||
| this_ds = xr.Dataset(coords={"ivt": ivt_da}) | ||
|
|
||
| fill_value = 6.0 | ||
| self.result = g1v.create_filled_array(this_ds, fill_value, this_da, ["ivt_str"]) | ||
|
|
||
| self.expected = np.full_like(ivt_da, fill_value) | ||
| self._compare_arrays() | ||
|
|
||
| def test_create_filled_array_fill6_extradim(self): | ||
| """ | ||
| Test create_filled_array() with fill_value = -7.0 and some non-ivt_str in new_dims that is | ||
| also not in this_da.coords | ||
| """ | ||
| this_da = xr.DataArray( | ||
| data=np.array([[1, 2], [3, 4]]), | ||
| dims=["lat", "lon"], | ||
| coords={"lat": self.lat_da, "lon": self.lon_da}, | ||
| ) | ||
|
|
||
| extradim_name = "extra_dim" | ||
| extradim_da = xr.DataArray( | ||
| data=[14, 15, 16], | ||
| dims=[extradim_name], | ||
| ) | ||
| this_ds = xr.Dataset(coords={extradim_name: extradim_da}) | ||
|
|
||
| fill_value = -7.0 | ||
| self.result = g1v.create_filled_array(this_ds, fill_value, this_da, [extradim_name]) | ||
|
|
||
| self.expected = np.full_like(extradim_da, fill_value) | ||
| self._compare_arrays() | ||
|
|
||
| def test_create_filled_array_fillfalse(self): | ||
| """Test create_filled_array() with false(y) fill_value""" | ||
| this_da = xr.DataArray( | ||
| data=np.array([[1, 2], [3, 4]]), | ||
| dims=["lat", "lon"], | ||
| coords={"lat": self.lat_da, "lon": self.lon_da}, | ||
| ) | ||
| dummy_ds = xr.Dataset() | ||
|
|
||
| fill_value = False | ||
| self.result = g1v.create_filled_array(dummy_ds, fill_value, this_da, ["lat"]) | ||
|
|
||
| self.expected = np.full_like(self.lat_da, np.nan) | ||
| self._compare_arrays() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unit_testing.setup_for_tests() | ||
| unittest.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,14 @@ | |
|
|
||
| import logging | ||
| import os | ||
| import stat | ||
| import sys | ||
| import glob | ||
| import string | ||
| import re | ||
| import pdb | ||
| import tempfile | ||
| import subprocess | ||
|
|
||
| from datetime import date, timedelta, datetime | ||
| from getpass import getuser | ||
|
|
@@ -191,6 +194,7 @@ def write_output(file, file_in, file_out, file_type): | |
| """ | ||
|
|
||
| # update attributes | ||
| logger.info("Updating metadata: %s", file_out) | ||
| title = "Modified " + file_type + " file" | ||
| summary = "Modified " + file_type + " file" | ||
| contact = "N/A" | ||
|
|
@@ -205,11 +209,51 @@ def write_output(file, file_in, file_out, file_type): | |
| description=description, | ||
| ) | ||
|
|
||
| logger.info("Writing: %s", file_out) | ||
| # mode 'w' overwrites file if it exists | ||
| file.to_netcdf(path=file_out, mode="w", format="NETCDF3_64BIT") | ||
| file.to_netcdf(path=file_out, mode="w", format="NETCDF4_CLASSIC") | ||
| logger.info("Successfully created: %s", file_out) | ||
| file.close() | ||
|
|
||
| logger.info("Trying to convert to NETCDF3_CLASSIC: %s", file_out) | ||
| if convert_netcdf_to_classic(file_out): | ||
| logger.info("Conversion succeeded") | ||
| else: | ||
| logger.info("Conversion failed, perhaps because nccopy wasn't found in your shell") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @samsrabin I'm just wondering the reasons behind the convert to classic being done here? I don't know of any compelling reasons to prefer classic over other formats. And classic has some pretty strong restrictions. And it seems preferable to me to write it out in the preferred format from the get go rather than convert. But I would like to hear the thinking here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think I meant for this all to be netCDF-3 64-bit. Will fix. |
||
|
|
||
|
|
||
| def convert_netcdf_to_classic(filepath): | ||
| """ | ||
| Try to convert netCDF to netCDF-3 Classic format using nccopy. | ||
| """ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| # Get temporary file path | ||
| dirpath = os.path.dirname(filepath) | ||
| with tempfile.NamedTemporaryFile(delete=False, dir=dirpath, suffix=".nc") as tmp: | ||
| temp_path = tmp.name | ||
|
|
||
| try: | ||
| subprocess.run(["nccopy", "-3", filepath, temp_path], check=True) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
| copy_permissions(filepath, temp_path) # nccopy doesn't preserve permissions | ||
| os.replace(temp_path, filepath) | ||
| return True | ||
| except subprocess.CalledProcessError: | ||
| return False | ||
|
|
||
|
|
||
| def copy_permissions(src_file, dst_file): | ||
| """ | ||
| Copy file permissions from src to dest | ||
| """ | ||
| # Get the mode (permissions) of the source file | ||
| src_mode = os.stat(src_file).st_mode | ||
|
|
||
| # Extract the permission bits | ||
| permissions = stat.S_IMODE(src_mode) | ||
|
|
||
| # Apply them to the destination file | ||
| os.chmod(dst_file, permissions) | ||
|
|
||
|
|
||
| def get_isosplit(iso_string, split): | ||
| """ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment explaining this.