Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a balanced field length example #533

Merged
merged 29 commits into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f9f51ac
balaneced field files
robfalck Jan 25, 2021
208c369
Merge branch 'master' of https://github.com/OpenMDAO/dymos into balan…
robfalck Jan 26, 2021
299ca97
balanced field case converges with ipopt but cleanup is needed
robfalck Jan 29, 2021
50d48d6
cs part 1
robfalck Feb 1, 2021
8885225
stall speed comp now computes weight itself
robfalck Feb 1, 2021
1115275
lift coef now computed in aero forces comp
robfalck Feb 1, 2021
c91e0da
removed stall speed comp
robfalck Feb 1, 2021
6bf8ae6
just need to get rid of groundroll dynamics
robfalck Feb 1, 2021
ea465bb
groundroll is a group with a single comp now
robfalck Feb 1, 2021
4ee4249
v_dot computed in aero_forces_comp is being problematic
robfalck Feb 1, 2021
12e4068
ground roll ODE is now successfully a single component
robfalck Feb 1, 2021
cbeaf72
climb now using a single-component ODE
robfalck Feb 1, 2021
491df3b
removed unnecessary files
robfalck Feb 1, 2021
fabe873
moved most parameters to the trajectory level, but restarts with traj…
robfalck Feb 1, 2021
8d5344a
trajectory simulation now records inputs to capture parameter values
robfalck Feb 1, 2021
743705d
case cleaned up, but no docs yet. possible bug in some traj parameter…
robfalck Feb 1, 2021
e1e4d5c
balanced field ODEs now share common functions
robfalck Feb 2, 2021
18d9d81
Added new test case to verify that default "val" for states, controls…
robfalck Feb 2, 2021
4a4fb97
conform sticking default value for parameter
robfalck Feb 2, 2021
9ed5a26
pep8 cleanup
robfalck Feb 2, 2021
674164c
added utility function to reshape default values to the proper shape
robfalck Feb 2, 2021
ee9740a
cleanup
robfalck Feb 3, 2021
3668cb3
plot added for the docs
robfalck Feb 3, 2021
e285d1b
added documentation of balanced field problem
robfalck Feb 4, 2021
f5cb996
pep8
robfalck Feb 4, 2021
57f29f3
simplified ODEs per code review, and updated docs to reflect the change
robfalck Feb 5, 2021
1a4e9c8
doc update
robfalck Feb 5, 2021
e7401d5
Fixed reference to old ODE
robfalck Feb 5, 2021
7263cb8
one more minor typo
robfalck Feb 5, 2021
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
Empty file.
289 changes: 289 additions & 0 deletions dymos/examples/balanced_field/balanced_field_odes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
import numpy as np
import openmdao.api as om


def compute_stall_speed(m, g, rho, S, CL_max, v):
"""
Compute the weight, stall speed, and ratio of the current airspeed to the stall speed.

Notes
-----
This function is complex-step-differentiable.
User is expected to provide inputs in a units-consistent manner.

Parameters
----------
m : float or np.array
Aircraft mass
g : float
Gravitational acceleration
rho : float or np.array
Atmospheric density
S : float or np.array
Aerodynamic reference area
CL_max : float or np.array
Maximum lift coefficient of the aircraft
v : float or np.array
True airspeed

Returns
-------
W : float or np.array
The aircraft weight.
v_stall : float or np.array
The stall speed of the aircraft.
v_over_v_stall : float or np.array
The ratio of the current aircraft speed to its stall speed.
"""
W = m * g
v_stall = np.sqrt(2 * W / rho / S / CL_max)
v_over_v_stall = v / v_stall
return W, v_stall, v_over_v_stall


def compute_aero(CL0, CD0, S, alpha, alpha_max, CL_max, AR, e, span, h, h_w, rho, v):
"""
Compute the aerodynamic forces (lift and drag) as well as some other auxiliary aerodynamic output variables.

Notes
-----
This function is complex-step-differentiable.
User is expected to provide inputs in a units-consistent manner.

Parameters
----------
CL0 : float or np.array
Zero-alpha lift coefficient
CD0 : float or np.array
Zero-lift drag coefficient
S : float or np.array
Aerodynamic reference area
alpha : float or np.array
Angle of attack
alpha_max : float or np.array
Maximum angle of attack
CL_max : float or np.array
Maximum lift coefficient
AR : float or np.array
Wing aspect ratio
e : float or np.array
Oswald's efficiency factor
span : float or np.array
Wingspan
h : float or np.array
Altitude
h_w : float or np.array
Height of wing above aircraft center of mass
rho : float or np.array
Atmospheric density
v : float or np.array
True airspeed

Returns
-------
CL : float or np.array
Lift coefficient
K : float or np.array
Drag-due-to-lift factor
q : float or np.array
Dynamic pressure
L : float or np.array
Lift
D : float or np.array
Drag
"""
CL = CL0 + (alpha / alpha_max) * (CL_max - CL0)
K_nom = 1.0 / (np.pi * AR * e)
b = span / 2.0
K = K_nom * 33 * ((h + h_w) / b) ** 1.5 / (1.0 + 33 * ((h + h_w) / b) ** 1.5)

