roms-tools 1.7.0__py3-none-any.whl → 2.1.0__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.
Files changed (124) hide show
  1. roms_tools/__init__.py +2 -1
  2. roms_tools/setup/boundary_forcing.py +246 -146
  3. roms_tools/setup/datasets.py +229 -69
  4. roms_tools/setup/download.py +13 -17
  5. roms_tools/setup/grid.py +777 -614
  6. roms_tools/setup/initial_conditions.py +168 -32
  7. roms_tools/setup/mask.py +115 -0
  8. roms_tools/setup/nesting.py +575 -0
  9. roms_tools/setup/plot.py +218 -63
  10. roms_tools/setup/regrid.py +4 -2
  11. roms_tools/setup/river_forcing.py +125 -29
  12. roms_tools/setup/surface_forcing.py +31 -25
  13. roms_tools/setup/tides.py +29 -14
  14. roms_tools/setup/topography.py +250 -153
  15. roms_tools/setup/utils.py +174 -44
  16. roms_tools/setup/vertical_coordinate.py +5 -16
  17. roms_tools/tests/test_setup/test_boundary_forcing.py +10 -5
  18. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +0 -1
  19. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +56 -201
  20. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_r/.zattrs +1 -1
  21. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_w/.zattrs +1 -1
  22. roms_tools/tests/test_setup/test_data/grid.zarr/{layer_depth_rho → sigma_r}/.zarray +2 -6
  23. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/.zattrs +7 -0
  24. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/0 +0 -0
  25. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zarray +20 -0
  26. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zattrs +7 -0
  27. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/0 +0 -0
  28. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -2
  29. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +58 -203
  30. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_r/.zattrs +1 -1
  31. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_w/.zattrs +1 -1
  32. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/.zattrs +1 -1
  33. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zarray +20 -0
  39. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zattrs +7 -0
  40. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/0 +0 -0
  41. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zarray +20 -0
  42. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zattrs +7 -0
  43. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/0 +0 -0
  44. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +2 -3
  45. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/.zattrs +1 -2
  46. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/.zarray +1 -1
  47. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zmetadata +5 -6
  49. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zarray +2 -2
  50. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zattrs +1 -2
  51. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_tracer/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zarray +2 -2
  53. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/tracer_name/0 +0 -0
  54. roms_tools/tests/test_setup/test_datasets.py +2 -2
  55. roms_tools/tests/test_setup/test_grid.py +110 -12
  56. roms_tools/tests/test_setup/test_initial_conditions.py +2 -1
  57. roms_tools/tests/test_setup/test_nesting.py +489 -0
  58. roms_tools/tests/test_setup/test_river_forcing.py +53 -15
  59. roms_tools/tests/test_setup/test_surface_forcing.py +3 -22
  60. roms_tools/tests/test_setup/test_tides.py +2 -1
  61. roms_tools/tests/test_setup/test_topography.py +106 -1
  62. roms_tools/tests/test_setup/test_validation.py +2 -2
  63. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/LICENSE +1 -1
  64. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/METADATA +9 -4
  65. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/RECORD +85 -108
  66. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/WHEEL +1 -1
  67. roms_tools/_version.py +0 -2
  68. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zarray +0 -24
  69. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zattrs +0 -9
  70. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zarray +0 -24
  72. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zattrs +0 -9
  73. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zarray +0 -24
  75. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zattrs +0 -9
  76. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/.zattrs +0 -9
  78. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zarray +0 -24
  80. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zattrs +0 -9
  81. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zarray +0 -24
  83. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zattrs +0 -9
  84. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zarray +0 -24
  86. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zattrs +0 -9
  87. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zarray +0 -24
  89. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zattrs +0 -9
  90. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zarray +0 -24
  92. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zattrs +0 -9
  93. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zarray +0 -24
  95. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zattrs +0 -9
  96. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zarray +0 -24
  98. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zattrs +0 -9
  99. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zarray +0 -24
  101. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zattrs +0 -9
  102. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/0 +0 -0
  105. roms_tools/tests/test_setup/test_vertical_coordinate.py +0 -91
  106. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zattrs +0 -0
  107. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zgroup +0 -0
  108. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zarray +0 -0
  109. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zattrs +0 -0
  110. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/0 +0 -0
  111. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zarray +0 -0
  112. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zattrs +0 -0
  113. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/0 +0 -0
  114. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zarray +0 -0
  115. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zattrs +0 -0
  116. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/0 +0 -0
  117. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zarray +0 -0
  118. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zattrs +0 -0
  119. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/0 +0 -0
  120. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zarray +0 -0
  121. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zattrs +0 -0
  122. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/0.0 +0 -0
  123. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zattrs +0 -0
  124. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/top_level.txt +0 -0
