Source code for zefir.potential.flows.base

"""
Base classes for complex potential flows.

This module provides the foundation for implementing potential flow types
with metadata support for automatic GUI generation.
"""

import numpy as np
from ...typing import Tuple, CplxNDArray, List, FrameType, ParamType

InvalidDerivLevel = ValueError


[docs] class FlowMetadata: """Metadata for a flow type to enable automatic GUI generation. This class stores information about flow parameters including their types, default values, ranges, and labels for GUI widget generation. Parameters ---------- name : str Display name for the flow type in the GUI parameters : dict Dictionary of parameter definitions where keys are parameter names and values are dicts with keys: type, default, min, max, labels (optional) has_position : bool, optional Whether the flow has a position parameter that can be set via mouse click Examples -------- >>> from zefir.typing import ParamType >>> metadata = FlowMetadata( ... "Source Flow", ... { ... "flow_rate": {"type": ParamType.FLOAT, "default": 1.0, "min": -100, "max": 100}, ... "center": {"type": ParamType.VECTOR2, "default": [0.0, 0.0], "min": -10, "max": 10} ... }, ... has_position=True ... ) """
[docs] def __init__( self, name: str, parameters: dict, has_position: bool = False ): self.name = name self.parameters = parameters # dict of param_name -> {type, default, min, max} self.has_position = has_position
[docs] class ComplexPotential: """Base class for complex potential flows with GUI metadata support. All potential flow classes should inherit from this base class. It provides common functionality for evaluating the complex potential and velocity field, along with parameter management for GUI integration. Attributes ---------- metadata : FlowMetadata Metadata describing the flow type and its parameters Methods ------- __call__(z, deriv=0) Evaluate the complex potential at points z velocity(z, frame=CARTESIAN) Compute the complex velocity at points z get_parameters() Get current parameter values as a dictionary update_parameters(params) Update flow parameters from a dictionary Examples -------- >>> from zefir.typing import ParamType >>> class MyFlow(ComplexPotential): ... metadata = FlowMetadata("My Flow", {"param": {"type": ParamType.FLOAT, "default": 1.0}}) ... def __init__(self, param=1.0): ... self._param = param ... def __call__(self, z, deriv=0): ... return self._param * z """ metadata = FlowMetadata("ComplexPotential", {})
[docs] def __init__(self): pass
[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 potential Returns ------- CplxNDArray Complex potential values at the given points Raises ------ InvalidDerivLevel If deriv is not 0 or 1 """ return 0.*z
[docs] def velocity( self, z:CplxNDArray, frame:FrameType=FrameType.CARTESIAN) -> CplxNDArray: """Returns the complex velocity field. If the requested frame is the cartesian one, vx +i vy is returned. If it is cylindrical, vr +i vtheta is returned. Parameters ---------- z : CplxNDArray Complex array of points where velocity is evaluated frame : FrameType, optional Coordinate frame: CARTESIAN (vx + i*vy) or CYLINDRICAL (vr + i*vtheta) Returns ------- CplxNDArray Complex velocity values at the given points Examples -------- >>> flow = UniformFlow((1.0, 0.0)) >>> z = np.array([0+0j, 1+0j, 0+1j]) >>> v = flow.velocity(z) >>> v array([1.+0.j, 1.+0.j, 1.+0.j]) """ dfdz = self(z, deriv=1) if frame == FrameType.CYLINDRICAL: dfdz *= np.exp(np.angle(z)) return np.conjugate(dfdz)
[docs] def get_parameters(self) -> dict: """Get current parameter values as a dictionary. Returns ------- dict Dictionary mapping parameter names to their current values Examples -------- >>> flow = UniformFlow((2.0, 1.0)) >>> flow.get_parameters() {'infinite_velocity': [2.0, 1.0]} """ params = {} for param_name in self.metadata.parameters.keys(): if hasattr(self, f'_{param_name}'): val = getattr(self, f'_{param_name}') if isinstance(val, complex): params[param_name] = [val.real, val.imag] else: params[param_name] = val return params
[docs] def update_parameters(self, params: dict): """Update flow parameters from a dictionary. Parameters ---------- params : dict Dictionary of parameter values to update Examples -------- >>> flow = UniformFlow((1.0, 0.0)) >>> flow.update_parameters({'infinite_velocity': [2.0, 1.0]}) """ for param_name, value in params.items(): if hasattr(self, f'_{param_name}'): if param_name == 'center' or param_name == 'infinite_velocity': if isinstance(value, (list, tuple)): value = complex(*value) setattr(self, f'_{param_name}', value)