"""
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 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)