roms_tools/setup/utils.py CHANGED
@@ -41,7 +41,7 @@ def nan_check(field, mask, error_message=None) -> None:
41
41
  da = xr.where(mask == 1, field, 0)
42
42
  if error_message is None:
43
43
  error_message = (
44
- "NaN values found in interpolated field. This likely occurs because the ROMS grid, including "
44
+ "NaN values found in regridded field. This likely occurs because the ROMS grid, including "
45
45
  "a small safety margin for interpolation, is not fully contained within the dataset's longitude/latitude range. Please ensure that the "
46
46
  "dataset covers the entire area required by the ROMS grid."
47
47
  )
@@ -107,10 +107,10 @@ def interpolate_from_rho_to_u(field, method="additive"):
107
107
  else:
108
108
  raise NotImplementedError(f"Unsupported method '{method}' specified.")
109
109
 
110
- if "lat_rho" in field_interpolated.coords:
111
- field_interpolated.drop_vars(["lat_rho"])
112
- if "lon_rho" in field_interpolated.coords:
113
- field_interpolated.drop_vars(["lon_rho"])
110
+ vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
111
+ for var in vars_to_drop:
112
+ if var in field_interpolated.coords:
113
+ field_interpolated = field_interpolated.drop_vars(var)
114
114
 
115
115
  field_interpolated = field_interpolated.swap_dims({"xi_rho": "xi_u"})
116
116
 
@@ -155,10 +155,10 @@ def interpolate_from_rho_to_v(field, method="additive"):
155
155
  else:
156
156
  raise NotImplementedError(f"Unsupported method '{method}' specified.")
157
157
 
158
- if "lat_rho" in field_interpolated.coords:
159
- field_interpolated.drop_vars(["lat_rho"])
160
- if "lon_rho" in field_interpolated.coords:
161
- field_interpolated.drop_vars(["lon_rho"])
158
+ vars_to_drop = ["lat_rho", "lon_rho", "eta_rho", "xi_rho"]
159
+ for var in vars_to_drop:
160
+ if var in field_interpolated.coords:
161
+ field_interpolated = field_interpolated.drop_vars(var)
162
162
 
163
163
  field_interpolated = field_interpolated.swap_dims({"eta_rho": "eta_v"})
164
164
 
