diff --git a/aviary/mission/energy_state/phases/build_landing.py b/aviary/mission/energy_state/phases/build_landing.py index 796a24c3b..5a97319a4 100644 --- a/aviary/mission/energy_state/phases/build_landing.py +++ b/aviary/mission/energy_state/phases/build_landing.py @@ -1,41 +1,9 @@ import openmdao.api as om from aviary.mission.energy_state.phases.simplified_landing import LandingGroup -from aviary.variable_info.variables import Aircraft, Mission class Landing: - """ - Define user constraints for a climb phase. - - Parameters - ---------- - ref_wing_area : float (0.0) - reference are of wing (ft^2) - Cl_max_ldg : float (None) - maximum coefficient of lift in landing configuration (None) - - Returns - ------- - Group - a Group object in OpenMDAO - """ - - def __init__( - self, - ref_wing_area=None, - Cl_max_ldg=None, - ): - self.ref_wing_area = ref_wing_area # ft**2 - self.Cl_max_ldg = Cl_max_ldg # no units - # note: need to clean this up so that some of these variables come from - # connections. - - __slots__ = ( - 'ref_wing_area', - 'Cl_max_ldg', - ) - def build_phase(self, use_detailed=False): """ Construct and return a new phase for landing analysis. @@ -56,15 +24,8 @@ def build_phase(self, use_detailed=False): 'Must set landing method to `use_detailed=False`, detailed landing is' ' not currently enabled.' ) - - ############## - # Add Inputs # - ############## - - landing = LandingGroup() - landing.set_input_defaults(Aircraft.Wing.AREA, val=self.ref_wing_area, units='ft**2') - landing.set_input_defaults( - Mission.Landing.LIFT_COEFFICIENT_MAX, val=self.Cl_max_ldg, units='unitless' - ) + else: + # Simple landing group + landing = LandingGroup() return landing diff --git a/aviary/mission/energy_state/phases/simplified_landing.py b/aviary/mission/energy_state/phases/simplified_landing.py index 7e193de4f..e6bddb64e 100644 --- a/aviary/mission/energy_state/phases/simplified_landing.py +++ b/aviary/mission/energy_state/phases/simplified_landing.py @@ -1,6 +1,6 @@ import openmdao.api as om -from aviary.constants import GRAV_ENGLISH_LBM, RHO_SEA_LEVEL_METRIC +from aviary.constants import GRAV_ENGLISH_LBM, RHO_SEA_LEVEL_ENGLISH from aviary.subsystems.atmosphere.atmosphere import Atmosphere from aviary.variable_info.functions import add_aviary_input, add_aviary_output from aviary.variable_info.variables import Aircraft, Dynamic, Mission @@ -10,36 +10,19 @@ class LandingCalc(om.ExplicitComponent): """Calculate the distance covered over the ground and approach velocity during landing.""" def setup(self): - add_aviary_input(self, Mission.FINAL_MASS, val=150_000) - - add_aviary_input( - self, - Dynamic.Atmosphere.DENSITY, - val=1.225, - units='kg/m**3', - desc='atmospheric density', - ) - - add_aviary_input(self, Aircraft.Wing.AREA, val=700) - - add_aviary_input(self, Mission.Landing.LIFT_COEFFICIENT_MAX, val=3) + add_aviary_input(self, Mission.FINAL_MASS, units='lbm') + add_aviary_input(self, Dynamic.Atmosphere.DENSITY, units='slug/ft**3') + add_aviary_input(self, Aircraft.Wing.AREA, units='ft**2') + add_aviary_input(self, Mission.Landing.LIFT_COEFFICIENT_MAX, units='unitless') - add_aviary_output(self, Mission.Landing.GROUND_DISTANCE, val=0) + add_aviary_output(self, Mission.Landing.GROUND_DISTANCE, units='ft') + add_aviary_output(self, Mission.Landing.INITIAL_VELOCITY, units='ft/s') - add_aviary_output(self, Mission.Landing.INITIAL_VELOCITY, val=0) - - self.declare_partials( - Mission.Landing.INITIAL_VELOCITY, - [ - Mission.Landing.LIFT_COEFFICIENT_MAX, - Aircraft.Wing.AREA, - Mission.FINAL_MASS, - ], - ) + self.declare_partials(Mission.Landing.INITIAL_VELOCITY, '*') self.declare_partials(Mission.Landing.GROUND_DISTANCE, '*') def compute(self, inputs, outputs): - rho_SL = RHO_SEA_LEVEL_METRIC + rho_SL = RHO_SEA_LEVEL_ENGLISH landing_weight = inputs[Mission.FINAL_MASS] * GRAV_ENGLISH_LBM rho = inputs[Dynamic.Atmosphere.DENSITY] planform_area = inputs[Aircraft.Wing.AREA] @@ -51,14 +34,16 @@ def compute(self, inputs, outputs): # distance covered during landing, which should be less. Cl_app = Cl_ldg_max / 1.3**2 - V_app = 17.18644 * (landing_weight / (planform_area * Cl_app)) ** 0.5 + + V_app = ((2 * landing_weight) / (rho * planform_area * Cl_app)) ** 0.5 + landing_distance = 2500 + 105 * landing_weight / (planform_area * rho_ratio * Cl_app * 1.69) outputs[Mission.Landing.GROUND_DISTANCE] = landing_distance outputs[Mission.Landing.INITIAL_VELOCITY] = V_app def compute_partials(self, inputs, J): - rho_SL = RHO_SEA_LEVEL_METRIC + rho_SL = RHO_SEA_LEVEL_ENGLISH landing_weight = inputs[Mission.FINAL_MASS] * GRAV_ENGLISH_LBM rho = inputs[Dynamic.Atmosphere.DENSITY] planform_area = inputs[Aircraft.Wing.AREA] @@ -68,29 +53,27 @@ def compute_partials(self, inputs, J): Cl_app = Cl_ldg_max / 1.3**2 - J[Mission.Landing.INITIAL_VELOCITY, Mission.Landing.LIFT_COEFFICIENT_MAX] = ( - 17.18644 - * 0.5 - * (landing_weight / (planform_area * Cl_app)) ** (-0.5) - * (-landing_weight) - / (planform_area * Cl_app**2) - / 1.3**2 + # INITIAL_VELOCITY (V_app) Partials + V_app = ((2 * landing_weight) / (rho * planform_area * Cl_app)) ** 0.5 + d_sqrt = 0.5 / V_app + + J[Mission.Landing.INITIAL_VELOCITY, Mission.FINAL_MASS] = ( + d_sqrt * (2 * GRAV_ENGLISH_LBM) / (rho * planform_area * Cl_app) + ) + + J[Mission.Landing.INITIAL_VELOCITY, Dynamic.Atmosphere.DENSITY] = ( + d_sqrt * (-2 * landing_weight) / (rho**2 * planform_area * Cl_app) ) + J[Mission.Landing.INITIAL_VELOCITY, Aircraft.Wing.AREA] = ( - 17.18644 - * 0.5 - * (landing_weight / (planform_area * Cl_app)) ** (-0.5) - * (-landing_weight) - / (planform_area**2 * Cl_app) + d_sqrt * (-2 * landing_weight) / (rho * planform_area**2 * Cl_app) ) - J[Mission.Landing.INITIAL_VELOCITY, Mission.FINAL_MASS] = ( - 17.18644 - * 0.5 - * (landing_weight / (planform_area * Cl_app)) ** (-0.5) - * GRAV_ENGLISH_LBM - / (planform_area * Cl_app) + + J[Mission.Landing.INITIAL_VELOCITY, Mission.Landing.LIFT_COEFFICIENT_MAX] = ( + d_sqrt * (-2 * landing_weight) / (rho * planform_area * Cl_app**2) / 1.3**2 ) + # GROUND DISTANCE Partials: J[Mission.Landing.GROUND_DISTANCE, Mission.FINAL_MASS] = ( 105 * GRAV_ENGLISH_LBM / (planform_area * rho_ratio * Cl_app * 1.69) ) @@ -107,8 +90,8 @@ def compute_partials(self, inputs, J): class LandingGroup(om.Group): """ - Calculate the distance covered over the ground and approach velocity - during landing with atmosphere is included. + Calculate the estimated Landing field length and velocity given the aircraft properties and atmospheric conditions. + Note this is not the actual distance covered over the ground. """ def setup(self): diff --git a/aviary/mission/energy_state/phases/test/test_building_landing.py b/aviary/mission/energy_state/phases/test/test_building_landing.py index 015e7968d..40ccd590a 100644 --- a/aviary/mission/energy_state/phases/test/test_building_landing.py +++ b/aviary/mission/energy_state/phases/test/test_building_landing.py @@ -5,7 +5,7 @@ from openmdao.utils.testing_utils import use_tempdirs from aviary.mission.energy_state.phases.build_landing import Landing -from aviary.variable_info.variables import Mission +from aviary.variable_info.variables import Aircraft, Mission @use_tempdirs @@ -13,16 +13,17 @@ class LandingPhaseTest(unittest.TestCase): """Test landing phase builder.""" def test_case1(self): - landing_options = Landing( - ref_wing_area=1370.0, # ft**2 - Cl_max_ldg=3, # no units - ) + landing_options = Landing() use_detailed = False landing = landing_options.build_phase(use_detailed=use_detailed) prob = om.Problem() prob.model = landing + prob.model.set_input_defaults(Mission.FINAL_MASS, val=150000.0, units='lbm') + prob.model.set_input_defaults(Mission.Landing.INITIAL_ALTITUDE, val=0, units='ft') + prob.model.set_input_defaults(Aircraft.Wing.AREA, val=1370.0, units='ft**2') + prob.model.set_input_defaults(Mission.Landing.LIFT_COEFFICIENT_MAX, val=3, units='unitless') prob.setup(force_alloc_complex=True) prob.run_model() partial_data = prob.check_partials( @@ -31,8 +32,10 @@ def test_case1(self): assert_check_partials(partial_data, atol=1e-12, rtol=1e-12) tol = 1e-6 - assert_near_equal(prob[Mission.Landing.GROUND_DISTANCE], 6332.4878059, tol) - assert_near_equal(prob[Mission.Landing.INITIAL_VELOCITY], 134.9752, tol) + assert_near_equal(prob[Mission.Landing.GROUND_DISTANCE], 6332.13214907, tol) + assert_near_equal( + prob.get_val(Mission.Landing.INITIAL_VELOCITY, units='kn'), 134.97550621, tol + ) if __name__ == '__main__': diff --git a/aviary/mission/energy_state/phases/test/test_simplified_landing.py b/aviary/mission/energy_state/phases/test/test_simplified_landing.py index 2620c1578..d77dd8618 100644 --- a/aviary/mission/energy_state/phases/test/test_simplified_landing.py +++ b/aviary/mission/energy_state/phases/test/test_simplified_landing.py @@ -21,20 +21,14 @@ def setUp(self): promotes=['*'], ) + self.prob.model.set_input_defaults(Mission.FINAL_MASS, val=152800.0, units='lbm') self.prob.model.set_input_defaults( - Mission.FINAL_MASS, val=152800.0, units='lbm' - ) # check (this is the design landing mass) - self.prob.model.set_input_defaults( - Dynamic.Atmosphere.DENSITY, - val=constants.RHO_SEA_LEVEL_METRIC, - units='kg/m**3', - ) # not exact value but should be close enough - self.prob.model.set_input_defaults( - Aircraft.Wing.AREA, val=1370.0, units='ft**2' - ) # check (this is the reference wing area) + Dynamic.Atmosphere.DENSITY, val=constants.RHO_SEA_LEVEL_METRIC, units='kg/m**3' + ) + self.prob.model.set_input_defaults(Aircraft.Wing.AREA, val=1370.0, units='ft**2') self.prob.model.set_input_defaults( Mission.Landing.LIFT_COEFFICIENT_MAX, val=3, units='unitless' - ) # check + ) self.prob.setup(check=False, force_alloc_complex=True) @@ -43,11 +37,10 @@ def test_case1(self): tol = 1e-5 + assert_near_equal(self.prob[Mission.Landing.GROUND_DISTANCE], 6403.64963504, tol) assert_near_equal( - self.prob[Mission.Landing.GROUND_DISTANCE], 6403.64963504, tol - ) # not actual value - # not actual value - assert_near_equal(self.prob[Mission.Landing.INITIAL_VELOCITY], 136.22914933, tol) + self.prob.get_val(Mission.Landing.INITIAL_VELOCITY, units='kn'), 136.22914933, tol + ) partial_data = self.prob.check_partials(out_stream=None, method='cs') assert_check_partials(partial_data, atol=1e-12, rtol=1e-12) @@ -73,6 +66,12 @@ def test_case1(self): LandingCalc(), promotes=['*'], ) + prob.model.set_input_defaults(Mission.FINAL_MASS, val=152800.0, units='lbm') + prob.model.set_input_defaults( + Dynamic.Atmosphere.DENSITY, val=constants.RHO_SEA_LEVEL_METRIC, units='kg/m**3' + ) + prob.model.set_input_defaults(Aircraft.Wing.AREA, val=1370.0, units='ft**2') + prob.model.set_input_defaults(Mission.Landing.LIFT_COEFFICIENT_MAX, val=3, units='unitless') prob.setup(check=False, force_alloc_complex=True) partial_data = prob.check_partials(out_stream=None, method='cs') @@ -91,18 +90,12 @@ def setUp(self): promotes=['*'], ) - self.prob.model.set_input_defaults( - Mission.FINAL_MASS, val=152800.0, units='lbm' - ) # check (this is the design landing mass) - self.prob.model.set_input_defaults( - Mission.Landing.INITIAL_ALTITUDE, val=35, units='ft' - ) # confirm initial altitude should be 35 ft. - self.prob.model.set_input_defaults( - Aircraft.Wing.AREA, val=1370.0, units='ft**2' - ) # check (this is the reference wing area) + self.prob.model.set_input_defaults(Mission.FINAL_MASS, val=152800.0, units='lbm') + self.prob.model.set_input_defaults(Mission.Landing.INITIAL_ALTITUDE, val=35, units='ft') + self.prob.model.set_input_defaults(Aircraft.Wing.AREA, val=1370.0, units='ft**2') self.prob.model.set_input_defaults( Mission.Landing.LIFT_COEFFICIENT_MAX, val=3, units='unitless' - ) # check + ) self.prob.setup(check=False, force_alloc_complex=True) @@ -111,11 +104,10 @@ def test_case1(self): tol = 1e-5 + assert_near_equal(self.prob[Mission.Landing.GROUND_DISTANCE], 6407.65299289, tol) assert_near_equal( - self.prob[Mission.Landing.GROUND_DISTANCE], 6407.65299289, tol - ) # not actual value - # not actual value - assert_near_equal(self.prob[Mission.Landing.INITIAL_VELOCITY], 136.22914933, tol) + self.prob.get_val(Mission.Landing.INITIAL_VELOCITY, units='kn'), 136.29923391, tol + ) partial_data = self.prob.check_partials( out_stream=None, excludes=['*.standard_atmosphere'], method='cs' diff --git a/aviary/mission/energy_state_problem_configurator.py b/aviary/mission/energy_state_problem_configurator.py index 09e761514..b9ca2812b 100644 --- a/aviary/mission/energy_state_problem_configurator.py +++ b/aviary/mission/energy_state_problem_configurator.py @@ -410,11 +410,7 @@ def add_post_mission_systems(self, aviary_group): ) if aviary_group.post_mission_info['include_landing']: - if 'aircraft:wing:area' in aviary_group.aviary_inputs: - self._add_landing_systems(aviary_group) - else: - print('Aircraft.Wing.AREA is not given. Set include_landing = False') - aviary_group.post_mission_info['include_landing'] = False + self._add_landing_systems(aviary_group) aviary_group.add_subsystem( 'range_constraint', @@ -490,14 +486,9 @@ def _add_post_mission_takeoff_systems(self, aviary_group): ) def _add_landing_systems(self, aviary_group): - landing_options = Landing( - ref_wing_area=aviary_group.aviary_inputs.get_val(Aircraft.Wing.AREA, units='ft**2'), - Cl_max_ldg=aviary_group.aviary_inputs.get_val( - Mission.Landing.LIFT_COEFFICIENT_MAX - ), # no units - ) + landing_options = Landing() - landing = landing_options.build_phase(False) + landing = landing_options.build_phase(use_detailed=False) aviary_group.add_subsystem( 'landing', diff --git a/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv b/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv index 3c02b09aa..a5d032e46 100644 --- a/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv @@ -90,7 +90,6 @@ aircraft:wing:thickness_to_chord_distribution,-1.0,0.15,0.1132,0.0928,0.0822,0.0 aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0,unitless mission:constraints:max_mach,0.85,unitless -mission:landing:initial_velocity,140,ft/s mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:rolling_friction_coefficient,0.025,unitless @@ -104,6 +103,7 @@ AERIN.FLTO,11000 AERIN.ITPAER,2 AERIN.MYAERO,0 AERIN.THROFF,0.25 +AERIN.VAPPR,140 AERIN.XLLAM,0 CONFIN.CH,39000 CONFIN.GW,874099 diff --git a/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv b/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv index 5598e7655..5da1a0206 100644 --- a/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv @@ -89,7 +89,6 @@ aircraft:wing:thickness_to_chord,0.11,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0,unitless mission:constraints:max_mach,0.85,unitless -mission:landing:initial_velocity,140,ft/s mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:rolling_friction_coefficient,0.025,unitless @@ -103,6 +102,7 @@ AERIN.FLTO,11000 AERIN.ITPAER,2 AERIN.MYAERO,0 AERIN.THROFF,0.25 +AERIN.VAPPR,140 AERIN.XLLAM,0 CONFIN.CH,39000 CONFIN.GW,874099 diff --git a/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv b/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv index e7df6bc29..88fcb949c 100644 --- a/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv @@ -155,7 +155,6 @@ aircraft:wing:wetted_area,2210.280228,ft**2 mission:constraints:max_mach,0.785,unitless mission:landing:drag_coefficient_min,0.045,unitless mission:landing:flare_rate,2.2,deg/s -mission:landing:initial_velocity,145.0,ft/s mission:landing:lift_coefficient_max,2.5,unitless mission:takeoff:angle_of_attack_runway,0.0,deg mission:takeoff:braking_friction_coefficient,0.35,unitless @@ -181,6 +180,7 @@ AERIN.flto,8190 AERIN.thrma,0 AERIN.throff,0.0 AERIN.thrss,0 +AERIN.vappr,145.0 AERIN.xllam,1 CONFIN.ch,43000.0,0.0,0.0,0.0,0.0,0.0 CONFIN.fixful,0 diff --git a/aviary/variable_info/variable_meta_data.py b/aviary/variable_info/variable_meta_data.py index dfef2a2cb..49c00d1ea 100644 --- a/aviary/variable_info/variable_meta_data.py +++ b/aviary/variable_info/variable_meta_data.py @@ -7202,8 +7202,8 @@ Mission.Landing.INITIAL_VELOCITY, meta_data=_MetaData, historical_name={ - 'GASP': 'INGASP.VGL', - 'FLOPS': 'AERIN.VAPPR', + 'GASP': 'DLAND.VGL', + 'FLOPS': 'SUMMARY.VAPP', }, units='ft/s', desc='approach velocity',