from __future__ import annotations
from osaft import log
from osaft.core.basecomposite import BaseFrequencyComposite
from osaft.core.frequency import Frequency
from osaft.core.functions import pi, sqrt
from osaft.core.variable import ActiveVariable, PassiveVariable
[docs]class InviscidFluid(BaseFrequencyComposite):
"""Class for an inviscid fluid
:param frequency: frequency in [Hz]
:param rho: density in [km/m^3]
:param c: speed of sound [m/s]
"""
def __init__(
self,
frequency: int | float | Frequency,
rho: float,
c: float,
) -> None:
"""Constructor method"""
super().__init__(frequency)
# Independent Variables
self._rho_f = PassiveVariable(rho, "density rho")
self._c_f = PassiveVariable(c, "speed of sound c")
# Dependent variables
self._k_f = ActiveVariable(self._compute_k_f, "wavenumber k_f")
self._kappa_f = ActiveVariable(
self._compute_kappa_f,
"compressibility kappa_f",
)
self._lambda_f = ActiveVariable(
self._compute_lambda_f,
"wavelength lambda_f",
)
# Dependencies
self._k_f.is_computed_by(self._c_f, self.frequency._omega)
self._lambda_f.is_computed_by(self._c_f, self.frequency._f)
self._kappa_f.is_computed_by(self._c_f, self._rho_f)
if type(self) is InviscidFluid:
log.debug(f"Creating {self}")
def __repr__(self) -> str:
return (
"InviscidFluid("
f"frequency={self.frequency}, "
f"rho={self.rho_f}, "
f"c={self.c_f}"
")"
)
# -------------------------------------------------------------------------
# Setters and Getters for Independent Variables
# -------------------------------------------------------------------------
@property
def c_f(self) -> float:
r"""Speed of sound :math:`c_f` [m\s].
:getter: returns the value for the frequency
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._c_f.value
@c_f.setter
def c_f(self, value: float) -> None:
self._c_f.value = value
@property
def rho_f(self) -> float:
"""Density :math:`\\rho_f` [kg/m^3].
:getter: returns the value for the density
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._rho_f.value
@rho_f.setter
def rho_f(self, value: float) -> None:
self._rho_f.value = value
# -------------------------------------------------------------------------
# Getters for Dependent Variables
# -------------------------------------------------------------------------
@property
def kappa_f(self) -> float:
r"""Returns the compressibility :math:`\kappa_f` [1/Pa]"""
return self._kappa_f.value
@property
def k_f(self) -> float:
"""Returns the wave number :math:`k_f` [rad s^-1]"""
return self._k_f.value
@property
def lambda_f(self) -> float:
"""Returns the wavelength :math:`\\lambda_f` [m]"""
return self._lambda_f.value
# -------------------------------------------------------------------------
# Dependent Variables Methods
# -------------------------------------------------------------------------
def _compute_k_f(self) -> float:
return self.omega / self.c_f
def _compute_kappa_f(self) -> float:
return 1 / self.c_f**2 / self.rho_f
def _compute_lambda_f(self) -> float:
return self.c_f / self.f
[docs]class ViscousFluid(InviscidFluid):
"""Class for a viscous fluid
:param frequency: frequency in [Hz]
:param rho: density in [km/m^3]
:param c: speed of sound [m/s]
:param eta_f: shear viscosity [Pa s]
:param zeta_f: bulk viscosity [Pa s]
"""
def __init__(
self,
frequency: int | float | Frequency,
rho: float,
c: float,
eta_f: float,
zeta_f: float,
):
"""Constructor method"""
super().__init__(frequency, rho, c)
# Independent Variables
self._eta_f = PassiveVariable(eta_f, "shear viscosity eta_f")
self._zeta_f = PassiveVariable(zeta_f, "bulk viscosity zeta_f")
# Dependent Variables
self._k_v = ActiveVariable(self._compute_k_v, "viscous wavenumber k_v")
self._delta = ActiveVariable(
self._compute_delta,
"boundary layer thickness delta",
)
self._lambda_v = ActiveVariable(
self._compute_lambda_v,
"viscous wavelength lambda_v",
)
# Dependencies
self._k_v.is_computed_by(
self.frequency._omega,
self._rho_f,
self._eta_f,
)
self._k_f.is_computed_by(self._rho_f, self._eta_f, self._zeta_f)
self._lambda_v.is_computed_by(self._k_v)
self._delta.is_computed_by(self._k_v)
if type(self) is ViscousFluid:
log.debug(f"Creating {self}")
def __repr__(self) -> str:
return (
"ViscousFluid("
f"frequency={self.frequency}, "
f"rho={self.rho_f}, "
f"c={self.c_f}, "
f"eta_f={self.eta_f}, "
f"zeta_f={self.zeta_f}"
")"
)
# -------------------------------------------------------------------------
# Setters and Getters for Independent Variables
# -------------------------------------------------------------------------
@property
def eta_f(self) -> float:
"""Shear viscosity :math:`\\eta_f` [Pa s].
:getter: returns the shear viscosity
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._eta_f.value
@eta_f.setter
def eta_f(self, value: float) -> None:
self._eta_f.value = value
@property
def zeta_f(self) -> float:
"""Bulk viscosity :math:`\\zeta_f` [Pa s].
:getter: returns the bulk viscosity
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._zeta_f.value
@zeta_f.setter
def zeta_f(self, value: float) -> None:
self._zeta_f.value = value
# -------------------------------------------------------------------------
# Getters for Dependent Variables
# -------------------------------------------------------------------------
@property
def k_f(self) -> complex:
"""Returns the wave number :math:`k_f` [1/m]"""
return self._k_f.value
@property
def k_v(self) -> complex:
"""Returns the viscous wave number :math:`k_v` [1/m]"""
return self._k_v.value
@property
def delta(self) -> float:
"""Returns the boundary layer thickness :math:`\\delta` [m]"""
return self._delta.value
@property
def lambda_v(self) -> float:
"""Returns the viscous wave length :math:`\\lambda_v` [m]"""
return self._lambda_v.value
# -------------------------------------------------------------------------
# Dependent Variables Methods
# -------------------------------------------------------------------------
def _compute_k_f(self) -> complex:
out = -1j * self.omega / (self.rho_f * self.c_f**2)
out *= self.zeta_f + 4 * self.eta_f / 3
out += 1
return self.omega / self.c_f / sqrt(out)
def _compute_k_v(self) -> complex:
if self.eta_f != 0:
return (1 + 1j) * sqrt(self.rho_f * self.omega / (2 * self.eta_f))
else:
return 0
def _compute_delta(self) -> float:
if self.k_v != 0:
return 1 / self.k_v.imag
else:
return 0
def _compute_lambda_v(self) -> float:
if self.k_v != 0:
return 2 * pi / self.k_v.real
else:
return 0
[docs]class ViscoelasticFluid(ViscousFluid):
"""Class for a viscoelastic fluid
:param frequency: frequency in [Hz]
:param rho: density in [km/m^3]
:param c: speed of sound [m/s]
:param eta_f: shear viscosity of fluid component [Pa s]
:param zeta_f: bulk viscosity of fluid component [Pa s]
:param eta_p: shear viscosity of polymer component [Pa s]
:param zeta_p: bulk viscosity of polymer component [Pa s]
:param lambda_M: relaxation time of fluid [s]
"""
def __init__(
self,
frequency: int | float | Frequency,
rho: float,
c: float,
eta_f: float,
eta_p: float,
zeta_f: float,
zeta_p: float,
lambda_M: float,
):
"""Constructor method"""
super().__init__(frequency, rho, c, eta_f, zeta_f)
# Independent Variables
self._eta_p = PassiveVariable(
eta_p,
"shear viscosity of polymer component " "eta_p",
)
self._zeta_p = PassiveVariable(
zeta_p,
"shear viscosity of polymer component " "zeta_p",
)
self._lambda_M = PassiveVariable(
lambda_M,
"relaxation time lambda_M",
)
# Dependent variables
self._eta_c = ActiveVariable(self._compute_eta_c)
self._zeta_c = ActiveVariable(self._compute_zeta_c)
# Dependencies
self._k_f.is_computed_by(
self.frequency._omega,
self._c_f,
self._rho_f,
self._eta_c,
self._zeta_c,
)
self._k_v.is_computed_by(self._eta_c, self._zeta_c)
self._eta_c.is_computed_by(
self.frequency._omega,
self._eta_f,
self._eta_p,
self._lambda_M,
)
self._zeta_c.is_computed_by(
self.frequency._omega,
self._zeta_f,
self._zeta_p,
self._lambda_M,
)
# no if clause needed because last in inheritance
log.debug(f"Creating {self}")
def __repr__(self) -> str:
return (
"ViscoelasticFluid("
f"frequency={self.frequency}, "
f"rho={self.rho_f}, "
f"c={self.c_f}, "
f"eta_f={self.eta_f}, "
f"eta_p={self.eta_p}, "
f"zeta_f={self.zeta_f}, "
f"zeta_p={self.zeta_p}, "
f"lambda_M={self.lambda_M} "
")"
)
# -------------------------------------------------------------------------
# Setters and Getters for Independent Variables
# -------------------------------------------------------------------------
@property
def eta_p(self) -> float:
"""Polymer component shear viscosity :math:`\\eta_p` [Pa s]
:getter: Returns the polymer component shear viscosity
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._eta_p.value
@eta_p.setter
def eta_p(self, value: float) -> None:
self._eta_p.value = value
@property
def zeta_p(self) -> float:
"""Polymer component bulk viscosity :math:`\\zeta_p` [Pa s]
:getter: Returns the polymer component bulk viscosity
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._zeta_p.value
@zeta_p.setter
def zeta_p(self, value: float) -> None:
self._zeta_p.value = value
@property
def lambda_M(self) -> float:
"""Relaxation time :math:`\\lambda_M` [s]
:getter: Returns the relaxation time of the fluid
:setter: automatically invokes
:meth:`osaft.core.variable.BaseVariable.notify`
"""
return self._lambda_M.value
@lambda_M.setter
def lambda_M(self, value: float) -> None:
self._lambda_M.value = value
# -------------------------------------------------------------------------
# Getters for Dependent Variables
# -------------------------------------------------------------------------
@property
def eta_c(self) -> complex:
"""Complex acoustic shear viscosity :math:`\\eta_c` [Pa s]"""
return self._eta_c.value
@property
def zeta_c(self) -> complex:
"""Complex acoustic bulk viscosity :math:`\\zeta_c` [Pa s]"""
return self._zeta_c.value
@property
def k_f(self) -> complex:
"""Returns the wave number :math:`k_f` [1/m]"""
return self._k_f.value
@property
def k_v(self) -> complex:
"""Returns the viscous wave number :math:`k_v` [1/m]"""
return self._k_v.value
# -------------------------------------------------------------------------
# Dependent Variables Methods
# -------------------------------------------------------------------------
def _compute_eta_c(self) -> complex:
return self.eta_f + self.eta_p / (1 - 1j * self.omega * self.lambda_M)
def _compute_zeta_c(self) -> complex:
return self.zeta_f + self.zeta_p / (
1 - 1j * self.omega * self.lambda_M
)
def _compute_k_f(self) -> complex:
return (
self.omega
/ self.c_f
/ sqrt(
1
- 1j
* self.omega
/ (self.rho_f * self.c_f**2)
* (self.zeta_c + 4 * self.eta_c / 3),
)
)
def _compute_k_v(self) -> complex:
if self.eta_c != 0:
return (1 + 1j) * sqrt(self.rho_f * self.omega / (2 * self.eta_c))
else:
return 0
if __name__ == "__main__":
pass