#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Water properties (:mod:`hydrac.model.water`)
============================================
.. autoclass:: Water
:members:
:private-members:
"""
import hydrac.calcul.seawater as sw
import numpy as np
from hydrac.util import constants
from hydrac.util.parameters import Parameters
import warnings
[docs]class Water():
"""
Water properties class.
This class sets the water properties to be used for hydroaacoustic data
water absorption correction and scattering model computation. It uses
several features of the package :mod:`hydrac.calcul.seawater` (ex: water
density calculation, sound speed velocity...).
Some default presets have been set for seawater, and rivers, as salinity
plays an important role dor water density calculation.
Parameters
----------
rho : float
Water density in kg/m\ :sup:`3`\
c : float
Sound velocity in water in m/s
T : float
Water temperature in degrees
S : float
Salinity in PSU
P : float
Depth in m
preset : str, {'sea','river'}
Automatically loads the water parameters saved as presets in the
package :mod:`hydrac.util.constants`
"""
def __init__(self, rho=1000, c=1480, T=None, S=None, P=None, preset=None):
self.paramw = Parameters({})
self.paramw.T = T
self.paramw.S = S
self.paramw.P = P
self.paramw.rho = rho
self.paramw.cw = c
self.paramw.preset = preset
if self.paramw.preset == 'sea':
self.paramw.P = 1
self.paramw.T = 10
self.paramw.S = 35
elif self.paramw.preset == 'river':
self.paramw.P = 1
self.paramw.T = 10
self.paramw.S = 0
elif self.paramw.preset == 'custom':
self.paramw.P = constants.water_custom['P']
self.paramw.S = constants.water_custom['S']
self.paramw.T = constants.water_custom['T']
if min(np.shape([self.paramw.T])) > 1 or min(
np.shape([self.paramw.S])) > 1 or min(
np.shape([self.paramw.P])) > 1:
print('No Matrix please')
elif (np.size(np.array(self.paramw.T)) == 1 or np.size(
np.array(self.paramw.S)) == 1 or np.size(
np.array(self.paramw.P)) == 1) and (self.paramw.T is
None or self.paramw.S is None or self.paramw.P is None):
print('Not enough input parameters - set to default values')
else:
self.paramw.cw = sw.svel(np.squeeze(self.paramw.S), np.squeeze(
self.paramw.T), np.squeeze(self.paramw.P))
self.paramw.rho = sw.dens(np.squeeze(self.paramw.S), np.squeeze(
self.paramw.T), np.squeeze(self.paramw.P))
self.paramw.D = self.paramw.P
[docs] def mean_TSCR(self, nm, mode_depl):
"""
Computes the mean of the target variable nm over the given timeseries
or profile.
Parameters
----------
nm : float (numpy.array)
parameter to be averaged
mode_depl : str, {'Mooring', 'Cast'}
Deployment strategy
"""
if type(nm) == int or np.size(np.array(nm)) == 1:
return nm
if mode_depl == 'Cast':
return np.nanmean(nm[np.min(
np.where(np.array(self.paramw.D) > 1)[0]):-1])
else:
# pour l'instant on prend moyenne mais a terme il faudra découper
# si grandes variations
return np.nanmean(nm[np.min(
np.where(np.array(self.paramw.D) > 1)[0]):-1])
[docs] def water_absorption_coefficient(self, f, id_time=None):
"""
Computes the attenuation due to water using the François & Garrison
(1982) model (https://doi.org/10.1121/1.388170).
Parameters
----------
f : float (numpy.array)
Sound frequency in Hz
id_time : int
Index at which the attenuaation is to be computed if wanted. Set to
None by default, meaning attenuation is computed everywhere.
Returns
-------
alph : float (numpy.array)
Water absorption coefficient in dB/m for each frequency and each
water parameter measurement.
"""
warnings.filterwarnings('ignore')
if id_time is None:
D = np.array(np.squeeze(self.paramw.D))
S = np.array(np.squeeze(self.paramw.S))
T = np.array(np.squeeze(self.paramw.T))
else:
D = self.paramw.D[id_time]
S = self.paramw.S[id_time]
T = self.paramw.T[id_time]
Z = -D
Z2 = Z * Z
f = np.array(f) / 1000
f_carre = f * f
Celerite = np.array(self.paramw.cw)
# contribution acide borique
A1 = (154 / Celerite)
P1 = 1
F1 = np.array(self.FBOH3(T, S))
# contribution sulfate de magnesium
A2 = 21.44 * S / Celerite * (1 + 0.025 * T)
P2 = (1 - 1.37e-4 * (-Z) + 6.2e-9 * Z2)
F2 = np.array(self.FMgSO4(T, S))
# contribution viscosite eau pure
T_carre = T * T
A31 = np.zeros(np.size(T))
A32 = np.zeros(np.size(T))
Index = 1
if np.size(T) > 1:
A31[np.where(
T <= 20)] = (4.937e-4 - 2.59e-5 * T[np.where(
T <= 20)] + 9.11e-7 * T_carre[np.where(
T <= 20)] - 1.5e-8 * T_carre[np.where(
T <= 20)] * T[np.where(
T <= 20)]) * Index
A32[np.where(
T > 20)] = (3.964e-4 - 1.146e-5 * T[np.where(
T > 20)] + 1.45e-7 * T_carre[np.where(
T > 20)] - 6.5e-10 * T_carre[np.where(
T > 20)] * T[np.where(
T > 20)]) * Index
else:
if T <= 20:
A31 = (4.937e-4 - 2.59e-5 * T + 9.11e-7 * T_carre - 1.5e-8 *
T_carre * T) * Index
A32 = 0
else:
A31 = 0
A32 = (3.964e-4 - 1.146e-5 * T + 1.45e-7 * T_carre - 6.5e-10 *
T_carre * T) * Index
A3 = A31 + A32
P3 = 1 - 3.83e-5 * (-Z) + 4.9e-10 * Z2
# calcul a en dB/km
a = (np.tile(A1 * P1 * F1, (len(np.atleast_1d(f)), 1)).T * np.tile(
f_carre, (len(np.atleast_1d(T)), 1))) / (np.tile(
F1 * F1, (len(np.atleast_1d(f)), 1)).T + np.tile(
f_carre, (len(np.atleast_1d(T)), 1))) + (
np.tile(A2 * P2 * F2, (len(np.atleast_1d(f)), 1)).T *
f_carre) / (np.tile(f_carre, (len(
np.atleast_1d(T)), 1)) + np.tile(F2 * F2, (len(
np.atleast_1d(f)), 1)).T) + np.tile(
A3 * P3, (len(np.atleast_1d(f)), 1)
).T * np.tile(f_carre, (len(
np.atleast_1d(T)), 1))
# passage en dB/m
alph = a / 1000
return alph
[docs] @staticmethod
def FBOH3(T, S):
"""
Boric acid contribution to water absorption used in François &
Garrison model (:func:`water_absorption_coefficient`)
Parameters
----------
T : float
Temperature in degrees
S : float
Salinity in PSU
"""
F = 2.8 * np.sqrt(S / 35) * 10 ** (4 - (1245 / (T + 273)))
return F
[docs] @staticmethod
def FMgSO4(T, S):
"""
Magnesium sulfate contribution to water absorption used in François &
Garrison model (:func:`water_absorption_coefficient`)
Parameters
----------
T : float
Temperature in degrees
S : float
Salinity in PSU
"""
F = 8.17 * 10 ** (8 - (1990 / (273 + T))) / (1 + 1.8e-3 * (S - 35))
return F
[docs] def water_absorption_coefficient2(self, f, TT=None):
"""
Computes the attenuation due to water with zeros salinity using the
Ainslie & McColm (1997) model (https://doi.org/10.1121/1.421258) at
low depth (z=0.5 m). This model is used as an alternative in case of
lack of data regarding the water properties. Yet this model as
presented here is only applicable to freshwater. With more information
the François & Garrison model is used
(:func:`water_absorption_coefficient`)
Parameters
----------
f : float (numpy.array)
Sound frequency in Hz
TT : float
Temperature in degrees
Returns
-------
alph : float (numpy.array)
Water absorption coefficient in dB/m for each frequency for the
peculiar temperature chosen by the user.
"""
if self.paramw.T is not None:
return 0.00049 * ((np.tile(np.array(f), (len(np.atleast_1d(
self.paramw.T)), 1))) / 1e3) ** 2 * np.exp(-(np.tile(
np.array(self.paramw.T), (len(
np.atleast_1d(f)), 1)).T / 27 + .5 / 17)) / 1000
else:
T = np.array(TT)
return 0.00049 * ((np.tile(np.array(f), (len(np.atleast_1d(
T)), 1))) / 1e3) ** 2 * np.exp(-(np.tile(np.array(
T), (len(np.atleast_1d(f)), 1)).T / 27 + .5 / 17)) / 1000