Source code for pterasoftware.free_flight_unsteady_ring_vortex_lattice_method
"""Contains the FreeFlightUnsteadyRingVortexLatticeMethodSolver class.
**Contains the following classes:**
FreeFlightUnsteadyRingVortexLatticeMethodSolver: A subclass of
CoupledUnsteadyRingVortexLatticeMethodSolver that solves FreeFlightUnsteadyProblems,
contributing the body angular rate (omega cross r) to the apparent velocity at every
evaluation point so that the inherited unsteady ring vortex lattice method models the
six-degree-of-freedom motion that the coupled MuJoCo dynamics produce.
**Contains the following functions:**
None
"""
from __future__ import annotations
from typing import cast
import numpy as np
from . import problems
from ._coupled_unsteady_ring_vortex_lattice_method import (
CoupledUnsteadyRingVortexLatticeMethodSolver,
)
# Body and geometry axes differ by a 180-degree rotation about y, so transforming a free
# vector (such as an angular velocity) from the first Airplane's body axes to its geometry
# axes negates the x and z components.
_BP1_TO_GP1_FLIP = np.array([-1.0, 1.0, -1.0], dtype=float)
_BP1_TO_GP1_FLIP.flags.writeable = False
[docs]
class FreeFlightUnsteadyRingVortexLatticeMethodSolver(
CoupledUnsteadyRingVortexLatticeMethodSolver
):
"""A subclass of CoupledUnsteadyRingVortexLatticeMethodSolver that solves
FreeFlightUnsteadyProblems.
In a FreeFlightUnsteadyProblem the body state at each time step comes from the
coupled MuJoCo rigid-body dynamics (carried out in FreeFlightUnsteadyProblem's
initialize_next_problem), so each step's OperatingPoint carries a body angular rate
(omegas_BP1__E) that the standard solver assumes is zero. This solver contributes
the apparent velocity from that body rate (omega cross r) at every collocation point
and bound line vortex leg center by overriding _currentOmegasRad_GP1__E, which the
inherited velocity calculations feed through _apply_body_rate.
**Key additions over parent CoupledUnsteadyRingVortexLatticeMethodSolver:** sets
_models_body_rates to True so the inherited constructor permits non-zero body rates,
and overrides _currentOmegasRad_GP1__E to supply the current OperatingPoint's body
angular rate (in the first Airplane's geometry axes, in radians per second).
"""
__slots__ = ()
_models_body_rates = True
def __init__(
self,
free_flight_unsteady_problem: problems.FreeFlightUnsteadyProblem,
) -> None:
"""The initialization method.
:param free_flight_unsteady_problem: The FreeFlightUnsteadyProblem to be solved.
:return: None
"""
if not isinstance(
free_flight_unsteady_problem, problems.FreeFlightUnsteadyProblem
):
raise TypeError(
"free_flight_unsteady_problem must be a FreeFlightUnsteadyProblem."
)
super().__init__(free_flight_unsteady_problem)
@property
def _free_flight_unsteady_problem(self) -> problems.FreeFlightUnsteadyProblem:
"""The solver's FreeFlightUnsteadyProblem, narrowed from the inherited
unsteady_problem.
The inherited unsteady_problem slot is typed as the base CoreUnsteadyProblem so
the parent solver can hold any coupled problem. This solver's constructor only
accepts a FreeFlightUnsteadyProblem, so the cast here is safe.
:return: This solver's FreeFlightUnsteadyProblem.
"""
return cast(problems.FreeFlightUnsteadyProblem, self.unsteady_problem)
def _currentOmegasRad_GP1__E(self) -> np.ndarray:
"""Finds the current time step's body angular velocity (in the first Airplane's
geometry axes, observed from the Earth frame).
**Notes:**
The current OperatingPoint stores the body angular rate in the first Airplane's
body axes, in degrees per second. This method transforms it to the first
Airplane's geometry axes and converts it to radians per second for the omega
cross r calculation in _apply_body_rate.
:return: A (3,) ndarray of floats representing the body angular velocity (in the
first Airplane's geometry axes, observed from the Earth frame). Its units
are in radians per second.
"""
omegas_GP1__E = self.current_operating_point.omegas_BP1__E * _BP1_TO_GP1_FLIP
return cast(np.ndarray, np.deg2rad(omegas_GP1__E))