@@ -733,27 +733,27 @@ def get_target_coords(grid, use_coarse_grid=False):
733
733
  """
734
734
  # Select grid variables based on whether the coarse grid is used
735
735
  if use_coarse_grid:
736
- lat, lon, angle, mask = (
737
- grid.ds.lat_coarse.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}),
738
- grid.ds.lon_coarse.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}),
739
- grid.ds.angle_coarse.rename(
740
- {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
741
- ),
742
- grid.ds.mask_coarse.rename(
743
- {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
744
- ),
736
+ lat = grid.ds.lat_coarse.rename(
737
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
745
738
  )
739
+ lon = grid.ds.lon_coarse.rename(
740
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
741
+ )
742
+ angle = grid.ds.angle_coarse.rename(
743
+ {"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"}
744
+ )
745
+ mask = grid.ds.get("mask_coarse")
746
+ if mask is not None:
747
+ mask = mask.rename({"eta_coarse": "eta_rho", "xi_coarse": "xi_rho"})
746
748
 
747
749
  lat_psi = grid.ds.get("lat_psi_coarse")
748
750
  lon_psi = grid.ds.get("lon_psi_coarse")
749
751
 
750
752
  else:
751
- lat, lon, angle, mask = (
752
- grid.ds.lat_rho,
753
- grid.ds.lon_rho,
754
- grid.ds.angle,
755
- grid.ds.mask_rho,
756
- )
753
+ lat = grid.ds.lat_rho
754
+ lon = grid.ds.lon_rho
755
+ angle = grid.ds.angle
756
+ mask = grid.ds.get("mask_rho")
757
757
  lat_psi = grid.ds.get("lat_psi")
758
758
  lon_psi = grid.ds.get("lon_psi")
759
759
 
@@ -927,37 +927,40 @@ def get_vector_pairs(variable_info):
927
927
  return vector_pairs
928
928
 
929
929
 
930
- def gc_dist(lon1, lat1, lon2, lat2):
931
- """Calculate the great circle distance between two points on the Earth's surface.
932
- Latitude and longitude must be provided in degrees (they will be converted to
933
- radians).
930
+ def gc_dist(lon1, lat1, lon2, lat2, input_in_degrees=True):
931
+ """Calculate the great circle distance between two points on the Earth's surface
932
+ using the Haversine formula.
934
933
 
935
- The function uses the Haversine formula to compute the shortest distance
936
- along the surface of a sphere (Earth), assuming the Earth is a perfect sphere.
934
+ Latitude and longitude are assumed to be in degrees by default. If `input_in_degrees` is set to `False`,
935
+ the input is assumed to already be in radians.
937
936
 
938
937
  Parameters
939
938
  ----------
940
939
  lon1, lat1 : float
941
- Longitude and latitude of the first point in degrees.
940
+ Longitude and latitude of the first point.
942
941
  lon2, lat2 : float
943
- Longitude and latitude of the second point in degrees.
942
+ Longitude and latitude of the second point.
943
+ input_in_degrees : bool, optional
944
+ If True (default), the input coordinates are assumed to be in degrees and will be converted to radians.
945
+ If False, the input is assumed to be in radians and no conversion is applied.
944
946
 
945
947
  Returns
946
948
  -------
947
949
  dis : float
948
950
  The great circle distance between the two points in meters.
949
- This is the shortest distance along the surface of a sphere (Earth).
950
951
 
951
952
  Notes
952
953
  -----
953
954
  The radius of the Earth is taken to be 6371315 meters.
954
955
  """
956
+
955
957
  # Convert degrees to radians
956
- d2r = np.pi / 180
957
- lon1 = lon1 * d2r
958
- lat1 = lat1 * d2r
959
- lon2 = lon2 * d2r
960
- lat2 = lat2 * d2r
958
+ if input_in_degrees:
959
+ d2r = np.pi / 180
960
+ lon1 = lon1 * d2r
961
+ lat1 = lat1 * d2r
962
+ lon2 = lon2 * d2r
963
+ lat2 = lat2 * d2r
961
964
 
962
965
  # Difference in latitudes and longitudes
963
966
  dlat = lat2 - lat1
@@ -1058,11 +1061,17 @@ def _to_yaml(forcing_object, filepath: Union[str, Path]) -> None:
1058
1061
  filepath = Path(filepath)
1059
1062
 
1060
1063
  # Step 1: Serialize Grid data
