import warnings
from dataclasses import asdict, dataclass
import numpy as np
#: Default number of heights
N_H = 8
#: Default number of track points
N_D = 600
#: Default number of speeds
N_S = 7
#: Default min speed ratio (relative to the reference speed unit)
S_MIN = 0.6
#: Default max speed ratio (relative to the reference speed unit)
S_MAX = 0.9
#: Default track-point length (in *km*)
TRACK_POINT = 10.0
#: Deprecated alias for :data:`TRACK_POINT`.
WAYPOINT = TRACK_POINT
#: Default reference speed unit (Mach 1 for aircraft, in *km/s*)
UNIT_SPEED = 0.3403
#: Deprecated alias for :data:`UNIT_SPEED`.
MAC = UNIT_SPEED
#: Default speed cost (additional speed cost is :math:`S * s^2`)
SPEED_COST = 50.0
#: Default base cost
CRUISE_COST = 20.0
#: Default upward maneuver cost
UP_COST = 100.0
#: Deprecated alias for :data:`UP_COST`.
CLIMB_COST = UP_COST
#: Default downward maneuver gain
DOWN_GAIN = 25.0
#: Default cost reduction due to height
HEIGHT_GAIN = 3.0
#: Default weight that increases cost by 1
INVERSE_WEIGHT = 4000.0
[docs]
@dataclass
class CostModel:
"""
Energy consumption and time cost(s) depending on various parameters.
All values are expressed in units per track point.
The model is generic and applies to any vehicle with state-dependent energy costs.
"Height" represents any discrete state dimension (altitude, depth, elevation, etc.),
and "weight" represents remaining energy (fuel, battery charge, etc.).
Attributes
----------
timings: :class:`~numpy.ndarray`
Time to travel the track point w.r.t. speed index.
speed_array: :class:`~numpy.ndarray`
Energy cost to travel the track point w.r.t. speed index.
cruise_matrix: :class:`~numpy.ndarray`
Base cost w.r.t. track point and height indices.
up_matrix: :class:`~numpy.ndarray`
Cost to increase height w.r.t. track point and height indices.
down_matrix: :class:`~numpy.ndarray`
Gain from decreasing height w.r.t. track point and height indices.
height_gain: :class:`float`
Cost reduction w.r.t. height index.
weight_cost: :class:`float`
Cost modifier w.r.t. remaining energy (spare weight).
"""
timings: np.ndarray
speed_array: np.ndarray
cruise_matrix: np.ndarray
up_matrix: np.ndarray
down_matrix: np.ndarray
height_gain: float
weight_cost: float
@property
def dict(self):
return asdict(self)
@property
def climb_matrix(self):
warnings.warn(
"`climb_matrix` is deprecated; use `up_matrix`.",
DeprecationWarning,
stacklevel=2,
)
return self.up_matrix
[docs]
class CostRandom(CostModel):
"""
A simple cost model with global parameters and randomized terrain.
Default parameters are tuned for aircraft cruise, but the model
generalizes to any vehicle. Adapt parameters for your domain.
Parameters
----------
n_h: :class:`int`, optional
Number of height/state steps.
n_d: :class:`int`, optional
Number of track points.
n_s: :class:`int`, optional
Number of speed levels.
s_min: :class:`float`, optional
Minimum speed ratio relative to the reference speed unit.
s_max: :class:`float`, optional
Maximum speed ratio relative to the reference speed unit.
track_point: :class:`float`, optional
Track point length (in km).
unit_speed: :class:`float`, optional
Reference speed unit (Mach 1 in km/s for aircraft).
speed_cost: :class:`float`, optional
Energy cost coefficient for speed.
cruise_cost: :class:`float`, optional
Base energy cost per track point.
up_cost: :class:`float`, optional
Energy cost for increasing height/state.
down_gain: :class:`float`, optional
Energy recovered when decreasing height/state.
height_gain: :class:`float`, optional
Efficiency improvement from higher states.
inverse_weight: :class:`float`, optional
Energy level at which cost increases by 1 unit.
waypoint: :class:`float`, optional
Deprecated alias for ``track_point``.
mac: :class:`float`, optional
Deprecated alias for ``unit_speed``.
climb_cost: :class:`float`, optional
Deprecated alias for ``up_cost``.
seed: :class:`int`, optional
Random seed for reproducible terrain generation.
"""
def __init__(
self,
n_h=N_H,
n_d=N_D,
n_s=N_S,
s_min=S_MIN,
s_max=S_MAX,
track_point=TRACK_POINT,
unit_speed=UNIT_SPEED,
speed_cost=SPEED_COST,
cruise_cost=CRUISE_COST,
up_cost=UP_COST,
down_gain=DOWN_GAIN,
height_gain=HEIGHT_GAIN,
inverse_weight=INVERSE_WEIGHT,
seed=None,
*,
waypoint=None,
mac=None,
climb_cost=None,
):
if waypoint is not None:
warnings.warn(
"`waypoint` is deprecated; use `track_point`.",
DeprecationWarning,
stacklevel=2,
)
track_point = waypoint
if mac is not None:
warnings.warn(
"`mac` is deprecated; use `unit_speed`.",
DeprecationWarning,
stacklevel=2,
)
unit_speed = mac
if climb_cost is not None:
warnings.warn(
"`climb_cost` is deprecated; use `up_cost`.",
DeprecationWarning,
stacklevel=2,
)
up_cost = climb_cost
if seed is not None:
np.random.seed(seed)
speeds = np.linspace(s_min, s_max, n_s)
timings = track_point / unit_speed / speeds
speed_array = speed_cost * speeds**2
cruise_matrix = cruise_cost * (0.5 + np.random.rand(n_d, n_h))
up_matrix = up_cost * (0.5 + np.random.rand(n_d, n_h))
down_matrix = down_gain * (0.5 + np.random.rand(n_d, n_h))
super().__init__(
timings=timings,
speed_array=speed_array,
cruise_matrix=cruise_matrix,
up_matrix=up_matrix,
down_matrix=down_matrix,
height_gain=height_gain,
weight_cost=1 / inverse_weight,
)