From 078f79a67c5ce4bcf82876903e24a85bed37a515 Mon Sep 17 00:00:00 2001 From: Bennett Date: Wed, 8 Apr 2026 19:23:03 -0400 Subject: [PATCH 1/4] change fallout to off_design_max_range --- aviary/core/aviary_group.py | 8 +- aviary/core/aviary_problem.py | 32 +++--- .../docs/examples/off_design_missions.ipynb | 76 ++++++++----- .../docs/user_guide_unreviewed/UI_levels.md | 2 +- .../off_design_missions.ipynb | 10 +- .../onboarding_level2.ipynb | 6 +- aviary/interface/test/test_reports.py | 2 +- aviary/interface/test/test_save_results.py | 8 +- .../large_turboprop_freighter_GASP.csv | 2 +- aviary/utils/fortran_to_aviary.py | 8 +- aviary/utils/process_input_decks.py | 10 +- .../benchmark_tests/test_bench_off_design.py | 100 ++++++++++-------- .../test_problem_types_GwGm.py | 82 +++++++------- aviary/variable_info/enums.py | 4 +- aviary/variable_info/variable_meta_data.py | 2 +- aviary/visualization/dashboard.py | 2 +- 16 files changed, 196 insertions(+), 158 deletions(-) diff --git a/aviary/core/aviary_group.py b/aviary/core/aviary_group.py index 9402c37a20..55224b6e9a 100644 --- a/aviary/core/aviary_group.py +++ b/aviary/core/aviary_group.py @@ -1342,7 +1342,7 @@ def add_design_variables(self, problem_type: ProblemType = None, verbosity=None) if self.require_range_residual: self.add_constraint(Mission.Constraints.RANGE_RESIDUAL, equals=0, ref=1000) - elif problem_type is ProblemType.ALTERNATE: + elif problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: # target range problem # fixed vehicle (design GTOW) but variable actual GTOW for off-design # get the design gross mass and set as the upper bound for the gross mass design variable @@ -1357,10 +1357,12 @@ def add_design_variables(self, problem_type: ProblemType = None, verbosity=None) self.add_constraint(Mission.Constraints.RANGE_RESIDUAL, equals=0, ref=1000) - elif problem_type is ProblemType.FALLOUT: + elif problem_type is ProblemType.OFF_DESIGN_MAX_RANGE: # fixed vehicle gross mass aviary finds optimal trajectory and maximum range if verbosity >= Verbosity.VERBOSE: - print('No additional aircraft design variables added for Fallout missions') + print( + 'No additional aircraft design variables added for OFF_DESIGN_MAX_RANGE missions' + ) elif problem_type is ProblemType.MULTI_MISSION: self.add_design_var( diff --git a/aviary/core/aviary_problem.py b/aviary/core/aviary_problem.py index f84ca0e6bc..25272f8b6a 100644 --- a/aviary/core/aviary_problem.py +++ b/aviary/core/aviary_problem.py @@ -693,10 +693,10 @@ def add_objective(self, objective_type=None, ref=None, verbosity=None): if self.problem_type is ProblemType.SIZING: self.model.add_objective(Mission.Objectives.FUEL, ref=ref) - elif self.problem_type is ProblemType.ALTERNATE: + elif self.problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: self.model.add_objective(Mission.Objectives.FUEL, ref=ref) - elif self.problem_type is ProblemType.FALLOUT: + elif self.problem_type is ProblemType.OFF_DESIGN_MAX_RANGE: # if ref > 0: # # Maximize range. # ref = -ref @@ -785,7 +785,7 @@ def add_composite_objective(self, *args, ref: float = None): If 2-tuple: (model, output) or (output, weight) If 1-tuple: (output) or 'fuel', 'fuel_burned', 'mass', 'range', 'time' If empty, information will be populated based on problem_type: - - If ProblemType = FALLOUT, objective = Mission.Objectives.RANGE + - If ProblemType = OFF_DESIGN_MAX_RANGE, objective = Mission.Objectives.RANGE - If ProblemType = Sizing or Alternate, objective = Mission.Objectives.FUEL Example inputs can be any of the following: @@ -857,9 +857,9 @@ def add_composite_objective(self, *args, ref: float = None): # in some cases the users provides no input and we can derive the objectie from the problem type: elif self.model.problem_type is ProblemType.SIZING: model, output, weight = default_model, Mission.Objectives.FUEL, default_weight - elif self.model.problem_type is ProblemType.ALTERNATE: + elif self.model.problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: model, output, weight = default_model, Mission.Objectives.FUEL, default_weight - elif self.model.problem_type is ProblemType.FALLOUT: + elif self.model.problem_type is ProblemType.OFF_DESIGN_MAX_RANGE: model, output, weight = default_model, Mission.Objectives.RANGE, default_weight else: raise ValueError( @@ -867,7 +867,7 @@ def add_composite_objective(self, *args, ref: float = None): f'Each argument must be one of the following: ' f'(output), (output, weight), (model, output), or (model, output, weight).' f'Outputs can be from the variable meta data, or can be: fuel_burned, fuel' - f'Or problem type must be set to SIZING, ALTERNATE, or FALLOUT' + f'Or problem type must be set to SIZING, ALTERNATE, or OFF_DESIGN_MAX_RANGE' ) objectives.append((model, output, weight)) # objectives = [ @@ -1433,7 +1433,7 @@ def run_off_design_mission( # NOTE once load_inputs is run, phase info details are stored in prob.model.configurator, # meaning any phase_info changes that happen after load inputs is ignored - if problem_type is ProblemType.ALTERNATE: + if problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: # Set mission range, aviary will calculate required fuel if mission_range is None: if verbosity >= Verbosity.VERBOSE: @@ -1452,9 +1452,9 @@ def run_off_design_mission( off_design_prob.load_inputs(inputs, phase_info, verbosity=verbosity) # Update inputs that are specific to problem type - # Some Alternate problem changes had to happen before load_inputs, all fallout problem + # Some Alternate problem changes had to happen before load_inputs, all OFF_DESIGN_MAX_RANGE problem # changes must come after load_inputs - if problem_type is ProblemType.ALTERNATE: + if problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: off_design_prob.aviary_inputs.set_val(Mission.RANGE, mission_range, units='NM') # set initial guess for Mission.GROSS_MASS to help optimizer with new design # variable bounds. @@ -1466,12 +1466,12 @@ def run_off_design_mission( Mission.GROSS_MASS, mission_gross_mass * 0.9, units='lbm' ) - elif problem_type is ProblemType.FALLOUT: + elif problem_type is ProblemType.OFF_DESIGN_MAX_RANGE: # Set mission fuel and calculate gross weight, aviary will calculate range if mission_gross_mass is None: if verbosity >= Verbosity.VERBOSE: warnings.warn( - 'Fallout problem type requested with no specified gross mass. Using design ' + 'OFF_DESIGN_MAX_RANGE problem type requested with no specified gross mass. Using design ' 'takeoff gross mass for the off-design mission.' ) mission_gross_mass = self.get_val(Aircraft.Design.GROSS_MASS, units='lbm')[0] @@ -1683,7 +1683,7 @@ def run_payload_range(self, verbosity=None): # we don't know if we actually filled the aircraft to exactly TOGW yet. Need to use # "fill_cargo" flag in off-design call economic_range_prob = self.economic_range_prob = self.run_off_design_mission( - problem_type=ProblemType.FALLOUT, + problem_type=ProblemType.OFF_DESIGN_MAX_RANGE, phase_info=phase_info, num_first_class=economic_mission_num_first, num_business=economic_mission_num_bus, @@ -1695,7 +1695,7 @@ def run_payload_range(self, verbosity=None): verbosity=verbosity, ) - # Pull the payload and range values from the fallout mission + # Pull the payload and range values from the OFF_DESIGN_MAX_RANGE mission payload_3 = float( economic_range_prob.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS) ) @@ -1718,7 +1718,7 @@ def run_payload_range(self, verbosity=None): ferry_cargo_mass = None ferry_range_gross_mass = operating_mass + max_usable_fuel ferry_range_prob = self.ferry_range_prob = self.run_off_design_mission( - problem_type=ProblemType.FALLOUT, + problem_type=ProblemType.OFF_DESIGN_MAX_RANGE, phase_info=phase_info, num_first_class=0, num_business=0, @@ -1742,7 +1742,7 @@ def run_payload_range(self, verbosity=None): payload_3 = payload_4 range_3 = range_4 - # Check if fallout missions ran successfully before writing to csv file + # Check if OFF_DESIGN_MAX_RANGE missions ran successfully before writing to csv file # If both missions ran successfully, writes the payload/range data to a csv file self.payload_range_data = payload_range_data = NamedValues() if ferry_range_prob.result.success and economic_range_prob.result.success: @@ -1769,7 +1769,7 @@ def run_payload_range(self, verbosity=None): return (economic_range_prob, ferry_range_prob) else: warnings.warn( - 'One or both of the fallout missions did not run successfully; payload/range ' + 'One or both of the OFF_DESIGN_MAX_RANGE missions did not run successfully; payload/range ' 'diagram was not generated.' ) else: diff --git a/aviary/docs/examples/off_design_missions.ipynb b/aviary/docs/examples/off_design_missions.ipynb index 69f34a7880..1b10867b67 100644 --- a/aviary/docs/examples/off_design_missions.ipynb +++ b/aviary/docs/examples/off_design_missions.ipynb @@ -60,7 +60,7 @@ "id": "f1a098f9", "metadata": {}, "source": [ - "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is a \"fallout\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"alternate\" mission, where the range of the mission is known, and Aviary solves the exact amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", + "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is a \"off_deign_max_range\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"alternate\" mission, where the range of the mission is known, and Aviary solves the exact amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", "\n", "An off-design mission is run using the {glue:md}`run_off_design_mission()` method of {glue:md}`AviaryProblem`. This method has significantly more flexibility than demonstrated in this example, including the ability to change the passengers and cargo loaded on the aircraft, and the option to change the mission profile flown via providing new phase_info. \n", "\n", @@ -74,9 +74,11 @@ "metadata": {}, "outputs": [], "source": [ - "# Fallout Mission - fixed-mass, varying range\n", - "fallout_prob = design_prob.run_off_design_mission(\n", - " problem_type='fallout', mission_gross_mass=115000, name='fallout_mission'\n", + "# OFF_DESIGN_MAX_RANGE Mission - fixed-mass, varying range\n", + "off_design_max_range_prob = design_prob.run_off_design_mission(\n", + " problem_type='off_design_max_range',\n", + " mission_gross_mass=115000,\n", + " name='off_design_max_range_mission',\n", ")\n", "\n", "# Alternate Mission - fixed range, varying fuel\n", @@ -90,7 +92,7 @@ "id": "d4f57227", "metadata": {}, "source": [ - "Once both missions are complete, we can compare the difference between the design and off-design missions. We are expecting fundamental design parameters of the aircraft, such as its maximum takeoff gross mass and its design range to remain unchanged. However, the takeoff gross mass for each particular mission should change, due to a change in the amount of fuel the aircraft is loaded with before takeoff, as well as mission range. We should see a reduction in both of these quantities for the off-design missions, with the pre-defined properties (gross mass for the fallout mission, mission range for the alternate mission) matching what we asked Aviary to fly." + "Once both missions are complete, we can compare the difference between the design and off-design missions. We are expecting fundamental design parameters of the aircraft, such as its maximum takeoff gross mass and its design range to remain unchanged. However, the takeoff gross mass for each particular mission should change, due to a change in the amount of fuel the aircraft is loaded with before takeoff, as well as mission range. We should see a reduction in both of these quantities for the off-design missions, with the pre-defined properties (gross mass for the off_design_max_range mission, mission range for the alternate mission) matching what we asked Aviary to fly." ] }, { @@ -114,15 +116,21 @@ "print(f'Design Gross Mass = {design_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm')\n", "print(f'Mission Gross Mass = {design_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')\n", "\n", - "print('\\nFallout Results')\n", + "print('\\nOFF_DESIGN_MAX_RANGE Results')\n", "print('---------------')\n", - "print(f'Design Range = {fallout_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", - "print(f'Mission Range = {fallout_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", - "print(f'Fuel Mass = {fallout_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", - "print(f'Operating Empty Mass = {fallout_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm')\n", - "print(f'Payload Mass = {fallout_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm')\n", - "print(f'Design Gross Mass = {fallout_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm')\n", - "print(f'Mission Gross Mass = {fallout_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')\n", + "print(f'Design Range = {off_design_max_range_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", + "print(f'Mission Range = {off_design_max_range_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", + "print(f'Fuel Mass = {off_design_max_range_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", + "print(\n", + " f'Operating Empty Mass = {off_design_max_range_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm'\n", + ")\n", + "print(\n", + " f'Payload Mass = {off_design_max_range_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm'\n", + ")\n", + "print(\n", + " f'Design Gross Mass = {off_design_max_range_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm'\n", + ")\n", + "print(f'Mission Gross Mass = {off_design_max_range_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')\n", "\n", "print('\\nAlternate Results')\n", "print('-----------------')\n", @@ -145,7 +153,7 @@ "\n", "This is done with a helper function {glue:md}`reload_aviary_problem()`. This function returns a fresh {glue:md}`AviaryProblem` loaded with all of the information from the sizing run. To reconstruct the design aircraft, {glue:md}`reload_aviary_problem` needs two pieces of information: a specific results file generated by Aviary after sizing an aircraft, and the original phase info used by the design mission.\n", "\n", - "Here we will reload the design problem, then use that reloaded problem to run the same \"fallout\" mission as before. When viewing the results, we can see they exactly match the results we got earlier." + "Here we will reload the design problem, then use that reloaded problem to run the same \"off_design_max_range\" mission as before. When viewing the results, we can see they exactly match the results we got earlier." ] }, { @@ -159,8 +167,10 @@ " 'advanced_single_aisle_FLOPS_out/reports/sizing_results.json', phase_info\n", ")\n", "\n", - "new_fallout_prob = design_prob.run_off_design_mission(\n", - " problem_type='fallout', mission_gross_mass=115000, name='fallout_mission'\n", + "new_off_design_max_range_prob = design_prob.run_off_design_mission(\n", + " problem_type='off_design_max_range',\n", + " mission_gross_mass=115000,\n", + " name='off_design_max_range_mission',\n", ")" ] }, @@ -175,17 +185,21 @@ }, "outputs": [], "source": [ - "print('\\nNew Fallout Results')\n", + "print('\\nNew OFF_DESIGN_MAX_RANGE Results')\n", "print('---------------')\n", - "print(f'Design Range = {new_fallout_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", - "print(f'Mission Range = {new_fallout_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", - "print(f'Fuel Mass = {new_fallout_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", - "print(f'Operating Empty Mass = {new_fallout_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm')\n", + "print(f'Design Range = {new_off_design_max_range_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", + "print(f'Mission Range = {new_off_design_max_range_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", + "print(f'Fuel Mass = {new_off_design_max_range_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", + "print(\n", + " f'Operating Empty Mass = {new_off_design_max_range_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm'\n", + ")\n", + "print(\n", + " f'Payload Mass = {new_off_design_max_range_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm'\n", + ")\n", "print(\n", - " f'Payload Mass = {new_fallout_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm'\n", + " f'Design Gross Mass = {new_off_design_max_range_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm'\n", ")\n", - "print(f'Design Gross Mass = {new_fallout_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm')\n", - "print(f'Mission Gross Mass = {new_fallout_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')" + "print(f'Mission Gross Mass = {new_off_design_max_range_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')" ] }, { @@ -202,14 +216,14 @@ "if not design_prob.result.success:\n", " raise ValueError('Design problem failed')\n", "\n", - "if not fallout_prob.result.success:\n", - " raise ValueError('Fallout problem failed')\n", + "if not off_design_max_range_prob.result.success:\n", + " raise ValueError('OFF_DESIGN_MAX_RANGE problem failed')\n", "\n", "if not alternate_prob.result.success:\n", " raise ValueError('Alternate problem failed')\n", "\n", - "if not new_fallout_prob.result.success:\n", - " raise ValueError('Fallout problem 2 failed')\n", + "if not new_off_design_max_range_prob.result.success:\n", + " raise ValueError('OFF_DESIGN_MAX_RANGE problem 2 failed')\n", "\n", "from openmdao.utils.assert_utils import assert_near_equal\n", "\n", @@ -224,7 +238,11 @@ "]\n", "\n", "for var in comparison_list:\n", - " assert_near_equal(fallout_prob.get_val(var), new_fallout_prob.get_val(var), tolerance=1e-6)" + " assert_near_equal(\n", + " off_design_max_range_prob.get_val(var),\n", + " new_off_design_max_range_prob.get_val(var),\n", + " tolerance=1e-6,\n", + " )" ] } ], diff --git a/aviary/docs/user_guide_unreviewed/UI_levels.md b/aviary/docs/user_guide_unreviewed/UI_levels.md index 328dbd6f57..89a036b280 100644 --- a/aviary/docs/user_guide_unreviewed/UI_levels.md +++ b/aviary/docs/user_guide_unreviewed/UI_levels.md @@ -68,7 +68,7 @@ Levels 2 and 3 will require code to control the more detailed aspects of the ana | **Level 1** | **Level 2** | **Level 3** | |:--------------------------------------------:|:---------------------------------:|:----------------------:| -| Mission Type (Sizing, Fallout) | All Level 1 Controls | All Level 2 Controls | +| Mission Type (Sizing, OFF_DESIGN_MAX_RANGE) | All Level 1 Controls | All Level 2 Controls | | Optimizer (IPOPT, SNOPT) | Specify phase order and options | Custom ODEs and EOMs | | Mission Description (Altitude, Mach, etc) | | | diff --git a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb index 664f25e1ff..0ca5bff286 100644 --- a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb +++ b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb @@ -17,15 +17,15 @@ "\n", "str_problem_type = Settings.PROBLEM_TYPE\n", "str_sizing = av.ProblemType.SIZING.value\n", - "str_alternate = av.ProblemType.ALTERNATE.value\n", - "str_fallout = av.ProblemType.FALLOUT.value\n", + "str_alternate = av.ProblemType.OFF_DESIGN_MIN_FUEL.value\n", + "str_off_design_max_range = av.ProblemType.OFF_DESIGN_MAX_RANGE.value\n", "\n", "\n", "str_alternate_snippet = f'```\\n{str_problem_type}, {str_alternate}\\n```'\n", "glue_variable('alternate_snippet', str_alternate_snippet, md_code=False)\n", "\n", - "fallout_snippet = f'```\\n{str_problem_type}, {str_fallout}\\n```'\n", - "glue_variable('fallout_snippet', fallout_snippet, md_code=False)\n", + "off_design_max_range_snippet = f'```\\n{str_problem_type}, {str_off_design_max_range}\\n```'\n", + "glue_variable('off_design_max_range_snippet', off_design_max_range_snippet, md_code=False)\n", "\n", "check_value(av.EquationsOfMotion.ENERGY_STATE.value, 'energy_state')\n", "check_value(av.EquationsOfMotion.TWO_DEGREES_OF_FREEDOM.value, '2DOF')\n", @@ -303,7 +303,7 @@ "# Use specific _load_off_design function to define the off-design mission.\n", "prob_alternate = _load_off_design(\n", " json_filename='off_design_documentation_example.json',\n", - " problem_type=av.ProblemType.ALTERNATE,\n", + " problem_type=av.ProblemType.OFF_DESIGN_MIN_FUEL,\n", " equations_of_motion=av.EquationsOfMotion.ENERGY_STATE,\n", " mass_method=LegacyCode.FLOPS,\n", " phase_info=phase_info,\n", diff --git a/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb b/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb index ff480bcf04..a300fb9e17 100644 --- a/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb +++ b/aviary/docs/user_guide_unreviewed/onboarding_level2.ipynb @@ -128,7 +128,7 @@ "| ------------ | --------- |\n", "| {glue:md}`SIZING` | {glue:md}`Mission.Objectives.FUEL` |\n", "| {glue:md}`ALTERNATE` | {glue:md}`Mission.Objectives.FUEL`|\n", - "| {glue:md}`FALLOUT` | {glue:md}`Mission.Objectives.RANGE` |\n", + "| {glue:md}`OFF_DESIGN_MAX_RANGE` | {glue:md}`Mission.Objectives.RANGE` |\n", "| {glue:md}`MULTI_MISSION` | No Default, user specified |" ] }, @@ -626,7 +626,7 @@ "EOM.TWO_DEGREES_OF_FREEDOM\n", "PT.SIZING\n", "PT.ALTERNATE\n", - "PT.FALLOUT" + "PT.OFF_DESIGN_MAX_RANGE" ] }, { @@ -642,7 +642,7 @@ "| ----------- | ----------- | ----------- | --------------- | ----- |\n", "| Aircraft.Design.GROSS_MASS | 10 | 900.e3| 175.e3 | lbm |\n", "\n", - "For default {glue:md}`2DOF` mission model, the design variables and constraints depend on the type of problems ({glue:md}`SIZING`, {glue:md}`ALTERNATE`, or {glue:md}`FALLOUT`, see {glue:md}`ProblemType` class in `aviary/variable_info/enums.py` for details). First, there are four common design variables and two common constraints. There are two more design variables and two constraints for sizing problems.\n", + "For default {glue:md}`2DOF` mission model, the design variables and constraints depend on the type of problems ({glue:md}`SIZING`, {glue:md}`ALTERNATE`, or {glue:md}`OFF_DESIGN_MAX_RANGE`, see {glue:md}`ProblemType` class in `aviary/variable_info/enums.py` for details). First, there are four common design variables and two common constraints. There are two more design variables and two constraints for sizing problems.\n", "\n", "| **Problem Type** | **Design Variables** | **Lower Bound** | **Upper Bound** | **Reference Value** | **Units** |\n", "| ----------- | ----------- | ----------- | ----------- | --------------- | ----- |\n", diff --git a/aviary/interface/test/test_reports.py b/aviary/interface/test/test_reports.py index 4568915e4f..7c2b37040a 100644 --- a/aviary/interface/test/test_reports.py +++ b/aviary/interface/test/test_reports.py @@ -149,7 +149,7 @@ def test_multiple_off_design_report_directories(self): prob.add_objective() prob.setup() prob.run_aviary_problem() - prob.run_off_design_mission(problem_type='fallout', mission_gross_mass=115000) + prob.run_off_design_mission(problem_type='off_design_max_range', mission_gross_mass=115000) prob.run_off_design_mission(problem_type='alternate', mission_range=1250) assert Path('testflo_off_design_1_out').is_dir() assert Path('testflo_off_design_out').is_dir() diff --git a/aviary/interface/test/test_save_results.py b/aviary/interface/test/test_save_results.py index 8ff5fad86c..8381581bb2 100644 --- a/aviary/interface/test/test_save_results.py +++ b/aviary/interface/test/test_save_results.py @@ -60,11 +60,13 @@ def test_alternate(self): prob.run_off_design_mission(problem_type='alternate', phase_info=local_phase_info) @require_pyoptsparse(optimizer='IPOPT') - def test_fallout(self): + def test_off_design_max_range(self): local_phase_info = deepcopy(phase_info) prob = reload_aviary_problem('interface/test/sizing_results_for_test.json') - prob.run_off_design_mission(problem_type='fallout', phase_info=local_phase_info) + prob.run_off_design_mission( + problem_type='off_design_max_range', phase_info=local_phase_info + ) def compare_files(self, test_file, validation_file): """ @@ -98,4 +100,4 @@ def compare_files(self, test_file, validation_file): # test = TestSizingResults() # test.test_save_json() - # test.test_fallout() + # test.test_off_design_max_range() diff --git a/aviary/models/aircraft/large_turboprop_freighter/large_turboprop_freighter_GASP.csv b/aviary/models/aircraft/large_turboprop_freighter/large_turboprop_freighter_GASP.csv index 97d850cfef..b5384298d8 100644 --- a/aviary/models/aircraft/large_turboprop_freighter/large_turboprop_freighter_GASP.csv +++ b/aviary/models/aircraft/large_turboprop_freighter/large_turboprop_freighter_GASP.csv @@ -1,7 +1,7 @@ ############ # SETTINGS # ############ -settings:problem_type, fallout, unitless +settings:problem_type, off_design_max_range, unitless settings:equations_of_motion, 2DOF settings:aerodynamics_method, GASP settings:mass_method, GASP diff --git a/aviary/utils/fortran_to_aviary.py b/aviary/utils/fortran_to_aviary.py index 9803ffd784..c9997c1dc7 100644 --- a/aviary/utils/fortran_to_aviary.py +++ b/aviary/utils/fortran_to_aviary.py @@ -510,20 +510,20 @@ def update_gasp_options(vehicle_data, verbosity=Verbosity.BRIEF): problem_type = 'sizing' if isinstance(design_range, list): - # if the design range target_range value is 0, set the problem_type to fallout + # if the design range target_range value is 0, set the problem_type to off_design_max_range if design_range[0] == 0: - problem_type = 'fallout' + problem_type = 'off_design_max_range' input_values.set_val(Settings.PROBLEM_TYPE, [problem_type]) design_range = 0 if problem_type == 'sizing': design_range = design_range[0] elif problem_type == 'alternate': design_range = design_range[2] - elif problem_type == 'fallout': + elif problem_type == 'off_design_max_range': design_range = 0 else: if design_range == 0: - input_values.set_val(Settings.PROBLEM_TYPE, ['fallout']) + input_values.set_val(Settings.PROBLEM_TYPE, ['off_design_max_range']) input_values.set_val(Aircraft.Design.RANGE, [design_range], distance_units) ## Passengers ## diff --git a/aviary/utils/process_input_decks.py b/aviary/utils/process_input_decks.py index eeb67e4a30..e501bde402 100644 --- a/aviary/utils/process_input_decks.py +++ b/aviary/utils/process_input_decks.py @@ -39,8 +39,8 @@ } problem_types = { 'sizing': ProblemType.SIZING, - 'alternate': ProblemType.ALTERNATE, - 'fallout': ProblemType.FALLOUT, + 'alternate': ProblemType.OFF_DESIGN_MIN_FUEL, + 'off_design_max_range': ProblemType.OFF_DESIGN_MAX_RANGE, } @@ -359,7 +359,7 @@ def initialization_guessing(aircraft_values: AviaryValues, initialization_guesse # takeoff mass not given if mission_mass <= 0: - if problem_type == ProblemType.ALTERNATE: + if problem_type == ProblemType.OFF_DESIGN_MIN_FUEL: fuel_mass = ( num_pax * ( @@ -378,14 +378,14 @@ def initialization_guessing(aircraft_values: AviaryValues, initialization_guesse ) + fuel_mass ) - elif problem_type == ProblemType.FALLOUT or problem_type == ProblemType.SIZING: + elif problem_type == ProblemType.OFF_DESIGN_MAX_RANGE or problem_type == ProblemType.SIZING: mission_mass = aircraft_values.get_val(Aircraft.Design.GROSS_MASS, units='lbm') initialization_guesses['actual_takeoff_mass'] = mission_mass if cruise_mass_final == 0: # no guess given if problem_type == ProblemType.SIZING: cruise_mass_final = 0.8 - elif problem_type == ProblemType.ALTERNATE: + elif problem_type == ProblemType.OFF_DESIGN_MIN_FUEL: cruise_mass_final = -1 # estimation based on payload and fuel if cruise_mass_final <= 0: diff --git a/aviary/validation_cases/benchmark_tests/test_bench_off_design.py b/aviary/validation_cases/benchmark_tests/test_bench_off_design.py index 750aa28087..77accd18b6 100644 --- a/aviary/validation_cases/benchmark_tests/test_bench_off_design.py +++ b/aviary/validation_cases/benchmark_tests/test_bench_off_design.py @@ -13,7 +13,7 @@ @use_tempdirs class TestEnergyStateOffDesign(unittest.TestCase): - """Test off-design capability for both fallout and alternate missions.""" + """Test off-design capability for both OFF_DESIGN_MAX_RANGE and alternate missions.""" def setUp(self): # run design case @@ -86,18 +86,20 @@ def compare_results(self, comparison_prob): ) @require_pyoptsparse(optimizer='SNOPT') - def test_fallout_mission_match(self): - # run a fallout mission with no changes, essentially recreating the design mission with + def test_off_design_max_range_mission_match(self): + # run a off_design_max_range mission with no changes, essentially recreating the design mission with # different constraints/design variables - prob_fallout = self.prob.run_off_design_mission(problem_type='fallout') - self.compare_results(prob_fallout) - self.assertTrue(prob_fallout.result.success) + prob_off_design_max_range = self.prob.run_off_design_mission( + problem_type='off_design_max_range' + ) + self.compare_results(prob_off_design_max_range) + self.assertTrue(prob_off_design_max_range.result.success) @require_pyoptsparse(optimizer='SNOPT') - def test_fallout_mission_changed(self): - # run a fallout mission with modified payload and gross mass (and therefore different fuel) - prob_fallout = self.prob.run_off_design_mission( - problem_type='fallout', + def test_off_design_max_range_mission_changed(self): + # run a off_design_max_range mission with modified payload and gross mass (and therefore different fuel) + prob_off_design_max_range = self.prob.run_off_design_mission( + problem_type='off_design_max_range', cargo_mass=5000, mission_gross_mass=150_000, num_first_class=1, @@ -105,72 +107,74 @@ def test_fallout_mission_changed(self): num_economy=75, ) assert_near_equal( - prob_fallout.get_val(Aircraft.Design.RANGE), + prob_off_design_max_range.get_val(Aircraft.Design.RANGE), self.prob.get_val(Aircraft.Design.RANGE), tolerance=1e-12, ) - assert_near_equal(prob_fallout.get_val(Mission.RANGE), 2438.6, tolerance=1e-3) + assert_near_equal(prob_off_design_max_range.get_val(Mission.RANGE), 2438.6, tolerance=1e-3) assert_near_equal( - prob_fallout.get_val(Mission.TOTAL_FUEL, 'lbm'), + prob_off_design_max_range.get_val(Mission.TOTAL_FUEL, 'lbm'), 29031.53317628, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Mission.OPERATING_MASS, 'lbm'), + prob_off_design_max_range.get_val(Mission.OPERATING_MASS, 'lbm'), 97743.46682372, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), 5000, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), 23225, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), 18225, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), self.prob.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_fallout.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), self.prob.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_fallout.get_val(Mission.GROSS_MASS, 'lbm'), + prob_off_design_max_range.get_val(Mission.GROSS_MASS, 'lbm'), 150000, tolerance=1e-12, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_FIRST_CLASS), + prob_off_design_max_range.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_FIRST_CLASS), 1, tolerance=1e-12, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_BUSINESS_CLASS), + prob_off_design_max_range.aviary_inputs.get_val( + Aircraft.CrewPayload.NUM_BUSINESS_CLASS + ), 5, tolerance=1e-12, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_ECONOMY_CLASS), + prob_off_design_max_range.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_ECONOMY_CLASS), 75, tolerance=1e-12, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), + prob_off_design_max_range.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), 81, tolerance=1e-12, ) - self.assertTrue(prob_fallout.result.success) + self.assertTrue(prob_off_design_max_range.result.success) @require_pyoptsparse(optimizer='SNOPT') def test_alternate_mission_match(self): @@ -261,7 +265,7 @@ def test_alternate_mission_changed(self): @use_tempdirs class Test2DOFOffDesign(unittest.TestCase): - """Test off-design capability for both fallout and alternate missions.""" + """Test off-design capability for both off_design_max_range and alternate missions.""" # TODO this test needs more manual verification to root out any remaining bugs @@ -328,75 +332,79 @@ def compare_results(self, comparison_prob): ) @require_pyoptsparse(optimizer='SNOPT') - def test_fallout_mission_match(self): - # run a fallout mission with no changes, essentially recreating the design mission with + def test_off_design_max_range_mission_match(self): + # run a off_design_max_range mission with no changes, essentially recreating the design mission with # different constraints/design variables - prob_fallout = self.prob.run_off_design_mission(problem_type='fallout') - self.compare_results(prob_fallout) + prob_off_design_max_range = self.prob.run_off_design_mission( + problem_type='off_design_max_range' + ) + self.compare_results(prob_off_design_max_range) @require_pyoptsparse(optimizer='SNOPT') - def test_fallout_mission_changed(self): - # run a fallout mission with modified payload and gross mass (and therefore different fuel) + def test_off_design_max_range_mission_changed(self): + # run a off_design_max_range mission with modified payload and gross mass (and therefore different fuel) prob = self.prob - prob_fallout = prob.run_off_design_mission( - problem_type='fallout', + prob_off_design_max_range = prob.run_off_design_mission( + problem_type='off_design_max_range', cargo_mass=5000, mission_gross_mass=155000.0, num_pax=75, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), + prob_off_design_max_range.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), prob.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), tolerance=1e-12, ) - assert_near_equal(prob_fallout.get_val(Mission.RANGE), 3994.25223046, tolerance=1e-4) assert_near_equal( - prob_fallout.get_val(Mission.TOTAL_FUEL, 'lbm'), + prob_off_design_max_range.get_val(Mission.RANGE), 3994.25223046, tolerance=1e-4 + ) + assert_near_equal( + prob_off_design_max_range.get_val(Mission.TOTAL_FUEL, 'lbm'), 39909.74193096, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Mission.OPERATING_MASS, 'lbm'), + prob_off_design_max_range.get_val(Mission.OPERATING_MASS, 'lbm'), 95090.25806904, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), 5000, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), 20000, tolerance=1e-5, ) assert_near_equal( - prob_fallout.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), 15000, tolerance=1e-6, ) # currently not a GASP variable # assert_near_equal( - # prob_fallout.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), + # prob_off_design_max_range.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), # prob.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), # tolerance=1e-12, # ) assert_near_equal( - prob_fallout.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), + prob_off_design_max_range.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), prob.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_fallout.get_val(Mission.GROSS_MASS, 'lbm'), + prob_off_design_max_range.get_val(Mission.GROSS_MASS, 'lbm'), 155000, tolerance=1e-12, ) assert_near_equal( - prob_fallout.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), + prob_off_design_max_range.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), 75, tolerance=1e-12, ) - self.assertTrue(prob_fallout.result.success) + self.assertTrue(prob_off_design_max_range.result.success) @require_pyoptsparse(optimizer='SNOPT') def test_alternate_mission_match(self): diff --git a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py index 00df256fa0..ed125f7e1c 100644 --- a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py +++ b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py @@ -20,34 +20,38 @@ def setUp(self) -> None: class TestOffDesign(TwoDOFTestCase): """ Build the model using a large single aisle commercial transport aircraft data using - GASP mass method and TWO_DEGREES_OF_FREEDOM mission method. Run a fallout mission to test off design. + GASP mass method and TWO_DEGREES_OF_FREEDOM mission method. Run a OFF_DESIGN_MAX_RANGE mission to test off design. """ @require_pyoptsparse(optimizer='IPOPT') def test_off_design_IPOPT(self): - # Fallout Mission - prob_fallout = av.AviaryProblem() - prob_fallout.load_inputs( + # OFF_DESIGN_MAX_RANGE Mission + prob_off_design_max_range = av.AviaryProblem() + prob_off_design_max_range.load_inputs( 'models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv', self.phase_info, verbosity=Verbosity.QUIET, ) - prob_fallout.problem_type = ProblemType.FALLOUT - prob_fallout.aviary_inputs.set_val('problem_type', ProblemType.FALLOUT, units='unitless') - prob_fallout.aviary_inputs.set_val( + prob_off_design_max_range.problem_type = ProblemType.OFF_DESIGN_MAX_RANGE + prob_off_design_max_range.aviary_inputs.set_val( + 'problem_type', ProblemType.OFF_DESIGN_MAX_RANGE, units='unitless' + ) + prob_off_design_max_range.aviary_inputs.set_val( 'aircraft:design:gross_mass', self.sized_mass, units='lbm' ) - prob_fallout.aviary_inputs.set_val('mission:gross_mass', self.sized_mass, units='lbm') + prob_off_design_max_range.aviary_inputs.set_val( + 'mission:gross_mass', self.sized_mass, units='lbm' + ) - prob_fallout.check_and_preprocess_inputs() + prob_off_design_max_range.check_and_preprocess_inputs() - prob_fallout.build_model() - prob_fallout.add_driver('IPOPT', max_iter=100) - prob_fallout.add_design_variables() - prob_fallout.add_objective() - prob_fallout.setup() - prob_fallout.run_aviary_problem() + prob_off_design_max_range.build_model() + prob_off_design_max_range.add_driver('IPOPT', max_iter=100) + prob_off_design_max_range.add_design_variables() + prob_off_design_max_range.add_objective() + prob_off_design_max_range.setup() + prob_off_design_max_range.run_aviary_problem() # Alternate Mission prob_alternate = av.AviaryProblem() @@ -56,9 +60,9 @@ def test_off_design_IPOPT(self): self.phase_info, verbosity=Verbosity.QUIET, ) - prob_alternate.problem_type = ProblemType.ALTERNATE + prob_alternate.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL prob_alternate.aviary_inputs.set_val( - 'problem_type', ProblemType.ALTERNATE, units='unitless' + 'problem_type', ProblemType.OFF_DESIGN_MIN_FUEL, units='unitless' ) prob_alternate.aviary_inputs.set_val( @@ -74,35 +78,39 @@ def test_off_design_IPOPT(self): prob_alternate.setup() prob_alternate.run_aviary_problem() - fallout_range = prob_fallout.get_val(av.Mission.RANGE) + off_design_max_range_range = prob_off_design_max_range.get_val(av.Mission.RANGE) alternate_mass = prob_alternate.get_val(av.Mission.GROSS_MASS) - assert_near_equal(fallout_range, self.sized_range, tolerance=0.02) + assert_near_equal(off_design_max_range_range, self.sized_range, tolerance=0.02) assert_near_equal(alternate_mass, self.sized_mass, tolerance=0.02) @require_pyoptsparse(optimizer='SNOPT') def test_off_design_SNOPT(self): - # Fallout Mission - prob_fallout = av.AviaryProblem() - prob_fallout.load_inputs( + # off_design_max_range Mission + prob_off_design_max_range = av.AviaryProblem() + prob_off_design_max_range.load_inputs( 'models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv', self.phase_info, verbosity=Verbosity.QUIET, ) - prob_fallout.problem_type = ProblemType.FALLOUT - prob_fallout.aviary_inputs.set_val('problem_type', ProblemType.FALLOUT, units='unitless') - prob_fallout.aviary_inputs.set_val( + prob_off_design_max_range.problem_type = ProblemType.OFF_DESIGN_MAX_RANGE + prob_off_design_max_range.aviary_inputs.set_val( + 'problem_type', ProblemType.OFF_DESIGN_MAX_RANGE, units='unitless' + ) + prob_off_design_max_range.aviary_inputs.set_val( 'aircraft:design:gross_mass', self.sized_mass, units='lbm' ) - prob_fallout.aviary_inputs.set_val('mission:gross_mass', self.sized_mass, units='lbm') + prob_off_design_max_range.aviary_inputs.set_val( + 'mission:gross_mass', self.sized_mass, units='lbm' + ) - prob_fallout.check_and_preprocess_inputs() - prob_fallout.build_model() - prob_fallout.add_driver('SNOPT', max_iter=100) - prob_fallout.add_design_variables() - prob_fallout.add_objective() - prob_fallout.setup() - prob_fallout.run_aviary_problem() + prob_off_design_max_range.check_and_preprocess_inputs() + prob_off_design_max_range.build_model() + prob_off_design_max_range.add_driver('SNOPT', max_iter=100) + prob_off_design_max_range.add_design_variables() + prob_off_design_max_range.add_objective() + prob_off_design_max_range.setup() + prob_off_design_max_range.run_aviary_problem() # Alternate Mission prob_alternate = av.AviaryProblem() @@ -111,9 +119,9 @@ def test_off_design_SNOPT(self): self.phase_info, verbosity=Verbosity.QUIET, ) - prob_alternate.problem_type = ProblemType.ALTERNATE + prob_alternate.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL prob_alternate.aviary_inputs.set_val( - 'problem_type', ProblemType.ALTERNATE, units='unitless' + 'problem_type', ProblemType.OFF_DESIGN_MIN_FUEL, units='unitless' ) prob_alternate.aviary_inputs.set_val( @@ -129,9 +137,9 @@ def test_off_design_SNOPT(self): prob_alternate.setup() prob_alternate.run_aviary_problem() - fallout_range = prob_fallout.get_val(av.Mission.RANGE) + off_design_max_range_range = prob_off_design_max_range.get_val(av.Mission.RANGE) alternate_mass = prob_alternate.get_val(av.Mission.GROSS_MASS) - assert_near_equal(fallout_range, self.sized_range, tolerance=0.02) + assert_near_equal(off_design_max_range_range, self.sized_range, tolerance=0.02) assert_near_equal(alternate_mass, self.sized_mass, tolerance=0.02) diff --git a/aviary/variable_info/enums.py b/aviary/variable_info/enums.py index 49c86e8c7c..68b07c9034 100644 --- a/aviary/variable_info/enums.py +++ b/aviary/variable_info/enums.py @@ -204,7 +204,7 @@ class ProblemType(Enum): and actual gross weight until the range closes to the off-design range. - FALLOUT: Requires a pre-sized aircraft. It holds the design gross + OFF_DESIGN_MAX_RANGE: Requires a pre-sized aircraft. It holds the design gross weight and empty weight constant. Using the specified actual gross weight, it will then find the maximum distance the off-design aircraft can fly. @@ -219,7 +219,7 @@ class ProblemType(Enum): SIZING = 'sizing' ALTERNATE = 'alternate' - FALLOUT = 'fallout' + OFF_DESIGN_MAX_RANGE = 'off_design_max_range' MULTI_MISSION = 'multimission' diff --git a/aviary/variable_info/variable_meta_data.py b/aviary/variable_info/variable_meta_data.py index 588ece73c3..1defd9720b 100644 --- a/aviary/variable_info/variable_meta_data.py +++ b/aviary/variable_info/variable_meta_data.py @@ -8199,7 +8199,7 @@ Settings.PROBLEM_TYPE, meta_data=_MetaData, historical_name={'GASP': None, 'FLOPS': None, 'LEAPS1': None}, - desc="Select from Aviary's built in problem types: SIZING, ALTERNATE, FALLOUT and MULTI_MISSION", + desc="Select from Aviary's built in problem types: SIZING, ALTERNATE, OFF_DESIGN_MAX_RANGE and MULTI_MISSION", option=True, types=ProblemType, default_value=None, diff --git a/aviary/visualization/dashboard.py b/aviary/visualization/dashboard.py index 1d7f0bc897..3d430e6989 100644 --- a/aviary/visualization/dashboard.py +++ b/aviary/visualization/dashboard.py @@ -1314,7 +1314,7 @@ def dashboard(script_name, port=0, run_in_background=False): 'Payload/Range Diagram', results_tabs_list, """ - Defines key operating points on the aircraft's payload-range envelope from Design and Fallout missions. + Defines key operating points on the aircraft's payload-range envelope from Design and OFF_DESIGN_MAX_RANGE missions. """, reports_dir / 'payload_range_data.csv', ) From 7f5f9d69bc00c8b75731eaa2768c78a514e3b1af Mon Sep 17 00:00:00 2001 From: Bennett Date: Wed, 8 Apr 2026 19:45:59 -0400 Subject: [PATCH 2/4] change alternate to off_design_min_fuel --- aviary/core/aviary_group.py | 2 +- aviary/core/aviary_problem.py | 14 +-- .../docs/examples/off_design_missions.ipynb | 36 ++++--- .../off_design_missions.ipynb | 22 ++-- .../payload_range_functionality.ipynb | 4 +- .../subsystems/fuel.ipynb | 2 +- aviary/interface/test/test_reports.py | 2 +- aviary/interface/test/test_save_results.py | 4 +- aviary/utils/process_input_decks.py | 2 +- .../benchmark_tests/test_bench_off_design.py | 100 +++++++++--------- .../test_problem_types_GwGm.py | 68 ++++++------ aviary/variable_info/enums.py | 4 +- aviary/variable_info/variable_meta_data.py | 2 +- 13 files changed, 139 insertions(+), 123 deletions(-) diff --git a/aviary/core/aviary_group.py b/aviary/core/aviary_group.py index 55224b6e9a..4854dc6ab4 100644 --- a/aviary/core/aviary_group.py +++ b/aviary/core/aviary_group.py @@ -1268,7 +1268,7 @@ def add_design_variables(self, problem_type: ProblemType = None, verbosity=None) and another for the gross mass of the aircraft computed during the mission. A constraint is also added to ensure that the residual range is zero. - If solving an alternate problem, only a design variable for the gross mass of the aircraft + If solving an OFF_DESIGN_MIN_FUEL problem, only a design variable for the gross mass of the aircraft computed during the mission is added. A constraint is also added to ensure that the residual range is zero. diff --git a/aviary/core/aviary_problem.py b/aviary/core/aviary_problem.py index 25272f8b6a..7728c54c1c 100644 --- a/aviary/core/aviary_problem.py +++ b/aviary/core/aviary_problem.py @@ -560,7 +560,7 @@ def add_design_variables(self, verbosity=None): and another for the gross mass of the aircraft computed during the mission. A constraint is also added to ensure that the residual range is zero. - If solving an alternate problem, only a design variable for the gross mass of the aircraft + If solving an OFF_DESIGN_MIN_FUEL problem, only a design variable for the gross mass of the aircraft computed during the mission is added. A constraint is also added to ensure that the residual range is zero. @@ -786,7 +786,7 @@ def add_composite_objective(self, *args, ref: float = None): If 1-tuple: (output) or 'fuel', 'fuel_burned', 'mass', 'range', 'time' If empty, information will be populated based on problem_type: - If ProblemType = OFF_DESIGN_MAX_RANGE, objective = Mission.Objectives.RANGE - - If ProblemType = Sizing or Alternate, objective = Mission.Objectives.FUEL + - If ProblemType = Sizing or OFF_DESIGN_MIN_FUEL, objective = Mission.Objectives.FUEL Example inputs can be any of the following: ('fuel') @@ -867,7 +867,7 @@ def add_composite_objective(self, *args, ref: float = None): f'Each argument must be one of the following: ' f'(output), (output, weight), (model, output), or (model, output, weight).' f'Outputs can be from the variable meta data, or can be: fuel_burned, fuel' - f'Or problem type must be set to SIZING, ALTERNATE, or OFF_DESIGN_MAX_RANGE' + f'Or problem type must be set to SIZING, OFF_DESIGN_MIN_FUEL, or OFF_DESIGN_MAX_RANGE' ) objectives.append((model, output, weight)) # objectives = [ @@ -1320,10 +1320,10 @@ def run_off_design_mission( for total cargo mass. mission_gross_mass : float, optional Gross mass of aircraft flying off-design mission, in pounds-mass. Defaults to design - gross mass. For missions where mass is solved for (such as ALTERNATE missions), this is + gross mass. For missions where mass is solved for (such as OFF_DESIGN_MIN_FUEL missions), this is the initial guess. mission_range : float, optional - [ALTERNATE missions only] + [OFF_DESIGN_MIN_FUEL missions only] Sets fixed range of flown off-design mission, in nautical miles. Unused for other mission types. optimizer : string, optional @@ -1438,7 +1438,7 @@ def run_off_design_mission( if mission_range is None: if verbosity >= Verbosity.VERBOSE: warnings.warn( - 'Alternate problem type requested with no specified range. Using design ' + 'OFF_DESIGN_MIN_FUEL problem type requested with no specified range. Using design ' 'mission range for the off-design mission.' ) mission_range = self.get_val(Mission.RANGE, units='NM')[0] @@ -1452,7 +1452,7 @@ def run_off_design_mission( off_design_prob.load_inputs(inputs, phase_info, verbosity=verbosity) # Update inputs that are specific to problem type - # Some Alternate problem changes had to happen before load_inputs, all OFF_DESIGN_MAX_RANGE problem + # Some OFF_DESIGN_MIN_FUEL problem changes had to happen before load_inputs, all OFF_DESIGN_MAX_RANGE problem # changes must come after load_inputs if problem_type is ProblemType.OFF_DESIGN_MIN_FUEL: off_design_prob.aviary_inputs.set_val(Mission.RANGE, mission_range, units='NM') diff --git a/aviary/docs/examples/off_design_missions.ipynb b/aviary/docs/examples/off_design_missions.ipynb index 1b10867b67..f0529d4a44 100644 --- a/aviary/docs/examples/off_design_missions.ipynb +++ b/aviary/docs/examples/off_design_missions.ipynb @@ -60,7 +60,7 @@ "id": "f1a098f9", "metadata": {}, "source": [ - "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is a \"off_deign_max_range\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"alternate\" mission, where the range of the mission is known, and Aviary solves the exact amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", + "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is a \"off_deign_max_range\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"off_design_min_fuel\" mission, where the range of the mission is known, and Aviary solves the exact amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", "\n", "An off-design mission is run using the {glue:md}`run_off_design_mission()` method of {glue:md}`AviaryProblem`. This method has significantly more flexibility than demonstrated in this example, including the ability to change the passengers and cargo loaded on the aircraft, and the option to change the mission profile flown via providing new phase_info. \n", "\n", @@ -81,9 +81,9 @@ " name='off_design_max_range_mission',\n", ")\n", "\n", - "# Alternate Mission - fixed range, varying fuel\n", - "alternate_prob = design_prob.run_off_design_mission(\n", - " problem_type='alternate', mission_range=1250, name='alternate_mission'\n", + "# OFF_DESIGN_MIN_FUEL Mission - fixed range, varying fuel\n", + "off_design_min_fuel_prob = design_prob.run_off_design_mission(\n", + " problem_type='off_design_min_fuel', mission_range=1250, name='off_design_min_fuel_mission'\n", ")" ] }, @@ -92,7 +92,7 @@ "id": "d4f57227", "metadata": {}, "source": [ - "Once both missions are complete, we can compare the difference between the design and off-design missions. We are expecting fundamental design parameters of the aircraft, such as its maximum takeoff gross mass and its design range to remain unchanged. However, the takeoff gross mass for each particular mission should change, due to a change in the amount of fuel the aircraft is loaded with before takeoff, as well as mission range. We should see a reduction in both of these quantities for the off-design missions, with the pre-defined properties (gross mass for the off_design_max_range mission, mission range for the alternate mission) matching what we asked Aviary to fly." + "Once both missions are complete, we can compare the difference between the design and off-design missions. We are expecting fundamental design parameters of the aircraft, such as its maximum takeoff gross mass and its design range to remain unchanged. However, the takeoff gross mass for each particular mission should change, due to a change in the amount of fuel the aircraft is loaded with before takeoff, as well as mission range. We should see a reduction in both of these quantities for the off-design missions, with the pre-defined properties (gross mass for the off_design_max_range mission, mission range for the off_design_min_fuel mission) matching what we asked Aviary to fly." ] }, { @@ -132,15 +132,21 @@ ")\n", "print(f'Mission Gross Mass = {off_design_max_range_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')\n", "\n", - "print('\\nAlternate Results')\n", + "print('\\nOFF_DESIGN_MIN_FUEL Results')\n", "print('-----------------')\n", - "print(f'Design Range = {alternate_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", - "print(f'Mission Range = {alternate_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", - "print(f'Fuel Mass = {alternate_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", - "print(f'Operating Empty Mass = {alternate_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm')\n", - "print(f'Payload Mass = {alternate_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm')\n", - "print(f'Design Gross Mass = {alternate_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm')\n", - "print(f'Mission Gross Mass = {alternate_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')" + "print(f'Design Range = {off_design_min_fuel_prob.get_val(av.Aircraft.Design.RANGE)[0]} nmi')\n", + "print(f'Mission Range = {off_design_min_fuel_prob.get_val(av.Mission.RANGE)[0]} nmi')\n", + "print(f'Fuel Mass = {off_design_min_fuel_prob.get_val(av.Mission.TOTAL_FUEL)[0]} lbm')\n", + "print(\n", + " f'Operating Empty Mass = {off_design_min_fuel_prob.get_val(av.Mission.OPERATING_MASS)[0]} lbm'\n", + ")\n", + "print(\n", + " f'Payload Mass = {off_design_min_fuel_prob.get_val(av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS)[0]} lbm'\n", + ")\n", + "print(\n", + " f'Design Gross Mass = {off_design_min_fuel_prob.get_val(av.Aircraft.Design.GROSS_MASS)[0]} lbm'\n", + ")\n", + "print(f'Mission Gross Mass = {off_design_min_fuel_prob.get_val(av.Mission.GROSS_MASS)[0]} lbm')" ] }, { @@ -219,8 +225,8 @@ "if not off_design_max_range_prob.result.success:\n", " raise ValueError('OFF_DESIGN_MAX_RANGE problem failed')\n", "\n", - "if not alternate_prob.result.success:\n", - " raise ValueError('Alternate problem failed')\n", + "if not off_design_min_fuel_prob.result.success:\n", + " raise ValueError('OFF_DESIGN_MIN_FUEL problem failed')\n", "\n", "if not new_off_design_max_range_prob.result.success:\n", " raise ValueError('OFF_DESIGN_MAX_RANGE problem 2 failed')\n", diff --git a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb index 0ca5bff286..0344381bd9 100644 --- a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb +++ b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb @@ -17,12 +17,14 @@ "\n", "str_problem_type = Settings.PROBLEM_TYPE\n", "str_sizing = av.ProblemType.SIZING.value\n", - "str_alternate = av.ProblemType.OFF_DESIGN_MIN_FUEL.value\n", + "str_off_design_min_fuel = av.ProblemType.OFF_DESIGN_MIN_FUEL.value\n", "str_off_design_max_range = av.ProblemType.OFF_DESIGN_MAX_RANGE.value\n", "\n", "\n", - "str_alternate_snippet = f'```\\n{str_problem_type}, {str_alternate}\\n```'\n", - "glue_variable('alternate_snippet', str_alternate_snippet, md_code=False)\n", + "str_off_design_min_fuel_snippet = f'```\\n{str_problem_type}, {str_off_design_min_fuel}\\n```'\n", + "glue_variable(\n", + " 'off_design_min_fuel_snippet', str_off_design_min_fueln_min_fuel_snippet, md_code=False\n", + ")\n", "\n", "off_design_max_range_snippet = f'```\\n{str_problem_type}, {str_off_design_max_range}\\n```'\n", "glue_variable('off_design_max_range_snippet', off_design_max_range_snippet, md_code=False)\n", @@ -79,18 +81,18 @@ "\n", "There are currently two types of off-design missions supported in Aviary:\n", "\n", - "- Alternate Missions: the mission's target range and aircraft payload mass are inputs and the fuel mass required is solved for. \n", - "- Fallout Missions: the aircraft payload and gross mass as inputs and the range of the aircraft is solved for. \n", + "- OFF_DESIGN_MIN_FUEL Missions: the mission's target range and aircraft payload mass are inputs and the fuel mass required is solved for. \n", + "- OFF_DESIGN_MAX_RANGE Missions: the aircraft payload and gross mass as inputs and the range of the aircraft is solved for. \n", "\n", "The off-design missions correspond to the different problem types that can have differing objectives which are discussed in detail in Level 2.\n", "The problem type determines what the optimizer can control to find a valid solution. \n", "- Sizing Missions allow the optimizer to control both the {glue:md}`summary_gross_mass` and {glue:md}`design_gross_mass` for the given mission and objective. \n", - "- Alternate Missions allow the optimizer to only control the {glue:md}`summary_gross_mass` for the mission.\n", - "- Fallout Missions don't allow the optimizer to control either {glue:md}`summary_gross_mass` or {glue:md}`design_gross_mass` but allows the optimizer to extend the range until the summary matches the design.\n", + "- OFF_DESIGN_MIN_FUEL Missions allow the optimizer to only control the {glue:md}`summary_gross_mass` for the mission.\n", + "- OFF_DESIGN_MAX_RANGE Missions don't allow the optimizer to control either {glue:md}`summary_gross_mass` or {glue:md}`design_gross_mass` but allows the optimizer to extend the range until the summary matches the design.\n", "\n", "There are currently 3 different methods for running an off-design mission within Aviary. \n", "\n", - "The first method is to take the input deck of an already sized aircraft and change its problem type to either `fallout` or `alternate`. \n", + "The first method is to take the input deck of an already sized aircraft and change its problem type to either `off_design_min_fuel` or `off_design_max_range`. \n", "\n", "```{note}\n", "The user may need to revise some of the values in their input deck for the off-design mission.\n", @@ -112,11 +114,11 @@ "ex: `aircraft_for_bench_FwFm.csv`.\n", "This is done by adding one of the following lines to its csv file:\n", "\n", - "{glue:md}`fallout_snippet`\n", + "{glue:md}`off_design_min_fuel_snippet`\n", "\n", "or\n", "\n", - "{glue:md}`alternate_snippet`\n", + "{glue:md}`off_design_max_range_snippet`\n", "\n", "Once the problem type is specified, run Aviary as you would any other mission. \n", "\n", diff --git a/aviary/docs/user_guide_unreviewed/payload_range_functionality.ipynb b/aviary/docs/user_guide_unreviewed/payload_range_functionality.ipynb index 0583a512d1..824a7e3975 100644 --- a/aviary/docs/user_guide_unreviewed/payload_range_functionality.ipynb +++ b/aviary/docs/user_guide_unreviewed/payload_range_functionality.ipynb @@ -21,8 +21,8 @@ "\n", "PAYLOAD_RANGE = av.Settings.PAYLOAD_RANGE\n", "\n", - "str_alternate_snippet = f'```\\n{PAYLOAD_RANGE}, {True}\\n```'\n", - "glue_variable('payload_range_toggle', str_alternate_snippet, md_code=True)\n", + "str_off_design_min_fuel_snippet = f'```\\n{PAYLOAD_RANGE}, {True}\\n```'\n", + "glue_variable('payload_range_toggle', str_off_design_min_fuel_snippet, md_code=True)\n", "glue_variable('payload_range', av.Settings.PAYLOAD_RANGE, md_code=True)\n", "\n", "\n", diff --git a/aviary/docs/user_guide_unreviewed/subsystems/fuel.ipynb b/aviary/docs/user_guide_unreviewed/subsystems/fuel.ipynb index 151760e14a..9bb152956f 100644 --- a/aviary/docs/user_guide_unreviewed/subsystems/fuel.ipynb +++ b/aviary/docs/user_guide_unreviewed/subsystems/fuel.ipynb @@ -42,7 +42,7 @@ "\n", "A gross mass constraint is used to ensure fuel mass never pushes the aircraft over the design gross mass of the vehicle.\n", "This constraint is alaways enforced by {glue:md}`Mission.Constraints.MASS_RESIDUAL`.\n", - "This is especially relevant in alternate missions where the aircraft has been designed and you are trying to assess how much fuel is needed to complete an off-design mission.\n", + "This is especially relevant in off_design_min_fuel missions where the aircraft has been designed and you are trying to assess how much fuel is needed to complete an off-design mission.\n", "If we let the fuel push the aircraft over the design gross mass, the landing gear can fail.\n", "During design missions, this sort of violation will force the optimizer to choose a larger value for design gross mass which will cause resizing in all the other supporting systems.\n", "\n", diff --git a/aviary/interface/test/test_reports.py b/aviary/interface/test/test_reports.py index 7c2b37040a..2ef99afcbe 100644 --- a/aviary/interface/test/test_reports.py +++ b/aviary/interface/test/test_reports.py @@ -150,7 +150,7 @@ def test_multiple_off_design_report_directories(self): prob.setup() prob.run_aviary_problem() prob.run_off_design_mission(problem_type='off_design_max_range', mission_gross_mass=115000) - prob.run_off_design_mission(problem_type='alternate', mission_range=1250) + prob.run_off_design_mission(problem_type='off_design_min_fuel', mission_range=1250) assert Path('testflo_off_design_1_out').is_dir() assert Path('testflo_off_design_out').is_dir() diff --git a/aviary/interface/test/test_save_results.py b/aviary/interface/test/test_save_results.py index 8381581bb2..e066e02d9b 100644 --- a/aviary/interface/test/test_save_results.py +++ b/aviary/interface/test/test_save_results.py @@ -53,11 +53,11 @@ def test_save_json(self): ) @require_pyoptsparse(optimizer='IPOPT') - def test_alternate(self): + def test_off_design_min_fuel(self): local_phase_info = deepcopy(phase_info) prob = reload_aviary_problem('interface/test/sizing_results_for_test.json') - prob.run_off_design_mission(problem_type='alternate', phase_info=local_phase_info) + prob.run_off_design_mission(problem_type='off_design_min_fuel', phase_info=local_phase_info) @require_pyoptsparse(optimizer='IPOPT') def test_off_design_max_range(self): diff --git a/aviary/utils/process_input_decks.py b/aviary/utils/process_input_decks.py index e501bde402..f141756849 100644 --- a/aviary/utils/process_input_decks.py +++ b/aviary/utils/process_input_decks.py @@ -39,7 +39,7 @@ } problem_types = { 'sizing': ProblemType.SIZING, - 'alternate': ProblemType.OFF_DESIGN_MIN_FUEL, + 'off_design_min_fuel': ProblemType.OFF_DESIGN_MIN_FUEL, 'off_design_max_range': ProblemType.OFF_DESIGN_MAX_RANGE, } diff --git a/aviary/validation_cases/benchmark_tests/test_bench_off_design.py b/aviary/validation_cases/benchmark_tests/test_bench_off_design.py index 77accd18b6..f19426cbeb 100644 --- a/aviary/validation_cases/benchmark_tests/test_bench_off_design.py +++ b/aviary/validation_cases/benchmark_tests/test_bench_off_design.py @@ -13,7 +13,7 @@ @use_tempdirs class TestEnergyStateOffDesign(unittest.TestCase): - """Test off-design capability for both OFF_DESIGN_MAX_RANGE and alternate missions.""" + """Test off-design capability for both OFF_DESIGN_MAX_RANGE and OFF_DESIGN_MIN_FUEL missions.""" def setUp(self): # run design case @@ -177,17 +177,19 @@ def test_off_design_max_range_mission_changed(self): self.assertTrue(prob_off_design_max_range.result.success) @require_pyoptsparse(optimizer='SNOPT') - def test_alternate_mission_match(self): - # run an alternate mission with no changes, essentially recreating the design mission with + def test_off_design_min_fuel_mission_match(self): + # run an off_design_min_fuel mission with no changes, essentially recreating the design mission with # different constraints/design variables - prob_alternate = self.prob.run_off_design_mission(problem_type='alternate') - self.compare_results(prob_alternate) + prob_off_design_min_fuel = self.prob.run_off_design_mission( + problem_type='off_design_min_fuel' + ) + self.compare_results(prob_off_design_min_fuel) @require_pyoptsparse(optimizer='SNOPT') - def test_alternate_mission_changed(self): - # run an alternate mission with modified range and payload - prob_alternate = self.prob.run_off_design_mission( - problem_type='alternate', + def test_off_design_min_fuel_mission_changed(self): + # run an off_design_min_fuel mission with modified range and payload + prob_off_design_min_fuel = self.prob.run_off_design_mission( + problem_type='off_design_min_fuel', cargo_mass=2500, mission_range=1800, num_first_class=1, @@ -195,77 +197,77 @@ def test_alternate_mission_changed(self): num_economy=144, ) assert_near_equal( - prob_alternate.get_val(Aircraft.Design.RANGE), + prob_off_design_min_fuel.get_val(Aircraft.Design.RANGE), self.prob.get_val(Aircraft.Design.RANGE), tolerance=1e-12, ) - assert_near_equal(prob_alternate.get_val(Mission.RANGE), 1800, tolerance=1e-6) + assert_near_equal(prob_off_design_min_fuel.get_val(Mission.RANGE), 1800, tolerance=1e-6) assert_near_equal( - prob_alternate.get_val(Mission.TOTAL_FUEL, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.TOTAL_FUEL, 'lbm'), 23663.00633808, tolerance=1e-5, ) assert_near_equal( - prob_alternate.get_val(Mission.OPERATING_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.OPERATING_MASS, 'lbm'), 97743.52792979, tolerance=1e-5, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), 2500, tolerance=1e-12, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), 36250, tolerance=1e-5, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), 33750, tolerance=1e-5, ) assert_near_equal( - prob_alternate.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), self.prob.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_alternate.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), self.prob.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_alternate.get_val(Mission.GROSS_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.GROSS_MASS, 'lbm'), 157656.53426787, tolerance=1e-5, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_FIRST_CLASS), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_FIRST_CLASS), 1, tolerance=1e-12, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_BUSINESS_CLASS), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_BUSINESS_CLASS), 5, tolerance=1e-12, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_ECONOMY_CLASS), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_ECONOMY_CLASS), 144, tolerance=1e-12, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), 150, tolerance=1e-12, ) - self.assertTrue(prob_alternate.result.success) + self.assertTrue(prob_off_design_min_fuel.result.success) @use_tempdirs class Test2DOFOffDesign(unittest.TestCase): - """Test off-design capability for both off_design_max_range and alternate missions.""" + """Test off-design capability for both off_design_max_range and off_design_min_fuel missions.""" # TODO this test needs more manual verification to root out any remaining bugs @@ -407,78 +409,80 @@ def test_off_design_max_range_mission_changed(self): self.assertTrue(prob_off_design_max_range.result.success) @require_pyoptsparse(optimizer='SNOPT') - def test_alternate_mission_match(self): - # run an alternate mission with no changes, essentially recreating the design mission with + def test_off_design_min_fuel_mission_match(self): + # run an off_design_min_fuel mission with no changes, essentially recreating the design mission with # different constraints/design variables - prob_alternate = self.prob.run_off_design_mission(problem_type='alternate') - self.compare_results(prob_alternate) + prob_off_design_min_fuel = self.prob.run_off_design_mission( + problem_type='off_design_min_fuel' + ) + self.compare_results(prob_off_design_min_fuel) @require_pyoptsparse(optimizer='SNOPT') - def test_alternate_mission_changed(self): - # run an alternate mission with modified range and payload + def test_off_design_min_fuel_mission_changed(self): + # run an off_design_min_fuel mission with modified range and payload prob = self.prob - alternate_phase_info = deepcopy(twodof_phase_info) - alternate_phase_info['desc1']['time_duration_bounds'] = ((200.0, 900.0), 's') + off_design_min_fuel_phase_info = deepcopy(twodof_phase_info) + off_design_min_fuel_phase_info['desc1']['time_duration_bounds'] = ((200.0, 900.0), 's') - prob_alternate = prob.run_off_design_mission( - problem_type='alternate', + prob_off_design_min_fuel = prob.run_off_design_mission( + problem_type='off_design_min_fuel', cargo_mass=2100, mission_range=1800, num_pax=150, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), prob.aviary_inputs.get_val(Aircraft.Design.RANGE, 'nmi'), tolerance=1e-12, ) - assert_near_equal(prob_alternate.get_val(Mission.RANGE), 1800, tolerance=1e-6) + assert_near_equal(prob_off_design_min_fuel.get_val(Mission.RANGE), 1800, tolerance=1e-6) assert_near_equal( - prob_alternate.get_val(Mission.TOTAL_FUEL, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.TOTAL_FUEL, 'lbm'), 21484.97566914, tolerance=1e-6, ) assert_near_equal( - prob_alternate.get_val(Mission.OPERATING_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.OPERATING_MASS, 'lbm'), 95090.25806904, tolerance=1e-6, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.CARGO_MASS, 'lbm'), 2100, tolerance=1e-12, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS, 'lbm'), 32100, tolerance=1e-6, ) assert_near_equal( - prob_alternate.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.CrewPayload.PASSENGER_PAYLOAD_MASS, 'lbm'), 30000, tolerance=1e-6, ) # currently not a GASP variable # assert_near_equal( - # prob_alternate.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), + # prob_off_design_min_fuel.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), # prob.get_val(Aircraft.Design.EMPTY_MASS, 'lbm'), # tolerance=1e-12, # ) assert_near_equal( - prob_alternate.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), prob.get_val(Aircraft.Design.GROSS_MASS, 'lbm'), tolerance=1e-12, ) assert_near_equal( - prob_alternate.get_val(Mission.GROSS_MASS, 'lbm'), + prob_off_design_min_fuel.get_val(Mission.GROSS_MASS, 'lbm'), 148675.23373818, tolerance=1e-6, ) assert_near_equal( - prob_alternate.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), + prob_off_design_min_fuel.aviary_inputs.get_val(Aircraft.CrewPayload.NUM_PASSENGERS), 150, tolerance=1e-12, ) - self.assertTrue(prob_alternate.result.success) + self.assertTrue(prob_off_design_min_fuel.result.success) @use_tempdirs @@ -564,7 +568,7 @@ def test_payload_range(self): test = Test2DOFOffDesign() # test = TestEnergyStateOffDesign() test.setUp() - test.test_alternate_mission_match() + test.test_off_design_min_fuel_mission_match() # test = PayloadRangeTest() # test.test_payload_range() diff --git a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py index ed125f7e1c..fefa1b5b71 100644 --- a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py +++ b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py @@ -53,35 +53,37 @@ def test_off_design_IPOPT(self): prob_off_design_max_range.setup() prob_off_design_max_range.run_aviary_problem() - # Alternate Mission - prob_alternate = av.AviaryProblem() - prob_alternate.load_inputs( + # off_design_min_fuel Mission + prob_off_design_min_fuel = av.AviaryProblem() + prob_off_design_min_fuel.load_inputs( 'models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv', self.phase_info, verbosity=Verbosity.QUIET, ) - prob_alternate.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL - prob_alternate.aviary_inputs.set_val( + prob_off_design_min_fuel.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL + prob_off_design_min_fuel.aviary_inputs.set_val( 'problem_type', ProblemType.OFF_DESIGN_MIN_FUEL, units='unitless' ) - prob_alternate.aviary_inputs.set_val( + prob_off_design_min_fuel.aviary_inputs.set_val( 'aircraft:design:gross_mass', self.sized_mass, units='lbm' ) - prob_alternate.aviary_inputs.set_val('mission:gross_mass', self.sized_mass, units='lbm') + prob_off_design_min_fuel.aviary_inputs.set_val( + 'mission:gross_mass', self.sized_mass, units='lbm' + ) - prob_alternate.check_and_preprocess_inputs() - prob_alternate.build_model() - prob_alternate.add_driver('IPOPT', max_iter=100) - prob_alternate.add_design_variables() - prob_alternate.add_objective() - prob_alternate.setup() - prob_alternate.run_aviary_problem() + prob_off_design_min_fuel.check_and_preprocess_inputs() + prob_off_design_min_fuel.build_model() + prob_off_design_min_fuel.add_driver('IPOPT', max_iter=100) + prob_off_design_min_fuel.add_design_variables() + prob_off_design_min_fuel.add_objective() + prob_off_design_min_fuel.setup() + prob_off_design_min_fuel.run_aviary_problem() off_design_max_range_range = prob_off_design_max_range.get_val(av.Mission.RANGE) - alternate_mass = prob_alternate.get_val(av.Mission.GROSS_MASS) + off_design_min_fuel_mass = prob_off_design_min_fuel.get_val(av.Mission.GROSS_MASS) assert_near_equal(off_design_max_range_range, self.sized_range, tolerance=0.02) - assert_near_equal(alternate_mass, self.sized_mass, tolerance=0.02) + assert_near_equal(off_design_min_fuel_mass, self.sized_mass, tolerance=0.02) @require_pyoptsparse(optimizer='SNOPT') def test_off_design_SNOPT(self): @@ -112,35 +114,37 @@ def test_off_design_SNOPT(self): prob_off_design_max_range.setup() prob_off_design_max_range.run_aviary_problem() - # Alternate Mission - prob_alternate = av.AviaryProblem() - prob_alternate.load_inputs( + # off_design_min_fuel Mission + prob_off_design_min_fuel = av.AviaryProblem() + prob_off_design_min_fuel.load_inputs( 'models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv', self.phase_info, verbosity=Verbosity.QUIET, ) - prob_alternate.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL - prob_alternate.aviary_inputs.set_val( + prob_off_design_min_fuel.problem_type = ProblemType.OFF_DESIGN_MIN_FUEL + prob_off_design_min_fuel.aviary_inputs.set_val( 'problem_type', ProblemType.OFF_DESIGN_MIN_FUEL, units='unitless' ) - prob_alternate.aviary_inputs.set_val( + prob_off_design_min_fuel.aviary_inputs.set_val( 'aircraft:design:gross_mass', self.sized_mass, units='lbm' ) - prob_alternate.aviary_inputs.set_val('mission:gross_mass', self.sized_mass, units='lbm') + prob_off_design_min_fuel.aviary_inputs.set_val( + 'mission:gross_mass', self.sized_mass, units='lbm' + ) - prob_alternate.check_and_preprocess_inputs() - prob_alternate.build_model() - prob_alternate.add_driver('SNOPT', max_iter=100) - prob_alternate.add_design_variables() - prob_alternate.add_objective() - prob_alternate.setup() - prob_alternate.run_aviary_problem() + prob_off_design_min_fuel.check_and_preprocess_inputs() + prob_off_design_min_fuel.build_model() + prob_off_design_min_fuel.add_driver('SNOPT', max_iter=100) + prob_off_design_min_fuel.add_design_variables() + prob_off_design_min_fuel.add_objective() + prob_off_design_min_fuel.setup() + prob_off_design_min_fuel.run_aviary_problem() off_design_max_range_range = prob_off_design_max_range.get_val(av.Mission.RANGE) - alternate_mass = prob_alternate.get_val(av.Mission.GROSS_MASS) + off_design_min_fuel_mass = prob_off_design_min_fuel.get_val(av.Mission.GROSS_MASS) assert_near_equal(off_design_max_range_range, self.sized_range, tolerance=0.02) - assert_near_equal(alternate_mass, self.sized_mass, tolerance=0.02) + assert_near_equal(off_design_min_fuel_mass, self.sized_mass, tolerance=0.02) if __name__ == '__main__': diff --git a/aviary/variable_info/enums.py b/aviary/variable_info/enums.py index 68b07c9034..e17005f1dd 100644 --- a/aviary/variable_info/enums.py +++ b/aviary/variable_info/enums.py @@ -199,7 +199,7 @@ class ProblemType(Enum): close to design range. This causes the empty weight and the fuel weight to change. - ALTERNATE: Requires a pre-sized aircraft. It holds the design gross + OFF_DESIGN_MIN_FUEL: Requires a pre-sized aircraft. It holds the design gross weight and empty weight constant. It then varies the fuel weight and actual gross weight until the range closes to the off-design range. @@ -218,7 +218,7 @@ class ProblemType(Enum): """ SIZING = 'sizing' - ALTERNATE = 'alternate' + OFF_DESIGN_MIN_FUEL = 'off_design_min_fuel' OFF_DESIGN_MAX_RANGE = 'off_design_max_range' MULTI_MISSION = 'multimission' diff --git a/aviary/variable_info/variable_meta_data.py b/aviary/variable_info/variable_meta_data.py index 1defd9720b..79d504c0a0 100644 --- a/aviary/variable_info/variable_meta_data.py +++ b/aviary/variable_info/variable_meta_data.py @@ -8199,7 +8199,7 @@ Settings.PROBLEM_TYPE, meta_data=_MetaData, historical_name={'GASP': None, 'FLOPS': None, 'LEAPS1': None}, - desc="Select from Aviary's built in problem types: SIZING, ALTERNATE, OFF_DESIGN_MAX_RANGE and MULTI_MISSION", + desc="Select from Aviary's built in problem types: SIZING, OFF_DESIGN_MIN_FUEL, OFF_DESIGN_MAX_RANGE and MULTI_MISSION", option=True, types=ProblemType, default_value=None, From 55f2fe931b6ac5420552a621de31d3079adf5110 Mon Sep 17 00:00:00 2001 From: Bennett Date: Wed, 8 Apr 2026 19:59:57 -0400 Subject: [PATCH 3/4] temporarily fix a little of the off_design docs page. It still requires complete overhaul --- .../off_design_missions.ipynb | 60 +++++++------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb index 0344381bd9..10b0717da2 100644 --- a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb +++ b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb @@ -141,11 +141,9 @@ "\n", "The `save_sizing_to_json()` argument saves the sizing mission's parameters to a JSON filename of the user's choosing where `sizing_problem.json` is the default filename.\n", "\n", - "To run a fallout or alternate mission these level 2 functions can be added to the end of the script.\n", + "To run a OFF_DESIGN_MAX_RANGE or OFF_DESIGN_MIN_FUEL mission the run_off_design method can be called at the end of the level2 script:\n", "\n", - "- `prob.fallout_mission(json_filename = 'sizing_problem.json')` \n", - "\n", - "- `prob.alternate_mission(json_filename = 'sizing_problem.json')`\n", + "- `prob.run_off_design_mission()'\n", "\n", "If an argument is left empty Aviary will assume it is the same as that of the sizing mission. \n", "Both off-design mission types take payload as an input parameter. \n", @@ -157,20 +155,6 @@ "This is a known bug and is currently being investigated. \n", "```\n", "\n", - "| `Inputs` | `Aviary Variable Name` | `Description`\n", - "| ----------------- | ----------------------------- | ----------------------------------------- |\n", - "| `num_first` | {glue:md}`num_first` | The number of first class passengers |\n", - "| `num_business` | {glue:md}`num_business` | The number of business class passengers |\n", - "| `num_economy` | {glue:md}`num_economy` | The number of economy class passengers |\n", - "| `num_pax` | {glue:md}`num_pax` | Total number of passengers |\n", - "| `wing_cargo` | {glue:md}`wing_cargo` | Cargo carried in wing |\n", - "| `misc_cargo` | {glue:md}`misc_cargo` | Additional cargo carried in fuselage |\n", - "| `cargo_mass` | {glue:md}`cargo_mass` | Total mass of as-flown cargo |\n", - "| `phase_info` | phase_info | The mission trajectory for the aircraft |\n", - "| `mission_range` | `target_range` | The target range for alternate missions |\n", - "| `mission_mass` | {glue:md}`summary_gross_mass` | The mission mass for fallout missions |\n", - "\n", - "\n", "```{note} \n", "Off-design missions **cannot** be run with more passengers than the original sizing mission. \n", "For example, if {glue:md}`num_first` within the Aviary inputs csv was set to 3, an off-design mission cannot be run where `num_first = 8` as the cabin for first class was sized for precisely 3 passengers.\n", @@ -195,7 +179,7 @@ "%%capture\n", "\n", "\"\"\"\n", - "This is an example of a sizing mission and alternate mission being run in the same script. \n", + "This is an example of a sizing mission and off_design_min_fuel mission being run in the same script. \n", "This is done with \n", "\"\"\"\n", "import aviary.api as av\n", @@ -237,10 +221,10 @@ "# save the sizing mission to a JSON file\n", "prob.save_sizing_to_json(json_filename='off_design_documentation_example.json')\n", "\n", - "# initialize alternate mission flying a much shorter distance.\n", + "# initialize off_design_min_fuel mission flying a much shorter distance.\n", "# if payload values are unspecified, off-design will uses the values from the sizing mission.\n", "# the mission_range argument is much shorter than the sizing mission's 1906 NM range.\n", - "prob.alternate_mission(\n", + "prob.off_design_min_fuel_mission(\n", " json_filename='off_design_documentation_example.json',\n", " num_first=0,\n", " num_business=0,\n", @@ -270,7 +254,7 @@ "Once defined, the `_load_off_design()` function essentially replaces the `load_inputs()` step of a standard level 2 problem. \n", "The user can then adjust payload quantities and `mission_range`/`mission_gross_mass` then complete the level 2 script as shown below. \n", "\n", - "- `problem_type`: Determines which type of off-design analysis the user would like to run. (alternate or fallout)\n", + "- `problem_type`: Determines which type of off-design analysis the user would like to run. (off_design_min_fuel or off_design_max_range)\n", "- `equations_of_motion`: Determines the type of trajectory calculations the user is running. ({glue:md}`energy_state` or {glue:md}`2DOF`)\n", "- `mass_method`: Specifies how mass calculations are handled (FLOPS or GASP)\n" ] @@ -298,12 +282,12 @@ "max_iter = 15\n", "# Load aircraft and options data from provided sources\n", "\n", - "# To run an alternate mission, we need the sized aircraft JSON file from the sizing mission.\n", - "# Set ProblemType to ALTERNATE, and specify the mission range and payload mass.\n", + "# To run an off_design_min_fuel mission, we need the sized aircraft JSON file from the sizing mission.\n", + "# Set ProblemType to off_design_min_fuel, and specify the mission range and payload mass.\n", "# mission_gross_mass does nothing).\n", "\n", "# Use specific _load_off_design function to define the off-design mission.\n", - "prob_alternate = _load_off_design(\n", + "prob_off_design_min_fuel = _load_off_design(\n", " json_filename='off_design_documentation_example.json',\n", " problem_type=av.ProblemType.OFF_DESIGN_MIN_FUEL,\n", " equations_of_motion=av.EquationsOfMotion.ENERGY_STATE,\n", @@ -321,27 +305,27 @@ ")\n", "\n", "# Run off-design mission the same way as any Level 2 aviary problem.\n", - "prob_alternate.check_and_preprocess_inputs()\n", + "prob_off_design_min_fuel.check_and_preprocess_inputs()\n", "\n", - "prob_alternate.add_pre_mission_systems()\n", + "prob_off_design_min_fuel.add_pre_mission_systems()\n", "\n", - "prob_alternate.add_phases()\n", + "prob_off_design_min_fuel.add_phases()\n", "\n", - "prob_alternate.add_post_mission_systems()\n", + "prob_off_design_min_fuel.add_post_mission_systems()\n", "\n", - "prob_alternate.link_phases()\n", + "prob_off_design_min_fuel.link_phases()\n", "\n", - "prob_alternate.add_driver(optimizer=optimizer, max_iter=max_iter)\n", + "prob_off_design_min_fuel.add_driver(optimizer=optimizer, max_iter=max_iter)\n", "\n", - "prob_alternate.add_design_variables()\n", + "prob_off_design_min_fuel.add_design_variables()\n", "\n", - "prob_alternate.add_objective()\n", + "prob_off_design_min_fuel.add_objective()\n", "\n", - "prob_alternate.setup()\n", + "prob_off_design_min_fuel.setup()\n", "\n", - "prob_alternate.set_initial_guesses()\n", + "prob_off_design_min_fuel.set_initial_guesses()\n", "\n", - "prob_alternate.run_aviary_problem()" + "prob_off_design_min_fuel.run_aviary_problem()" ] }, { @@ -354,7 +338,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "aviary_dev", "language": "python", "name": "python3" }, @@ -368,7 +352,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.14.3" } }, "nbformat": 4, From d3c79fb8790f80a66a58170a7f883472b2e362b0 Mon Sep 17 00:00:00 2001 From: Bennett Date: Fri, 10 Apr 2026 17:02:09 -0400 Subject: [PATCH 4/4] updating off_design docs --- aviary/docs/examples/off_design_missions.ipynb | 15 ++++++++------- .../off_design_missions.ipynb | 16 +++++++--------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/aviary/docs/examples/off_design_missions.ipynb b/aviary/docs/examples/off_design_missions.ipynb index f0529d4a44..a26b90af58 100644 --- a/aviary/docs/examples/off_design_missions.ipynb +++ b/aviary/docs/examples/off_design_missions.ipynb @@ -27,10 +27,11 @@ "metadata": {}, "source": [ "# Off-Design Missions\n", - "Once an aircraft has been sized, it is common to test how it performs on missions different than its design mission. In Aviary, these missions (which do not modify the aircraft design in any way) are referred to as off-design missions. There are two main ways that off-design missions can be run: immediately following a design mission run in a Python script, or at a later time by loading the results of a perviously run design mission.\n", + "Once an aircraft has been sized, it is common to test how it performs on missions different than its design mission. In Aviary, these missions (which do not modify the aircraft design in any way) are referred to as off-design missions.\n", + "There are two main ways that off-design missions can be run: immediately following a design mission run in a Python script, or at a later time by loading the results of a perviously run design mission.\n", "\n", "## Running Off-Design Missions in the Same Script\n", - "It is fairly simple to run the initial aircraft sizing and all desired off-design missions in the same script. The following script runs the inital design problem - it is almost identical to the [simple mission example](./simple_mission.ipynb), but in this case we modify the target range to be longer. This is done so when run the aircraft on a shorter range off-design mission later there is a more obvious change in takeoff gross mass and mission fuel burn. We also save the {glue:md}`AviaryProblem` object returned by the {glue:md}`run_aviary` function call. We will use this later to run our off-design missions." + "It is fairly simple to run the initial aircraft sizing and all desired off-design missions in the same script. The following script runs the inital design problem - it is almost identical to the [simple mission example](./simple_mission.ipynb), but in this case we modify the target range to be longer. This is done so when we run the aircraft on a shorter range off-design mission later there is a more obvious change in takeoff gross mass and mission fuel burn. We also save the {glue:md}`AviaryProblem` object returned by the {glue:md}`run_aviary` function call. We will use this later to run our off-design missions." ] }, { @@ -60,7 +61,7 @@ "id": "f1a098f9", "metadata": {}, "source": [ - "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is a \"off_deign_max_range\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"off_design_min_fuel\" mission, where the range of the mission is known, and Aviary solves the exact amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", + "Now we will run two off-design missions, one of each pre-defined type in Aviary. First is an \"off_deign_max_range\" mission, which is a mission where the takeoff gross mass of the aircraft is pre-defined, and the aircraft is flown as far as it can with the amount of fuel allowed under that gross mass limit. Second is an \"off_design_min_fuel\" mission, where the range of the mission is known, and Aviary solves the minimum amount of fuel (and therefore the takeoff gross mass) needed for the aircraft to fly that range.\n", "\n", "An off-design mission is run using the {glue:md}`run_off_design_mission()` method of {glue:md}`AviaryProblem`. This method has significantly more flexibility than demonstrated in this example, including the ability to change the passengers and cargo loaded on the aircraft, and the option to change the mission profile flown via providing new phase_info. \n", "\n", @@ -74,14 +75,14 @@ "metadata": {}, "outputs": [], "source": [ - "# OFF_DESIGN_MAX_RANGE Mission - fixed-mass, varying range\n", + "# OFF_DESIGN_MAX_RANGE Mission - fixed mass, solve for max range\n", "off_design_max_range_prob = design_prob.run_off_design_mission(\n", " problem_type='off_design_max_range',\n", " mission_gross_mass=115000,\n", " name='off_design_max_range_mission',\n", ")\n", "\n", - "# OFF_DESIGN_MIN_FUEL Mission - fixed range, varying fuel\n", + "# OFF_DESIGN_MIN_FUEL Mission - fixed range, solve for min fuel\n", "off_design_min_fuel_prob = design_prob.run_off_design_mission(\n", " problem_type='off_design_min_fuel', mission_range=1250, name='off_design_min_fuel_mission'\n", ")" @@ -254,7 +255,7 @@ ], "metadata": { "kernelspec": { - "display_name": "aviary", + "display_name": "aviary_dev", "language": "python", "name": "python3" }, @@ -268,7 +269,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.12" + "version": "3.14.3" } }, "nbformat": 4, diff --git a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb index 10b0717da2..6839009be5 100644 --- a/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb +++ b/aviary/docs/user_guide_unreviewed/off_design_missions.ipynb @@ -22,9 +22,7 @@ "\n", "\n", "str_off_design_min_fuel_snippet = f'```\\n{str_problem_type}, {str_off_design_min_fuel}\\n```'\n", - "glue_variable(\n", - " 'off_design_min_fuel_snippet', str_off_design_min_fueln_min_fuel_snippet, md_code=False\n", - ")\n", + "glue_variable('off_design_min_fuel_snippet', str_off_design_min_fuel_snippet, md_code=False)\n", "\n", "off_design_max_range_snippet = f'```\\n{str_problem_type}, {str_off_design_max_range}\\n```'\n", "glue_variable('off_design_max_range_snippet', off_design_max_range_snippet, md_code=False)\n", @@ -38,8 +36,8 @@ "TWO_DEGREES_OF_FREEDOM = av.EquationsOfMotion.TWO_DEGREES_OF_FREEDOM\n", "glue_variable('2DOF', TWO_DEGREES_OF_FREEDOM.name, md_code=True)\n", "\n", - "# file_path = av.get_path('examples/run_off_design_example.py').relative_to(av.top_dir.parent)\n", - "# glue_variable('run_off_design_example.py', file_path, md_code=True)\n", + "file_path = av.get_path('docs/examples/run_off_design_example.py').relative_to(av.top_dir.parent)\n", + "glue_variable('run_off_design_example.py', file_path, md_code=True)\n", "\n", "# file_path2 = av.get_path('examples/run_level2_example.py').relative_to(av.top_dir.parent)\n", "# glue_variable('run_level2_example.py', file_path2, md_code=True)\n", @@ -61,7 +59,7 @@ "\n", "glue_variable('objectives_fuel', av.Mission.Objectives.FUEL, md_code=True)\n", "glue_variable('objectives_range', av.Mission.Objectives.RANGE, md_code=True)\n", - "glue_variable('summary_gross_mass', av.Mission.GROSS_MASS, md_code=True)" + "glue_variable('mission_gross_mass', av.Mission.GROSS_MASS, md_code=True)" ] }, { @@ -86,9 +84,9 @@ "\n", "The off-design missions correspond to the different problem types that can have differing objectives which are discussed in detail in Level 2.\n", "The problem type determines what the optimizer can control to find a valid solution. \n", - "- Sizing Missions allow the optimizer to control both the {glue:md}`summary_gross_mass` and {glue:md}`design_gross_mass` for the given mission and objective. \n", - "- OFF_DESIGN_MIN_FUEL Missions allow the optimizer to only control the {glue:md}`summary_gross_mass` for the mission.\n", - "- OFF_DESIGN_MAX_RANGE Missions don't allow the optimizer to control either {glue:md}`summary_gross_mass` or {glue:md}`design_gross_mass` but allows the optimizer to extend the range until the summary matches the design.\n", + "- Sizing Missions allow the optimizer to control both the {glue:md}`mission_gross_mass` and {glue:md}`design_gross_mass` for the given mission and objective. \n", + "- OFF_DESIGN_MIN_FUEL Missions allow the optimizer to only control the {glue:md}`mission_gross_mass` for the mission.\n", + "- OFF_DESIGN_MAX_RANGE Missions don't allow the optimizer to control either {glue:md}`mission_gross_mass` or {glue:md}`design_gross_mass` but allows the optimizer to extend the range until the summary matches the design.\n", "\n", "There are currently 3 different methods for running an off-design mission within Aviary. \n", "\n",