Source code for zefir.potential.flows.doublet

"""
Doublet flow implementation.

This module provides the DoubletFlow class representing a doublet (dipole) flow
pattern commonly used to model flow around cylinders.
"""

import numpy as np
from ...typing import Tuple, CplxNDArray, ParamType
from .base import ComplexPotential, FlowMetadata, InvalidDerivLevel


[docs] class DoubletFlow(ComplexPotential): """A doublet flow defined by its intensity and center. A doublet (or dipole) flow represents the limiting case of a source and sink of equal strength brought infinitely close together. The complex potential is: .. math:: f(z) = \\frac{K}{2\\pi} \\frac{1}{z - z_0} where :math:`K` is the intensity and :math:`z_0` is the center position. Parameters ---------- intensity : float Doublet intensity K center : Tuple[float, float] or complex Position (x, y) or complex number representing the center Attributes ---------- _center : complex Complex representation of the center position _intensity : float Scaled intensity (intensity / 2π) _original_intensity : float Original intensity value Examples -------- >>> flow = DoubletFlow(1.0, (0.0, 0.0)) >>> z = np.array([1+0j, 2+0j, 0+1j]) >>> potential = flow(z) >>> velocity = flow.velocity(z) """ metadata = FlowMetadata( "Doublet", { "intensity": { "type": ParamType.FLOAT, "default": 1.0, "min": -100, "max": 100 }, "center": { "type": ParamType.VECTOR2, "default": [0.0, 0.0], "min": -10, "max": 10, "labels": ["X center", "Y center"] } }, has_position=True )
[docs] def __init__(self, intensity:float, center:Tuple[float, float]|complex): if isinstance(center, complex): self._center = center else: self._center = complex(*center) self._intensity = intensity/(2*np.pi) self._original_intensity = intensity
[docs] def __call__( self, z: CplxNDArray, deriv:int=0) -> CplxNDArray: """Evaluate the complex potential or its derivative. Parameters ---------- z : CplxNDArray Complex array of points where the potential is evaluated deriv : int, optional Derivative level: 0 for potential, 1 for velocity Returns ------- CplxNDArray Complex potential (deriv=0) or velocity (deriv=1) Raises ------ InvalidDerivLevel If deriv is not 0 or 1 """ if deriv == 0: return self._intensity/(z-self._center) elif deriv == 1: return -self._intensity/(z-self._center)**2 else: raise InvalidDerivLevel()
[docs] def update_parameters(self, params: dict): """Update flow parameters from a dictionary. Parameters ---------- params : dict Dictionary with keys "intensity" and/or "center" """ for param_name, value in params.items(): if param_name == "intensity": self._intensity = value / (2 * np.pi) self._original_intensity = value elif param_name == "center": if isinstance(value, (list, tuple)): value = complex(*value) self._center = value elif hasattr(self, f'_{param_name}'): setattr(self, f'_{param_name}', value)
[docs] def get_parameters(self) -> dict: """Get current parameter values as a dictionary. Returns ------- dict Dictionary with "intensity" and "center" keys """ return { "intensity": self._original_intensity, "center": [self._center.real, self._center.imag] }