Source code for openmdao.examples.hohmann_transfer

""" Find the minimum delta-V for a Hohmann Transer from Low Earth Orbit (LEO)
to GEostationary Orbit (GEO)
"""

import numpy as np
from openmdao.api import Problem, Group, Component, IndepVarComp, ExecComp, \
                         ScipyOptimizer


[docs]class VCircComp(Component): """ Computes the circular orbit velocity given a radius and gravitational parameter. """ def __init__(self, radius=6378.14+400, mu=398600.4418): super(VCircComp, self).__init__() # Derivative specification self.deriv_options['type'] = 'user' self.deriv_options['check_type'] = 'cs' self.deriv_options['check_step_size'] = 1.0e-16 self.add_param('r', val=radius, desc='Radius from central body', units='km') self.add_param('mu', val=mu, desc='Gravitational parameter of central body', units='km**3/s**2') self.add_output('vcirc', val=1.0, desc='Circular orbit velocity at given radius ' 'and gravitational parameter', units='km/s')
[docs] def solve_nonlinear(self, params, unknowns, resids): r = params['r'] mu = params['mu'] unknowns['vcirc'] = np.sqrt(mu/r)
[docs] def linearize(self, params, unknowns, resids): r = params['r'] mu = params['mu'] vcirc = unknowns['vcirc'] J = {} J['vcirc', 'mu'] = 0.5/(r*vcirc) J['vcirc', 'r'] = -0.5*mu/(vcirc*r**2) return J
[docs]class DeltaVComp(Component): def __init__(self): super(DeltaVComp, self).__init__() # Derivative specification self.deriv_options['type'] = 'user' self.deriv_options['check_type'] = 'cs' self.deriv_options['check_step_size'] = 1.0e-16 self.add_param('v1', val=1.0, desc='Initial velocity', units='km/s') self.add_param('v2', val=1.0, desc='Final velocity', units='km/s') self.add_param('dinc', val=1.0, desc='Plane change', units='rad') # Note: We're going to use trigonometric functions on dinc. The # automatic unit conversion in OpenMDAO comes in handy here. self.add_output('delta_v', val=0.0, desc='Delta-V', units='km/s')
[docs] def solve_nonlinear(self, params, unknowns, resids): v1 = params['v1'] v2 = params['v2'] dinc = params['dinc'] unknowns['delta_v'] = v1**2 + v2**2 - 2*v1*v2*np.cos(dinc)
[docs] def linearize(self, params, unknowns, resids): v1 = params['v1'] v2 = params['v2'] dinc = params['dinc'] J = {} J['delta_v', 'v1'] = 2*v1 - 2*v2*np.cos(dinc) J['delta_v', 'v2'] = 2*v2 - 2*v1*np.cos(dinc) J['delta_v', 'dinc'] = 2*v1*v2*np.sin(dinc) return J
[docs]class TransferOrbitComp(Component): def __init__(self): super(TransferOrbitComp, self).__init__() # Derivative specification self.deriv_options['type'] = 'fd' self.add_param('mu', val=398600.4418, desc='Gravitational parameter of central body', units='km**3/s**2') self.add_param('rp', val=7000.0, desc='periapsis radius', units='km') self.add_param('ra', val=42164.0, desc='apoapsis radius', units='km') self.add_output('vp', val=0.0, desc='periapsis velocity', units='km/s') self.add_output('va', val=0.0, desc='apoapsis velocity', units='km/s')
[docs] def solve_nonlinear(self, params, unknowns, resids): mu = params['mu'] rp = params['rp'] ra = params['ra'] a = (ra+rp)/2.0 e = (a-rp)/a p = a*(1.0-e**2) h = np.sqrt(mu*p) unknowns['vp'] = h/rp unknowns['va'] = h/ra
if __name__ == '__main__': prob = Problem(root=Group()) root = prob.root root.add('mu_comp', IndepVarComp('mu', val=0.0, units='km**3/s**2'), promotes=['mu']) root.add('r1_comp', IndepVarComp('r1', val=0.0, units='km'), promotes=['r1']) root.add('r2_comp', IndepVarComp('r2', val=0.0, units='km'), promotes=['r2']) root.add('dinc1_comp', IndepVarComp('dinc1', val=0.0, units='deg'), promotes=['dinc1']) root.add('dinc2_comp', IndepVarComp('dinc2', val=0.0, units='deg'), promotes=['dinc2']) root.add('leo', system=VCircComp()) root.add('geo', system=VCircComp()) root.add('transfer', system=TransferOrbitComp()) root.connect('r1', ['leo.r', 'transfer.rp']) root.connect('r2', ['geo.r', 'transfer.ra']) root.connect('mu', ['leo.mu', 'geo.mu', 'transfer.mu']) root.add('dv1', system=DeltaVComp()) root.connect('leo.vcirc', 'dv1.v1') root.connect('transfer.vp', 'dv1.v2') root.connect('dinc1', 'dv1.dinc') root.add('dv2', system=DeltaVComp()) root.connect('transfer.va', 'dv2.v1') root.connect('geo.vcirc', 'dv2.v2') root.connect('dinc2', 'dv2.dinc') root.add('dv_total', system=ExecComp('delta_v=dv1+dv2', units={'delta_v': 'km/s', 'dv1': 'km/s', 'dv2': 'km/s'}), promotes=['delta_v']) root.connect('dv1.delta_v', 'dv_total.dv1') root.connect('dv2.delta_v', 'dv_total.dv2') root.add('dinc_total', system=ExecComp('dinc=dinc1+dinc2', units={'dinc': 'deg', 'dinc1': 'deg', 'dinc2': 'deg'}), promotes=['dinc']) root.connect('dinc1', 'dinc_total.dinc1') root.connect('dinc2', 'dinc_total.dinc2') prob.driver = ScipyOptimizer() prob.driver.add_desvar('dinc1', lower=0, upper=28.5) prob.driver.add_desvar('dinc2', lower=0, upper=28.5) prob.driver.add_constraint('dinc', lower=28.5, upper=28.5, scaler=1.0) prob.driver.add_objective('delta_v', scaler=1.0) # Setup the problem prob.setup() # Set initial values prob['mu'] = 398600.4418 prob['r1'] = 6778.137 prob['r2'] = 42164.0 prob['dinc1'] = 0.0 prob['dinc2'] = 28.5 # Use run_once to evaluate the model at the initial guess. # This will give us the :math:`\Delta V` for performing # the entire plane change at apogee. prob.run_once() dv_all_apogee = prob['delta_v'] # Go! prob.run() print('Impulse 1:') print(' Delta-V: {0:6.4f} km/s'.format(prob['dv1.delta_v'])) print(' Inclination Change: {0:6.4f} deg'.format(prob['dinc1'])) print('Impulse 2:') print(' Delta-V: {0:6.4f} km/s'.format(prob['dv2.delta_v'])) print(' Inclination Change: {0:6.4f} deg'.format(prob['dinc2'])) print('Total Delta-V: {0:6.4f} km/s'.format(prob['delta_v'])) print('Total Plane Change: {0:6.4f} deg'.format(prob['dinc'])) print('\nPerforming the plane change at apogee gives a ' 'Delta-V of {0:6.4f} km/s'.format(dv_all_apogee))