roms-tools 1.6.0__py3-none-any.whl → 1.6.2__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/__init__.py +4 -1
- roms_tools/_version.py +1 -1
- roms_tools/setup/boundary_forcing.py +155 -52
- roms_tools/setup/datasets.py +5 -5
- roms_tools/setup/grid.py +9 -11
- roms_tools/setup/initial_conditions.py +82 -25
- roms_tools/setup/plot.py +68 -10
- roms_tools/setup/surface_forcing.py +60 -42
- roms_tools/setup/tides.py +35 -13
- roms_tools/setup/utils.py +15 -6
- roms_tools/tests/test_setup/test_boundary_forcing.py +140 -18
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_ALT_CO2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/ALK_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_ALT_CO2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DIC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOCr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DON_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DONr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/DOPr_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Fe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/Lig_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NH4_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/NO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/O2_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/PO4_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/SiO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diatSi_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/diazP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spCaCO3_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spChl_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spFe_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/spP_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/bgc_boundary_forcing_from_climatology.zarr/zooC_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +0 -7
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +0 -3
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +0 -1
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +0 -1
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +0 -1
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +0 -1
- roms_tools/tests/test_setup/test_datasets.py +8 -3
- roms_tools/tests/test_setup/test_grid.py +6 -5
- roms_tools/tests/test_setup/test_initial_conditions.py +8 -4
- roms_tools/tests/test_setup/test_surface_forcing.py +47 -27
- roms_tools/tests/test_setup/test_tides.py +6 -4
- {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/METADATA +2 -1
- {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/RECORD +159 -159
- {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/LICENSE +0 -0
- {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/WHEEL +0 -0
- {roms_tools-1.6.0.dist-info → roms_tools-1.6.2.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from importlib.metadata import version as _version
|
|
2
|
-
|
|
2
|
+
import logging # noqa: F811
|
|
3
3
|
|
|
4
4
|
try:
|
|
5
5
|
__version__ = _version("roms_tools")
|
|
@@ -13,3 +13,6 @@ from roms_tools.setup.tides import TidalForcing # noqa: F401
|
|
|
13
13
|
from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
|
|
14
14
|
from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
|
|
15
15
|
from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
|
|
16
|
+
|
|
17
|
+
# Configure logging when the package is imported
|
|
18
|
+
logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
|
roms_tools/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Do not change! Do not track in version control!
|
|
2
|
-
__version__ = "1.6.
|
|
2
|
+
__version__ = "1.6.2"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import xarray as xr
|
|
2
2
|
import numpy as np
|
|
3
3
|
import pandas as pd
|
|
4
|
+
from scipy.ndimage import label
|
|
5
|
+
import logging
|
|
4
6
|
import yaml
|
|
5
7
|
import importlib.metadata
|
|
6
|
-
import warnings
|
|
7
8
|
from typing import Dict, Union, List
|
|
8
9
|
from dataclasses import dataclass, field, asdict
|
|
9
10
|
from roms_tools.setup.grid import Grid
|
|
@@ -197,6 +198,13 @@ class BoundaryForcing:
|
|
|
197
198
|
].isel(**bdry_coords[location][direction])
|
|
198
199
|
|
|
199
200
|
if not self.apply_2d_horizontal_fill:
|
|
201
|
+
self._validate_1d_fill(
|
|
202
|
+
processed_fields,
|
|
203
|
+
variable_info,
|
|
204
|
+
bdry_coords,
|
|
205
|
+
direction,
|
|
206
|
+
bdry_data.dim_names["depth"],
|
|
207
|
+
)
|
|
200
208
|
processed_fields = apply_1d_horizontal_fill(processed_fields)
|
|
201
209
|
|
|
202
210
|
# vertical regridding
|
|
@@ -329,38 +337,46 @@ class BoundaryForcing:
|
|
|
329
337
|
"is_vector": False,
|
|
330
338
|
"vector_pair": None,
|
|
331
339
|
"is_3d": False,
|
|
340
|
+
"validate": True,
|
|
332
341
|
},
|
|
333
|
-
"temp": default_info,
|
|
334
|
-
"salt": default_info,
|
|
342
|
+
"temp": {**default_info, "validate": True},
|
|
343
|
+
"salt": {**default_info, "validate": False},
|
|
335
344
|
"u": {
|
|
336
345
|
"location": "u",
|
|
337
346
|
"is_vector": True,
|
|
338
347
|
"vector_pair": "v",
|
|
339
348
|
"is_3d": True,
|
|
349
|
+
"validate": True,
|
|
340
350
|
},
|
|
341
351
|
"v": {
|
|
342
352
|
"location": "v",
|
|
343
353
|
"is_vector": True,
|
|
344
354
|
"vector_pair": "u",
|
|
345
355
|
"is_3d": True,
|
|
356
|
+
"validate": True,
|
|
346
357
|
},
|
|
347
358
|
"ubar": {
|
|
348
359
|
"location": "u",
|
|
349
360
|
"is_vector": True,
|
|
350
361
|
"vector_pair": "vbar",
|
|
351
362
|
"is_3d": False,
|
|
363
|
+
"validate": False,
|
|
352
364
|
},
|
|
353
365
|
"vbar": {
|
|
354
366
|
"location": "v",
|
|
355
367
|
"is_vector": True,
|
|
356
368
|
"vector_pair": "ubar",
|
|
357
369
|
"is_3d": False,
|
|
370
|
+
"validate": False,
|
|
358
371
|
},
|
|
359
372
|
}
|
|
360
373
|
elif self.type == "bgc":
|
|
361
374
|
variable_info = {}
|
|
362
375
|
for var_name in data.var_names.keys():
|
|
363
|
-
|
|
376
|
+
if var_name == "ALK":
|
|
377
|
+
variable_info[var_name] = {**default_info, "validate": True}
|
|
378
|
+
else:
|
|
379
|
+
variable_info[var_name] = {**default_info, "validate": False}
|
|
364
380
|
|
|
365
381
|
return variable_info
|
|
366
382
|
|
|
@@ -506,9 +522,78 @@ class BoundaryForcing:
|
|
|
506
522
|
|
|
507
523
|
return ds
|
|
508
524
|
|
|
525
|
+
def _validate_1d_fill(
|
|
526
|
+
self, processed_fields, variable_info, bdry_coords, direction, depth_dim
|
|
527
|
+
):
|
|
528
|
+
"""Check if any boundary is divided by land and issue a warning if so,
|
|
529
|
+
suggesting the use of 2D horizontal fill for safer regridding.
|
|
530
|
+
|
|
531
|
+
Parameters
|
|
532
|
+
----------
|
|
533
|
+
processed_fields : dict
|
|
534
|
+
A dictionary where keys are variable names and values are `xarray.DataArray`
|
|
535
|
+
objects representing the processed data for each variable.
|
|
536
|
+
|
|
537
|
+
variable_info : dict
|
|
538
|
+
A dictionary containing metadata about each variable (e.g., location,
|
|
539
|
+
whether it's a 3D variable, etc.). Used to retrieve information for
|
|
540
|
+
validating each variable.
|
|
541
|
+
|
|
542
|
+
bdry_coords : dict
|
|
543
|
+
A dictionary containing boundary coordinates for different directions (north, south,
|
|
544
|
+
east, west), used to slice the boundary-specific data for each variable.
|
|
545
|
+
|
|
546
|
+
direction : str
|
|
547
|
+
The boundary direction being processed (e.g., "north", "south", "east", or "west").
|
|
548
|
+
|
|
549
|
+
depth_dim : str
|
|
550
|
+
The dimension representing depth (e.g., 'z', 'depth', etc.), used when slicing 3D
|
|
551
|
+
data for a specific depth level.
|
|
552
|
+
|
|
553
|
+
Returns
|
|
554
|
+
-------
|
|
555
|
+
None
|
|
556
|
+
If a boundary is divided by land, a warning is issued. No return value is provided.
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
for var_name in processed_fields.keys():
|
|
560
|
+
# Only validate variables based on "validate" flag if use_dask is False
|
|
561
|
+
if not self.use_dask or variable_info[var_name]["validate"]:
|
|
562
|
+
location = variable_info[var_name]["location"]
|
|
563
|
+
|
|
564
|
+
# Select the appropriate mask based on variable location
|
|
565
|
+
if location == "rho":
|
|
566
|
+
mask = self.grid.ds.mask_rho
|
|
567
|
+
elif location == "u":
|
|
568
|
+
mask = self.grid.ds.mask_u
|
|
569
|
+
elif location == "v":
|
|
570
|
+
mask = self.grid.ds.mask_v
|
|
571
|
+
|
|
572
|
+
mask = mask.isel(**bdry_coords[location][direction])
|
|
573
|
+
|
|
574
|
+
if variable_info[var_name]["is_3d"]:
|
|
575
|
+
da = processed_fields[var_name].isel({depth_dim: 0, "time": 0})
|
|
576
|
+
else:
|
|
577
|
+
da = processed_fields[var_name].isel({"time": 0})
|
|
578
|
+
|
|
579
|
+
wet_nans = xr.where(da.where(mask).isnull(), 1, 0)
|
|
580
|
+
# Apply label to find connected components of wet NaNs
|
|
581
|
+
labeled_array, num_features = label(wet_nans)
|
|
582
|
+
left_margin = labeled_array[0]
|
|
583
|
+
right_margin = labeled_array[-1]
|
|
584
|
+
if left_margin != 0:
|
|
585
|
+
num_features = num_features - 1
|
|
586
|
+
if right_margin != 0:
|
|
587
|
+
num_features = num_features - 1
|
|
588
|
+
if num_features > 0:
|
|
589
|
+
logging.warning(
|
|
590
|
+
f"For {var_name}, the {direction}ern boundary is divided by land. It would be safer (but slower) to use `apply_2d_horizontal_fill = True`."
|
|
591
|
+
)
|
|
592
|
+
|
|
509
593
|
def _validate(self, ds, variable_info, bdry_coords):
|
|
510
|
-
"""Validate the dataset for NaN values at the first time step
|
|
511
|
-
|
|
594
|
+
"""Validate the dataset for NaN values at the first time step (bry_time=0) for
|
|
595
|
+
specified variables. If NaN values are found at wet points, this function raises
|
|
596
|
+
an error.
|
|
512
597
|
|
|
513
598
|
Parameters
|
|
514
599
|
----------
|
|
@@ -530,14 +615,11 @@ class BoundaryForcing:
|
|
|
530
615
|
Notes
|
|
531
616
|
-----
|
|
532
617
|
Validation is performed on the initial boundary time step (`bry_time=0`) for each
|
|
533
|
-
variable in the dataset.
|
|
534
|
-
a warning is issued instead of a strict NaN check, as the data may not be reliably validated.
|
|
535
|
-
Conversely, if `apply_2d_horizontal_fill` is True, a strict NaN check is performed, raising
|
|
536
|
-
a ValueError if any NaN values are detected.
|
|
618
|
+
variable in the dataset.
|
|
537
619
|
"""
|
|
538
|
-
|
|
539
|
-
#
|
|
540
|
-
|
|
620
|
+
for var_name in variable_info:
|
|
621
|
+
# only validate variables based on "validate" flag if use_dask is false
|
|
622
|
+
if not self.use_dask or variable_info[var_name]["validate"]:
|
|
541
623
|
location = variable_info[var_name]["location"]
|
|
542
624
|
|
|
543
625
|
# Select the appropriate mask based on variable location
|
|
@@ -547,38 +629,29 @@ class BoundaryForcing:
|
|
|
547
629
|
mask = self.grid.ds.mask_u
|
|
548
630
|
elif location == "v":
|
|
549
631
|
mask = self.grid.ds.mask_v
|
|
550
|
-
else:
|
|
551
|
-
continue # Skip if location is not recognized
|
|
552
632
|
|
|
553
633
|
for direction in ["south", "east", "north", "west"]:
|
|
554
634
|
if self.boundaries[direction]:
|
|
555
635
|
bdry_var_name = f"{var_name}_{direction}"
|
|
556
636
|
|
|
557
637
|
# Check for NaN values at the first time step using the nan_check function
|
|
638
|
+
if self.apply_2d_horizontal_fill:
|
|
639
|
+
error_message = None
|
|
640
|
+
else:
|
|
641
|
+
error_message = (
|
|
642
|
+
f"{bdry_var_name} consists entirely of NaNs after regridding. "
|
|
643
|
+
f"This may be due to the {direction}ern boundary being on land in the "
|
|
644
|
+
f"{self.source['name']} data, which could have a coarser resolution than the ROMS domain. "
|
|
645
|
+
f"Try setting `apply_2d_horizontal_fill = True` to resolve this issue."
|
|
646
|
+
)
|
|
647
|
+
|
|
558
648
|
nan_check(
|
|
559
649
|
ds[bdry_var_name].isel(bry_time=0),
|
|
560
650
|
mask.isel(**bdry_coords[location][direction]),
|
|
651
|
+
error_message=error_message,
|
|
561
652
|
)
|
|
562
|
-
else:
|
|
563
|
-
# Can't apply strict NaN check because land values haven't been filled before regridding step; instead warn user
|
|
564
|
-
for direction in ["south", "east", "north", "west"]:
|
|
565
|
-
if self.boundaries[direction]:
|
|
566
|
-
for var_name in variable_info:
|
|
567
|
-
bdry_var_name = f"{var_name}_{direction}"
|
|
568
|
-
if ds[bdry_var_name].isel(bry_time=0).isnull().any().values:
|
|
569
|
-
warnings.warn(
|
|
570
|
-
f"NaN values detected in regridded variables along the {direction}ern boundary. This may indicate that the entire boundary is on land in the source data, or that the source data does not cover this boundary.",
|
|
571
|
-
UserWarning,
|
|
572
|
-
)
|
|
573
|
-
# Break after the first warning for this direction to avoid duplicates
|
|
574
|
-
break
|
|
575
653
|
|
|
576
|
-
def plot(
|
|
577
|
-
self,
|
|
578
|
-
var_name,
|
|
579
|
-
time=0,
|
|
580
|
-
layer_contours=False,
|
|
581
|
-
) -> None:
|
|
654
|
+
def plot(self, var_name, time=0, layer_contours=False, ax=None) -> None:
|
|
582
655
|
"""Plot the boundary forcing field for a given time-slice.
|
|
583
656
|
|
|
584
657
|
Parameters
|
|
@@ -634,6 +707,8 @@ class BoundaryForcing:
|
|
|
634
707
|
If True, contour lines representing the boundaries between vertical layers will
|
|
635
708
|
be added to the plot. For clarity, the number of layer
|
|
636
709
|
contours displayed is limited to a maximum of 10. Default is False.
|
|
710
|
+
ax : matplotlib.axes.Axes, optional
|
|
711
|
+
The axes to plot on. If None, a new figure is created.
|
|
637
712
|
|
|
638
713
|
Returns
|
|
639
714
|
-------
|
|
@@ -649,7 +724,14 @@ class BoundaryForcing:
|
|
|
649
724
|
if var_name not in self.ds:
|
|
650
725
|
raise ValueError(f"Variable '{var_name}' is not found in dataset.")
|
|
651
726
|
|
|
652
|
-
field = self.ds[var_name].isel(bry_time=time)
|
|
727
|
+
field = self.ds[var_name].isel(bry_time=time)
|
|
728
|
+
|
|
729
|
+
if self.use_dask:
|
|
730
|
+
from dask.diagnostics import ProgressBar
|
|
731
|
+
|
|
732
|
+
with ProgressBar():
|
|
733
|
+
field = field.load()
|
|
734
|
+
|
|
653
735
|
title = field.long_name
|
|
654
736
|
|
|
655
737
|
if "s_rho" in field.dims:
|
|
@@ -693,30 +775,36 @@ class BoundaryForcing:
|
|
|
693
775
|
interface_depth = None
|
|
694
776
|
|
|
695
777
|
_section_plot(
|
|
696
|
-
field,
|
|
778
|
+
field,
|
|
779
|
+
interface_depth=interface_depth,
|
|
780
|
+
title=title,
|
|
781
|
+
kwargs=kwargs,
|
|
782
|
+
ax=ax,
|
|
697
783
|
)
|
|
698
784
|
else:
|
|
699
|
-
_line_plot(field, title=title)
|
|
785
|
+
_line_plot(field, title=title, ax=ax)
|
|
700
786
|
|
|
701
787
|
def save(
|
|
702
|
-
self,
|
|
788
|
+
self,
|
|
789
|
+
filepath: Union[str, Path],
|
|
790
|
+
np_eta: int = None,
|
|
791
|
+
np_xi: int = None,
|
|
792
|
+
group: bool = False,
|
|
703
793
|
) -> None:
|
|
704
|
-
"""Save the boundary forcing fields to netCDF4 files.
|
|
705
|
-
|
|
706
|
-
This method saves the dataset by grouping it into subsets based on the data frequency. The subsets are then written
|
|
707
|
-
to one or more netCDF4 files. The filenames of the output files reflect the temporal coverage of the data.
|
|
794
|
+
"""Save the boundary forcing fields to one or more netCDF4 files.
|
|
708
795
|
|
|
709
|
-
|
|
796
|
+
This method saves the dataset either as a single file or as multiple files depending on the partitioning and grouping options.
|
|
797
|
+
The dataset can be saved in two modes:
|
|
710
798
|
|
|
711
|
-
|
|
799
|
+
1. **Single File Mode (default)**:
|
|
800
|
+
- If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
|
|
801
|
+
- The file is named based on the `filepath`, with `.nc` automatically appended.
|
|
712
802
|
|
|
713
|
-
|
|
714
|
-
|
|
803
|
+
2. **Partitioned Mode**:
|
|
804
|
+
- If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
|
|
805
|
+
- Each tile is saved as a separate netCDF4 file, and filenames are modified with an index (e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`).
|
|
715
806
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
- If either `np_eta` or `np_xi` is specified, the dataset is divided into spatial tiles along the eta-axis and xi-axis.
|
|
719
|
-
- Each spatial tile is saved as a separate netCDF4 file.
|
|
807
|
+
Additionally, if `group` is set to `True`, the dataset is first grouped into temporal subsets, resulting in multiple grouped files before partitioning and saving.
|
|
720
808
|
|
|
721
809
|
Parameters
|
|
722
810
|
----------
|
|
@@ -728,6 +816,8 @@ class BoundaryForcing:
|
|
|
728
816
|
The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
|
|
729
817
|
np_xi : int, optional
|
|
730
818
|
The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
|
|
819
|
+
group: bool, optional
|
|
820
|
+
If `True`, groups the dataset into multiple files based on temporal data frequency. Defaults to `False`.
|
|
731
821
|
|
|
732
822
|
Returns
|
|
733
823
|
-------
|
|
@@ -742,7 +832,18 @@ class BoundaryForcing:
|
|
|
742
832
|
if filepath.suffix == ".nc":
|
|
743
833
|
filepath = filepath.with_suffix("")
|
|
744
834
|
|
|
745
|
-
|
|
835
|
+
if self.use_dask:
|
|
836
|
+
from dask.diagnostics import ProgressBar
|
|
837
|
+
|
|
838
|
+
with ProgressBar():
|
|
839
|
+
self.ds.load()
|
|
840
|
+
|
|
841
|
+
if group:
|
|
842
|
+
dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
|
|
843
|
+
else:
|
|
844
|
+
dataset_list = [self.ds]
|
|
845
|
+
output_filenames = [str(filepath)]
|
|
846
|
+
|
|
746
847
|
saved_filenames = save_datasets(
|
|
747
848
|
dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
|
|
748
849
|
)
|
|
@@ -783,6 +884,7 @@ class BoundaryForcing:
|
|
|
783
884
|
"boundaries": self.boundaries,
|
|
784
885
|
"source": self.source,
|
|
785
886
|
"type": self.type,
|
|
887
|
+
"apply_2d_horizontal_fill": self.apply_2d_horizontal_fill,
|
|
786
888
|
"model_reference_date": self.model_reference_date.isoformat(),
|
|
787
889
|
}
|
|
788
890
|
}
|
|
@@ -796,7 +898,7 @@ class BoundaryForcing:
|
|
|
796
898
|
# Write header
|
|
797
899
|
file.write(header)
|
|
798
900
|
# Write YAML data
|
|
799
|
-
yaml.dump(yaml_data, file, default_flow_style=False)
|
|
901
|
+
yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
|
|
800
902
|
|
|
801
903
|
@classmethod
|
|
802
904
|
def from_yaml(
|
|
@@ -930,6 +1032,7 @@ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
|
|
|
930
1032
|
raise ValueError(
|
|
931
1033
|
f"No valid horizontal dimension found for variable '{var_name}'."
|
|
932
1034
|
)
|
|
1035
|
+
|
|
933
1036
|
# Forward and backward fill in the horizontal direction
|
|
934
1037
|
filled = one_dim_fill(
|
|
935
1038
|
processed_fields[var_name], selected_horizontal_dim, direction="forward"
|
roms_tools/setup/datasets.py
CHANGED
|
@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from typing import Dict, Optional, Union, List
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
import
|
|
9
|
+
import logging
|
|
10
10
|
from roms_tools.setup.utils import (
|
|
11
11
|
assign_dates_to_climatology,
|
|
12
12
|
interpolate_from_climatology,
|
|
@@ -410,7 +410,7 @@ class Dataset:
|
|
|
410
410
|
ds[time_dim].where(before_start, drop=True).max()
|
|
411
411
|
)
|
|
412
412
|
else:
|
|
413
|
-
|
|
413
|
+
logging.warning("No records found at or before the start_time.")
|
|
414
414
|
closest_before_start = ds[time_dim].min()
|
|
415
415
|
|
|
416
416
|
# Identify records after or at end_time
|
|
@@ -420,7 +420,7 @@ class Dataset:
|
|
|
420
420
|
ds[time_dim].where(after_end, drop=True).min()
|
|
421
421
|
)
|
|
422
422
|
else:
|
|
423
|
-
|
|
423
|
+
logging.warning("No records found at or after the end_time.")
|
|
424
424
|
closest_after_end = ds[time_dim].max()
|
|
425
425
|
|
|
426
426
|
# Select records within the time range and add the closest before/after
|
|
@@ -451,11 +451,11 @@ class Dataset:
|
|
|
451
451
|
if ds.sizes[time_dim] > 1:
|
|
452
452
|
# Pick the time closest to self.start_time
|
|
453
453
|
ds = ds.isel({time_dim: 0})
|
|
454
|
-
|
|
454
|
+
logging.info(
|
|
455
455
|
f"Selected time entry closest to the specified start_time ({self.start_time}) within the range [{self.start_time}, {self.start_time + timedelta(hours=24)}]: {ds[time_dim].values}"
|
|
456
456
|
)
|
|
457
457
|
else:
|
|
458
|
-
|
|
458
|
+
logging.warning(
|
|
459
459
|
"Dataset does not contain any time information. Please check if the time dimension "
|
|
460
460
|
"is correctly named or if the dataset includes time data."
|
|
461
461
|
)
|
roms_tools/setup/grid.py
CHANGED
|
@@ -13,7 +13,7 @@ from roms_tools.setup.plot import _plot, _section_plot, _profile_plot, _line_plo
|
|
|
13
13
|
from roms_tools.setup.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
|
|
14
14
|
from roms_tools.setup.vertical_coordinate import sigma_stretch, compute_depth
|
|
15
15
|
from roms_tools.setup.utils import extract_single_value, save_datasets
|
|
16
|
-
import
|
|
16
|
+
import logging
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
19
|
RADIUS_OF_EARTH = 6371315.0 # in m
|
|
@@ -350,11 +350,7 @@ class Grid:
|
|
|
350
350
|
_plot(self.ds, straddle=self.straddle)
|
|
351
351
|
|
|
352
352
|
def plot_vertical_coordinate(
|
|
353
|
-
self,
|
|
354
|
-
varname="layer_depth_rho",
|
|
355
|
-
s=None,
|
|
356
|
-
eta=None,
|
|
357
|
-
xi=None,
|
|
353
|
+
self, varname="layer_depth_rho", s=None, eta=None, xi=None, ax=None
|
|
358
354
|
) -> None:
|
|
359
355
|
"""Plot the vertical coordinate system for a given eta-, xi-, or s-slice.
|
|
360
356
|
|
|
@@ -376,6 +372,8 @@ class Grid:
|
|
|
376
372
|
The eta-index to plot. Default is None.
|
|
377
373
|
xi : int, optional
|
|
378
374
|
The xi-index to plot. Default is None.
|
|
375
|
+
ax : matplotlib.axes.Axes, optional
|
|
376
|
+
The axes to plot on. If None, a new figure is created. Note that this argument does not work for horizontal plots that display the eta- and xi-dimensions at the same time.
|
|
379
377
|
|
|
380
378
|
Returns
|
|
381
379
|
-------
|
|
@@ -477,12 +475,13 @@ class Grid:
|
|
|
477
475
|
interface_depth=interface_depth,
|
|
478
476
|
title=title,
|
|
479
477
|
kwargs=kwargs,
|
|
478
|
+
ax=ax,
|
|
480
479
|
)
|
|
481
480
|
else:
|
|
482
481
|
if "s_rho" in field.dims or "s_w" in field.dims:
|
|
483
|
-
_profile_plot(field, title=title)
|
|
482
|
+
_profile_plot(field, title=title, ax=ax)
|
|
484
483
|
else:
|
|
485
|
-
_line_plot(field, title=title)
|
|
484
|
+
_line_plot(field, title=title, ax=ax)
|
|
486
485
|
|
|
487
486
|
def save(
|
|
488
487
|
self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
|
|
@@ -679,7 +678,7 @@ class Grid:
|
|
|
679
678
|
# Write header
|
|
680
679
|
file.write(header)
|
|
681
680
|
# Write YAML data
|
|
682
|
-
yaml.dump(yaml_data, file, default_flow_style=False)
|
|
681
|
+
yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
|
|
683
682
|
|
|
684
683
|
@classmethod
|
|
685
684
|
def from_yaml(cls, filepath: Union[str, Path]) -> "Grid":
|
|
@@ -728,9 +727,8 @@ class Grid:
|
|
|
728
727
|
roms_tools_version_current = "unknown"
|
|
729
728
|
|
|
730
729
|
if roms_tools_version_header != roms_tools_version_current:
|
|
731
|
-
|
|
730
|
+
logging.warning(
|
|
732
731
|
f"Current roms-tools version ({roms_tools_version_current}) does not match the version in the YAML header ({roms_tools_version_header}).",
|
|
733
|
-
UserWarning,
|
|
734
732
|
)
|
|
735
733
|
|
|
736
734
|
if grid_data is None:
|