1061
- # Convert the grid attribute to a dictionary and remove non-serializable fields
1062
- grid_data = asdict(forcing_object.grid)
1063
- grid_data.pop("ds", None) # Remove 'ds' attribute (non-serializable)
1064
- grid_data.pop("straddle", None) # Remove 'straddle' if it's non-essential
1065
- grid_yaml_data = {"Grid": grid_data}
1064
+ # Check if the forcing_object has a grid attribute
1065
+ if hasattr(forcing_object, "grid") and forcing_object.grid is not None:
1066
+ grid_data = asdict(forcing_object.grid)
1067
+ grid_yaml_data = {"Grid": _pop_grid_data(grid_data)}
1068
+ else:
1069
+ parent_grid_data = asdict(forcing_object.parent_grid)
1070
+ parent_grid_yaml_data = {"ParentGrid": _pop_grid_data(parent_grid_data)}
1071
+ child_grid_data = asdict(forcing_object.child_grid)
1072
+ child_grid_yaml_data = {"ChildGrid": _pop_grid_data(child_grid_data)}
1073
+
1074
+ grid_yaml_data = {**parent_grid_yaml_data, **child_grid_yaml_data}
1066
1075
 
1067
1076
  # Step 2: Get ROMS Tools version
1068
1077
  # Fetch the version of the 'roms-tools' package for inclusion in the YAML header
@@ -1081,7 +1090,16 @@ def _to_yaml(forcing_object, filepath: Union[str, Path]) -> None:
1081
1090
  filtered_field_names = [
1082
1091
  param
1083
1092
  for param in field_names
1084
- if param not in ("grid", "ds", "use_dask", "climatology")
1093
+ if param
1094
+ not in (
1095
+ "grid",
1096
+ "parent_grid",
1097
+ "child_grid",
1098
+ "ds",
1099
+ "use_dask",
1100
+ "bypass_validation",
1101
+ "climatology",
1102
+ )
1085
1103
  ]
1086
1104
 
1087
1105
  for field_name in filtered_field_names:
@@ -1110,6 +1128,14 @@ def _to_yaml(forcing_object, filepath: Union[str, Path]) -> None:
1110
1128
  yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
1111
1129
 
1112
1130
 
1131
+ def _pop_grid_data(grid_data):
1132
+ grid_data.pop("ds", None) # Remove 'ds' attribute (non-serializable)
1133
+ grid_data.pop("straddle", None)
1134
+ grid_data.pop("verbose", None)
1135
+
1136
+ return grid_data
1137
+
1138
+
1113
1139
  def _from_yaml(forcing_object: Type, filepath: Union[str, Path]) -> Dict[str, Any]:
