Source code for hydrac.model.water

#!/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