q = 0.5 * rho * v ** 2
L = q * S * CL
D = q * S * (CD0 + K * CL ** 2)

return CL, K, q, L, D


class GroundRollODEComp(om.ExplicitComponent):

def initialize(self):
self.options.declare('num_nodes', types=int)
self.options.declare('g', types=(float, int), default=9.80665, desc='gravitational acceleration (m/s**2)')

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

self.add_input('rho', val=1.225, desc='atmospheric density', units='kg/m**3')
self.add_input('S', val=124.7, desc='aerodynamic reference area', units='m**2')
self.add_input('CD0', val=0.03, desc='zero-lift drag coefficient', units=None)

self.add_input('AR', val=9.45, desc='wing aspect ratio', units=None)
self.add_input('e', val=0.801, desc='Oswald span efficiency factor', units=None)
self.add_input('span', val=35.7, desc='Wingspan', units='m')
self.add_input('h', val=0.0, desc='altitude', units='m')
self.add_input('h_w', val=1.0, desc='height of the wing above the CG', units='m')

self.add_input('CL0', val=0.5, desc='zero-alpha lift coefficient', units=None)
self.add_input('CL_max', val=2.0, desc='maximum lift coefficient', units=None)
self.add_input('alpha', val=np.ones(nn), desc='angle of attack', units='rad')
self.add_input('alpha_max', val=np.radians(10), desc='angle of attack at CL_max', units='rad')
self.add_input('T', val=120101.98, desc='thrust', units='N')
self.add_input('mu_r', val=0.05, desc='runway friction coefficient', units=None)

self.add_input('v', val=np.ones(nn), desc='true airspeed', units='m/s')
self.add_input('m', val=np.ones(nn), desc='aircraft mass', units='kg')

self.add_output('CL', val=np.ones(nn), desc='lift coefficient', units=None)
self.add_output('K', val=np.ones(nn), desc='drag-due-to-lift factor', units=None)
self.add_output('q', val=np.ones(nn), desc='dynamic pressure', units='Pa')
self.add_output('L', val=np.ones(nn), desc='lift', units='N')
self.add_output('D', val=np.ones(nn), desc='drag', units='N')
self.add_output('W', val=np.ones(nn), desc='aircraft weight', units='N')
self.add_output('v_stall', val=np.ones(nn), desc='stall speed', units='m/s')
self.add_output('v_over_v_stall', val=np.ones(nn), desc='stall speed ratio', units=None)

self.add_output(name='v_dot', val=np.ones(nn), desc='rate of change of velocity magnitude',
units='m/s**2', tags=['state_rate_source:v'])
self.add_output(name='r_dot', val=np.ones(nn), desc='rate of change of range', units='m/s',
tags=['state_rate_source:r'])
self.add_output(name='F_r', val=np.ones(nn), desc='runway normal force', units='N')

self.declare_coloring(wrt='*', method='cs')
self.declare_partials(of='*', wrt='*', method='cs')

def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
g = self.options['g']
rho = inputs['rho']
v = inputs['v']
S = inputs['S']
CD0 = inputs['CD0']

h = inputs['h']
h_w = inputs['h_w']
span = inputs['span']
AR = inputs['AR']
e = inputs['e']

CL0 = inputs['CL0']
alpha = inputs['alpha']
alpha_max = inputs['alpha_max']
CL_max = inputs['CL_max']
m = inputs['m']

T = inputs['T']
mu_r = inputs['mu_r']

outputs['W'], outputs['v_stall'], outputs['v_over_v_stall'] = \
compute_stall_speed(m, g, rho, S, CL_max, v)

outputs['CL'], outputs['K'], outputs['q'], outputs['L'], outputs['D'] = \
compute_aero(CL0, CD0, S, alpha, alpha_max, CL_max, AR, e, span, h, h_w, rho, v)

calpha = np.cos(alpha)
salpha = np.sin(alpha)

outputs['F_r'] = m * g - outputs['L'] * calpha - T * salpha
outputs['v_dot'] = (T * calpha - outputs['D'] - outputs['F_r'] * mu_r) / m
outputs['r_dot'] = v