1114
1140
  """Extract the configuration data for a given forcing object from a YAML file.
1115
1141
 
@@ -1179,3 +1205,107 @@ def _convert_from_iso_format(value):
1179
1205
  except ValueError:
1180
1206
  # Return None or raise an exception if parsing fails
1181
1207
  return value
1208
+
1209
+
1210
+ def handle_boundaries(field):
1211
+ """Adjust the boundaries of a 2D field by copying values from adjacent cells.
1212
+
1213
+ Parameters
1214
+ ----------
1215
+ field : numpy.ndarray or xarray.DataArray
1216
+ A 2D array representing a field (e.g., topography or mask) whose boundary values
1217
+ need to be adjusted.
1218
+
1219
+ Returns
1220
+ -------
1221
+ field : numpy.ndarray or xarray.DataArray
1222
+ The input field with adjusted boundary values.
1223
+ """
1224
+
1225
+ field[0, :] = field[1, :]
1226
+ field[-1, :] = field[-2, :]
1227
+ field[:, 0] = field[:, 1]
1228
+ field[:, -1] = field[:, -2]
1229
+
1230
+ return field
1231
+
1232
+
1233
+ def get_boundary_coords():
1234
+ """This function determines the boundary points for the grid variables by specifying
1235
+ the indices for the south, east, north, and west boundaries.
1236
+
1237
+ Returns
1238
+ -------
1239
+ dict
1240
+ A dictionary containing the boundary coordinates for different variable types.
1241
+ The dictionary has the following structure:
1242
+ - Keys: Variable types ("rho", "u", "v", "vector").
1243
+ - Values: Nested dictionaries that map each direction ("south", "east", "north", "west")
1244
+ to another dictionary specifying the boundary coordinates, represented by grid indices
1245
+ for the respective variable types. For example:
1246
+ - "rho" variables (e.g., `eta_rho`, `xi_rho`)
1247
+ - "u" variables (e.g., `xi_u`)
1248
+ - "v" variables (e.g., `eta_v`)
1249
+ - "vector" variables with lists of indices for multiple grid points (e.g., `eta_rho`, `xi_rho`).
1250
+ """
1251
+
1252
+ bdry_coords = {
1253
+ "rho": {
1254
+ "south": {"eta_rho": 0},
1255
+ "east": {"xi_rho": -1},
1256
+ "north": {"eta_rho": -1},
1257
+ "west": {"xi_rho": 0},
1258
+ },
1259
+ "u": {
1260
+ "south": {"eta_rho": 0},
1261
+ "east": {"xi_u": -1},
1262
+ "north": {"eta_rho": -1},
1263
+ "west": {"xi_u": 0},
1264
+ },
1265
+ "v": {
1266
+ "south": {"eta_v": 0},
1267
+ "east": {"xi_rho": -1},
1268
+ "north": {"eta_v": -1},
1269
+ "west": {"xi_rho": 0},
1270
+ },
1271
+ "vector": {
1272
+ "south": {"eta_rho": [0, 1]},
1273
+ "east": {"xi_rho": [-2, -1]},
1274
+ "north": {"eta_rho": [-2, -1]},
1275
+ "west": {"xi_rho": [0, 1]},
1276
+ },
1277
+ }
1278
+
1279
+ return bdry_coords
1280
+
1281
+
1282
+ def wrap_longitudes(grid_ds, straddle):
1283
+ """Adjusts longitude values in a dataset to handle dateline crossing.
1284
+
1285
+ Parameters
1286
+ ----------
1287
+ grid_ds : xr.Dataset
1288
+ The dataset containing longitude variables to adjust.
1289
+ straddle : bool
1290
+ If True, adjusts longitudes to the range [-180, 180] for datasets
1291
+ that straddle the dateline. If False, adjusts longitudes to the
1292
+ range [0, 360].
1293
+
1294
+ Returns
1295
+ -------
1296
+ xr.Dataset
1297
+ The dataset with adjusted longitude values.
1298
+ """
1299
+ for lon_dim in ["lon_rho", "lon_u", "lon_v"]:
1300
+ if straddle:
1301
+ grid_ds[lon_dim] = xr.where(
1302
+ grid_ds[lon_dim] > 180,
1303
+ grid_ds[lon_dim] - 360,
1304
+ grid_ds[lon_dim],
1305
+ )
1306
+ else:
1307
+ grid_ds[lon_dim] = xr.where(
1308
+ grid_ds[lon_dim] < 0, grid_ds[lon_dim] + 360, grid_ds[lon_dim]
1309
+ )
1310
+
1311
+ return grid_ds
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
  import xarray as xr
3
+ from roms_tools.setup.utils import transpose_dimensions
3
4
 
4
5
 
5
6
  def compute_cs(sigma, theta_s, theta_b):
@@ -83,7 +84,7 @@ def compute_depth(zeta, h, hc, cs, sigma):
83
84
 
84
85
  Parameters
85
86
  ----------
86
- zeta : xr.DataArray
87
+ zeta : xr.DataArray or scalar
87
88
  The sea surface height.
88
89
  h : xr.DataArray
89
90
  The depth of the sea bottom.
@@ -98,23 +99,11 @@ def compute_depth(zeta, h, hc, cs, sigma):
98
99
  -------
99
100
  z : xr.DataArray
100
101
  The depth at different sigma levels.
101
-
102
- Raises
103
- ------
104
- ValueError
105
- If theta_s or theta_b are less than or equal to zero.
106
102
  """
107
103
 
