Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions aviary/subsystems/propulsion/engine_deck.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ class EngineDeck(EngineModel):
update
"""

# EngineDecks using GLOBAL_THROTTLE = False will have unique maximum throttle levels per flight
# condition (not always 1) - max engine values must be handled manually inside this component
# TODO this can be updated so that if GLOBAL_THROTTLE = True, the max engine components in
# build_mission() are skipped, and this flag is set to False.
compute_max_values = True

def __init__(
self,
name='engine_deck',
Expand Down Expand Up @@ -335,12 +341,12 @@ def _set_variable_flags(self):
def _setup(self, data):
"""
Read in and process engine data.
- Check data consistency.
- Convert altitudes to geometric.
- Sort and pack data.
- Determine reference thrust.
- Normalize throttles & hybrid throttles.
- Fill flight idle points if requested.
- Check data consistency
- Convert altitudes to geometric (optional)
- Sort and pack data
- Determine reference thrust (optional)
- Normalize throttles & hybrid throttles
- Fill flight idle points (optional)
"""
self._read_data(data)

Expand Down Expand Up @@ -904,7 +910,7 @@ def build_mission(self, num_nodes, aviary_inputs, **kwargs) -> om.Group:
# reduced data set?
if self.use_thrust or self.use_shaft_power:
if self.global_throttle or (self.global_hybrid_throttle and self.use_hybrid_throttle):
# create IndepVarComp to pass maximum throttle is to max thrust interpolator
# create IndepVarComp to pass maximum throttle to max thrust interpolator
fixed_throttles = om.IndepVarComp()
if self.global_throttle:
fixed_throttles.add_output(
Expand Down Expand Up @@ -985,7 +991,7 @@ def build_mission(self, num_nodes, aviary_inputs, **kwargs) -> om.Group:
max_thrust_engine = om.MetaModelSemiStructuredComp(
method=interp_method, extrapolate=False, vec_size=num_nodes
)

# TODO engine could have other inputs!! Don't hardcode these
if interp_sort == 'altitude':
max_thrust_engine.add_input(
Dynamic.Mission.ALTITUDE,
Expand Down Expand Up @@ -1085,23 +1091,38 @@ def build_mission(self, num_nodes, aviary_inputs, **kwargs) -> om.Group:
if self.use_thrust or self.use_shaft_power:
if self.global_throttle or (self.global_hybrid_throttle and self.use_hybrid_throttle):
engine_group.add_subsystem(
'fixed_max_throttles', fixed_throttles, promotes_outputs=['*']
'fixed_max_throttles',
fixed_throttles, # , promotes_outputs=['*']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove all of the commented out promotes statements.

)

if not (
self.global_throttle or (self.global_hybrid_throttle and self.use_hybrid_throttle)
):
else:
engine_group.add_subsystem(
'interp_max_throttles',
interp_throttles,
promotes_inputs=['*'],
promotes_outputs=['*'],
# promotes_outputs=['*'],
)

engine_group.add_subsystem(
'max_interpolation', max_thrust_engine, promotes_inputs=['*']
'max_interpolation',
max_thrust_engine,
promotes_inputs=[
Dynamic.Atmosphere.MACH,
Dynamic.Mission.ALTITUDE,
], # , promotes_inputs=['*']
)

# manually connect max throttles - do not promote them out of group
if self.global_throttle or (self.global_hybrid_throttle and self.use_hybrid_throttle):
engine_group.connect(
'fixed_max_throttles.throttle_max',
'max_interpolation.throttle_max',
)
else:
engine_group.connect(
'interp_max_throttles.throttle_max',
'max_interpolation.throttle_max',
)

if uncorrect_shp:
engine_group.add_subsystem(
'uncorrect_max_shaft_power',
Expand Down Expand Up @@ -1171,6 +1192,14 @@ def build_mission(self, num_nodes, aviary_inputs, **kwargs) -> om.Group:

return engine_group

def mission_inputs(self, **kwargs):
inputs = [inp.value for inp in self.inputs]
return inputs

def mission_outputs(self, **kwargs):
outputs = [out.value for out in self.outputs]
return outputs

def get_parameters(self):
params = {
Aircraft.Engine.SCALE_FACTOR: {
Expand Down
4 changes: 4 additions & 0 deletions aviary/subsystems/propulsion/engine_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class EngineModel(SubsystemBuilder):
"""

default_name = 'engine_model'
# Flag that sets if this engine computes maximum values (e.g. max thrust, shaft power) for a
# given flight condition. If False, during mission Aviary will create a duplicate copy of the
# engine that is given max throttle and hybrid throttle (1.0) to compute max values.
compute_max_values = False

