"""Public factory helpers for schema-driven airfoil construction."""
from __future__ import annotations
from typing import Literal
from .airfoil import Airfoil
from .airfoil_schema import (
AirfoilDefinitionSpec,
Naca4AirfoilParamsSpec,
Naca4AirfoilSpec,
Naca4ModifiedAirfoilParamsSpec,
Naca4ModifiedAirfoilSpec,
Naca5AirfoilParamsSpec,
Naca5AirfoilSpec,
Naca5ModifiedAirfoilParamsSpec,
Naca5ModifiedAirfoilSpec,
)
from .naca4_airfoil import Naca4AirfoilClassic, Naca4AirfoilParams
from .naca4_modified_airfoil import (
Naca4ModifiedAirfoilClassic,
Naca4ModifiedAirfoilParams,
)
from .naca5_airfoil import Naca5AirfoilClassic, Naca5AirfoilParams
from .naca5_modified_airfoil import (
Naca5ModifiedAirfoilClassic,
Naca5ModifiedAirfoilParams,
)
[docs]
class AirfoilFactory:
"""
Construct public airfoil runtime objects from user-facing specs.
This factory centralizes the mapping from serialized airfoil schema
objects to the concrete runtime classes used by the geometry library.
"""
[docs]
@staticmethod
def from_spec(spec: AirfoilDefinitionSpec) -> Airfoil:
"""
Build an airfoil object from a schema definition.
Parameters
----------
spec : AirfoilDefinitionSpec
Serialized airfoil definition to convert into a runtime object.
Returns
-------
Airfoil
Runtime airfoil instance corresponding to ``spec``.
Raises
------
ValueError
If a NACA specification does not provide exactly one of
``designation`` or ``params``.
NotImplementedError
If the airfoil type is not yet supported by the factory.
"""
if isinstance(spec, Naca4AirfoilSpec):
if spec.designation is not None and spec.params is None:
return Naca4AirfoilClassic(spec)
if spec.designation is None and spec.params is not None:
return Naca4AirfoilParams(spec)
raise ValueError(
"NACA 4-digit specs must provide exactly one of designation "
"or params."
)
if isinstance(spec, Naca4ModifiedAirfoilSpec):
if spec.designation is not None and spec.params is None:
return Naca4ModifiedAirfoilClassic(spec)
if spec.designation is None and spec.params is not None:
return Naca4ModifiedAirfoilParams(spec)
raise ValueError(
"Modified NACA 4-digit specs must provide exactly one of "
"designation or params."
)
if isinstance(spec, Naca5AirfoilSpec):
if spec.designation is not None and spec.params is None:
return Naca5AirfoilClassic(spec)
if spec.designation is None and spec.params is not None:
return Naca5AirfoilParams(spec)
raise ValueError(
"NACA 5-digit specs must provide exactly one of designation "
"or params."
)
if isinstance(spec, Naca5ModifiedAirfoilSpec):
if spec.designation is not None and spec.params is None:
return Naca5ModifiedAirfoilClassic(spec)
if spec.designation is None and spec.params is not None:
return Naca5ModifiedAirfoilParams(spec)
raise ValueError(
"Modified NACA 5-digit specs must provide exactly one of "
"designation or params."
)
raise NotImplementedError(
f"AirfoilFactory does not support {type(spec).__name__} yet."
)
[docs]
@staticmethod
def naca4_from_designation(
designation: str,
) -> Naca4AirfoilClassic:
"""
Build a classic NACA 4-digit airfoil from a designation.
Parameters
----------
designation : str
Four-digit NACA designation such as ``"2412"``.
Returns
-------
Naca4AirfoilClassic
Runtime airfoil built from the classic designation.
Raises
------
ValueError
If ``designation`` is not a valid 4-digit NACA code.
"""
return Naca4AirfoilClassic.from_designation(designation)
[docs]
@staticmethod
def naca4_from_params(
*,
m: float,
p: float,
t: float,
trailing_edge: Literal["standard", "sharp"] = "standard",
leading_edge_radius: Literal["standard", "exact"] = "standard",
) -> Naca4AirfoilParams:
"""
Build a parametric NACA 4-digit airfoil from explicit params.
Parameters
----------
m : float
Maximum camber as a fraction of chord.
p : float
Chordwise location of maximum camber as a fraction of chord.
t : float
Maximum thickness as a fraction of chord.
trailing_edge : {"standard", "sharp"}, default="standard"
Trailing-edge closure model for the thickness distribution.
leading_edge_radius : {"standard", "exact"}, default="standard"
Leading-edge radius treatment for the thickness distribution.
Returns
-------
Naca4AirfoilParams
Runtime airfoil built from the explicit parameters.
Raises
------
ValueError
If any parameter lies outside the supported NACA 4-digit range.
"""
params = Naca4AirfoilParamsSpec(
m=m,
p=p,
t=t,
trailing_edge=trailing_edge,
leading_edge_radius=leading_edge_radius,
)
return Naca4AirfoilParams(Naca4AirfoilSpec(params=params))
[docs]
@staticmethod
def naca4_modified_from_designation(
designation: str,
) -> Naca4ModifiedAirfoilClassic:
"""
Build a classic modified NACA 4-digit airfoil from a designation.
Parameters
----------
designation : str
Modified NACA 4-digit designation such as ``"0003-64"``.
Returns
-------
Naca4ModifiedAirfoilClassic
Runtime airfoil built from the modified-series designation.
Raises
------
ValueError
If ``designation`` is not a valid modified NACA 4-digit code.
"""
return Naca4ModifiedAirfoilClassic.from_designation(designation)
[docs]
@staticmethod
def naca4_modified_from_params(
*,
m: float,
p: float,
t: float,
leading_edge_index: float,
max_thickness_location: float,
trailing_edge: Literal["standard", "sharp"] = "standard",
) -> Naca4ModifiedAirfoilParams:
"""
Build a parametric modified NACA 4-digit airfoil from params.
Parameters
----------
m : float
Maximum camber as a fraction of chord.
p : float
Chordwise location of maximum camber as a fraction of chord.
t : float
Maximum thickness as a fraction of chord.
leading_edge_index : float
Modified-series leading-edge shape index.
max_thickness_location : float
Chordwise location of maximum thickness as a fraction of chord.
trailing_edge : {"standard", "sharp"}, default="standard"
Trailing-edge closure model for the thickness distribution.
Returns
-------
Naca4ModifiedAirfoilParams
Runtime airfoil built from the explicit parameters.
Raises
------
ValueError
If any parameter lies outside the supported modified-series
range.
"""
params = Naca4ModifiedAirfoilParamsSpec(
m=m,
p=p,
t=t,
leading_edge_index=leading_edge_index,
max_thickness_location=max_thickness_location,
trailing_edge=trailing_edge,
)
return Naca4ModifiedAirfoilParams(
Naca4ModifiedAirfoilSpec(params=params)
)
[docs]
@staticmethod
def naca5_from_designation(
designation: str,
) -> Naca5AirfoilClassic:
"""
Build a classic NACA 5-digit airfoil from a designation.
Parameters
----------
designation : str
Five-digit NACA designation such as ``"23012"``.
Returns
-------
Naca5AirfoilClassic
Runtime airfoil built from the classic designation.
Raises
------
ValueError
If ``designation`` is not a valid 5-digit NACA code.
"""
return Naca5AirfoilClassic.from_designation(designation)
[docs]
@staticmethod
def naca5_from_params(
*,
ideal_lift_coefficient: float,
max_camber_location: float,
reflexed: bool,
t: float,
trailing_edge: Literal["standard", "sharp"] = "standard",
leading_edge_radius: Literal["standard", "exact"] = "standard",
) -> Naca5AirfoilParams:
"""
Build a parametric NACA 5-digit airfoil from explicit params.
Parameters
----------
ideal_lift_coefficient : float
Design lift coefficient associated with the camber line.
max_camber_location : float
Chordwise location of maximum camber as a fraction of chord.
reflexed : bool
Whether to build the reflexed camber-line family.
t : float
Maximum thickness as a fraction of chord.
trailing_edge : {"standard", "sharp"}, default="standard"
Trailing-edge closure model for the thickness distribution.
leading_edge_radius : {"standard", "exact"}, default="standard"
Leading-edge radius treatment for the thickness distribution.
Returns
-------
Naca5AirfoilParams
Runtime airfoil built from the explicit parameters.
Raises
------
ValueError
If any parameter lies outside the supported NACA 5-digit range.
"""
params = Naca5AirfoilParamsSpec(
ideal_lift_coefficient=ideal_lift_coefficient,
max_camber_location=max_camber_location,
reflexed=reflexed,
t=t,
trailing_edge=trailing_edge,
leading_edge_radius=leading_edge_radius,
)
return Naca5AirfoilParams(Naca5AirfoilSpec(params=params))
[docs]
@staticmethod
def naca5_modified_from_designation(
designation: str,
) -> Naca5ModifiedAirfoilClassic:
"""
Build a classic modified NACA 5-digit airfoil from a designation.
Parameters
----------
designation : str
Modified NACA 5-digit designation string.
Returns
-------
Naca5ModifiedAirfoilClassic
Runtime airfoil built from the modified-series designation.
Raises
------
ValueError
If ``designation`` is not a valid modified NACA 5-digit code.
"""
return Naca5ModifiedAirfoilClassic.from_designation(designation)
[docs]
@staticmethod
def naca5_modified_from_params(
*,
ideal_lift_coefficient: float,
max_camber_location: float,
reflexed: bool,
t: float,
leading_edge_index: float,
max_thickness_location: float,
trailing_edge: Literal["standard", "sharp"] = "standard",
) -> Naca5ModifiedAirfoilParams:
"""
Build a parametric modified NACA 5-digit airfoil from params.
Parameters
----------
ideal_lift_coefficient : float
Design lift coefficient associated with the camber line.
max_camber_location : float
Chordwise location of maximum camber as a fraction of chord.
reflexed : bool
Whether to build the reflexed camber-line family.
t : float
Maximum thickness as a fraction of chord.
leading_edge_index : float
Modified-series leading-edge shape index.
max_thickness_location : float
Chordwise location of maximum thickness as a fraction of chord.
trailing_edge : {"standard", "sharp"}, default="standard"
Trailing-edge closure model for the thickness distribution.
Returns
-------
Naca5ModifiedAirfoilParams
Runtime airfoil built from the explicit parameters.
Raises
------
ValueError
If any parameter lies outside the supported modified-series
range.
"""
params = Naca5ModifiedAirfoilParamsSpec(
ideal_lift_coefficient=ideal_lift_coefficient,
max_camber_location=max_camber_location,
reflexed=reflexed,
t=t,
leading_edge_index=leading_edge_index,
max_thickness_location=max_thickness_location,
trailing_edge=trailing_edge,
)
return Naca5ModifiedAirfoilParams(
Naca5ModifiedAirfoilSpec(params=params)
)
__all__ = ["AirfoilFactory"]