roms-tools 0.1.0__py3-none-any.whl → 0.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
roms_tools/setup/grid.py CHANGED
@@ -1,12 +1,16 @@
1
1
  import copy
2
- from dataclasses import dataclass, field
2
+ from dataclasses import dataclass, field, asdict
3
3
 
4
4
  import numpy as np
5
5
  import xarray as xr
6
+ import yaml
7
+ import importlib.metadata
6
8
 
7
-
8
- from roms_tools.setup.topography import _add_topography_and_mask
9
+ from roms_tools.setup.topography import _add_topography_and_mask, _add_velocity_masks
9
10
  from roms_tools.setup.plot import _plot
11
+ from roms_tools.setup.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
12
+
13
+ import warnings
10
14
 
11
15
  RADIUS_OF_EARTH = 6371315.0 # in m
12
16
 
@@ -41,7 +45,7 @@ class Grid:
41
45
  The default is 0, which means that the x-direction of the grid is aligned with lines of constant latitude.
42
46
  topography_source : str, optional
43
47
  Specifies the data source to use for the topography. Options are
44
- "etopo5". The default is "etopo5".
48
+ "ETOPO5". The default is "ETOPO5".
45
49
  smooth_factor : float, optional
46
50
  The smoothing factor used in the domain-wide Gaussian smoothing of the
47
51
  topography. Smaller values result in less smoothing, while larger
@@ -97,7 +101,7 @@ class Grid:
97
101
  center_lon: float
98
102
  center_lat: float
99
103
  rot: float = 0
100
- topography_source: str = "etopo5"
104
+ topography_source: str = "ETOPO5"
101
105
  smooth_factor: int = 8
102
106
  hmin: float = 5.0
103
107
  rmax: float = 0.2
@@ -130,7 +134,7 @@ class Grid:
130
134
  self._straddle()
131
135
 
132
136
  def add_topography_and_mask(
133
- self, topography_source="etopo5", smooth_factor=8, hmin=5.0, rmax=0.2
137
+ self, topography_source="ETOPO5", smooth_factor=8, hmin=5.0, rmax=0.2
134
138
  ) -> None:
135
139
  """
136
140
  Add topography and mask to the grid dataset.
@@ -144,7 +148,7 @@ class Grid:
144
148
  ----------
145
149
  topography_source : str, optional
146
150
  Specifies the data source to use for the topography. Options are
147
- "etopo5". The default is "etopo5".
151
+ "ETOPO5". The default is "ETOPO5".
148
152
  smooth_factor : float, optional
149
153
  The smoothing factor used in the domain-wide Gaussian smoothing of the
150
154
  topography. Smaller values result in less smoothing, while larger
@@ -204,6 +208,37 @@ class Grid:
204
208
  """
205
209
  self.ds.to_netcdf(filepath)
206
210
 
211
+ def to_yaml(self, filepath: str) -> None:
212
+ """
213
+ Export the parameters of the class to a YAML file, including the version of roms-tools.
214
+
215
+ Parameters
216
+ ----------
217
+ filepath : str
218
+ The path to the YAML file where the parameters will be saved.
219
+ """
220
+ data = asdict(self)
221
+ data.pop("ds", None)
222
+ data.pop("straddle", None)
223
+
224
+ # Include the version of roms-tools
225
+ try:
226
+ roms_tools_version = importlib.metadata.version("roms-tools")
227
+ except importlib.metadata.PackageNotFoundError:
228
+ roms_tools_version = "unknown"
229
+
230
+ # Create header
231
+ header = f"---\nroms_tools_version: {roms_tools_version}\n---\n"
232
+
233
+ # Use the class name as the top-level key
234
+ yaml_data = {self.__class__.__name__: data}
235
+
236
+ with open(filepath, "w") as file:
237
+ # Write header
238
+ file.write(header)
239
+ # Write YAML data
240
+ yaml.dump(yaml_data, file, default_flow_style=False)
241
+
207
242
  @classmethod
208
243
  def from_file(cls, filepath: str) -> "Grid":
