Density#

Hydrological_model_validator.Processing.Density.calc_density(temp_3d: ndarray, sal_3d: ndarray, depths: ndarray, valid_mask, density_method: str) ndarray[source]#

Calculate seawater density based on temperature, salinity, and depth using the specified density method.

Parameters:
  • temp_3d (np.ndarray) – 3D array of temperature values (depth x lat x lon).

  • sal_3d (np.ndarray) – 3D array of salinity values (depth x lat x lon).

  • depths (np.ndarray) – 1D array of depth levels in meters (length = depth dimension of temp_3d).

  • valid_mask (np.ndarray or None) – Optional mask array; if None, valid values are where temp and sal are not NaN. (In this function, valid_mask is redefined internally, so input is ignored.)

  • density_method (str) – Method for density calculation. One of: “EOS”, “EOS80”, “TEOS10”.

Returns:

3D array of seawater density values (depth x lat x lon).

Return type:

np.ndarray

Raises:

ValueError – If density_method is not one of the supported options.

Examples

>>> import numpy as np
>>> import gsw
>>> depth_levels = np.array([0, 10, 20])
>>> temp = np.array([
...     [[10, 11], [12, 13]],
...     [[9, 10], [11, 12]],
...     [[8, 9], [10, 11]]
... ])
>>> sal = np.array([
...     [[35, 35], [35, 35]],
...     [[34.5, 34.5], [34.5, 34.5]],
...     [[34, 34], [34, 34]]
... ])
>>> density = calc_density(temp, sal, depth_levels, None, "EOS")
>>> print(density.shape)
(3, 2, 2)
>>> print(np.round(density, 2))
[[[1025.    1024.8 ]
  [1025.2   1025.4 ]]
[[1024.6 1024.4 ]

[1024.8 1025. ]]

[[1024.2 1024. ]

[1024.4 1024.6 ]]]

Hydrological_model_validator.Processing.Density.compute_Bleast(mask3d: ndarray) ndarray[source]#

Extract the first (top) layer from a 3D mask array along the depth axis.

Parameters:

mask3d (np.ndarray) – 3D array of shape (depth, rows, cols).

Returns:

2D array of shape (rows, cols) corresponding to the first layer mask3d[0, :, :].

Return type:

np.ndarray

Examples

>>> mask3d = np.array([[[1, 0], [0, 1]],
                      [[0, 1], [1, 0]]])
>>> compute_Bleast(mask3d)
array([[1, 0],
       [0, 1]])
Hydrological_model_validator.Processing.Density.compute_Bmost(mask3d: ndarray) ndarray[source]#

Compute a 2D array by summing the 3D mask array along the depth axis.

Parameters:

mask3d (np.ndarray) – 3D binary mask array with shape (depth, rows, cols), where valid data points are typically marked as 1, invalid as 0.

Returns:

2D array (rows, cols) where each element is the count of valid depth levels (sum of mask) at that spatial location.

Return type:

np.ndarray

Notes

Summation is performed over the depth dimension (axis=0), equivalent to counting valid levels for each (row, col).

Examples

>>> mask3d = np.array([[[1, 0], [0, 1]],
                      [[0, 1], [1, 0]]])
>>> compute_Bmost(mask3d)
array([[1, 1],
       [1, 1]])
Hydrological_model_validator.Processing.Density.compute_dense_water_volume(IDIR: str | Path, mask3d: ndarray, filename_fragments: dict, density_method: str, dz: float = 2.0, dx: float = 800.0, dy: float = 800.0, dens_threshold: float = 1029.2) List[Dict][source]#

Compute the volume of dense water masses (density >= dens_threshold kg/m³) over time from oceanographic temperature and salinity data.

Parameters:
  • IDIR (Union[str, Path]) – Directory path containing yearly subfolders with compressed NetCDF files. Each subfolder named like ‘outputYYYY’ contains the data for year YYYY.

  • mask3d (np.ndarray) – 3D boolean mask array of shape (depth, Y, X), where True means the cell is masked/excluded.

  • filename_fragments (dict) – Dictionary with keys ‘ffrag1’, ‘ffrag2’, ‘ffrag3’ used to construct filenames for the data files.

  • density_method (str) – Method to calculate seawater density. Must be one of “EOS”, “EOS80”, or “TEOS10”.

  • dz (float, optional) – Vertical thickness of each grid layer in meters (default 2.0 m).

  • dx (float, optional) – Horizontal grid spacing in meters along the x-axis (default 800.0 m).

  • dy (float, optional) – Horizontal grid spacing in meters along the y-axis (default 800.0 m).

  • dens_threshold (float, optional) – Density threshold in kg/m³ to define dense water (default 1029.2 kg/m³).

