Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions aviary/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,10 @@
)
from aviary.utils.options import list_options
from aviary.constants import (
GRAV_ENGLISH_FLOPS,
GRAV_ENGLISH_GASP,
GRAV_ENGLISH_LBM,
GRAV_METRIC_FLOPS,
GRAV_METRIC_GASP,
PSLS_PSF,
RADIUS_EARTH_METRIC,
RHO_SEA_LEVEL_ENGLISH,
RHO_SEA_LEVEL_METRIC,
TSLS_DEGR,
Expand Down
4 changes: 1 addition & 3 deletions aviary/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
) # Venus Global Reference Atmospheric Model (Venus-GRAM): User Guide, NASA/TM-20210022168

RADIUS_EARTH = (6371009, 'm') # Source: GRS80, mean earth radius (rounded to nearest meter)
# convert_geopotential_altitude() is a python utility function that require RADIUS_EARTH to be specified in meters!

RADIUS_MARS = (
3386200,
Expand All @@ -28,9 +29,7 @@

# GNS = 9.8236930 # grav_accel_at_surface_earth # TODO: Remove this from other parts of Aviary
GRAV_METRIC_GASP = 9.81 # m/s^2
GRAV_METRIC_FLOPS = 9.80665 # m/s^2
GRAV_ENGLISH_GASP = 32.2 # ft/s^2
GRAV_ENGLISH_FLOPS = 32.17399 # ft/s^2
GRAV_ENGLISH_LBM = 1.0 # lbf/lbm
# See issue 1169 for the value of RHO_SEA_LEVEL_ENGLISH
RHO_SEA_LEVEL_ENGLISH = 0.0023769 # slug/ft^3
Expand All @@ -39,4 +38,3 @@
PSLS_PSF = 2116.22
# sea level standard temperature in deg R
TSLS_DEGR = 518.67
RADIUS_EARTH_METRIC = 6367533.0 # m (meridional)
124 changes: 42 additions & 82 deletions aviary/mission/energy_state/ode/landing_eom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,36 @@
import numpy as np
import openmdao.api as om

from aviary.constants import GRAV_METRIC_FLOPS as grav_metric
from aviary.mission.energy_state.ode.takeoff_eom import (
Accelerations,
DistanceRates,
FlightPathAngleRate,
VelocityRate,
)
from aviary.utils.aviary_values import AviaryValues
from aviary.variable_info.functions import add_aviary_input
from aviary.variable_info.functions import add_aviary_input, add_aviary_option
from aviary.variable_info.variables import Dynamic, Mission


class FlareEOM(om.Group):
"""Define a group for calculating equations of motion from start of flare to touchdown."""

def initialize(self):
options = self.options

options.declare('num_nodes', default=1, types=int, lower=0)

options.declare(
'aviary_options',
types=AviaryValues,
desc='collection of Aircraft/Mission specific options',
)
self.options.declare('num_nodes', default=1, types=int, lower=0)

def setup(self):
options = self.options

nn = options['num_nodes']
aviary_options = options['aviary_options']

kwargs = {'num_nodes': nn, 'climbing': True}
nn = self.options['num_nodes']

inputs = [Dynamic.Mission.FLIGHT_PATH_ANGLE, Dynamic.Mission.VELOCITY]
outputs = [Dynamic.Mission.DISTANCE_RATE, Dynamic.Mission.ALTITUDE_RATE]

self.add_subsystem(
'distance_rates',
DistanceRates(**kwargs),
DistanceRates(num_nodes=nn, climbing=True),
promotes_inputs=inputs,
promotes_outputs=outputs,
)

kwargs = {'num_nodes': nn, 'aviary_options': aviary_options}

inputs = [
Dynamic.Vehicle.MASS,
Dynamic.Vehicle.LIFT,
Expand All @@ -61,7 +45,10 @@ def setup(self):
outputs = ['forces_horizontal', 'forces_vertical']

self.add_subsystem(
'sum_forces', FlareSumForces(**kwargs), promotes_inputs=inputs, promotes_outputs=outputs
'sum_forces',
FlareSumForces(num_nodes=nn),
promotes_inputs=inputs,
promotes_outputs=outputs,
)

inputs = ['forces_horizontal', 'forces_vertical', Dynamic.Vehicle.MASS]
Expand Down Expand Up @@ -120,7 +107,7 @@ def setup(self):

self.add_subsystem(
'glide_slope_forces',
GlideSlopeForces(**kwargs),
GlideSlopeForces(num_nodes=nn),
promotes_inputs=inputs,
promotes_outputs=outputs,
)
Expand All @@ -145,27 +132,19 @@ class GlideSlopeForces(om.ExplicitComponent):
"""Define a component for calculating forces for evaluation of glide slope criteria."""

def initialize(self):
options = self.options
self.options.declare('num_nodes', default=1, types=int, lower=0)

options.declare('num_nodes', default=1, types=int, lower=0)

options.declare(
'aviary_options',
types=AviaryValues,
desc='collection of Aircraft/Mission specific options',
)
add_aviary_option(self, Mission.GRAVITY, units='m/s**2')
add_aviary_option(self, Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, units='rad')
add_aviary_option(self, Mission.Takeoff.THRUST_INCIDENCE, units='rad')

def setup(self):
options = self.options

nn = options['num_nodes']
nn = self.options['num_nodes']

add_aviary_input(self, Dynamic.Vehicle.MASS, shape=nn, units='kg')
add_aviary_input(self, Dynamic.Vehicle.LIFT, shape=nn, units='N')
add_aviary_input(self, Dynamic.Vehicle.DRAG, shape=nn, units='N')

add_aviary_input(self, Dynamic.Vehicle.ANGLE_OF_ATTACK, shape=nn, units='rad')

add_aviary_input(self, Dynamic.Mission.FLIGHT_PATH_ANGLE, shape=nn, units='rad')

self.add_output(
Expand All @@ -184,21 +163,16 @@ def setup(self):
)

def setup_partials(self):
options = self.options

nn = options['num_nodes']
nn = self.options['num_nodes']

rows_cols = np.arange(nn)

self.declare_partials('*', '*', rows=rows_cols, cols=rows_cols)

def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
options = self.options

aviary_options: AviaryValues = options['aviary_options']

alpha0 = aviary_options.get_val(Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, 'rad')
t_inc = aviary_options.get_val(Mission.Takeoff.THRUST_INCIDENCE, 'rad')
alpha0 = self.options[Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY][0]
t_inc = self.options[Mission.Takeoff.THRUST_INCIDENCE][0]
grav_metric = self.options[Mission.GRAVITY][0]

mass = inputs[Dynamic.Vehicle.MASS]
lift = inputs[Dynamic.Vehicle.LIFT]
Expand Down Expand Up @@ -230,12 +204,9 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
outputs['required_thrust'] = (f_h + f_v) / (2.0)

def compute_partials(self, inputs, J, discrete_inputs=None):
options = self.options

aviary_options: AviaryValues = options['aviary_options']

alpha0 = aviary_options.get_val(Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, 'rad')
t_inc = aviary_options.get_val(Mission.Takeoff.THRUST_INCIDENCE, 'rad')
alpha0 = self.options[Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY][0]
t_inc = self.options[Mission.Takeoff.THRUST_INCIDENCE][0]
grav_metric = self.options[Mission.GRAVITY][0]

mass = inputs[Dynamic.Vehicle.MASS]
lift = inputs[Dynamic.Vehicle.LIFT]
Expand Down Expand Up @@ -302,28 +273,20 @@ class FlareSumForces(om.ExplicitComponent):
"""

def initialize(self):
options = self.options
self.options.declare('num_nodes', default=1, types=int, lower=0)

options.declare('num_nodes', default=1, types=int, lower=0)

options.declare(
'aviary_options',
types=AviaryValues,
desc='collection of Aircraft/Mission specific options',
)
add_aviary_option(self, Mission.GRAVITY, units='m/s**2')
add_aviary_option(self, Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, units='rad')
add_aviary_option(self, Mission.Takeoff.THRUST_INCIDENCE, units='rad')

def setup(self):
options = self.options

nn = options['num_nodes']
nn = self.options['num_nodes']

add_aviary_input(self, Dynamic.Vehicle.MASS, shape=nn, units='kg')
add_aviary_input(self, Dynamic.Vehicle.LIFT, shape=nn, units='N')
add_aviary_input(self, Dynamic.Vehicle.Propulsion.THRUST_TOTAL, shape=nn, units='N')
add_aviary_input(self, Dynamic.Vehicle.DRAG, shape=nn, units='N')

add_aviary_input(self, Dynamic.Vehicle.ANGLE_OF_ATTACK, shape=nn, units='rad')

add_aviary_input(self, Dynamic.Mission.FLIGHT_PATH_ANGLE, shape=nn, units='rad')

self.add_output(
Expand All @@ -341,9 +304,9 @@ def setup(self):
)

def setup_partials(self):
options = self.options
nn = self.options['num_nodes']

nn = options['num_nodes']
grav_metric = self.options[Mission.GRAVITY][0]

rows_cols = np.arange(nn)

Expand All @@ -368,12 +331,9 @@ def setup_partials(self):
self.declare_partials('*', wrt, rows=rows_cols, cols=rows_cols)

def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
options = self.options

aviary_options: AviaryValues = options['aviary_options']

alpha0 = aviary_options.get_val(Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, 'rad')
t_inc = aviary_options.get_val(Mission.Takeoff.THRUST_INCIDENCE, 'rad')
alpha0 = self.options[Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY][0]
t_inc = self.options[Mission.Takeoff.THRUST_INCIDENCE][0]
grav_metric = self.options[Mission.GRAVITY][0]

mass = inputs[Dynamic.Vehicle.MASS]
lift = inputs[Dynamic.Vehicle.LIFT]
Expand Down Expand Up @@ -406,12 +366,8 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
outputs['forces_vertical'] = f_v

def compute_partials(self, inputs, J, discrete_inputs=None):
options = self.options

aviary_options: AviaryValues = options['aviary_options']

alpha0 = aviary_options.get_val(Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY, 'rad')
t_inc = aviary_options.get_val(Mission.Takeoff.THRUST_INCIDENCE, 'rad')
alpha0 = self.options[Mission.Takeoff.ANGLE_OF_ATTACK_RUNWAY][0]
t_inc = self.options[Mission.Takeoff.THRUST_INCIDENCE][0]

lift = inputs[Dynamic.Vehicle.LIFT]
thrust = inputs[Dynamic.Vehicle.Propulsion.THRUST_TOTAL]
Expand Down Expand Up @@ -470,10 +426,10 @@ def initialize(self):
desc='current friction coefficient, either rolling friction or breaking friction',
)

def setup(self):
options = self.options
add_aviary_option(self, Mission.GRAVITY, units='m/s**2')

nn = options['num_nodes']
def setup(self):
nn = self.options['num_nodes']

add_aviary_input(self, Dynamic.Vehicle.MASS, shape=nn, units='kg')
add_aviary_input(self, Dynamic.Vehicle.LIFT, shape=nn, units='N')
Expand All @@ -495,9 +451,9 @@ def setup(self):
)

def setup_partials(self):
options = self.options
nn = self.options['num_nodes']

nn = options['num_nodes']
grav_metric = self.options[Mission.GRAVITY][0]

rows_cols = np.arange(nn)

Expand Down Expand Up @@ -544,6 +500,8 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
nn = options['num_nodes']
friction_coefficient = options['friction_coefficient']

grav_metric = self.options[Mission.GRAVITY][0]

mass = inputs[Dynamic.Vehicle.MASS]
lift = inputs[Dynamic.Vehicle.LIFT]
thrust = inputs[Dynamic.Vehicle.Propulsion.THRUST_TOTAL]
Expand All @@ -567,6 +525,8 @@ def compute_partials(self, inputs, J, discrete_inputs=None):
nn = options['num_nodes']
friction_coefficient = options['friction_coefficient']

grav_metric = self.options[Mission.GRAVITY][0]

mass = inputs[Dynamic.Vehicle.MASS]
lift = inputs[Dynamic.Vehicle.LIFT]

Expand Down
8 changes: 2 additions & 6 deletions aviary/mission/energy_state/ode/landing_ode.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ class FlareODE(_BaseODE):
"""Define the ODE for the flare phase of landing."""

def setup(self):
options = self.options

nn = options['num_nodes']
nn = self.options['num_nodes']
self.add_subsystem(name='atmosphere', subsys=Atmosphere(num_nodes=nn), promotes=['*'])

# NOTE: the following are potentially significant differences in implementation
Expand All @@ -55,11 +53,9 @@ def setup(self):

self.add_subsystems()

kwargs = {'num_nodes': nn, 'aviary_options': options['aviary_options']}

self.add_subsystem(
'landing_eom',
FlareEOM(**kwargs),
FlareEOM(num_nodes=nn),
promotes_inputs=[
Dynamic.Mission.FLIGHT_PATH_ANGLE,
Dynamic.Mission.VELOCITY,
Expand Down
16 changes: 9 additions & 7 deletions aviary/mission/energy_state/ode/required_thrust.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import numpy as np
import openmdao.api as om
from aviary.variable_info.functions import add_aviary_input
from aviary.variable_info.functions import add_aviary_input, add_aviary_option

from aviary.constants import GRAV_METRIC_FLOPS as gravity
from aviary.variable_info.variables import Dynamic
from aviary.variable_info.variables import Dynamic, Mission


class RequiredThrust(om.ExplicitComponent):
Expand All @@ -14,6 +13,7 @@ class RequiredThrust(om.ExplicitComponent):

def initialize(self):
self.options.declare('num_nodes', types=int)
add_aviary_option(self, Mission.GRAVITY, units='m/s**2')

def setup(self):
nn = self.options['num_nodes']
Expand Down Expand Up @@ -49,28 +49,30 @@ def setup(self):
self.declare_partials('thrust_required', Dynamic.Vehicle.MASS, rows=ar, cols=ar)

def compute(self, inputs, outputs):
grav_metric = self.options[Mission.GRAVITY][0]
drag = inputs[Dynamic.Vehicle.DRAG]
altitude_rate = inputs[Dynamic.Mission.ALTITUDE_RATE]
velocity = inputs[Dynamic.Mission.VELOCITY]
velocity_rate = inputs[Dynamic.Mission.VELOCITY_RATE]
mass = inputs[Dynamic.Vehicle.MASS]

thrust_required = drag + (altitude_rate * gravity / velocity + velocity_rate) * mass
thrust_required = drag + (altitude_rate * grav_metric / velocity + velocity_rate) * mass

outputs['thrust_required'] = thrust_required

def compute_partials(self, inputs, partials):
grav_metric = self.options[Mission.GRAVITY][0]
altitude_rate = inputs[Dynamic.Mission.ALTITUDE_RATE]
velocity = inputs[Dynamic.Mission.VELOCITY]
velocity_rate = inputs[Dynamic.Mission.VELOCITY_RATE]
mass = inputs[Dynamic.Vehicle.MASS]

partials['thrust_required', Dynamic.Vehicle.DRAG] = 1.0
partials['thrust_required', Dynamic.Mission.ALTITUDE_RATE] = gravity / velocity * mass
partials['thrust_required', Dynamic.Mission.ALTITUDE_RATE] = grav_metric / velocity * mass
partials['thrust_required', Dynamic.Mission.VELOCITY] = (
-altitude_rate * gravity / velocity**2 * mass
-altitude_rate * grav_metric / velocity**2 * mass
)
partials['thrust_required', Dynamic.Mission.VELOCITY_RATE] = mass
partials['thrust_required', Dynamic.Vehicle.MASS] = (
altitude_rate * gravity / velocity + velocity_rate
altitude_rate * grav_metric / velocity + velocity_rate
)
Loading
Loading