diff --git a/_posts/2015-06-15-Lithium_Air_Regional_Jet_Optimization.md b/_posts/2015-06-15-Lithium_Air_Regional_Jet_Optimization.md new file mode 100644 index 00000000..95b84c16 --- /dev/null +++ b/_posts/2015-06-15-Lithium_Air_Regional_Jet_Optimization.md @@ -0,0 +1,71 @@ +-- +layout: post +title: Lithium Air Regional Jet Optimization +date: 2015-06-15 14:25:00 +categories: blog +description: Learn the sizing and regression framework in an optimization problemx + + + +permalink: /guides/lithium_air_regional_jet_optimization.html +--- + + + + +## Introduction +The purpose of this tutorial is to introduce the user to the sizing/optimization process, which uses machine learning regression techniques to accelerate convergence. + +The user, is he/she so chooses, could directly run Optimize.py (located in Tutorials/Lithium_Air_Jet_Sizing). However this would be a relatively slow process (~5,200 SUAVE mission evaluations), due to the fact that each step in the optimizer would have to use the same initial guess for y to converge on mass, energy, and power. Here we're going to be smarter, and tabulate the converged solutions, using them to inform the initial guesses. Go to Sizing.py, in the "setup" function, and uncomment "procedure.write_optimization." + +

+def setup():
+    
+    # ------------------------------------------------------------------
+    #   Analysis Procedure
+    # ------------------------------------------------------------------ 
+    
+    # size the base config
+    procedure = Process()
+    procedure.run_sizing_loop       = run_sizing_loop #size aircraft and run mission
+    procedure.evaluate_field_length = evaluate_field_length
+    procedure.evaluate_constraints  = evaluate_constraints
+    #procedure.write_optimization    = write_optimization  #only use when writing an optimization problem
+    return procedure
+
+
+ + +What this does is makes it so, at each converged step, SUAVE tabulates the optimization variables, constraints, as well as objective, which allows the user use to view how the optimization proceeds, as well as whether it can satisfy the constraints. + +Additionally,SUAVE automatically creates a file called "sizing_outputs.txt" which outputs the converged sizing results vs. the optimization variables, allowing it to be read in and used to choose a more informed initial guess.. This saves a large amount of computational time, especially for gradient-based optimization. To use this, go to the "run_sizing_loop" function, to the section where you choose your sizing options, and change "sizing_loop.initial_step" from "Default" to "Table" + + +

+    sizing_loop.tolerance                                      = 1E-4 #percentage difference in mass and energy between iterations
+    sizing_loop.initial_step                                   = 'Default' #Default, Table, SVR
+    sizing_loop.update_method                                  = 'successive_substitution' #'successive_substitution','newton-raphson', 'broyden'
+    sizing_loop.default_y                                      = y
+    sizing_loop.min_y                                          = min_y
+    sizing_loop.max_y                                          = max_y
+    sizing_loop.default_scaling                                = scaling
+    sizing_loop.sizing_evaluation                              = sizing_evaluation
+    
+
+ +At this point, it is worth looking into changes to the optimizer options to ensure consistency. Because the solution is converged to 1E-4, the user needs to ensure that the finite differencing step is taken reduced to account for this. Go to Optimize.py, and look at the line where the Optimizer is called + +