Returns:

List of dictionaries, each containing: - ‘date’: datetime object for the first day of each month, - ‘volume_m3’: volume of dense water (in cubic meters) for that month.

Return type:

List[Dict]

Notes

  • The function expects yearly subdirectories named as ‘outputYYYY’ inside IDIR.

  • Temperature and salinity are read from compressed NetCDF files.

  • Cells masked by mask3d are excluded from volume calculation.

  • Density is computed per cell per time using the specified density method.

  • The dense water volume is calculated by counting cells exceeding the density threshold and multiplying by the cell volume (dx * dy * dz).

Examples

>>> from pathlib import Path
>>> import numpy as np
>>> mask = np.zeros((50, 100, 100), dtype=bool)  # no masked cells
>>> filename_fragments = {'ffrag1': 'data', 'ffrag2': 'temp', 'ffrag3': 'sal'}
>>> IDIR = Path('/path/to/ocean_data')
>>> dense_volumes = compute_dense_water_volume(
...     IDIR=IDIR,
...     mask3d=mask,
...     filename_fragments=filename_fragments,
...     density_method="EOS80",
...     dz=2.0,
...     dx=800.0,
...     dy=800.0,
...     dens_threshold=1029.2
... )
>>> print(dense_volumes[0])
{'date': datetime.datetime(2000, 1, 1, 0, 0), 'volume_m3': 1234567.89}
Hydrological_model_validator.Processing.Density.compute_density_bottom(temperature_data: dict, salinity_data: dict, Bmost: ndarray, method: str, dz: float = 2.0) dict[source]#

Compute seawater density (kg/m³) at benthic depth using the specified method.

Parameters:
  • temperature_data (dict) – Dictionary keyed by year (int or str), each containing a list of 12 arrays representing bottom temperature for each month.

  • salinity_data (dict) – Dictionary keyed by year, each containing a list of 12 arrays representing bottom salinity for each month.

  • Bmost (np.ndarray) – 2D array (Y, X) containing the 1-based index of the deepest valid vertical level.

  • method (str) – Method to compute density. Supported options: - ‘EOS’: Linear equation of state approximation. - ‘EOS80’: EOS-80 potential density at surface. - ‘TEOS10’: TEOS-10 absolute density with pressure.

  • dz (float, optional) – Vertical layer thickness in meters (default is 2.0 m).

Returns:

density_data – Dictionary keyed by year, each containing a list of 12 arrays with computed density fields corresponding to the monthly bottom data.

Return type:

dict

Raises:

ValueError – If an unsupported method is passed or the density output shape is unexpected.

Example

>>> # Assume temperature_data and salinity_data are loaded dictionaries as described
>>> # Bmost is a 2D numpy array indicating bottom layer indices
>>> method = 'EOS80'
>>> density = compute_density_bottom(temperature_data, salinity_data, Bmost, method)
>>> # density is a dict {year: [12 arrays]}, each array is density at benthic depth for that month
Hydrological_model_validator.Processing.Density.filter_dense_water_masses(density_data: Dict[int, List[ndarray]], threshold: float = 1029.2) Dict[int, List[ndarray]][source]#

Filter density data to retain only dense water masses with density values greater than or equal to the specified threshold.

Parameters:
  • density_data (dict) – Dictionary with keys as years (int) and values as lists of 12 2D numpy arrays, each representing monthly seawater density fields.

  • threshold (float, optional) – Density threshold in kg/m³ for defining dense water masses. Values below this threshold will be masked out (default is 1029.2).

Returns:

filtered_data – Dictionary with the same structure as input, where density values below the threshold are replaced with np.nan, retaining only dense water masses.

Return type:

dict

Examples

>>> import numpy as np
>>> density_data = {
...     2000: [np.array([[1029.3, 1028.9], [1029.5, 1027.0]]) for _ in range(12)],
...     2001: [np.array([[1029.1, 1029.0], [1028.0, 1030.0]]) for _ in range(12)]
... }
>>> filtered = filter_dense_water_masses(density_data, threshold=1029.2)
>>> print(filtered[2000][0])
[[1029.3    nan]
 [1029.5    nan]]
>>> print(filtered[2001][0])
[[   nan    nan]
 [   nan 1030.0]]