diff --git a/aviary/docs/getting_started/installation.md b/aviary/docs/getting_started/installation.md index e2484307d..d8d4fea7d 100644 --- a/aviary/docs/getting_started/installation.md +++ b/aviary/docs/getting_started/installation.md @@ -17,6 +17,10 @@ If you also want to install all packages used for the Aviary tests _and_ externa If you are a developer and plan to modify parts of the Aviary code, install in an "editable mode" with pip: + pip install . + +If you are a developer and plan to modify parts of the Aviary code, install in an "editable mode" with ``pip``: + pip install -e . This installs the package in the current environment such that changes to the Python code don't require re-installation. diff --git a/aviary/interface/default_phase_info/simple.py b/aviary/interface/default_phase_info/simple.py index 0efd931c3..29dcd56ea 100644 --- a/aviary/interface/default_phase_info/simple.py +++ b/aviary/interface/default_phase_info/simple.py @@ -39,6 +39,7 @@ "fix_duration": False, "initial_bounds": ((0.0, 0.0), "min"), "duration_bounds": ((64.0, 192.0), "min"), + "add_initial_mass_constraint": False, }, "initial_guesses": {"times": ([0, 128], "min")}, }, diff --git a/aviary/interface/graphical_input.py b/aviary/interface/graphical_input.py index babc6e72d..1fb3c7c18 100644 --- a/aviary/interface/graphical_input.py +++ b/aviary/interface/graphical_input.py @@ -145,6 +145,7 @@ def create_bounds(center): 'optimize_mach': optimize_mach_phase_vars[i].get(), 'optimize_altitude': optimize_altitude_phase_vars[i].get(), 'polynomial_control_order': polynomial_order, + 'use_polynomial_control': True, 'num_segments': num_segments, 'order': 3, 'solve_for_range': False, diff --git a/aviary/interface/methods_for_level2.py b/aviary/interface/methods_for_level2.py index cb9818663..f932b0eef 100644 --- a/aviary/interface/methods_for_level2.py +++ b/aviary/interface/methods_for_level2.py @@ -217,6 +217,9 @@ def load_inputs(self, input_filename, phase_info=None, engine_builder=None): elif self.mission_method is SOLVED: from aviary.interface.default_phase_info.solved import phase_info + print('Loaded default phase_info for' + f'{self.mission_method.value.lower()} equations of motion') + # create a new dictionary that only contains the phases from phase_info self.phase_info = {} @@ -1196,7 +1199,7 @@ def add_post_mission_systems(self, include_landing=True): """ if include_landing and self.post_mission_info['include_landing']: - if self.mission_method is HEIGHT_ENERGY: + if self.mission_method is HEIGHT_ENERGY or self.mission_method is SIMPLE: self._add_flops_landing_systems() elif self.mission_method is TWO_DEGREES_OF_FREEDOM: self._add_gasp_landing_systems() @@ -2412,16 +2415,30 @@ def _add_flops_landing_systems(self): 'traj.climb.initial_states:mass') self.model.connect(Mission.Takeoff.GROUND_DISTANCE, 'traj.climb.initial_states:range') - self.model.connect(Mission.Takeoff.FINAL_VELOCITY, - 'traj.climb.initial_states:velocity') - self.model.connect(Mission.Takeoff.FINAL_ALTITUDE, - 'traj.climb.initial_states:altitude') + if self.mission_method is HEIGHT_ENERGY: + self.model.connect(Mission.Takeoff.FINAL_VELOCITY, + 'traj.climb.initial_states:velocity') + self.model.connect(Mission.Takeoff.FINAL_ALTITUDE, + 'traj.climb.initial_states:altitude') + else: + pass + # TODO: connect this correctly + # mass is the most important to connect but these others should + # be connected as well + # self.model.connect(Mission.Takeoff.FINAL_VELOCITY, + # 'traj.climb.initial_states:mach') + # self.model.connect(Mission.Takeoff.FINAL_ALTITUDE, + # 'traj.climb.controls:altitude') self.model.connect('traj.descent.states:mass', Mission.Landing.TOUCHDOWN_MASS, src_indices=[-1]) # TODO: approach velocity should likely be connected - self.model.connect('traj.descent.states:altitude', Mission.Landing.INITIAL_ALTITUDE, - src_indices=[-1]) + if self.mission_method is HEIGHT_ENERGY: + self.model.connect('traj.descent.states:altitude', Mission.Landing.INITIAL_ALTITUDE, + src_indices=[-1]) + else: + self.model.connect('traj.descent.control_values:altitude', Mission.Landing.INITIAL_ALTITUDE, + src_indices=[0]) def _add_gasp_landing_systems(self): self.model.add_subsystem( diff --git a/aviary/interface/test/test_check_phase_info.py b/aviary/interface/test/test_check_phase_info.py index cf455372b..1c0cf7fde 100644 --- a/aviary/interface/test/test_check_phase_info.py +++ b/aviary/interface/test/test_check_phase_info.py @@ -36,6 +36,12 @@ def test_correct_input_gasp(self): self.assertTrue(check_phase_info(phase_info_gasp, mission_method=TWO_DEGREES_OF_FREEDOM)) + def test_incorrect_mission_method(self): + # Let's pass an incorrect mission_method name + incorrect_mission_method = 'INVALID_METHOD' + with self.assertRaises(ValueError): + check_phase_info(phase_info_flops, mission_method=incorrect_mission_method) + if __name__ == '__main__': unittest.main() diff --git a/aviary/interface/test/test_phase_info.py b/aviary/interface/test/test_phase_info.py index 3d470a70b..1f960f088 100644 --- a/aviary/interface/test/test_phase_info.py +++ b/aviary/interface/test/test_phase_info.py @@ -94,8 +94,7 @@ def test_phase_info_parameterization_gasp(self): prob = AviaryProblem() - csv_path = pkg_resources.resource_filename( - "aviary", "models/small_single_aisle/small_single_aisle_GwGm.csv") + csv_path = "models/small_single_aisle/small_single_aisle_GwGm.csv" prob.load_inputs(csv_path, phase_info) prob.check_and_preprocess_inputs() @@ -131,8 +130,7 @@ def test_phase_info_parameterization_flops(self): prob = AviaryProblem() - csv_path = pkg_resources.resource_filename( - "aviary", "models/test_aircraft/aircraft_for_bench_FwFm.csv") + csv_path = "models/test_aircraft/aircraft_for_bench_FwFm.csv" prob.load_inputs(csv_path, phase_info) prob.check_and_preprocess_inputs() diff --git a/aviary/interface/utils/check_phase_info.py b/aviary/interface/utils/check_phase_info.py index 1e592be4d..cc40add8b 100644 --- a/aviary/interface/utils/check_phase_info.py +++ b/aviary/interface/utils/check_phase_info.py @@ -226,8 +226,10 @@ def check_phase_info(phase_info, mission_method): elif mission_method is SIMPLE: return else: - raise ValueError( - "Invalid mission_method. Please choose either 'FLOPS', 'GASP', 'simple', or 'solved'.") + possible_values = ["'"+e.value+"'" for e in EquationsOfMotion] + possible_values[-1] = "or " + possible_values[-1] + raise ValueError("Invalid mission_method. Please choose from " + + ", ".join(possible_values) + ".") # Check if all phases exist in phase_info for phase in phase_info: diff --git a/aviary/mission/flops_based/phases/simple_energy_phase.py b/aviary/mission/flops_based/phases/simple_energy_phase.py index e5ffd1928..3514be333 100644 --- a/aviary/mission/flops_based/phases/simple_energy_phase.py +++ b/aviary/mission/flops_based/phases/simple_energy_phase.py @@ -1,5 +1,3 @@ -from math import isclose - import dymos as dm from aviary.mission.flops_based.phases.phase_builder_base import ( @@ -154,6 +152,7 @@ def build_phase(self, aviary_options: AviaryValues = None): optimize_altitude = user_options.get_val('optimize_altitude') input_initial = user_options.get_val('input_initial') polynomial_control_order = user_options.get_item('polynomial_control_order')[0] + use_polynomial_control = user_options.get_val('use_polynomial_control') throttle_enforcement = user_options.get_val('throttle_enforcement') mach_bounds = user_options.get_item('mach_bounds') altitude_bounds = user_options.get_item('altitude_bounds') @@ -162,6 +161,8 @@ def build_phase(self, aviary_options: AviaryValues = None): initial_altitude = user_options.get_item('initial_altitude')[0] final_altitude = user_options.get_item('final_altitude')[0] solve_for_range = user_options.get_val('solve_for_range') + no_descent = user_options.get_val('no_descent') + no_climb = user_options.get_val('no_climb') ############## # Add States # @@ -205,13 +206,22 @@ def build_phase(self, aviary_options: AviaryValues = None): ################ # Add Controls # ################ - phase.add_polynomial_control( - Dynamic.Mission.MACH, - targets=Dynamic.Mission.MACH, units=mach_bounds[1], - opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1], - rate_targets=[Dynamic.Mission.MACH_RATE], - order=polynomial_control_order, ref=0.5, - ) + if use_polynomial_control: + phase.add_polynomial_control( + Dynamic.Mission.MACH, + targets=Dynamic.Mission.MACH, units=mach_bounds[1], + opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1], + rate_targets=[Dynamic.Mission.MACH_RATE], + order=polynomial_control_order, ref=0.5, + ) + else: + phase.add_control( + Dynamic.Mission.MACH, + targets=Dynamic.Mission.MACH, units=mach_bounds[1], + opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1], + rate_targets=[Dynamic.Mission.MACH_RATE], + ref=0.5, + ) if optimize_mach and fix_initial: phase.add_boundary_constraint( @@ -224,13 +234,22 @@ def build_phase(self, aviary_options: AviaryValues = None): ) # Add altitude rate as a control - phase.add_polynomial_control( - Dynamic.Mission.ALTITUDE, - targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1], - opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1], - rate_targets=[Dynamic.Mission.ALTITUDE_RATE], - order=polynomial_control_order, ref=altitude_bounds[0][1], - ) + if use_polynomial_control: + phase.add_polynomial_control( + Dynamic.Mission.ALTITUDE, + targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1], + opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1], + rate_targets=[Dynamic.Mission.ALTITUDE_RATE], + order=polynomial_control_order, ref=altitude_bounds[0][1], + ) + else: + phase.add_control( + Dynamic.Mission.ALTITUDE, + targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1], + opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1], + rate_targets=[Dynamic.Mission.ALTITUDE_RATE], + ref=altitude_bounds[0][1], + ) if optimize_altitude and fix_initial: phase.add_boundary_constraint( @@ -278,9 +297,20 @@ def build_phase(self, aviary_options: AviaryValues = None): output_name=Dynamic.Mission.THROTTLE, units='unitless' ) + phase.add_timeseries_output( + Dynamic.Mission.VELOCITY, + output_name=Dynamic.Mission.VELOCITY, units='m/s' + ) + ################### # Add Constraints # ################### + if no_descent: + phase.add_path_constraint(Dynamic.Mission.ALTITUDE_RATE, lower=0.0) + + if no_climb: + phase.add_path_constraint(Dynamic.Mission.ALTITUDE_RATE, upper=0.0) + required_available_climb_rate, units = user_options.get_item( 'required_available_climb_rate') @@ -344,6 +374,8 @@ def _extra_ode_init_kwargs(self): EnergyPhase._add_meta_data('polynomial_control_order', val=None) +EnergyPhase._add_meta_data('use_polynomial_control', val=True) + EnergyPhase._add_meta_data('add_initial_mass_constraint', val=False) EnergyPhase._add_meta_data('fix_initial', val=True) @@ -360,6 +392,12 @@ def _extra_ode_init_kwargs(self): 'required_available_climb_rate', val=None, units='m/s', desc='minimum avaliable climb rate') +EnergyPhase._add_meta_data( + 'no_climb', val=False, desc='aircraft is not allowed to climb during phase') + +EnergyPhase._add_meta_data( + 'no_descent', val=False, desc='aircraft is not allowed to descend during phase') + EnergyPhase._add_meta_data('constrain_final', val=False) EnergyPhase._add_meta_data('input_initial', val=False) diff --git a/aviary/models/test_aircraft/aircraft_for_bench_GwFm_simple.csv b/aviary/models/test_aircraft/aircraft_for_bench_GwFm_simple.csv new file mode 100644 index 000000000..b0c9ace8d --- /dev/null +++ b/aviary/models/test_aircraft/aircraft_for_bench_GwFm_simple.csv @@ -0,0 +1,273 @@ +aircraft:air_conditioning:mass_scaler,1.0,unitless +aircraft:anti_icing:mass_scaler,1.0,unitless +aircraft:apu:mass_scaler,1.1,unitless +aircraft:avionics:mass_scaler,1.2,unitless +aircraft:canard:area,0.0,ft**2 +aircraft:canard:aspect_ratio,0.0,unitless +aircraft:canard:thickness_to_chord,0.0,unitless +aircraft:controls:cockpit_control_mass_scaler,1,unitless +aircraft:controls:control_mass_increment,0,lbm +aircraft:controls:stability_augmentation_system_mass_scaler,1,unitless +aircraft:controls:stability_augmentation_system_mass,0,lbm +aircraft:crew_and_payload:cargo_container_mass_scaler,1.0,unitless +aircraft:crew_and_payload:cargo_mass,10040,lbm +aircraft:crew_and_payload:flight_crew_mass_scaler,1.0,unitless +aircraft:crew_and_payload:mass_per_passenger,180.0,lbm +aircraft:crew_and_payload:misc_cargo,0.0,lbm +aircraft:crew_and_payload:non_flight_crew_mass_scaler,1.0,unitless +aircraft:crew_and_payload:num_business_class,0,unitless +aircraft:crew_and_payload:num_first_class,11,unitless +aircraft:crew_and_payload:num_flight_attendants,3,unitless +aircraft:crew_and_payload:num_flight_crew,2,unitless +aircraft:crew_and_payload:num_galley_crew,0,unitless +aircraft:crew_and_payload:num_non_flight_crew,3,unitless +aircraft:crew_and_payload:num_passengers,180,unitless +aircraft:crew_and_payload:num_tourist_class,158,unitless +aircraft:crew_and_payload:passenger_mass_with_bags,200,lbm +aircraft:crew_and_payload:passenger_service_mass_scaler,1.0,unitless +aircraft:crew_and_payload:wing_cargo,0.0,lbm +aircraft:design:base_area,0.0,ft**2 +aircraft:design:cg_delta,0.25,unitless +aircraft:design:cockpit_control_mass_coefficient,16.5,unitless +aircraft:design:compute_htail_volume_coeff,False,unitless +aircraft:design:compute_vtail_volume_coeff,False,unitless +aircraft:design:drag_increment,0.00175,unitless +aircraft:design:empty_mass_margin_scaler,0.0,unitless +aircraft:design:equipment_mass_coefficients,928.0,0.0736,0.112,0.14,1959.0,1.65,551.0,11192.0,5.0,3.0,50.0,7.6,12.0,unitless +aircraft:design:lift_curve_slope,7.1765,1/rad +aircraft:design:lift_dependent_drag_coeff_factor,0.909839381134961,unitless +aircraft:design:touchdown_mass,152800.0,lbm +aircraft:design:max_structural_speed,402.5,mi/h +aircraft:design:part25_structural_category,3,unitless +aircraft:design:reserves,5000.,unitless +aircraft:design:smooth_mass_discontinuities,False,unitless +aircraft:design:static_margin,0.03,unitless +aircraft:design:structural_mass_increment,0,lbm +aircraft:design:subsonic_drag_coeff_factor,1.0,unitless +aircraft:design:supercritical_drag_shift,0.033,unitless +aircraft:design:supersonic_drag_coeff_factor,1.0,unitless +aircraft:design:ulf_calculated_from_maneuver,False,unitless +aircraft:design:use_alt_mass,False,unitless +aircraft:design:zero_lift_drag_coeff_factor,0.930890028006548,unitless +aircraft:electrical:has_hybrid_system,False,unitless +aircraft:electrical:mass_scaler,1.25,unitless +aircraft:engine:constant_fuel_consumption,0.,lbm/h +aircraft:engine:data_file,models/engines/turbofan_28k.deck,unitless +aircraft:engine:engine_mass_specific,0.21366,lbm/lbf +aircraft:engine:flight_idle_thrust_fraction,0.0,unitless +aircraft:engine:flight_idle_max_fraction,1.0,unitless +aircraft:engine:flight_idle_min_fraction,0.08,unitless +aircraft:engine:fuel_flow_scaler_constant_term,0.,unitless +aircraft:engine:fuel_flow_scaler_linear_term,0.,unitless +aircraft:engine:generate_flight_idle,True,unitless +aircraft:engine:geopotential_alt,False,unitless +aircraft:engine:has_propellers,False,unitless +aircraft:engine:ignore_negative_thrust,False,unitless +aircraft:engine:interpolation_method,slinear,unitless +aircraft:engine:mass_scaler,1.0,unitless +aircraft:engine:mass,7400,lbm +aircraft:engine:additional_mass_fraction,0.14,unitless +aircraft:engine:num_engines,2,unitless +aircraft:engine:num_fuselage_engines,0,unitless +aircraft:engine:num_wing_engines,2,unitless +aircraft:engine:pod_mass_scaler,1,unitless +aircraft:engine:pylon_factor,1.25,unitless +aircraft:engine:reference_diameter,5.8,ft +aircraft:engine:reference_mass,7400,lbm +aircraft:engine:reference_sls_thrust,28928.1,lbf +aircraft:engine:scale_mass,True,unitless +aircraft:engine:scale_performance,True,unitless +aircraft:engine:scaled_sls_thrust,28928.1,lbf +aircraft:engine:subsonic_fuel_flow_scaler,1.,unitless +aircraft:engine:supersonic_fuel_flow_scaler,1.,unitless +aircraft:engine:thrust_reversers_mass_scaler,0.0,unitless +aircraft:engine:type,7,unitless +aircraft:engine:wing_locations,[0.26869218],unitless +aircraft:fins:area,0.0,ft**2 +aircraft:fins:mass_scaler,1.0,unitless +aircraft:fins:mass,0.0,lbm +aircraft:fins:num_fins,0,unitless +aircraft:fins:taper_ratio,10.0,unitless +aircraft:fuel:auxiliary_fuel_capacity,0.0,lbm +aircraft:fuel:density_ratio,1.0,unitless +aircraft:fuel:density,6.687,lbm/galUS +aircraft:fuel:fuel_margin,0,unitless +aircraft:fuel:fuel_system_mass_coefficient,0.041,unitless +aircraft:fuel:fuel_system_mass_scaler,1.0,unitless +aircraft:fuel:fuselage_fuel_capacity,0.0,lbm +aircraft:fuel:num_tanks,7,unitless +aircraft:fuel:total_capacity,45694.0,lbm +aircraft:fuel:unusable_fuel_mass_scaler,1.0,unitless +aircraft:fuel:wing_fuel_fraction,0,unitless +aircraft:fuel:wing_volume_geometric_max,1114.0,ft**3 +aircraft:furnishings:mass_scaler,1.1,unitless +aircraft:fuselage:aisle_width,24,inch +aircraft:fuselage:avg_diameter,12.75,ft +aircraft:fuselage:delta_diameter,4.5,ft +aircraft:fuselage:flat_plate_area_increment,0.25,ft**2 +aircraft:fuselage:form_factor,1.25,unitless +aircraft:fuselage:length,128.0,ft +aircraft:fuselage:mass_coefficient,128,unitless +aircraft:fuselage:mass_scaler,1.0,unitless +aircraft:fuselage:mass,18357.13345514,lbm +aircraft:fuselage:max_height,13.17,ft +aircraft:fuselage:max_width,12.33,ft +aircraft:fuselage:military_cargo_floor,False,unitless +aircraft:fuselage:nose_fineness,1,unitless +aircraft:fuselage:num_aisles,1,unitless +aircraft:fuselage:num_fuselages,1,unitless +aircraft:fuselage:num_seats_abreast,6,unitless +aircraft:fuselage:passenger_compartment_length,85.5,ft +aircraft:fuselage:pilot_compartment_length,9.5,ft +aircraft:fuselage:planform_area,1578.24,ft**2 +aircraft:fuselage:pressure_differential,7.5,psi +aircraft:fuselage:provide_surface_area,True,unitless +aircraft:fuselage:seat_pitch,29,inch +aircraft:fuselage:seat_width,20.2,inch +aircraft:fuselage:tail_fineness,3,unitless +aircraft:fuselage:wetted_area_factor,4000,unitless +aircraft:fuselage:wetted_area_scaler,1.0,unitless +aircraft:fuselage:wetted_area,4158.62,ft**2 +aircraft:horizontal_tail:area,355.0,ft**2 +aircraft:horizontal_tail:aspect_ratio,4.75,unitless +aircraft:horizontal_tail:average_chord,9.577,ft +aircraft:horizontal_tail:form_factor,1.25,unitless +aircraft:horizontal_tail:mass_coefficient,0.232,unitless +aircraft:horizontal_tail:mass_scaler,1.0,unitless +aircraft:horizontal_tail:moment_ratio,0.2307,unitless +aircraft:horizontal_tail:span,42.254,ft +aircraft:horizontal_tail:sweep,25,deg +aircraft:horizontal_tail:taper_ratio,0.22,unitless +aircraft:horizontal_tail:thickness_to_chord,0.125,unitless +aircraft:horizontal_tail:vertical_tail_fraction,0.0,unitless +aircraft:horizontal_tail:volume_coefficient,1.189,unitless +aircraft:horizontal_tail:wetted_area_scaler,1.0,unitless +aircraft:horizontal_tail:wetted_area,592.65,ft**2 +aircraft:hydraulics:mass_scaler,1.0,unitless +aircraft:hydraulics:system_pressure,3000.0,lbf/ft**2 +aircraft:instruments:mass_scaler,1.25,unitless +aircraft:landing_gear:carrier_based,False,unitless +aircraft:landing_gear:fixed_gear,True,unitless +aircraft:landing_gear:main_gear_location,0.15,unitless +aircraft:landing_gear:main_gear_mass_coefficient,0.85,unitless +aircraft:landing_gear:main_gear_mass_scaler,1.1,unitless +aircraft:landing_gear:main_gear_mass,6366.615,lbm +aircraft:landing_gear:main_gear_oleo_length,102.0,inch +aircraft:landing_gear:mass_coefficient,0.04,unitless +aircraft:landing_gear:nose_gear_mass_scaler,1.0,unitless +aircraft:landing_gear:nose_gear_oleo_length,67.0,inch +aircraft:landing_gear:tail_hook_mass_scaler,1,unitless +aircraft:landing_gear:total_mass_scaler,1,unitless +aircraft:nacelle:avg_diameter,7.94,ft +aircraft:nacelle:avg_length,12.3,ft +aircraft:nacelle:clearance_ratio,0.2,unitless +aircraft:nacelle:core_diameter_ratio,1.25,unitless +aircraft:nacelle:count_factor,2,unitless +aircraft:nacelle:fineness,2,unitless +aircraft:nacelle:form_factor,1.5,unitless +aircraft:nacelle:mass_scaler,1.0,unitless +aircraft:nacelle:mass_specific,3,lbm/ft**2 +aircraft:nacelle:surface_area,329.615,ft**2 +aircraft:nacelle:wetted_area_scaler,1.0,unitless +aircraft:paint:mass_per_unit_area,0.037,lbm/ft**2 +aircraft:propulsion:engine_oil_mass_scaler,1.0,unitless +aircraft:propulsion:misc_mass_scaler,1.0,unitless +aircraft:propulsion:total_num_engines,2,unitless +aircraft:strut:area_ratio,0,unitless +aircraft:strut:chord,0,ft +aircraft:strut:dimensional_location_specified,True,unitless +aircraft:strut:fuselage_interference_factor,0,unitless +aircraft:vertical_tail:area,284.0,ft**2 +aircraft:vertical_tail:aspect_ratio,1.75,unitless +aircraft:vertical_tail:average_chord,16.832,ft +aircraft:vertical_tail:form_factor,1.25,unitless +aircraft:vertical_tail:mass_coefficient,0.289,unitless +aircraft:vertical_tail:mass_scaler,1.0,unitless +aircraft:vertical_tail:moment_ratio,2.362,unitless +aircraft:vertical_tail:num_tails,1,unitless +aircraft:vertical_tail:span,27.996,ft +aircraft:vertical_tail:sweep,35,deg +aircraft:vertical_tail:taper_ratio,0.33,unitless +aircraft:vertical_tail:thickness_to_chord,0.1195,unitless +aircraft:vertical_tail:volume_coefficient,0.145,unitless +aircraft:vertical_tail:wetted_area_scaler,1.0,unitless +aircraft:vertical_tail:wetted_area,581.13,ft**2 +aircraft:wing:aeroelastic_tailoring_factor,0.0,unitless +aircraft:wing:airfoil_technology,1.92669766647637,unitless +aircraft:wing:area,1370.0,ft**2 +aircraft:wing:aspect_ratio,10.13,unitless +aircraft:wing:average_chord,12.615,ft +aircraft:wing:bending_mass_scaler,1.0,unitless +aircraft:wing:center_distance,0.463,unitless +aircraft:wing:choose_fold_location,True,unitless +aircraft:wing:chord_per_semispan,0.31,0.23,0.084,unitless +aircraft:wing:composite_fraction,0.2,unitless +aircraft:wing:control_surface_area,137,ft**2 +aircraft:wing:control_surface_area_ratio,0.1,unitless +aircraft:wing:flap_chord_ratio,0.3,unitless +aircraft:wing:flap_deflection_landing,40,deg +aircraft:wing:flap_deflection_takeoff,10,deg +aircraft:wing:flap_type,double_slotted,unitless +aircraft:wing:fold_dimensional_location_specified,False,unitless +aircraft:wing:form_factor,1.25,unitless +aircraft:wing:fuselage_interference_factor,1.1,unitless +aircraft:wing:glove_and_bat,134.0,ft**2 +aircraft:wing:has_fold,False,unitless +aircraft:wing:has_strut,False,unitless +aircraft:wing:height,8,ft +aircraft:wing:high_lift_mass_coefficient,1.9,unitless +aircraft:wing:incidence,0,deg +aircraft:wing:input_station_dist,0.,0.2759,0.9367,unitless +aircraft:wing:load_distribution_control,2.0,unitless +aircraft:wing:load_fraction,1.0,unitless +aircraft:wing:load_path_sweep_dist,0.,22.,deg +aircraft:wing:loading_above_20,True,unitless +aircraft:wing:loading,128.0,lbf/ft**2 +aircraft:wing:mass_coefficient,102.5,unitless +aircraft:wing:mass_scaler,1.0,unitless +aircraft:wing:max_camber_at_70_semispan,0.0,unitless +aircraft:wing:max_thickness_location,0.4,unitless +aircraft:wing:min_pressure_location,0.3,unitless +aircraft:wing:misc_mass_scaler,1.0,unitless +aircraft:wing:mounting_type,0,unitless +aircraft:wing:num_integration_stations,50,unitless +aircraft:wing:shear_control_mass_scaler,1.0,unitless +aircraft:wing:span_efficiency_reduction,False,unitless +aircraft:wing:span,117.83,ft +aircraft:wing:strut_bracing_factor,0.0,unitless +aircraft:wing:surface_ctrl_mass_coefficient,0.95,unitless +aircraft:wing:surface_ctrl_mass_scaler,1.0,unitless +aircraft:wing:sweep,25.0,deg +aircraft:wing:taper_ratio,0.33,unitless +aircraft:wing:thickness_to_chord_dist,0.145,0.115,0.104,unitless +aircraft:wing:thickness_to_chord_root,0.15,unitless +aircraft:wing:thickness_to_chord_tip,0.12,unitless +aircraft:wing:thickness_to_chord_unweighted,0.1397,unitless +aircraft:wing:thickness_to_chord,0.13,unitless +aircraft:wing:ultimate_load_factor,3.91650835,unitless +aircraft:wing:var_sweep_mass_penalty,0.0,unitless +aircraft:wing:wetted_area_scaler,1.0,unitless +aircraft:wing:wetted_area,2396.56,ft**2 +aircraft:wing:zero_lift_angle,-1.2,deg +mission:constraints:max_mach,0.785,unitless +mission:design:cruise_altitude,25000,ft +mission:design:gross_mass,175400.0,lbm +mission:design:lift_coefficient_max_flaps_up,1.2596,unitless +mission:design:range,3500,NM +mission:design:thrust_takeoff_per_eng,28928.1,lbf +mission:landing:airport_altitude,0,ft +mission:landing:drag_coefficient_flap_increment,0.0406,unitless +mission:landing:lift_coefficient_flap_increment,1.0293,unitless +mission:landing:lift_coefficient_max,2.8155,unitless +mission:summary:cruise_mach,0.785,unitless +mission:summary:fuel_flow_scaler,1.0,unitless +mission:summary:gross_mass,175400,lbm +mission:takeoff:airport_altitude,0,ft +mission:takeoff:drag_coefficient_flap_increment,0.0085,unitless +mission:takeoff:fuel_simple,577,lbm +mission:takeoff:lift_coefficient_flap_increment,0.4182,unitless +mission:takeoff:lift_coefficient_max,3.0,unitless +mission:takeoff:lift_over_drag,17.354,unitless +mission:takeoff:rolling_friction_coefficient,0.0175,unitless +settings:equations_of_motion,simple +settings:mass_method,GASP \ No newline at end of file diff --git a/aviary/validation_cases/benchmark_tests/test_bench_FwFm.py b/aviary/validation_cases/benchmark_tests/test_bench_FwFm.py index ef9c25759..67b81a3bc 100644 --- a/aviary/validation_cases/benchmark_tests/test_bench_FwFm.py +++ b/aviary/validation_cases/benchmark_tests/test_bench_FwFm.py @@ -1,5 +1,4 @@ from copy import deepcopy -import os import unittest import numpy as np @@ -9,15 +8,12 @@ from aviary.interface.methods_for_level1 import run_aviary from aviary.validation_cases.benchmark_utils import \ compare_against_expected_values +from aviary.variable_info.variables import Dynamic @use_tempdirs class ProblemPhaseTestCase(unittest.TestCase): - def bench_test_swap_4_FwFm(self): - local_phase_info = deepcopy(phase_info) - prob = run_aviary('models/test_aircraft/aircraft_for_bench_FwFm.csv', - local_phase_info) - + def setUp(self): expected_dict = {} expected_dict['times'] = np.array([[120.], @@ -265,9 +261,111 @@ def bench_test_swap_4_FwFm(self): [116.22447082], [102.07377559]]) - compare_against_expected_values(prob, expected_dict) + self.expected_dict = expected_dict + + def bench_test_swap_4_FwFm(self): + local_phase_info = deepcopy(phase_info) + prob = run_aviary('models/test_aircraft/aircraft_for_bench_FwFm.csv', + local_phase_info) + + compare_against_expected_values(prob, self.expected_dict) + + def bench_test_swap_4_FwFm_simple(self): + phase_info = { + "pre_mission": {"include_takeoff": True, "optimize_mass": True}, + "climb": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + 'fix_initial': {Dynamic.Mission.MASS: False, Dynamic.Mission.RANGE: False}, + 'input_initial': True, + "optimize_mach": True, + "optimize_altitude": True, + "polynomial_control_order": None, + "use_polynomial_control": False, + "num_segments": 6, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.2, "unitless"), + "final_mach": (0.79, "unitless"), + "mach_bounds": ((0.1, 0.8), "unitless"), + "initial_altitude": (0.0, "ft"), + "final_altitude": (35000.0, "ft"), + "altitude_bounds": ((0.0, 36000.0), "ft"), + "throttle_enforcement": "path_constraint", + "constrain_final": False, + "fix_duration": False, + "initial_bounds": ((0.0, 0.0), "min"), + "duration_bounds": ((5.0, 50.0), "min"), + "no_descent": True, + "add_initial_mass_constraint": False, + }, + "initial_guesses": {"times": ([0, 40.0], "min")}, + }, + "cruise": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + "optimize_mach": True, + "optimize_altitude": True, + "polynomial_control_order": 1, + "use_polynomial_control": True, + "num_segments": 1, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.79, "unitless"), + "final_mach": (0.79, "unitless"), + "mach_bounds": ((0.78, 0.8), "unitless"), + "initial_altitude": (35000.0, "ft"), + "final_altitude": (35000.0, "ft"), + "altitude_bounds": ((35000.0, 35000.0), "ft"), + "throttle_enforcement": "boundary_constraint", + "fix_initial": False, + "constrain_final": False, + "fix_duration": False, + "initial_bounds": ((64.0, 192.0), "min"), + "duration_bounds": ((60.0, 7200.0), "min"), + }, + "initial_guesses": {"times": ([128, 113], "min")}, + }, + "descent": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + "optimize_mach": True, + "optimize_altitude": True, + "polynomial_control_order": None, + "use_polynomial_control": False, + "num_segments": 5, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.79, "unitless"), + "final_mach": (0.3, "unitless"), + "mach_bounds": ((0.2, 0.8), "unitless"), + "initial_altitude": (35000.0, "ft"), + "final_altitude": (500.0, "ft"), + "altitude_bounds": ((0.0, 35000.0), "ft"), + "throttle_enforcement": "path_constraint", + "fix_initial": False, + "constrain_final": True, + "fix_duration": False, + "initial_bounds": ((120.5, 361.5), "min"), + "duration_bounds": ((5.0, 60.0), "min"), + "no_climb": False + }, + "initial_guesses": {"times": ([241, 58], "min")}, + }, + "post_mission": { + "include_landing": True, + "constrain_range": True, + "target_range": (3360.0, "nmi"), + }, + } + + prob = run_aviary( + 'models/test_aircraft/aircraft_for_bench_FwFm_simple.csv', phase_info) + + compare_against_expected_values(prob, self.expected_dict, simple_flag=True) if __name__ == '__main__': test = ProblemPhaseTestCase() - test.bench_test_swap_4_FwFm() + test.setUp() + test.bench_test_swap_4_FwFm_simple() diff --git a/aviary/validation_cases/benchmark_tests/test_bench_GwFm.py b/aviary/validation_cases/benchmark_tests/test_bench_GwFm.py index a6748628f..a33f0ee75 100644 --- a/aviary/validation_cases/benchmark_tests/test_bench_GwFm.py +++ b/aviary/validation_cases/benchmark_tests/test_bench_GwFm.py @@ -6,7 +6,6 @@ Large Single Aisle 1 data ''' from copy import deepcopy -import os import unittest import numpy as np @@ -16,15 +15,12 @@ from aviary.interface.methods_for_level1 import run_aviary from aviary.validation_cases.benchmark_utils import \ compare_against_expected_values +from aviary.variable_info.variables import Dynamic @use_tempdirs class ProblemPhaseTestCase(unittest.TestCase): - def bench_test_swap_1_GwFm(self): - local_phase_info = deepcopy(phase_info) - prob = run_aviary('models/test_aircraft/aircraft_for_bench_GwFm.csv', - local_phase_info) - + def setUp(self): expected_dict = {} expected_dict['times'] = np.array([[120.], [163.76271231], @@ -271,9 +267,109 @@ def bench_test_swap_1_GwFm(self): [116.34759903], [102.07377559]]) - compare_against_expected_values(prob, expected_dict) + self.expected_dict = expected_dict + + def bench_test_swap_1_GwFm(self): + local_phase_info = deepcopy(phase_info) + prob = run_aviary( + 'models/test_aircraft/aircraft_for_bench_GwFm.csv', local_phase_info) + + compare_against_expected_values(prob, self.expected_dict) + + def bench_test_swap_1_GwFm_simple(self): + phase_info = { + "pre_mission": {"include_takeoff": True, "optimize_mass": True}, + "climb": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + 'fix_initial': {Dynamic.Mission.MASS: False, Dynamic.Mission.RANGE: False}, + 'input_initial': True, + "optimize_mach": True, + "optimize_altitude": True, + "use_polynomial_control": False, + "num_segments": 6, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.2, "unitless"), + "final_mach": (0.79, "unitless"), + "mach_bounds": ((0.1, 0.8), "unitless"), + "initial_altitude": (0.0, "ft"), + "final_altitude": (35000.0, "ft"), + "altitude_bounds": ((0.0, 36000.0), "ft"), + "throttle_enforcement": "path_constraint", + "constrain_final": False, + "fix_duration": False, + "initial_bounds": ((0.0, 0.0), "min"), + "duration_bounds": ((5.0, 50.0), "min"), + "no_descent": True, + "add_initial_mass_constraint": False, + }, + "initial_guesses": {"times": ([0, 40.0], "min")}, + }, + "cruise": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + "optimize_mach": True, + "optimize_altitude": True, + "polynomial_control_order": 1, + "use_polynomial_control": True, + "num_segments": 1, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.79, "unitless"), + "final_mach": (0.79, "unitless"), + "mach_bounds": ((0.78, 0.8), "unitless"), + "initial_altitude": (35000.0, "ft"), + "final_altitude": (35000.0, "ft"), + "altitude_bounds": ((35000.0, 35000.0), "ft"), + "throttle_enforcement": "boundary_constraint", + "fix_initial": False, + "constrain_final": False, + "fix_duration": False, + "initial_bounds": ((64.0, 192.0), "min"), + "duration_bounds": ((60.0, 7200.0), "min"), + }, + "initial_guesses": {"times": ([128, 113], "min")}, + }, + "descent": { + "subsystem_options": {"core_aerodynamics": {"method": "computed"}}, + "user_options": { + "optimize_mach": True, + "optimize_altitude": True, + "use_polynomial_control": False, + "num_segments": 5, + "order": 3, + "solve_for_range": False, + "initial_mach": (0.79, "unitless"), + "final_mach": (0.3, "unitless"), + "mach_bounds": ((0.2, 0.8), "unitless"), + "initial_altitude": (35000.0, "ft"), + "final_altitude": (500.0, "ft"), + "altitude_bounds": ((0.0, 35000.0), "ft"), + "throttle_enforcement": "path_constraint", + "fix_initial": False, + "constrain_final": True, + "fix_duration": False, + "initial_bounds": ((120.5, 361.5), "min"), + "duration_bounds": ((5.0, 60.0), "min"), + "no_climb": False + }, + "initial_guesses": {"times": ([241, 58], "min")}, + }, + "post_mission": { + "include_landing": True, + "constrain_range": True, + "target_range": (3360.0, "nmi"), + }, + } + + prob = run_aviary('models/test_aircraft/aircraft_for_bench_GwFm_simple.csv', phase_info, + max_iter=15) + + compare_against_expected_values(prob, self.expected_dict, simple_flag=True) if __name__ == '__main__': - z = ProblemPhaseTestCase() - z.bench_test_swap_1_GwFm() + test = ProblemPhaseTestCase() + test.setUp() + test.bench_test_swap_1_GwFm_simple() diff --git a/aviary/validation_cases/benchmark_utils.py b/aviary/validation_cases/benchmark_utils.py index 8540817a2..22b1b97dd 100644 --- a/aviary/validation_cases/benchmark_utils.py +++ b/aviary/validation_cases/benchmark_utils.py @@ -5,7 +5,7 @@ from aviary.utils.test_utils.assert_utils import warn_timeseries_near_equal -def compare_against_expected_values(prob, expected_dict): +def compare_against_expected_values(prob, expected_dict, simple_flag=False): expected_times = expected_dict['times'] expected_altitudes = expected_dict['altitudes'] @@ -22,14 +22,24 @@ def compare_against_expected_values(prob, expected_dict): for idx, phase in enumerate(['climb', 'cruise', 'descent']): times.extend(prob.get_val(f'traj.{phase}.timeseries.time', units='s')) - altitudes.extend(prob.get_val( - f'traj.{phase}.timeseries.states:altitude', units='m')) + if simple_flag: + try: + altitudes.extend(prob.get_val( + f'traj.{phase}.timeseries.polynomial_controls:altitude', units='m')) + except KeyError: + altitudes.extend(prob.get_val( + f'traj.{phase}.timeseries.controls:altitude', units='m')) + velocities.extend(prob.get_val( + f'traj.{phase}.timeseries.velocity', units='m/s')) + else: + altitudes.extend(prob.get_val( + f'traj.{phase}.timeseries.states:altitude', units='m')) + velocities.extend(prob.get_val( + f'traj.{phase}.timeseries.states:velocity', units='m/s')) masses.extend( prob.get_val(f'traj.{phase}.timeseries.states:mass', units='kg')) ranges.extend( prob.get_val(f'traj.{phase}.timeseries.states:range', units='m')) - velocities.extend(prob.get_val( - f'traj.{phase}.timeseries.states:velocity', units='m/s')) times = np.array(times) altitudes = np.array(altitudes)