+    def main():
+        
+        problem = setup()
+    
+        output = scipy_setup.SciPy_Solve(problem, sense_step = 1E-2, solver = 'SLSQP')
+        print output
+        
+        Plot_Mission.plot_mission(problem)
+
+        return
+
+ +Note that, the "sense_step," that is, the finite differencing step is set to 1E-2 (it is recommended to set the finite differencing step to the square root of the solution tolerance). Now try running the problem (type "python Optimize.py" while in the Lithium_Air_Sizing), and observe the weight reduction as well as the number of iterations it takes to converge; in this case, it takes ~1,000 SUAVE sizing/mission evaluations to converge, decreasing computational cost by a factor of 5. Feel free to play with the optimization options, as well as sizing options to observe there impact on the solution path. diff --git a/_posts/2015-06-15-Lithium_Air_Regional_Jet_Sizing.md b/_posts/2015-06-15-Lithium_Air_Regional_Jet_Sizing.md index 004650e5..58447e35 100644 --- a/_posts/2015-06-15-Lithium_Air_Regional_Jet_Sizing.md +++ b/_posts/2015-06-15-Lithium_Air_Regional_Jet_Sizing.md @@ -3,11 +3,11 @@ layout: post title: Lithium Air Regional Jet Sizing date: 2015-06-15 14:25:00 categories: blog -description: Incorporate more novel propulsion configurations +description: Incorporate more novel propulsion configurations, introduce sizing methodology -permalink: /guides/lithium_air_regional_jet_analysis.html +permalink: /guides/lithium_air_regional_jet_sizing.html --- @@ -15,12 +15,14 @@ permalink: /guides/lithium_air_regional_jet_analysis.html ## Introduction -The purpose of this tutorial is to highlight some of SUAVE's more exotic propulsion system capabilities. This tutorial assumes that the user has completed the Boeing 737-800 tutorial, and has some familiarity with SUAVE's propulsion system data structures. +The purpose of this tutorial is to highlight some of SUAVE's more exotic propulsion system capabilities, as well as the sizing methodology recently introduced ## Baseline Case - Open the file called "tut_lithium_air_jet_sizing.py" in a text editor or IDE. + Open the file called "Lithium_Air_Sizing/Sizing.py" in a text editor or IDE. Type "python Sizing.py" to run the file. - Look over the plots, to gain a feel for the various idiosyncrasies of the design. Note the significant rise in aircraft mass, as a result of the lithium-air batteries. + + + Look over the plots, to gain a feel for the various idiosyncrasies of the design. Note the significant rise in aircraft mass, as a result of the lithium-air batteries, which accumulates oxygen as it discharges. ![li air mass](../images/li_air_mass.png) @@ -30,9 +32,9 @@ The purpose of this tutorial is to highlight some of SUAVE's more exotic propuls battery.specific_power =.67*Units.kW/Units.kg -Run the script (python tut_lithium_air_jet.py). -Now try changing the motor efficiency from .95 to .9 and running the script (line 513). +## Sizing Procedure +Now try changing the motor efficiency from .95 to .9 and running the script by going to Vehicle.py

     net.nacelle_diameter  = ducted_fan.nacelle_diameter