108
- # Expand dimensions
109
- sigma = sigma.expand_dims(dim={"eta_rho": h.eta_rho, "xi_rho": h.xi_rho})
110
- cs = cs.expand_dims(dim={"eta_rho": h.eta_rho, "xi_rho": h.xi_rho})
111
-
112
- s = (hc * sigma + h * cs) / (hc + h)
113
- z = zeta + (zeta + h) * s
104
+ z = (hc * sigma + h * cs) / (hc + h)
105
+ z = zeta + (zeta + h) * z
114
106
 
115
- if "s_rho" in z.dims:
116
- z = z.transpose("s_rho", "eta_rho", "xi_rho")
117
- elif "s_w" in z.dims:
118
- z = z.transpose("s_w", "eta_rho", "xi_rho")
107
+ z = -transpose_dimensions(z)
119
108
 
120
109
  return z
@@ -241,10 +241,14 @@ def test_boundary_forcing_save(boundary_forcing, tmp_path):
241
241
  def test_bgc_boundary_forcing_plot(bgc_boundary_forcing_from_climatology):
242
242
  """Test plot method."""
243
243
 
244
- bgc_boundary_forcing_from_climatology.plot(var_name="ALK_south")
245
- bgc_boundary_forcing_from_climatology.plot(var_name="ALK_east")
246
- bgc_boundary_forcing_from_climatology.plot(var_name="ALK_north")
247
- bgc_boundary_forcing_from_climatology.plot(var_name="ALK_west")
244
+ bgc_boundary_forcing_from_climatology.plot(
245
+ var_name="ALK_south", layer_contours=True
246
+ )
247
+ bgc_boundary_forcing_from_climatology.plot(var_name="ALK_east", layer_contours=True)
248
+ bgc_boundary_forcing_from_climatology.plot(
249
+ var_name="ALK_north", layer_contours=True
250
+ )
251
+ bgc_boundary_forcing_from_climatology.plot(var_name="ALK_west", layer_contours=True)
248
252
 
249
253
 
250
254
  def test_bgc_boundary_forcing_save(bgc_boundary_forcing_from_climatology, tmp_path):
@@ -390,7 +394,8 @@ def test_from_yaml_missing_boundary_forcing(tmp_path, request, use_dask):
390
394
  center_lon: -10
391
395
  center_lat: 61
392
396
  rot: -20
393
- topography_source: ETOPO5
397
+ topography_source:
398
+ name: ETOPO5
394
399
  hmin: 5.0