209
244
  """
@@ -222,6 +257,11 @@ class Grid:
222
257
  # Load the dataset from the file
223
258
  ds = xr.open_dataset(filepath)
224
259
 
260
+ if not all(mask in ds for mask in ["mask_u", "mask_v"]):
261
+ ds = _add_velocity_masks(ds)
262
+ if not all(coord in ds for coord in ["lat_u", "lon_u", "lat_v", "lon_v"]):
263
+ ds = _add_lat_lon_at_velocity_points(ds)
264
+
225
265
  # Create a new Grid instance without calling __init__ and __post_init__
226
266
  grid = cls.__new__(cls)
227
267
 
@@ -251,6 +291,62 @@ class Grid:
251
291
 
252
292
  return grid
253
293
 
294
+ @classmethod
295
+ def from_yaml(cls, filepath: str) -> "Grid":
296
+ """
297
+ Create an instance of the class from a YAML file.
298
+
299
+ Parameters
300
+ ----------
301
+ filepath : str
302
+ The path to the YAML file from which the parameters will be read.
303
+
304
+ Returns
305
+ -------
306
+ Grid
307
+ An instance of the Grid class.
308
+ """
309
+ # Read the entire file content
310
+ with open(filepath, "r") as file:
311
+ file_content = file.read()
312
+
313
+ # Split the content into YAML documents
314
+ documents = list(yaml.safe_load_all(file_content))
315
+
316
+ header_data = None
317
+ grid_data = None
318
+
319
+ # Iterate over documents to find the header and grid configuration
320
+ for doc in documents:
321
+ if doc is None:
322
+ continue
323
+ if "roms_tools_version" in doc:
324
+ header_data = doc
325
+ elif "Grid" in doc:
326
+ grid_data = doc["Grid"]
327
+
328
+ if header_data is None:
329
+ raise ValueError("Version of ROMS-Tools not found in the YAML file.")
330
+ else:
331
+ # Check the roms_tools_version
332
+ roms_tools_version_header = header_data.get("roms_tools_version")
333
+ # Get current version of roms-tools
334
+ try:
335
+ roms_tools_version_current = importlib.metadata.version("roms-tools")
336
+ except importlib.metadata.PackageNotFoundError:
337
+ roms_tools_version_current = "unknown"
338
+
339
+ if roms_tools_version_header != roms_tools_version_current:
340
+ warnings.warn(
341
+ f"Current roms-tools version ({roms_tools_version_current}) does not match the version in the YAML header ({roms_tools_version_header}).",
342
+ UserWarning,
343
+ )
344
+
345
+ if grid_data is None:
346
+ raise ValueError("No Grid configuration found in the YAML file.")
347
+
348
+ return cls(**grid_data)
349
+
254
350
  # override __repr__ method to only print attributes that are actually set
255
351
  def __repr__(self) -> str:
256
352
  cls = self.__class__
@@ -304,6 +400,7 @@ class Grid:
304
400
 
305
401
  if bathymetry:
306
402
  kwargs = {"cmap": "YlGnBu"}
403
+
307
404
  _plot(
308
405
  self.ds,
309
406
  field=self.ds.h.where(self.ds.mask_rho),
@@ -719,6 +816,18 @@ def _create_grid_ds(
719
816
  },
720
817
  )
721
818
 
819
+ ds["tra_lon"] = center_lon
820
+ ds["tra_lon"].attrs["long_name"] = "Longitudinal translation of base grid"
821
+ ds["tra_lon"].attrs["units"] = "degrees East"
822
+
823
+ ds["tra_lat"] = center_lat
824
+ ds["tra_lat"].attrs["long_name"] = "Latitudinal translation of base grid"
825
+ ds["tra_lat"].attrs["units"] = "degrees North"
826
+
827
+ ds["rotate"] = rot
828
+ ds["rotate"].attrs["long_name"] = "Rotation of base grid"
829
+ ds["rotate"].attrs["units"] = "degrees"
830
+
722
831
  ds["lon_rho"] = xr.Variable(
723
832
  data=lon * 180 / np.pi,
724
833
  dims=["eta_rho", "xi_rho"],
@@ -731,23 +840,21 @@ def _create_grid_ds(
731
840
  attrs={"long_name": "latitude of rho-points", "units": "degrees North"},
732
841
  )
733
842
 
734
- ds["tra_lon"] = center_lon
735
- ds["tra_lon"].attrs["long_name"] = "Longitudinal translation of base grid"
736
- ds["tra_lon"].attrs["units"] = "degrees East"
737
-
738
- ds["tra_lat"] = center_lat
739
- ds["tra_lat"].attrs["long_name"] = "Latitudinal translation of base grid"
740
- ds["tra_lat"].attrs["units"] = "degrees North"
741
-
742
- ds["rotate"] = rot
743
- ds["rotate"].attrs["long_name"] = "Rotation of base grid"
744
- ds["rotate"].attrs["units"] = "degrees"
843
+ ds = _add_lat_lon_at_velocity_points(ds)
745
844
 
746
845
  return ds
747
846
 
748
847
 
749
848
  def _add_global_metadata(ds, size_x, size_y):
750
- ds.attrs["Type"] = "ROMS grid produced by roms-tools"
849
+ ds.attrs["title"] = "ROMS grid created by ROMS-Tools"
850
+
851
+ # Include the version of roms-tools
852
+ try:
853
+ roms_tools_version = importlib.metadata.version("roms-tools")
854
+ except importlib.metadata.PackageNotFoundError:
855
+ roms_tools_version = "unknown"
856
+
857
+ ds.attrs["roms_tools_version"] = roms_tools_version
751
858
  ds.attrs["size_x"] = size_x
752
859
  ds.attrs["size_y"] = size_y
753
860
 
@@ -807,3 +914,22 @@ def _f2c_xdir(f):
807
914
  fc[-1, :] = f[-1, :] + 0.5 * (f[-1, :] - f[-2, :])
808
915
 
809
916
  return fc
917
+
918
+
919
+ def _add_lat_lon_at_velocity_points(ds):
920
+
921
+ lat_u = interpolate_from_rho_to_u(ds["lat_rho"])
922
+ lon_u = interpolate_from_rho_to_u(ds["lon_rho"])
923
+ lat_v = interpolate_from_rho_to_v(ds["lat_rho"])
924
+ lon_v = interpolate_from_rho_to_v(ds["lon_rho"])
925
+
926
+ lat_u.attrs = {"long_name": "latitude of u-points", "units": "degrees North"}
927
+ lon_u.attrs = {"long_name": "longitude of u-points", "units": "degrees East"}
928
+ lat_v.attrs = {"long_name": "latitude of v-points", "units": "degrees North"}
929
+ lon_v.attrs = {"long_name": "longitude of v-points", "units": "degrees East"}
930
+
931
+ ds = ds.assign_coords(
932
+ {"lat_u": lat_u, "lon_u": lon_u, "lat_v": lat_v, "lon_v": lon_v}
933
+ )
934
+
935
+ return ds