From 7861c18d685a256479d0088daa29eef592abfe30 Mon Sep 17 00:00:00 2001 From: alperaltuntas Date: Sun, 29 Dec 2024 09:42:51 -0700 Subject: [PATCH 1/4] minor changes ahead of visualCaseGen integration in CrocoDash: - correct the first arg of a classmethod (from self to cls). - introduce optional write_to_file arg in setup_bathymetry. - introduce optional bathymetry obj arg in tidy_bathymetry. --- regional_mom6/regional_mom6.py | 61 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/regional_mom6/regional_mom6.py b/regional_mom6/regional_mom6.py index cc843959..7ac5b846 100644 --- a/regional_mom6/regional_mom6.py +++ b/regional_mom6/regional_mom6.py @@ -575,7 +575,7 @@ class experiment: @classmethod def create_empty( - self, + cls, longitude_extent=None, latitude_extent=None, date_range=None, @@ -596,7 +596,7 @@ def create_empty( """ Substitute init method to creates an empty expirement object, with the opportunity to override whatever values wanted. """ - expt = self( + expt = cls( longitude_extent=None, latitude_extent=None, date_range=None, @@ -632,8 +632,8 @@ def create_empty( expt.longitude_extent = longitude_extent expt.ocean_mask = None expt.layout = None - self.segments = {} - self.boundaries = boundaries + cls.segments = {} + cls.boundaries = boundaries return expt def __init__( @@ -1760,6 +1760,7 @@ def setup_bathymetry( vertical_coordinate_name="elevation", # This is to match GEBCO fill_channels=False, positive_down=False, + write_to_file=True, ): """ Cut out and interpolate the chosen bathymetry and then fill inland lakes. @@ -1783,6 +1784,7 @@ def setup_bathymetry( but can also connect extra islands to land. Default: ``False``. positive_down (Optional[bool]): If ``True``, it assumes that bathymetry vertical coordinate is positive down. Default: ``False``. + write_to_file (Optional[bool]): Whether to write the bathymetry to a file. Default: ``True``. """ ## Convert the provided coordinate names into a dictionary mapping to the @@ -1856,9 +1858,10 @@ def setup_bathymetry( ) bathymetry_output.depth.attrs["long_name"] = "Elevation relative to sea level" bathymetry_output.depth.attrs["coordinates"] = "lon lat" - bathymetry_output.to_netcdf( - self.mom_input_dir / "bathymetry_original.nc", mode="w", engine="netcdf4" - ) + if write_to_file: + bathymetry_output.to_netcdf( + self.mom_input_dir / "bathymetry_original.nc", mode="w", engine="netcdf4" + ) tgrid = xr.Dataset( data_vars={ @@ -1894,10 +1897,11 @@ def setup_bathymetry( tgrid.lat.attrs["_FillValue"] = 1e20 tgrid.depth.attrs["units"] = "meters" tgrid.depth.attrs["coordinates"] = "lon lat" - tgrid.to_netcdf( - self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" - ) - tgrid.close() + if write_to_file: + tgrid.to_netcdf( + self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" + ) + tgrid.close() bathymetry_output = bathymetry_output.load() @@ -1914,19 +1918,21 @@ def setup_bathymetry( ) regridder = xe.Regridder(bathymetry_output, tgrid, "bilinear", parallel=False) bathymetry = regridder(bathymetry_output) - bathymetry.to_netcdf( - self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" - ) + if write_to_file: + bathymetry.to_netcdf( + self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" + ) print( "Regridding successful! Now calling `tidy_bathymetry` method for some finishing touches..." ) - self.tidy_bathymetry(fill_channels, positive_down) + self.tidy_bathymetry(fill_channels, positive_down, bathymetry=bathymetry) print("setup bathymetry has finished successfully.") - return + + return bathymetry def tidy_bathymetry( - self, fill_channels=False, positive_down=False, vertical_coordinate_name="depth" + self, fill_channels=False, positive_down=False, vertical_coordinate_name="depth", bathymetry=None ): """ An auxiliary function for bathymetry used to fix up the metadata and remove inland @@ -1944,6 +1950,9 @@ def tidy_bathymetry( but can also connect extra islands to land. Default: ``False``. positive_down (Optional[bool]): If ``False`` (default), assume that bathymetry vertical coordinate is positive down, as is the case in GEBCO for example. + bathymetry (Optional[xr.Dataset]): The bathymetry dataset to tidy up. If not provided, + it will read the bathymetry from the file ``bathymetry_unfinished.nc`` in the input directory + that was created by :func:`~setup_bathymetry`. """ ## reopen bathymetry to modify @@ -1951,9 +1960,10 @@ def tidy_bathymetry( "Tidy bathymetry: Reading in regridded bathymetry to fix up metadata...", end="", ) - bathymetry = xr.open_dataset( - self.mom_input_dir / "bathymetry_unfinished.nc", engine="netcdf4" - ) + if read_bathy_from_file := bathymetry is None: + bathymetry = xr.open_dataset( + self.mom_input_dir / "bathymetry_unfinished.nc", engine="netcdf4" + ) ## Ensure correct encoding bathymetry = xr.Dataset( @@ -2115,11 +2125,12 @@ def tidy_bathymetry( ~(bathymetry.depth <= self.minimum_depth), self.minimum_depth + 0.1 ) - bathymetry.expand_dims({"ntiles": 1}).to_netcdf( - self.mom_input_dir / "bathymetry.nc", - mode="w", - encoding={"depth": {"_FillValue": None}}, - ) + if read_bathy_from_file: + bathymetry.expand_dims({"ntiles": 1}).to_netcdf( + self.mom_input_dir / "bathymetry.nc", + mode="w", + encoding={"depth": {"_FillValue": None}}, + ) print("done.") return From 8f59ebb05345b3cb2f8215d9a852f761208fd839 Mon Sep 17 00:00:00 2001 From: alperaltuntas Date: Tue, 31 Dec 2024 13:43:25 -0700 Subject: [PATCH 2/4] Add aiohttp and copernicusmarine dependencies --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e5eb7a54..ed909cc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,8 @@ dependencies = [ "xarray <= 2024.7.0", "xesmf >= 0.8.4", "f90nml >= 1.4.1", + "aiohttp >= 3.9.5,<3.10.0", + "copernicusmarine >= 1.2.4,<1.3.0" ] [build-system] From bbda1b99f3e9e013f4465344856732b441436821 Mon Sep 17 00:00:00 2001 From: alperaltuntas Date: Tue, 31 Dec 2024 13:46:37 -0700 Subject: [PATCH 3/4] add hgrid_path and vgrid_path args to __init__ to allow non-standard filenames. Similarly, add thickesses arg to _make_vgrid. --- regional_mom6/regional_mom6.py | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/regional_mom6/regional_mom6.py b/regional_mom6/regional_mom6.py index 7ac5b846..96121eb7 100644 --- a/regional_mom6/regional_mom6.py +++ b/regional_mom6/regional_mom6.py @@ -650,7 +650,9 @@ def __init__( longitude_extent=None, latitude_extent=None, hgrid_type="even_spacing", + hgrid_path=None, vgrid_type="hyperbolic_tangent", + vgrid_path=None, repeat_year_forcing=False, minimum_depth=4, tidal_constituents=["M2"], @@ -673,7 +675,7 @@ def __init__( self.mom_run_dir = Path(mom_run_dir) self.mom_input_dir = Path(mom_input_dir) - self.toolpath_dir = Path(toolpath_dir) + self.toolpath_dir = Path(toolpath_dir) if toolpath_dir is not None else None self.mom_run_dir.mkdir(exist_ok=True) self.mom_input_dir.mkdir(exist_ok=True) @@ -695,8 +697,12 @@ def __init__( self.tidal_constituents = tidal_constituents if hgrid_type == "from_file": + if hgrid_path is None: + hgrid_path = self.mom_input_dir / "hgrid.nc" + else: + hgrid_path = Path(hgrid_path) try: - self.hgrid = xr.open_dataset(self.mom_input_dir / "hgrid.nc") + self.hgrid = xr.open_dataset(hgrid_path) self.longitude_extent = ( float(self.hgrid.x.min()), float(self.hgrid.x.max()), @@ -712,13 +718,20 @@ def __init__( ) raise ValueError else: + if hgrid_path: + raise ValueError("hgrid_path can only be set if hgrid_type is 'from_file'.") self.longitude_extent = tuple(longitude_extent) self.latitude_extent = tuple(latitude_extent) self.hgrid = self._make_hgrid() if vgrid_type == "from_file": + if vgrid_path is None: + vgrid_path = self.mom_input_dir / "vgrid.nc" + else: + vgrid_path = Path(vgrid_path) + try: - self.vgrid = xr.open_dataset(self.mom_input_dir / "vcoord.nc") + vgrid_from_file = xr.open_dataset(vgrid_path) except: print( @@ -726,7 +739,10 @@ def __init__( + f"Make sure `vcoord.nc`exists in {self.mom_input_dir} directory." ) raise ValueError + self.vgrid = self._make_vgrid(vgrid_from_file.dz.data) else: + if vgrid_path: + raise ValueError("vgrid_path can only be set if vgrid_type is 'from_file'.") self.vgrid = self._make_vgrid() self.segments = {} @@ -906,7 +922,7 @@ def _make_hgrid(self): return hgrid - def _make_vgrid(self): + def _make_vgrid(self, thicknesses=None): """ Generates a vertical grid based on the ``number_vertical_layers``, the ratio of largest to smallest layer thickness (``layer_thickness_ratio``) and the @@ -914,9 +930,10 @@ def _make_vgrid(self): (All these parameters are specified at the class level.) """ - thicknesses = hyperbolictan_thickness_profile( - self.number_vertical_layers, self.layer_thickness_ratio, self.depth - ) + if thicknesses is None: + thicknesses = hyperbolictan_thickness_profile( + self.number_vertical_layers, self.layer_thickness_ratio, self.depth + ) zi = np.cumsum(thicknesses) zi = np.insert(zi, 0, 0.0) # add zi = 0.0 as first interface @@ -1531,7 +1548,15 @@ def get_glorys_rectangular(self, raw_boundaries_path): ) print( - f"script `get_glorys_data.sh` has been created at {raw_boundaries_path}.\n Run this script via bash to download the data from a terminal with internet access. \nYou will need to enter your Copernicus Marine username and password.\nIf you don't have an account, make one here:\nhttps://data.marine.copernicus.eu/register" + f"The script `get_glorys_data.sh` has been generated at:\n {raw_boundaries_path}.\n" + f"To download the data, run this script using `bash` in a terminal with internet access.\n\n" + f"Important instructions:\n" + f"1. You will need your Copernicus Marine username and password.\n" + f" If you do not have an account, you can create one here: \n" + f" https://data.marine.copernicus.eu/register\n" + f"2. You will be prompted to enter your Copernicus Marine credentials multiple times: once for each dataset.\n" + f"3. Depending on the dataset size, the download process may take significant time and resources.\n" + f"4. Thus, on certain systems, you may need to run this script as a batch job.\n" ) return From 9665892ad88f081265ff87855adaa65f486fe3db Mon Sep 17 00:00:00 2001 From: alperaltuntas Date: Mon, 6 Jan 2025 14:39:59 -0700 Subject: [PATCH 4/4] black reformatting --- regional_mom6/regional_mom6.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/regional_mom6/regional_mom6.py b/regional_mom6/regional_mom6.py index 96121eb7..80986c43 100644 --- a/regional_mom6/regional_mom6.py +++ b/regional_mom6/regional_mom6.py @@ -719,7 +719,9 @@ def __init__( raise ValueError else: if hgrid_path: - raise ValueError("hgrid_path can only be set if hgrid_type is 'from_file'.") + raise ValueError( + "hgrid_path can only be set if hgrid_type is 'from_file'." + ) self.longitude_extent = tuple(longitude_extent) self.latitude_extent = tuple(latitude_extent) self.hgrid = self._make_hgrid() @@ -742,7 +744,9 @@ def __init__( self.vgrid = self._make_vgrid(vgrid_from_file.dz.data) else: if vgrid_path: - raise ValueError("vgrid_path can only be set if vgrid_type is 'from_file'.") + raise ValueError( + "vgrid_path can only be set if vgrid_type is 'from_file'." + ) self.vgrid = self._make_vgrid() self.segments = {} @@ -1885,7 +1889,9 @@ def setup_bathymetry( bathymetry_output.depth.attrs["coordinates"] = "lon lat" if write_to_file: bathymetry_output.to_netcdf( - self.mom_input_dir / "bathymetry_original.nc", mode="w", engine="netcdf4" + self.mom_input_dir / "bathymetry_original.nc", + mode="w", + engine="netcdf4", ) tgrid = xr.Dataset( @@ -1924,7 +1930,9 @@ def setup_bathymetry( tgrid.depth.attrs["coordinates"] = "lon lat" if write_to_file: tgrid.to_netcdf( - self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" + self.mom_input_dir / "bathymetry_unfinished.nc", + mode="w", + engine="netcdf4", ) tgrid.close() @@ -1945,7 +1953,9 @@ def setup_bathymetry( bathymetry = regridder(bathymetry_output) if write_to_file: bathymetry.to_netcdf( - self.mom_input_dir / "bathymetry_unfinished.nc", mode="w", engine="netcdf4" + self.mom_input_dir / "bathymetry_unfinished.nc", + mode="w", + engine="netcdf4", ) print( "Regridding successful! Now calling `tidy_bathymetry` method for some finishing touches..." @@ -1957,7 +1967,11 @@ def setup_bathymetry( return bathymetry def tidy_bathymetry( - self, fill_channels=False, positive_down=False, vertical_coordinate_name="depth", bathymetry=None + self, + fill_channels=False, + positive_down=False, + vertical_coordinate_name="depth", + bathymetry=None, ): """ An auxiliary function for bathymetry used to fix up the metadata and remove inland