from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Optional
import numpy as np
from osaft.core.functions import LegendreFunctions as Leg
[docs]class BaseStreaming(ABC):
""" Base class for the Streaming Field that defines the common
interface.
This base class is used for axisymmetrical models.
"""
# Required properties by child class
# -------------------------------------------------------------------------
# Abstract Methods
# -------------------------------------------------------------------------
@property
@abstractmethod
def range_N_max(self):
pass
@property
@abstractmethod
def range_1_N_max(self):
pass
[docs] @abstractmethod
def Phi(self, l: int, r: float) -> float:
""":math:`\\Phi_l(r)`
:param l: index l
:param r: radial coordinate [m]
:return: Phi_l(r)
"""
pass
[docs] @abstractmethod
def d_Phi(self, l: int, r: float) -> float:
""":math:`\\partial_r \\Phi_l(r)`
:param l: index l
:param r: radial coordinate [m]
:return: d_Phi_l(l, r)
"""
pass
[docs] @abstractmethod
def Psi(self, l: int, r: float) -> float:
""":math:`\\Psi_l(r)`
:param l: index l
:param r: radial coordinate [m]
:return: Psi_l(l, r)
"""
pass
[docs] @abstractmethod
def d_Psi(self, l: int, r: float) -> float:
""":math:`\\partial_r \\Psi_l(r)`
:param l: index l
:param r: radial coordinate [m]
:return: d_Psi(l, r)
"""
pass
[docs] @abstractmethod
def V_S_r(self, l: int, r: float, ac: bool) -> float:
""":math:`V_{Srl}(r)`
:param l: index l
:param r: radial coordinate [m]
:param ac: if True only the incident field contribution
"""
pass
[docs] @abstractmethod
def V_S_theta(self, l: int, r: float, ac: bool) -> float:
""":math:`V_{S \\theta l}(r)`
:param l: index l
:param r: radial coordinate [m]
:param ac: if True only the incident field contribution
"""
pass
# -------------------------------------------------------------------------
# User Facing Methods
# -------------------------------------------------------------------------
[docs] def radial_Stokes_drift_coefficients(
self, r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Radial Stokes drift coefficient [m/s]
(Eq. F8)
:param r: radial coordinate [m]
:param mode: mode
:return: radial Stokes drift coefficient
"""
def legendre_coeffs(l, x):
return self.V_S_r(l, x, False) - self.V_S_r(l, x, True)
if mode is not None:
if mode > 0:
return legendre_coeffs(mode, r)
else:
return 0
else:
return np.array(
[0.0] + [legendre_coeffs(n, r) for n in self.range_1_N_max],
)
[docs] def radial_Stokes_drift(
self, r: float, theta: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Radial Stokes drift velocity [m/s]
(Eq. F6)
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode
:return: radial Stokes drift velocity
"""
if mode is not None:
return Leg.cos_monomial(
mode, theta, self.radial_Stokes_drift_coefficients(r, mode),
)
else:
return Leg.cos_poly(
theta, self.radial_Stokes_drift_coefficients(r),
)
[docs] def tangential_Stokes_drift_coefficients(
self,
r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Tangential Stokes drift coefficient [m/s]
(Eq. F9)
:param r: radial coordinate [m]
:param mode: mode
:return: tangential Stokes drift coefficient
"""
def legendre_coeffs(l, x):
return self.V_S_theta(l, x, False) - self.V_S_theta(l, x, True)
if mode is not None:
if mode > 0:
return legendre_coeffs(mode, r)
else:
return 0
else:
return np.array(
[0.0] + [legendre_coeffs(n, r) for n in self.range_1_N_max],
)
[docs] def tangential_Stokes_drift(
self, r: float, theta: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Tangential Stokes drift velocity [m/s]
(Eq. F7)
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode
:return: tangential Stokes drift velocity
"""
if mode is not None:
return Leg.first_cos_monomial(
mode, theta,
self.tangential_Stokes_drift_coefficients(r, mode),
)
else:
return Leg.first_cos_poly(
theta, self.tangential_Stokes_drift_coefficients(r),
)
[docs] def radial_Euler_streaming_coefficients(
self,
r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Radial Euler streaming coefficient [m/s]
(Eq. 47)
:param r: radial coordinate [m]
:param mode: mode
:return: radial Euler streaming coefficient
"""
def legendre_coeffs(l, x):
return self.d_Phi(l, x) - l * (l + 1) / x * self.Psi(l, x)
if mode is not None:
if mode > 0:
return legendre_coeffs(mode, r)
else:
return 0
else:
return np.array(
[0.0] + [legendre_coeffs(n, r) for n in self.range_1_N_max],
)
[docs] def radial_Euler_streaming(
self,
r: float,
theta: float | np.ndarray,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Radial Euler streaming velocity [m/s]
(Eq. 47)
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode
:return: radial Euler streaming velocity
"""
if mode is not None:
return Leg.cos_monomial(
mode, theta,
self.radial_Euler_streaming_coefficients(r, mode),
)
else:
return Leg.cos_poly(
theta, self.radial_Euler_streaming_coefficients(r),
)
[docs] def tangential_Euler_streaming_coefficients(
self,
r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Tangential Euler streaming coefficient [m/s]
(Eq. 48)
:param r: radial coordinate [m]
:param mode: mode
:return: tangential Euler streaming coefficient
"""
def legendre_coeffs(l, x):
return (self.Phi(l, x) - self.Psi(l, x)) / x - self.d_Psi(l, x)
if mode is not None:
if mode > 0:
return legendre_coeffs(mode, r)
else:
return 0
else:
return np.array(
[0.0] + [legendre_coeffs(n, r) for n in self.range_1_N_max],
)
[docs] def tangential_Euler_streaming(
self,
r: float,
theta: float | np.ndarray,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Tangential Euler streaming coefficient [m/s]
(Eq. 48)
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode
:return: tangential Euler streaming coefficients
"""
if mode is not None:
return Leg.first_cos_monomial(
mode, theta,
self.tangential_Euler_streaming_coefficients(r, mode),
)
else:
return Leg.first_cos_poly(
theta, self.tangential_Euler_streaming_coefficients(r),
)
[docs] def radial_Lagrange_streaming_coefficients(
self,
r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Radial Lagrange streaming coefficient [m/s]
(Eq. 51)
:param r: radial coordinate [m]
:param mode: mode
:return: radial Lagrange streaming coefficient
"""
if mode is not None:
return (
self.radial_Euler_streaming_coefficients(r, mode)
+ self.radial_Stokes_drift_coefficients(r, mode)
)
else:
return (
self.radial_Euler_streaming_coefficients(r)
+ self.radial_Stokes_drift_coefficients(r)
)
[docs] def tangential_Lagrange_streaming_coefficients(
self,
r: float,
mode: Optional[int] = None,
) -> float | np.ndarray:
"""Tangential Lagrange streaming coefficient [m/s]
(Eq. 52)
:param r: radial coordinate [m]
:param mode: mode
:return: tangential Lagrange streaming coefficient
"""
if mode is not None:
return (
self.tangential_Euler_streaming_coefficients(r, mode)
+ self.tangential_Stokes_drift_coefficients(r, mode)
)
else:
return (
self.tangential_Euler_streaming_coefficients(r)
+ self.tangential_Stokes_drift_coefficients(r)
)
[docs] def radial_Lagrange_streaming(
self,
r: float,
theta: float | np.ndarray,
mode: Optional[int] = None,
) -> float:
""" Returns the value for the radial Lagrange streaming velocity in
[m/s].
This method must be implemented by every theory to have a common
interface for other modules.
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode, if `None` all modes to `N_max`
"""
if mode is not None:
return (
self.radial_Euler_streaming(r, theta, mode)
+ self.radial_Stokes_drift(r, theta, mode)
)
else:
return (
self.radial_Euler_streaming(r, theta)
+ self.radial_Stokes_drift(r, theta)
)
[docs] def tangential_Lagrange_streaming(
self,
r: float,
theta: float | np.ndarray,
mode: Optional[int] = None,
) -> float:
""" Returns the value for the tangential Lagrange streaming velocity
in [m/s].
This method must be implemented by every theory to have a common
interface for other modules.
:param r: radial coordinate [m]
:param theta: tangential coordinate [rad]
:param mode: mode, if `None` all modes to `N_max`
"""
if mode is not None:
return (
self.tangential_Euler_streaming(r, theta, mode)
+ self.tangential_Stokes_drift(r, theta, mode)
)
else:
return (
self.tangential_Euler_streaming(r, theta)
+ self.tangential_Stokes_drift(r, theta)
)
if __name__ == '__main__':
pass