def test(self): """ This is an opt problem that tests the wingbox model with wave drag and the fuel vol constraint """ # Create a dictionary to store options about the surface mesh_dict = { 'num_y': 7, 'num_x': 2, 'wing_type': 'CRM', 'symmetry': True, 'num_twist_cp': 6, 'chord_cos_spacing': 0, 'span_cos_spacing': 0, } mesh, twist_cp = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name': 'wing', # name of the surface 'type': 'aerostruct', 'symmetry': True, # if true, model one half of wing # reflected across the plane y = 0 'S_ref_type': 'wetted', # how we compute the wing area, # can be 'wetted' or 'projected' 'fem_model_type': 'wingbox', 'spar_thickness_cp': np.array([0.004, 0.005, 0.005, 0.008, 0.008, 0.01]), # [m] 'skin_thickness_cp': np.array([0.005, 0.01, 0.015, 0.020, 0.025, 0.026]), 'twist_cp': np.array([4., 5., 8., 8., 8., 9.]), 'mesh': mesh, 'num_x': mesh.shape[0], 'num_y': mesh.shape[1], 'data_x_upper': upper_x, 'data_x_lower': lower_x, 'data_y_upper': upper_y, 'data_y_lower': lower_y, 'strength_factor_for_upper_skin': 1., # Aerodynamic performance of the lifting surface at # an angle of attack of 0 (alpha=0). # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. 'CL0': 0.0, # CL of the surface at alpha=0 'CD0': 0.0078, # CD of the surface at alpha=0 # Airfoil properties for viscous drag calculation 'k_lam': 0.05, # percentage of chord with laminar # flow, used for viscous drag 't_over_c_cp': np.array([0.08, 0.08, 0.08, 0.10, 0.10, 0.08]), 'original_wingbox_airfoil_t_over_c': 0.12, 'c_max_t': .38, # chordwise location of maximum thickness 'with_viscous': True, 'with_wave': True, # if true, compute wave drag # Structural values are based on aluminum 7075 'E': 73.1e9, # [Pa] Young's modulus 'G': ( 73.1e9 / 2 / 1.33 ), # [Pa] shear modulus (calculated using E and the Poisson's ratio here) 'yield': (420.e6 / 1.5), # [Pa] allowable yield stress 'mrho': 2.78e3, # [kg/m^3] material density 'strength_factor_for_upper_skin': 1.0, # the yield stress is multiplied by this factor for the upper skin # 'fem_origin' : 0.35, # normalized chordwise location of the spar 'wing_weight_ratio': 1.25, 'struct_weight_relief': True, # Constraints 'exact_failure_constraint': False, # if false, use KS function 'fuel_density': 803., 'Wf_reserve': 15000., } surfaces = [surf_dict] # Create the problem and assign the model group prob = Problem() # Add problem information as an independent variables component indep_var_comp = IndepVarComp() indep_var_comp.add_output('v', val=.85 * 295.07, units='m/s') indep_var_comp.add_output('alpha', val=0., units='deg') indep_var_comp.add_output('M', val=0.85) indep_var_comp.add_output('re', val=0.348 * 295.07 * .85 * 1. / (1.43 * 1e-5), units='1/m') indep_var_comp.add_output('rho', val=0.348, units='kg/m**3') indep_var_comp.add_output('CT', val=0.53 / 3600, units='1/s') indep_var_comp.add_output('R', val=14.307e6, units='m') indep_var_comp.add_output('W0', val=(143000 - 2.5 * 11600 + 34000) + 15000, units='kg') indep_var_comp.add_output('a', val=295.07, units='m/s') indep_var_comp.add_output('load_factor', val=1.) indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m') prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Loop over each surface in the surfaces list for surface in surfaces: # Get the surface name and create a group to contain components # only for this surface name = surface['name'] aerostruct_group = Aerostruct(surface=surface) # Add tmp_group to the problem with the name of the surface. prob.model.add_subsystem(name, aerostruct_group) # Loop through and add a certain number of aero points for i in range(1): point_name = 'AS_point_{}'.format(i) # Connect the parameters within the model for each aero point # Create the aero point group and add it to the model AS_point = AerostructPoint(surfaces=surfaces) prob.model.add_subsystem(point_name, AS_point) # Connect flow properties to the analysis point prob.model.connect('v', point_name + '.v') prob.model.connect('alpha', point_name + '.alpha') prob.model.connect('M', point_name + '.M') prob.model.connect('re', point_name + '.re') prob.model.connect('rho', point_name + '.rho') prob.model.connect('CT', point_name + '.CT') prob.model.connect('R', point_name + '.R') prob.model.connect('W0', point_name + '.W0') prob.model.connect('a', point_name + '.a') prob.model.connect('empty_cg', point_name + '.empty_cg') prob.model.connect('load_factor', point_name + '.load_factor') for surface in surfaces: prob.model.connect('load_factor', name + '.load_factor') com_name = point_name + '.' + name + '_perf.' prob.model.connect(name + '.K', point_name + '.coupled.' + name + '.K') # Connect aerodyamic mesh to coupled group mesh prob.model.connect(name + '.mesh', point_name + '.coupled.' + name + '.mesh') prob.model.connect( name + '.element_weights', point_name + '.coupled.' + name + '.element_weights') prob.model.connect(name + '.nodes', point_name + '.coupled.' + name + '.nodes') # Connect performance calculation variables prob.model.connect(name + '.nodes', com_name + 'nodes') prob.model.connect( name + '.cg_location', point_name + '.' + 'total_perf.' + name + '_cg_location') prob.model.connect( name + '.structural_weight', point_name + '.' + 'total_perf.' + name + '_structural_weight') # Connect wingbox properties to von Mises stress calcs prob.model.connect(name + '.Qz', com_name + 'Qz') prob.model.connect(name + '.Iz', com_name + 'Iz') prob.model.connect(name + '.J', com_name + 'J') prob.model.connect(name + '.A_enc', com_name + 'A_enc') prob.model.connect(name + '.htop', com_name + 'htop') prob.model.connect(name + '.hbottom', com_name + 'hbottom') prob.model.connect(name + '.hfront', com_name + 'hfront') prob.model.connect(name + '.hrear', com_name + 'hrear') prob.model.connect(name + '.spar_thickness', com_name + 'spar_thickness') prob.model.connect(name + '.skin_thickness', com_name + 'skin_thickness') prob.model.connect(name + '.t_over_c', com_name + 't_over_c') #======================================================================================= # Here we add the fuel volume constraint componenet to the model #======================================================================================= prob.model.add_subsystem('fuel_vol_delta', WingboxFuelVolDelta(surface=surface)) prob.model.connect('AS_point_0.fuelburn', 'fuel_vol_delta.fuelburn') prob.model.connect('wing.nodes', 'fuel_vol_delta.nodes') prob.model.connect('wing.A_int', 'fuel_vol_delta.A_int') #======================================================================================= #======================================================================================= from openmdao.api import ScipyOptimizeDriver prob.driver = ScipyOptimizeDriver() prob.driver.options['tol'] = 1e-9 # from openmdao.api import pyOptSparseDriver # prob.driver = pyOptSparseDriver() # prob.driver.add_recorder(SqliteRecorder("cases.sql")) # prob.driver.options['optimizer'] = "SNOPT" # prob.driver.opt_settings['Major optimality tolerance'] = 1e-6 # prob.driver.opt_settings['Major feasibility tolerance'] = 1e-8 # prob.driver.opt_settings['Major iterations limit'] = 200 prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5) prob.model.add_design_var('wing.twist_cp', lower=-15., upper=15., scaler=0.1) prob.model.add_design_var('wing.spar_thickness_cp', lower=0.003, upper=0.1, scaler=1e2) prob.model.add_design_var('wing.skin_thickness_cp', lower=0.003, upper=0.1, scaler=1e2) prob.model.add_design_var('wing.geometry.t_over_c_cp', lower=0.07, upper=0.2, scaler=10.) prob.model.add_constraint('AS_point_0.total_perf.CL', equals=0.5) prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.) #======================================================================================= # Here we add the fuel volume constraint #======================================================================================= prob.model.add_constraint('fuel_vol_delta.fuel_vol_delta', lower=0.) #======================================================================================= #======================================================================================= # Set up the problem prob.setup() # from openmdao.api import view_model # view_model(prob) prob.run_driver() # prob.check_partials(form='central', compact_print=True) # print(prob['AS_point_0.fuelburn'][0]) # print(prob['wing.structural_weight'][0]/1.25) assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 85033.119351, 1e-5) assert_rel_error(self, prob['wing.structural_weight'][0] / 1.25, 185666.261281, 1e-5)
prob.model.connect(name + '.A_enc', com_name + 'A_enc') prob.model.connect(name + '.htop', com_name + 'htop') prob.model.connect(name + '.hbottom', com_name + 'hbottom') prob.model.connect(name + '.hfront', com_name + 'hfront') prob.model.connect(name + '.hrear', com_name + 'hrear') prob.model.connect(name + '.spar_thickness', com_name + 'spar_thickness') prob.model.connect(name + '.t_over_c', com_name + 't_over_c') prob.model.connect('alpha', 'AS_point_0' + '.alpha') prob.model.connect('alpha_maneuver', 'AS_point_1' + '.alpha') # Here we add the fuel volume constraint componenet to the model prob.model.add_subsystem('fuel_vol_delta', WingboxFuelVolDelta(surface=surface)) prob.model.connect('wing.struct_setup.fuel_vols', 'fuel_vol_delta.fuel_vols') prob.model.connect('AS_point_0.fuelburn', 'fuel_vol_delta.fuelburn') if surf_dict['distributed_fuel_weight']: prob.model.connect('wing.struct_setup.fuel_vols', 'AS_point_0.coupled.wing.struct_states.fuel_vols') prob.model.connect('fuel_mass', 'AS_point_0.coupled.wing.struct_states.fuel_mass') prob.model.connect('wing.struct_setup.fuel_vols', 'AS_point_1.coupled.wing.struct_states.fuel_vols') prob.model.connect('fuel_mass', 'AS_point_1.coupled.wing.struct_states.fuel_mass') comp = ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn', units='kg')
prob.model.connect(name + '.t_over_c', com_name + 't_over_c') coupled_name = point_name + '.coupled.' + name prob.model.connect('point_masses', coupled_name + '.point_masses') prob.model.connect('point_mass_locations', coupled_name + '.point_mass_locations') #docs checkpoint 17 prob.model.connect('alpha', 'AS_point_0' + '.alpha') prob.model.connect('alpha_maneuver', 'AS_point_1' + '.alpha') #docs checkpoint 18 # Here we add the fuel volume constraint componenet to the model prob.model.add_subsystem('fuel_vol_delta', WingboxFuelVolDelta(surface=surface)) prob.model.connect('wing.struct_setup.fuel_vols', 'fuel_vol_delta.fuel_vols') prob.model.connect('AS_point_0.fuelburn', 'fuel_vol_delta.fuelburn') if surf_dict['distributed_fuel_weight']: prob.model.connect('wing.struct_setup.fuel_vols', 'AS_point_0.coupled.wing.struct_states.fuel_vols') prob.model.connect('fuel_mass', 'AS_point_0.coupled.wing.struct_states.fuel_mass') prob.model.connect('wing.struct_setup.fuel_vols', 'AS_point_1.coupled.wing.struct_states.fuel_vols') prob.model.connect('fuel_mass', 'AS_point_1.coupled.wing.struct_states.fuel_mass') #docs checkpoint 19 comp = om.ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn', units='kg') prob.model.add_subsystem('fuel_diff', comp, promotes_inputs=['fuel_mass'],
def test(self): # Create a dictionary to store options about the surface mesh_dict = { 'num_y': 7, 'num_x': 3, 'wing_type': 'uCRM_based', 'symmetry': True, 'chord_cos_spacing': 0, 'span_cos_spacing': 0, 'num_twist_cp': 6, } mesh, twist_cp = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name': 'wing', # name of the surface 'symmetry': True, # if true, model one half of wing 'S_ref_type': 'wetted', # how we compute the wing area, # can be 'wetted' or 'projected' 'mesh': mesh, 'twist_cp': np.array([4., 5., 8., 8., 8., 9.]), 'fem_model_type': 'wingbox', 'data_x_upper': upper_x, 'data_x_lower': lower_x, 'data_y_upper': upper_y, 'data_y_lower': lower_y, 'spar_thickness_cp': np.array([0.004, 0.005, 0.005, 0.008, 0.008, 0.01]), # [m] 'skin_thickness_cp': np.array([0.005, 0.01, 0.015, 0.020, 0.025, 0.026]), 'original_wingbox_airfoil_t_over_c': 0.12, # Aerodynamic deltas. # These CL0 and CD0 values are added to the CL and CD # obtained from aerodynamic analysis of the surface to get # the total CL and CD. # These CL0 and CD0 values do not vary wrt alpha. # They can be used to account for things that are not included, such as contributions from the fuselage, nacelles, tail surfaces, etc. 'CL0': 0.0, 'CD0': 0.0078, 'with_viscous': True, # if true, compute viscous drag 'with_wave': True, # if true, compute wave drag # Airfoil properties for viscous drag calculation 'k_lam': 0.05, # percentage of chord with laminar # flow, used for viscous drag 'c_max_t': .38, # chordwise location of maximum thickness 't_over_c_cp': np.array([0.08, 0.08, 0.08, 0.10, 0.10, 0.08]), # Structural values are based on aluminum 7075 'E': 73.1e9, # [Pa] Young's modulus 'G': ( 73.1e9 / 2 / 1.33 ), # [Pa] shear modulus (calculated using E and the Poisson's ratio here) 'yield': (420.e6 / 1.5), # [Pa] allowable yield stress 'mrho': 2.78e3, # [kg/m^3] material density 'strength_factor_for_upper_skin': 1.0, # the yield stress is multiplied by this factor for the upper skin 'wing_weight_ratio': 1.25, 'exact_failure_constraint': False, # if false, use KS function 'struct_weight_relief': True, 'distributed_fuel_weight': True, 'fuel_density': 803., # [kg/m^3] fuel density (only needed if the fuel-in-wing volume constraint is used) 'Wf_reserve': 15000., # [kg] reserve fuel mass } surfaces = [surf_dict] # Create the problem and assign the model group prob = Problem() # Add problem information as an independent variables component indep_var_comp = IndepVarComp() indep_var_comp.add_output('v', val=np.array([.85 * 295.07, .64 * 340.294]), units='m/s') indep_var_comp.add_output('alpha', val=0., units='deg') indep_var_comp.add_output('alpha_maneuver', val=0., units='deg') indep_var_comp.add_output('Mach_number', val=np.array([0.85, 0.64])) indep_var_comp.add_output('re',val=np.array([0.348*295.07*.85*1./(1.43*1e-5), \ 1.225*340.294*.64*1./(1.81206*1e-5)]), units='1/m') indep_var_comp.add_output('rho', val=np.array([0.348, 1.225]), units='kg/m**3') indep_var_comp.add_output('CT', val=0.53 / 3600, units='1/s') indep_var_comp.add_output('R', val=14.307e6, units='m') indep_var_comp.add_output('W0', val=148000 + surf_dict['Wf_reserve'], units='kg') indep_var_comp.add_output('speed_of_sound', val=np.array([295.07, 340.294]), units='m/s') indep_var_comp.add_output('load_factor', val=np.array([1., 2.5])) indep_var_comp.add_output('empty_cg', val=np.zeros((3)), units='m') indep_var_comp.add_output('fuel_mass', val=10000., units='kg') prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Loop over each surface in the surfaces list for surface in surfaces: # Get the surface name and create a group to contain components # only for this surface name = surface['name'] aerostruct_group = AerostructGeometry(surface=surface) # Add group to the problem with the name of the surface. prob.model.add_subsystem(name, aerostruct_group) # Loop through and add a certain number of aerostruct points for i in range(2): point_name = 'AS_point_{}'.format(i) # Connect the parameters within the model for each aerostruct point # Create the aero point group and add it to the model AS_point = AerostructPoint(surfaces=surfaces, internally_connect_fuelburn=False) prob.model.add_subsystem(point_name, AS_point) # Connect flow properties to the analysis point prob.model.connect('v', point_name + '.v', src_indices=[i]) prob.model.connect('Mach_number', point_name + '.Mach_number', src_indices=[i]) prob.model.connect('re', point_name + '.re', src_indices=[i]) prob.model.connect('rho', point_name + '.rho', src_indices=[i]) prob.model.connect('CT', point_name + '.CT') prob.model.connect('R', point_name + '.R') prob.model.connect('W0', point_name + '.W0') prob.model.connect('speed_of_sound', point_name + '.speed_of_sound', src_indices=[i]) prob.model.connect('empty_cg', point_name + '.empty_cg') prob.model.connect('load_factor', point_name + '.load_factor', src_indices=[i]) prob.model.connect('fuel_mass', point_name + '.total_perf.L_equals_W.fuelburn') prob.model.connect('fuel_mass', point_name + '.total_perf.CG.fuelburn') for surface in surfaces: name = surface['name'] if surf_dict['distributed_fuel_weight']: prob.model.connect('load_factor', point_name + '.coupled.load_factor', src_indices=[i]) com_name = point_name + '.' + name + '_perf.' prob.model.connect( name + '.local_stiff_transformed', point_name + '.coupled.' + name + '.local_stiff_transformed') prob.model.connect(name + '.nodes', point_name + '.coupled.' + name + '.nodes') # Connect aerodyamic mesh to coupled group mesh prob.model.connect(name + '.mesh', point_name + '.coupled.' + name + '.mesh') if surf_dict['struct_weight_relief']: prob.model.connect( name + '.element_mass', point_name + '.coupled.' + name + '.element_mass') # Connect performance calculation variables prob.model.connect(name + '.nodes', com_name + 'nodes') prob.model.connect( name + '.cg_location', point_name + '.' + 'total_perf.' + name + '_cg_location') prob.model.connect( name + '.structural_mass', point_name + '.' + 'total_perf.' + name + '_structural_mass') # Connect wingbox properties to von Mises stress calcs prob.model.connect(name + '.Qz', com_name + 'Qz') prob.model.connect(name + '.J', com_name + 'J') prob.model.connect(name + '.A_enc', com_name + 'A_enc') prob.model.connect(name + '.htop', com_name + 'htop') prob.model.connect(name + '.hbottom', com_name + 'hbottom') prob.model.connect(name + '.hfront', com_name + 'hfront') prob.model.connect(name + '.hrear', com_name + 'hrear') prob.model.connect(name + '.spar_thickness', com_name + 'spar_thickness') prob.model.connect(name + '.t_over_c', com_name + 't_over_c') prob.model.connect('alpha', 'AS_point_0' + '.alpha') prob.model.connect('alpha_maneuver', 'AS_point_1' + '.alpha') # Here we add the fuel volume constraint componenet to the model prob.model.add_subsystem('fuel_vol_delta', WingboxFuelVolDelta(surface=surface)) prob.model.connect('wing.struct_setup.fuel_vols', 'fuel_vol_delta.fuel_vols') prob.model.connect('AS_point_0.fuelburn', 'fuel_vol_delta.fuelburn') if surf_dict['distributed_fuel_weight']: prob.model.connect( 'wing.struct_setup.fuel_vols', 'AS_point_0.coupled.wing.struct_states.fuel_vols') prob.model.connect( 'fuel_mass', 'AS_point_0.coupled.wing.struct_states.fuel_mass') prob.model.connect( 'wing.struct_setup.fuel_vols', 'AS_point_1.coupled.wing.struct_states.fuel_vols') prob.model.connect( 'fuel_mass', 'AS_point_1.coupled.wing.struct_states.fuel_mass') comp = ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn') prob.model.add_subsystem('fuel_diff', comp, promotes_inputs=['fuel_mass'], promotes_outputs=['fuel_diff']) prob.model.connect('AS_point_0.fuelburn', 'fuel_diff.fuelburn') ## Use these settings if you do not have pyOptSparse or SNOPT prob.driver = ScipyOptimizeDriver() prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['tol'] = 1e-8 recorder = SqliteRecorder("unit_test.db") prob.driver.add_recorder(recorder) # We could also just use prob.driver.recording_options['includes']=['*'] here, but for large meshes the database file becomes extremely large. So we just select the variables we need. prob.driver.recording_options['includes'] = [ 'alpha', 'rho', 'v', 'cg', 'AS_point_1.cg', 'AS_point_0.cg', 'AS_point_0.coupled.wing_loads.loads', 'AS_point_1.coupled.wing_loads.loads', 'AS_point_0.coupled.wing.normals', 'AS_point_1.coupled.wing.normals', 'AS_point_0.coupled.wing.widths', 'AS_point_1.coupled.wing.widths', 'AS_point_0.coupled.aero_states.wing_sec_forces', 'AS_point_1.coupled.aero_states.wing_sec_forces', 'AS_point_0.wing_perf.CL1', 'AS_point_1.wing_perf.CL1', 'AS_point_0.coupled.wing.S_ref', 'AS_point_1.coupled.wing.S_ref', 'wing.geometry.twist', 'wing.mesh', 'wing.skin_thickness', 'wing.spar_thickness', 'wing.t_over_c', 'wing.structural_mass', 'AS_point_0.wing_perf.vonmises', 'AS_point_1.wing_perf.vonmises', 'AS_point_0.coupled.wing.def_mesh', 'AS_point_1.coupled.wing.def_mesh', ] prob.driver.recording_options['record_objectives'] = True prob.driver.recording_options['record_constraints'] = True prob.driver.recording_options['record_desvars'] = True prob.driver.recording_options['record_inputs'] = True prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5) prob.model.add_design_var('wing.twist_cp', lower=-15., upper=15., scaler=0.1) prob.model.add_design_var('wing.spar_thickness_cp', lower=0.003, upper=0.1, scaler=1e2) prob.model.add_design_var('wing.skin_thickness_cp', lower=0.003, upper=0.1, scaler=1e2) prob.model.add_design_var('wing.geometry.t_over_c_cp', lower=0.07, upper=0.2, scaler=10.) prob.model.add_design_var('fuel_mass', lower=0., upper=2e5, scaler=1e-5) prob.model.add_design_var('alpha_maneuver', lower=-15., upper=15) prob.model.add_constraint('AS_point_0.CL', equals=0.5) prob.model.add_constraint('AS_point_1.L_equals_W', equals=0.) prob.model.add_constraint('AS_point_1.wing_perf.failure', upper=0.) prob.model.add_constraint('fuel_vol_delta.fuel_vol_delta', lower=0.) prob.model.add_constraint('fuel_diff', equals=0.) # Set up the problem prob.setup() prob.run_driver() # print(prob['AS_point_0.fuelburn'][0]) # print(prob['wing.structural_mass'][0]/surf_dict['wing_weight_ratio']) # print(prob['wing.geometry.t_over_c_cp']) assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 101937.827384, 1e-5) assert_rel_error( self, prob['wing.structural_mass'][0] / surf_dict['wing_weight_ratio'], 36539.6437566, 1e-5) assert_rel_error( self, prob['wing.geometry.t_over_c_cp'], np.array([ 0.10247881, 0.08207636, 0.11114547, 0.13114547, 0.10207636, 0.09365598 ]), 1e-5)