OpenConcept
OpenConcept is a toolkit for the conceptual design of aircraft. It is open source (GitHub: https://github.com/mdolab/openconcept) and MIT licensed. OpenConcept was developed in order to model and optimize aircraft with electric propulsion at low computational cost. The tools are built on top of NASA Glenn’s OpenMDAO framework, which in turn is written in Python.
OpenConcept is capable of modeling a wide range of propulsion systems, including detailed thermal management systems.
The following figure (from this paper) shows one such system that is modeled in the N3_HybridSingleAisle_Refrig.py
example.

The following charts show more than 250 individually optimized hybrid-electric light twin aircraft (similar to a King Air C90GT). Optimizing hundreds of configurations can be done in a couple of hours on a standard laptop computer.

The reason for OpenConcept’s efficiency is the analytic derivatives built into each analysis routine and component. Accurate, efficient derivatives enable the use of Newton nonlinear equation solutions and gradient-based optimization at low computational cost.
Getting Started
OpenConcept can be pip installed directly from PyPI with pip install openconcept
.
To run the examples or edit the source code:
Clone the repo to disk (
git clone https://github.com/mdolab/openconcept
)Navigate to the root
openconcept
folderRun
pip install -e .
to install the package (the-e
can be omitted if not editing the source)
Get started by following the tutorials to learn the most important parts of OpenConcept. The features section of the documentation describes most of the components and system models available in OpenConcept.
Dependencies
OpenConcept is tested regularly on builds with the oldest and latest supported package versions. The package versions in the oldest and latest builds are the following:
Package |
Oldest |
Latest |
---|---|---|
Python |
3.8 |
3.11 |
OpenMDAO |
3.10 |
latest |
NumPy |
1.20 |
latest |
SciPy |
1.6.0 |
latest |
OpenAeroStruct |
latest |
latest |
Please Cite Us!
Please cite this software by reference to the conference paper:
Benjamin J. Brelje and Joaquim R.R.A. Martins. “Development of a Conceptual Design Model for Aircraft Electric Propulsion with Efficient Gradients”, 2018 AIAA/IEEE Electric Aircraft Technologies Symposium, AIAA Propulsion and Energy Forum, (AIAA 2018-4979) DOI: 10.2514/6.2018-4979
@inproceedings{Brelje2018,
address = {{C}incinnati,~{OH}},
author = {Benjamin J. Brelje and Joaquim R. R. A. Martins},
booktitle = {2018 AIAA/IEEE Electric Aircraft Technologies Symposium},
month = {July},
title = {Development of a Conceptual Design Model for Aircraft Electric Propulsion with Efficient Gradients},
year = {2018},
doi = {10.2514/6.2018-4979}
}
If using the integrated OpenAeroStruct VLM or aerostructural aerodynamic models, please cite the following journal paper:
Eytan J. Adler and Joaquim R.R.A. Martins, “Efficient Aerostructural Wing Optimization Considering Mission Analysis”, AIAA Journal of Aircraft, December 2022. DOI: 10.2514/1.c037096
@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}
}
Minimal Example
This example shows how to set up an OpenConcept aircraft and mission analysis model. The goal here is to use only what is absolutely necessary with the idea of introducing the starting point for building more complicated and detailed models. It uses a simplified aircraft model and basic mission profile with climb, cruise, and descent phases.
OpenConcept leans heavily on OpenMDAO for the numerical framework, in particular ExplicitComponent
, ImplicitComponent
, Group
, Problem
, the OpenMDAO solvers, and the optimizer interfaces.
If you are not already familiar with OpenMDAO, we strongly recommend going through their basic user guide.
Note
The script described in this tutorial is called minimal.py and can be found in the examples folder.
Imports
import openmdao.api as om
from openconcept.mission import BasicMission
import numpy as np
We start by importing the necessary modules. In this example, we need OpenMDAO to use its classes and solvers. From OpenConcept, we use the BasicMission mission analysis component. Lastly, we use NumPy to initialize vectors and matplotlib to make figures at the end.
Aircraft model
At it’s most basic, an OpenConcept aircraft model takes in a lift coefficient and throttle position (from 0 to 1) and returns thrust, weight, and drag.
In the code, these variables are named "fltcond|CL"
, "throttle"
, "thrust"
, "weight"
, and "drag"
, respectively.
You can add as much or as little detail in this computation of the outputs as you’d like, but all the model cares is that the outputs can be controlled by the inputs.
OpenConcept provides component models to build these aircraft systems.
The complexity can grow rapidly, so for now we will not use any of these OpenConcept models; instead we develop a minimal aircraft model. We assume constant weight across the whole mission. Thrust is modeled as maximum thrust times the throttle. Drag is computed as lift divided by lift-to-drag ratio.
Let’s first take a look at the code for the whole aircraft model, and then we’ll explain each part. The whole model looks like this:
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"]
)
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
We start by defining the options for the model. These two options are required for all OpenConcept aircraft models:
"num_nodes"
: OpenConcept numerically integrates states w.r.t. time in each mission phase. This option will tell the aircraft model how many numerical integration points are used in the phase. All the required inputs and outputs listed above are vectors of length"num_nodes"
."flight_phase"
: The mission analysis group sets this option to a string that is the name of the current flight phase. For example in the basic three-phase mission, this will be set either to"climb"
,"cruise"
, or"descent"
. This option can be used by the aircraft model to set phase-specific values. For example, in takeoff segments the user may want to set different aerodynamic parameters that correspond to a flaps-extended configuration.
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")
Next, we add the inputs and outputs to the aircraft model.
We start with the lift coefficient and throttle—the inputs required by OpenConcept.
Note that the shape of these inputs is defined as the number of nodes.
In other words, these inputs are vectors of length "num_nodes"
.
There are other parameters that the mission analysis will automatically connect to the aircraft model if they’re defined as inputs. A set of flight condition variables and other aircraft parameters defined by the user at the top level analysis group.
The available flight condition variables are the following:
Variable name |
Property |
Vector length |
---|---|---|
fltcond|CL |
Lift coefficient |
|
fltcond|q |
Dynamic pressure |
|
fltcond|rho |
Density |
|
fltcond|p |
Pressure |
|
fltcond|T |
Temperature (includes increment) |
|
fltcond|a |
Speed of sound |
|
fltcond|TempIncrement |
Increment on the 1976 Standard Atmosphere temperature |
|
fltcond|M |
Mach number |
|
fltcond|Utrue |
True airspeed |
|
fltcond|Ueas |
Equivalent airspeed |
|
fltcond|groundspeed |
Ground speed |
|
fltcond|vs |
Vertical speed |
|
fltcond|h |
Altitude |
|
fltcond|h_initial |
Initial altitude in phase |
1 |
fltcond|h_final |
Final altitude in phase |
1 |
fltcond|cosgamma |
Cosine of the flight path angle |
|
fltcond|singamma |
Sine of the flight path angle |
|
The aircraft parameters that the mission analysis passes through to the aircraft model are those set by the user in the top-level group (called "MissionAnalysis"
in our case).
It will pass any variable whose name starts with "ac|"
.
Here, we define four aircraft parameters that are used by the aircraft model: "ac|geom|wing|S_ref"
(wing area), "ac||weights|TOW"
(aircraft weight), "ac|propulsion|max_thrust"
, and "ac|aero|L_over_D"
(lift-to-drag ratio).
We will see in the mission tutorial section how they are defined at the top level.
Next, we add the outputs needed by OpenConcept to converge the mission: "weight"
, "drag"
, and "thrust"
.
Finally, we declare the derivatives of the outputs w.r.t. the inputs. In this case, we have OpenMDAO compute all the partial derivatives using complex step. In practice, analytically defining the partial derivatives offers more accurate and faster derivative computations.
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"]
)
In this section, we actually compute the output values using the inputs. The weight is simply equal to the defined weight. The thrust is equal to the throttle input times the max thrust input. The drag is equal to the lift divided by L/D, where lift is computed using the dynamic pressure, lift coefficient, and wing area.
Mission
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|*"],
)
Now that we have an aircraft model, we need to define the mission it will fly. In OpenConcept, we do this by defining a top-level OpenMDAO group. This group usually contains two components:
An
IndepVarComp
orDictIndepVarComp
with the aircraft parameters (the values are automatically passed to the aircraft model)The OpenConcept mission analysis block
In this case we have only four aircraft parameters, so we define them in the script using an OpenMDAO IndepVarComp
.
Following tutorials will use a slightly different method to keep the parameters more organized.
These outputs are promoted from the IndepVarComp
to the level of the MissionAnalysis
group (promote_outputs=["*"]
promotes all outputs).
Next we add the mission analysis.
In this tutorial, we use OpenConcept’s BasicMission
, which consists of a climb, a cruise, and a descent flight phase.
The aircraft model class is passed into the BasicMission
, along with the number of nodes per phase.
BasicMission
will instantiate copies of the aircraft model class in each flight phase and promote all inputs from the aircraft model that begin with "ac|"
to the BasicMission
level.
We promote all inputs that begin with "ac|*"
from the BasicMission
to the MissionAnalysis
level.
This way, OpenMDAO will automatically connect the outputs from the IndepVarComp
to the aircraft parameter inputs of the aircraft models in each phase.
In this group, we declare an option to set the number of nodes per phase so that the user can initialize the value when the problem is set up.
Note
"ac|geom|wing|S_ref"
is a required top-level aircraft parameter that must be defined.
OpenConcept uses it to compute lift coefficient from lift force.
Which other aircraft parameters are required is dependent on which OpenConcept models the user chooses to include in the aircraft model.
Run script
Setup problem
Now that we have the necessary models. The last step before running the model is setting up the OpenMDAO problem and providing the necessary values to define the mission profile.
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
We initialize an OpenMDAO Problem and add the MissionAnalysis
class we defined as the problem’s model.
Here is where we specify the number of nodes per flight phase, using 11.
Next, we add a solver that is used to determine the throttle and lift coefficient values that satisfy the steady flight force balance across the mission.
We use OpenMDAO’s Newton solver and assign a direct linear solver to solve each subiteration of the nonlinear solver.
Once the problem is setup, we set the necessary values to specify the mission profile.
BasicMission
has climb, cruise, and descent phases, but we still need to tell it the speed each is flown at, the cruise altitude, etc.
This mission requires a vertical speed and airspeed in each phase.
It also requires an initial cruise altitude and total mission length.
For more details, see the mission analysis documentation.
Run it!
Finally, we actually run the analysis.
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()
After running the model, we do a bit of postprocessing to visualize the results. The first thing we do is create an N2 diagram. This allows you to explore the structure of the model and the values of each variable. Lastly, we get some values from the model and create plot of some values, using matplotlib.
The model should converge in a few iterations. The plot it generates should look like this:
The N2 diagram for the model is the following:
Summary
In this tutorial, we developed a simple run script to explain how to set up an OpenConcept mission analysis. The aircraft model is very simplified and does not use any of OpenConcept’s models. Nonetheless, it obeys all the input/output requirements of an OpenConcept aircraft and thus can be used in the mission analysis.
You may notice that the results of this analysis are not particularly useful. It does not offer any information about fuel burn, energy usage, component temperatures, or sizing requirements. In the next tutorial, we’ll develop a more comprehensive aircraft model that is more useful for conceptual design.
The final script looks like this:
"""
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()
# 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
# rst Setup problem (end)
# rst Run (beg)
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()
# rst Run (end)
Using the Integrator
In this tutorial, we will build off the previous minimal example tutorial by adding a numerical integrator to compute fuel burn throughout the mission. This will require the following additions to the previous aircraft model:
A component to compute fuel flow rate using thrust and thrust-specific fuel consumption (TSFC)
An integrator to integrate the fuel flow rate w.r.t. time to compute the weight of fuel burned at each numerical integration point
A component to compute the weight at each integration point by subtracting the fuel burned from the takeoff weight
Other than these changes to the aircraft model the code will look very similar to the minimal example, so we will gloss over some details that are described more in the minimal example. If you have not done so already, it is recommended to go through that tutorial first.
Note
The script described in this tutorial is called minimal_integrator.py and can be found in the examples folder.
Imports
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
The notable addition to the imports is OpenConcept’s Integrator class. We also import the Aircraft class and setup_problem function from the previous tutorial.
Aircraft model
The aircraft model is no longer a set of explicit equations; it now requires a combination of OpenMDAO components to compute the weight.
For this reason, the aircraft model is now an OpenMDAO Group
instead of an ExplicitComponent
.
Let’s first take a look at the code for the entire aircraft model and then we will break down what is happening in each section.
class AircraftWithFuelBurn(om.Group):
"""
This model takes the simplified aircraft model from the minimal example, but adds
a fuel flow computation using TSFC and integrates it to compute fuel burn.
"""
# 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"]
# Add the aircraft model from the minimal example to build off of.
# Don't promote the weight from this model because we're going to compute a new
# one using the fuel burn.
# rst Simple aircraft (beg)
self.add_subsystem(
"simple_aircraft",
Aircraft(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"throttle",
"fltcond|q",
"ac|geom|wing|S_ref",
"ac|weights|TOW",
"ac|propulsion|max_thrust",
"ac|aero|L_over_D",
],
promotes_outputs=["thrust", "drag"],
)
# rst Simple aircraft (end)
# Use an OpenMDAO ExecComp to compute the fuel flow rate using the thrust and TSFC
# rst Fuel flow (beg)
self.add_subsystem(
"fuel_flow_calc",
om.ExecComp(
"fuel_flow = TSFC * thrust",
fuel_flow={"units": "kg/s", "shape": nn},
TSFC={"units": "kg/N/s", "shape": 1},
thrust={"units": "N", "shape": nn},
),
promotes_inputs=[("TSFC", "ac|propulsion|TSFC"), "thrust"],
)
# rst Fuel flow (end)
# Integrate the fuel flow rate to compute fuel burn
# rst Integrator (beg)
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")
# rst Integrator (end)
# Compute the current weight by subtracting the fuel burned from the takeoff weight
# rst Weight (beg)
self.add_subsystem(
"weight_calc",
om.ExecComp(
"weight = TOW - fuel_burned",
units="kg",
weight={"shape": nn},
TOW={"shape": 1},
fuel_burned={"shape": nn},
),
promotes_inputs=[("TOW", "ac|weights|TOW")],
promotes_outputs=["weight"],
)
self.connect("fuel_integrator.fuel_burned", "weight_calc.fuel_burned")
# rst Weight (end)
Options
The options are the same as the minimal example tutorial.
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
Setup
Note
The order you add components to OpenMDAO groups (using add_subsystem
) matters!
Generally, it is best to try to add components in the order that achieves as much feed-forward variable passing as possible.
For example, we have a component that computes thrust and another that takes thrust as an input.
To make this feed-forward, we add the component that takes thrust as an input after the component that computes it.
Thrust and drag from minimal aircraft
The first step in setting up the new aircraft model is to add the simplified aircraft to the group. We still use this model to compute thrust and drag, but the weight calculation will be modified. For this reason, we promote only the thrust and drag outputs to the new aircraft model group level. All the inputs are still required, so we promote them all to the group level (this way OpenConcept will automatically connect them as we discussed last time). If you are confused about the promotion, check out the OpenMDAO documentation.
self.add_subsystem(
"simple_aircraft",
Aircraft(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"throttle",
"fltcond|q",
"ac|geom|wing|S_ref",
"ac|weights|TOW",
"ac|propulsion|max_thrust",
"ac|aero|L_over_D",
],
promotes_outputs=["thrust", "drag"],
)
Fuel flow rate
Next, we need to compute the fuel flow rate to pass to the integrator.
Since this is a simple function of thrust and TSFC, we use an OpenMDAO ExecComp
(the OpenMDAO docs are very thorough if you are confused about the syntax).
We give it the fuel flow equation we want it to evaluate and define the units and shape of each parameter.
Notice that fuel flow and thrust are both vectors because they are evaluated at each numerical integration point and will change throughout each flight phase.
The TSFC is a scalar because it is a single constant parameter defined for the aircraft.
Finally, we promote the inputs.
Thrust is automatically connected to the thrust output from the minimal aircraft model.
TSFC is promoted to a name beginning with "ac|"
so that the mission analysis promotes the variable to the top level so we can set it the same way as the other aircraft parameters.
self.add_subsystem(
"fuel_flow_calc",
om.ExecComp(
"fuel_flow = TSFC * thrust",
fuel_flow={"units": "kg/s", "shape": nn},
TSFC={"units": "kg/N/s", "shape": 1},
thrust={"units": "N", "shape": nn},
),
promotes_inputs=[("TSFC", "ac|propulsion|TSFC"), "thrust"],
)
Integrator
Now we are ready to add the integration.
This is done by adding an OpenConcept Integrator
component to the model.
After adding the integrator, we add an integrated variable and associated variable to integrate using the integrator’s add_integrand
method.
Let’s step through all the details of these calls—there’s a lot to unpack.
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")
When Integrator
is initialized, there are a few important options that must be set.
As we’ve seen before, we set num_nodes
to tell it how many integration points to use.
diff_units
are the units of the differential.
For example, in our equation we are computing
The differential is \(dt\) and has units of time (we’ll use seconds here).
The time_setup
option sets what information the integrator uses to figure out the time at each integration point.
The options are "dt"
, "duration"
, or "bounds"
.
"dt"
creates an input called"dt"
that specifies the time spacing between each numerical integration point"duration"
creates an input called"duration"
that specifies the total time of the phase. The time between each integration point is computed by dividing the duration by the number of time steps (number of nodes minus one). This is the most common choice for the time setup and has the advantage that OpenConcept automatically connects the"duration"
input to the mission-level duration, so there is no manual time connection needed."bounds"
creates inputs called"t_initial"
and"t_final"
that specify the initial and final time of the phase. This internally computes duration and then time is computed the same was as for the duration approach.
The final option is the integration scheme.
The two options are "bdf3"
and "simpson"
.
"bdf3"
uses the third-order-accurate BDF3 integration scheme.
"simpson"
uses Simpson’s rule.
Simpson’s rule is the most common choice for use in OpenConcept.
In the next line we add information about the quantity we want to integrate.
We first define the name of the integrated quantity: "fuel_burned"
.
This will become a vector output of the integrator (accessed in this case as "fuel_integrator.fuel_burned"
).
We then define the rate we want integrated: "fuel_flow"
.
This will create a vector input to the integrator called "fuel_flow"
.
It also automatically adds an input called "fuel_flow_initial"
and an output called "fuel_flow_final"
.
Instead of appending "_initial"
or "_final"
, these names can be set manually using the start_name
and end_name
optional arguments.
The final value variable of each phase is automatically linked to the initial value variable of the following one.
The initial value in the first mission phase is zero by default but can be changed either using the start_val
optional argument or by setting the variable in a usual OpenMDAO way with an IndepVarComp
or set_input_defaults
.
We set the units of the fuel burn (the integrated quantity) to kilograms.
Other available options can be found in the Integrator
source docs.
The final step is to connect the fuel flow output from the fuel flow computation component to the integrator’s fuel flow input.
Mission
The rest of the code will look very similar to the minimal example.
class MissionAnalysisWithFuelBurn(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|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|*"],
)
The mission is identical except for two changes.
Firstly, we set the TSFC variable called "ac|propulsion|TSFC"
.
Secondly, the aircraft_model
passed to the mission analysis component is now AircraftWithFuelBurn
.
Run script
We reuse the setup_problem
function from the minimal example.
The remaining code is the same, except for adding a couple more variables of interest to the output plot.
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()
The model should converge in a few iterations. The plot it generates should look like this:
The N2 diagram for the model is the following:
You can see that the weight is no longer constant. This results in a varying throttle in the cruise phase, unlike the constant throttle from the minimal example. Also notice that the fuel flow and throttle have the exact same shape, which makes sense because they are directly related by a factor of TSFC.
Summary
In this tutorial, we extended the previous minimal aircraft example to use an integrator to compute fuel burn. Our aircraft model is now an OpenMDAO group with a few more components in it. We compute fuel flow using thrust output by the aircraft model and TSFC. The integrator integrates fuel flow to compute fuel burn. Finally, we compute the aircraft weight by subtracting the fuel burned from the takeoff weight.
The time input for the integrator is connected automatically and the final integrated value from one phase is connected to the initial value for the following one with some Ben Brelje magic.
You’re encouraged to figure out how this works for yourself by looking at the source code for the PhaseGroup
and TrajectoryGroup
(these are used by the flight phase and mission analysis groups).
The final script looks like this:
"""
This example builds off the original minimal example,
but adds a numerical integrator to integrate fuel burn
and update the weight accordingly.
"""
# rst Imports (beg)
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
# rst Imports (end)
# rst Aircraft (beg)
class AircraftWithFuelBurn(om.Group):
"""
This model takes the simplified aircraft model from the minimal example, but adds
a fuel flow computation using TSFC and integrates it to compute fuel burn.
"""
# 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"]
# Add the aircraft model from the minimal example to build off of.
# Don't promote the weight from this model because we're going to compute a new
# one using the fuel burn.
# rst Simple aircraft (beg)
self.add_subsystem(
"simple_aircraft",
Aircraft(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"throttle",
"fltcond|q",
"ac|geom|wing|S_ref",
"ac|weights|TOW",
"ac|propulsion|max_thrust",
"ac|aero|L_over_D",
],
promotes_outputs=["thrust", "drag"],
)
# rst Simple aircraft (end)
# Use an OpenMDAO ExecComp to compute the fuel flow rate using the thrust and TSFC
# rst Fuel flow (beg)
self.add_subsystem(
"fuel_flow_calc",
om.ExecComp(
"fuel_flow = TSFC * thrust",
fuel_flow={"units": "kg/s", "shape": nn},
TSFC={"units": "kg/N/s", "shape": 1},
thrust={"units": "N", "shape": nn},
),
promotes_inputs=[("TSFC", "ac|propulsion|TSFC"), "thrust"],
)
# rst Fuel flow (end)
# Integrate the fuel flow rate to compute fuel burn
# rst Integrator (beg)
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")
# rst Integrator (end)
# Compute the current weight by subtracting the fuel burned from the takeoff weight
# rst Weight (beg)
self.add_subsystem(
"weight_calc",
om.ExecComp(
"weight = TOW - fuel_burned",
units="kg",
weight={"shape": nn},
TOW={"shape": 1},
fuel_burned={"shape": nn},
),
promotes_inputs=[("TOW", "ac|weights|TOW")],
promotes_outputs=["weight"],
)
self.connect("fuel_integrator.fuel_burned", "weight_calc.fuel_burned")
# rst Weight (end)
# rst Mission (beg)
class MissionAnalysisWithFuelBurn(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|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|*"],
)
# rst Mission (end)
# rst Run (beg)
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()
# rst Run (end)
Turboprop Model and Mission Analysis
This tutorial builds on the previous tutorial by vastly improving the aircraft model. We’ll use components from OpenConcept to model the turboshaft engine, constant speed propeller, aerodynamics, and weight. We’ll also use a new mission profile that models takeoff by performing a balanced field length computation. The model here could be considered the first “useful” aircraft model since it more accurately models the relationship between throttle, thrust, and fuel flow and also the aerodynamics. This aircraft model is based on the Socata TBM 850 aircraft.
Note
The script described in this tutorial is called TBM850.py and can be found in the examples folder.
Imports
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
Compared to the previous openconcept/examples, this adds a handful of imports from OpenConcept.
We import the propulsion system, aerodynamic model, weight estimate, and a few math utilities.
We also import a new type of mission analysis we haven’t seen in previous tutorials: FullMissionAnalysis
.
This includes a balanced field length takeoff calculation.
Finally, we import acdata
from the TBM’s data file.
acdata
is a dictionary that organizes aircraft parameters (this is an alternative to what we’ve done so far of defining these values in the mission group).
Aircraft model
This aircraft model builds on the aircraft in the integrator tutorial by replacing the simple thrust and drag model we developed with much more detailed OpenConcept models.
The propulsion system uses OpenConcept’s TurbopropPropulsionSystem
, which couples a turboshaft engine to a constant speed propeller.
We also use OpenConcept’s PolarDrag
component to compute drag using a simple drag polar.
The final addition is an operating empty weight (OEW) computation.
The OEW output is not used in the weight calculation, but it is a useful output to know (perhaps for optimization) and shows off another OpenConcept component.
Let’s take a look at the aircraft model as a whole and then we’ll dive into each part.
class TBM850AirplaneModel(om.Group):
"""
A custom model specific to the TBM 850 airplane
This class will be passed in to the mission analysis code.
"""
# rst Options
def initialize(self):
self.options.declare("num_nodes", default=1)
self.options.declare("flight_phase", default=None)
# rst Setup
def setup(self):
nn = self.options["num_nodes"]
flight_phase = self.options["flight_phase"]
# ======================================== Propulsion ========================================
# rst Propulsion (beg)
# A propulsion system needs to be defined in order to provide thrust information
self.add_subsystem(
"propmodel",
TurbopropPropulsionSystem(num_nodes=nn),
promotes_inputs=[
"fltcond|rho",
"fltcond|Utrue",
"ac|propulsion|engine|rating",
"ac|propulsion|propeller|diameter",
"throttle",
],
promotes_outputs=["thrust"],
)
self.set_input_defaults("propmodel.prop1.rpm", val=np.full(nn, 2000.0), units="rpm")
# rst Propulsion (end)
# ======================================== Aerodynamics ========================================
# rst Aero (beg)
# Use a different drag coefficient for takeoff versus cruise
if flight_phase not in ["v0v1", "v1v0", "v1vr", "rotate"]:
cd0_source = "ac|aero|polar|CD0_cruise"
else:
cd0_source = "ac|aero|polar|CD0_TO"
self.add_subsystem(
"drag",
PolarDrag(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"ac|geom|wing|S_ref",
"ac|geom|wing|AR",
("CD0", cd0_source),
"fltcond|q",
("e", "ac|aero|polar|e"),
],
promotes_outputs=["drag"],
)
# rst Aero (end)
# ======================================== Weights ========================================
# rst Weight (beg)
# 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")
# rst Weight (end)
Options
The options are the same as the previous tutorials.
def initialize(self):
self.options.declare("num_nodes", default=1)
self.options.declare("flight_phase", default=None)
Setup
Now we’ll break down the components of the setup method for the aircraft model.
Propulsion
We use OpenConcept’s TurbopropPropulsionSystem
to estimate the thrust as a function of throttle.
It uses a turboshaft, which assumes constant TSFC, connected to a constant speed propeller, which uses a propeller map.
# A propulsion system needs to be defined in order to provide thrust information
self.add_subsystem(
"propmodel",
TurbopropPropulsionSystem(num_nodes=nn),
promotes_inputs=[
"fltcond|rho",
"fltcond|Utrue",
"ac|propulsion|engine|rating",
"ac|propulsion|propeller|diameter",
"throttle",
],
promotes_outputs=["thrust"],
)
self.set_input_defaults("propmodel.prop1.rpm", val=np.full(nn, 2000.0), units="rpm")
The propulsion system requires some flight conditions, an engine rating, a propeller diameter, and a throttle. We set the propeller speed to 2000 rpm. The propulsion system computes thrust, which is promoted, and fuel flow, which will be connected to the fuel burn integrator.
Aerodynamics
For the aerodynamics, we use a simple drag polar that computes drag using the equation
where \(q\) is dynamic pressure, \(S\) is wing reference area, \(C_{D0}\) is the zero-lift drag coefficient, \(C_L\) is the lift coefficient, \(e\) is the span efficiency, and \(AR\) is the aspect ratio.
# Use a different drag coefficient for takeoff versus cruise
if flight_phase not in ["v0v1", "v1v0", "v1vr", "rotate"]:
cd0_source = "ac|aero|polar|CD0_cruise"
else:
cd0_source = "ac|aero|polar|CD0_TO"
self.add_subsystem(
"drag",
PolarDrag(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"ac|geom|wing|S_ref",
"ac|geom|wing|AR",
("CD0", cd0_source),
"fltcond|q",
("e", "ac|aero|polar|e"),
],
promotes_outputs=["drag"],
)
We use a different zero-lift drag coefficient for the takeoff phases than for the climb, cruise, and descent phases because we assume the aircraft is not in its clean configuration on takeoff (e.g., flaps extended). This uses the name of the flight phase option to figure out which phase the model is in.
We then add the PolarDrag
OpenConcept component and promote the necessary variables.
Some of the inputs are renamed using OpenMDAO’s parenthesis format, which allows the selection on the fly of which \(C_{D0}\) value to connect.
Weights
Finally, we add the weight models.
# 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")
We begin by adding an operating empty weight estimate, using OpenConcept’s SingleTurboPropEmptyWeight
component.
Including this component is not necessary because the computed OEW value is not used
in the thrust, weight, or drag estimate returned to the OpenConcept mission.
However, OEW is a useful value to know, so we compute it anyway.
It can be a little trick to figure out what is actually getting passed to the OEW component’s inputs because there are so many and each one is not promoted individually (instead it uses the "*"
glob pattern that promotes all of them).
You’re encouraged to look at the N2 diagram to see what is being connected here.
The OEW component does not compute its own turboshaft and propeller weight because those are computed in the TurbopropPropulsionSystem
.
The next two components added should look pretty familiar.
We add an integrator to integrate the fuel flow computed in the TurbopropPropulsionSystem
and then subtract it from the takeoff weight to compute the current weight.
One change from the integrator tutorial is instead of using an OpenMDAO ExecComp
to do the final arithmetic, we use OpenConcept’s AddSubtractComp
.
This provides an easy way to combine variables by adding and subtracting and includes analytical derivatives.
Mission
This mission group has two changes from previous openconcept/examples.
The aircraft parameters are added from the data file using a DictIndepVarComp
instead of manually defining them as before with an IndepVarComp
.
This allows the parameters to be defined in one location (that is not the run script).
The second change is the switch to using a FullMissionAnalysis
mission, which adds a balanced field length calculation to the BasicMission
we used previously.
The DictIndepVarComp
takes in a nested Python dictionary, which in this case is imported from the TBM’s aircraft data file.
Values from the dictionary can be added as outputs by calling add_output_from_dict
and passing in a string with the keys for the nested dictionary for the variable you want separated by a pipe.
class TBMAnalysisGroup(om.Group):
"""
This is an example of a balanced field takeoff and three-phase mission analysis.
"""
def initialize(self):
self.options.declare("num_nodes", default=11)
def setup(self):
nn = self.options["num_nodes"]
# Define a bunch of design varaiables and airplane-specific parameters
dv_comp = self.add_subsystem("dv_comp", DictIndepVarComp(acdata), promotes_outputs=["*"])
# Aerodynamic parameters
dv_comp.add_output_from_dict("ac|aero|CLmax_TO")
dv_comp.add_output_from_dict("ac|aero|polar|e")
dv_comp.add_output_from_dict("ac|aero|polar|CD0_TO")
dv_comp.add_output_from_dict("ac|aero|polar|CD0_cruise")
# Geometric parameters
dv_comp.add_output_from_dict("ac|geom|wing|S_ref")
dv_comp.add_output_from_dict("ac|geom|wing|AR")
dv_comp.add_output_from_dict("ac|geom|wing|c4sweep")
dv_comp.add_output_from_dict("ac|geom|wing|taper")
dv_comp.add_output_from_dict("ac|geom|wing|toverc")
dv_comp.add_output_from_dict("ac|geom|hstab|S_ref")
dv_comp.add_output_from_dict("ac|geom|hstab|c4_to_wing_c4")
dv_comp.add_output_from_dict("ac|geom|vstab|S_ref")
dv_comp.add_output_from_dict("ac|geom|fuselage|S_wet")
dv_comp.add_output_from_dict("ac|geom|fuselage|width")
dv_comp.add_output_from_dict("ac|geom|fuselage|length")
dv_comp.add_output_from_dict("ac|geom|fuselage|height")
dv_comp.add_output_from_dict("ac|geom|nosegear|length")
dv_comp.add_output_from_dict("ac|geom|maingear|length")
# Weight parameters
dv_comp.add_output_from_dict("ac|weights|MTOW")
dv_comp.add_output_from_dict("ac|weights|W_fuel_max")
dv_comp.add_output_from_dict("ac|weights|MLW")
# Propulsion parameters
dv_comp.add_output_from_dict("ac|propulsion|engine|rating")
dv_comp.add_output_from_dict("ac|propulsion|propeller|diameter")
# Other parameters
dv_comp.add_output_from_dict("ac|num_passengers_max")
dv_comp.add_output_from_dict("ac|q_cruise")
# Run a full mission analysis including takeoff, climb, cruise, and descent
self.add_subsystem(
"analysis",
FullMissionAnalysis(num_nodes=nn, aircraft_model=TBM850AirplaneModel),
promotes_inputs=["*"],
promotes_outputs=["*"],
)
Run script
Setup problem
We start by writing a function to set up the problem, assign solvers, and define the mission profile, just as we did in the minimal example. The new addition here is the setup of the takeoff segment. We set initial guesses for the takeoff speeds to initialize the solver with reasonable guesses. This improves the convergence behavior. We also set the structural fudge value, a multiplier on the structural weights, to account for additional weights not modeled by the empty weight component. Finally, we decrease the throttle values for the takeoff segments from the default of 1 to 0.826.
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
Run it!
Now we get to actually run the problem. After running it, we print some values from the solved problem. The plotting section from previous tutorials is used twice to add a plot showing the takeoff portion of the mission.
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
# Run the analysis
prob = run_tbm_analysis()
prob.run_model()
# Generate N2 diagram
om.n2(prob, outfile="turboprop_n2.html", show_browser=not hide_viz)
# =============== Print some useful outputs ================
print_vars = [
{"var": "ac|weights|MTOW", "name": "MTOW", "units": "lb"},
{"var": "climb.OEW", "name": "OEW", "units": "lb"},
{"var": "rotate.fuel_used_final", "name": "Rotate fuel", "units": "lb"},
{"var": "climb.fuel_used_final", "name": "Climb fuel", "units": "lb"},
{"var": "cruise.fuel_used_final", "name": "Cruise fuel", "units": "lb"},
{"var": "descent.fuel_used_final", "name": "Fuel used", "units": "lb"},
{"var": "rotate.range_final", "name": "TOFL (over 35ft obstacle)", "units": "ft"},
{"var": "engineoutclimb.gamma", "name": "Climb angle at V2", "units": "deg"},
]
print("\n=======================================================================\n")
for var in print_vars:
print(f"{var['name']}: {prob.get_val(var['var'], units=var['units']).item()} {var['units']}")
# =============== Takeoff plot ================
import matplotlib.pyplot as plt
takeoff_fig, takeoff_axs = plt.subplots(1, 3, figsize=[9, 2.7], constrained_layout=True)
takeoff_axs = takeoff_axs.flatten() # change 1x3 mtx of axes into 4-element vector
# Define variables to plot
takeoff_vars = [
{"var": "fltcond|h", "name": "Altitude", "units": "ft"},
{"var": "fltcond|Utrue", "name": "True airspeed", "units": "kn"},
{"var": "throttle", "name": "Throttle", "units": None},
]
for idx_fig, var in enumerate(takeoff_vars):
takeoff_axs[idx_fig].set_xlabel("Range (ft)")
takeoff_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
colors = ["tab:blue", "tab:orange", "tab:green", "tab:red"]
for i, phase in enumerate(["v0v1", "v1vr", "rotate", "v1v0"]):
takeoff_axs[idx_fig].plot(
prob.get_val(f"{phase}.range", units="ft"),
prob.get_val(f"{phase}.{var['var']}", units=var["units"]),
"-o",
c=colors[i],
markersize=2.0,
)
takeoff_fig.legend(
[r"V0 $\rightarrow$ V1", r"V1 $\rightarrow$ Vr", "Rotate", r"V1 $\rightarrow$ V0"],
loc=(0.067, 0.6),
fontsize="small",
)
takeoff_fig.suptitle("Takeoff phases")
takeoff_fig.savefig("turboprop_takeoff_results.svg", transparent=True)
# =============== Mission plot ================
mission_fig, mission_axs = plt.subplots(2, 3, figsize=[9, 4.8], constrained_layout=True)
mission_axs = mission_axs.flatten() # change 2x2 mtx of axes into 4-element vector
# Define variables to plot
mission_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": "propmodel.fuel_flow", "name": "Fuel flow", "units": "g/s"},
{"var": "weight", "name": "Weight", "units": "kg"},
]
for idx_fig, var in enumerate(mission_vars):
mission_axs[idx_fig].set_xlabel("Range (nmi)")
mission_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"]:
mission_axs[idx_fig].plot(
prob.get_val(f"{phase}.range", units="nmi"),
prob.get_val(f"{phase}.{var['var']}", units=var["units"]),
"-o",
c="tab:blue",
markersize=2.0,
)
mission_fig.suptitle("Mission")
mission_fig.savefig("turboprop_mission_results.svg", transparent=True)
if not hide_viz:
plt.show()
The plot from the takeoff phases looks like this:
The balanced field length consists of four phases. The first models accelerating from a standstill to the decision speed, V1. If an engine fails (in an airplane with at least two engines) before V1, the pilot brakes to a stop. The second phase in the legend models this braking. If it fails after V1, the takeoff continues to the rotation speed and takes off with the remaining engine(s). This is modeled by the third and fourth phases in the legend.
The mission looks like this:
Compared to the previous tutorial, this model more accurately models the fuel flow and thrust force. It also incorporates a better drag model.
The N2 diagram for the model is the following:
Summary
In this tutorial, we created a more detailed aircraft using OpenConcept’s models for propulsion, aerodynamics, and weights. We also incorporated a mission profile that includes a balanced field length takeoff.
Hopefully, the tutorials this far have given you a baseline knowledge that is sufficient to have a general idea of what is going on in each part of OpenConcept. From here we recommend diving into different parts of the OpenConcept source code to gain an idea of how to build more complex models. This page recommends some models to dig through next to learn more about OpenConcept.
The final script looks like this:
# rst Imports (beg)
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
# rst Imports (end)
# rst Aircraft (beg)
class TBM850AirplaneModel(om.Group):
"""
A custom model specific to the TBM 850 airplane
This class will be passed in to the mission analysis code.
"""
# rst Options
def initialize(self):
self.options.declare("num_nodes", default=1)
self.options.declare("flight_phase", default=None)
# rst Setup
def setup(self):
nn = self.options["num_nodes"]
flight_phase = self.options["flight_phase"]
# ======================================== Propulsion ========================================
# rst Propulsion (beg)
# A propulsion system needs to be defined in order to provide thrust information
self.add_subsystem(
"propmodel",
TurbopropPropulsionSystem(num_nodes=nn),
promotes_inputs=[
"fltcond|rho",
"fltcond|Utrue",
"ac|propulsion|engine|rating",
"ac|propulsion|propeller|diameter",
"throttle",
],
promotes_outputs=["thrust"],
)
self.set_input_defaults("propmodel.prop1.rpm", val=np.full(nn, 2000.0), units="rpm")
# rst Propulsion (end)
# ======================================== Aerodynamics ========================================
# rst Aero (beg)
# Use a different drag coefficient for takeoff versus cruise
if flight_phase not in ["v0v1", "v1v0", "v1vr", "rotate"]:
cd0_source = "ac|aero|polar|CD0_cruise"
else:
cd0_source = "ac|aero|polar|CD0_TO"
self.add_subsystem(
"drag",
PolarDrag(num_nodes=nn),
promotes_inputs=[
"fltcond|CL",
"ac|geom|wing|S_ref",
"ac|geom|wing|AR",
("CD0", cd0_source),
"fltcond|q",
("e", "ac|aero|polar|e"),
],
promotes_outputs=["drag"],
)
# rst Aero (end)
# ======================================== Weights ========================================
# rst Weight (beg)
# 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")
# rst Weight (end)
# rst Mission (beg)
class TBMAnalysisGroup(om.Group):
"""
This is an example of a balanced field takeoff and three-phase mission analysis.
"""
def initialize(self):
self.options.declare("num_nodes", default=11)
def setup(self):
nn = self.options["num_nodes"]
# Define a bunch of design varaiables and airplane-specific parameters
dv_comp = self.add_subsystem("dv_comp", DictIndepVarComp(acdata), promotes_outputs=["*"])
# Aerodynamic parameters
dv_comp.add_output_from_dict("ac|aero|CLmax_TO")
dv_comp.add_output_from_dict("ac|aero|polar|e")
dv_comp.add_output_from_dict("ac|aero|polar|CD0_TO")
dv_comp.add_output_from_dict("ac|aero|polar|CD0_cruise")
# Geometric parameters
dv_comp.add_output_from_dict("ac|geom|wing|S_ref")
dv_comp.add_output_from_dict("ac|geom|wing|AR")
dv_comp.add_output_from_dict("ac|geom|wing|c4sweep")
dv_comp.add_output_from_dict("ac|geom|wing|taper")
dv_comp.add_output_from_dict("ac|geom|wing|toverc")
dv_comp.add_output_from_dict("ac|geom|hstab|S_ref")
dv_comp.add_output_from_dict("ac|geom|hstab|c4_to_wing_c4")
dv_comp.add_output_from_dict("ac|geom|vstab|S_ref")
dv_comp.add_output_from_dict("ac|geom|fuselage|S_wet")
dv_comp.add_output_from_dict("ac|geom|fuselage|width")
dv_comp.add_output_from_dict("ac|geom|fuselage|length")
dv_comp.add_output_from_dict("ac|geom|fuselage|height")
dv_comp.add_output_from_dict("ac|geom|nosegear|length")
dv_comp.add_output_from_dict("ac|geom|maingear|length")
# Weight parameters
dv_comp.add_output_from_dict("ac|weights|MTOW")
dv_comp.add_output_from_dict("ac|weights|W_fuel_max")
dv_comp.add_output_from_dict("ac|weights|MLW")
# Propulsion parameters
dv_comp.add_output_from_dict("ac|propulsion|engine|rating")
dv_comp.add_output_from_dict("ac|propulsion|propeller|diameter")
# Other parameters
dv_comp.add_output_from_dict("ac|num_passengers_max")
dv_comp.add_output_from_dict("ac|q_cruise")
# Run a full mission analysis including takeoff, climb, cruise, and descent
self.add_subsystem(
"analysis",
FullMissionAnalysis(num_nodes=nn, aircraft_model=TBM850AirplaneModel),
promotes_inputs=["*"],
promotes_outputs=["*"],
)
# rst Mission (end)
# rst Setup problem (beg)
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
# rst Setup problem (end)
# rst Run (beg)
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
# Run the analysis
prob = run_tbm_analysis()
prob.run_model()
# Generate N2 diagram
om.n2(prob, outfile="turboprop_n2.html", show_browser=not hide_viz)
# =============== Print some useful outputs ================
print_vars = [
{"var": "ac|weights|MTOW", "name": "MTOW", "units": "lb"},
{"var": "climb.OEW", "name": "OEW", "units": "lb"},
{"var": "rotate.fuel_used_final", "name": "Rotate fuel", "units": "lb"},
{"var": "climb.fuel_used_final", "name": "Climb fuel", "units": "lb"},
{"var": "cruise.fuel_used_final", "name": "Cruise fuel", "units": "lb"},
{"var": "descent.fuel_used_final", "name": "Fuel used", "units": "lb"},
{"var": "rotate.range_final", "name": "TOFL (over 35ft obstacle)", "units": "ft"},
{"var": "engineoutclimb.gamma", "name": "Climb angle at V2", "units": "deg"},
]
print("\n=======================================================================\n")
for var in print_vars:
print(f"{var['name']}: {prob.get_val(var['var'], units=var['units']).item()} {var['units']}")
# =============== Takeoff plot ================
import matplotlib.pyplot as plt
takeoff_fig, takeoff_axs = plt.subplots(1, 3, figsize=[9, 2.7], constrained_layout=True)
takeoff_axs = takeoff_axs.flatten() # change 1x3 mtx of axes into 4-element vector
# Define variables to plot
takeoff_vars = [
{"var": "fltcond|h", "name": "Altitude", "units": "ft"},
{"var": "fltcond|Utrue", "name": "True airspeed", "units": "kn"},
{"var": "throttle", "name": "Throttle", "units": None},
]
for idx_fig, var in enumerate(takeoff_vars):
takeoff_axs[idx_fig].set_xlabel("Range (ft)")
takeoff_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
colors = ["tab:blue", "tab:orange", "tab:green", "tab:red"]
for i, phase in enumerate(["v0v1", "v1vr", "rotate", "v1v0"]):
takeoff_axs[idx_fig].plot(
prob.get_val(f"{phase}.range", units="ft"),
prob.get_val(f"{phase}.{var['var']}", units=var["units"]),
"-o",
c=colors[i],
markersize=2.0,
)
takeoff_fig.legend(
[r"V0 $\rightarrow$ V1", r"V1 $\rightarrow$ Vr", "Rotate", r"V1 $\rightarrow$ V0"],
loc=(0.067, 0.6),
fontsize="small",
)
takeoff_fig.suptitle("Takeoff phases")
takeoff_fig.savefig("turboprop_takeoff_results.svg", transparent=True)
# =============== Mission plot ================
mission_fig, mission_axs = plt.subplots(2, 3, figsize=[9, 4.8], constrained_layout=True)
mission_axs = mission_axs.flatten() # change 2x2 mtx of axes into 4-element vector
# Define variables to plot
mission_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": "propmodel.fuel_flow", "name": "Fuel flow", "units": "g/s"},
{"var": "weight", "name": "Weight", "units": "kg"},
]
for idx_fig, var in enumerate(mission_vars):
mission_axs[idx_fig].set_xlabel("Range (nmi)")
mission_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"]:
mission_axs[idx_fig].plot(
prob.get_val(f"{phase}.range", units="nmi"),
prob.get_val(f"{phase}.{var['var']}", units=var["units"]),
"-o",
c="tab:blue",
markersize=2.0,
)
mission_fig.suptitle("Mission")
mission_fig.savefig("turboprop_mission_results.svg", transparent=True)
if not hide_viz:
plt.show()
# rst Run (end)
More Examples
While we work on more extensive tutorials, you can learn more about OpenConcept by diving into the source code and playing around with OpenConcept’s models. This page suggests logical next places to look to gain an understanding of what is possible.
Propulsion modeling
The turboprop example uses OpenConcept’s TurbopropPropulsionSystem
.
If you have not already looked at the underlying code for that model, that’s a good place to start.
The component’s source code is in openconcept/propulsion/systems/simple_turboprop.py
and can be found on GitHub here.
After that, we recommend looking at the TwinTurbopropPropulsionSystem
in the same file to understand how to build models with more than one propulsor that properly handle the failed engine case on takeoff.
This propulsion system is used in the KingAirC90GT.py
example.
For an introduction to hybrid electric propulsion systems, have a look at the TwinSeriesHybridElectricPropulsionSystem
.
This model is in openconcept/propulsion/systems/simple_series_hybrid.py
or on GitHub here.
The HybridTwin.py
example aircraft shows how to use this propulsion system and sets up an optimization problem for it.
Finally, the B738.py
example shows the use of the CFM56 surrogate model from pyCycle.
There are similar propulsion models for the N+3 geared turbofan and a parallel hybrid version of the same N+3 engine.
Thermal management modeling
One of OpenConcept’s key contributions is the ability to model an aircraft thermal management system and the associated unsteady heat flows.
AllElectricSinglePropulsionSystemWithThermal_Incompressible
models an all-electric propulsion architecture with a thermal management system.
The code is located in openconcept/propulsion/systems/simple_all_electric.py
and on GitHub here.
The ElectricSinglewithThermal.py
example models the TBM aircraft from the turboprop example, but retrofit with this all-electric propulsion system.
The TwinSeriesHybridElectricThermalPropulsionSystem
model adds a thermal management system to the twin-engine series hybrid propulsion system.
That model can be found in openconcept/propulsion/systems/thermal_series_hybrid.py
or on GitHub here.
The HybridTwin_thermal.py
example shows how to use this propulsion system.
The most detailed thermal management system model is in the N3_HybridSingleAisle_Refrig.py
example aircraft.
It models a parallel hybrid single aisle commercial transport aircraft.
The model includes an electric motor with a cooling jacket, a battery with a bandolier cooling system, a refrigerator, compressible ducts, pumps, and even coolant hose models.
Other useful places to look
The B738_VLM_drag.py
and B738_aerostructural.py
examples show how to use the OpenAeroStruct VLM and aerostructural models.
Aerodynamics
OpenConcept’s aerodynamics components provide an estimate of drag as a function of lift coefficient, flight conditions, and other parameters.
Simple drag polar: PolarDrag
PolarDrag
is the most basic aerodynamic model and uses a parabolic drag polar.
This component computes the drag force given the flight condition (dynamic pressure and lift coefficient) at each node along the mission profile.
In run script, users should set the values for the following aircraft design parameters, or declare them as design variables.
Variable name |
Property |
---|---|
ac|geom|wing|S_ref |
Reference wing area |
ac|geom|wing|AR |
Wing aspect ratio |
e |
Wing Oswald efficiency |
CD0 |
Zero-lift drag coefficient |
Using OpenAeroStruct
Instead of the simple drag polar model, you can use OpenAeroStruct to compute the drag. This allows you to parametrize the aircraft wing design in more details and explore the effects of wing geometry, including taper, sweep, and twist. OpenAeroStruct implements the vortex-lattice method (VLM) for aerodynamics and beam-based finite element method (FEM) for structures (in the case of the aerostructural drag polar). For more detail, please check the documentation.
The wing is currently limited to simple planform geometries. Additionally, the wing does not include a tail to trim it because there is no OpenConcept weight position model with which to trim.
OpenConcept uses a surrogate model trained by OpenAeroStruct analyses to reduce the computational cost. The data generation and surrogate training is automated; specifying the training grid manually may improve accuracy or decrease computational cost.
VLM-based aerodynamic model: VLMDragPolar
This model uses the vortex-lattice method (VLM) to compute the drag. The inputs to this model are the flight conditions (Mach number, altitude, dynamic pressure, lift coefficient) and aircraft design parameters.
Users should set the following design parameters and options in the run script.
Variable name |
Property |
Type |
---|---|---|
ac|geom|wing|S_ref |
Reference wing area |
float |
ac|geom|wing|AR |
Wing aspect ratio |
float |
ac|geom|wing|taper |
Taper ratio |
float |
ac|geom|wing|c4sweep |
Sweep angle at quarter chord |
float |
ac|geom|wing|twist |
Spanwise distribution of twist, from wint tip to root. |
1D ndarray, lendth |
ac|aero|CD_nonwing |
Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc. |
float |
fltcond|TempIncrement |
Temperature increment for non-standard day |
float |
Variable name |
Property |
Type |
---|---|---|
|
VLM mesh size (number of vertices) in chordwise direction. |
int |
|
VLM mesh size (number of vertices) in spanwise direction. |
int |
|
Number of spanwise control points for twist distribution. |
int |
There are other advanced options, e.g., the surrogate training points in Mach-alpha-altitude space. The default settings should work fine for these advanced options, but if you want to make changes, please refer to the source docs.
Aerostructural model: AeroStructDragPolar
This model is similar to the VLM-based aerodynamic model, but it performs aerostructural analysis (that couples VLM and structural FEM) instead of aerodynamic analysis (just FEM). This means that we now consider the wing deformation due to aerodynamic loads, which is important for high aspect ratio wings. The structural model does not include point loads (e.g., for the engine) or distributed fuel loads.
The additional input variables users need to set in the run script are listed below.
Like the num_twist
option, you may need to set num_toverc
, num_skin
, and num_spar
.
Variable name |
Property |
Type |
---|---|---|
ac|geom|wing|toverc |
Spanwise distribution of thickness to chord ratio. |
1D ndarray, lendth |
ac|geom|wing|skin_thickness |
Spanwise distribution of skin thickness. |
1D ndarray, lendth |
ac|geom|wing|spar_thickness |
Spanwise distribution of spar thickness. |
1D ndarray, lendth |
In addition to the drag, the aerostructural model also outputs the structural failure indicator (failure
) and wing weight (ac|weights|W_wing
).
The failure variable must be negative (failure <= 0
) to constrain wingbox stresses to be less than the yield stress.
Understanding the surrogate modeling
OpenConcept uses surrogate models based on OpenAeroStruct analyses to reduce the computational cost for mission analysis.
The surrogate models are trained in the 3D input space of Mach number, angle of attack, and altitude.
The outputs of the surrogate models are CL and CD (and failure for AeroStructDragPolar
).
For more details about the surrogate models, see our paper.
Other models
The aerodynamics module also includes a couple components that may be useful to be aware of:
StallSpeed
, which uses \(C_{L, \text{max}}\), aircraft weight, and wing area to compute the stall speed
Lift
, which computes lift force using lift coefficient, wing area, and dynamic pressure
Atmospherics
Most of OpenConcept’s atmospheric models use the 1976 Standard Atmosphere.
For more details, see the source documentation for the ComputeAtmosphericProperties
component.
Models for specific atmospheric properties are also available on their own:
TrueAirspeedComp
EquivalentAirpseedComp
TemperatureComp
SpeedOfSoundComp
PressureComp
MachNumberComp
DynamicPressureComp
DensityComp
The code is adapted from this paper, with permission:
@conference{Jasa2018b,
Address = {Orlando, FL},
Author = {John P. Jasa and John T. Hwang and Joaquim R. R. A. Martins},
Booktitle = {2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum},
Month = {January},
Title = {Design and Trajectory Optimization of a Morphing Wing Aircraft},
Year = {2018}
}
Costs
OpenConcept’s existing cost model is rather rudimentary and computes operating costs for a turboprop.
The component is called TurbopropOperatingCost
.
Unfortunately, there are not example aircraft that use this model, but the source code is relatively readable to figure out what inputs and outputs are needed.
Other cost models may be added in the future.
Energy Storage
This module contains components that can store energy. For now this consists only of battery models, but hydrogen tanks would go here too, for example.
Battery models
SimpleBattery
This component simple uses a simple equation to relate the electrical power draw to the heat generated: \(\text{heat} = \text{electricity load} (1 - \eta)\). Cost is assumed to be a linear function of weight. Component sizing margin is computed which describes the electrical load to the max power of the battery (defined by battery weight and specific power). This is not automatically forced to be less than one, so the user is responsible for checking/enforcing this in an analysis or optimization.
Warning
This component does not track its state of charge, so without an additional integrator there is no way to know when the battery has been depleted. For this reason, it is recommended to use the SOCBattery
.
SOCBattery
This component uses the same model as the SimpleBattery
, but adds an integrator to compute the state of charge (from 0.0 to 1.0).
By default, it starts at a state of charge of 1.0 (100% charge).
Mission
The mission analysis computes the fuel and/or battery energy consumption for a specified flight mission. You can also keep track of the temperature of components if you have thermal models.
In OpenConcept, a mission consists of multiple phases: phases are the building blocks of the mission. For example, a basic three-phase mission is composed of a climb phase, cruise phase, and descent phase.
Mission profiles
OpenConcept implements several mission profiles that users can use for an analysis.
The missions are implemented in openconcept/mission/profiles.py
.
You can also make your own mission profile following the format from these examples.
Basic three-phase mission: BasicMission
This is a basic climb-cruise-descent mission for a fixed-wing aircraft.
For this mission, users should specify the following variables in the run script:
takeoff altitude
takeoff|h0
, default is 0 ft.cruise altitude
cruise|h0
.mission range
mission_range
.payload weight
payload
.vertical speed
<climb, cruise, descent>.fltcond|vs
for each phase.airspeed
<climb, cruise, descent>.fltcond|Ueas
for each phase.(optional)
takeoff|v2
if you include a ground roll phase before climb. The ground roll phase is not included by default.
The duration of each phase is automatically set given the cruise altitude and mission range.
Full mission including takeoff: FullMissionAnalysis
This adds a balanced-field takeoff analysis to the three-phase mission. The additional takeoff phases are:
v0v1
: from a standstill to decision speed (v1). An instance ofGroundRollPhase
.v1vr
: from v1 to rotation. An instance ofGroundRollPhase
.rotate
: rotation in the air before steady climb. An instance ofRotationPhase
orRobustRotationPhase
.v1vr
: energency stopping from v1 to a stop. An instance ofGroundRollPhase
.
We use BLIImplicitSolve
to solve for the decision speed v1
where the one-engine-out takeoff distance is equal to the braking distance for rejected takeoff.
The optional variables you may set in the run scripts are
throttle for takeoff phases
<v0v1, v1vr, rotate>.throttle
, default is 1.0.ground rolling friction coefficient
<v0v1, v1vr, v1v0>.braking
, default is 0.03 for accelerating phases and 0.4 for braking.altitude
<v0v1, v1vr, rotate, v1v0>.fltcond|h
.obstacle clearance height
rotate.h_obs
, default is 35 ft.CL/CLmax ration in rotation
rotate.CL_rotate_mult
, default is 0.83.
It may be necessary to set initial values for the takeoff airspeeds (<v0v1, v1vr, v1v0>.fltcond|Utrue
) before the solver is called to improve convergence.
Mission with reserve: MissionWithReserve
This adds a reserve mission and loiter phase to the three-phase mission. Additional variables you need to set in the run script are
vertical speed and airspeed for additional phases:
<reserve_climb, reserve_cruise, reserve_descent, loiter>.<fltcond|Ueas, fltcond|vs>
reserve range
reserve_range
and altitudereserve|h0
.loiter duration
loiter_duration
and loiter altitudeloiter|h0
.
Phase types
A phase is a building block of a mission profile.
The phases and relevant classes are implemented in openconcept/mission/phases.py
.
Users usually don’t need to modify these code when creating their own mission profile.
Steady flight: SteadyFlightPhase
The SteadyFlightPhase
class can be instantiated for steady climb, cruise, descent, and loiter phases.
For this phase, you need to specify the airspeed (<phase_name>.fltcond|Ueas
) and vertical speed (<phase_name>.fltcond|Ueas
) in your run script.
You may optionally set the duration of the phase (<phase_name>.duration
), or alternatively, the duration can be set automatically in the mission profile group.
To ensure steady flight, both vertical and horizontal accelerations will be set to 0.
It first computes the lift coefficient required for zero vertical accelration; CL is then passed to the aircraft model, which returns the lift and drag.
Then, it solves for the throttle values such that horizontal acceleration is zero.
This is done by solving a system of nonlinear equations (horizontal acceleration = 0
) w.r.t. throttle using a BalanceComp for each phase.
Balanced-field takeoff
Balanced-field takeoff analysis is implemented in the following classes: BFLImplicitSolve
, GroundRollPhase
, RotationPhase
, RobustRotationPhase
, ClimbAnglePhase
.
Unlike the steady flight phases, the takeoff phases are not steady and acceleration is non-zero.
Therefore, the engine throttle needs to be specified to compute the acceleration (100% by defalut for accelerating phases and 0 for braking).
Users can also set the throttle manually in the run script.
The acceleration is then integrated to compute the velocity.
Mission groups
OpenConcept provides some groups that make mission analysis and phase definition easier.
PhaseGroup
This is the base class for an OpenConcept mission phase.
It automatically identifies Integrator
instances within the model and links the time duration variable to them.
It also collects the names of all the integrand states so that the TrajectoryGroup
can find them to link across phases.
IntegratorGroup
The IntegratorGroup
is an alternative way of setting up and integrator (the Integrator
component is used more frequently).
This group adds an ODE integration component (called "ode_integ"
), locates output variables tagged with the “integrate” tag, and automatically connects the tagged rate source to the integrator.
TrajectoryGroup
This is the base class for a mission profile.
It provides the link_phases
method which is used to connect integration variables across mission phases.
Propulsion
Propulsion systems
User can build their own propulsion systems following the format of the example systems listed here. For details of each propulsion systems, see the source docs.
All-electric propulsion
This is an electric propulsion system consisting of a constant-speed propeller, motor, and battery.
In addition, this models a thermal management system using a compressible (AllElectricSinglePropulsionSystemWithThermal_Compressible
) or incompressible (AllElectricSinglePropulsionSystemWithThermal_Incompressible
) 1D duct with heat exchanger.
This model takes the motor throttle as a control input and computes the thrust and battery state of charge (SOC).
Turboprop
This is a simple turboprop system consisting of constant-speed propeller(s) and turboshaft(s).
TurbopropPropulsionSystem
implements a system of one propeller and one turboshaft.
Also, TwinTurbopropPropulsionSystem
implements a twin system that has two propellers and turboshafts.
Users can create their own turboprop model by changing the parameters, e.g. engine rating and propeller diameter.
This model takes the engine throttle as a control input and computes the thrust and fuel flow.
Series-hybrid electric propulsion
In series-hybrid propulsion systems, motor(s) draws electrical load from both battery and turboshaft generator. The control inputs are the motor throttle, turboshaft throttle, and the power split fraction between the battery and generator; The turboshaft throttle must be driven by an implicit solver or optimizer to equate the generator’s electric power output and the engine power required by the splitter. Given these inputs, the model the computes the thrust, fuel flow, and electric load.
OpenConcept implements both single and twin series-hybrid electric propulsion systems in simple_series_hybrid.py
.
TwinSeriesHybridElectricPropulsionSystem
is the recommended one to use.
Others require the user to explicitly set up additional components to generate feasible analysis results (see the comments in the code).
The systems with thermal management components are also implemented in thermal_series_hybrid.py
.
Turbofan
OpenConcept implements two turbofan engine models, CFM56 and a geared turbofan (GTF) for NASA’s N+3 aircraft. Both are surrogate models derived from pyCycle, a thermodynamic cycle modeling tool built on the OpenMDAO framework. The inputs to the turbofan models are the engine throttle and flight conditions (Mach number and altitude), and outputs are the thrust and fuel flow. In addition, the CFM56 model outputs the turbine inlet temperature, and the N+3 model outputs the surge margin.
We also implement a N+3 engine with hybridization, which requires hybrid shaft power as an additional input.
Models
The propulsion systems are made up of individual propulsion components. Available individual models are listed here.
Electric motor: SimpleMotor
An electric motor model that computes shaft power by multiplying throttle by the motor’s electrical power rating and efficiency.
The electrical power that does not go toward shaft power is modeled as heat (see the LiquidCooledMotor
to add thermal management to this motor model).
Weight and cost are linear functions of the electric power rating.
Turboshaft: SimpleTurboshaft
Computes shaft power by multiplying throttle by the engine’s rated shaft power. The fuel flow is computed by multiplying the generated shaft power by a provided power-specific fuel consumption. As with the electric motor, cost and weight are modeled as linear functions of the power rating.
Propeller: SimplePropeller
This model uses an empirical propeller efficiency map for a constant speed turboprop propeller under the hood. For low speed, it uses a static thrust coefficient map from Raymer[1]. Propeller maps for three and four bladed propellers are included.
Generator: SimpleGenerator
This model uses essentially the same model as SimpleMotor
but in reverse.
It takes in a shaft power and computes electrical power and heat generated.
Power splitter: PowerSplit
This component enables electrical or mechanical shaft power to be split to two components. It uses either a fractional or fixed split method where fractional splits the input power by a fraction (set by an input) and fixed sends a specified amount of power (set by an input) to one of the outputs. The efficiency can be changed from the default of 100%, which results in some heat being generated. Cost and weight are modeled as linear functions of the power rating.
Thermal
OpenConcept includes a range of thermal components to model thermal management systems, primarily for electrified propulsion architectures.
Most of these components are used in example aircraft.
The N3_HybridSingleAisle_Refrig.py
example uses most of these components, so it’s a good place to look for example usage.
The propulsion system in HybridTwin_thermal.py
uses other ones.
Liquid-cooled components
These group together basic thermal models to create components that model the effect of heat being added to some thermal mass (or it’s massless) and then dumped into a liquid coolant loop.
Generic: LiquidCooledComp
This is a generic liquid cooled component.
Given some heat generated, it models the accumulation of the heat within some thermal mass (in mass mode), which is then dumped into a coolant stream using a ConstantSurfaceTemperatureColdPlate_NTU
.
In massless mode, the temperature of the component is set such that the amount of heat it generates is the same as the amount of heat entering the coolant.
Battery: LiquidCooledBattery
This component performs a similar function to the LiquidCooledComp
but it introduces a model of heat accumulation and heat transfer to coolant that is specific to a bandolier-style battery cooling system.
It also enables tracking of battery core and surface temperatures.
The thermal model is from the BandolierCoolingSystem
.
Motor: LiquidCooledMotor
This component performs a similar function to the LiquidCooledComp
but it introduces a model of heat accumulation and heat transfer to coolant that is specific to an electric motor with a liquid cooling jacket around it.
The thermal model is from the MotorCoolingJacket
.
Refrigerator: HeatPumpWithIntegratedCoolantLoop
This models a refrigerator (a.k.a. chiller) that is connected to two coolant loops: one on the hot side and one on the cold side. Coolant in the loop on the cold side is chilled. Heat extracted from cold side coolant (and additional heat due to inefficiency) is added to coolant on the hot side loop. The model also enables the use of a bypass, which connects the cold and hot side loops around the refrigerator.
Ducts
The heat generated onboard needs to be dumped to the atmosphere somehow. In many cases, this is done using a heat exchanger in a duct. Ambient air flows through the duct and extracts heat from the heat exchanger. These ducts are designed to model the effect on drag of adding a ducted heat exchanger to an aircraft.
ImplicitCompressibleDuct
This component models a ducted heat exchanger with compressible flow assumptions, which means the effects of heat addition are captured.
The ImplicitCompressibleDuct
includes a heat exchanger (HXGroup
) within the model.
If you’d like to define your own heat exchanger outside the duct, use the (ImplicitCompressibleDuct_ExternalHX
).
ImplicitCompressibleDuct_ExternalHX
The same as the ImplicitCompressibleDuct
but without a heat exchanger within the model.
Details on how to incorporate the heat exchanger can be found in comments in the code.
ExplicitIncompressibleDuct
This duct models a ducted heat exchanger at incompressible flow conditions. Because it cannot model flow with heat addition, it is generally a conservative estimate of the cooling drag. It assumes that the static pressure at the duct’s exist is the ambient pressure, which may or may not be a reasonable assumption.
Note
Given the limitations of the duct, it is recommended to use one of the compressible duct models instead.
Heat exchanger: HXGroup
A detailed liquid-air compact heat exchanger model.
For more details on the model, see the source code in openconcept/thermal/heat_exchanger.py
.
Heat pipe: HeatPipe
Models an ammonia heat pipe. A heat pipe is a device that takes advantage of evaporation and condensation of a working fluid to move heat passively and rapidly. The model may be inaccurate for temperatures outside the -75 to 100 deg C range because the ammonia properties interpolate data. To change the working fluid, a new surrogate model of fluid properties would be required.
Coolant pump: SimplePump
Coolant pump model that computes the required electrical load given flow properties and a required pressure rise. The pressure drop required is accumulated from hoses and the heat exchanger. Weight is a linear function of the rated power. The component sizing margin is computed as the electrical load required divided by the power rating.
Coolant hose: SimpleHose
A hose to transport coolant. The purpose of modeling the hoses on the aircraft level is that it computes a weight added to the aircraft and the pressure drop to size the pumps.
Manifolds
These components are used to split and combine coolant flows.
FlowSplit
This component splits coolant mass flow based on a fractional parameter.
FlowCombine
This component combines to coolant flow streams. It includes equations to compute the output coolant temperature of the combined input flows.
Heat transfer to coolant
At some point in the liquid-cooled thermal management system, heat must be transferred to the coolant.
These components provide general ways of doing this.
Other models provide-component specific methods for this, such as the LiquidCooledBattery
and LiquidCooledMotor
.
PerfectHeatTransferComp
This component assumes that all heat enters the coolant with no thermal resistance.
ConstantSurfaceTemperatureColdPlate_NTU
This component models a microchannel cold plate with a uniform temperature.
Unlike the PerfectHeatTransferComp
, it computes heat entering the coolant using some thermal resistance computed based on the plate geometry.
Coolant reservoir: CoolantReservoir
This component models a reservoir of coolant that can be used to buffer transient temperature changes by adding thermal mass to the system.
Thermal component residuals
For modelling temperatures of components, OpenConcept methods can be separated in two categories. The first assumes that the component has mass, which means it can accumulate heat so the heat added to it may not be the same as the heat removed from it by the cooling system. The second assumes the component is massless, which means that the heat added to it equals the heat removed.
The first category can more accurately model the temperature, particularly when the component has significant mass and the conditions/heat flows change substantially in time. Deciding when it is important to model the mass requires engineering judgement, but if you can afford the added complexity it is usually a good choice to model mass.
At the most basic level, we need some sort of base equation to solve for each of these two cases. This is the function these components provide.
Both of these are used in LiquidCooledComp
, so look there for example usage.
ThermalComponentWithMass
When thermal mass is considered, the base equation we need is one that defines the rate of change of temperature of the component for a given amount of heat added and removed. An integrator should then be attached to integrate the temperature rate of change, computing component temperature.
ThermalComponentMassless
When mass is ignored, we use a different structure.
We figure out the temperature of the component that results in the heat added being equal to the heat removed.
This becomes an implicit relationship, so ThermalComponentMassless
computes net heat flow that will be driven to zero by the solver.
The temperature it outputs should be used in the heat addition/removal models (e.g., the heat flow to the coolant depends on the temperature of the component and the coolant).
Weights
This module provides empty weight approximations using mostly empirical textbook methods. For now there are only models for small turboprop-sized aircraft, but more may be added in the future. Component positions within the aircraft are not considered; all masses are accumulated into a single number.
Single-engine turboprop OEW: SingleTurboPropEmptyWeight
This model combines estimates from Raymer[1] and Roskam[2] to compute the total operating empty weight of a small single-engine turboprop aircraft. The engine and propeller weight are not computed since OpenConcept’s turboshaft and propeller models compute those separately. Thus, those weights must be provided to this component by the user.
This model uses the following components from the openconcept.weights module to estimate the total empty weight:
WingWeight_SmallTurboprop
EmpennageWeight_SmallTurboprop
FuselageWeight_SmallTurboprop
NacelleWeight_SmallSingleTurboprop
LandingGearWeight_SmallTurboprop
FuelSystemWeight_SmallTurboprop
EquipmentWeight_SmallTurboprop
For turboprops with multiple engines, NacelleWeight_MultiTurboprop
may be used instead of NacelleWeight_SmallSingleTurboprop
.
Twin-engine series hybrid OEW: TwinSeriesHybridEmptyWeight
This model uses all the same components as SingleTurboPropEmptyWeight
, except it adds weight inputs required by the user to account for the hybrid propulsion system.
The additional weights, which are computed by other OpenConcept components, are electric motor weight and generator weight.
Utilities
OpenConcept provides utilities for math and other generally-useful operations.
Math
Integration: Integrator
This integrator is perhaps the most important part of the OpenConcept interface because it is critical for mission analysis, energy usage modeling, and unsteady thermal models. It can use the BDF3 integration scheme or Simpson’s rule to perform integration. By default it uses BDF3, but Simpson’s rule is the most common one used by OpenConcept.
For a description of how to use it, see the integrator tutorial.
Addition and subtraction: AddSubtractComp
This component can add/subtract a combination of vectors and scalars (any vector in the equation must be the same length). Scaling factors on each input can be defined to switch between addition and subtraction (and other scaling factors if desired).
Multiplication and division: ElementMultiplyDivideComp
Similar to the AddSubtractComp
, but instead of scaling factors you specify whether each input is divided (by default multiplied).
Vector manipulation
VectorConcatenateComp
Concatenates one or more sets of more than one vector into one or more output vectors.
VectorSplitComp
Splits one or more vectors into one or more sets of 2+ vectors.
Differentiation: FirstDerivative
Differentiates a vector using a second or fourth order finite difference approximation.
Maximum: MaxComp
Returns the maximum value of a vector input.
Minimum: MinComp
Returns the minimum value of a vector input.
General
Outputs from dictionary: DictIndepVarComp
Creates a component with outputs defined by keys in a nested dictionary.
The values of each output are taken from the dictionary.
Each variable from the dictionary must be added by the user with add_output_from_dict
.
This component is based on OpenMDAO’s IndepVarComp
.
Linear interpolation: LinearInterpolator
Creates a vector that linearly interpolates between an initial and final value.
Rename variables: DVLabel
Helper component that is needed when variables must be passed directly from input to output of an element with no other component in between.
This component is adapted from Justin Gray’s pyCycle software.
Select elements from vector: SelectorComp
Given a set of vector inputs, this component allows the user to specify which input each spot in the vector output pulls from.
Dymos parameters from dictionary: DymosDesignParamsFromDict
Creates Dymos parameters from an external file with a Python dictionary.
Visulization
plot_trajectory
Plot data from a mission.
plot_trajectory_grid
Plot data from multiple missions against each other.
Source Docs
openconcept.aerodynamics
aerodynamics.py
Aerodynamic analysis routines usable for multiple purposes / flight phases
- class openconcept.aerodynamics.aerodynamics.PolarDrag(**kwargs)
Bases:
ExplicitComponent
Calculates drag force based on drag polar formulation
- Inputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
fltcond|q (float) – Dynamic pressure (vector, Pascals)
ac|geom|wing|S_ref (float) – Reference wing area (scalar, m**2)
ac|geom|wing|AR (float) – Wing aspect ratio (scalar, dimensionless)
CD0 (float) – Zero-lift drag coefficient (scalar, dimensionless)
e (float) – Wing Oswald efficiency (scalar, dimensionless)
- Outputs:
drag (float) – Drag force (vector, Newtons)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
- class openconcept.aerodynamics.aerodynamics.Lift(**kwargs)
Bases:
ExplicitComponent
Calculates lift force based on CL, dynamic pressure, and wing area
- Inputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
fltcond|q (float) – Dynamic pressure (vector, Pascals)
ac|geom|wing|S_ref (float) – Reference wing area (scalar, m**2)
- Outputs:
lift (float) – Lift force (vector, Newtons)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
- class openconcept.aerodynamics.aerodynamics.StallSpeed(**kwargs)
Bases:
ExplicitComponent
Calculates stall speed based on CLmax, wing area, and weight
- Inputs:
CLmax (float) – Maximum lift coefficient (scalar, dimensionless)
weight (float) – Aircraft weight (scalar, kg)
ac|geom|wing|S_ref (float) – Reference wing area (scalar, m**2)
- Outputs:
Vstall_eas (float) – Stall speed (scalar, m/s)
openconcept.aerodynamics.openaerostruct
aerostructural.py
- class openconcept.aerodynamics.openaerostruct.aerostructural.AerostructDragPolar(**kwargs)
Bases:
Group
Drag polar and wing weight estimate generated using OpenAeroStruct’s aerostructural analysis capabilities and a surrogate model to decrease the computational cost.
This component cannot currently handle fuel loads on the wing, nor can it handle point loads applied to the structure.
Notes
The spanwise variables (twist, toverc, skin/spar thickness) are ordered starting at the tip and moving to the root; a twist of [-1, 0, 1] would have a tip twist of -1 deg and root twist of 1 deg
Set the OMP_NUM_THREADS environment variable to 1 for much better parallel training performance!
- Inputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
fltcond|h (float) – Altitude (vector, m)
fltcond|q (float) – Dynamic pressure (vector, Pascals)
ac|geom|wing|S_ref (float) – Full planform area (scalar, m^2)
ac|geom|wing|AR (float) – Aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Quarter chord sweep angle (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options), NOT num_nodes
ac|geom|wing|toverc (float) – List of thickness to chord ratios at control points of spline (vector, dimensionless) NOTE: length of vector is num_toverc (set in options)
ac|geom|wing|skin_thickness (float) – List of skin thicknesses at control points of spline (vector, m) NOTE: length of vector is num_skin (set in options)
ac|geom|wing|spar_thickness (float) – List of spar thicknesses at control points of spline (vector, m) NOTE: length of vector is num_spar (set in options)
ac|aero|CD_nonwing (float) – Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc.; this value is simply added to the drag coefficient computed by OpenAeroStruct (scalar, dimensionless)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC) NOTE: fltcond|TempIncrement is a scalar in this component but a vector in OC. This will be the case for the forseeable future because of the way the OASDataGen component is set up. To make it work, TempIncrement would need to be an input to the surrogate, which is not worth the extra training cost (at minimum a 2x increase).
- Outputs:
drag (float) – Drag force (vector, Newtons)
failure (float) – KS aggregation quantity obtained by combining the failure criteria for each FEM node. Must be < 0 to constrain wingboxes stresses to be less than yield stress. Used to simplify the optimization problem by reducing the number of constraints (vector, dimensionless)
ac|weights|W_wing (float) – Weight of the wing (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points per mission segment (scalar, dimensionless)
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
num_toverc (int) – Number of spline control points for thickness to chord ratio (scalar, dimensionless)
num_skin (int) – Number of spline control points for skin thickness (scalar, dimensionless)
num_spar (int) – Number of spline control points for spar thickness (scalar, dimensionless)
alpha_train (list or ndarray) – List of angle of attack values at which to train the model (ndarray, degrees)
Mach_train (list or ndarray) – List of Mach numbers at which to train the model (ndarray, dimensionless)
alt_train (list or ndarray) – List of altitude values at which to train the model (ndarray, m)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
- class openconcept.aerodynamics.openaerostruct.aerostructural.OASDataGen(**kwargs)
Bases:
ExplicitComponent
Generates a grid of OpenAeroStruct lift and drag data to train a surrogate model. The grid is defined by the options and the planform geometry by the inputs. This component will only recalculate the lift and drag grid when the planform shape changes.
Notes
The spanwise variables (twist, toverc, skin/spar thickness) are ordered starting at the tip and moving to the root; a twist of [-1, 0, 1] would have a tip twist of -1 deg and root twist of 1 deg
- Inputs:
ac|geom|wing|S_ref (float) – Full planform area (scalar, m^2)
ac|geom|wing|AR (float) – Aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Quarter chord sweep angle (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options)
ac|geom|wing|toverc (float) – List of thickness to chord ratios at control points of spline (vector, dimensionless) NOTE: length of vector is num_toverc (set in options)
ac|geom|wing|skin_thickness (float) – List of skin thicknesses at control points of spline (vector, m) NOTE: length of vector is num_skin (set in options)
ac|geom|wing|spar_thickness (float) – List of spar thicknesses at control points of spline (vector, m) NOTE: length of vector is num_spar (set in options)
ac|aero|CD_nonwing (float) – Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc.; this value is simply added to the drag coefficient computed by OpenAeroStruct (scalar, dimensionless)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC)
- Outputs:
CL_train (3-dim ndarray) – Grid of lift coefficient data to train structured surrogate model
CD_train (3-dim ndarray) – Grid of drag coefficient data to train structured surrogate model
failure_train (float) – Grid of KS structural failure constraint to train structured surrogate; constrain failure < 0
W_wing (float) – Wing structural weight (scalar, kg)
- Options:
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
num_toverc (int) – Number of spline control points for thickness to chord ratio (scalar, dimensionless)
num_skin (int) – Number of spline control points for skin thickness (scalar, dimensionless)
num_spar (int) – Number of spline control points for spar thickness (scalar, dimensionless)
Mach_train (list or ndarray) – List of Mach numbers at which to train the model (ndarray, dimensionless)
alpha_train (list or ndarray) – List of angle of attack values at which to train the model (ndarray, degrees)
alt_train (list or ndarray) – List of altitude values at which to train the model (ndarray, m)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
regen_tol (float) – Difference in input variable above which to regenerate the training data.
- class openconcept.aerodynamics.openaerostruct.aerostructural.Aerostruct(**kwargs)
Bases:
Group
Perform a coupled aerostructural analysis using OpenAeroStruct. This component currently does not support distributed fuel loads or point loads added to the structure.
Notes
The spanwise variables (twist, toverc, skin/spar thickness) are ordered starting at the tip and moving to the root; a twist of [-1, 0, 1] would have a tip twist of -1 deg and root twist of 1 deg
- Inputs:
fltcond|alpha (float) – Angle of attack (scalar, degrees)
fltcond|M (float) – Mach number (scalar, dimensionless)
fltcond|h (float) – Altitude (scalar, m)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC)
ac|geom|wing|S_ref (float) – Wing planform area (scalar, m^2)
ac|geom|wing|AR (float) – Wing aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Wing taper ratio (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Wing sweep measured at quarter chord (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options)
ac|geom|wing|toverc (float) – List of thickness to chord ratios at control points of spline (vector, dimensionless) NOTE: length of vector is num_toverc (set in options)
ac|geom|wing|skin_thickness (float) – List of skin thicknesses at control points of spline (vector, m) NOTE: length of vector is num_skin (set in options)
ac|geom|wing|spar_thickness (float) – List of spar thicknesses at control points of spline (vector, m) NOTE: length of vector is num_spar (set in options)
- Outputs:
fltcond|CL (float) – Lift coefficient of wing (scalar, dimensionless)
fltcond|CD (float) – Drag coefficient of wing (scalar, dimensionless)
failure (float) – KS structural failure constraint; constrain failure < 0 (scalar, dimensionless)
ac|weights|W_wing (float) – Wing structural weight (scalar, kg)
- Options:
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
num_toverc (int) – Number of spline control points for thickness to chord ratio (scalar, dimensionless)
num_skin (int) – Number of spline control points for skin thickness (scalar, dimensionless)
num_spar (int) – Number of spline control points for spar thickness (scalar, dimensionless)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
- class openconcept.aerodynamics.openaerostruct.aerostructural.AerostructDragPolarExact(**kwargs)
Bases:
Group
Warning
This component is far more computationally expensive than the AerostructDragPolar component, which uses a surrogate. For missions with many flight segments, many num_nodes, or wing models with high num_x and num_y values this component will result in a system that returns a memory error when solved with a DirectSolver linear solver because the Jacobian is too large to be factorized. Unless you know what you’re doing, this component should not be used (use AerostructDragPolar instead).
Drag polar and wing weight estimate generated using OpenAeroStruct’s aerostructural analysis capabilities directly, without a surrogate in the loop.
- Inputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
fltcond|h (float) – Altitude (vector, m)
fltcond|q (float) – Dynamic pressure (vector, Pascals)
ac|geom|wing|S_ref (float) – Full planform area (scalar, m^2)
ac|geom|wing|AR (float) – Aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Quarter chord sweep angle (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options), NOT num_nodes
ac|geom|wing|toverc (float) – List of thickness to chord ratios at control points of spline (vector, dimensionless) NOTE: length of vector is num_toverc (set in options)
ac|geom|wing|skin_thickness (float) – List of skin thicknesses at control points of spline (vector, m) NOTE: length of vector is num_skin (set in options)
ac|geom|wing|spar_thickness (float) – List of spar thicknesses at control points of spline (vector, m) NOTE: length of vector is num_spar (set in options)
ac|aero|CD_nonwing (float) – Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc.; this value is simply added to the drag coefficient computed by OpenAeroStruct (scalar, dimensionless)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC) NOTE: fltcond|TempIncrement is a scalar in this component but a vector in OC. This will be the case for the forseeable future because of the way the OASDataGen component is set up. To make it work, TempIncrement would need to be an input to the surrogate, which is not worth the extra training cost (at minimum a 2x increase).
- Outputs:
drag (float) – Drag force (vector, Newtons)
failure (float) – KS aggregation quantity obtained by combining the failure criteria for each FEM node. Must be < 0 to constrain wingboxes stresses to be less than yield stress. Used to simplify the optimization problem by reducing the number of constraints (vector, dimensionless)
ac|weights|W_wing (float) – Weight of the wing (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points per mission segment (scalar, dimensionless)
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
num_toverc (int) – Number of spline control points for thickness to chord ratio (scalar, dimensionless)
num_skin (int) – Number of spline control points for skin thickness (scalar, dimensionless)
num_spar (int) – Number of spline control points for spar thickness (scalar, dimensionless)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
drag_polar.py
- class openconcept.aerodynamics.openaerostruct.drag_polar.VLMDragPolar(**kwargs)
Bases:
Group
Drag polar generated using OpenAeroStruct’s vortex lattice method and a surrogate model to decrease the computational cost.
Notes
Twist is ordered starting at the tip and moving to the root; a twist of [-1, 0, 1] would have a tip twist of -1 deg and root twist of 1 deg
Set the OMP_NUM_THREADS environment variable to 1 for much better parallel training performance!
- Inputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
fltcond|h (float) – Altitude (vector, m)
fltcond|q (float) – Dynamic pressure (vector, Pascals)
ac|geom|wing|S_ref (float) – Full planform area (scalar, m^2)
ac|geom|wing|AR (float) – Aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Quarter chord sweep angle (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options), NOT num_nodes
ac|aero|CD_nonwing (float) – Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc.; this value is simply added to the drag coefficient computed by OpenAeroStruct (scalar, dimensionless)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC) NOTE: fltcond|TempIncrement is a scalar in this component but a vector in OC. This will be the case for the forseeable future because of the way the VLMDataGen component is set up. To make it work, TempIncrement would need to be an input to the surrogate, which is not worth the extra training cost (at minimum a 2x increase).
- Outputs:
drag (float) – Drag force (vector, Newtons)
- Options:
num_nodes (int) – Number of analysis points per mission segment (scalar, dimensionless)
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
alpha_train (list or ndarray) – List of angle of attack values at which to train the model (ndarray, degrees)
Mach_train (list or ndarray) – List of Mach numbers at which to train the model (ndarray, dimensionless)
alt_train (list or ndarray) – List of altitude values at which to train the model (ndarray, m)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
- class openconcept.aerodynamics.openaerostruct.drag_polar.VLMDataGen(**kwargs)
Bases:
ExplicitComponent
Generates a grid of OpenAeroStruct lift and drag data to train a surrogate model. The grid is defined by the options and the planform geometry by the inputs. This component will only recalculate the lift and drag grid when the planform shape changes. All VLMDataGen components in the model must use the same training points and surf_options.
- Inputs:
ac|geom|wing|S_ref (float) – Full planform area (scalar, m^2)
ac|geom|wing|AR (float) – Aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Quarter chord sweep angle (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options)
ac|aero|CD_nonwing (float) – Drag coefficient of components other than the wing; e.g. fuselage, tail, interference drag, etc.; this value is simply added to the drag coefficient computed by OpenAeroStruct (scalar, dimensionless)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC)
- Outputs:
CL_train (3-dim ndarray) – Grid of lift coefficient data to train structured surrogate model
CD_train (3-dim ndarray) – Grid of drag coefficient data to train structured surrogate model
- Options:
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
Mach_train (list or ndarray) – List of Mach numbers at which to train the model (ndarray, dimensionless)
alpha_train (list or ndarray) – List of angle of attack values at which to train the model (ndarray, degrees)
alt_train (list or ndarray) – List of altitude values at which to train the model (ndarray, m)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
- class openconcept.aerodynamics.openaerostruct.drag_polar.VLM(**kwargs)
Bases:
Group
Computes lift and drag using OpenAeroStruct’s vortex lattice implementation.
Notes
Twist is ordered starting at the tip and moving to the root; a twist of [-1, 0, 1] would have a tip twist of -1 deg and root twist of 1 deg
- Inputs:
fltcond|alpha (float) – Angle of attack (scalar, degrees)
fltcond|M (float) – Mach number (scalar, dimensionless)
fltcond|h (float) – Altitude (scalar, m)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (scalar, degC)
ac|geom|wing|S_ref (float) – Wing planform area (scalar, m^2)
ac|geom|wing|AR (float) – Wing aspect ratio (scalar, dimensionless)
ac|geom|wing|taper (float) – Wing taper ratio (scalar, dimensionless)
ac|geom|wing|c4sweep (float) – Wing sweep measured at quarter chord (scalar, degrees)
ac|geom|wing|twist (float) – List of twist angles at control points of spline (vector, degrees) NOTE: length of vector is num_twist (set in options)
- Outputs:
fltcond|CL (float) – Lift coefficient of wing (scalar, dimensionless)
fltcond|CD (float) – Drag coefficient of wing (scalar, dimensionless)
- Options:
num_x (int) – Number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – Number of points in y (spanwise) direction for one wing because uses symmetry (scalar, dimensionless)
num_twist (int) – Number of spline control points for twist (scalar, dimensionless)
surf_options (dict) – Dictionary of OpenAeroStruct surface options; any options provided here will override the default ones; see the OpenAeroStruct documentation for more information. Because the geometry transformations are excluded in this model (to simplify the interface), the <transformation>_cp options are not supported. The input ac|geom|wing|twist is the same as modifying the twist_cp option in the surface dictionary. The mesh geometry modification is limited to adjusting the input parameters to this component.
- class openconcept.aerodynamics.openaerostruct.drag_polar.PlanformMesh(**kwargs)
Bases:
ExplicitComponent
Generate an OpenAeroStruct mesh based on basic wing design parameters. Resulting mesh is for a half wing (meant to use with OpenAeroStruct symmetry), but the input reference area is for the full wing.
- Inputs:
S (float) – full planform area (scalar, m^2)
AR (float) – aspect ratio (scalar, dimensionless)
taper (float) – taper ratio (must be >0 and <=1); tip chord / root chord (scalar, dimensionless)
sweep (float) – quarter chord sweep angle (scalar, degrees)
- Outputs:
mesh (ndarray) – OpenAeroStruct 3D mesh (num_x x num_y x 3 ndarray, m)
- Options:
num_x (int) – number of points in x (streamwise) direction (scalar, dimensionless)
num_y (int) – number of points in y (spanwise) direction (scalar, dimensionless)
openconcept.atmospherics
atmospherics_data.py
This module provides 1976 Standard Atmosphere constants and calculations.
Adapted from: J.P. Jasa, J.T. Hwang, and J.R.R.A. Martins: Design and Trajectory Optimization of a Morphing Wing Aircraft 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum, January 2018
compute_atmos_props.py
- class openconcept.atmospherics.compute_atmos_props.ComputeAtmosphericProperties(**kwargs)
Bases:
Group
Computes pressure, density, temperature, dyn pressure, and true airspeed
- Inputs:
fltcond|h (float) – Altitude (vector, km)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|TempIncrement (float) – Temperature increment for non-standard day (vector, degC)
- Outputs:
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|q (float) – Dynamic pressure (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
true_airspeed_in (bool) – Flip to true if input vector is Utrue, not Ueas. If this is true, fltcond|Utrue will be an input and fltcond|Ueas will be an output.
density_comp.py
- class openconcept.atmospherics.density_comp.DensityComp(**kwargs)
Bases:
ExplicitComponent
This component computes density from pressure and temperature.
Adapted from: J.P. Jasa, J.T. Hwang, and J.R.R.A. Martins: Design and Trajectory Optimization of a Morphing Wing Aircraft 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum, January 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)
dynamic_pressure_comp.py
- class openconcept.atmospherics.dynamic_pressure_comp.DynamicPressureComp(**kwargs)
Bases:
ExplicitComponent
Calculates dynamic pressure from true airspeed and density.
- Inputs:
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|rho (float) – Density (vector, m/s)
- Outputs:
fltcond|q (float) – Dynamic pressure (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
mach_number_comp.py
- class openconcept.atmospherics.mach_number_comp.MachNumberComp(**kwargs)
Bases:
ExplicitComponent
Computes mach number from true airspeed and speed of sound
- Inputs:
fltcond|a (float) – Speed of sound (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
- Outputs:
fltcond|M (float) – Mach number (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
pressure_comp.py
- class openconcept.atmospherics.pressure_comp.PressureComp(**kwargs)
Bases:
ExplicitComponent
This component computes pressure from altitude using the 1976 Standard Atmosphere.
Adapted from: J.P. Jasa, J.T. Hwang, and J.R.R.A. Martins: Design and Trajectory Optimization of a Morphing Wing Aircraft 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum, January 2018
- Inputs:
fltcond|h (float) – Altitude (vector, m)
- Outputs:
fltcond|p (float) – Pressure at flight condition (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run, i.e. length of vector inputs (scalar, dimensionless)
speedofsound_comp.py
- class openconcept.atmospherics.speedofsound_comp.SpeedOfSoundComp(**kwargs)
Bases:
ExplicitComponent
This component computes speed of sound from temperature.
Adapted from: J.P. Jasa, J.T. Hwang, and J.R.R.A. Martins: Design and Trajectory Optimization of a Morphing Wing Aircraft 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum, January 2018
- Inputs:
fltcond|T (float) – Temperature at flight condition (vector, K)
- Outputs:
fltcond|a (float) – Speed of sound (vector, m/s)
- Options:
num_nodes (int) – Number of analysis points to run, i.e. length of vector inputs (scalar, dimensionless)
temperature_comp.py
- class openconcept.atmospherics.temperature_comp.TemperatureComp(**kwargs)
Bases:
ExplicitComponent
This component computes temperature from altitude using the 1976 Standard Atmosphere.
Adapted from: J.P. Jasa, J.T. Hwang, and J.R.R.A. Martins: Design and Trajectory Optimization of a Morphing Wing Aircraft 2018 AIAA/ASCE/AHS/ASC Structures, Structural Dynamics, and Materials Conference; AIAA SciTech Forum, January 2018
- Inputs:
fltcond|h (float) – Altitude (vector, m)
fltcond|TempIncrement (float) – Offset for temperature; useful for modeling hot (+ increment) or cold (- increment) days (vector, deg C)
- Outputs:
fltcond|T (float) – Temperature at flight condition (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run, i.e. length of vector inputs (scalar, dimensionless)
true_airspeed.py
- class openconcept.atmospherics.true_airspeed.TrueAirspeedComp(**kwargs)
Bases:
ExplicitComponent
Computes true airspeed from equivalent airspeed and density
- Inputs:
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
- Outputs:
fltcond|Utrue (float) – True airspeed (vector, m/s)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
- class openconcept.atmospherics.true_airspeed.EquivalentAirspeedComp(**kwargs)
Bases:
ExplicitComponent
Computes equivalent airspeed from true airspeed and density
- Inputs:
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|Utrue (float) – True airspeed (vector, m/s)
- Outputs:
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length) (default 1)
openconcept.energy_storage
battery.py
- class openconcept.energy_storage.battery.SOCBattery(**kwargs)
Bases:
Group
Same as SimpleBattery but also tracks state of charge
- Inputs:
battery_weight (float) – Weight of the battery pack (scalar, kg)
elec_load (float) – Electric power draw upstream (vector, W)
SOC_initial (float) – Initial state of charge (default 1) (scalar, dimensionless)
duration (float) – Length of the mission phase (corresponding to num_nodes) (scalar, s)
- Outputs:
SOC (float) – State of charge of the battery on a scale of 0 to 1 (vector, dimensionless)
max_energy (float) – Total energy in the battery at 100% SOC (scalar, Wh)
heat_out (float) – Waste heat produced (vector, W)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_sizing_margin (float) – Equal to 1 when producing full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
efficiency (float) – Shaft power efficiency. Sensible range 0.0 to 1.0 (default 1.0)
specific_power (float) – Rated power per unit weight (default 5000, W/kg)
default_specific_energy (float) – Battery energy per unit weight NOTE UNITS (default 300, !!!! Wh/kg) Can be set using variable input ‘specific_energy’ as well if doing a sweep
cost_inc (float) – Cost per unit weight (default 50, USD/kg)
cost_base (float) – Base cost (default 1 USD)
- class openconcept.energy_storage.battery.SimpleBattery(**kwargs)
Bases:
ExplicitComponent
A simple battery which tracks power limits and generates heat.
Specific energy assumption INCLUDING internal losses should be used The efficiency parameter only generates heat
- Inputs:
battery_weight (float) – Weight of the battery pack (scalar, kg)
elec_load (float) – Electric power draw upstream (vector, W)
- Outputs:
max_energy (float) – Total energy in the battery at 100% SOC (scalar, Wh)
heat_out (float) – Waste heat produced (vector, W)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_sizing_margin (float) – Equal to 1 when producing full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
efficiency (float) – Shaft power efficiency. Sensible range 0.0 to 1.0 (default 1.0)
specific_power (float) – Rated power per unit weight (default 5000, W/kg)
specific_energy (float) – Battery energy per unit weight NOTE UNITS (default 300, !!!! Wh/kg) Can override this with variable input during a sweep (input specific_energy)
cost_inc (float) – Cost per unit weight (default 50, USD/kg)
cost_base (float) – Base cost (default 1 USD)
openconcept.mission
mission_groups.py
phases.py
- class openconcept.mission.phases.ClimbAngleComp(**kwargs)
Bases:
ExplicitComponent
Computes steady climb angle based on excess thrust.
This is a helper function and shouldn’t be instantiated in the top-level model directly.
- Inputs:
drag (float) – Aircraft drag at v2 (climb out) flight condition (scalar, N)
weight (float) – Takeoff weight (scalar, kg)
thrust (float) – Thrust at the v2 (climb out) flight condition (scalar, N)
- Outputs:
gamma (float) – Climb out flight path angle (scalar, rad)
- Options:
num_nodes (int) – Number of points to run
- class openconcept.mission.phases.FlipVectorComp(**kwargs)
Bases:
ExplicitComponent
Reverses the order of an OpenMDAO vector
This is a helper function and shouldn’t be instantiated in the top-level model directly.
- Inputs:
vec_in (float) – Incoming vector in forward order
- Outputs:
vec_out (float) – Reversed order version of vec_in
- Options:
num_nodes (int) – Number of points to run
negative (boolean) – Whether to apply a negative scaler. Default False preserves vector values. True returns all values with negative sign.
units (string or None) – Units for vec_in and vec_out (Default None) Specify as an OpenMDAO unit string (e.g. ‘kg’)
- class openconcept.mission.phases.BFLImplicitSolve(**kwargs)
Bases:
ImplicitComponent
Computes a residual equation so Newton solver can set v1 to analyze balanced field length
- This residual is equal to zero if:
The rejected takeoff and engine-out takeoff distances are equal, or:
V1 is equal to VR and the engine out takeoff distance is longer than the RTO distance
Since this is a discontinous function, the partial derivatives are written in a special way to ‘coax’ the V1 value into the right setting with a Newton step. It’s kind of a hack.
- Inputs:
distance_continue (float) – Engine-out takeoff distance (scalar, m)
distance_abort (float) – Distance to full-stop when takeoff is rejected at V1 (scalar, m)
takeoff|vr (float) – Rotation speed (scalar, m/s)
- Outputs:
takeoff|v1 (float) – Decision speed (scalar, m/s)
- class openconcept.mission.phases.Groundspeeds(**kwargs)
Bases:
ExplicitComponent
Computes groundspeed for vectorial true airspeed and true vertical speed.
This is a helper function for the main mission analysis routines and shouldn’t be instantiated directly.
- Inputs:
fltcond|vs (float) – Vertical speed for all mission phases (vector, m/s)
fltcond|Utrue (float) – True airspeed for all mission phases (vector, m/s)
- Outputs:
fltcond|groundspeed (float) – True groundspeed for all mission phases (vector, m/s)
fltcond|cosgamma (float) – Cosine of the flght path angle for all mission phases (vector, dimensionless)
fltcond|singamma (float) – Sine of the flight path angle for all mission phases (vector, dimensionless)
- Options:
num_nodes (int) – Number of points to run
- class openconcept.mission.phases.HorizontalAcceleration(**kwargs)
Bases:
ExplicitComponent
Computes acceleration during takeoff run and effectively forms the T-D residual.
- Inputs:
weight (float) – Aircraft weight (scalar, kg)
drag (float) – Aircraft drag at each analysis point (vector, N)
lift (float) – Aircraft lift at each analysis point (vector, N)
thrust (float) – Thrust at each TO analysis point (vector, N)
fltcond|singamma (float) – The sine of the flight path angle gamma (vector, dimensionless)
braking (float) – Effective rolling friction multiplier at each point (vector, dimensionless)
- Outputs:
accel_horiz (float) – Aircraft horizontal acceleration (vector, m/s**2)
- Options:
num_nodes (int) – Number of analysis points to run
- class openconcept.mission.phases.VerticalAcceleration(**kwargs)
Bases:
ExplicitComponent
Computes acceleration during takeoff run in the vertical plane. Only used during full unsteady takeoff performance analysis due to stability issues
- Inputs:
weight (float) – Aircraft weight (scalar, kg)
drag (float) – Aircraft drag at each analysis point (vector, N)
lift (float) – Aircraft lift at each analysis point (vector, N)
thrust (float) – Thrust at each TO analysis point (vector, N)
fltcond|singamma (float) – The sine of the flight path angle gamma (vector, dimensionless)
fltcond|cosgamma (float) – The sine of the flight path angle gamma (vector, dimensionless)
- Outputs:
accel_vert (float) – Aircraft horizontal acceleration (vector, m/s**2)
- Options:
num_nodes (int) – Number of analysis points to run
- class openconcept.mission.phases.SteadyFlightCL(**kwargs)
Bases:
ExplicitComponent
Computes lift coefficient at each analysis point
This is a helper function for the main mission analysis routine and shouldn’t be instantiated directly.
- Inputs:
weight (float) – Aircraft weight at each analysis point (vector, kg)
fltcond|q (float) – Dynamic pressure at each analysis point (vector, Pascal)
ac|geom|wing|S_ref (float) – Reference wing area (scalar, m**2)
fltcond|cosgamma (float) – Cosine of the flght path angle for all mission phases (vector, dimensionless)
- Outputs:
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis nodes to run
- class openconcept.mission.phases.GroundRollPhase(**kwargs)
Bases:
PhaseGroup
This component group models the ground roll phase of a takeoff (acceleration before flight) User-settable parameters include: throttle (default 100 percent) rolling friction coeff (default 0.03 for accelerating phases and 0.4 for braking) propulsor_active (default 1 for v0 to v1, 0 for v1 to vr and braking) to model engine failure altitude (fltcond|h)
The BaseAircraftGroup object is passed in. The BaseAircraftGroup should be built to accept the following inputs and return the following outputs. The outputs should be promoted to the top level in the component.
- Inputs:
range (float) – Total distance travelled (vector, m)
fltcond|h (float) – Altitude (vector, m)
fltcond|vs (float) – Vertical speed (vector, m/s)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|q (float) – Dynamic pressure (vector, Pa)
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
throttle (float) – Motor / propeller throttle setting scaled from 0 to 1 or slightly more (vector, dimensionless)
propulsor_active (float) – If a multi-propulsor airplane, a failure condition should be modeled in the propulsion model by multiplying throttle by propulsor_active. It will generally be 1.0 unless a failure condition is being modeled, in which case it will be 0 (vector, dimensionless)
braking (float) – Brake friction coefficient (default 0.4 for dry runway braking, 0.03 for resistance unbraked) Should not be applied in the air or nonphysical effects will result (vector, dimensionless)
lift (float) – Lift force (vector, N)
- Outputs:
thrust (float) – Total thrust force produced by all propulsors (vector, N)
drag (float) – Total drag force in the airplane axis produced by all sources of drag (vector, N)
weight (float) – Weight (mass, really) of the airplane at each point in time. (vector, kg)
ac|geom|wing|S_ref – Wing reference area (scalar, m**2)
ac|aero|CLmax_TO – CLmax with flaps in max takeoff position (scalar, dimensionless)
ac|weights|MTOW – Maximum takeoff weight (scalar, kg)
- class openconcept.mission.phases.RotationPhase(**kwargs)
Bases:
PhaseGroup
This group models the transition from ground roll to climb out during a takeoff using force balance in the vertical and horizontal directions.
User-settable parameters include: throttle (default 100 percent) rolling friction coeff (default 0.03 for accelerating phases and 0.4 for braking) propulsor_active (default 1 for v0 to v1, 0 for v1 to vr and braking) to model engine failure altitude (fltcond|h) obstacle clearance hight (h_obs) default 35 feet per FAR 25 Rotation CL/CLmax ratio (default 0.83)
The BaseAircraftGroup object is passed in. The BaseAircraftGroup should be built to accept the following inputs and return the following outputs. The outputs should be promoted to the top level in the component.
- Inputs:
range (float) – Total distance travelled (vector, m)
fltcond|h (float) – Altitude (vector, m)
fltcond|vs (float) – Vertical speed (vector, m/s)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|q (float) – Dynamic pressure (vector, Pa)
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
throttle (float) – Motor / propeller throttle setting scaled from 0 to 1 or slightly more (vector, dimensionless)
propulsor_active (float) – If a multi-propulsor airplane, a failure condition should be modeled in the propulsion model by multiplying throttle by propulsor_active. It will generally be 1.0 unless a failure condition is being modeled, in which case it will be 0 (vector, dimensionless)
braking (float) – Percentage brakes applied, from 0 to 1. Should not be applied in the air or nonphysical effects will result (vector, dimensionless)
lift (float) – Lift force (vector, N)
- Outputs:
thrust (float) – Total thrust force produced by all propulsors (vector, N)
drag (float) – Total drag force in the airplane axis produced by all sources of drag (vector, N)
weight (float) – Weight (mass, really) of the airplane at each point in time. Generally will need to be integrated by Dymos as a state with a rate source (vector, kg)
ac|geom|wing|S_ref – Wing reference area (scalar, m**2)
ac|aero|CLmax_TO – CLmax with flaps in max takeoff position (scalar, dimensionless)
ac|weights|MTOW – Maximum takeoff weight (scalar, kg)
- class openconcept.mission.phases.SteadyFlightPhase(**kwargs)
Bases:
PhaseGroup
This component group models steady flight conditions. Settable mission parameters include: Airspeed (fltcond|Ueas) Vertical speed (fltcond|vs) Duration of the phase (duration)
Throttle is set automatically to ensure steady flight
The BaseAircraftGroup object is passed in. The BaseAircraftGroup should be built to accept the following inputs and return the following outputs. The outputs should be promoted to the top level in the component.
- Inputs:
range (float) – Total distance travelled (vector, m)
fltcond|h (float) – Altitude (vector, m)
fltcond|vs (float) – Vertical speed (vector, m/s)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|q (float) – Dynamic pressure (vector, Pa)
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
throttle (float) – Motor / propeller throttle setting scaled from 0 to 1 or slightly more (vector, dimensionless)
propulsor_active (float) – If a multi-propulsor airplane, a failure condition should be modeled in the propulsion model by multiplying throttle by propulsor_active. It will generally be 1.0 unless a failure condition is being modeled, in which case it will be 0 (vector, dimensionless)
braking (float) – Brake friction coefficient (default 0.4 for dry runway braking, 0.03 for resistance unbraked) Should not be applied in the air or nonphysical effects will result (vector, dimensionless)
lift (float) – Lift force (vector, N)
- Outputs:
thrust (float) – Total thrust force produced by all propulsors (vector, N)
drag (float) – Total drag force in the airplane axis produced by all sources of drag (vector, N)
weight (float) – Weight (mass, really) of the airplane at each point in time. (vector, kg)
ac|geom|wing|S_ref – Wing reference area (scalar, m**2)
ac|aero|CLmax_TO – CLmax with flaps in max takeoff position (scalar, dimensionless)
ac|weights|MTOW – Maximum takeoff weight (scalar, kg)
- class openconcept.mission.phases.ClimbAnglePhase(**kwargs)
Bases:
Group
This component checks the climb angle for a single flight condition at the V2 speed. No integration is performed.
User settable parameter includes the V2/Vstall multiple (default 1.2)
Useful for ensuring all-engine climb gradients in optimization. Choose flight_phase = AllEngineClimbAngle or EngineOutClimbAngle to set the propulsor_active property correctly.
- Inputs:
range (float) – Total distance travelled (vector, m)
fltcond|h (float) – Altitude (vector, m)
fltcond|vs (float) – Vertical speed (vector, m/s)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|q (float) – Dynamic pressure (vector, Pa)
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
throttle (float) – Motor / propeller throttle setting scaled from 0 to 1 or slightly more (vector, dimensionless)
propulsor_active (float) – If a multi-propulsor airplane, a failure condition should be modeled in the propulsion model by multiplying throttle by propulsor_active. It will generally be 1.0 unless a failure condition is being modeled, in which case it will be 0 (vector, dimensionless)
lift (float) – Lift force (vector, N)
- Outputs:
thrust (float) – Total thrust force produced by all propulsors (vector, N)
drag (float) – Total drag force in the airplane axis produced by all sources of drag (vector, N)
weight (float) – Weight (mass, really) of the airplane at each point in time. Generally will need to be integrated by Dymos as a state with a rate source (vector, kg)
ac|geom|wing|S_ref – Wing reference area (scalar, m**2)
ac|aero|CLmax_TO – CLmax with flaps in max takeoff position (scalar, dimensionless)
ac|weights|MTOW – Maximum takeoff weight (scalar, kg)
- class openconcept.mission.phases.TakeoffTransition(**kwargs)
Bases:
ExplicitComponent
Computes distance and altitude at end of circular transition.
Based on TO distance analysis method in Raymer book. Obstacle clearance height set for GA / Part 23 aircraft Override for analyzing Part 25 aircraft
- Inputs:
fltcond|Utrue – Transition true airspeed (generally avg of vr and v2) (scalar, m/s)
gamma (float) – Climb out flight path angle (scalar, rad)
- Outputs:
s_transition (float) – Horizontal distance during transition to v2 climb out (scalar, m)
h_transition (float) – Altitude at transition point (scalar, m)
t_transition (float) – Elapsed time in transition (scalar, s)
- Options:
h_obstacle (float) – Obstacle height to clear (in meters) (default 10.66, equiv. 35 ft)
load_factor (float) – Load factor during rotation and transition (default 1.2 from Raymer book)
- class openconcept.mission.phases.TakeoffClimb(**kwargs)
Bases:
ExplicitComponent
Computes ground distance from end of transition until obstacle is cleared.
Analysis based on Raymer book.
- Inputs:
gamma (float) – Climb out flight path angle (scalar, rad)
h_transition (float) – Altitude at transition point (scalar, m)
- Outputs:
s_climb (float) – Horizontal distance from end of transition until obstacle is cleared (scalar, m)
- Options:
h_obstacle (float) – Obstacle height to clear (in meters) (default 10.66, equiv. 35 ft)
- class openconcept.mission.phases.RobustRotationPhase(**kwargs)
Bases:
PhaseGroup
This adds general mission analysis capabilities to an existing airplane model. The BaseAircraftGroup object is passed in. It should be built to accept the following inputs and return the following outputs. The outputs should be promoted to the top level in the component.
- Inputs:
range (float) – Total distance travelled (vector, m)
fltcond|h (float) – Altitude (vector, m)
fltcond|vs (float) – Vertical speed (vector, m/s)
fltcond|Ueas (float) – Equivalent airspeed (vector, m/s)
fltcond|Utrue (float) – True airspeed (vector, m/s)
fltcond|p (float) – Pressure (vector, Pa)
fltcond|rho (float) – Density (vector, kg/m3)
fltcond|T (float) – Temperature (vector, K)
fltcond|q (float) – Dynamic pressure (vector, Pa)
fltcond|CL (float) – Lift coefficient (vector, dimensionless)
throttle (float) – Motor / propeller throttle setting scaled from 0 to 1 or slightly more (vector, dimensionless)
propulsor_active (float) – If a multi-propulsor airplane, a failure condition should be modeled in the propulsion model by multiplying throttle by propulsor_active. It will generally be 1.0 unless a failure condition is being modeled, in which case it will be 0 (vector, dimensionless)
braking (float) – Percentage brakes applied, from 0 to 1. Should not be applied in the air or nonphysical effects will result (vector, dimensionless)
lift (float) – Lift force (vector, N)
- Outputs:
thrust (float) – Total thrust force produced by all propulsors (vector, N)
drag (float) – Total drag force in the airplane axis produced by all sources of drag (vector, N)
weight (float) – Weight (mass, really) of the airplane at each point in time. Generally will need to be integrated by Dymos as a state with a rate source (vector, kg)
ac|geom|wing|S_ref – Wing reference area (scalar, m**2)
ac|aero|CLmax_TO – CLmax with flaps in max takeoff position (scalar, dimensionless)
ac|weights|MTOW – Maximum takeoff weight (scalar, kg)
profiles.py
- class openconcept.mission.profiles.MissionWithReserve(**kwargs)
Bases:
TrajectoryGroup
This analysis group is set up to compute all the major parameters of a fixed wing mission, including climb, cruise, and descent as well as Part 25 reserve fuel phases. The 5% of block fuel is not accounted for here.
To use this analysis, pass in an aircraft model following OpenConcept interface. Namely, the model should consume the following: - flight conditions (fltcond|q/rho/p/T/Utrue/Ueas/…) - aircraft design parameters (ac|*) - lift coefficient (fltcond|CL; either solved from steady flight or assumed during ground roll) - throttle - propulsor_failed (value 0 when failed, 1 when not failed)
and produce top-level outputs: - thrust - drag - weight
the following parameters need to either be defined as design variables or given as top-level analysis outputs from the airplane model: - ac|geom|S_ref - ac|aero|CL_max_flaps30 - ac|weights|MTOW
- Inputs:
ac|* (various) – All relevant airplane design variables to pass to the airplane model
takeoff|h (float) – Takeoff obstacle clearance height (default 50 ft)
cruise|h0 (float) – Initial cruise altitude (default 28000 ft)
payload (float) – Mission payload (default 1000 lbm)
mission_range (float) – Design range (deault 1250 NM)
- Options:
aircraft_model (class) – An aircraft model class with the standard OpenConcept interfaces promoted correctly
num_nodes (int) – Number of analysis points per phase. Higher is more accurate but more expensive
- class openconcept.mission.profiles.BasicMission(**kwargs)
Bases:
TrajectoryGroup
This analysis group is set up to compute all the major parameters of a fixed wing mission, including climb, cruise, and descent but no Part 25 reserves To use this analysis, pass in an aircraft model following OpenConcept interface. Namely, the model should consume the following: - flight conditions (fltcond|q/rho/p/T/Utrue/Ueas/…) - aircraft design parameters (ac|*) - lift coefficient (fltcond|CL; either solved from steady flight or assumed during ground roll) - throttle - propulsor_failed (value 0 when failed, 1 when not failed) and produce top-level outputs: - thrust - drag - weight the following parameters need to either be defined as design variables or given as top-level analysis outputs from the airplane model: - ac|geom|S_ref - ac|aero|CL_max_flaps30 - ac|weights|MTOW
- Inputs:
ac|* (various) – All relevant airplane design variables to pass to the airplane model
takeoff|h (float) – Takeoff obstacle clearance height (default 50 ft)
cruise|h0 (float) – Initial cruise altitude (default 28000 ft)
payload (float) – Mission payload (default 1000 lbm)
mission_range (float) – Design range (deault 1250 NM)
- Options:
aircraft_model (class) – An aircraft model class with the standard OpenConcept interfaces promoted correctly
num_nodes (int) – Number of analysis points per phase. Higher is more accurate but more expensive
- class openconcept.mission.profiles.FullMissionAnalysis(**kwargs)
Bases:
TrajectoryGroup
This analysis group is set up to compute all the major parameters of a fixed wing mission, including balanced-field takeoff, climb, cruise, and descent.
To use this analysis, pass in an aircraft model following OpenConcept interface. Namely, the model should consume the following: - flight conditions (fltcond|q/rho/p/T/Utrue/Ueas/…) - aircraft design parameters (ac|*) - lift coefficient (fltcond|CL; either solved from steady flight or assumed during ground roll) - throttle - propulsor_failed (value 0 when failed, 1 when not failed)
and produce top-level outputs: - thrust - drag - weight
the following parameters need to either be defined as design variables or given as top-level analysis outputs from the airplane model: - ac|geom|S_ref - ac|aero|CL_max_flaps30 - ac|weights|MTOW
- Inputs:
ac|* (various) – All relevant airplane design variables to pass to the airplane model
takeoff|h (float) – Takeoff obstacle clearance height (default 50 ft)
cruise|h0 (float) – Initial cruise altitude (default 28000 ft)
payload (float) – Mission payload (default 1000 lbm)
mission_range (float) – Design range (deault 1250 NM)
- Outputs:
takeoff|v1 (float) – Decision speed
- Options:
aircraft_model (class) – An aircraft model class with the standard OpenConcept interfaces promoted correctly
num_nodes (int) – Number of analysis points per phase. Higher is more accurate but more expensive
transition_method (str) – Analysis method to compute distance, altitude, and time during transition Default “simplified” is the Raymer circular arc method and is more robust Option “ode” is a 2DOF ODE integration method which is arguably just as inaccurate and less robust
openconcept.propulsion
N3.py
- openconcept.propulsion.N3.N3Hybrid(num_nodes=1, plot=False)
Returns OpenMDAO component for thrust deck for optimized N+3 GTF with hybridization.
- Inputs:
throttle (float) – Engine throttle. Controls power and fuel flow. Produces 100% of rated power at throttle = 1. Should be in range 0 to 1 or slightly above 1. (vector, dimensionless)
fltcond|h (float) – Altitude (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
hybrid_power (float) – Shaft power added to LP shaft (vector, kW)
- Outputs:
thrust (float) – Thrust developed by the engine (vector, lbf)
fuel_flow (float) – Fuel flow consumed (vector, lbm/s)
surge_margin (float) – Surge margin (vector, percent)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
- openconcept.propulsion.N3.N3(num_nodes=1, plot=False)
Returns OpenMDAO component for thrust deck for optimized N+3 GTF without hybridization.
- Inputs:
throttle (float) – Engine throttle. Controls power and fuel flow. Produces 100% of rated power at throttle = 1. Should be in range 0 to 1 or slightly above 1. (vector, dimensionless)
fltcond|h (float) – Altitude (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
- Outputs:
thrust (float) – Thrust developed by the engine (vector, lbf)
fuel_flow (float) – Fuel flow consumed (vector, lbm/s)
surge_margin (float) – Surge margin (vector, percent)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
cfm56.py
- openconcept.propulsion.cfm56.CFM56(num_nodes=1, plot=False)
Returns OpenMDAO component for engine deck to model CFM56 turbofan.
- Inputs:
throttle (float) – Engine throttle. Controls power and fuel flow. Produces 100% of rated power at throttle = 1. Should be in range 0 to 1 or slightly above 1. (vector, dimensionless)
fltcond|h (float) – Altitude (vector, dimensionless)
fltcond|M (float) – Mach number (vector, dimensionless)
- Outputs:
thrust (float) – Thrust developed by the engine (vector, lbf)
fuel_flow (float) – Fuel flow consumed (vector, lbm/s)
T4 (float) – Turbine inlet temperature (vector, Rankine)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
generator.py
- class openconcept.propulsion.generator.SimpleGenerator(**kwargs)
Bases:
ExplicitComponent
A simple generator which transforms shaft power into electrical power.
- Inputs:
shaft_power_in (float) – Shaft power in to the generator (vector, W)
elec_power_rating (float) – Electric (not mech) design power (scalar, W)
- Outputs:
elec_power_out (float) – Electric power produced by the generator (vector, W)
heat_out (float) – Waste heat produced (vector, W)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_weight (float) – Weight of the component (scalar, kg)
component_sizing_margin (float) – Equal to 1 when producing full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
efficiency (float) – Shaft power efficiency. Sensible range 0.0 to 1.0 (default 1)
weight_inc (float) – Weight per unit rated power (default 1/5000, kg/W)
weight_base (float) – Base weight (default 0, kg)
cost_inc (float) – Cost per unit rated power (default 0.134228, USD/W)
cost_base (float) – Base cost (default 1 USD) B
motor.py
- class openconcept.propulsion.motor.SimpleMotor(**kwargs)
Bases:
ExplicitComponent
A simple motor which creates shaft power and draws electrical load.
- Inputs:
throttle (float) – Power control setting. Should be [0, 1]. (vector, dimensionless)
elec_power_rating (float) – Electric (not mech) design power. (scalar, W)
- Outputs:
shaft_power_out (float) – Shaft power output from motor (vector, W)
elec_load (float) – Electrical load consumed by motor (vector, W)
heat_out (float) – Waste heat produced (vector, W)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_weight (float) – Weight of the component (scalar, kg)
component_sizing_margin (float) – Equal to 1 when producing full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
efficiency (float) – Shaft power efficiency. Sensible range 0.0 to 1.0 (default 1)
weight_inc (float) – Weight per unit rated power (default 1/5000, kg/W)
weight_base (float) – Base weight (default 0, kg)
cost_inc (float) – Cost per unit rated power (default 0.134228, USD/W)
cost_base (float) – Base cost (default 1 USD) B
propeller.py
- class openconcept.propulsion.propeller.SimplePropeller(**kwargs)
Bases:
Group
This propeller is representative of a constant-speed prop.
The technology may be old. A general, empirical efficiency map for a constant speed turboprop is used for most of the flight regime. A static thrust coefficient map (from Raymer) is used for advance ratio < 0.2 (low speed). Linear interpolation from static thrust to dynamic thrust tables at J = 0.1 to 0.2.
- Inputs:
shaft_power_in (float) – Shaft power driving the prop (vector, W)
diameter (float) – Prop diameter (scalar, m)
rpm (float) – Prop RPM (vector, RPM)
fltcond|rho (float) – Air density (vector, kg/m**3)
fltcond|Utrue (float) – True airspeed (vector, m/s)
- Outputs:
thrust (float) – Propeller thrust (vector, N)
component_weight (float) – Prop weight (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
num_blades (int) – Number of propeller blades (default 4)
design_cp (float) – Design cruise power coefficient (cp)
design_J (float) – Design advance ratio (J)
splitter.py
- class openconcept.propulsion.splitter.PowerSplit(**kwargs)
Bases:
ExplicitComponent
A power split mechanism for mechanical or electrical power.
- Inputs:
power_in (float) – Power fed to the splitter. (vector, W)
power_rating (float) – Maximum rated power of the split mechanism. (scalar, W)
power_split_fraction – If
'rule'
is set to'fraction'
, sets percentage of input power directed to Output A (minus losses). (vector, dimensionless)power_split_amount – If
'rule'
is set to'fixed'
, sets amount of input power to Output A (minus losses). (vector, W)
- Outputs:
power_out_A (float) – Power sent to first output (vector, W)
power_out_B (float) – Power sent to second output (vector, W)
heat_out (float) – Waste heat produced (vector, W)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_weight (float) – Weight of the component (scalar, kg)
component_sizing_margin (float) – Equal to 1 when fed full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
rule (str) – Power split control rule to use; either
'fixed'
where a set amount of power is sent to Output A or'fraction'
where a fraction of the total power is sent to Output Aefficiency (float) – Component efficiency (default 1)
weight_inc (float) – Weight per unit rated power (default 0, kg/W)
weight_base (float) – Base weight (default 0, kg)
cost_inc (float) – Nonrecurring cost per unit power (default 0, USD/W)
cost_base (float) – Base cost (default 0 USD)
turboshaft.py
- class openconcept.propulsion.turboshaft.SimpleTurboshaft(**kwargs)
Bases:
ExplicitComponent
A simple turboshaft which generates shaft power consumes fuel.
This model assumes constant power specific fuel consumption (PSFC).
- Inputs:
shaft_power_rating (float) – Rated power of the turboshaft (scalar, W)
throttle (float) – Engine throttle. Controls power and fuel flow. Produces 100% of rated power at throttle = 1. Should be in range 0 to 1 or slightly above 1. (vector, dimensionless)
- Outputs:
shaft_power_out (float) – Shaft power produced by the engine (vector, W)
fuel_flow (float) – Fuel flow consumed (vector, kg/s)
component_cost (float) – Nonrecurring cost of the component (scalar, USD)
component_weight (float) – Weight of the component (scalar, kg)
component_sizing_margin (float) – Equal to 1 when producing full rated power (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
psfc (float) – Power specific fuel consumption. (default 0.6*1.69e-7 kg/W/s) Conversion from lb/hp/hr to kg/W/s is 1.69e-7
weight_inc (float) – Weight per unit rated power Override this with a reasonable value for your power class (default 0, kg/W)
weight_base (float) – Base weight This is a bad assumption for most turboshafts (default 0, kg)
cost_inc (float) – Nonrecurring cost per unit power (default 1.04, USD/W)
cost_base (float) – Base cost (default 0 USD)
openconcept.propulsion.systems
simple_all_electric.py
- class openconcept.propulsion.systems.simple_all_electric.AllElectricSinglePropulsionSystemWithThermal_Compressible(**kwargs)
Bases:
Group
This is an example model of the an electric propulsion system consisting of a constant-speed prop, motor, and battery. Thermal management is provided using a compressible 1D duct with heat exchanger.
- Inputs:
ac|propulsion|motor|rating (float) – The maximum rated continuous shaft power of the motor
ac|propulsion|propeller|diameter (float) – Diameter of the propeller
ac|weights|W_battery (float) – Battery weight
- Options:
num_nodes (float) – Number of analysis points to run (default 1)
- class openconcept.propulsion.systems.simple_all_electric.AllElectricSinglePropulsionSystemWithThermal_Incompressible(**kwargs)
Bases:
Group
This is an example model of the an electric propulsion system consisting of a constant-speed prop, motor, and battery. Thermal management is provided using a incompressible approximation of a 1D duct with heat exchanger.
- Inputs:
ac|propulsion|motor|rating (float) – The maximum rated continuous shaft power of the motor
ac|propulsion|propeller|diameter (float) – Diameter of the propeller
ac|weights|W_battery (float) – Battery weight
- Options:
num_nodes (float) – Number of analysis points to run (default 1)
simple_series_hybrid.py
- class openconcept.propulsion.systems.simple_series_hybrid.TwinSeriesHybridElectricPropulsionSystem(**kwargs)
Bases:
Group
This is an example model of a series-hybrid propulsion system. One motor draws electrical load from two sources in a fractional split| a battery pack, and a turbogenerator setup. The control inputs are the power split fraction and the motor throttle setting; the turboshaft throttle matches the power level necessary to drive the generator at the required power level.
Fuel flows and prop thrust should be fairly accurate. Heat constraints haven’t yet been incorporated.
The “pilot” controls thrust by varying the motor throttles from 0 to 100+% of rated power. She may also vary the percentage of battery versus fuel being used by varying the power_split_fraction
This module alone cannot produce accurate fuel flows, battery loads, etc. You must do the following, either with an implicit solver or with the optimizer: - Set eng1.throttle such that gen1.elec_power_out = hybrid_split.power_out_A
The battery does not track its own state of charge (SOC); it is connected to elec_load simply so that the discharge rate can be compared to the discharge rate capability of the battery. SOC and fuel flows should be time-integrated at a higher level (in the mission analysis codes)
Arrows show flow of information. In openConcept, mechanical power operates on a ‘push’ basis, while electrical load operates on a ‘pull’ basis. We reconcile these flows across an implicit gap by driving a residual to 0 using a solver.
eng1.throttle hybrid_split.power_split_fraction motor1.throttle || || || eng1 --shaft_power_out--> gen1 --elec_power_out--> {IMPLICIT GAP} <--power_out_B || <--elec_load-- motor1 --shaft_power_out --> prop1 -->thrust || hybrid_split <--elec_load ++ || batt1.elec_load <--power_out_A <--elec_load-- motor2 --shaft_power_out --> prop2 -->thrust V V || fuel_flow (integrate over time) elec_load (integrate over time to obtain SOC) motor2.throttle
- class openconcept.propulsion.systems.simple_series_hybrid.SeriesHybridElectricPropulsionSystem(**kwargs)
Bases:
Group
This is an example model of a series-hybrid propulsion system. One motor draws electrical load from two sources in a fractional split| a battery pack, and a turbogenerator setup. The control inputs are the power split fraction and the motor throttle setting; the turboshaft throttle matches the power level necessary to drive the generator at the required power level. Fuel flows and prop thrust should be fairly accurate. Heat constraints have not yet been incorporated.
- class openconcept.propulsion.systems.simple_series_hybrid.SingleSeriesHybridElectricPropulsionSystem(**kwargs)
Bases:
Group
This is an example model of a series-hybrid propulsion system. One motor draws electrical load from two sources in a fractional split| a battery pack, and a turbogenerator setup. The control inputs are the power split fraction and the motor throttle setting; the turboshaft throttle matches the power level necessary to drive the generator at the required power level.
Fuel flows and prop thrust should be fairly accurate. Heat constraints haven’t yet been incorporated.
The “pilot” controls thrust by varying the motor throttles from 0 to 100+% of rated power. She may also vary the percentage of battery versus fuel being used by varying the power_split_fraction.
This module alone cannot produce accurate fuel flows, battery loads, etc. You must do the following, either with an implicit solver or with the optimizer: - Set eng1.throttle such that gen1.elec_power_out = hybrid_split.power_out_A
The battery does not track its own state of charge (SOC); it is connected to elec_load simply so that the discharge rate can be compared to the discharge rate capability of the battery. SOC and fuel flows should be time-integrated at a higher level (in the mission analysis codes).
- Inputs:
ac|propulsion|engine|rating (float) – Turboshaft range extender power rating (scalar, kW)
ac|propulsion|propeller|diameter (float) – Propeller diameter (scalar, m)
ac|propulsion|motor|rating (float) – Motor power rating (scalar, kW)
ac|propulsion|generator|rating (float) – Range extender elec gen rating (scalar, kW)
ac|weights|W_battery (float) – Battery weight (scalar, kg)
TODO list all the control inputs
- Outputs:
thrust (float) – Propulsion system total thrust (vector, N)
fuel_flow (float) – Fuel flow consumed by the turboshaft (vector, kg/s)
- Options:
num_nodes (float) – Number of analysis points to run (default 1)
specific_energy (float) – Battery specific energy (default 300 Wh/kg)
simple_turboprop.py
- class openconcept.propulsion.systems.simple_turboprop.TurbopropPropulsionSystem(**kwargs)
Bases:
Group
This is an example model of the simplest possible propulsion system consisting of a constant-speed prop and a turboshaft.
This is the Pratt and Whitney Canada PT6A-66D with 4-bladed propeller used by the SOCATA-DAHER TBM-850.
- Inputs:
ac|propulsion|engine|rating (float) – The maximum rated shaft power of the engine (scalar, default 850 hp)
ac|propulsion|propeller|diameter (float) – Diameter of the propeller (scalar, default 2.3 m)
throttle (float) – Throttle for the turboshaft (vector)
fltcond|rho (float) – Air density (vector, kg/m**3)
fltcond|Utrue (float) – True airspeed (vector, m/s)
- Outputs:
thrust (float) – Thrust force (vector, N)
fuel_flow (float) – Fuel mass flow rate (vector, kg/s)
- Options:
num_nodes (float) – Number of analysis points to run (default 1)
- class openconcept.propulsion.systems.simple_turboprop.TwinTurbopropPropulsionSystem(**kwargs)
Bases:
Group
This is an example model multiple constant-speed props and turboshafts. These are two P&W Canada PT6A-135A with 4-bladed Hartzell propellers used by the Beechcraft King Air C90GT https://www.easa.europa.eu/sites/default/files/dfu/TCDS_EASA-IM-A-503_C90-Series%20issue%206.pdf
- Inputs:
ac|propulsion|engine|rating (float) – The maximum rated shaft power of the engine (scalar, default 850 hp)
ac|propulsion|propeller|diameter (float) – Diameter of the propeller (scalar, default 2.3 m)
throttle (float) – Throttle for the turboshaft (vector)
fltcond|rho (float) – Air density (vector, kg/m**3)
fltcond|Utrue (float) – True airspeed (vector, m/s)
propulsor_active (float) – 1 if second propulsor is active or 0 if not (vector)
- Outputs:
thrust (float) – Thrust force (vector, N)
fuel_flow (float) – Fuel mass flow rate (vector, kg/s)
- Options:
num_nodes (float) – Number of analysis points to run (default 1)
thermal_series_hybrid.py
- class openconcept.propulsion.systems.thermal_series_hybrid.TwinSeriesHybridElectricThermalPropulsionSystem(**kwargs)
Bases:
Group
This is an example model of a series-hybrid propulsion system. One motor draws electrical load from two sources in a fractional split| a battery pack, and a turbogenerator setup. The control inputs are the power split fraction and the motor throttle setting; the turboshaft throttle matches the power level necessary to drive the generator at the required power level.
Fuel flows and prop thrust should be fairly accurate. Heat constraints haven’t yet been incorporated.
The “pilot” controls thrust by varying the motor throttles from 0 to 100+% of rated power. She may also vary the percentage of battery versus fuel being used by varying the power_split_fraction
This module alone cannot produce accurate fuel flows, battery loads, etc. You must do the following, either with an implicit solver or with the optimizer: - Set eng1.throttle such that gen1.elec_power_out = hybrid_split.power_out_A
The battery does not track its own state of charge (SOC); it is connected to elec_load simply so that the discharge rate can be compared to the discharge rate capability of the battery. SOC and fuel flows should be time-integrated at a higher level (in the mission analysis codes)
Arrows show flow of information. In openConcept, mechanical power operates on a ‘push’ basis, while electrical load operates on a ‘pull’ basis. We reconcile these flows across an implicit gap by driving a residual to 0 using a solver.
eng1.throttle hybrid_split.power_split_fraction motor1.throttle || || || eng1 --shaft_power_out--> gen1 --elec_power_out--> {IMPLICIT GAP} <--power_out_B || <--elec_load-- motor1 --shaft_power_out --> prop1 -->thrust || hybrid_split <--elec_load ++ || batt1.elec_load <--power_out_A <--elec_load-- motor2 --shaft_power_out --> prop2 -->thrust V V || fuel_flow (integrate over time) elec_load (integrate over time to obtain SOC) motor2.throttle
- class openconcept.propulsion.systems.thermal_series_hybrid.TwinSeriesHybridElectricThermalPropulsionRefrigerated(**kwargs)
Bases:
Group
This is an example model of a series-hybrid propulsion system that uses active refrigeration to cool the electrical components. Other than the addition of a refrigerator in the coolant loop, this model is identical to TwinSeriesHybridElectricPropulsionSystem. One motor draws electrical load from two sources in a fractional split| a battery pack, and a turbogenerator setup. The control inputs are the power split fraction and the motor throttle setting; the turboshaft throttle matches the power level necessary to drive the generator at the required power level.
Fuel flows and prop thrust should be fairly accurate. Heat constraints haven’t yet been incorporated.
The “pilot” controls thrust by varying the motor throttles from 0 to 100+% of rated power. She may also vary the percentage of battery versus fuel being used by varying the power_split_fraction
This module alone cannot produce accurate fuel flows, battery loads, etc. You must do the following, either with an implicit solver or with the optimizer: - Set eng1.throttle such that gen1.elec_power_out = hybrid_split.power_out_A
The battery does not track its own state of charge (SOC); it is connected to elec_load simply so that the discharge rate can be compared to the discharge rate capability of the battery. SOC and fuel flows should be time-integrated at a higher level (in the mission analysis codes)
Arrows show flow of information. In openConcept, mechanical power operates on a ‘push’ basis, while electrical load operates on a ‘pull’ basis. We reconcile these flows across an implicit gap by driving a residual to 0 using a solver.
eng1.throttle hybrid_split.power_split_fraction motor1.throttle || || || eng1 --shaft_power_out--> gen1 --elec_power_out--> {IMPLICIT GAP} <--power_out_B || <--elec_load-- motor1 --shaft_power_out --> prop1 -->thrust || hybrid_split <--elec_load ++ || batt1.elec_load <--power_out_A <--elec_load-- motor2 --shaft_power_out --> prop2 -->thrust V V || fuel_flow (integrate over time) elec_load (integrate over time to obtain SOC) motor2.throttle
openconcept.thermal
battery_cooling.py
- class openconcept.thermal.battery_cooling.LiquidCooledBattery(**kwargs)
Bases:
Group
A battery with liquid cooling
- Inputs:
q_in (float) – Heat produced by the operating component (vector, W)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
T_in (float) – Instantaneous coolant inflow temperature (vector, K)
battery_weight (float) – Battery weight (scalar, kg)
n_cpb (float) – Number of cells long per “bandolier” actual count is 2x (scalar, default 82, Tesla)
t_channel (float) – Thickness (width) of the cooling channel in the bandolier (scalar, default 1mm)
T_initial (float) – Initial temperature of the battery (only required in thermal mass mode) (scalar, K)
duration (float) – Duration of mission segment, only required in unsteady mode
- Outputs:
T_out (float) – Instantaneous coolant outlet temperature (vector, K)
T (float) – Battery volume averaged temperature (vector, K)
T_core (float) – Battery core temperature (vector, K)
T_surface (float) – Battery surface temperature (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run
quasi_steady (bool) – Whether or not to treat the component as having thermal mass num_nodes : float The number of analysis points to run
coolant_specific_heat (float) – Specific heat of the coolant (J/kg/K) (default 3801, glycol/water)
fluid_k (float) – Thermal conductivity of the coolant (W/m/K)
nusselt (float) – Nusselt number of the coolant channel (default 7.54 for uniform surf temp)
cell_kr (float) – Thermal conductivity of the cell in the radial direction (W/m/k)
cell_diameter (float) – Battery diameter (default 21mm for 2170 cell)
cell_height (float) – Battery height (default 70mm for 2170 cell)
cell_mass (float) – Battery weight (default 70g for 2170 cell)
cell_specific_heat (float) – Mass average specific heat of the battery (default 900, LiIon cylindrical cell)
battery_weight_fraction (float) – Fraction of battery by weight that is cells (default 0.72 knocks down Tesla by a bit)
- class openconcept.thermal.battery_cooling.BandolierCoolingSystem(**kwargs)
Bases:
ExplicitComponent
Computes battery heat transfer for a parameteric battery based on Tesla’s Model 3 design.
Assumptions: Heat generated uniformly in the cell Weight per cell and thermal resistance stay constant even as specific energy varies parametrically (this means that cell count is constant with pack WEIGHT, not pack ENERGY as technology improves) Cylindrical cells attached to Tesla-style thermal ribbon Liquid cooling Heat transfer through axial direction only (not baseplate) 2170 cells (21 mm diameter, 70mm tall) Battery thermal model assumes unsteady cell temperature, quasi-steady temperature gradients
Note
See the
LiquidCooledBattery
for a group that already integrates this component with a battery.- Inputs:
q_in (float) – Heat generation rate in the battery (vector, W)
T_in (float) – Coolant inlet temperature (vector, K)
T_battery (float) – Volume averaged battery temperature (vector, K)
mdot_coolant (float) – Mass flow rate of coolant through the bandolier (vector, kg/s)
battery_weight (float) – Weight of the battery (overall). Default 100kg (scalar)
n_cpb (float) – Number of cells long per “bandolier” actual count is 2x (scalar, default 82, Tesla)
t_channel (float) – Thickness (width) of the cooling channel in the bandolier (scalar, default 1mm)
- Outputs:
dTdt (float) – Time derivative dT/dt (Tbar in the paper) (vector, K/s)
T_surface (float) – Surface temp of the battery (vector, K)
T_core (float) – Center temp of the battery (vector, K)
q (float) – Heat transfer rate from the motor to the fluid (vector, W)
T_out (float) – Outlet fluid temperature (vector, K)
- Options:
num_nodes (float) – The number of analysis points to run
coolant_specific_heat (float) – Specific heat of the coolant (J/kg/K) (default 3801, glycol/water)
fluid_k (float) – Thermal conductivity of the coolant (W/m/K)
nusselt (float) – Nusselt number of the coolant channel (default 7.54 for uniform surf temp)
cell_kr (float) – Thermal conductivity of the cell in the radial direction (W/m/k)
cell_diameter (float) – Battery diameter (default 21mm for 2170 cell)
cell_height (float) – Battery height (default 70mm for 2170 cell)
cell_mass (float) – Battery weight (default 70g for 2170 cell)
cell_specific_heat (float) – Mass average specific heat of the battery (default 900, LiIon cylindrical cell)
battery_weight_fraction (float) – Fraction of battery by weight that is cells (default 0.72 knocks down Tesla by a bit)
chiller.py
- class openconcept.thermal.chiller.LinearSelector(**kwargs)
Bases:
ExplicitComponent
Averages thermal parameters given bypass
- Inputs:
T_in_hot (float) – Incoming coolant temperature on the hot side (vector, K)
T_in_cold (float) – Incoming coolant temperature on the cold side (vector, K)
T_out_refrig_cold (float) – Coolant temperature in chiller outlet on the cold side (vector, K)
T_out_refrig_hot (float) – Coolant temperature in chiller outlet on the hot side (vector, K)
power_rating (float) – Rated electric power (scalar, W)
bypass (float) – Bypass parameter in range 0 - 1 (inclusive); 0 represents full refrig and no bypass, 1 all bypass no refrig (vector, None)
- Outputs:
T_out_cold (float) – Outgoing coolant temperature on the cold side (vector, K)
T_out_hot (float) – Outgoing coolant temperature on the hot side (vector, K)
elec_load (float) – Electrical load (vector, W)
- Options:
num_nodes (int) – The number of analysis points to run
- class openconcept.thermal.chiller.COPHeatPump(**kwargs)
Bases:
ExplicitComponent
Models heat transfer to coolant loop assuming zero thermal resistance.
- Inputs:
COP (float) – Coeff of performance set by optimizer (vector, dimensionless)
power_rating (float) – Shaft work in the refrigerator (scalar, W)
- Outputs:
q_in_1 (float) – Heat transfer rate INTO side 1 (vector, W)
q_in_2 (float) – Heat transfer rate INTO side 2 (vector, W)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, default 1)
- class openconcept.thermal.chiller.HeatPumpWeight(**kwargs)
Bases:
ExplicitComponent
Computes weight and power metrics for the vapor cycle machine. Defaults based on limited published data and guesswork.
- Inputs:
power_rating (float) – Rated electric power (scalar, W)
specific_power (float) – Power per weight (scalar, W/kg)
- Outputs:
component_weight (float) – Component weight (including coolants + motor) (scalar, kg)
- class openconcept.thermal.chiller.HeatPumpWithIntegratedCoolantLoop(**kwargs)
Bases:
Group
Models chiller with integrated coolant inputs and outputs on the hot and cold sides. Can bypass the chiller using linearly interpolated control points control.bypass_start and control.bypass_end outputs (0 is full refrigerator, 1 is full bypass, continuous in between).
- Inputs:
T_in_hot (float) – Incoming coolant temperature on the hot side (vector, K)
T_in_cold (float) – Incoming coolant temperature on the cold side (vector, K)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
power_rating (float) – Rated electric power, default 1 kW (scalar, W)
specific_power (float) – Heat pump power per weight, default 200 W/kg (scalar, W/kg)
eff_factor (float) – Heat pump Carnot efficiency factor, default 0.4 (scalar, None)
control.bypass_start (float) – Bypass value (in range 0-1) at beginning used for linear interpolation, 0 is full refrigerator and 1 is full bypass; must access via control component (i.e. with “control.bypass_start”) (scalar, None)
control.bypass_end (float) – Bypass value (in range 0-1) at end used for linear interpolation, 0 is full refrigerator and 1 is full bypass; must access via control component (i.e. with “control.bypass_end”) (scalar, None)
- Outputs:
T_out_hot (float) – Outgoing coolant temperature on the hot side (vector, K)
T_out_cold (float) – Outgoing coolant temperature on the cold side (vector, K)
component_weight (float) – Component weight (including coolants + motor) (scalar, kg)
elec_load (float) – Electrical load (vector, W)
- Options:
num_nodes (int) – The number of analysis points to run
specific_heat (float) – Specific heat of the coolant (scalar, J/kg/K, default 3801 glycol/water)
- class openconcept.thermal.chiller.COPExplicit(**kwargs)
Bases:
ExplicitComponent
Computes “soft” coefficient of performance (COP) that doesn’t blow up as T_h - T_c approaches zero
- Inputs:
T_c (float) – Cold side temperature (vector, K)
T_h (float) – Hot side temperature (vector, K)
eff_factor (float) – Efficiency factor (scalar, None)
- Outputs:
COP (float) – Coefficient of performance (vector, None)
- Options:
num_nodes (int) – The number of analysis points to run
ducts.py
- class openconcept.thermal.ducts.ExplicitIncompressibleDuct(**kwargs)
Bases:
ExplicitComponent
This is a very approximate model of a duct at incompressible speeds. Its advantage is low computational cost and improved convergence even at low speed. It CANNOT model flow with heat addition so it will generally be a conservative estimate on the cooling drag. Assumes the static pressure at the outlet duct = p_inf which may or may not be a good assumption.
- Inputs:
fltcond|Utrue (float) – True airspeed in the freestream (vector, m/s)
fltcond|rho (float) – Density in the freestream (vector, kg/m**3)
area_nozzle (float) – Cross-sectional area of the outlet nozzle (vector, m**2) Generally must be the narrowest portion of the duct for analysis to be valid
delta_p_hex (float) – Pressure drop across the heat exchanger (vector, Pa)
- Outputs:
mdot (float) – Mass flow rate through the duct (vector, kg/s)
drag (float) – Drag force on the duct positive rearwards (vector, N)
- Options:
num_nodes (float) – Number of analysis points to run
static_pressure_loss_factor (float) – Delta p / local dynamic pressure to model inlet and nozzle losses (vector, dimensionless) Default 0.00
gross_thrust_factor (float) – Fraction of total gross thrust to recover (aka Cfg) Accounts for duct losses, in particular nozzle losses Default 0.98
- class openconcept.thermal.ducts.TemperatureIsentropic(**kwargs)
Bases:
ExplicitComponent
Compute static temperature via isentropic relation
- Inputs:
Tt (float) – Total temperature (vector, K)
M (float) – Mach number (vector, dimensionless)
- Outputs:
T (float) – Static temperature (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
gamma (float) – Specific heat ratio (scalar, dimensionless)
- class openconcept.thermal.ducts.TotalTemperatureIsentropic(**kwargs)
Bases:
ExplicitComponent
Compute total temperature via isentropic relation
- Inputs:
T (float) – Static temperature (vector, K)
M (float) – Mach number (vector, dimensionless)
- Outputs:
Tt (float) – Static temperature (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
gamma (float) – Specific heat ratio (scalar, dimensionless)
- class openconcept.thermal.ducts.PressureIsentropic(**kwargs)
Bases:
ExplicitComponent
Compute static pressure via isentropic relation
- Inputs:
pt (float) – Total pressure (vector, Pa)
M (float) – Mach number (vector, dimensionless)
- Outputs:
p (float) – Static temperature (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
gamma (float) – Specific heat ratio (scalar, dimensionless)
- class openconcept.thermal.ducts.TotalPressureIsentropic(**kwargs)
Bases:
ExplicitComponent
Compute total pressure via isentropic relation
- Inputs:
p (float) – Static pressure (vector, Pa)
M (float) – Mach number (vector, dimensionless)
- Outputs:
pt (float) – Total pressure (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
gamma (float) – Specific heat ratio (scalar, dimensionless)
- class openconcept.thermal.ducts.DensityIdealGas(**kwargs)
Bases:
ExplicitComponent
Compute density from ideal gas law
- Inputs:
p (float) – Static pressure (vector, Pa)
T (float) – Static temperature (vector, K)
- Outputs:
rho (float) – Density (vector, kg/m**3)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
R (float) – Gas constant (scalar, J / kg / K)
- class openconcept.thermal.ducts.SpeedOfSound(**kwargs)
Bases:
ExplicitComponent
Compute speed of sound
- Inputs:
T (float) – Static temperature (vector, K)
- Outputs:
a (float) – Speed of sound (vector, m/s)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
R (float) – Gas constant (scalar, J / kg / K)
gamma (float) – Specific heat ratio (scalar dimensionless)
- class openconcept.thermal.ducts.MachNumberfromSpeed(**kwargs)
Bases:
ExplicitComponent
Compute Mach number from TAS and speed of sound
- Inputs:
Utrue (float) – True airspeed (vector, m/s)
a (float) – Speed of sound (vector, m/s)
- Outputs:
M (float) – Mach number (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.ducts.HeatAdditionPressureLoss(**kwargs)
Bases:
ExplicitComponent
Adds / removes heat and pressure gain / loss
- Inputs:
Tt_in (float) – Total temperature in (vector, K)
pt_in (float) – Total pressure in (vector, Pa)
mdot (float) – Mass flow (vector, kg/s)
delta_p (float) – Pressure gain / loss (vector, Pa)
pressure_recovery (float) – Total pressure gain / loss as a multiple (vector, dimensionless)
heat_in (float) – Heat addition (subtraction) rate (vector, W)
cp (float) – Specific heat (scalar, J/kg/K)
- Outputs:
Tt_out (float) – Total temperature out (vector, K)
pt_out (float) – Total pressure out (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.ducts.MassFlow(**kwargs)
Bases:
ExplicitComponent
Computes mass flow explicity from other parameters. Designed for use at the nozzle / min area point.
- Inputs:
M (float) – Mach number at this station (vector, dimensionless)
rho (float) – Density at this station (vector, kg/m**3)
area (float) – Flow cross sectional area at this station (vector, m**2)
a (float) – Speed of sound (vector, m/s)
- Outputs:
mdot (float) – Mass flow rate (vector, kg/s)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.ducts.DuctExitPressureRatioImplicit(**kwargs)
Bases:
ImplicitComponent
Compute duct exit pressure ratio based on total pressure and ambient pressure
- Inputs:
p_exit (float) – Exit static pressure (vector, Pa)
pt (float) – Total pressure (vector, Pa)
- Outputs:
nozzle_pressure_ratio (float) – Computed nozzle pressure ratio (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.ducts.DuctExitMachNumber(**kwargs)
Bases:
ExplicitComponent
Compute duct exit Mach number based on nozzle pressure ratio
- Inputs:
nozzle_pressure_ratio (float) – Computed nozzle pressure ratio (vector, dimensionless)
- Outputs:
M (float) – Computed nozzle Mach number(vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
gamma (float) – Specific heat ratio (scalar, dimensionless)
- class openconcept.thermal.ducts.NetForce(**kwargs)
Bases:
ExplicitComponent
Compute net force based on inlet and outlet pressures and velocities
- Inputs:
mdot (float) – Mass flow rate (vector, kg/s)
Utrue_inf (float) – Freestream true airspeed (vector, m/s)
p_inf (float) – Static pressure in the free stream. (vector, Pa)
area_nozzle (float) – Nozzle cross sectional area (vector, m**2)
p_nozzle (float) – Static pressure at the nozzle. Equal to p_inf unless choked (vector, Pa)
rho_nozzle (float) – Density at the nozzle (vector, kg/m**3)
- Outputs:
F_net (float) – Overall net force (positive is forward thrust) (vector, N)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.ducts.Inlet(**kwargs)
Bases:
Group
This group takes in ambient flight conditions and computes total quantities for downstream use
- Inputs:
T (float) – Temperature (vector, K)
p (float) – Ambient static pressure (vector, Pa)
Utrue (float) – True airspeed (vector, m/s)
- Outputs:
Tt (float) – Total temperature (vector, K)
pt (float) – Total pressure (vector, Pa)
- Options:
num_nodes (int) – Number of conditions to analyze
- class openconcept.thermal.ducts.DuctStation(**kwargs)
Bases:
Group
A ‘normal’ station in a duct flow.
- Inputs:
pt_in (float) – Upstream total pressure (vector, Pa)
Tt_in (float) – Upstream total temperature (vector, K)
mdot (float) – Mass flow (vector, kg/s)
delta_p (float) – Pressure gain (loss) at this station (vector, Pa)
heat_in (float) – Heat addition (loss) rate at this station (vector, W)
- Outputs:
pt_out (float) – Downstream total pressure (vector, Pa)
Tt_out (float) – Downstream total temperature (vector, K)
- Options:
num_nodes (int) – Number of conditions to analyze
- class openconcept.thermal.ducts.NozzlePressureLoss(**kwargs)
Bases:
ExplicitComponent
This group adds proportional pressure loss to the nozzle component
- Inputs:
pt_in (float) – Total pressure upstream of the nozzle (vector, Pa)
mdot (float) – Mass flow (vector, kg/s)
area (float) – Nozzle cross sectional area (vector, m**2)
dynamic_pressure_loss_factor (float) – Total pressure loss as a fraction of dynamic pressure
- Outputs:
pt (float) – Total pressure downstream of the nozzle (vector, Pa)
- Options:
num_nodes (int) – Number of conditions to analyze
- class openconcept.thermal.ducts.OutletNozzle(**kwargs)
Bases:
Group
- This group is designed to be the farthest downstream point in a ducted heat exchanger model.
Mass flow is set based on the upstream total pressure and ambient static pressure.
- Inputs:
p_exit (float) – Exit static pressure. Normally set to ambient flight pressure (vector, Pa)
pt (float) – Total pressure upstream of the nozzle (vector, Pa)
Tt (float) – Total temperature upstream of the nozzle (vector, K)
area (float) – Nozzle cross sectional area (vector, m**2)
- Outputs:
mdot (float) – Mass flow (vector, kg/s)
- Options:
num_nodes (int) – Number of conditions to analyze
- class openconcept.thermal.ducts.ImplicitCompressibleDuct(**kwargs)
Bases:
Group
Ducted heat exchanger with compressible flow assumptions
- class openconcept.thermal.ducts.ImplicitCompressibleDuct_ExternalHX(**kwargs)
Bases:
Group
Ducted heat exchanger with compressible flow assumptions
heat_exchanger.py
- class openconcept.thermal.heat_exchanger.OffsetStripFinGeometry(**kwargs)
Bases:
ExplicitComponent
Computes geometric and solid parameters of a offset strip fin plate-fin heat exchanger.
Geometric parameters as published by Manglik and Bergles, ‘Heat Transfer and Pressure Drop Correlations for the Rectangular Offset Strip Fin Compact Heat Exchanger’, Experimental Thermal and Fluid Science, 1995 DOI https://doi.org/10.1016/0894-1777(94)00096-Q
- Inputs:
channel_width_hot (float) – Width of each finned flow channel on the hot side (scalar, m)
channel_height_hot (float) – Height of each finned flow channel on the hot side (scalar, m)
fin_length_hot (float) – Length of each offset strip fin on the hot side (scalar, m)
channel_width_cold (float) – Width of each finned flow channel on the cold side (scalar, m)
channel_height_cold (float) – Height of each finned flow channel on the cold side (scalar, m)
fin_length_cold (float) – Length of each offset strip fin on the cold side (scalar, m)
fin_thickness (float) – Thickness of fin material (scalar, m)
plate_thickness (float) – Thickness of plate divider material (scalar, m)
case_thickness (float) – Thickness of the outer case material (scalar, m)
n_wide_cold (float) – Number of channels wide (cold side) (scalar, dimensionless)
n_long_cold (float) – Number of fins long (cold side) (scalar, dimensionless)
n_tall (float) – Number of times to stack the hot/cold combo (scalar, dimensionless)
material_rho (float) – Density of the heat exchanger material (scalar, kg/m**3)
- Outputs:
component_weight (float) – Weight / mass of the heat exchanger material (scalar, kg)
length_overall (float) – Overall heat exchanger length as viewed from cold side (scalar, m)
height_overall (float) – Overall heat exhcanger height (scalar, m)
width_overall (float) – Overall heat exchanger width as viewed from cold side (scalar, m)
frontal_area (float) – Frontal area of the heat exchanger (cold side) (scalar, m**2)
xs_area_cold (float) – Cross-sectional flow area of the cold side (scalar, m**2)
heat_transfer_area_cold (float) – Total heat transfer surface area of the cold side (scalar, m**2)
dh_cold (float) – Hydraulic diameter of the cold side flow channels (scalar, m)
fin_area_ratio_cold (float) – Ratio of fin area to total heat transfer area (scalar, dimensionless)
contraction_ratio_cold (float) – Ratio of flow xs area to total xs area of the cold side (scalar, dimensionless)
alpha_cold (float) – Ratio of fin channel width to height on the cold side (scalar, dimensionless)
delta_cold (float) – Ratio of fin thickness to length on the cold side (scalar, dimensionless)
gamma_cold (float) – Ratio of fin thickness to flow width on the cold side (scalar, dimensionless)
xs_area_hot (float) – Cross-sectional flow area of the hot side (scalar, m**2)
heat_transfer_area_hot (float) – Total heat transfer surface area of the hot side (scalar, m**2)
dh_hot (float) – Hydraulic diameter of the hot side flow channels (scalar, m)
fin_area_ratio_hot (float) – Ratio of fin area to total heat transfer area (scalar, dimensionless)
contraction_ratio_hot (float) – Ratio of flow xs area to total xs area of the hot side (scalar, dimensionless)
alpha_hot (float) – Ratio of fin channel width to height on the hot side (scalar, dimensionless)
delta_hot (float) – Ratio of fin thickness to length on the hot side (scalar, dimensionless)
gamma_hot (float) – Ratio of fin thickness to flow width on the hot side (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.OffsetStripFinData(**kwargs)
Bases:
ExplicitComponent
Computes Fanning friction factor f and Coburn j factor for offset strip fin geometry Correlations from empirical data published by Manglik and Bergles, ‘Heat Transfer and Pressure Drop Correlations for the Rectangular Offset Strip Fin Compact Heat Exchanger’, Experimental Thermal and Fluid Science, 1995 DOI https://doi.org/10.1016/0894-1777(94)00096-Q Equations 34 and 35
- Inputs:
Re_dh_cold (float) – Hydraulic diameter reynolds number of the cold side (vector, dimensionless)
alpha_cold (float) – Ratio of fin channel width to height on the cold side (scalar, dimensionless)
delta_cold (float) – Ratio of fin thickness to length on the cold side (scalar, dimensionless)
gamma_cold (float) – Ratio of fin thickness to flow width on the cold side (scalar, dimensionless)
Re_dh_hot (float) – Hydraulic diameter reynolds number of the hot side (vector, dimensionless)
alpha_hot (float) – Ratio of fin channel width to height on the hot side (scalar, dimensionless)
delta_hot (float) – Ratio of fin thickness to length on the hot side (scalar, dimensionless)
gamma_hot (float) – Ratio of fin thickness to flow width on the hot side (scalar, dimensionless)
- Outputs:
j_cold (float) – Colburn j factor for cold side (vector, dimensionless)
f_cold (float) – Fanning friction factor for cold side (vector, dimensionless)
j_hot (float) – Colburn j factor for hot side (vector, dimensionless)
f_hot (float) – Fanning friction factor for hot side (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.HydraulicDiameterReynoldsNumber(**kwargs)
Bases:
ExplicitComponent
Computes Re_dh for both sides of a heat exchanger
- Inputs:
mdot_cold (float) – Mass flow rate of the cold side (vector, kg/s)
mu_cold (float) – Dynamic viscosity of the cold side fluid (scalar, kg/m/s)
xs_area_cold (float) – Cross-sectional flow area of the cold side (scalar, m**2)
dh_cold (float) – Hydraulic diameter of the cold side flow channels (scalar, m)
mdot_hot (float) – Mass flow rate of the hot side (vector, kg/s)
mu_hot (float) – Dynamic viscosity of the hot side fluid (scalar, kg/m/s)
xs_area_hot (float) – Cross-sectional flow area of the hot side (scalar, m**2)
dh_hot (float) – Hydraulic diameter of the hot side flow channels (scalar, m)
- Outputs:
Re_dh_cold (float) – Reynolds number based on the hydraulic diameter, cold side (vector, dimensionless)
Re_dh_hot (float) – Reynolds number based on the hydraulic diameter, hot side (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.NusseltFromColburnJ(**kwargs)
Bases:
ExplicitComponent
Computes Nu from the Colburn j factor, Re, and Pr (mu, cp, k). Nu = j * Redh * (cp mu / k) ^(1/3)
- Inputs:
Re_dh_cold (float) – Reynolds number based on the hydraulic diameter, cold side (vector, dimensionless)
j_cold (float) – Colburn j factor (vector, dimensionless)
k_cold (float) – Thermal conductivity of the cold side fluid (scalar, W/m/K)
mu_cold (float) – Dynamic viscosity of the cold side fluid (scalar, kg/m/s)
cp_cold (float) – Specific heat at constant pressure, cold side (scalar, J/kg/K)
Re_dh_hot (float) – Reynolds number based on the hydraulic diameter, hot side (vector, dimensionless)
j_hot (float) – Colburn j factor (vector, dimensionless)
k_hot (float) – Thermal conductivity of the hot side fluid (scalar, W/m/K)
mu_hot (float) – Dynamic viscosity of the hot side fluid (scalar, kg/m/s)
cp_hot (float) – Specific heat at constant pressure, hot side (scalar, J/kg/K)
- Outputs:
Nu_dh_cold (float) – Hydraulic diameter Nusselt number (vector, dimensionless)
Nu_dh_hot (float) – Hydraulic diameter Nusselt number (vector, dimensionless
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.ConvectiveCoefficient(**kwargs)
Bases:
ExplicitComponent
Computes h from Nu_Dh (hydraulic diam Nusselt number), Dh (hyd diam), and k (thermal conductivity)
- Inputs:
Nu_dh_cold (float) – Hydraulic diameter Nusselt number (vector, dimensionless)
dh_cold (float) – Hydraulic diameter of the cold side flow channels (scalar, m)
k_cold (float) – Thermal conductivity of the cold side fluid (scalar, W/m/K)
Nu_dh_cold (float) – Hydraulic diameter Nusselt number (vector, dimensionless)
dh_cold (float) – Hydraulic diameter of the cold side flow channels (scalar, m)
k_cold (float) – Thermal conductivity of the cold side fluid (scalar, W/m/K)
- Outputs:
h_conv_cold (float) – Convective heat transfer coefficient (vector, dimensionless)
h_conv_hot (float) – Convective heat transfer coefficient (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.FinEfficiency(**kwargs)
Bases:
ExplicitComponent
Computes overall heat transfer efficiency eta_0 including fin efficiency This accounts for the actual heat transfer being less than if the temperature of the fin were uniform. If conduction is not perfect, temperature drop along the fin results in less than unity efficiency.
Method described in Fundamentals of Heat and Mass Transfer 6th Edition (Incropera and DeWitt)
- Inputs:
h_conv_cold (float) – Convective heat transfer coefficient (vector, dimensionless)
fin_area_ratio_cold (float) – Ratio of fin area to total heat transfer area (scalar, dimensionless)
channel_height_cold (float) – Height of each finned flow channel on the cold side (scalar, m)
h_conv_hot (float) – Convective heat transfer coefficient (vector, dimensionless)
fin_area_ratio_hot (float) – Ratio of fin area to total heat transfer area (scalar, dimensionless)
channel_height_hot (float) – Height of each finned flow channel on the hot side (scalar, m)
fin_thickness (float) – Thickness of fin material (scalar, m)
material_k (float) – Thermal conductivity of fin material (scalar, W/m/K)
- Outputs:
eta_overall_cold (float) – Overall heat transfer efficiency including fin efficiency (vector, dimensionless)
eta_overall_hot (float) – Overall heat transfer efficiency including fin efficiency (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.UAOverall(**kwargs)
Bases:
ExplicitComponent
Computes overall heat transfer coefficient for a heat exchanger
Method from Kays and London and Incropera and DeWitt
- Inputs:
h_conv_cold (float) – Convective heat transfer coefficient (vector, W/m**2/K)
heat_transfer_area_cold (float) – Total cold-side heat transfer area (scalar, m**2)
eta_overall_cold (float) – Overall thermal efficiency for the cold side (vector, dimensionless)
h_conv_hot (float) – Convective heat transfer coefficient (vector, W/m**2/K)
heat_transfer_area_hot (float) – Total hot-side heat transfer area (scalar, m**2)
eta_overall_hot (float) – Overall thermal efficiency for the colhotd side (vector, dimensionless)
- Outputs:
UA_overall (float) – Inverse overall thermal resistance for the entire heat exchanger (vector, W/K)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
fouling_factor_hot (float) – Fouling factor, hot side (scalar, m**2 K / W)
fouling_factor_cold (float) – Fouling factor, cold side (scalar, m**2 K /W)
- class openconcept.thermal.heat_exchanger.NTUMethod(**kwargs)
Bases:
ExplicitComponent
Computes number of thermal units and maximum possible heat transfer.
Method described in Incropera and DeWitt and Kays and London
- Inputs:
UA_overall (float) – Overall inverse thermal resistance (vector, W/K)
mdot_cold (float) – Mass flow rate, cold side (vector, kg/s)
T_in_cold (float) – Inlet temperature, cold side (vector, K)
cp_cold (float) – Specific heat at constant pressure, cold side (scalar, J/kg/K)
mdot_hot (float) – Mass flow rate, cold side (vector, kg/s)
T_in_hot (float) – Inlet temperature, hot side (vector, K)
cp_hot (float) – Specific heat at constant pressure, cold side (scalar, J/kg/K)
- Outputs:
NTU (float) – Number of thermal units (vector, dimensionless)
heat_max (float) – Maximum possible heat transfer (vector, W)
C_ratio (float) – The ratio of the maximum mdot*cp to the maximum (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.CrossFlowNTUEffectiveness(**kwargs)
Bases:
ExplicitComponent
Computes the heat transfer effectiveness of a crossflow heat exchanger
Expression from Kays and London
- Inputs:
NTU (float) – Number of thermal units (vector, dimensionless)
C_ratio (float) – Ratio of mdot * cp _min to _max (vector, dimensionless)
- Outputs:
effectiveness (float) – Heat transfer effectiveness (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.NTUEffectivenessActualHeatTransfer(**kwargs)
Bases:
ExplicitComponent
Computes the actual heat transfer and outlet temperatures of a heat exchanger using the NTU-effectiveness method described in Kays and London and Incropera and DeWitt
- Inputs:
effectiveness (float) – Heat transfer effectiveness (vector, dimensionless)
heat_max (float) – Maximum possible heat transfer (vector, W)
- Outputs:
heat_transfer (float) – Actual heat transfer from hot to cold side (vector, W)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.OutletTemperatures(**kwargs)
Bases:
ExplicitComponent
Computes outlet temperatures of hot and cold streams, given mass flow rates, cp, and heat transfer
- Inputs:
heat_transfer (float) – Actual heat transfer from hot to cold side (vector, W)
mdot_cold (float) – Mass flow rate, cold side (vector, kg/s)
T_in_cold (float) – Inlet temperature, cold side (vector, K)
cp_cold (float) – Specific heat at constant pressure, cold side (scalar, J/kg/K)
mdot_hot (float) – Mass flow rate, cold side (vector, kg/s)
T_in_hot (float) – Inlet temperature, hot side (vector, K)
cp_hot (float) – Specific heat at constant pressure, cold side (scalar, J/kg/K)
- Outputs:
T_out_cold (float) – Outlet temperature, cold side (vector, K)
T_out_hot (float) – Outlet temperature, hot side (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.PressureDrop(**kwargs)
Bases:
ExplicitComponent
Computes total pressure drop in the hot and cold streams
Method and estimated parameters from Kays and London
- Inputs:
length_overall (float) – Overall length of the cold side flowpath (scalar, m)
width_overall (float) – Overall length of the hot side flowpath (scalar, m)
f_cold (float) – Fanning friction factor (vector, dimensionless)
mdot_cold (float) – Mass flow rate, cold side (vector, kg/s)
rho_cold (float) – Inlet density, cold side (vector, kg/m**3)
dh_cold (float) – Hydraulic diameter of the cold side flow channels (scalar, m)
xs_area_cold (float) – Cross-sectional flow area of the cold side (scalar, m**2)
f_hot (float) – Fanning friction factor (vector, dimensionless)
mdot_hot (float) – Mass flow rate, hot side (vector, kg/s)
rho_hot (float) – Inlet density, hot side (vector, kg/m**3)
dh_hot (float) – Hydraulic diameter of the hot side flow channels (scalar, m)
xs_area_hot (float) – Cross-sectional flow area of the hot side (scalar, m**2)
- Outputs:
delta_p_cold (float) – Pressure drop, cold side. Negative is pressure drop (vector, Pa)
delta_p_hot (float) – Pressure drop, cold side. Negative is pressure drop (vector, Pa)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
Kc_cold (float) – Irreversible contraction loss coefficient (per Kays and London) (scalar, dimensionless)
Ke_cold (float) – Irreversible expansion loss coefficient (per Kays and London) (scalar, dimensionless)
Kc_hot (float) – Irreversible contraction loss coefficient (per Kays and London) (scalar, dimensionless)
Ke_hot (float) – Irreversible expansion loss coefficient (per Kays and London) (scalar, dimensionless)
- class openconcept.thermal.heat_exchanger.HXGroup(**kwargs)
Bases:
Group
A heat exchanger model for use with the duct models Note that there are many design variables defined as dvs which could be varied in optimization.
- Inputs:
mdot_cold (float) – Mass flow rate of the cold side (air) (vector, kg/s)
T_in_cold (float) – Inflow temperature of the cold side (air) (vector, K)
rho_cold (float) – Inflow density of the cold side (air) (vector, kg/m**3)
mdot_hot (float) – Mass flow rate of the hot side (liquid) (vector, kg/s)
T_in_hot (float) – Inflow temperature of the hot side (liquid) (vector, kg/s)
rho_hot (float) – Inflow density of the hot side (liquid) (vector, kg/m**3)
heat_pipe.py
- class openconcept.thermal.heat_pipe.HeatPipe(**kwargs)
Bases:
Group
Model for an ammonia heat pipe. After model has been run, make sure that the heat transfer is always less than the q_max output!
- Inputs:
T_evap (float) – Temperature of connection to evaporator end of heat pipe (vector, degC)
q (float) – Heat transferred from evaporator side to condenser side by heat pipe (vector, W)
length (float) – Heat pipe length (scalar, m)
inner_diam (float) – Inner diameter of heat pipe vapor/wick section (scalar, m)
n_pipes (float) – Number of heat pipes in parallel; non-integer values are nonphysical but maintained for gradient optimization purposes (scalar, dimensionless)
T_design (float) – Max temperature expected in heat pipe, used to compute weight (scalar, degC)
- Outputs:
q_max (float) – Maximum heat transfer possible by heat pipes before dry-out (vector, W)
T_cond (float) – Temperature of connection to condenser end of heat pipe (vector, degC)
weight (float) – Weight of heat pipe walls, excludes working fluid (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
length_evap (float) – Length of evaporator, default 0.25 m (scalar, m)
length_cond (float) – Length of condenser, default 0.25 m (scalar, m)
wall_conduct (float) – Thermal conductivity of wall material, default aluminum 7075 (scalar, W/(m-K))
wick_thickness (float) – Thickness of internal wick liner in heat pipe, default no wick (scalar, m)
wick_conduct (float) – Thermal conductivity of wick liner, default 4 (scalar, W/(m-K)) Note: default is from wick resistance on slide 21 of https://www.youtube.com/watch?v=JnS0ui8Pt64 and backs out thermal conductivity using an assumed thickness of ~0.005” (rough estimate based on the x-section picture)
yield_stress (float) – Yield stress of the heat pipe materia;, default 7075 aluminum (scalar, MPa)
rho_wall (float) – Density of the wall material, default 7075 aluminum (scalar, kg/m^3)
stress_safety_factor (float) – Factor of safety for the wall hoop stress, default 4 (scalar, dimensionless)
theta (float) – Tilt from vertical of heat pipe, default 0 deg; MUST be greater than or equal to 0 less than 90 (scalar, deg)
q_max_warn (float) – User will be warned if q input exceeds q_max_warn * q_max, default 0.75 (scalar, dimensionless)
- class openconcept.thermal.heat_pipe.HeatPipeThermalResistance(**kwargs)
Bases:
ExplicitComponent
Computes thermal resistance of a heat pipe with metal exterior, wicking liner, and vapor core.
- Inputs:
inner_diam (float) – Inner diameter of vapor/wick portion of heat pipe (scalar, m)
wall_thickness (float) – Thickness of outer metallic wall of heat pipe, default 1.25 mm (scalar, m)
q (float) – Heat transferred through the pipe per unit time, should always be positive; must be specified if vapor_resistance = True, otherwise unused (vector, W)
delta_T (float) – Vapor temperature drop from one end of the heat pipe to the other; must be specified if vapor_resistance = True, otherwise unused (vector, degC)
- Outputs:
thermal_resistance (float) – Effective thermal resistance of heat pipe, takes into account heat entering/exiting through pipe and wick boundary radially and traveling axially along the pipe (vector, K/W)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
length_evap (float) – Length of evaporator, default 0.25 m (scalar, m)
length_cond (float) – Length of condenser, default 0.25 m (scalar, m)
wall_conduct (float) – Thermal conductivity of wall material, default aluminum 7075 (scalar, W/(m-K))
wick_thickness (float) – Thickness of internal wick liner in heat pipe, default no wick (scalar, m)
wick_conduct (float) – Thermal conductivity of wick liner, default 4 (scalar, W/(m-K)) Note: default is from wick resistance on slide 21 of https://www.youtube.com/watch?v=JnS0ui8Pt64 and backs out thermal conductivity using an assumed thickness of ~0.005” (rough estimate based on the x-section picture)
vapor_resistance (bool) – Set to true to include vapor resistance (usually negligible) in calculation, default false. If set to true, the q and temp inputs MUST be connected
- class openconcept.thermal.heat_pipe.HeatPipeVaporTempDrop(**kwargs)
Bases:
ExplicitComponent
Inputs the vapor space temp drop due to pressure drop
This component is hard-coded on ammonia and uses a curve fit of the slope of the temp-pressure curve applicable from -10C to 100C operating temperatures
- Inputs:
q (float) – Heat transfer in the heat pipe (vector, W)
temp (float) – Mean temp of the heat pipe (vector, degC)
rho_vapor (float) – Vapor density (vector, kg/m3)
vapor_pressure (float) – Vapor pressure (vector, Pa)
inner_diam (float) – Inner diameter of the heat pipe (scalar, m)
length (float) – Length of the heat pipe (scalar, m)
- Outputs:
delta_T (float) – Temperature differential across the vapor phase (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run, default 1 (scalar, dimensionless)
Other options shouldn’t be adjusted since they’re for ammonia and there is a
hardcoded curve fit also for ammonia in the compute method
- class openconcept.thermal.heat_pipe.HeatPipeWeight(**kwargs)
Bases:
ExplicitComponent
Computes the weight of a heat pipe neglecting liquid/vapor weight. Uses a simple expression for hoop stress times a factor of safety.
- Inputs:
design_pressure (float) – The maximum design vapor pressure (scalar, MPa)
inner_diam (float) – Inner diameter of the heat pipe (scalar, m)
length (float) – Length of the heat pipe (scalar, m)
- Outputs:
heat_pipe_weight (float) – The material weight of the heat pipe tube (scalar, kg)
wall_thickness (float) – Thickness of heat pipe walls (scalar, m)
- Options:
yield_stress (float) – Yield stress of the heat pipe material in MPa
rho_wall (float) – Density of the wall material in kg/m3
stress_safety_factor (float) – Factor of safety for the wall hoop stress
- class openconcept.thermal.heat_pipe.AmmoniaProperties(**kwargs)
Bases:
Group
Computes properties of ammonia at liquid-vapor equilibrium as a function of temperature using a cubic interpolation of data here: https://en.wikipedia.org/wiki/Ammonia_(data_page)#Vapor%E2%80%93liquid_equilibrium_data
NOTE: Data is from -75 to 100 deg C, any temps outside this range may be inaccurate
- Inputs:
temp (float) – Temperature of ammonia liquid/vapor (vector, degC)
- Outputs:
rho_liquid (float) – Ammonia liquid density (vector, kg/m^3)
rho_vapor (float) – Ammonia vapor density (vector, kg/m^3)
vapor_pressure (float) – Ammonia vapor pressure (vector, kPa)
- Options:
num_nodes (int) – Number of analysis points to run, default 1 (scalar, dimensionless)
- class openconcept.thermal.heat_pipe.QMaxHeatPipe(**kwargs)
Bases:
Group
Computes the maximum possible heat transfer rate of an ammonia heat pipe. As a rule of thumb, the heat pipe should stay below 75% of this value.
- NOTE: This model uses experimental data to compute the ammonia surface tension
and liquid/vapor density, so it is invalid for any other working fluid. The walls are assumed to be 7075 Al with a factor of safety of 4
- Inputs:
inner_diam (float) – Inner diameter of heat pipe (scalar, m)
length (float) – Length of the heat pipe (scalar, m)
temp (float) – Average temperature in heat pipe (vector, degC)
design_temp (float) – Max design temperature of the heat pipe (scalar, degC)
- Outputs:
q_max (float) – Maximum heat transfer possible in heat pipe (vector, W)
heat_pipe_weight (float) – Weight of heat pipe walls (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points to run, default 1 (scalar, dimensionless)
theta (float) – Tilt from vertical, default 0 deg (scalar, deg)
yield_stress (float) – Yield stress of the heat pipe material in MPa
rho_wall (float) – Density of the wall material in kg/m3
stress_safety_factor (float) – Factor of safety for the wall hoop stress
- class openconcept.thermal.heat_pipe.QMaxAnalyticalPart(**kwargs)
Bases:
ExplicitComponent
Computes the analytical part of the Q max calculation. For the overall Q max calculation, use the QMaxHeatPipe group, not this component.
Equations from https://www.1-act.com/resources/heat-pipe-performance/. Surface tension data from page 16 of http://web.iiar.org/membersonly/PDF/CO/databook_ch2.pdf. Both accessed on Aug 9, 2022.
- Inputs:
inner_diam (float) – Inner diameter of heat pipe (scalar, m)
temp (float) – Average temperature in heat pipe (vector, K)
rho_liquid (float) – Density of working fluid in liquid form (vector, m)
rho_vapor (float) – Density of working fluid in vapor form (vector, m)
- Outputs:
q_max (float) – Maximum heat transfer possible in heat pipe (vector, W)
- Options:
num_nodes (int) – Number of analysis points to run, default 1 (scalar, dimensionless)
theta (float) – Tilt from vertical, default 0 deg (scalar, deg)
latent_heat (float) – Latent heat of vaporization, default ammonia 1,371,200 J/kg (scalar, J/kg)
surface_tension_base (float) – Surface tension at 0 deg C, default ammonia (in 0-50 deg C range) 0.026 N/m (scalar, N/m)
surface_tension_incr (float) – Surface tension sensitivity w.r.t. temperature (used for linear estimate), default ammonia (in 0-50 deg C range) -2.3e-4 N/m/degC (scalar, N/m/degC)
- class openconcept.thermal.heat_pipe.QMaxWarning(**kwargs)
Bases:
ExplicitComponent
Component to warn user if the heat transfer ever exceeds a specified fraction of Q max.
- Inputs:
q (float) – Heat transferred from evaporator side to condenser side by heat pipe (vector, W)
q_max (float) – Maximum heat transfer possible by heat pipes before dry-out (vector, W)
- Outputs:
None
- Options:
num_nodes (int) – Number of analysis points to run (scalar, dimensionless)
q_max_warn (float) – User will be warned if q input exceeds q_max_warn * q_max, default 0.75 (scalar, dimensionless)
hose.py
- class openconcept.thermal.hose.SimpleHose(**kwargs)
Bases:
ExplicitComponent
A coolant hose used to track pressure drop and weight in long hose runs.
- Inputs:
hose_diameter (float) – Inner diameter of the hose (scalar, m)
hose_length – Length of the hose (scalar, m)
hose_design_pressure – Max operating pressure of the hose (scalar, Pa)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
rho_coolant (float) – Coolant density (vector, kg/m3)
mu_coolant (float) – Coolant viscosity (scalar, kg/m/s)
- Outputs:
delta_p (float) – Pressure drop in the hose - positive is loss (vector, kg/s)
component_weight (float) – Weight of hose AND coolant (scalar, kg)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
hose_operating_stress (float) – Hoop stress at design pressure (Pa) set to 300 Psi equivalent per empirical data
hose_density (float) – Material density of the hose (kg/m3) set to 0.049 lb/in3 equivalent per empirical data
manifold.py
- class openconcept.thermal.manifold.FlowSplit(**kwargs)
Bases:
ExplicitComponent
Split incoming flow from one inlet into two outlets at a fractional ratio.
- Inputs:
mdot_in (float) – Mass flow rate of incoming fluid (vector, kg/s)
mdot_split_fraction (float) – Fraction of incoming mass flow directed to output A, must be in range 0-1 inclusive (vector, dimensionless)
- Outputs:
mdot_out_A (float) – Mass flow rate directed to first output (vector, kg/s)
mdot_out_B (float) – Mass flow rate directed to second output (vector, kg/s)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
- class openconcept.thermal.manifold.FlowCombine(**kwargs)
Bases:
ExplicitComponent
Combines two incoming flows into a single outgoing flow and does a weighted average of their temperatures based on the mass flow rate of each to compute the outlet temp.
- Inputs:
mdot_in_A (float) – Mass flow rate of fluid from first inlet, should be nonegative (vector, kg/s)
mdot_in_B (float) – Mass flow rate of fluid from second inlet, should be nonnegative (vector, kg/s)
T_in_A (float) – Temperature of fluid from first inlet (vector, K)
T_in_B (float) – Temperature of fluid from second inlet (vector, K)
- Outputs:
mdot_out (float) – Outgoing fluid mass flow rate (vector, kg/s)
T_out (float) – Outgoing fluid temperature (vector, K)
- Options:
num_nodes (int) – Number of analysis points (scalar, default 1)
motor_cooling.py
- class openconcept.thermal.motor_cooling.LiquidCooledMotor(**kwargs)
Bases:
Group
A component (heat producing) with thermal mass cooled by a cold plate.
- Inputs:
q_in (float) – Heat produced by the operating component (vector, W)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
T_in (float) – Instantaneous coolant inflow temperature (vector, K)
motor_weight (float) – Object mass (only required in thermal mass mode) (scalar, kg)
T_initial (float) – Initial temperature of the cold plate (only required in thermal mass mode) / object (scalar, K)
duration (float) – Duration of mission segment, only required in unsteady mode
power_rating (float) – Rated power of the motor (scalar, kW)
- Outputs:
T_out (float) – Instantaneous coolant outlet temperature (vector, K)
T (float) – Windings temperature (vector, K)
- Options:
motor_specific_heat (float) – Specific heat capacity of the object in J / kg / K (default 921 = aluminum)
coolant_specific_heat (float) – Specific heat capacity of the coolant in J / kg / K (default 3801, glycol/water)
num_nodes (int) – Number of analysis points to run
quasi_steady (bool) – Whether or not to treat the component as having thermal mass
case_cooling_coefficient (float) – Watts of heat transfer per square meter of case surface area per K temperature differential (default 1100 W/m^2/K)
- class openconcept.thermal.motor_cooling.MotorCoolingJacket(**kwargs)
Bases:
ExplicitComponent
Computes motor winding temperature assuming well-designed, high-power-density aerospace motor. This component is based on the following assumptions: - 2020 technology level - 200kW-1MW class inrunner PM motor - Liquid cooling of the stators - “Reasonable” coolant flow rates (component will validate this) - Thermal performance similiar to the Siemens SP200D motor
The component assumes a constant heat transfer coefficient based on the surface area of the motor casing (not counting front and rear faces) The MagniX Magni 250/500 and Siemens SP200D motors were measured using rough photogrammetry.
Magni250: 280kW rated power, ~0.559m OD, 0.2m case “depth” (along thrust axis) Magni500: 560kW rated power, ~0.652m OD, 0.4m case “depth” Siemens SP200D: 200kW rated power, ~0.63m OD, ~0.16 case “depth”
Based on these dimensions I assume 650kW per square meter of casing surface area. This includes only the cylindrical portion, not the front and rear motor faces.
Using a thermal FEM image of the SP200D, I estimate a temperature rise of 23K from coolant inlet temperature (~85C) to winding max temp (~108C) at the steady state operating point. With 95% efficiency at 200kW, this is about 1373 W / m^2 casing area / K. We’ll reduce that somewhat since this is a direct oil cooling system, and assume 1100 W/m^2/K instead.
Dividing 1.1 kW/m^2/K by 650kWrated/m^2 gives: 1.69e-3 kW / kWrated / K At full rated power and 95% efficiency, this is 29.5C steady state temp rise which the right order of magnitude.
Note
See the
LiquidCooledMotor
for a group that already integrates this component with an electric motor.- Inputs:
q_in (float) – Heat production rate in the motor (vector, W)
T_in (float) – Coolant inlet temperature (vector, K)
T (float) – Temperature of the motor windings (vector, K)
mdot_coolant (float) – Mass flow rate of the coolant (vector, kg/s)
power_rating (float) – Rated steady state power of the motor (scalar, W)
motor_weight (float) – Weight of electric motor (scalar, kg)
- Outputs:
dTdt (float) – Time derivative dT/dt (vector, K/s)
q (float) – Heat transfer rate from the motor to the fluid (vector, W)
T_out (float) – Outlet fluid temperature (vector, K)
- Options:
num_nodes (float) – The number of analysis points to run
coolant_specific_heat (float) – Specific heat of the coolant (J/kg/K) (default 3801, glycol/water)
case_cooling_coefficient (float) – Watts of heat transfer per square meter of case surface area per K temperature differential (default 1100 W/m^2/K)
case_area_coefficient (float) – rated motor power per square meter of case surface area (default 650,000 W / m^2)
motor_specific_heat (float) – Specific heat of the motor casing (J/kg/K) (default 921, alu)
pump.py
- class openconcept.thermal.pump.SimplePump(**kwargs)
Bases:
ExplicitComponent
A pump that circulates coolant against pressure. The default parameters are based on a survey of commercial airplane fuel pumps of a variety of makes and models.
- Inputs:
power_rating (float) – Maximum rated electrical power (scalar, W)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
rho_coolant (float) – Coolant density (vector, kg/m3)
delta_p (float) – Pressure rise provided by the pump (vector, kg/s)
- Outputs:
elec_load (float) – Electricity used by the pump (vector, W)
component_weight (float) – Pump weight (scalar, kg)
component_sizing_margin (float) – Fraction of total power rating used via elec_load (vector, dimensionless)
- Options:
num_nodes (int) – Number of analysis points to run (sets vec length; default 1)
efficiency (float) – Pump electrical + mech efficiency. Sensible range 0.0 to 1.0 (default 0.35)
weight_base (float) – Base weight of pump, doesn’t change with power rating (default 0)
weight_inc (float) – Incremental weight of pump, scales linearly with power rating (default 1/450 kg/W)
thermal.py
- class openconcept.thermal.thermal.PerfectHeatTransferComp(**kwargs)
Bases:
ExplicitComponent
Models heat transfer to coolant loop assuming zero thermal resistance.
- Inputs:
T_in (float) – Incoming coolant temperature (vector, K)
q (float) – Heat flow into fluid stream; positive is heat addition (vector, W)
mdot_coolant (float) – Coolant mass flow (vector, kg/s)
- Outputs:
T_out (float) – Outgoing coolant temperature (vector, K)
T_average (float) – Average coolant temperature (vector K)
- Options:
num_nodes (int) – Number of analysis points to run (scalar, default 1)
specific_heat (float) – Specific heat of the coolant (scalar, J/kg/K, default 3801 glycol/water)
- class openconcept.thermal.thermal.ThermalComponentWithMass(**kwargs)
Bases:
ExplicitComponent
Computes thermal residual of a component with heating, cooling, and thermal mass
- Inputs:
q_in (float) – Heat generated by the component (vector, W)
q_out (float) – Heat to waste stream (vector, W)
mass (float) – Thermal mass (scalar, kg)
- Outputs:
dTdt (float) – First derivative of temperature (vector, K/s)
- Options:
specific_heat (float) – Specific heat capacity of the object in J / kg / K (default 921 = aluminum)
num_nodes (int) – The number of analysis points to run
- class openconcept.thermal.thermal.ThermalComponentMassless(**kwargs)
Bases:
ImplicitComponent
Computes thermal residual of a component with heating, cooling, and thermal mass
- Inputs:
q_in (float) – Heat generated by the component (vector, W)
q_out (float) – Heat to waste stream (vector, W)
- Outputs:
T_object (float) – Object temperature (vector, K/s)
- Options:
num_nodes (int) – The number of analysis points to run
- class openconcept.thermal.thermal.ConstantSurfaceTemperatureColdPlate_NTU(**kwargs)
Bases:
ExplicitComponent
Computes heat rejection to fluid stream of a microchannel cold plate with uniform temperature
- Inputs:
T_in (float) – Coolant inlet temperature (vector, K)
T_surface (float) – Temperature of the cold plate (vector, K)
mdot_coolant (float) – Mass flow rate of the coolant (vector, kg/s)
channel_length (float) – Length of each microchannel (scalar, m)
channel_width (float) – Width of each microchannel (scalar, m)
channel_height (float) – Height of each microchannel (scalar, m)
n_parallel (float) – Number of fluid channels (scalar, dimensionless)
- Outputs:
q (float) – Heat transfer rate from the plate to the fluid (vector, W)
T_out (float) – Outlet fluid temperature (vector, K)
- Options:
num_nodes (int) – The number of analysis points to run
fluid_rho (float) – Coolant density in kg/m**3 (default 0.997, water)
fluid_k (float) – Thermal conductivity of the fluid (W/m/K) (default 0.405, glycol/water)
nusselt (float) – Hydraulic diameter Nusselt number of the coolant in the channels (default 7.54 for constant temperature infinite parallel plate)
specific_heat (float) – Specific heat of the coolant (J/kg/K) (default 3801, glycol/water)
- class openconcept.thermal.thermal.LiquidCooledComp(**kwargs)
Bases:
Group
A component (heat producing) with thermal mass cooled by a cold plate.
- Inputs:
q_in (float) – Heat produced by the operating component (vector, W)
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
T_in (float) – Instantaneous coolant inflow temperature (vector, K)
mass (float) – Object mass (only required in thermal mass mode) (scalar, kg)
T_initial (float) – Initial temperature of the cold plate (only required in thermal mass mode) / object (scalar, K)
duration (float) – Duration of mission segment, only required in unsteady mode
channel_width (float) – Width of coolant channels (scalar, m)
channel_height (float) – Height of coolant channels (scalar, m)
channel_length (float) – Length of coolant channels (scalar, m)
n_parallel (float) – Number of identical coolant channels (scalar, dimensionless)
- Outputs:
T_out (float) – Instantaneous coolant outlet temperature (vector, K)
T (float) – Object temperature (vector, K)
- Options:
specific_heat_object (float) – Specific heat capacity of the object in J / kg / K (default 921 = aluminum)
specific_heat_coolant (float) – Specific heat capacity of the coolant in J / kg / K (default 3801, glycol/water)
num_nodes (int) – Number of analysis points to run
quasi_steady (bool) – Whether or not to treat the component as having thermal mass
- class openconcept.thermal.thermal.CoolantReservoir(**kwargs)
Bases:
Group
A reservoir of coolant capable of buffering temperature
- Inputs:
mdot_coolant (float) – Coolant mass flow rate (vector, kg/s)
T_in (float) – Coolant inflow temperature (vector, K)
mass (float) – Object mass (only required in thermal mass mode) (scalar, kg)
T_initial (float) – Initial temperature of the coolant reservoir(only required in thermal mass mode) / object (scalar, K)
duration (float) – Time step of each mission segment (one for each segment) (scalar, s) If a single segment is provided (by default) this variable will be called just ‘dt’ only required in thermal mass mode
- Outputs:
T_out (float) – Coolant outlet temperature (vector, K)
- Options:
num_nodes (int) – Number of analysis points to run
- class openconcept.thermal.thermal.CoolantReservoirRate(**kwargs)
Bases:
ExplicitComponent
Computes dT/dt of a coolant reservoir based on inflow and current temps and flow rate
- Inputs:
T_in (float) – Coolant stream in (vector, K)
T_out (float) – Temperature of the reservoir (vector, K)
mass (float) – Total quantity of coolant (scalar, kg)
mdot_coolant (float) – Mass flow rate of the coolant (vector, kg/s)
- Outputs:
dTdt (float) – First derivative of temperature (vector, K/s)
- Options:
num_nodes (int) – The number of analysis points to run
openconcept.utilities
constants.py
dict_indepvarcomp.py
- class openconcept.utilities.dict_indepvarcomp.DictIndepVarComp(data_dict, **kwargs)
Bases:
IndepVarComp
Create indep variables from an external file with a Python dictionary.
Outputs from this component are read from a Python dictionary and given a name matching their location in the data tree.
For example, let’s assume we have stored some data about a vehicle in a dictionary which can be accessed using the Python expression
vehicledata['wheels']['diameter']
. The structured_name in this case is'wheels|diameter'
.The user instantiates a component as
DictIndepVarComp(vehicledata)
and adds an output as follows:component_instance.add_output_from_dict('wheels|diameter')
.Outputs are created after initialization and are user-defined.
- _data_dict
A structured dictionary object with input data to read from.
- Type:
dict
- __init__(data_dict, **kwargs)
Initialize the component and store the data dictionary as an attribute.
- Parameters:
data_dict (dict) – A structured dictionary object with input data to read from
- add_output_from_dict(structured_name, separator='|', **kwargs)
Create a new output based on data from the data dictionary
- Parameters:
structured_name (string) – A string matching the file structure in the dictionary object Pipe symbols indicate treeing down one level Example ‘aero:CLmax_flaps30’ accesses data_dict[‘aero’][‘CLmax_flaps30’]
separator (string) – Separator to tree down into the data dict. Default ‘|’ probably shouldn’t be overridden
- class openconcept.utilities.dict_indepvarcomp.DymosDesignParamsFromDict(data_dict, dymos_traj)
Bases:
object
Create Dymos parameters from an external file with a Python dictionary.
- _data_dict
A structured dictionary object with input data to read from.
- Type:
dict
- __init__(data_dict, dymos_traj)
Initialize the component and store the data dictionary as an attribute.
- Parameters:
data_dict (dict) – A structured dictionary object with input data to read from
dymos_traj (Dymos trajectory) – A Dymos trajectory object with phases already added
- add_output_from_dict(structured_name, separator='|', opt=False, dynamic=False, **kwargs)
Create a new output based on data from the data dictionary
- Parameters:
structured_name (string) – A string matching the file structure in the dictionary object Pipe symbols indicate treeing down one level Example ‘aero:CLmax_flaps30’ accesses data_dict[‘aero’][‘CLmax_flaps30’]
separator (string) – Separator to tree down into the data dict. Default ‘|’ probably shouldn’t be overridden
dvlabel.py
- class openconcept.utilities.dvlabel.DVLabel(vars_list)
Bases:
ExplicitComponent
Helper component that is needed when variables must be passed directly from input to output of an element with no other component in between.
This component is adapted from Justin Gray’s pyCycle software.
- Inputs:
Inputs to this component are set upon initialization.
- Outputs:
Outputs from this component are set upon initialization.
- Options:
vars_list (iterable) – A list of lists. One outer list entry per variable. Format: [[‘input name’,’output name’,’val’,’units’]]
linearinterp.py
- class openconcept.utilities.linearinterp.LinearInterpolator(**kwargs)
Bases:
ExplicitComponent
Create a linearly interpolated set of points including two end points
- Inputs:
start_val (float) – Starting value (scalar; units set from “units” option)
end_val (float) – Ending value (scalar; units set from “units” option)
- Outputs:
vec (float) – Vector of linearly interpolated points (scalar; units set from “units” opt)
- Options:
units (str, None) – Units for inputs and outputs
num_nodes (int) – Number of linearly interpolated points to produce (minimum/default 2)
selector.py
- class openconcept.utilities.selector.SelectorComp(**kwargs)
Bases:
ExplicitComponent
Selects an output from the set of user-specified inputs based on the selector input at runtime.
For example, if the inputs argument is [‘A’, ‘B’, ‘C’], then a selector value of 0 would return input ‘A’, a selector value of 1 would return input ‘B’, and a selector value of 2 would return input ‘C’.
In practice, the inputs may be vectors. Suppose ‘A’ has the value [10, 10, 10, 10], ‘B’ has the value [5, 5, 5, 5], and ‘C’ has the value [7, 7, 7, 7], then a selector of [0, 1, 2, 1] would make the ‘result’ output take on the value [10, 5, 7, 5].
If the selector value is out of range, a warning is raised and zero is returned where the selector value is invalid.
- Inputs:
selector (int) – Selects which input to route to the output based on the order they were specified; must be in the range [0, # of inputs) and if the data type is not already an int, it is rounded to the nearest integer value (vector, default 0)
user defined inputs (any) – The data inputs must be specified by the user using the input_names option and all inputs must have the same units, if none are specified error is raised (vector)
- Outputs:
result (same as selected input) – The same value as the input selected by the selector input (vector)
- Options:
num_nodes (int) – Length of all inputs and outputs (scalar, default 1)
input_names (iterable of strings) – List of the names of the the user-specified inputs
units (string) – OpenMDAO-style units of the inputs; all inputs should have these units
visualization.py
- openconcept.utilities.visualization.plot_trajectory_grid(cases, x_var, x_unit, y_vars, y_units, phases, x_label=None, y_labels=None, grid_layout=[5, 2], marker='o', savefig=None, figsize=None)
Plots multiple trajectories against each other Cases is a list of OpenMDAO CaseReader cases which act like OpenMDAO problems
openconcept.utilities.math
add_subtract_comp.py
Definition of the Add/Subtract Component.
- class openconcept.utilities.math.add_subtract_comp.AddSubtractComp(output_name=None, input_names=None, vec_size=1, length=1, val=1.0, scaling_factors=None, **kwargs)
Bases:
ExplicitComponent
Compute a vectorized element-wise addition or subtraction.
Use the add_equation method to define any number of add/subtract relations User defines the names of the input and output variables using add_equation(output_name=’my_output’, input_names=[‘a’,’b’, ‘c’, …])
\[result = a * \textrm{scaling factor}_a + b * \textrm{scaling factor}_b + c * \textrm{scaling factor}_c + ...\]- where:
all inputs shape (vec_size, n)
b is of shape (vec_size, n)
c is of shape (vec_size, n)
Result is of shape (vec_size, n)
All input vectors must be of the same shape, specified by the options ‘vec_size’ and ‘length’. Alternatively, a list of ‘vec_size’ can be provided where the entries are all either the same number, or 1. This allows a vector quantity to be multiplied / divided by scalar(s). Use scaling factor -1 for subtraction.
- _add_systems
List of equation systems to be initialized with the system.
- Type:
list
- __init__(output_name=None, input_names=None, vec_size=1, length=1, val=1.0, scaling_factors=None, **kwargs)
Allow user to create an addition/subtracton system with one-liner.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_size (int) – Length of the first dimension of the input and output vectors (i.e number of rows, or vector length for a 1D vector) Default is 1 Alternatively, if a list, must be all same number, or 1. e.g. [1, 9, 9, 9]. Must be same length as # of inputs
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 which results in input/output vectors of size (vec_size,)
scaling_factors (iterable of numeric) – Scaling factors to apply to each input. Use [1,1,…] for addition, [1,-1,…] for subtraction Must be same length as input_names Default is None which results in a scaling factor of 1 on each input (element-wise addition)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
**kwargs (str) – Any other arguments to pass to the addition system (same as add_output method for ExplicitComponent) Examples include units (str or None), desc (str)
- add_equation(output_name, input_names, vec_size=1, length=1, val=1.0, units=None, res_units=None, desc='', lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=None, scaling_factors=None)
Add an addition/subtraction relation.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_size (int) – Length of the first dimension of the input and output vectors (i.e number of rows, or vector length for a 1D vector) Default is 1 Alternatively, if a list, must be all same number, or 1. e.g. [1, 9, 9, 9]. Must be same length as # of inputs
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 which results in input/output vectors of size (vec_size,)
scaling_factors (iterable of numeric) – Scaling factors to apply to each input. Use [1,1,…] for addition, [1,-1,…] for subtraction Must be same length as input_names Default is None which results in a scaling factor of 1 on each input (element-wise addition)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
units (str or None) – Units in which the output variables will be provided to the component during execution. Default is None, which means it has no units.
res_units (str or None) – Units in which the residuals of this output will be given to the user when requested. Default is None, which means it has no units.
desc (str) – description of the variable.
lower (float or list or tuple or ndarray or Iterable or None) – lower bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no lower bound. Default is None.
upper (float or list or tuple or ndarray or or Iterable None) – upper bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no upper bound. Default is None.
ref (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 1. Default is 1.
ref0 (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 0. Default is 0.
res_ref (float or ndarray) – Scaling parameter. The value in the user-defined res_units of this output’s residual when the scaled value is 1. Default is 1.
- add_output()
Use add_equation instead of add_output to define equation systems.
- setup()
Set up the addition/subtraction system at run time.
- compute(inputs, outputs)
Compute the element wise addition or subtraction of inputs using numpy + operator.
- Parameters:
inputs (Vector) – unscaled, dimensional input variables read via inputs[key]
outputs (Vector) – unscaled, dimensional output variables read via outputs[key]
combine_split_comp.py
Definition of the Vector Combiner/Splitter Component.
- class openconcept.utilities.math.combine_split_comp.VectorConcatenateComp(output_name=None, input_names=None, vec_sizes=None, length=1, val=1.0, **kwargs)
Bases:
ExplicitComponent
Concatenate one or more sets of more than one vector into one or more output vectors.
Use the add_relation method to define any number of concat relationships User defines the names of the input and output variables using add_relation(output_name=’my_output’, input_names=[‘a’,’b’, ‘c’, …],vec_sizes=[10,10,5,…])
For each relation declared: All input vectors must be of the same second dimension, specified by the option ‘length’. The number of vec_sizes given must match the number of inputs declared. Input units must be compatible with output units for each relation.
- _add_systems
List of equation systems to be initialized with the system.
- Type:
list
- __init__(output_name=None, input_names=None, vec_sizes=None, length=1, val=1.0, **kwargs)
Allow user to create an addition/subtracton system with one-liner.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_sizes (iterable of int) – (required) Lengths of the first dimension of each input vector (i.e number of rows, or vector length for a 1D vector)
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 (i.e. a vector of scalars)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
**kwargs (str) – Any other arguments to pass to the addition system (same as add_output method for ExplicitComponent) Examples include units (str or None), desc (str)
- add_relation(output_name, input_names, vec_sizes, length=1, val=1.0, units=None, res_units=None, desc='', lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=None)
Add a concatenation relation.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_sizes (iterable of int) – (required) Lengths of the first dimension of each input vector (i.e number of rows, or vector length for a 1D vector)
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 (i.e. a vector of scalars)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
units (str or None) – Units in which the output variables will be provided to the component during execution. Default is None, which means it has no units.
res_units (str or None) – Units in which the residuals of this output will be given to the user when requested. Default is None, which means it has no units.
desc (str) – description of the variable.
lower (float or list or tuple or ndarray or Iterable or None) – lower bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no lower bound. Default is None.
upper (float or list or tuple or ndarray or or Iterable None) – upper bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no upper bound. Default is None.
ref (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 1. Default is 1.
ref0 (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 0. Default is 0.
res_ref (float or ndarray) – Scaling parameter. The value in the user-defined res_units of this output’s residual when the scaled value is 1. Default is 1.
- add_output()
Use add_relation instead of add_output to define concatenate relations.
- setup()
Set up the component at run time from both add_relation and __init__.
- compute(inputs, outputs)
Concatenate the vector(s) using numpy.
- Parameters:
inputs (Vector) – unscaled, dimensional input variables read via inputs[key]
outputs (Vector) – unscaled, dimensional output variables read via outputs[key]
- class openconcept.utilities.math.combine_split_comp.VectorSplitComp(output_names=None, input_name=None, vec_sizes=None, length=1, val=1.0, **kwargs)
Bases:
ExplicitComponent
Splits one or more vectors into one or more sets of 2+ vectors.
Use the add_relation method to define any number of splitter relationships User defines the names of the input and output variables using add_relation(output_names=[‘a’,’b’, ‘c’, …],input_name=’my_input’,vec_sizes=[10,10,5,…])
For each relation declared: All output vectors must be of the same second dimension, specified by the option ‘length’. The first dim length of the input vector must equal the sum of the first dim lengths of the output vectors. The number of vec_sizes given must match the number of outputs declared. Input units must be compatible with output units for each relation.
- _add_systems
List of equation systems to be initialized with the system.
- Type:
list
- __init__(output_names=None, input_name=None, vec_sizes=None, length=1, val=1.0, **kwargs)
Allow user to create an addition/subtracton system with one-liner.
- Parameters:
output_names (iterable of str) – (required) names of the output (split) variables in this component’s namespace.
input_name (str) – (required) names of the input variable for this system
vec_sizes (iterable of int) – (required) Lengths of the first dimension of each input vector (i.e number of rows, or vector length for a 1D vector)
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 (i.e. a vector of scalars)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
**kwargs (str) – Any other arguments to pass to the addition system (same as add_output method for ExplicitComponent) Examples include units (str or None), desc (str)
- add_relation(output_names, input_name, vec_sizes, length=1, val=1.0, units=None, res_units=None, desc='', lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=None)
Add a concatenation relation.
- Parameters:
output_names (iterable of str) – (required) names of the output (split) variables in this component’s namespace.
input_name (str) – (required) names of the input variable for this system
vec_sizes (iterable of int) – (required) Lengths of the first dimension of each input vector (i.e number of rows, or vector length for a 1D vector)
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 (i.e. a vector of scalars)
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
units (str or None) – Units in which the output variables will be provided to the component during execution. Default is None, which means it has no units.
res_units (str or None) – Units in which the residuals of this output will be given to the user when requested. Default is None, which means it has no units.
desc (str) – description of the variable.
lower (float or list or tuple or ndarray or Iterable or None) – lower bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no lower bound. Default is None.
upper (float or list or tuple or ndarray or or Iterable None) – upper bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no upper bound. Default is None.
ref (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 1. Default is 1.
ref0 (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 0. Default is 0.
res_ref (float or ndarray) – Scaling parameter. The value in the user-defined res_units of this output’s residual when the scaled value is 1. Default is 1.
- add_output()
Use add_relation instead of add_output to define split relations.
- setup()
Set up the component at run time from both add_relation and __init__.
- compute(inputs, outputs)
Split the vector(s) using numpy.
- Parameters:
inputs (Vector) – unscaled, dimensional input variables read via inputs[key]
outputs (Vector) – unscaled, dimensional output variables read via outputs[key]
derivatives.py
- openconcept.utilities.math.derivatives.first_deriv(dts, q, n_segments=1, n_simpson_intervals_per_segment=2, order=4)
This method differentiates a quantity over time using fourth order finite differencing
A “segment” is defined as a portion of the quantity vector q with a constant delta t (or delta x, etc). This routine is designed to be used in the context of Simpson’s rule integration where segment endpoints are coincident in time and n_points_per_seg = 2*n + 1
- Inputs:
dts (float) – “Time” step between points for each segment. Length of dts must equal n_segments (vector) Note that this is half the corresponding Simpson integration timestep
q (float) – Quantity to be differentiated (vector) Total length of q = n_segments * n_points_per_seg
n_segments (int) – Number of segments to differentiate. Each segment has constant dt (scalar)
n_simpson_intervals_per_segment (int) – Number of Simpson’s rule intervals per segment. Minimum is 2. (scalar) The number of points per segment = 2*n + 1
order (int) – Order of accuracy (choose 2 or 4)
- Returns:
dqdt – Derivative of q with respect to time (vector)
- Return type:
float
- openconcept.utilities.math.derivatives.first_deriv_partials(dts, q, n_segments=1, n_simpson_intervals_per_segment=2, order=4)
This method provides the Jacobian of a temporal first derivative
A “segment” is defined as a portion of the quantity vector q with a constant delta t (or delta x, etc). This routine is designed to be used in the context of Simpson’s rule integration where segment endpoints are coincident in time and n_points_per_seg = 2*n + 1
- Inputs:
dts (float) – “Time” step between points for each segment. Length of dts must equal n_segments (vector) Note that this is half the corresponding Simpson integration timestep
q (float) – Quantity to be differentiated (vector) Total length of q = n_segments * n_points_per_seg
n_segments (int) – Number of segments to differentiate. Each segment has constant dt (scalar)
n_simpson_intervals_per_segment (int) – Number of Simpson’s rule intervals per segment. Minimum is 2. (scalar) The number of points per segment = 2*n + 1
- Returns:
d_dqdt_dq (list of floats) – Jacobian of the temporal derivative vector dqdt with respect its quantity vector q (NxN sparse array) returned in CSR format as rowidx, colidx, data
d_dqdt_ddt (list of list of floats) – Jacobians of the temporal derivative vector dqdt with respect to segment timestep dt[i] (Nx1 sparse arrays) returned in CSR format as rowidxs[i], colidxs[i], data[i]
- class openconcept.utilities.math.derivatives.FirstDerivative(**kwargs)
Bases:
ExplicitComponent
This component differentiates a vector using a second or fourth order finite difference approximation
- Inputs:
q (float) – The vector quantity to differentiate. q is defined consistent with the Simpson’s Rule formulation used in the rest of this package q is comprised of multiple “segments” or phases with equal temporal spacing. The endpoints of each segment correspond to exacty the same timepoint as the endpoint of the next section q is of length nn_tot where nn_tot = n_segments x(2 x num_intervals + 1) Each segment of q is of length nn_seg = (2 x num_intervals + 1)
integrals.py
- openconcept.utilities.math.integrals.bdf3_cache_matrix(n, all_bdf=False)
This implements the base block Jacobian of the BDF3 method. BDF3 is third order accurate and suitable for stiff systems.
The first couple of points are handled by 3rd-order offset finite difference stencils.
- openconcept.utilities.math.integrals.multistep_integrator(q0, dqdt, dts, tri_mat, repeat_mat, segment_names=None, segments_to_count=None, partials=True)
This implements the base block Jacobian of the BDF3 method. BDF3 is third order accurate and suitable for stiff systems. A central-difference approximation and BDF2 are used for the first couple of points, so strictly speaking this method is only second order accurate.
- class openconcept.utilities.math.integrals.Integrator(**kwargs)
Bases:
ExplicitComponent
Integrates rate variables implicitly. Add new integrated quantities by using the add_integrand method. “q” inputs here are illustrative only.
- Inputs:
duration (float) – The duration of the integration interval (can also use dt) (scalar)
dq_dt (float) – Rate to integrate (vector)
q_initial (float) – Starting value of quantity (scalar)
- Outputs:
q (float) – The vector quantity corresponding integral of dqdt over time Will have units ‘rate_units’ / ‘diff_units’
q_final (float) – The final value of the vector (scalar) Useful for connecting the end of one integrator to beginning of another
- Options:
num_nodes (int) – num_nodes = 2N + 1 where N = num_intervals The total length of the vector q is 2N + 1
diff_units (str) – The units of the integrand (none by default)
method (str) – Numerical method (default ‘bdf3’; alternatively, ‘simpson’)
time_setup (str) – Time configuration (default ‘dt’) ‘dt’ creates input ‘dt’ ‘duration’ creates input ‘duration’ ‘bounds’ creates inputs ‘t_initial’, ‘t_final’
- add_integrand(name, rate_name=None, start_name=None, end_name=None, val=0.0, start_val=0.0, units=None, rate_units=None, zero_start=False, final_only=False, lower=-1e+30, upper=1e+30)
Add a new integrated variable q = integrate(dqdt) + q0 This will add an output with the integrated quantity, an output with the final value, an input with the rate source, and an input for the initial quantity.
- Parameters:
name (str) – The name of the integrated variable to be created.
rate_name (str) – The name of the input rate (default name”_rate”)
start_name (str) – The name of the initial value input (default value name”_initial”)
end_name (str) – The name of the end value output (default value name”_final”)
units (str or None) – Units for the integrated quantity (or inferred automatically from rate_units)
rate_units (str or None) – Units of the rate (can be inferred automatically from units)
zero_start (bool) – If true, eliminates start value input and always begins from zero (default False)
final_only (bool) – If true, only integrates final quantity, not all the intermediate points (default False)
val (float) – Default value for the integrated output (default 0.0) Can be scalar or shape num_nodes
start_val (float) – Default value for the initial value input (default 0.0)
upper (float) – Upper bound on integrated quantity
lower (float) – Lower bound on integrated quantity
- class openconcept.utilities.math.integrals.OldIntegrator(**kwargs)
Bases:
ExplicitComponent
This component integrates a vector using a BDF3 formulation with 2nd order startup.
- Inputs:
dqdt (float) – The vector quantity to integrate. Length of the vector = (2 * num_intervals + 1) * num_segments
segment|dt (float) – The timestep of “segment” (scalar) 1 per segment
q_initial (float) – Starting value of the quantity (scalar)
- Outputs:
q (float) – The vector quantity corresponding integral of dqdt over time Will have units ‘rate_units’ / ‘diff_units’
q_final (float) – The final value of the vector (scalar) Useful for connecting the end of one integrator to beginning of another
- Options:
segment_names (list) – A list of str with the names of the individual segments By default, if no segment_names are provided, one segment will be assumed and segment|dt will just be named “dt”
segments_to_count (list) – A list of str with the names of segments to be included in the integration. By default, ALL segments will be included.
num_nodes (int) – num_nodes = 2N + 1 where N = num_intervals The total length of the vector q is n_segments x (2N + 1)
quantity_units (str) – The units of quantity being integrated (not the rate)
diff_units (str) – The units of the integrand (none by default)
rate_units (str) – The units of the rate being integrated
method (str) – Numerical method (default ‘bdf3’; alternatively, ‘simpson)
zero_start (bool) – If True, disables q_initial input (default False)
final_only (bool) – If True, disables q output (q_final only) (default False)
time_setup (str) – Time configuration (default ‘dt’) ‘dt’ creates input ‘dt’ ‘duration’ creates input ‘duration’ ‘bounds’ creates inputs ‘t_initial’, ‘t_final’
max_min_comp.py
- class openconcept.utilities.math.max_min_comp.MaxComp(**kwargs)
Bases:
ExplicitComponent
Takes in a vector and outputs a scalar that is the value of the maximum element in the input.
- Inputs:
array (any type that supports comparison (<, >, etc.)) – Array of which the maximum is found (vector)
- Outputs:
max (same as data type of input array) – The maximum value of the input array (scalar)
- Options:
num_nodes (int) – Length of all inputs and outputs (scalar, default 1)
units (string) – OpenMDAO-style units of input and output
- class openconcept.utilities.math.max_min_comp.MinComp(**kwargs)
Bases:
ExplicitComponent
Takes in a vector and outputs a scalar that is the value of the minimum element in the input.
- Inputs:
array (any type that supports comparison (<, >, etc.)) – Array of which the minimum is found (vector)
- Outputs:
min (same as data type of input array) – The minimum value of the input array (scalar)
- Options:
num_nodes (int) – Length of all inputs and outputs (scalar, default 1)
units (string) – OpenMDAO-style units of input and output
multiply_divide_comp.py
Definition of the Element Multiply Component.
- class openconcept.utilities.math.multiply_divide_comp.ElementMultiplyDivideComp(output_name=None, input_names=None, vec_size=1, length=1, val=1.0, scaling_factor=1, divide=None, input_units=None, **kwargs)
Bases:
ExplicitComponent
Compute a vectorized element-wise multiplication and/or division.
Use the add_equation method to define any number of mult/div relations User defines the names of the input and output variables using add_equation(output_name=’my_output’, input_names=[‘a’,’b’, ‘c’, …], divide=[False,False,True,…])
\[result = (a * b / c ....) * \textrm{scaling factor}\]- where:
all inputs shape (vec_size, n)
b is of shape (vec_size, n)
c is of shape (vec_size, n)
Result is of shape (vec_size, n)
All input vectors must be of the same shape, specified by the options ‘vec_size’ and ‘length’. Alternatively, a list of ‘vec_size’ can be provided where the entries are all either the same number, or 1. This allows a vector quantity to be multiplied / divided by scalar(s). Use scaling factor -1 for subtraction.
- _add_systems
List of equation systems to be initialized with the system.
- Type:
list
- __init__(output_name=None, input_names=None, vec_size=1, length=1, val=1.0, scaling_factor=1, divide=None, input_units=None, **kwargs)
Allow user to create an multiplication system with one-liner.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_size (int) – Length of the first dimension of the input and output vectors (i.e number of rows, or vector length for a 1D vector) Default is 1 Alternatively, if a list, must be all same number, or 1. e.g. [1, 9, 9, 9]. Must be same length as # of inputs
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 which results in input/output vectors of size (vec_size,)
scaling_factor (numeric) – Scaling factor to apply to the whole system Default is 1
divide (iterable of bool or None) – True to use division operator, False to use mult operator Default is None which results in mult of every input Length is same as number of inputs
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
input_units (iterable of str) – Units for each of the input vectors in order. Output units will be dimensionally consistent.
**kwargs (str) – Any other arguments to pass to the addition system (same as add_output method for ExplicitComponent) Examples include units (str or None), desc (str)
- 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)
Add a multiplication relation.
- Parameters:
output_name (str) – (required) name of the result variable in this component’s namespace.
input_names (iterable of str) – (required) names of the input variables for this system
vec_size (int) – Length of the first dimension of the input and output vectors (i.e number of rows, or vector length for a 1D vector) Default is 1 Alternatively, if a list, must be all same number, or 1. e.g. [1, 9, 9, 9]. Must be same length as # of inputs
length (int) – Length of the second dimension of the input and ouptut vectors (i.e. number of columns) Default is 1 which results in input/output vectors of size (vec_size,)
scaling_factor (numeric) – Scaling factor to apply to the whole system Default is 1
divide (iterable of bool or None) – True to use division operator, False to use mult operator Default is None which results in mult of every input Length is same as number of inputs
val (float or list or tuple or ndarray) – The initial value of the variable being added in user-defined units. Default is 1.0.
input_units (iterable of str) – Units for each of the input vectors in order. Output units will be dimensionally consistent.
res_units (str or None) – Units in which the residuals of this output will be given to the user when requested. Default is None, which means it has no units.
desc (str) – description of the variable.
lower (float or list or tuple or ndarray or Iterable or None) – lower bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no lower bound. Default is None.
upper (float or list or tuple or ndarray or or Iterable None) – upper bound(s) in user-defined units. It can be (1) a float, (2) an array_like consistent with the shape arg (if given), or (3) an array_like matching the shape of val, if val is array_like. A value of None means this output has no upper bound. Default is None.
ref (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 1. Default is 1.
ref0 (float or ndarray) – Scaling parameter. The value in the user-defined units of this output variable when the scaled value is 0. Default is 0.
res_ref (float or ndarray) – Scaling parameter. The value in the user-defined res_units of this output’s residual when the scaled value is 1. Default is 1.
tags (list of str) – Tags to apply to the output variable
- add_output()
Use add_equation instead of add_output to define equation systems.
- setup()
Set up the addition/subtraction system at run time.
- compute(inputs, outputs)
Compute the element wise multiplication or division of inputs using numpy.
- Parameters:
inputs (Vector) – unscaled, dimensional input variables read via inputs[key]
outputs (Vector) – unscaled, dimensional output variables read via outputs[key]
Development Roadmap
- Known issues to be addressed include:
No distinction right now between calibrated, equivalent, indicated airspeeds (compressibility effects) in the standard atmosphere
Limited validation of the takeoff performance code (it is hard to find actual CLmax and drag polar data!)
- Future ideas include:
Unifying the ODE integration math with NASA’s Dymos toolkit
Adding locations to weights to be able to include stability constraints and trim OpenAeroStruct aerodynamic models
Incorporate OpenVSP for visualizations of the configuration
Publications
2022
Eytan J. Adler and Joaquim R.R.A. Martins, Efficient Aerostructural Wing Optimization Considering Mission Analysis, Journal of Aircraft, 2022 DOI: 10.2514/1.c037096. PDF is available here.
@article{Adler2022d,
author = {Adler, Eytan J. and Martins, Joaquim R. R. A.},
title = {Efficient Aerostructural Wing Optimization Considering Mission Analysis},
doi = {10.2514/1.c037096},
issn = {1533-3868},
journal = {Journal of Aircraft},
month = {December},
publisher = {American Institute of Aeronautics and Astronautics},
year = {2022}
}
Mahmoud Fouda, Eytan J. Adler, Jasper H. Bussemaker, Joaquim R. R. A. Martins, D. F. Kurtulus, Luca Boggero, and Björn Nagel, Automated Hybrid Propulsion Model Construction for Conceptual Aircraft Design and Optimization, 33rd Congress of the International Council of the Aeronautical Sciences, Stockholm, Sweden, September 2022, PDF is available here.
@inproceedings{Fouda2022,
author = {Mahmoud Fouda and Eytan J. Adler and Jasper H. Bussemaker and Joaquim R. R. A. Martins and D. F. Kurtulus and Luca Boggero and Bj\"orn Nagel},
title = {Automated Hybrid Propulsion Model Construction for Conceptual Aircraft Design and Optimization},
booktitle = {33rd Congress of the International Council of the Aeronautical Sciences},
month = {September},
year = {2022}
}
Eytan J. Adler, Benjamin J. Brelje, and Joaquim R.R.A. Martins, Thermal Management System Optimization for a Parallel Hybrid Aircraft Considering Mission Fuel Burn, Aerospace, 9(5), 2022 DOI: 10.3390/aerospace9050243. PDF is available here.
@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}
}
Eytan J. Adler and Joaquim R.R.A. Martins, Aerostructural wing design optimization considering full mission analysis, 2022 AIAA SciTech Forum, San Diego, CA, January 2022, DOI: 10.2514/6.2022-0382. PDF is available here.
@inproceedings{Adler2022a,
author = {Eytan J. Adler and Joaquim R. R. A. Martins},
title = {Aerostructural wing design optimization considering full mission analysis},
booktitle = {AIAA SciTech Forum},
doi = {10.2514/6.2022-0382},
month = {January},
year = {2022}
}
2019
Benjamin J. Brelje, John P. Jasa, Joaquim R.R.A. Martins, and Justin S. Gray, Development of a Conceptual-Level Thermal Management System Design Capability in OpenConcept, NATO Research Symposium on Hybrid/Electric Aero-Propulsion Systems for Military Applications (AVT-RSY-323), 2019, DOI: 10.14339/STO-MP-AVT-323. PDF is available here.
@inproceedings{Brelje2019d,
address = {Trondheim, NO},
author = {Brelje, Benjamin J. and Jasa, John P. and Martins, Joaquim R. R. A. and Gray, Justin S.},
booktitle = {NATO Research Symposium on Hybrid/Electric Aero-Propulsion Systems for Military Applications (AVT-RSY-323)},
doi = {10.14339/STO-MP-AVT-323},
institution = {NATO Research and Technology Organization},
keywords = {OpenMDAO, OpenConcept, ccavd},
month = oct,
title = {Development of a Conceptual-Level Thermal Management System Design Capability in {OpenConcept}},
year = {2019}
}
2018
Benjamin J. Brelje and Joaquim R.R.A. Martins, “Development of a Conceptual Design Model for Aircraft Electric Propulsion with Efficient Gradients”, 2018 AIAA/IEEE Electric Aircraft Technologies Symposium, AIAA Propulsion and Energy Forum, (AIAA 2018-4979), DOI: 10.2514/6.2018-4979. PDF is available here.
@inproceedings{Brelje2018,
address = {{C}incinnati,~{OH}},
author = {Benjamin J. Brelje and Joaquim R. R. A. Martins},
booktitle = {2018 AIAA/IEEE Electric Aircraft Technologies Symposium},
month = {July},
title = {Development of a Conceptual Design Model for Aircraft Electric Propulsion with Efficient Gradients},
year = {2018},
doi = {10.2514/6.2018-4979}
}