class TakeoffClimbODEComp(om.ExplicitComponent):
"""
The ODE System for an aircraft takeoff climb.

Computes the rates for states v (true airspeed) gam (flight path angle) r (range) and h (altitude).

References
----------
.. [1] Raymer, Daniel. Aircraft design: a conceptual approach. American Institute of
Aeronautics and Astronautics, Inc., 2012.
"""
def initialize(self):
self.options.declare('num_nodes', types=int)
self.options.declare('g', types=(float, int), default=9.80665, desc='gravitational acceleration (m/s**2)')

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

# Scalar (constant) inputs
self.add_input('rho', val=1.225, desc='atmospheric density at runway', units='kg/m**3')
self.add_input('S', val=124.7, desc='aerodynamic reference area', units='m**2')
self.add_input('CD0', val=0.03, desc='zero-lift drag coefficient', units=None)
self.add_input('CL0', val=0.5, desc='zero-alpha lift coefficient', units=None)
self.add_input('CL_max', val=2.0, desc='maximum lift coefficient for linear fit', units=None)
self.add_input('alpha_max', val=np.radians(10), desc='angle of attack at CL_max', units='rad')
self.add_input('h_w', val=1.0, desc='height of the wing above the CG', units='m')
self.add_input('AR', val=9.45, desc='wing aspect ratio', units=None)
self.add_input('e', val=0.801, desc='Oswald span efficiency factor', units=None)
self.add_input('span', val=35.7, desc='Wingspan', units='m')
self.add_input('T', val=1.0, desc='thrust', units='N')

# Dynamic inputs (can assume a different value at every node)
self.add_input('h', shape=(nn,), desc='altitude', units='m')
self.add_input('m', shape=(nn,), desc='aircraft mass', units='kg')
self.add_input('v', shape=(nn,), desc='aircraft true airspeed', units='m/s')
self.add_input('gam', shape=(nn,), desc='flight path angle', units='rad')
self.add_input('alpha', shape=(nn,), desc='angle of attack', units='rad')

# Outputs
self.add_output('CL', shape=(nn,), desc='lift coefficient', units=None)
self.add_output('q', shape=(nn,), desc='dynamic pressure', units='Pa')
self.add_output('L', shape=(nn,), desc='lift force', units='N')
self.add_output('D', shape=(nn,), desc='drag force', units='N')
self.add_output('K', val=np.ones(nn), desc='drag-due-to-lift factor', units=None)
self.add_output('F_r', shape=(nn,), desc='runway normal force', units='N')
self.add_output('v_dot', shape=(nn,), desc='rate of change of speed', units='m/s**2',
tags=['state_rate_source:v'])
self.add_output('gam_dot', shape=(nn,), desc='rate of change of flight path angle',
units='rad/s', tags=['state_rate_source:gam'])
self.add_output('h_dot', shape=(nn,), desc='rate of change of altitude', units='m/s',
tags=['state_rate_source:h'])
self.add_output('r_dot', shape=(nn,), desc='rate of change of range', units='m/s',
tags=['state_rate_source:r'])
self.add_output('W', shape=(nn,), desc='aircraft weight', units='N')
self.add_output('v_stall', shape=(nn,), desc='stall speed', units='m/s')
self.add_output('v_over_v_stall', shape=(nn,), desc='stall speed ratio', units=None)

self.declare_partials(of='*', wrt='*', method='cs')
self.declare_coloring(wrt='*', method='cs')

def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
g = self.options['g']

# Compute factor k to include ground effect on lift
rho = inputs['rho']
v = inputs['v']
S = inputs['S']
CD0 = inputs['CD0']
m = inputs['m']
T = inputs['T']
gam = inputs['gam']
h = inputs['h']
h_w = inputs['h_w']
span = inputs['span']
AR = inputs['AR']
CL0 = inputs['CL0']
alpha = inputs['alpha']
alpha_max = inputs['alpha_max']
CL_max = inputs['CL_max']
e = inputs['e']

outputs['W'], outputs['v_stall'], outputs['v_over_v_stall'] = \
compute_stall_speed(m, g, rho, S, CL_max, v)

outputs['CL'], outputs['K'], outputs['q'], outputs['L'], outputs['D'] = \
compute_aero(CL0, CD0, S, alpha, alpha_max, CL_max, AR, e, span, h, h_w, rho, v)

# Compute the downward force on the landing gear
calpha = np.cos(alpha)
salpha = np.sin(alpha)

# Compute the dynamics
cgam = np.cos(gam)
sgam = np.sin(gam)

outputs['F_r'] = m * g - outputs['L'] * calpha - T * salpha
outputs['v_dot'] = (T * calpha - outputs['D']) / m - g * sgam
outputs['gam_dot'] = (T * salpha + outputs['L']) / (m * v) - (g / v) * cgam
outputs['h_dot'] = v * sgam
outputs['r_dot'] = v * cgam
Empty file.
Loading