### Setup OpenMDAO Problem Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/minimal_example.rst.txt Initializes the OpenMDAO problem with the MissionAnalysis model and configures the Newton solver for steady flight force balance. ```python prob = om.Problem() prob.model = MissionAnalysis(num_nodes=11) prob.model.nonlinear_solver = om.NewtonSolver(solve_subsystems=True) prob.model.nonlinear_solver.options['maxiter'] = 10 prob.model.nonlinear_solver.options['iprint'] = 2 prob.model.linear_solver = om.DirectSolver() prob.setup() ``` -------------------------------- ### Setup and Run TBM Analysis Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Configures the OpenMDAO problem, sets mission parameters, and provides initial guesses for convergence. ```python def run_tbm_analysis(): # Set up OpenMDAO to analyze the airplane nn = 11 prob = om.Problem() prob.model = TBMAnalysisGroup(num_nodes=nn) prob.model.nonlinear_solver = om.NewtonSolver(iprint=2, solve_subsystems=True) prob.model.linear_solver = om.DirectSolver() prob.setup() # Set required mission parameters. Each phase needs a vertical speed and airspeed. # The entire mission needs a cruise altitude and range. prob.set_val("climb.fltcond|vs", np.full(nn, 1500.0), units="ft/min") prob.set_val("climb.fltcond|Ueas", np.full(nn, 124.0), units="kn") prob.set_val("cruise.fltcond|vs", np.full(nn, 0.01), units="ft/min") prob.set_val("cruise.fltcond|Ueas", np.full(nn, 201.0), units="kn") prob.set_val("descent.fltcond|vs", np.full(nn, -600.0), units="ft/min") prob.set_val("descent.fltcond|Ueas", np.full(nn, 140.0), units="kn") prob.set_val("cruise|h0", 28e3, units="ft") prob.set_val("mission_range", 500, units="nmi") # Guesses for takeoff speeds to help with convergence prob.set_val("v0v1.fltcond|Utrue", np.full(nn, 50), units="kn") prob.set_val("v1vr.fltcond|Utrue", np.full(nn, 85), units="kn") prob.set_val("v1v0.fltcond|Utrue", np.full(nn, 85), units="kn") ``` -------------------------------- ### Configure Model Setup and Inputs/Outputs Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Sets up required OpenConcept inputs, custom aircraft parameters, and model outputs, while declaring partial derivatives using the complex step method. ```python def setup(self): nn = self.options["num_nodes"] # ======== Inputs passed from the mission analysis ======== # These are required by OpenConcept self.add_input("fltcond|CL", shape=nn) # lift coefficient self.add_input("throttle", shape=nn) # throttle from 0 to 1 # These are additional inputs used by the model self.add_input("fltcond|q", shape=nn, units="Pa") # dynamic pressure self.add_input("ac|geom|wing|S_ref", shape=1, units="m**2") # wing planform area self.add_input("ac|weights|TOW", shape=1, units="kg") # constant weight value self.add_input("ac|propulsion|max_thrust", shape=1, units="N") self.add_input("ac|aero|L_over_D", shape=1) # ======== Outputs sent back to the mission analysis ======== self.add_output("weight", shape=nn, units="kg") self.add_output("drag", shape=nn, units="N") self.add_output("thrust", shape=nn, units="N") # ======== Use complex step for this simple example ======== self.declare_partials(["*"], ["*"], method="cs") ``` -------------------------------- ### Define Mission Setup Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/integrator.html Configures independent variables and adds a mission subsystem to the OpenMDAO model. ```python def setup(self): iv = self.add_subsystem("ac_vars", om.IndepVarComp(), promotes_outputs=["*"]) iv.add_output("ac|geom|wing|S_ref", val=25.0, units="m**2") iv.add_output("ac|weights|TOW", val=5e3, units="kg") iv.add_output("ac|propulsion|max_thrust", val=1e4, units="N") iv.add_output("ac|propulsion|TSFC", val=20.0, units="g/kN/s") iv.add_output("ac|aero|L_over_D", val=10.0) # Define the mission self.add_subsystem( "mission", BasicMission(aircraft_model=AircraftWithFuelBurn, num_nodes=self.options["num_nodes"]), promotes_inputs=["ac|*"], ) ``` -------------------------------- ### Setup OpenMDAO Problem Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Initializes the OpenMDAO problem with the MissionAnalysis model, configures Newton and Direct solvers, and sets mission profile parameters. ```python def setup_problem(model=MissionAnalysis): """ Define the OpenMDAO problem """ nn = 11 prob = om.Problem() prob.model = model(num_nodes=nn) # Set up the solver prob.model.nonlinear_solver = om.NewtonSolver(iprint=2, solve_subsystems=True) prob.model.linear_solver = om.DirectSolver() # Set up the problem prob.setup() # Define the mission profile by setting vertical speed and airspeed for each segment prob.set_val("mission.climb.fltcond|vs", np.full(nn, 500.0), units="ft/min") prob.set_val("mission.cruise.fltcond|vs", np.full(nn, 0.0), units="ft/min") prob.set_val("mission.descent.fltcond|vs", np.full(nn, -500.0), units="ft/min") prob.set_val("mission.climb.fltcond|Ueas", np.full(nn, 150.0), units="kn") prob.set_val("mission.cruise.fltcond|Ueas", np.full(nn, 200.0), units="kn") prob.set_val("mission.descent.fltcond|Ueas", np.full(nn, 150.0), units="kn") # The mission also needs the cruise altitude and the range prob.set_val("mission.cruise|h0", 15e3, units="ft") prob.set_val("mission.mission_range", 400.0, units="nmi") return prob ``` -------------------------------- ### Run Integrator Script Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/integrator.rst.txt Executes the problem setup and adds variables of interest for plotting. ```python # rst Run (beg) prob.setup() prob.run_model() print('Fuel burned:', prob.get_val('mission.fuel_integrator.fuel_burned_final', units='kg')) # rst Run (end) ``` -------------------------------- ### Initialize Integrator Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/integrator.rst.txt Configures the Integrator component with integration points, time setup, and integration scheme. ```python self.add_subsystem('fuel_integrator', Integrator(num_nodes=nn, diff_units='s', time_setup='duration', method='simpson')) self.add_subsystem('fuel_burn_comp', AddIntegratedQuantity(name='fuel_burned', rate_name='fuel_flow', units='kg')) self.connect('fuel_burn_comp.fuel_burned', 'fuel_integrator.fuel_burned') self.connect('fuel_flow', 'fuel_integrator.fuel_flow') ``` -------------------------------- ### Setup Problem and Mission Profile Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/turboprop.rst.txt Configures the OpenMDAO problem, sets initial guesses for takeoff speeds, and adjusts throttle settings for takeoff segments. ```python # rst Setup problem (beg) prob = om.Problem() prob.model = om.Group() # ... (setup code omitted for brevity) ... # Set initial guesses for the takeoff speeds # This helps the solver converge more quickly prob.set_val('mission.takeoff.v0', 0.0, units='kn') prob.set_val('mission.takeoff.v1', 80.0, units='kn') prob.set_val('mission.takeoff.vr', 90.0, units='kn') prob.set_val('mission.takeoff.v2', 100.0, units='kn') # Set the structural fudge factor # This is a multiplier on the structural weights to account for # additional weights not modeled by the empty weight component prob.set_val('structural_fudge', 1.1) # Decrease the throttle values for the takeoff segments # The default is 1.0, but we set it to 0.826 for the takeoff prob.set_val('mission.takeoff.throttle', 0.826) # rst Setup problem (end) ``` -------------------------------- ### openconcept.utilities.linearinterp.LinearInterpolator Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/utilities/linearinterp.html A component that creates a linearly interpolated set of points including the start and end values. ```APIDOC ## class LinearInterpolator ### Description Creates a linearly interpolated set of points including two end points. ### Inputs - **start_val** (float) - Starting value (units set from "units" option) - **end_val** (float) - Ending value (units set from "units" option) ### Outputs - **vec** (float) - Vector of linearly interpolated points ### Options - **units** (str, None) - Units for inputs and outputs - **num_nodes** (int) - Number of linearly interpolated points to produce (minimum/default 2) ``` -------------------------------- ### Initialize OpenConcept Model Options Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Defines the required num_nodes and flight_phase options for an OpenConcept aircraft model. ```python def initialize(self): self.options.declare("num_nodes", default=1, desc="Number of analysis points per phase") self.options.declare("flight_phase", default=None) # required by OpenConcept but unused in this example ``` -------------------------------- ### Initialize Model Options Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Initializes the model options for number of nodes and flight phase. ```python def initialize(self): self.options.declare("num_nodes", default=1) self.options.declare("flight_phase", default=None) ``` -------------------------------- ### Configure TBM analysis problem Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Initializes the OpenMDAO problem with solvers, mission parameters, and takeoff segment guesses for the TBM aircraft model. ```python def run_tbm_analysis(): # Set up OpenMDAO to analyze the airplane nn = 11 prob = om.Problem() prob.model = TBMAnalysisGroup(num_nodes=nn) prob.model.nonlinear_solver = om.NewtonSolver(iprint=2, solve_subsystems=True) prob.model.linear_solver = om.DirectSolver() prob.setup() # Set required mission parameters. Each phase needs a vertical speed and airspeed. # The entire mission needs a cruise altitude and range. prob.set_val("climb.fltcond|vs", np.full(nn, 1500.0), units="ft/min") prob.set_val("climb.fltcond|Ueas", np.full(nn, 124.0), units="kn") prob.set_val("cruise.fltcond|vs", np.full(nn, 0.01), units="ft/min") prob.set_val("cruise.fltcond|Ueas", np.full(nn, 201.0), units="kn") prob.set_val("descent.fltcond|vs", np.full(nn, -600.0), units="ft/min") prob.set_val("descent.fltcond|Ueas", np.full(nn, 140.0), units="kn") prob.set_val("cruise|h0", 28e3, units="ft") prob.set_val("mission_range", 500, units="nmi") # Guesses for takeoff speeds to help with convergence prob.set_val("v0v1.fltcond|Utrue", np.full(nn, 50), units="kn") prob.set_val("v1vr.fltcond|Utrue", np.full(nn, 85), units="kn") prob.set_val("v1v0.fltcond|Utrue", np.full(nn, 85), units="kn") # Set some airplane-specific values. The throttle edits are to derate the takeoff power of the PT6A prob["climb.OEW.structural_fudge"] = 1.67 prob["v0v1.throttle"] = np.full(nn, 0.826) prob["v1vr.throttle"] = np.full(nn, 0.826) prob["rotate.throttle"] = np.full(nn, 0.826) return prob ``` -------------------------------- ### Execute Problem and Print Results Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/turboprop.rst.txt Runs the optimization/analysis and outputs key performance metrics, followed by plotting the takeoff mission phase. ```python # rst Run (beg) # Run the problem prob.run_model() # Print some values from the solved problem print('Takeoff distance:', prob.get_val('mission.takeoff.distance', units='ft')) print('Fuel used:', prob.get_val('mission.fuel_used', units='lb')) # Plot the takeoff portion of the mission # The plotting section from previous tutorials is used here # to add a plot showing the takeoff portion of the mission plot_takeoff(prob) # rst Run (end) ``` -------------------------------- ### VectorSplitComp.__init__ Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/utilities.math/combine_split_comp.html Initializes the VectorSplitComp component to split input vectors into multiple output variables. ```APIDOC ## VectorSplitComp.__init__(output_names=None, input_name=None, vec_sizes=None, length=1, val=1.0, **kwargs) ### Description Initializes a system to split a single input vector into multiple output vectors. ### Parameters - **output_names** (iterable of str) - Required - Names of the output (split) variables in this component’s namespace. - **input_name** (str) - Required - Name of the input variable for this system. - **vec_sizes** (iterable of int) - Required - Lengths of the first dimension of each input vector. - **length** (int) - Optional - Length of the second dimension of the input and output vectors. Default is 1. - **val** (float or list or tuple or ndarray) - Optional - Initial value of the variable. Default is 1.0. - **kwargs** (str) - Optional - Additional arguments such as units or description. ``` -------------------------------- ### Add and Configure an Integrator Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/integrator.html Instantiate an Integrator component and define the integrand to perform numerical integration. ```python integ = self.add_subsystem( "fuel_integrator", Integrator(num_nodes=nn, diff_units="s", time_setup="duration", method="simpson") ) integ.add_integrand("fuel_burned", rate_name="fuel_flow", units="kg") self.connect("fuel_flow_calc.fuel_flow", "fuel_integrator.fuel_flow") ``` -------------------------------- ### Import Necessary Modules Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Initial imports required for OpenMDAO, OpenConcept mission analysis, and NumPy. ```python import openmdao.api as om from openconcept.mission import BasicMission import numpy as np ``` -------------------------------- ### Execute Analysis and Visualize Results Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Runs the model, generates an N2 diagram, and creates matplotlib plots for altitude, vertical speed, airspeed, and throttle. ```python if __name__ == "__main__": # Process command line argument to optionally not show figures and N2 diagram import argparse parser = argparse.ArgumentParser() parser.add_argument( "--hide_visuals", default=False, action="store_true", help="Do not show matplotlib figure or open N2 diagram in browser", ) hide_viz = parser.parse_args().hide_visuals # Setup the problem and run the analysis prob = setup_problem() prob.run_model() # Generate N2 diagram om.n2(prob, outfile="minimal_example_n2.html", show_browser=not hide_viz) # Create plot with results import matplotlib.pyplot as plt fig, axs = plt.subplots(2, 2, constrained_layout=True) axs = axs.flatten() # change 2x2 mtx of axes into 4-element vector # Define variables to plot plot_vars = [ {"var": "fltcond|h", "name": "Altitude", "units": "ft"}, {"var": "fltcond|vs", "name": "Vertical speed", "units": "ft/min"}, {"var": "fltcond|Utrue", "name": "True airspeed", "units": "kn"}, {"var": "throttle", "name": "Throttle", "units": None}, ] for idx_fig, var in enumerate(plot_vars): axs[idx_fig].set_xlabel("Range (nmi)") axs[idx_fig].set_ylabel(f"{var['name']}" if var["units"] is None else f"{var['name']} ({var['units']})") # Loop through each flight phase and plot the current variable from each for phase in ["climb", "cruise", "descent"]: axs[idx_fig].plot( prob.get_val(f"mission.{phase}.range", units="nmi"), prob.get_val(f"mission.{phase}.{var['var']}", units=var["units"]), "-o", c="tab:blue", markersize=2.0, ) fig.savefig("minimal_example_results.svg", transparent=True) if not hide_viz: plt.show() ``` -------------------------------- ### Importing OpenConcept components for turboprop modeling Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Required imports for the TBM 850 aircraft model, including propulsion, aerodynamics, weight, and mission analysis modules. ```python import numpy as np import openmdao.api as om # OpenConcept imports for the airplane model from openconcept.propulsion import TurbopropPropulsionSystem from openconcept.aerodynamics import PolarDrag from openconcept.weights import SingleTurboPropEmptyWeight from openconcept.mission import FullMissionAnalysis from openconcept.examples.aircraft_data.TBM850 import data as acdata from openconcept.utilities import AddSubtractComp, Integrator, DictIndepVarComp ``` -------------------------------- ### Configure Mission Profile Parameters Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Sets vertical speed, airspeed, cruise altitude, and range for mission segments. ```python # Define the mission profile by setting vertical speed and airspeed for each segment prob.set_val("mission.climb.fltcond|vs", np.full(nn, 500.0), units="ft/min") prob.set_val("mission.cruise.fltcond|vs", np.full(nn, 0.0), units="ft/min") prob.set_val("mission.descent.fltcond|vs", np.full(nn, -500.0), units="ft/min") prob.set_val("mission.climb.fltcond|Ueas", np.full(nn, 150.0), units="kn") prob.set_val("mission.cruise.fltcond|Ueas", np.full(nn, 200.0), units="kn") prob.set_val("mission.descent.fltcond|Ueas", np.full(nn, 150.0), units="kn") # The mission also needs the cruise altitude and the range prob.set_val("mission.cruise|h0", 15e3, units="ft") prob.set_val("mission.mission_range", 400.0, units="nmi") return prob ``` -------------------------------- ### Run Mission Analysis Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/minimal_example.rst.txt Executes the mission analysis model after the problem has been configured. ```python prob.run_model() ``` -------------------------------- ### Add Weight and Fuel Integration Subsystems Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Configures the OEW estimation, fuel flow integration, and weight calculation components within the OpenConcept model. ```python # Empty weight calculation; requires many aircraft inputs, see SingleTurboPropEmptyWeight source for more details. # This OEW calculation is not used in the weight calculation, but it is a useful output for aircraft design/optimization. self.add_subsystem( "OEW", SingleTurboPropEmptyWeight(), promotes_inputs=["*", ("P_TO", "ac|propulsion|engine|rating")], promotes_outputs=["OEW"], ) self.connect("propmodel.prop1.component_weight", "W_propeller") self.connect("propmodel.eng1.component_weight", "W_engine") # Airplanes that consume fuel need to integrate fuel usage across the mission and subtract it from TOW intfuel = self.add_subsystem( "intfuel", Integrator(num_nodes=nn, method="simpson", diff_units="s", time_setup="duration"), promotes_outputs=["fuel_used_final"], ) intfuel.add_integrand("fuel_used", rate_name="fuel_flow", val=1.0, units="kg") self.connect("propmodel.fuel_flow", "intfuel.fuel_flow") # Compute weight as MTOW minus fuel burned (assumes takeoff at MTOW) self.add_subsystem( "weight", AddSubtractComp( output_name="weight", input_names=["ac|weights|MTOW", "fuel_used"], units="kg", vec_size=[1, nn], scaling_factors=[1, -1], ), promotes_inputs=["ac|weights|MTOW"], promotes_outputs=["weight"], ) self.connect("intfuel.fuel_used", "weight.fuel_used") ``` -------------------------------- ### openconcept.atmospherics.density_comp.DensityComp Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/atmospherics/density_comp.html An OpenMDAO ExplicitComponent that computes density from pressure and temperature. ```APIDOC ## class openconcept.atmospherics.density_comp.DensityComp(**kwargs) ### Description This component computes density from pressure and temperature. It is adapted from the methodology described by Jasa, Hwang, and Martins (2018). ### Inputs - **fltcond|p** (float) - Pressure at flight condition (vector, Pa) - **fltcond|T** (float) - Temperature at flight condition (vector, K) ### Outputs - **fltcond|rho** (float) - Density at flight condition (vector, kg/m^3) ### Options - **num_nodes** (int) - Number of analysis points to run, i.e. length of vector inputs (scalar, dimensionless) ``` -------------------------------- ### CFM56() Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/propulsion/cfm56.html Initializes the CFM56 turbofan engine component. ```APIDOC ## CFM56(num_nodes=1, plot=False) ### Description Returns an OpenMDAO component for an engine deck to model the CFM56 turbofan. ### Parameters - **num_nodes** (int) - Optional - Number of analysis points to run (sets vector length; default 1). - **plot** (bool) - Optional - Whether to plot results (default False). ### Inputs - **throttle** (float) - Engine throttle (0 to 1+). Controls power and fuel flow. - **fltcond|h** (float) - Altitude. - **fltcond|M** (float) - Mach number. ### Outputs - **thrust** (float) - Thrust developed by the engine (lbf). - **fuel_flow** (float) - Fuel flow consumed (lbm/s). - **T4** (float) - Turbine inlet temperature (Rankine). ``` -------------------------------- ### Importing Integrator and Dependencies Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/integrator.html Required imports for integrating OpenConcept components and the Integrator class. ```python import openmdao.api as om from openconcept.examples.minimal import Aircraft, setup_problem # build off this aircraft model from openconcept.mission import BasicMission from openconcept.utilities import Integrator ``` -------------------------------- ### Run Mission Analysis and Plot Results Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/integrator.html Executes the mission analysis problem and generates plots for flight conditions and fuel consumption metrics. ```python if __name__ == "__main__": # Process command line argument to optionally not show figures and N2 diagram import argparse parser = argparse.ArgumentParser() parser.add_argument( "--hide_visuals", default=False, action="store_true", help="Do not show matplotlib figure or open N2 diagram in browser", ) hide_viz = parser.parse_args().hide_visuals # Setup the problem and run the analysis prob = setup_problem(model=MissionAnalysisWithFuelBurn) prob.run_model() # Generate N2 diagram om.n2(prob, outfile="minimal_integrator_n2.html", show_browser=not hide_viz) # Create plot with results import matplotlib.pyplot as plt fig, axs = plt.subplots(2, 3, figsize=[9, 4.8], constrained_layout=True) axs = axs.flatten() # change 2x3 mtx of axes into 4-element vector # Define variables to plot plot_vars = [ {"var": "fltcond|h", "name": "Altitude", "units": "ft"}, {"var": "fltcond|vs", "name": "Vertical speed", "units": "ft/min"}, {"var": "fltcond|Utrue", "name": "True airspeed", "units": "kn"}, {"var": "throttle", "name": "Throttle", "units": None}, {"var": "fuel_flow_calc.fuel_flow", "name": "Fuel flow", "units": "g/s"}, {"var": "weight", "name": "Weight", "units": "kg"}, ] for idx_fig, var in enumerate(plot_vars): axs[idx_fig].set_xlabel("Range (nmi)") axs[idx_fig].set_ylabel(f"{var['name']}" if var["units"] is None else f"{var['name']} ({var['units']})") # Loop through each flight phase and plot the current variable from each for phase in ["climb", "cruise", "descent"]: axs[idx_fig].plot( prob.get_val(f"mission.{phase}.range", units="nmi"), prob.get_val(f"mission.{phase}.{var['var']}", units=var["units"]), "-o", c="tab:blue", markersize=2.0, ) fig.savefig("minimal_integrator_results.svg", transparent=True) if not hide_viz: plt.show() ``` -------------------------------- ### ElementMultiplyDivideComp Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/utilities.math/multiply_divide_comp.html Initializes a new instance of the ElementMultiplyDivideComp class to perform vectorized element-wise multiplication and division. ```APIDOC ## class openconcept.utilities.math.multiply_divide_comp.ElementMultiplyDivideComp ### Description Compute a vectorized element-wise multiplication and/or division. The result is calculated as (a * b / c ...) * scaling_factor. ### Constructor `ElementMultiplyDivideComp(output_name=None, input_names=None, vec_size=1, length=1, val=1.0, scaling_factor=1, divide=None, input_units=None, **kwargs)` ### Parameters - **output_name** (str) - Required - Name of the result variable. - **input_names** (iterable of str) - Required - Names of the input variables. - **vec_size** (int) - Optional - Length of the first dimension of the input/output vectors. Default is 1. - **length** (int) - Optional - Length of the second dimension of the input/output vectors. Default is 1. - **scaling_factor** (numeric) - Optional - Scaling factor to apply to the whole system. Default is 1. - **divide** (iterable of bool or None) - Optional - True to use division, False to use multiplication. Default is None. - **val** (float/list/tuple/ndarray) - Optional - Initial value of the variable. Default is 1.0. - **input_units** (iterable of str) - Optional - Units for each input vector. ### Methods #### add_equation(output_name, input_names, vec_size=1, length=1, val=1.0, res_units=None, desc='', lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=None, scaling_factor=1, divide=None, input_units=None, tags=None) Adds a multiplication/division relation to the component. ``` -------------------------------- ### Define Mission with Integrator Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/integrator.rst.txt Sets up the mission analysis component using the AircraftWithFuelBurn model. ```python # rst Mission (beg) prob.model.add_subsystem('mission', MissionWithFuelBurn(num_nodes=nn, aircraft_model=AircraftWithFuelBurn)) prob.model.set_val('mission.ac|propulsion|TSFC', 0.5, units='1/hr') # rst Mission (end) ``` -------------------------------- ### Implement Compute Method Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html Defines the compute method to calculate weight, thrust, and drag based on input parameters. ```python def compute(self, inputs, outputs): outputs["weight"] = inputs["ac|weights|TOW"] outputs["thrust"] = inputs["throttle"] * inputs["ac|propulsion|max_thrust"] outputs["drag"] = ( inputs["fltcond|q"] * inputs["fltcond|CL"] * inputs["ac|geom|wing|S_ref"] / inputs["ac|aero|L_over_D"] ) ``` -------------------------------- ### Define a minimal aircraft model and mission analysis Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/minimal_example.html This script defines an explicit OpenMDAO component for aircraft physics and a group for mission analysis, followed by a function to configure the OpenMDAO problem. ```python """ This is a minimal example meant to show the absolute simplest aircraft model and mission analysis you can set up. This does not use OpenConcept's models for the aircraft propulsion and aerodynamics, but it does use the mission analysis methods. """ # rst Imports (beg) import openmdao.api as om from openconcept.mission import BasicMission import numpy as np # rst Imports (end) # rst Aircraft (beg) class Aircraft(om.ExplicitComponent): """ An overly simplified aircraft model. This one is defined as an explicit component to use simple equations to compute weight, drag, and thrust. In practice, it would be an OpenMDAO group that integrates models for propulsion, aerodynamics, weights, etc. """ # rst Options def initialize(self): self.options.declare("num_nodes", default=1, desc="Number of analysis points per phase") self.options.declare("flight_phase", default=None) # required by OpenConcept but unused in this example # rst Setup def setup(self): nn = self.options["num_nodes"] # ======== Inputs passed from the mission analysis ======== # These are required by OpenConcept self.add_input("fltcond|CL", shape=nn) # lift coefficient self.add_input("throttle", shape=nn) # throttle from 0 to 1 # These are additional inputs used by the model self.add_input("fltcond|q", shape=nn, units="Pa") # dynamic pressure self.add_input("ac|geom|wing|S_ref", shape=1, units="m**2") # wing planform area self.add_input("ac|weights|TOW", shape=1, units="kg") # constant weight value self.add_input("ac|propulsion|max_thrust", shape=1, units="N") self.add_input("ac|aero|L_over_D", shape=1) # ======== Outputs sent back to the mission analysis ======== self.add_output("weight", shape=nn, units="kg") self.add_output("drag", shape=nn, units="N") self.add_output("thrust", shape=nn, units="N") # ======== Use complex step for this simple example ======== self.declare_partials(["*"], ["*"], method="cs") # rst Compute def compute(self, inputs, outputs): outputs["weight"] = inputs["ac|weights|TOW"] outputs["thrust"] = inputs["throttle"] * inputs["ac|propulsion|max_thrust"] outputs["drag"] = ( inputs["fltcond|q"] * inputs["fltcond|CL"] * inputs["ac|geom|wing|S_ref"] / inputs["ac|aero|L_over_D"] ) # rst Aircraft (end) # rst Mission (beg) class MissionAnalysis(om.Group): """ OpenMDAO group for basic three-phase climb, cruise, descent mission. The only top-level aircraft variable that the aircraft model uses is the wing area, so that must be defined. """ def initialize(self): self.options.declare("num_nodes", default=1, desc="Number of analysis points per phase") def setup(self): iv = self.add_subsystem("ac_vars", om.IndepVarComp(), promotes_outputs=["*"]) iv.add_output("ac|geom|wing|S_ref", val=25.0, units="m**2") iv.add_output("ac|weights|TOW", val=5e3, units="kg") iv.add_output("ac|propulsion|max_thrust", val=1e4, units="N") iv.add_output("ac|aero|L_over_D", val=10.0) # Define the mission self.add_subsystem( "mission", BasicMission(aircraft_model=Aircraft, num_nodes=self.options["num_nodes"]), promotes_inputs=["ac|*"], ) # rst Mission (end) # rst Setup problem (beg) def setup_problem(model=MissionAnalysis): """ Define the OpenMDAO problem """ nn = 11 prob = om.Problem() prob.model = model(num_nodes=nn) # Set up the solver prob.model.nonlinear_solver = om.NewtonSolver(iprint=2, solve_subsystems=True) prob.model.linear_solver = om.DirectSolver() # Set up the problem prob.setup() ``` -------------------------------- ### Configure Aircraft Parameters Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Sets structural fudge factors and throttle settings for the PT6A engine to derate takeoff power. ```python # Set some airplane-specific values. The throttle edits are to derate the takeoff power of the PT6A prob["climb.OEW.structural_fudge"] = 1.67 prob["v0v1.throttle"] = np.full(nn, 0.826) prob["v1vr.throttle"] = np.full(nn, 0.826) prob["rotate.throttle"] = np.full(nn, 0.826) return prob ``` -------------------------------- ### SimpleBattery Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_srcdocs/packages/energy_storage/battery.html A basic battery component that tracks power limits and generates heat, inheriting from OpenMDAO ExplicitComponent. ```APIDOC ## SimpleBattery ### Description SimpleBattery is a component that models basic battery power limits and heat generation without tracking state of charge. ### Inputs - **battery_weight** (float) - Weight of the battery pack (kg) - **elec_load** (float) - Electric power draw upstream (W) ### Outputs - **max_energy** (float) - Total energy at 100% SOC (Wh) - **heat_out** (float) - Waste heat produced (W) - **component_cost** (float) - Nonrecurring cost (USD) - **component_sizing_margin** (float) - Sizing margin (dimensionless) ### Options - **num_nodes** (int) - Number of analysis points - **efficiency** (float) - Shaft power efficiency (0.0 to 1.0) - **specific_power** (float) - Rated power per unit weight (W/kg) - **specific_energy** (float) - Energy per unit weight (Wh/kg) - **cost_inc** (float) - Cost per unit weight (USD/kg) - **cost_base** (float) - Base cost (USD) ``` -------------------------------- ### Define Mission Parameters with DictIndepVarComp Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/tutorials/turboprop.rst.txt Uses a nested dictionary to define aircraft parameters, allowing for centralized configuration outside the main run script. ```python # rst Mission (beg) # Add aircraft parameters from the data file # The DictIndepVarComp allows us to define parameters in a separate file # and then pull them into the OpenMDAO problem ivc = prob.model.add_subsystem('params', om.IndepVarComp(), promotes=['*']) # We use a DictIndepVarComp to load the parameters from the TBM850 data file # This is a convenient way to manage a large number of parameters # The keys are separated by a pipe '|' to access nested dictionary values ivc = prob.model.add_subsystem('params', DictIndepVarComp(TBM850_data), promotes=['*']) ivc.add_output_from_dict('ac|weights|MTOW', 'MTOW') ivc.add_output_from_dict('ac|weights|W_fuel_max', 'W_fuel_max') ivc.add_output_from_dict('ac|aero|CLmax_land', 'CLmax_land') ivc.add_output_from_dict('ac|aero|CLmax_takeoff', 'CLmax_takeoff') ivc.add_output_from_dict('ac|aero|CD0_TO', 'CD0_TO') ivc.add_output_from_dict('ac|aero|CD0_cruise', 'CD0_cruise') ivc.add_output_from_dict('ac|aero|e', 'e') ivc.add_output_from_dict('ac|aero|AR', 'AR') ivc.add_output_from_dict('ac|aero|span', 'span') ivc.add_output_from_dict('ac|geom|S_ref', 'S_ref') # Add the mission group # The FullMissionAnalysis includes a balanced field length calculation # which is a more advanced mission analysis than the BasicMission prob.model.add_subsystem('mission', FullMissionAnalysis(num_nodes=11, aircraft_model=TBM850Turboprop, takeoff_strategy='balanced_field'), promotes_inputs=['*'], promotes_outputs=['*']) # rst Mission (end) ``` -------------------------------- ### Integrate Fuel Usage and Calculate Weight Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/tutorials/turboprop.html Integrates fuel flow over time and subtracts the consumed fuel from the maximum takeoff weight. ```python # Airplanes that consume fuel need to integrate fuel usage across the mission and subtract it from TOW intfuel = self.add_subsystem( "intfuel", Integrator(num_nodes=nn, method="simpson", diff_units="s", time_setup="duration"), promotes_outputs=["fuel_used_final"], ) intfuel.add_integrand("fuel_used", rate_name="fuel_flow", val=1.0, units="kg") self.connect("propmodel.fuel_flow", "intfuel.fuel_flow") # Compute weight as MTOW minus fuel burned (assumes takeoff at MTOW) self.add_subsystem( "weight", AddSubtractComp( output_name="weight", input_names=["ac|weights|MTOW", "fuel_used"], units="kg", vec_size=[1, nn], scaling_factors=[1, -1], ), promotes_inputs=["ac|weights|MTOW"], promotes_outputs=["weight"], ), self.connect("intfuel.fuel_used", "weight.fuel_used") ``` -------------------------------- ### Cite OpenAeroStruct Integration Paper Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/index.rst.txt BibTeX entry for referencing the journal paper regarding aerostructural wing optimization. ```bibtex @article{Adler2022d, author = {Adler, Eytan J. and Martins, Joaquim R. R. A.}, doi = {10.2514/1.c037096}, issn = {1533-3868}, journal = {Journal of Aircraft}, month = {December}, publisher = {American Institute of Aeronautics and Astronautics}, title = {Efficient Aerostructural Wing Optimization Considering Mission Analysis}, year = {2022} } ``` -------------------------------- ### Cite the OpenAeroStruct journal paper Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/index.html BibTeX entry for referencing the aerostructural wing optimization methodology. ```bibtex @article{Adler2022d, author = {Adler, Eytan J. and Martins, Joaquim R. R. A.}, doi = {10.2514/1.c037096}, issn = {1533-3868}, journal = {Journal of Aircraft}, month = {December}, publisher = {American Institute of Aeronautics and Astronautics}, title = {Efficient Aerostructural Wing Optimization Considering Mission Analysis}, year = {2022} } ``` -------------------------------- ### BibTeX Citation for Thermal Management System Paper Source: https://mdolab-openconcept.readthedocs-hosted.com/en/latest/_sources/publications.rst.txt Use this BibTeX entry to cite the 2022 Aerospace journal paper on thermal management system optimization. ```bibtex @article{Adler2022b, article-number = {243}, author = {Adler, Eytan J. and Brelje, Benjamin J. and Martins, Joaquim R. R. A.}, doi = {10.3390/aerospace9050243}, issn = {2226-4310}, journal = {Aerospace}, keywords = {OpenMDAO, OpenConcept}, month = apr, number = {5}, title = {Thermal Management System Optimization for a Parallel Hybrid Aircraft Considering Mission Fuel Burn}, volume = {9}, year = {2022} } ```