|
| 1 | +import openmdao.api as om |
| 2 | +from openmdao.utils.assert_utils import assert_near_equal |
| 3 | + |
| 4 | +from aviary.interface.methods_for_level2 import AviaryGroup |
| 5 | +from aviary.mission.gasp_based.phases.time_integration_traj import FlexibleTraj |
| 6 | +from aviary.mission.flops_based.phases.time_integration_phases import \ |
| 7 | + SGMHeightEnergy, SGMDetailedTakeoff, SGMDetailedLanding |
| 8 | +from aviary.subsystems.premission import CorePreMission |
| 9 | +from aviary.utils.functions import set_aviary_initial_values |
| 10 | +from aviary.variable_info.enums import Verbosity |
| 11 | +from aviary.variable_info.variables import Aircraft, Dynamic, Mission |
| 12 | +from aviary.variable_info.variables_in import VariablesIn |
| 13 | + |
| 14 | +from aviary.interface.default_phase_info.height_energy import aero, prop, geom |
| 15 | +from aviary.subsystems.propulsion.engine_deck import EngineDeck |
| 16 | +from aviary.utils.process_input_decks import create_vehicle |
| 17 | +from aviary.utils.preprocessors import preprocess_propulsion |
| 18 | +from aviary.variable_info.variable_meta_data import _MetaData as BaseMetaData |
| 19 | + |
| 20 | +import warnings |
| 21 | +import unittest |
| 22 | +import importlib |
| 23 | + |
| 24 | + |
| 25 | +@unittest.skipUnless(importlib.util.find_spec("pyoptsparse") is not None, "pyoptsparse is not installed") |
| 26 | +class HE_SGMDescentTestCase(unittest.TestCase): |
| 27 | + def setUp(self): |
| 28 | + aviary_inputs, initial_guesses = create_vehicle( |
| 29 | + 'models/test_aircraft/aircraft_for_bench_FwFm.csv') |
| 30 | + aviary_inputs.set_val(Aircraft.Engine.SCALED_SLS_THRUST, val=28690, units="lbf") |
| 31 | + aviary_inputs.set_val(Dynamic.Mission.THROTTLE, val=0, units="unitless") |
| 32 | + aviary_inputs.set_val(Mission.Takeoff.ROLLING_FRICTION_COEFFICIENT, |
| 33 | + val=0.0175, units="unitless") |
| 34 | + aviary_inputs.set_val(Mission.Takeoff.BRAKING_FRICTION_COEFFICIENT, |
| 35 | + val=0.35, units="unitless") |
| 36 | + ode_args = dict(aviary_options=aviary_inputs, core_subsystems=[prop, geom, aero]) |
| 37 | + preprocess_propulsion(aviary_inputs, [EngineDeck(options=aviary_inputs)]) |
| 38 | + |
| 39 | + ode_args['num_nodes'] = 1 |
| 40 | + ode_args['subsystem_options'] = {'core_aerodynamics': {'method': 'computed'}} |
| 41 | + |
| 42 | + self.ode_args = ode_args |
| 43 | + self.aviary_inputs = aviary_inputs |
| 44 | + self.tol = 1e-5 |
| 45 | + |
| 46 | + def setup_prob(self, phases) -> om.Problem: |
| 47 | + prob = om.Problem() |
| 48 | + prob.driver = om.pyOptSparseDriver() |
| 49 | + prob.driver.options["optimizer"] = 'IPOPT' |
| 50 | + prob.driver.opt_settings['tol'] = 1.0E-6 |
| 51 | + prob.driver.opt_settings['mu_init'] = 1e-5 |
| 52 | + prob.driver.opt_settings['max_iter'] = 50 |
| 53 | + prob.driver.opt_settings['print_level'] = 5 |
| 54 | + |
| 55 | + aviary_options = self.ode_args['aviary_options'] |
| 56 | + subsystems = self.ode_args['core_subsystems'] |
| 57 | + |
| 58 | + traj = FlexibleTraj( |
| 59 | + Phases=phases, |
| 60 | + promote_all_auto_ivc=True, |
| 61 | + traj_final_state_output=[Dynamic.Mission.MASS, |
| 62 | + Dynamic.Mission.DISTANCE, |
| 63 | + Dynamic.Mission.ALTITUDE], |
| 64 | + traj_initial_state_input=[ |
| 65 | + Dynamic.Mission.MASS, |
| 66 | + Dynamic.Mission.DISTANCE, |
| 67 | + Dynamic.Mission.ALTITUDE, |
| 68 | + ], |
| 69 | + ) |
| 70 | + prob.model = AviaryGroup(aviary_options=aviary_options, |
| 71 | + aviary_metadata=BaseMetaData) |
| 72 | + prob.model.add_subsystem( |
| 73 | + 'pre_mission', |
| 74 | + CorePreMission(aviary_options=aviary_options, |
| 75 | + subsystems=subsystems), |
| 76 | + promotes_inputs=['aircraft:*', 'mission:*'], |
| 77 | + promotes_outputs=['aircraft:*', 'mission:*'] |
| 78 | + ) |
| 79 | + prob.model.add_subsystem('traj', traj, |
| 80 | + promotes=['aircraft:*', 'mission:*'] |
| 81 | + ) |
| 82 | + |
| 83 | + prob.model.add_subsystem( |
| 84 | + "fuel_obj", |
| 85 | + om.ExecComp( |
| 86 | + "reg_objective = overall_fuel/10000", |
| 87 | + reg_objective={"val": 0.0, "units": "unitless"}, |
| 88 | + overall_fuel={"units": "lbm"}, |
| 89 | + ), |
| 90 | + promotes_inputs=[ |
| 91 | + ("overall_fuel", Mission.Summary.TOTAL_FUEL_MASS), |
| 92 | + ], |
| 93 | + promotes_outputs=[("reg_objective", Mission.Objectives.FUEL)], |
| 94 | + ) |
| 95 | + |
| 96 | + prob.model.add_objective(Mission.Objectives.FUEL, ref=1e4) |
| 97 | + |
| 98 | + prob.model.add_subsystem( |
| 99 | + 'input_sink', |
| 100 | + VariablesIn(aviary_options=self.aviary_inputs, |
| 101 | + meta_data=BaseMetaData), |
| 102 | + promotes_inputs=['*'], |
| 103 | + promotes_outputs=['*']) |
| 104 | + |
| 105 | + with warnings.catch_warnings(): |
| 106 | + |
| 107 | + # Set initial default values for all LEAPS aircraft variables. |
| 108 | + set_aviary_initial_values( |
| 109 | + prob.model, self.aviary_inputs, meta_data=BaseMetaData) |
| 110 | + |
| 111 | + warnings.simplefilter("ignore", om.PromotionWarning) |
| 112 | + |
| 113 | + prob.setup() |
| 114 | + |
| 115 | + return prob |
| 116 | + |
| 117 | + def run_simulation(self, phases, initial_values: dict): |
| 118 | + prob = self.setup_prob(phases) |
| 119 | + |
| 120 | + for key, val in initial_values.items(): |
| 121 | + prob.set_val(key, **val) |
| 122 | + |
| 123 | + prob.run_model() |
| 124 | + |
| 125 | + distance = prob.get_val('traj.distance_final', units='NM')[0] |
| 126 | + mass = prob.get_val('traj.mass_final', units='lbm')[0] |
| 127 | + alt = prob.get_val('traj.altitude_final', units='ft')[0] |
| 128 | + |
| 129 | + final_states = {'distance': distance, 'mass': mass, 'altitude': alt} |
| 130 | + return final_states |
| 131 | + |
| 132 | + # def test_takeoff(self): |
| 133 | + # initial_values_takeoff = { |
| 134 | + # "traj.altitude_initial": {'val': 0, 'units': "ft"}, |
| 135 | + # "traj.mass_initial": {'val': 171000, 'units': "lbm"}, |
| 136 | + # "traj.distance_initial": {'val': 0, 'units': "NM"}, |
| 137 | + # "traj.velocity": {'val': .1, 'units': "m/s"}, |
| 138 | + # } |
| 139 | + |
| 140 | + # ode_args = self.ode_args |
| 141 | + # ode_args['friction_key'] = Mission.Takeoff.ROLLING_FRICTION_COEFFICIENT |
| 142 | + # brake_release_to_decision = SGMDetailedTakeoff( |
| 143 | + # ode_args, |
| 144 | + # simupy_args=dict(verbosity=Verbosity.DEBUG,) |
| 145 | + # ) |
| 146 | + # brake_release_to_decision.clear_triggers() |
| 147 | + # brake_release_to_decision.add_trigger(Dynamic.Mission.VELOCITY, value=167.85, units='kn') |
| 148 | + |
| 149 | + # phases = {'HE': { |
| 150 | + # 'ode': brake_release_to_decision, |
| 151 | + # 'vals_to_set': {} |
| 152 | + # }} |
| 153 | + |
| 154 | + # final_states = self.run_simulation(phases, initial_values_takeoff) |
| 155 | + # # assert_near_equal(final_states['altitude'], 500, self.tol) |
| 156 | + # assert_near_equal(final_states['velocity'], 167.85, self.tol) |
| 157 | + |
| 158 | + def test_cruise(self): |
| 159 | + initial_values_cruise = { |
| 160 | + "traj.altitude_initial": {'val': 35000, 'units': "ft"}, |
| 161 | + "traj.mass_initial": {'val': 171000, 'units': "lbm"}, |
| 162 | + "traj.distance_initial": {'val': 0, 'units': "NM"}, |
| 163 | + "traj.mach": {'val': .8, 'units': "unitless"}, |
| 164 | + } |
| 165 | + |
| 166 | + SGMCruise = SGMHeightEnergy( |
| 167 | + self.ode_args, |
| 168 | + phase_name='cruise', |
| 169 | + simupy_args=dict(verbosity=Verbosity.QUIET,) |
| 170 | + ) |
| 171 | + SGMCruise.triggers[0].value = 160000 |
| 172 | + |
| 173 | + phases = {'HE': { |
| 174 | + 'ode': SGMCruise, |
| 175 | + 'vals_to_set': {} |
| 176 | + }} |
| 177 | + |
| 178 | + final_states = self.run_simulation(phases, initial_values_cruise) |
| 179 | + assert_near_equal(final_states['mass'], 160000, self.tol) |
| 180 | + |
| 181 | + # def test_landing(self): |
| 182 | + # initial_values_landing = { |
| 183 | + # "traj.altitude_initial": {'val': 35000, 'units': "ft"}, |
| 184 | + # "traj.mass_initial": {'val': 171000, 'units': "lbm"}, |
| 185 | + # "traj.distance_initial": {'val': 0, 'units': "NM"}, |
| 186 | + # "traj.velocity": {'val': 300, 'units': "m/s"}, |
| 187 | + # } |
| 188 | + |
| 189 | + # ode_args = self.ode_args |
| 190 | + # ode_args['friction_key'] = Mission.Takeoff.BRAKING_FRICTION_COEFFICIENT |
| 191 | + # phases = {'HE': { |
| 192 | + # 'ode': SGMDetailedLanding( |
| 193 | + # ode_args, |
| 194 | + # simupy_args=dict(verbosity=Verbosity.QUIET,) |
| 195 | + # ), |
| 196 | + # 'vals_to_set': {} |
| 197 | + # }} |
| 198 | + |
| 199 | + # final_states = self.run_simulation(phases, initial_values_landing) |
| 200 | + # assert_near_equal(final_states['altitude'], 0, self.tol) |
| 201 | + |
| 202 | + |
| 203 | +if __name__ == '__main__': |
| 204 | + unittest.main() |
0 commit comments