The multi-point optimization of Atlas distributes the optimization across 4 design points. Each of these points represents a different operating condition for the vehicle:
Each of these design points is represented by a separate AeroStructural assembly instance, each with an AtlasConfiguration component with settings to represent the differences between the design points.
First, for the low altitude design point:
class ConfigLow(AtlasConfiguration):
""" Atlas configuration for low altitude """
Omega_opt = Float(iotype='in', desc='rotor angular velocity')
H_opt = Float(iotype='in', desc='height of aircraft')
def execute(self):
super(ConfigLow, self).execute()
# use optimizer provided values
self.Omega = self.Omega_opt
self.h = self.H_opt + self.zWire
class AeroStructuralLow(AeroStructural):
""" AeroStructural assembly for low altitude case in multipoint optimization """
def configure(self):
super(AeroStructuralLow, self).configure()
# replace config with optimizer driven config
self.replace('config', ConfigLow())
# create passthroughs for variables used by the optimizer
self.create_passthrough('config.Omega_opt')
self.create_passthrough('config.H_opt')
self.create_passthrough('struc.Mtot')
self.create_passthrough('results.Ttot')
self.create_passthrough('results.Ptot')
Now, the high-altitude design point:
class ConfigHigh(AtlasConfiguration):
""" Atlas configuration for high altitude """
Omega_opt = Float(iotype='in', desc='rotor angular velocity')
Cl0_opt = Float(iotype='in')
Cl1_opt = Float(iotype='in')
H_opt = Float(iotype='in', desc='height of aircraft')
TWire_opt = Float(iotype='in', desc='')
def execute(self):
super(ConfigHigh, self).execute()
# use optimizer provided values
self.Omega = self.Omega_opt
self.Cl[0] = self.Cl0_opt
self.Cl[1] = self.Cl1_opt
self.h = self.H_opt + self.zWire
self.TWire = [self.TWire_opt]
class AeroStructuralHigh(AeroStructural):
""" AeroStructural assembly for high altitude case in multipoint optimization """
def configure(self):
super(AeroStructuralHigh, self).configure()
# replace config with optimizer driven config
self.replace('config', ConfigHigh())
# create passthroughs for variables used by the optimizer
self.create_passthrough('config.Omega_opt')
self.create_passthrough('config.Cl0_opt')
self.create_passthrough('config.Cl1_opt')
self.create_passthrough('config.H_opt')
self.create_passthrough('config.TWire_opt')
self.create_passthrough('struc.Mtot')
self.create_passthrough('results.Ttot')
self.create_passthrough('results.Ptot')
Then the wind design point:
class ConfigWind(AtlasConfiguration):
""" Atlas configuration for wind case """
Omega_opt = Float(iotype='in', desc='rotor angular velocity')
OmegaRatio = Float(iotype='in')
Cl_opt = Array(iotype='in')
H_opt = Float(iotype='in', desc='height of aircraft')
TWire_opt = Float(iotype='in', desc='')
vw_opt = Float(iotype='in', desc='wind velocity')
def execute(self):
super(ConfigWind, self).execute()
# use optimizer provided values
self.Omega = (self.Omega_opt**3 * self.OmegaRatio)**(1./3.)
self.Cl = self.Cl_opt
self.h = self.H_opt + self.zWire
self.TWire = [self.TWire_opt]
self.vw = self.vw_opt
# FIXME: the following two flags are ignored
self.flags.FreeWake = 0 # momentum theory
self.flags.AeroStr = 0 # assume flat wing (no deformation)
class AeroStructuralWind(AeroStructural):
""" AeroStructural assembly for wind case in multipoint optimization """
def configure(self):
super(AeroStructuralWind, self).configure()
# replace config with optimizer driven config
self.replace('config', ConfigWind())
# create passthroughs for variables used by the optimizer
self.create_passthrough('config.Omega_opt')
self.create_passthrough('config.OmegaRatio')
self.create_passthrough('config.Cl_opt')
self.create_passthrough('config.H_opt')
self.create_passthrough('config.TWire_opt')
self.create_passthrough('config.vw_opt')
self.create_passthrough('struc.Mtot')
self.create_passthrough('results.Ttot')
self.create_passthrough('results.Ptot')
Finally, the gravity design point:
class ConfigGravity(AtlasConfiguration):
""" Atlas configuration for gravity case """
Omega_opt = Float(iotype='in', desc='rotor angular velocity')
OmegaRatio = Float(iotype='in')
Cl_opt = Array(iotype='in')
H_opt = Float(iotype='in', desc='height of aircraft')
TWire_opt = Float(iotype='in', desc='')
def execute(self):
super(ConfigGravity, self).execute()
# use optimizer provided values
self.Omega = (self.Omega_opt**3 * self.OmegaRatio)**(1./3.)
self.Cl = self.Cl_opt
self.h = self.H_opt + self.zWire
self.TWire = [self.TWire_opt]
self.flags.Load = 1 # gravity and wire forces only
class AeroStructuralGravity(AeroStructural):
""" AeroStructural assembly for gravity case in multipoint optimization """
def configure(self):
super(AeroStructuralGravity, self).configure()
# replace config with optimizer driven config
self.replace('config', ConfigGravity())
# create passthroughs for variables used by the optimizer
self.create_passthrough('config.Omega_opt')
self.create_passthrough('config.OmegaRatio')
self.create_passthrough('config.Cl_opt')
self.create_passthrough('config.H_opt')
self.create_passthrough('config.TWire_opt')
self.create_passthrough('struc.Mtot')
self.create_passthrough('results.Ttot')
self.create_passthrough('results.Ptot')
Now, these 4 design points are assembled together. Parameterizations of each design point are exposed as input variables. Summary outputs are exposed using passthroughs.
class Multipoint(Assembly):
""" Assembly for multipoint AeroStructural optimization.
Evaluates AeroStructural for four cases:
low altitude
high altitude
wind
gravity only
"""
# configuration inputs
alt_low = Float(iotype='in', desc='low altitude')
alt_high = Float(iotype='in', desc='high altitude')
alt_ratio = Float(iotype='in', desc='proportion of time near ground')
TWire_high = Float(iotype='in')
TWire_wind = Float(iotype='in')
TWire_grav = Float(iotype='in')
OmegaRatio = Float(iotype='in')
vw = Float(iotype='in', desc='wind velocity')
Cl_max = Array(iotype='in')
# optimizer parameters
Omega_low = Float(iotype='in', desc='rotor angular velocity, low altitude')
Omega_high = Float(iotype='in', desc='rotor angular velocity, high altitude')
Cl0_high = Float(iotype='in')
Cl1_high = Float(iotype='in')
# outputs
P = Float(iotype='out', desc='')
def configure(self):
# low altitude
self.add('low', AeroStructuralLow())
self.connect('Omega_low', 'low.Omega_opt')
self.connect('alt_low', 'low.H_opt')
self.create_passthrough('low.Mtot', 'Mtot_low')
self.create_passthrough('low.Ttot', 'Ttot_low')
# high altitude
# need a different rotor speed and lift distribution at altitude
self.add('high', AeroStructuralHigh())
self.connect('Omega_high', 'high.Omega_opt')
self.connect('Cl0_high', 'high.Cl0_opt')
self.connect('Cl1_high', 'high.Cl1_opt')
self.connect('alt_high', 'high.H_opt')
self.connect('TWire_high', 'high.TWire_opt')
self.create_passthrough('high.Mtot', 'Mtot_high')
self.create_passthrough('high.Ttot', 'Ttot_high')
# wind case
self.add('wind', AeroStructuralWind())
self.connect('Omega_high', 'wind.Omega_opt')
self.connect('OmegaRatio', 'wind.OmegaRatio')
self.connect('Cl_max', 'wind.Cl_opt')
self.connect('alt_high', 'wind.H_opt')
self.connect('TWire_wind', 'wind.TWire_opt')
self.connect('vw', 'wind.vw_opt')
# gravity case
self.add('grav', AeroStructuralGravity())
self.connect('Omega_high', 'grav.Omega_opt')
self.connect('OmegaRatio', 'grav.OmegaRatio')
self.connect('Cl_max', 'grav.Cl_opt')
self.connect('alt_high', 'grav.H_opt')
self.connect('TWire_grav', 'grav.TWire_opt')
# total power
self.connect('alt_ratio*low.Ptot + (1 - alt_ratio)*high.Ptot', 'P')
self.driver.workflow.add(['low', 'high', 'wind', 'grav'])
Finally, the multipoint optimization is set up to operate on an instance of the Multipoint assembly, with parameters for each of the 4 design cases being set.
class HeliOptM(Assembly):
""" Multipoint aero-structural optimization """
def configure(self):
# add an optimizer and a multi-point AeroStructural assembly
if pyopt_driver and 'SNOPT' in pyopt_driver._check_imports():
self.add("driver", pyopt_driver.pyOptDriver())
self.driver.optimizer = "SNOPT"
self.driver.options = {
# any changes to default SNOPT options?
}
else:
print 'SNOPT not available, using SLSQP'
self.add('driver', SLSQPdriver())
self.add('mp', Multipoint())
self.mp.alt_low = 0.5 # low altitude
self.mp.alt_high = 3.5 # high altitude
self.mp.alt_ratio = 35./60. # proportion of time near ground
self.mp.TWire_high = 900
self.mp.TWire_wind = 2100
self.mp.TWire_grav = 110
self.mp.OmegaRatio = 2
self.mp.vw = 0/3.6 # zero
self.mp.Cl_max = [1.4, 1.35, 1.55] # max control
# objective: minimize total power
self.driver.add_objective('mp.P')
# parameter: rotor speed
self.driver.add_parameter('mp.Omega_low',
low=0.15*2*pi, high=0.25*2*pi)
self.mp.Omega_low = 0.20*2*pi # initial value
self.driver.add_parameter('mp.Omega_high',
low=0.15*2*pi, high=0.19*2*pi)
self.mp.Omega_high = 0.17*2*pi # initial value
# parameter: lift distribution at high altitude
self.driver.add_parameter('mp.Cl0_high',
low=0.8, high=1.4)
self.mp.Cl0_high = 1.
self.driver.add_parameter('mp.Cl1_high',
low=0.8, high=1.3)
self.mp.Cl1_high = 1.
# constraint: lift >= weight
self.driver.add_constraint('mp.Mtot_low*9.8-mp.Ttot_low<=0')
self.driver.add_constraint('mp.Mtot_high*9.8-mp.Ttot_high<=0')
# TODO: optional constraints
# if flags.ConFail:
# Structural Failure in Rotor Spar (ConFail)
# Buckling failure of spar (ConFailBuck)
# Tensile failure in wire (ConFailWire)
#
# if flags.ConDef:
# Constraints on Maximum Deformation (ConDelta)
#
# if flags.MultiPoint && flags.ConJigCont:
# Consistent jig twist (ConAlphaJig)
#
# if flags.MultiPoint && flags.ConWireCont
# Wire stretch consistency (conWire)
# Optimization Constraints (not used... yet)
vrCon = VariableTree()
vrCon.MaxDelta = -0.1
vrCon.MinDelta = 0.1
vrCon.FOSmat = 0.55 # 1.3
vrCon.FOSbuck = 0.5 # 1.3
vrCon.FOSquadbuck = 5.
vrCon.FOStorbuck = 0.5 # 1.5
vrCon.FOSwire = 0.5 # 2
This can be run similar to the single-point optimization:
opt = set_as_top(HeliOptM())
print 'Starting multipoint optimization at %s ...' % time.strftime('%X')
time1 = time.time()
opt.run()
time2 = time.time()
print 'Optimization complete at %s (elapsed time: %5.2f minutes)' \
% (time.strftime('%X'), ((time2-time1)/60))
print
print 'Objective: P =', opt.mp.P
print 'Constraint: Low Weight-Lift =', opt.mp.Mtot_low*9.8-opt.mp.Ttot_low
print 'Constraint: High Weight-Lift =', opt.mp.Mtot_high*9.8-opt.mp.Ttot_high
print 'Parameter: Omega (Low) =', opt.mp.Omega_low
print 'Parameter: Omega (High) =', opt.mp.Omega_high
The multipoint optimization can be peformed by running the heli_opt_multipoint.py file located in the “examples” directory.