Source code for vivyd.models.facchinetti_delangre_biolley

from ..typing import arrf64
from .viv_model import VIVModel
from .generalized_model import GeneralizedModel

from dataclasses import dataclass
from numpy import array, pi


[docs] @dataclass(kw_only=True, slots=True) class FacchinettiDelangreBiolleyModel(VIVModel): """ Facchinetti, Delangre and Biolley (2004) wake-oscillator model for vortex-induced vibrations on circular cylinders :cite:`facchinetti2004`. By identifying the lift and transverse drag forces, the mechanical equation of motion of the cylinder is written as .. math:: \\ddot{y} + \\left(\\frac{c}{m} + \\frac{\\gamma}{\\mu} \\omega_s \\right) \\dot{y} + \\frac{k}{m} y = M \\omega_s^2 q, and the equation of the wake is written as .. math:: \\ddot{q} + \\varepsilon \\omega_s (q^2 - 1) \\dot{q} + \\omega_s^2 q = \\frac{A}{D} \\ddot{y}, with .. math:: \\omega_s = 2 \\pi \\text{St} \\frac{U_\\infty}{D}. Parameters ---------- diam : float, optional Diameter of the cylinder, :math:`D`. Default is 0.18 m. St : float, optional Strouhal number, :math:`\\text{St}`. Default is 0.18. c_m : float, optional Specific mechanical damping, :math:`c/m`. Default is 0.05. k_m : float, optional Specific mechanical stiffness, :math:`k/m`. Default is :math:`4 \\pi^2`. m_lin : float, optional Mass per unit length of the cylinder, :math:`m_\\text{lin}`. Default is 25.0 kg/m. rho : float, optional Density of the fluid, :math:`\\rho`. Default is 1.25 kg/m³. u_inf : float, optional Free-stream velocity, :math:`U_\\infty`. Default is 1.0 m/s. Cm : float, optional Added mass coefficient, :math:`C_M`. Default is 1.0. Cd : float, optional Drag coefficient, :math:`C_D`. Default is 1.2. Cl0 : float, optional Lift coefficient at zero angle of attack, :math:`C_{L 0}`. Default is 0.4. eps : float, optional Wake nonlinearity parameter, :math:`\\varepsilon`. Default is 0.3. A : float, optional Structure-to-wake coupling parameter, :math:`A`. Default is 12.0. """ state_size = 4 diam : float = 0.18 """Diameter of the cylinder, :math:`D`.""" St : float = 0.18 """Strouhal number, :math:`\\text{St}`.""" c_m : float = 0.05 """Specific mechanical damping, :math:`c/m`.""" k_m : float = 4.0 * pi**2 """Specific mechanical stiffness, :math:`k/m`.""" m_lin: float = 25.0 """Mass per unit length of the cylinder, :math:`m_\\text{lin}`.""" rho : float = 1.25 """Density of the fluid, :math:`\\rho`.""" u_inf: float = 1.0 """Free-stream velocity, :math:`U_\\infty`.""" Cm : float = 1.0 """Added mass coefficient, :math:`C_M`.""" Cd : float = 1.2 """Drag coefficient, :math:`C_D`.""" Cl0 : float = 0.4 """Lift coefficient at zero angle of attack, :math:`C_{L 0}`.""" eps : float = 0.3 """Wake nonlinearity parameter, :math:`\\varepsilon`.""" A : float = 12.0 """Structure-to-wake coupling parameter, :math:`A`.""" @property def St_star(self) -> float: """Angular Strouhal number, :math:`\\text{St}^* = 2 \\pi \\text{St}`.""" return 2.0 * pi * self.St @property def ws(self) -> float: """Strouhal's vortex shedding pulsation, :math:`\\omega_s = 2 \\pi \\text{St} \\dfrac{U_\\infty}{D}`.""" return self.St_star * self.u_inf / self.diam @property def gamma(self) -> float: """Drag-induced wake damping coefficient, :math:`\\gamma = C_D / (2 \\text{St}^*)`.""" return self.Cd / (2.0 * self.St_star) @property def m_fluid(self) -> float: """Mass of the fluid displaced by the cylinder, :math:`m_\\text{fluid} = C_M \\dfrac{\\rho \\pi D^2}{4}`.""" return self.Cm * self.rho * pi * self.diam**2 / 4.0 @property def mu(self) -> float: """Mass ratio, :math:`\\mu = \\dfrac{m_\\text{lin} + m_\\text{fluid}}{\\rho D^2}`.""" return (self.m_lin + self.m_fluid) / (self.rho * self.diam**2) @property def M(self) -> float: """Structure-wake coupling coefficient, :math:`M = \\dfrac{C_{L 0}}{4 \\text{St}^{*2} \\mu}`.""" return self.Cl0 / (4.0 * self.St_star**2 * self.mu)
[docs] def rhs(self, t: float, state: arrf64, **kwargs) -> arrf64: """ Compute the right-hand side of the system of ODEs. Args: t: Time. state: State vector indexed [y_dot, y, q_dot, q]. Returns: arrf64: Derivatives of the state vector indexed [dy_dot, dy, dq_dot, dq]. """ self.validate_state(state) y_dot, y, q_dot, q = state lhs = (self.c_m + self.gamma/self.mu * self.ws) * y_dot + self.k_m * y rhs = self.M * self.ws**2 * self.diam * q dy_dot = rhs - lhs dy = y_dot lhs = self.eps * self.ws * (q**2 - 1.0) * q_dot + self.ws**2 * q rhs = self.A / self.diam * dy_dot dq_dot = rhs - lhs dq = q_dot return array([dy_dot, dy, dq_dot, dq])
@property def generalized_params(self) -> dict[str, float]: """ A dictionary containing the parameters of the model in a format compatible with the constructor of :class:`GeneralizedModel`. """ return { "c_m" : self.c_m, "k_m" : self.k_m, "ca_m" : self.gamma/self.mu * self.ws, "B0" : self.M * self.ws**2 * self.diam, "gamma": self.eps * self.ws, "a" : 1.0, "kappa": self.ws**2, "A2" : self.A / self.diam }
[docs] def to_generalized(self) -> GeneralizedModel: """ Returns ------- GeneralizedModel A generalized model instance equivalent to the present model. """ return GeneralizedModel(**self.generalized_params)
[docs] def to_taichi(self) -> GeneralizedModel: """ Returns ------- GeneralizedModel A Taichi-compatible generalized model instance equivalent to the present model. """ return self.to_generalized()