395
400
  """
396
401
  )
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "center_lat": 0,
3
3
  "center_lon": -20,
4
- "coordinates": "interface_depth_rho interface_depth_u interface_depth_v layer_depth_rho layer_depth_u layer_depth_v",
5
4
  "hc": 300.0,
6
5
  "hmin": 5.0,
7
6
  "roms_tools_version": "0.1.dev157+dirty",
@@ -3,7 +3,6 @@
3
3
  ".zattrs": {
4
4
  "center_lat": 0,
5
5
  "center_lon": -20,
6
- "coordinates": "interface_depth_rho interface_depth_u interface_depth_v layer_depth_rho layer_depth_u layer_depth_v",
7
6
  "hc": 300.0,
8
7
  "hmin": 5.0,
9
8
  "roms_tools_version": "0.1.dev157+dirty",
@@ -42,7 +41,7 @@
42
41
  "_ARRAY_DIMENSIONS": [
43
42
  "s_rho"
44
43
  ],
45
- "long_name": "S-coordinate stretching curves at rho-points",
44
+ "long_name": "Vertical stretching function at rho-points",
46
45
  "units": "nondimensional"
47
46
  },
48
47
  "Cs_w/.zarray": {
@@ -69,7 +68,7 @@
69
68
  "_ARRAY_DIMENSIONS": [
70
69
  "s_w"
71
70
  ],
72
- "long_name": "S-coordinate stretching curves at w-points",
71
+ "long_name": "Vertical stretching function at w-points",
73
72
  "units": "nondimensional"
74
73
  },
75
74
  "angle/.zarray": {
@@ -196,105 +195,6 @@
196
195
  "long_name": "Final bathymetry at rho-points",
197
196
  "units": "meter"
198
197
  },
199
- "interface_depth_rho/.zarray": {
200
- "chunks": [
201
- 101,
202
- 3,
203
- 3
204
- ],
205
- "compressor": {
206
- "blocksize": 0,
207
- "clevel": 5,
208
- "cname": "lz4",
209
- "id": "blosc",
210
- "shuffle": 1
211
- },
212
- "dtype": "<f4",
213
- "fill_value": "NaN",
214
- "filters": null,
215
- "order": "C",
216
- "shape": [
217
- 101,
218
- 3,
219
- 3
220
- ],
221
- "zarr_format": 2
222
- },
223
- "interface_depth_rho/.zattrs": {
224
- "_ARRAY_DIMENSIONS": [
225
- "s_w",
226
- "eta_rho",
227
- "xi_rho"
228
- ],
229
- "long_name": "Interface depth at rho-points",
230
- "units": "m"
231
- },
232
- "interface_depth_u/.zarray": {
233
- "chunks": [
234
- 101,
235
- 3,
236
- 2
237
- ],
238
- "compressor": {
239
- "blocksize": 0,
240
- "clevel": 5,
241
- "cname": "lz4",
242
- "id": "blosc",
243
- "shuffle": 1
244
- },
245
- "dtype": "<f4",
246
- "fill_value": "NaN",
247
- "filters": null,
248
- "order": "C",
249
- "shape": [
250
- 101,
251
- 3,
252
- 2
253
- ],
254
- "zarr_format": 2
255
- },
256
- "interface_depth_u/.zattrs": {
257
- "_ARRAY_DIMENSIONS": [
258
- "s_w",
259
- "eta_rho",
260
- "xi_u"
261
- ],
262
- "long_name": "Interface depth at u-points",
263
- "units": "m"
264
- },
265
- "interface_depth_v/.zarray": {
266
- "chunks": [
267
- 101,
268
- 2,
269
- 3
270
- ],
271
- "compressor": {
272
- "blocksize": 0,
273
- "clevel": 5,
274
- "cname": "lz4",
275
- "id": "blosc",
276
- "shuffle": 1
277
- },
278
- "dtype": "<f4",
279
- "fill_value": "NaN",
280
- "filters": null,
281
- "order": "C",
282
- "shape": [
283
- 101,
284
- 2,
285
- 3
286
- ],
287
- "zarr_format": 2
288
- },
289
- "interface_depth_v/.zattrs": {
290
- "_ARRAY_DIMENSIONS": [
291
- "s_w",
292
- "eta_v",
293
- "xi_rho"
294
- ],
295
- "long_name": "Interface depth at v-points",
296
- "units": "m"
297
- },
298
198
  "lat_coarse/.zarray": {
299
199
  "chunks": [
300
200
  2,
@@ -415,105 +315,6 @@
415
315
  "long_name": "latitude of v-points",
416
316
  "units": "degrees North"
417
317
  },
418
- "layer_depth_rho/.zarray": {
419
- "chunks": [
420
- 100,
421
- 3,
422
- 3
423
- ],
424
- "compressor": {
425
- "blocksize": 0,
426
- "clevel": 5,
427
- "cname": "lz4",
428
- "id": "blosc",
429
- "shuffle": 1
430
- },
431
- "dtype": "<f4",
432
- "fill_value": "NaN",
433
- "filters": null,
434
- "order": "C",
435
- "shape": [
436
- 100,
437
- 3,
438
- 3
439
- ],
440
- "zarr_format": 2
441
- },
442
- "layer_depth_rho/.zattrs": {
443
- "_ARRAY_DIMENSIONS": [
444
- "s_rho",
445
- "eta_rho",
446
- "xi_rho"
447
- ],
448
- "long_name": "Layer depth at rho-points",
449
- "units": "m"
450
- },
451
- "layer_depth_u/.zarray": {
452
- "chunks": [
453
- 100,
454
- 3,
455
- 2
456
- ],
457
- "compressor": {
458
- "blocksize": 0,
459
- "clevel": 5,
460
- "cname": "lz4",
461
- "id": "blosc",
462
- "shuffle": 1
463
- },
464
- "dtype": "<f4",
465
- "fill_value": "NaN",
466
- "filters": null,
467
- "order": "C",
468
- "shape": [
469
- 100,
470
- 3,
471
- 2
472
- ],
473
- "zarr_format": 2
474
- },
475
- "layer_depth_u/.zattrs": {
476
- "_ARRAY_DIMENSIONS": [
477
- "s_rho",
478
- "eta_rho",
479
- "xi_u"
480
- ],
481
- "long_name": "Layer depth at u-points",
482
- "units": "m"
483
- },
484
- "layer_depth_v/.zarray": {
485
- "chunks": [
486
- 100,
487
- 2,
488
- 3
489
- ],
490
- "compressor": {
491
- "blocksize": 0,
492
- "clevel": 5,
493
- "cname": "lz4",
494
- "id": "blosc",
495
- "shuffle": 1
496
- },
497
- "dtype": "<f4",
498
- "fill_value": "NaN",
499
- "filters": null,
500
- "order": "C",
501
- "shape": [
502
- 100,
503
- 2,
504
- 3
505
- ],
506
- "zarr_format": 2
507
- },
508
- "layer_depth_v/.zattrs": {
509
- "_ARRAY_DIMENSIONS": [
510
- "s_rho",
511
- "eta_v",
512
- "xi_rho"
513
- ],
514
- "long_name": "Layer depth at v-points",
515
- "units": "m"
516
- },
517
318
  "lon_coarse/.zarray": {
518
319
  "chunks": [
519
320
  2,
@@ -820,6 +621,60 @@
820
621
  "long_name": "Curvilinear coordinate metric in eta-direction",
821
622
  "units": "meter-1"
822
623
  },
624
+ "sigma_r/.zarray": {
625
+ "chunks": [
626
+ 100
627
+ ],
628
+ "compressor": {
629
+ "blocksize": 0,
630
+ "clevel": 5,
631
+ "cname": "lz4",
632
+ "id": "blosc",
633
+ "shuffle": 1
634
+ },
635
+ "dtype": "<f4",
636
+ "fill_value": "NaN",
637
+ "filters": null,
638
+ "order": "C",
639
+ "shape": [
640
+ 100
641
+ ],
642
+ "zarr_format": 2
643
+ },
644
+ "sigma_r/.zattrs": {
645
+ "_ARRAY_DIMENSIONS": [
646
+ "s_rho"
647
+ ],
648
+ "long_name": "Fractional vertical stretching coordinate at rho-points",
649
+ "units": "nondimensional"
650
+ },
651
+ "sigma_w/.zarray": {
652
+ "chunks": [
653
+ 101
654
+ ],
655
+ "compressor": {
656
+ "blocksize": 0,
657
+ "clevel": 5,
658
+ "cname": "lz4",
659
+ "id": "blosc",
660
+ "shuffle": 1
661
+ },
662
+ "dtype": "<f4",
663
+ "fill_value": "NaN",
664
+ "filters": null,
665
+ "order": "C",
666
+ "shape": [
667
+ 101
668
+ ],
669
+ "zarr_format": 2
670
+ },
671
+ "sigma_w/.zattrs": {
672
+ "_ARRAY_DIMENSIONS": [
673
+ "s_w"
674
+ ],
675
+ "long_name": "Fractional vertical stretching coordinate at w-points",
676
+ "units": "nondimensional"
677
+ },
823
678
  "spherical/.zarray": {
824
679
  "chunks": [],
825
680
  "compressor": null,
@@ -2,6 +2,6 @@
2
2
  "_ARRAY_DIMENSIONS": [
3
3
  "s_rho"
4
4
  ],
5
- "long_name": "S-coordinate stretching curves at rho-points",
5
+ "long_name": "Vertical stretching function at rho-points",
6
6
  "units": "nondimensional"
7
7
  }