def __init__(
self, name: str = None, options: AviaryValues = None, meta_data: dict = None, **kwargs
Expand Down
36 changes: 22 additions & 14 deletions aviary/subsystems/propulsion/gearbox/gearbox_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ def build_mission(self, num_nodes, aviary_inputs):
"""Builds an OpenMDAO system for the mission computations of the subsystem."""
return GearboxMission(num_nodes=num_nodes)

def mission_inputs(self, **kwargs):
inputs = [Aircraft.Engine.Gearbox.GEAR_RATIO, Aircraft.Engine.Gearbox.EFFICIENCY]
return inputs

def mission_outputs(self, **kwargs):
return []

def get_design_vars(self):
"""
Design vars are only tested to see if they exist in pre_mission
Expand Down Expand Up @@ -82,11 +89,11 @@ def get_parameters(self, aviary_inputs=None, phase_info=None):
'units': 'unitless',
'static_target': True,
},
Aircraft.Engine.Gearbox.SHAFT_POWER_DESIGN: {
'val': 1.0,
'units': 'kW',
'static_target': True,
},
# Aircraft.Engine.Gearbox.SHAFT_POWER_DESIGN: {
# 'val': 1.0,
# 'units': 'kW',
# 'static_target': True,
# },
}

return parameters
Expand All @@ -97,21 +104,22 @@ def get_mass_names(self):
def get_timeseries(self):
return [
Dynamic.Vehicle.Propulsion.SHAFT_POWER + '_out',
Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out',
# Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out',
Dynamic.Vehicle.Propulsion.RPM + '_out',
Dynamic.Vehicle.Propulsion.TORQUE + '_out',
Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
# Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
]

def get_constraints(self):
if self.include_constraints:
constraints = {
Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL: {
'lower': 0.0,
'type': 'path',
'units': 'kW',
}
}
constraints = {}
# constraints = {
# Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL: {
# 'lower': 0.0,
# 'type': 'path',
# 'units': 'kW',
# }
# }
else:
constraints = {}
return constraints
84 changes: 42 additions & 42 deletions aviary/subsystems/propulsion/gearbox/model/gearbox_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,46 +69,46 @@ def setup(self):

# Determine the maximum power available at this flight condition
# this is used for excess power constraints
self.add_subsystem(
'shaft_power_max_comp',
om.ExecComp(
'shaft_power_out = shaft_power_in * efficiency',
shaft_power_in={'val': np.ones(n), 'units': 'kW'},
shaft_power_out={'val': np.ones(n), 'units': 'kW'},
efficiency={'val': 1.0, 'units': 'unitless'},
has_diag_partials=True,
),
promotes_inputs=[
('shaft_power_in', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in'),
('efficiency', Aircraft.Engine.Gearbox.EFFICIENCY),
],
promotes_outputs=[
('shaft_power_out', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out')
],
)
# self.add_subsystem(
# 'shaft_power_max_comp',
# om.ExecComp(
# 'shaft_power_out = shaft_power_in * efficiency',
# shaft_power_in={'val': np.ones(n), 'units': 'kW'},
# shaft_power_out={'val': np.ones(n), 'units': 'kW'},
# efficiency={'val': 1.0, 'units': 'unitless'},
# has_diag_partials=True,
# ),
# promotes_inputs=[
# ('shaft_power_in', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in'),
# ('efficiency', Aircraft.Engine.Gearbox.EFFICIENCY),
# ],
# promotes_outputs=[
# ('shaft_power_out', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out')
# ],
# )

# We must ensure the design shaft power that was provided to pre-mission is
# larger than the maximum shaft power that could be drawn by the mission.
# Note this is a larger value than the actual maximum shaft power drawn during
# the mission because the aircraft might need to climb to avoid obstacles at
# anytime during the mission
self.add_subsystem(
'shaft_power_residual',
om.ExecComp(
'shaft_power_residual = shaft_power_design - shaft_power_max',
shaft_power_max={'val': np.ones(n), 'units': 'kW'},
shaft_power_design={'val': 1.0, 'units': 'kW'},
shaft_power_residual={'val': np.ones(n), 'units': 'kW'},
has_diag_partials=True,
),
promotes_inputs=[
('shaft_power_max', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in'),
('shaft_power_design', Aircraft.Engine.Gearbox.SHAFT_POWER_DESIGN),
],
promotes_outputs=[
(
'shaft_power_residual',
Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
)
],
)
# # We must ensure the design shaft power that was provided to pre-mission is
# # larger than the maximum shaft power that could be drawn by the mission.
# # Note this is a larger value than the actual maximum shaft power drawn during
# # the mission because the aircraft might need to climb to avoid obstacles at
# # anytime during the mission
# self.add_subsystem(
# 'shaft_power_residual',
# om.ExecComp(
# 'shaft_power_residual = shaft_power_design - shaft_power_max',
# shaft_power_max={'val': np.ones(n), 'units': 'kW'},
# shaft_power_design={'val': 1.0, 'units': 'kW'},
# shaft_power_residual={'val': np.ones(n), 'units': 'kW'},
# has_diag_partials=True,
# ),
# promotes_inputs=[
# ('shaft_power_max', Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in'),
# ('shaft_power_design', Aircraft.Engine.Gearbox.SHAFT_POWER_DESIGN),
# ],
# promotes_outputs=[
# (
# 'shaft_power_residual',
# Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
# )
# ],
# )
12 changes: 6 additions & 6 deletions aviary/subsystems/propulsion/gearbox/test/test_gearbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ def test_gearbox_mission(self):

prob.set_val(av.Dynamic.Vehicle.Propulsion.RPM + '_in', [5000, 6195, 6195], units='rpm')
prob.set_val(av.Dynamic.Vehicle.Propulsion.SHAFT_POWER + '_in', [100, 200, 375], units='hp')
prob.set_val(
av.Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in', [375, 300, 375], units='hp'
)
# prob.set_val(
# av.Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_in', [375, 300, 375], units='hp'
# )
prob.set_val(av.Aircraft.Engine.Gearbox.GEAR_RATIO, 12.6, units=None)
prob.set_val(av.Aircraft.Engine.Gearbox.EFFICIENCY, 0.98, units=None)

Expand All @@ -65,17 +65,17 @@ def test_gearbox_mission(self):
shaft_power = prob.get_val(av.Dynamic.Vehicle.Propulsion.SHAFT_POWER + '_out', 'hp')
rpm = prob.get_val(av.Dynamic.Vehicle.Propulsion.RPM + '_out', 'rpm')
torque = prob.get_val(av.Dynamic.Vehicle.Propulsion.TORQUE + '_out', 'ft*lbf')
shaft_power_max = prob.get_val(av.Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out', 'hp')
# shaft_power_max = prob.get_val(av.Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out', 'hp')

shaft_power_expected = [98.0, 196.0, 367.5]
rpm_expected = [396.82539683, 491.66666667, 491.66666667]
torque_expected = [1297.0620786, 2093.72409783, 3925.73268342]
shaft_power_max_expected = [367.5, 294.0, 367.5]
# shaft_power_max_expected = [367.5, 294.0, 367.5]

assert_near_equal(shaft_power, shaft_power_expected, tolerance=1e-6)
assert_near_equal(rpm, rpm_expected, tolerance=1e-6)
assert_near_equal(torque, torque_expected, tolerance=1e-6)
assert_near_equal(shaft_power_max, shaft_power_max_expected, tolerance=1e-6)
# assert_near_equal(shaft_power_max, shaft_power_max_expected, tolerance=1e-6)

partial_data = prob.check_partials(out_stream=None, method='cs')
assert_check_partials(partial_data, atol=1e-9, rtol=1e-9)
Expand Down
34 changes: 27 additions & 7 deletions aviary/subsystems/propulsion/propeller/propeller_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,30 @@ def build_mission(self, num_nodes, aviary_inputs):
num_nodes=num_nodes, aviary_options=aviary_inputs, propeller_data=self.data
)

def mission_inputs(self, **kwargs):
inputs = [
Dynamic.Atmosphere.MACH,
Aircraft.Engine.Propeller.TIP_SPEED_MAX,
Aircraft.Engine.Propeller.TIP_MACH_MAX,
Dynamic.Atmosphere.DENSITY,
Dynamic.Mission.VELOCITY,
Aircraft.Engine.Propeller.DIAMETER,
Aircraft.Engine.Propeller.ACTIVITY_FACTOR,
Aircraft.Engine.Propeller.INTEGRATED_LIFT_COEFFICIENT,
Aircraft.Nacelle.AVG_DIAMETER,
Dynamic.Atmosphere.SPEED_OF_SOUND,
Dynamic.Vehicle.Propulsion.RPM,
]
return inputs

def mission_outputs(self, **kwargs):
outputs = [Dynamic.Vehicle.Propulsion.THRUST]
return outputs

def get_design_vars(self):
"""
Design vars are only tested to see if they exist in pre_mission
Returns a dictionary of design variables for the gearbox subsystem, where the keys are the
Returns a dictionary of design variables for the propeller subsystem, where the keys are the
names of the design variables, and the values are dictionaries that contain the units for
the design variable, the lower and upper bounds for the design variable, and any
additional keyword arguments required by OpenMDAO for the design variable.
Expand Down Expand Up @@ -117,13 +137,13 @@ def get_parameters(self, aviary_inputs=None, phase_info=None):
return parameters

def get_mass_names(self):
return [Aircraft.Engine.Gearbox.MASS]
return []

def get_timeseries(self):
return [
Dynamic.Vehicle.Propulsion.SHAFT_POWER + '_out',
Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out',
Dynamic.Vehicle.Propulsion.RPM + '_out',
Dynamic.Vehicle.Propulsion.TORQUE + '_out',
Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
Dynamic.Vehicle.Propulsion.SHAFT_POWER,
# Dynamic.Vehicle.Propulsion.SHAFT_POWER_MAX + '_out',
Dynamic.Vehicle.Propulsion.RPM,
Dynamic.Vehicle.Propulsion.TORQUE,
# Mission.Constraints.GEARBOX_SHAFT_POWER_RESIDUAL,
]
Loading
Loading