@@ -43,23 +45,173 @@ Now try changing the motor efficiency from .95 to .9 and running the script (lin
 
 Note the sensitivity of vehicle energy and mass requirements to these various propulsion system assumptions.
 
-Now try changing the cruise range of the aircraft (line 868, segment.distance).
+Now try changing the cruise range of the aircraft. Go to Mission.py, find the cruise segment, and change segment.distance
 
  
 

     segment = Segments.Cruise.Constant_Speed_Constant_Altitude()
     segment.tag = "cruise"
-
-    # connect vehicle configuration
+    
     segment.analyses.extend( analyses.cruise )
+    
+    segment.air_speed  = 230 * Units['m/s']
+    segment.distance   = 2000 * Units.nautical_miles
+    
+
+ +Try changing other parameters (e.g. specific power, cruise altitude), and observe their effects on the overall design. + +Now look into how a an aircraft sizing problem is set up within SUAVE. A diagram of the process with a summary can be seen below, for reference + + +![sizing_diagram](../images/sizing_diagram.png) + +Go to the function "run_sizing_loop" within Sizing.py. The default initial guesses for your sizing parameters can be seen in the lines below + +

+    #initial guesses
+    m_guess    = 60000.       
+    Ereq_guess = 100000000000.  
+    Preq_guess=  200000. 
+ 
+  
+    scaling       = np.array([1E4,1E11,1E7])
+    y             = np.array([m_guess, Ereq_guess, Preq_guess])/scaling
+    min_y         = [.05, 1E-5,10.]
+    max_y         = [10., 10., 10.]
+    
+
+
+ +For a given aircraft, a numpy array we call "y" is used to define its set of sizing parameters. In this case, because it is a battery-powered aircraft, we iterate on aircraft total mass, battery energy, as well as battery power. Scaling is used to make the parameter guesses of order 1, as it makes some of the sizing methods more stable and robust. The default initial guess is then assigned to the sizing loop object, as seen below - # segment attributes - segment.atmosphere = atmosphere - segment.planet = planet +

+    sizing_loop.tolerance                                      = 1E-4 #percentage difference in mass and energy between iterations
+    sizing_loop.initial_step                                   = 'Default' #Default, Table, SVR
+    sizing_loop.update_method                                  = 'successive_substitution' #'successive_substitution','newton-raphson', 'broyden'
+    sizing_loop.default_y                                      = y
+    sizing_loop.min_y                                          = min_y
+    sizing_loop.max_y                                          = max_y
+    sizing_loop.default_scaling                                = scaling
+    sizing_loop.sizing_evaluation                              = sizing_evaluation
+    
+
+
+ + + +In this case, only successive substitution is used to solve the problem, although that can be changed to newton-raphson or Broyden's method if the user chooses. Let's look at how the sizing loop works; it queries a function called "sizing_evaluation." Go to that script to see how it's set up. + + +

+def sizing_evaluation(y,nexus, scaling):
+
+    #unpack inputs
+    m_guess     = y[0]*scaling[0]
+    Ereq_guess  = y[1]*scaling[1]
+    Preq_guess  = y[2]*scaling[2]
+
- + +How it works is it takes the initial guesses, unpacks them, then assigns them to the aircraft, as seen below + + +

+   #assign guesses to aircraft
+    configs.base.m_guess = m_guess
+    configs.base.Ereq    = Ereq_guess
+    configs.base.Preq    = Preq_guess
 
-    segment.air_speed  = 230.
-    segment.distance   = 1947. * Units.nmi
 
 
-Try changing other parameters (e.g. specific power, cruise altitude), and observe their effects on the overall design. \ No newline at end of file +This function acts as an interface to allow communication between the Sizing_Loop object, which contains a number of methods to aid in evaluating the aircraft, with the specific aircraft observed here. It then calls the simple_sizing function, and evaluates the mission, as seen below. + +

+    #run size aircraft geometry/mass based on guess
+    simple_sizing(nexus)
+    analyses.finalize()
+    results = evaluate_mission(configs,mission)
+    
+
+ +Now let's look at how they communicate. Go to the simple_sizing script within Sizing.py, and observe that it unpacks your mass, energy, and power scripts + +

+    #unpack guesses
+    m_guess = base.m_guess
+    Ereq    = base.Ereq
+    Preq    = base.Preq
+
+ +then assigns them to the aircraft + +

+    base.mass_properties.max_takeoff   = m_guess
+    base.mass_properties.max_zero_fuel = m_guess  #just used for weight calculation
+    design_thrust                      = base.thrust_loading*m_guess*9.81 
+    Sref                               = m_guess/base.wing_loading
+    
+    #assign area
+    base.reference_area                     = Sref
+    base.wings['main_wing'].areas.reference = base.reference_area
+    
+
+ + +It also uses the guess for battery and power to determine battery characteristics, as well as determine the takeoff weight. + +

+    # battery calcs
+    SUAVE.Methods.Power.Battery.Sizing.initialize_from_energy_and_power(battery,Ereq,Preq, max='soft')
+    battery.current_energy = [battery.max_energy] #initialize list of current energy
+    m_air                  =SUAVE.Methods.Power.Battery.Variable_Mass.find_total_mass_gain(battery)
+    
+    
+
+ +Remember that this is a lithium-air aircraft, so the difference in takeoff and landing weight is the oxygen accumulated during flight. + + + +

+    #evaluate total mass breakdown
+    breakdown                             = analyses.base.weights.evaluate()
+    breakdown.battery                     = battery.mass_properties.mass
+    breakdown.air                         = m_air
+    base.mass_properties.breakdown        = breakdown
+    base.mass_properties.operating_empty  = breakdown.empty 
+    
+    #m_full = GTOW, m_end = GLW
+    m_full                       = breakdown.empty+battery.mass_properties.mass+breakdown.payload
+    m_end                        = m_full+m_air
+    base.mass_properties.takeoff = m_full
+    
+
+ + +Now go back to the sizing_evaluation in Sizing.py to observe how this framework handles outputs. + + +

+    # battery calcs
+      
+    mass_out = results.segments[-1].conditions.weights.total_mass[-1,0]  #actual landing weight
+    Ereq_out = results.e_total
+    Preq_out = results.Pmax
+             
+    #errors  
+    dm       = (mass_out-m_guess)/m_guess
+    dE_total = (Ereq_out-Ereq_guess)/Ereq_guess
+    dPower   = (Preq_out-Preq_guess)/Preq_guess
+    
+    #pack up results
+    nexus.results = results 
+    
+    # return it to sizing loop (within SUAVE/Sizing/Sizing_Loop.py
+    f     = np.array([dm, dE_total, dPower])
+    y_out = np.array([mass_out, Ereq_out, Preq_out ])/scaling
+    
+    
+    
+
+It calculates the norm of the errors, then assigns them to an array f, and returns the expected y parameters to the Sizing_Loop object. \ No newline at end of file diff --git a/images/li_air_mass.png b/images/li_air_mass.png index b0babc8e..487a47c8 100644 Binary files a/images/li_air_mass.png and b/images/li_air_mass.png differ diff --git a/images/sizing_diagram.png b/images/sizing_diagram.png new file mode 100644 index 00000000..d5eebcc4 Binary files /dev/null and b/images/sizing_diagram.png differ