def test_brachistochrone_integrated_control_radau_ps(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s', targets=['v']) phase.add_state('theta', targets='theta', fix_initial=False, rate_source='theta_dot', units='rad') phase.add_control('theta_dot', units='deg/s', rate_continuity=True, lower=0, upper=60) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.model.options['assembled_jac_type'] = 'csc' p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.states:theta'] = np.radians( phase.interpolate(ys=[0.05, 100.0], nodes='state_input')) p['phase0.controls:theta_dot'] = phase.interpolate( ys=[50, 50], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) sim_out = phase.simulate(times_per_seg=20) x_sol = p.get_val('phase0.timeseries.states:x') y_sol = p.get_val('phase0.timeseries.states:y') v_sol = p.get_val('phase0.timeseries.states:v') theta_sol = p.get_val('phase0.timeseries.states:theta') theta_dot_sol = p.get_val('phase0.timeseries.controls:theta_dot') time_sol = p.get_val('phase0.timeseries.time') x_sim = sim_out.get_val('phase0.timeseries.states:x') y_sim = sim_out.get_val('phase0.timeseries.states:y') v_sim = sim_out.get_val('phase0.timeseries.states:v') theta_sim = sim_out.get_val('phase0.timeseries.states:theta') theta_dot_sim = sim_out.get_val('phase0.timeseries.controls:theta_dot') time_sim = sim_out.get_val('phase0.timeseries.time') x_interp = interp1d(time_sim[:, 0], x_sim[:, 0]) y_interp = interp1d(time_sim[:, 0], y_sim[:, 0]) v_interp = interp1d(time_sim[:, 0], v_sim[:, 0]) theta_interp = interp1d(time_sim[:, 0], theta_sim[:, 0]) theta_dot_interp = interp1d(time_sim[:, 0], theta_dot_sim[:, 0]) assert_near_equal(x_interp(time_sol), x_sol, tolerance=1.0E-5) assert_near_equal(y_interp(time_sol), y_sol, tolerance=1.0E-5) assert_near_equal(v_interp(time_sol), v_sol, tolerance=1.0E-5) assert_near_equal(theta_interp(time_sol), theta_sol, tolerance=1.0E-5) assert_near_equal(theta_dot_interp(time_sol), theta_dot_sol, tolerance=1.0E-5)
def test_cp_rev_mode(self): ''' ----------------------------------------------------- The erroneous output contained these values: Raw Forward Derivative (Jfor) [[62.5 62.5 62.5]] Raw Reverse Derivative (Jrev) [[31.25 31.25 31.25]] Raw FD Derivative (Jfd) [[62.49998444 62.49998444 62.49998444]] ... Raw Forward Derivative (Jfor) [[62.5 62.5 62.5 62.5]] Raw Reverse Derivative (Jrev) [[31.25 31.25 31.25 31.25]] Raw FD Derivative (Jfd) [[62.49998444 62.49998444 62.49998444 62.49998444]] ----------------------------------------------------- The corrected output contains these values: Raw Forward Derivative (Jfor) [[62.5 62.5 62.5]] Raw Reverse Derivative (Jrev) [[62.5 62.5 62.5]] Raw FD Derivative (Jfd) [[62.49998444 62.49998444 62.49998444]] ... Raw Forward Derivative (Jfor) [[62.5 62.5 62.5 62.5]] Raw Reverse Derivative (Jrev) [[62.5 62.5 62.5 62.5]] Raw FD Derivative (Jfd) [[62.49998444 62.49998444 62.49998444 62.49998444]] ----------------------------------------------------- ''' size = 7 comm = MPI.COMM_WORLD rank = comm.rank sizes, offsets = evenly_distrib_idxs(comm.size, size) prob = om.Problem() model = prob.model # Create a distributed source for the distributed input. ivc = om.IndepVarComp() ivc.add_output('x_dist', np.zeros(sizes[rank]), distributed=True) ivc.add_output('x_nd', val=1) model.add_subsystem("indep", ivc) model.add_subsystem("D1", MixedDistrib2()) model.add_subsystem('con_cmp1', om.ExecComp('con1 = y**2'), promotes=['con1', 'y']) model.connect('indep.x_dist', 'D1.in_dist') model.connect('indep.x_nd', ['D1.in_nd','y']) prob.driver = om.ScipyOptimizeDriver() prob.driver.options['optimizer'] = 'SLSQP' model.add_design_var('indep.x_nd', lower=5, upper=10) model.add_constraint('con1', upper=90) model.add_objective('D1.out_nd') prob.setup(force_alloc_complex=True) # Set initial values of distributed variable. x_dist_init = np.ones(sizes[rank]) prob.set_val('indep.x_dist', x_dist_init) # Set initial values of non-distributed variable. prob.set_val('indep.x_nd', 10) prob.run_model() stream = StringIO() prob.check_partials(out_stream=stream) out_str = stream.getvalue() msg = "Problem.check_partials() output contains a reverse partial derivative divided by comm size." self.assertNotRegex(out_str, ".*31.25 31.25 31.25.*", msg)
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3, compressed=True, optimizer='SLSQP', dynamic_simul_derivs=True, force_alloc_complex=False, solve_segments=False, run_driver=True): p = om.Problem(model=om.Group()) if optimizer == 'SNOPT': p.driver = om.pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.opt_settings['Major iterations limit'] = 100 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings['iSumm'] = 6 else: p.driver = om.ScipyOptimizeDriver() if dynamic_simul_derivs: p.driver.declare_coloring() if transcription == 'gauss-lobatto': transcription = dm.GaussLobatto(num_segments=num_segments, order=transcription_order, compressed=compressed) fix_final = not solve_segments elif transcription == 'radau-ps': transcription = dm.Radau(num_segments=num_segments, order=transcription_order, compressed=compressed) fix_final = not solve_segments traj = dm.Trajectory() phase = dm.Phase(ode_class=BrachistochroneVectorStatesODE, transcription=transcription) traj.add_phase('phase0', phase) p.model.add_subsystem('traj0', traj) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') # can't fix final position if you're solving the segments phase.add_state('pos', fix_initial=True, fix_final=fix_final, solve_segments=solve_segments, ref=[1, 1]) # phase.add_state('v', fix_initial=True, fix_final=False, solve_segments=solve_segments) # phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665) if not fix_final: phase.add_boundary_constraint('pos', loc='final', equals=[10, 5]) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True, force_alloc_complex=force_alloc_complex) p['traj0.phase0.t_initial'] = 0.0 p['traj0.phase0.t_duration'] = 1.8016 pos0 = [0, 10] posf = [10, 5] p['traj0.phase0.states:pos'] = phase.interp('pos', [pos0, posf]) p['traj0.phase0.states:v'] = phase.interp('v', [0, 9.9]) p['traj0.phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['traj0.phase0.parameters:g'] = 9.80665 p.run_model() if run_driver: p.run_driver() return p
def test_feature_vectorized_derivs(self): import numpy as np import openmdao.api as om SIZE = 5 class ExpensiveAnalysis(om.ExplicitComponent): def setup(self): self.add_input('x', val=np.ones(SIZE)) self.add_input('y', val=np.ones(SIZE)) self.add_output('f', shape=1) self.declare_partials('f', 'x') self.declare_partials('f', 'y') def compute(self, inputs, outputs): outputs['f'] = np.sum(inputs['x']**inputs['y']) def compute_partials(self, inputs, J): J['f', 'x'] = inputs['y'] * inputs['x']**(inputs['y'] - 1) J['f', 'y'] = (inputs['x']**inputs['y']) * np.log(inputs['x']) class CheapConstraint(om.ExplicitComponent): def setup(self): self.add_input('y', val=np.ones(SIZE)) self.add_output('g', shape=SIZE) row_col = np.arange(SIZE, dtype=int) self.declare_partials('g', 'y', rows=row_col, cols=row_col) self.limit = 2 * np.arange(SIZE) def compute(self, inputs, outputs): outputs['g'] = inputs['y']**2 - self.limit def compute_partials(self, inputs, J): J['g', 'y'] = 2 * inputs['y'] p = om.Problem() p.model.set_input_defaults('x', val=2 * np.ones(SIZE)) p.model.set_input_defaults('y', val=2 * np.ones(SIZE)) p.model.add_subsystem('obj', ExpensiveAnalysis(), promotes=['x', 'y', 'f']) p.model.add_subsystem('constraint', CheapConstraint(), promotes=['y', 'g']) p.model.add_design_var('x', lower=.1, upper=10000) p.model.add_design_var('y', lower=-1000, upper=10000) p.model.add_constraint('g', upper=0, vectorize_derivs=True) p.model.add_objective('f') p.setup(mode='rev') p.run_model() p.driver = om.ScipyOptimizeDriver() p.run_driver() assert_near_equal(p['x'], [0.10000691, 0.1, 0.1, 0.1, 0.1], 1e-5) assert_near_equal(p['y'], [0, 1.41421, 2.0, 2.44948, 2.82842], 1e-5)
def test_brachistochrone_upstream_control(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm import matplotlib.pyplot as plt plt.switch_backend('Agg') from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE # # Define the OpenMDAO problem # p = om.Problem(model=om.Group()) # Instantiate the transcription so we can get the number of nodes from it while # building the problem. tx = dm.GaussLobatto(num_segments=10, order=3) # Add an indep var comp to provide the external control values ivc = p.model.add_subsystem('control_ivc', om.IndepVarComp(), promotes_outputs=['*']) # Add the output to provide the values of theta at the control input nodes of the transcription. ivc.add_output('theta', shape=(tx.grid_data.subset_num_nodes['control_input']), units='rad') # Add this external control as a design variable p.model.add_design_var('theta', units='rad', lower=1.0E-5, upper=np.pi) # Connect this to controls:theta in the appropriate phase. # connect calls are cached, so we can do this before we actually add the trajectory to the problem. p.model.connect('theta', 'traj.phase0.controls:theta') # # Define a Trajectory object # traj = dm.Trajectory() p.model.add_subsystem('traj', subsys=traj) # # Define a Dymos Phase object with GaussLobatto Transcription # phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx) traj.add_phase(name='phase0', phase=phase) # # Set the time options # Time has no targets in our ODE. # We fix the initial time so that the it is not a design variable in the optimization. # The duration of the phase is allowed to be optimized, but is bounded on [0.5, 10]. # phase.set_time_options(fix_initial=True, duration_bounds=(0.5, 10.0), units='s') # # Set the time options # Initial values of positions and velocity are all fixed. # The final value of position are fixed, but the final velocity is a free variable. # The equations of motion are not functions of position, so 'x' and 'y' have no targets. # The rate source points to the output in the ODE which provides the time derivative of the # given state. phase.add_state('x', fix_initial=True, fix_final=True, units='m', rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=True, units='m', rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, units='m/s', rate_source='vdot', targets=['v']) # Define theta as a control. # Use opt=False to allow it to be connected to an external source. # Arguments lower and upper are no longer valid for an input control. phase.add_control(name='theta', targets=['theta'], opt=False) # Minimize final time. phase.add_objective('time', loc='final') # Set the driver. p.driver = om.ScipyOptimizeDriver() # Allow OpenMDAO to automatically determine our sparsity pattern. # Doing so can significant speed up the execution of Dymos. p.driver.declare_coloring() # Setup the problem p.setup(check=True) # Now that the OpenMDAO problem is setup, we can set the values of the states and controls. p.set_val('traj.phase0.states:x', phase.interp('x', [0, 10]), units='m') p.set_val('traj.phase0.states:y', phase.interp('y', [10, 5]), units='m') p.set_val('traj.phase0.states:v', phase.interp('v', [0, 5]), units='m/s') p.set_val('traj.phase0.controls:theta', phase.interp('theta', [90, 90]), units='deg') # Run the driver to solve the problem p.run_driver() # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Check the validity of our results by using scipy.integrate.solve_ivp to # integrate the solution. sim_out = traj.simulate() # Plot the results fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4.5)) axes[0].plot(p.get_val('traj.phase0.timeseries.states:x'), p.get_val('traj.phase0.timeseries.states:y'), 'ro', label='solution') axes[0].plot(sim_out.get_val('traj.phase0.timeseries.states:x'), sim_out.get_val('traj.phase0.timeseries.states:y'), 'b-', label='simulation') axes[0].set_xlabel('x (m)') axes[0].set_ylabel('y (m/s)') axes[0].legend() axes[0].grid() axes[1].plot(p.get_val('traj.phase0.timeseries.time'), p.get_val('traj.phase0.timeseries.controls:theta', units='deg'), 'ro', label='solution') axes[1].plot(sim_out.get_val('traj.phase0.timeseries.time'), sim_out.get_val('traj.phase0.timeseries.controls:theta', units='deg'), 'b-', label='simulation') axes[1].set_xlabel('time (s)') axes[1].set_ylabel(r'$\theta$ (deg)') axes[1].legend() axes[1].grid() plt.show()
def test(self): import numpy as np from openaerostruct.geometry.utils import generate_mesh from openaerostruct.integration.aerostruct_groups import AerostructGeometry, AerostructPoint from openaerostruct.utils.constants import grav_constant import openmdao.api as om # Create a dictionary to store options about the surface mesh_dict = {'num_y' : 5, 'num_x' : 2, 'wing_type' : 'CRM', 'symmetry' : True, 'num_twist_cp' : 5} mesh, twist_cp = generate_mesh(mesh_dict) surface = { # Wing definition 'name' : 'wing', # name of the surface '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' : 'tube', 'thickness_cp' : np.array([.1, .2, .3]), 'twist_cp' : twist_cp, 'mesh' : mesh, # 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.015, # 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.15]), # thickness over chord ratio (NACA0015) 'c_max_t' : .303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous' : True, 'with_wave' : False, # if true, compute wave drag # Structural values are based on aluminum 7075 'E' : 70.e9, # [Pa] Young's modulus of the spar 'G' : 30.e9, # [Pa] shear modulus of the spar 'yield' : 500.e6 / 2.5, # [Pa] yield stress divided by 2.5 for limiting case 'mrho' : 3.e3, # [kg/m^3] material density 'fem_origin' : 0.35, # normalized chordwise location of the spar 'wing_weight_ratio' : 2., 'struct_weight_relief' : False, # True to add the weight of the structure to the loads on the structure 'distributed_fuel_weight' : False, # Constraints 'exact_failure_constraint' : False, # if false, use KS function } # Create the problem and assign the model group prob = om.Problem() # Add problem information as an independent variables component indep_var_comp = om.IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=5., units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('re', val=1.e6, units='1/m') indep_var_comp.add_output('rho', val=0.38, units='kg/m**3') indep_var_comp.add_output('CT', val=grav_constant * 17.e-6, units='1/s') indep_var_comp.add_output('R', val=11.165e6, units='m') indep_var_comp.add_output('W0', val=0.4 * 3e5, units='kg') indep_var_comp.add_output('speed_of_sound', val=295.4, 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=['*']) aerostruct_group = AerostructGeometry(surface=surface) name = 'wing' # Add tmp_group to the problem with the name of the surface. prob.model.add_subsystem(name, aerostruct_group) point_name = 'AS_point_0' # Create the aero point group and add it to the model AS_point = AerostructPoint(surfaces=[surface]) prob.model.add_subsystem(point_name, AS_point, promotes_inputs=['v', 'alpha', 'Mach_number', 're', 'rho', 'CT', 'R', 'W0', 'speed_of_sound', 'empty_cg', 'load_factor']) 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') # Connect performance calculation variables prob.model.connect(name + '.radius', com_name + '.radius') prob.model.connect(name + '.thickness', com_name + '.thickness') 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') prob.model.connect(name + '.t_over_c', com_name + '.t_over_c') prob.driver = om.ScipyOptimizeDriver() prob.driver.options['tol'] = 1e-9 recorder = om.SqliteRecorder("aerostruct.db") prob.driver.add_recorder(recorder) prob.driver.recording_options['record_derivatives'] = True prob.driver.recording_options['includes'] = ['*'] # Setup problem and add design variables, constraint, and objective prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.) prob.model.add_design_var('wing.thickness_cp', lower=0.01, upper=0.5, scaler=1e2) prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.) prob.model.add_constraint('AS_point_0.wing_perf.thickness_intersects', upper=0.) # Add design variables, constraisnt, and objective on the problem prob.model.add_design_var('alpha', lower=-10., upper=10.) prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.) prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5) # Set up the problem prob.setup(check=True) prob.run_driver() assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 97696.33252514644, 1e-8)
def test_brachistochrone_integrated_control_radau_ps(self): import numpy as np import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() traj = dm.Trajectory() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=10)) traj.add_phase('phase0', phase) p.model.add_subsystem('traj', traj) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', units='m/s') phase.add_state('theta', targets='theta', fix_initial=False, rate_source='theta_dot') phase.add_control('theta_dot', units='deg/s', rate_continuity=True, shape=(1, ), lower=0, upper=60) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) phase.set_simulate_options(rtol=1.0E-8, atol=1.0E-8) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p.set_val('traj.phase0.states:x', phase.interp('x', ys=[0, 10])) p.set_val('traj.phase0.states:y', phase.interp('y', ys=[10, 5])) p.set_val('traj.phase0.states:v', phase.interp('v', ys=[0, 9.9])) p.set_val('traj.phase0.states:theta', phase.interp('theta', ys=[5, 100.5]), units='deg') p.set_val('traj.phase0.controls:theta_dot', phase.interp('theta_dot', ys=[50, 50])) # Solve for the optimal trajectory dm.run_problem(p, simulate=True, make_plots=True) sol_case = om.CaseReader('dymos_solution.db').get_case('final') sim_case = om.CaseReader('dymos_simulation.db').get_case('final') # Test the results assert_near_equal(sol_case.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) x_sol = sol_case.get_val('traj.phase0.timeseries.states:x') y_sol = sol_case.get_val('traj.phase0.timeseries.states:y') v_sol = sol_case.get_val('traj.phase0.timeseries.states:v') theta_sol = sol_case.get_val('traj.phase0.timeseries.states:theta') theta_dot_sol = sol_case.get_val( 'traj.phase0.timeseries.controls:theta_dot') time_sol = sol_case.get_val('traj.phase0.timeseries.time') x_sim = sim_case.get_val('traj.phase0.timeseries.states:x') y_sim = sim_case.get_val('traj.phase0.timeseries.states:y') v_sim = sim_case.get_val('traj.phase0.timeseries.states:v') theta_sim = sim_case.get_val('traj.phase0.timeseries.states:theta') theta_dot_sim = sim_case.get_val( 'traj.phase0.timeseries.controls:theta_dot') time_sim = sim_case.get_val('traj.phase0.timeseries.time') x_interp = interp1d(time_sim[:, 0], x_sim[:, 0]) y_interp = interp1d(time_sim[:, 0], y_sim[:, 0]) v_interp = interp1d(time_sim[:, 0], v_sim[:, 0]) theta_interp = interp1d(time_sim[:, 0], theta_sim[:, 0]) theta_dot_interp = interp1d(time_sim[:, 0], theta_dot_sim[:, 0]) assert_near_equal(x_interp(time_sol), x_sol, tolerance=1.0E-4) assert_near_equal(y_interp(time_sol), y_sol, tolerance=1.0E-4) assert_near_equal(v_interp(time_sol), v_sol, tolerance=1.0E-4) assert_near_equal(theta_interp(time_sol), theta_sol, tolerance=1.0E-4) assert_near_equal(theta_dot_interp(time_sol), theta_dot_sol, tolerance=1.0E-4)
def test_parameter_multiple_boundary_constraints(self): expected = 'In phase phase0, parameter `g` is subject to multiple boundary ' \ 'or path constraints.\nParameters are single values that do not change in ' \ 'time, and may only be used in a single boundary or path constraint.' for tx in (dm.GaussLobatto, dm.Radau, dm.ExplicitShooting): with self.subTest(): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx(num_segments=5, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=True, units='m/s**2', val=9.80665) # We'll let g vary, but make sure it hits the desired value. # It's a static parameter, so it shouldn't matter whether we enforce it # at the start or the end of the phase, so here we'll do both. # Note if we make these equality constraints, some optimizers (SLSQP) will # see the problem as infeasible. phase.add_boundary_constraint('x', loc='final', units='m', equals=10) phase.add_boundary_constraint('y', loc='final', units='m', equals=5) phase.add_boundary_constraint('g', loc='initial', units='m/s**2', upper=9.80665) phase.add_boundary_constraint('g', loc='final', units='m/s**2', upper=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() with self.assertRaises(RuntimeError) as e: p.setup() self.assertEqual(str(e.exception), expected)
def test_parameter_path_constraint(self): for tx in (dm.GaussLobatto, dm.Radau, dm.ExplicitShooting): with self.subTest(): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx(num_segments=5, order=3)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=True, units='m/s**2', val=9.80665) # We'll let g vary, but make sure it hits the desired value. # It's a static parameter, so it shouldn't matter whether we enforce it # at the start or the end of the phase. In this case we'll use a path # constraint to enforce it, but it will still just be a single scalar constraint. phase.add_boundary_constraint('x', loc='final', units='m', equals=10) phase.add_boundary_constraint('y', loc='final', units='m', equals=5) phase.add_path_constraint('g', units='m/s**2', upper=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['phase0.parameters:g'] = 5 p.run_driver() assert_near_equal(p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-4) assert_near_equal( p.get_val('phase0.timeseries.parameters:g')[0], 9.80665, tolerance=1.0E-6) assert_near_equal( p.get_val('phase0.timeseries.parameters:g')[-1], 9.80665, tolerance=1.0E-6)
def test_brachistochrone_forward_shooting(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") # Trigger a warning. phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.RungeKutta(num_segments=20)) assert issubclass(w[-1].category, DeprecationWarning) expected_msg = 'The RungeKutta transcription is deprecated and ' \ 'will be removed in Dymos v1.0.0.\nFor equivalent behavior, users ' \ 'should switch to GaussLobatto(order=3, solve_segments=\'forward\') or ' \ 'GaussLobatto(order=3, solve_segments=\'backward\')' self.assertEqual(str(w[-1].message), expected_msg) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(0.5, 2.0)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665) # Final state values can't be controlled with simple bounds in ExplicitPhase, # so use nonlinear boundary constraints instead. phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = 0 p['phase0.states:y'] = 10 p['phase0.states:v'] = 0 p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p['phase0.time'][-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = phase.simulate() assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1, 0], 10, tolerance=1.0E-3) assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1, 0], 5, tolerance=1.0E-3)
def test_control_rate2_boundary_constraint_gl(self): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=20, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=False) phase.add_state('y', fix_initial=True, fix_final=False) phase.add_state('v', fix_initial=True, fix_final=False) phase.add_control('theta', continuity=True, rate_continuity=True, rate2_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665) phase.add_boundary_constraint('theta_rate2', loc='final', equals=0.0, units='deg/s**2') # Minimize time at the end of the phase phase.add_objective('time') p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100]) p['phase0.parameters:g'] = 8 p.run_driver() plt.plot(p.get_val('phase0.timeseries.states:x'), p.get_val('phase0.timeseries.states:y'), 'ko') plt.figure() plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.controls:theta'), 'ro') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate'), 'bo') plt.plot(p.get_val('phase0.timeseries.time'), p.get_val('phase0.timeseries.control_rates:theta_rate2'), 'go') plt.show() assert_near_equal( p.get_val('phase0.timeseries.control_rates:theta_rate2')[-1], 0, tolerance=1.0E-6)
def test_brachistochrone_for_docs_gauss_lobatto(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.brachistochrone import BrachistochroneODE import matplotlib.pyplot as plt # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase('phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state('x', rate_source=BrachistochroneODE.states['x']['rate_source'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('y', rate_source=BrachistochroneODE.states['y']['rate_source'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('v', rate_source=BrachistochroneODE.states['v']['rate_source'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['traj.phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['traj.phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() plot_results([('traj.phase0.timeseries.states:x', 'traj.phase0.timeseries.states:y', 'x (m)', 'y (m)'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:theta', 'time (s)', 'theta (deg)')], title='Brachistochrone Solution\nHigh-Order Gauss-Lobatto Method', p_sol=p, p_sim=exp_out) plt.show()
def test_control_rate2_path_constraint_gl(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_rel_error import dymos as dm from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10, order=5)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state( 'x', fix_initial=True, fix_final=True, rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units']) phase.add_state( 'y', fix_initial=True, fix_final=True, rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units']) phase.add_state( 'v', fix_initial=True, rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units']) phase.add_control( 'theta', units='deg', targets=BrachistochroneODE.parameters['theta']['targets'], rate_continuity=False, lower=0.01, upper=179.9) phase.add_design_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', opt=False, val=9.80665) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) phase.add_path_constraint('theta_rate2', lower=-200, upper=200, units='rad/s**2') p.model.linear_solver = om.DirectSolver() p.model.options['assembled_jac_type'] = 'csc' p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
def test_timeseries_radau(self, test_smaller_timeseries=False): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.Radau(num_segments=8, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state( 'x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state( 'y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state( 'v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control( 'theta', continuity=True, rate_continuity=True, opt=True, targets=BrachistochroneODE.parameters['theta']['targets'], units='deg', lower=0.01, upper=179.9, ref=1, ref0=0) if test_smaller_timeseries: phase.add_design_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], opt=True, units='m/s**2', val=9.80665, include_timeseries=False) else: phase.add_design_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], opt=True, units='m/s**2', val=9.80665) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.options['assembled_jac_type'] = 'csc' p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.design_parameters:g'] = 9.80665 p.run_driver() gd = phase.options['transcription'].grid_data state_input_idxs = gd.subset_node_indices['state_input'] control_input_idxs = gd.subset_node_indices['control_input'] assert_near_equal(p.get_val('phase0.time'), p.get_val('phase0.timeseries.time')[:, 0]) assert_near_equal(p.get_val('phase0.time_phase'), p.get_val('phase0.timeseries.time_phase')[:, 0]) for state in ('x', 'y', 'v'): assert_near_equal( p.get_val('phase0.states:{0}'.format(state)), p.get_val('phase0.timeseries.states:' '{0}'.format(state))[state_input_idxs]) for control in ('theta', ): assert_near_equal( p.get_val('phase0.controls:{0}'.format(control)), p.get_val('phase0.timeseries.controls:' '{0}'.format(control))[control_input_idxs]) for dp in ('g', ): for i in range(gd.subset_num_nodes['all']): if test_smaller_timeseries: with self.assertRaises(KeyError): p.get_val( 'phase0.timeseries.design_parameters:{0}'.format( dp)) else: assert_near_equal( p.get_val( 'phase0.design_parameters:{0}'.format(dp))[0, :], p.get_val('phase0.timeseries.design_parameters:' '{0}'.format(dp))[i]) # call simulate to test SolveIVP transcription exp_out = phase.simulate() if test_smaller_timeseries: with self.assertRaises(KeyError): exp_out.get_val( 'phase0.timeseries.design_parameters:{0}'.format(dp)) else: # no error accessing timseries.design_parameter exp_out.get_val( 'phase0.timeseries.design_parameters:{0}'.format(dp)) # Test that the state rates are output in both the radau and solveivp timeseries outputs t_sol = p.get_val('phase0.timeseries.time') t_sim = exp_out.get_val('phase0.timeseries.time') for state_name in ('x', 'y', 'v'): rate_sol = p.get_val(f'phase0.timeseries.state_rates:{state_name}') rate_sim = exp_out.get_val( f'phase0.timeseries.state_rates:{state_name}') rate_t_sim = interp1d(t_sim.ravel(), rate_sim.ravel()) assert_near_equal(rate_t_sim(t_sol), rate_sol, tolerance=1.0E-3)
def test(self): # Create a dictionary to store options about the surface # OM: vary 'num_y' and 'num_x' to change the size of the mesh mesh_dict = { 'num_y': 5, 'num_x': 2, 'wing_type': 'rect', 'symmetry': True } mesh = generate_mesh(mesh_dict) surf_dict = { # Wing definition 'name': 'wing', # name of the surface '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': 'tube', 'thickness_cp': np.ones((2)) * .1, 'twist_cp': np.ones((2)), 'mesh': mesh, # 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.015, # 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.15]), # thickness over chord ratio (NACA0015) 'c_max_t': .303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous': True, 'with_wave': False, # if true, compute wave drag # Structural values are based on aluminum 7075 'E': 70.e9, # [Pa] Young's modulus of the spar 'G': 30.e9, # [Pa] shear modulus of the spar 'yield': 500.e6 / 2.5, # [Pa] yield stress divided by 2.5 for limiting case 'mrho': 3.e3, # [kg/m^3] material density 'fem_origin': 0.35, # normalized chordwise location of the spar 'wing_weight_ratio': 2., 'struct_weight_relief': False, # True to add the weight of the structure to the loads on the structure 'distributed_fuel_weight': False, # Constraints 'exact_failure_constraint': False, # if false, use KS function } surfaces = [surf_dict] # Create the problem and assign the model group prob = om.Problem() # Add problem information as an independent variables component indep_var_comp = om.IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=9., units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('re', val=1.e6, units='1/m') indep_var_comp.add_output('rho', val=0.38, units='kg/m**3') indep_var_comp.add_output('CT', val=grav_constant * 17.e-6, units='1/s') indep_var_comp.add_output('R', val=11.165e6, units='m') indep_var_comp.add_output('W0', val=0.4 * 3e5, units='kg') indep_var_comp.add_output('speed_of_sound', val=295.4, 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 = AerostructGeometry(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('Mach_number', point_name + '.Mach_number') 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('speed_of_sound', point_name + '.speed_of_sound') prob.model.connect('empty_cg', point_name + '.empty_cg') prob.model.connect('load_factor', point_name + '.load_factor') for surface in surfaces: 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') # Connect performance calculation variables prob.model.connect(name + '.radius', com_name + '.radius') prob.model.connect(name + '.thickness', com_name + '.thickness') 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') prob.model.connect(name + '.t_over_c', com_name + '.t_over_c') prob.driver = om.ScipyOptimizeDriver() prob.driver.options['tol'] = 1e-9 # Setup problem and add design variables, constraint, and objective prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.) prob.model.add_design_var('wing.thickness_cp', lower=0.01, upper=0.5, scaler=1e2) prob.model.add_constraint('AS_point_0.wing_perf.failure', upper=0.) prob.model.add_constraint('AS_point_0.wing_perf.thickness_intersects', upper=0.) # Add design variables, constraisnt, and objective on the problem prob.model.add_design_var('alpha', lower=-10., upper=10.) prob.model.add_constraint('AS_point_0.L_equals_W', equals=0.) prob.model.add_objective('AS_point_0.fuelburn', scaler=1e-5) # Set up the problem prob.setup() prob.run_driver() assert_rel_error(self, prob['AS_point_0.fuelburn'][0], 68345.6633812, 1e-5)
def run_wisdem(fname_wt_input, fname_modeling_options, fname_opt_options): # Load all yaml inputs and validate (also fills in defaults) wt_initial = WindTurbineOntologyPython(fname_wt_input, fname_modeling_options, fname_opt_options) wt_init, modeling_options, opt_options = wt_initial.get_input_data() # Initialize openmdao problem. If running with multiple processors in MPI, use parallel finite differencing equal to the number of cores used. # Otherwise, initialize the WindPark system normally. Get the rank number for parallelization. We only print output files using the root processor. blade_opt_options = opt_options['optimization_variables']['blade'] tower_opt_options = opt_options['optimization_variables']['tower'] control_opt_options = opt_options['optimization_variables']['control'] if MPI: # Determine the number of design variables n_DV = 0 if blade_opt_options['aero_shape']['twist']['flag']: n_DV += blade_opt_options['aero_shape']['twist']['n_opt'] - 2 if blade_opt_options['aero_shape']['chord']['flag']: n_DV += blade_opt_options['aero_shape']['chord']['n_opt'] - 3 if blade_opt_options['aero_shape']['af_positions']['flag']: n_DV += modeling_options['blade']['n_af_span'] - blade_opt_options[ 'aero_shape']['af_positions']['af_start'] - 1 if blade_opt_options['structure']['spar_cap_ss']['flag']: n_DV += blade_opt_options['structure']['spar_cap_ss']['n_opt'] - 2 if blade_opt_options['structure']['spar_cap_ps'][ 'flag'] and not blade_opt_options['structure']['spar_cap_ps'][ 'equal_to_suction']: n_DV += blade_opt_options['structure']['spar_cap_ps']['n_opt'] - 2 if opt_options['optimization_variables']['control']['tsr']['flag']: n_DV += 1 if opt_options['optimization_variables']['control']['servo'][ 'pitch_control']['flag']: n_DV += 2 if opt_options['optimization_variables']['control']['servo'][ 'torque_control']['flag']: n_DV += 2 if opt_options['optimization_variables']['control']['servo'][ 'flap_control']['flag']: n_DV += 2 if 'dac' in blade_opt_options: if blade_opt_options['dac']['te_flap_end']['flag']: n_DV += modeling_options['blade']['n_te_flaps'] if blade_opt_options['dac']['te_flap_ext']['flag']: n_DV += modeling_options['blade']['n_te_flaps'] if tower_opt_options['outer_diameter']['flag']: n_DV += modeling_options['tower']['n_height'] if tower_opt_options['layer_thickness']['flag']: n_DV += (modeling_options['tower']['n_height'] - 1) * modeling_options['tower']['n_layers'] if opt_options['driver']['form'] == 'central': n_DV *= 2 # Extract the number of cores available max_cores = MPI.COMM_WORLD.Get_size() if max_cores / 2. != np.round(max_cores / 2.): exit( 'ERROR: the parallelization logic only works for an even number of cores available' ) # Define the color map for the parallelization, determining the maximum number of parallel finite difference (FD) evaluations based on the number of design variables (DV). OpenFAST on/off changes things. if modeling_options['Analysis_Flags']['OpenFAST']: # If openfast is called, the maximum number of FD is the number of DV, if we have the number of cores available that doubles the number of DVs, otherwise it is half of the number of DV (rounded to the lower integer). We need this because a top layer of cores calls a bottom set of cores where OpenFAST runs. if max_cores > 2. * n_DV: n_FD = n_DV else: n_FD = int(np.floor(max_cores / 2)) # The number of OpenFAST runs is the minimum between the actual number of requested OpenFAST simulations, and the number of cores available (minus the number of DV, which sit and wait for OF to complete) # need to calculate the number of OpenFAST runs from the user input n_OF_runs = 0 if modeling_options['openfast']['dlc_settings']['run_power_curve']: if modeling_options['openfast']['dlc_settings']['Power_Curve'][ 'turbulent_power_curve']: n_OF_runs += len( modeling_options['openfast']['dlc_settings'] ['Power_Curve']['U']) * len( modeling_options['openfast']['dlc_settings'] ['Power_Curve']['Seeds']) else: n_OF_runs += len(modeling_options['openfast'] ['dlc_settings']['Power_Curve']['U']) if modeling_options['openfast']['dlc_settings'][ 'run_IEC'] or modeling_options['openfast']['dlc_settings'][ 'run_blade_fatigue']: for dlc in modeling_options['openfast']['dlc_settings']['IEC']: dlc_vars = list(dlc.keys()) # Number of wind speeds if 'U' not in dlc_vars: if dlc['DLC'] == 1.4: # assuming 1.4 is run at [V_rated-2, V_rated, V_rated] and +/- direction change n_U = 6 elif dlc[ 'DLC'] == 5.1: # assuming 1.4 is run at [V_rated-2, V_rated, V_rated] n_U = 3 elif dlc['DLC'] in [ 6.1, 6.3 ]: # assuming V_50 for [-8, 8] deg yaw error n_U = 2 else: print( 'Warning: for OpenFAST DLC %1.1f specified in the Analysis Options, wind speeds "U" must be provided' % dlc['DLC']) else: n_U = len(dlc['U']) # Number of seeds if 'Seeds' not in dlc_vars: if dlc['DLC'] == 1.4: # not turbulent n_Seeds = 1 else: print( 'Warning: for OpenFAST DLC %1.1f specified in the Analysis Options, turbulent seeds "Seeds" must be provided' % dlc['DLC']) else: n_Seeds = len(dlc['Seeds']) n_OF_runs += n_U * n_Seeds n_DV = max([n_DV, 1]) max_parallel_OF_runs = max( [int(np.floor((max_cores - n_DV) / n_DV)), 1]) n_OF_runs_parallel = min([int(n_OF_runs), max_parallel_OF_runs]) modeling_options['openfast']['dlc_settings'][ 'n_OF_runs'] = n_OF_runs else: # If OpenFAST is not called, the number of parallel calls to compute the FDs is just equal to the minimum of cores available and DV n_FD = min([max_cores, n_DV]) n_OF_runs_parallel = 1 # Define the color map for the cores (how these are distributed between finite differencing and openfast runs) n_FD = max([n_FD, 1]) comm_map_down, comm_map_up, color_map = map_comm_heirarchical( n_FD, n_OF_runs_parallel) rank = MPI.COMM_WORLD.Get_rank() color_i = color_map[rank] comm_i = MPI.COMM_WORLD.Split(color_i, 1) else: color_i = 0 rank = 0 folder_output = opt_options['general']['folder_output'] if rank == 0 and not os.path.isdir(folder_output): os.mkdir(folder_output) if color_i == 0: # the top layer of cores enters, the others sit and wait to run openfast simulations if MPI: if modeling_options['Analysis_Flags']['OpenFAST']: # Parallel settings for OpenFAST modeling_options['openfast']['analysis_settings'][ 'mpi_run'] = True modeling_options['openfast']['analysis_settings'][ 'mpi_comm_map_down'] = comm_map_down modeling_options['openfast']['analysis_settings'][ 'cores'] = n_OF_runs_parallel # Parallel settings for OpenMDAO wt_opt = om.Problem(model=om.Group(num_par_fd=n_FD), comm=comm_i) wt_opt.model.add_subsystem('comp', WindPark( modeling_options=modeling_options, opt_options=opt_options), promotes=['*']) else: # Sequential finite differencing and openfast simulations modeling_options['openfast']['analysis_settings']['cores'] = 1 wt_opt = om.Problem(model=WindPark( modeling_options=modeling_options, opt_options=opt_options)) # If at least one of the design variables is active, setup an optimization if opt_options['opt_flag']: # If a step size for the driver-level finite differencing is provided, use that step size. Otherwise use a default value. if 'step_size' in opt_options['driver']: step_size = opt_options['driver']['step_size'] else: step_size = 1.e-6 # Solver has specific meaning in OpenMDAO wt_opt.model.approx_totals(method='fd', step=step_size, form=opt_options['driver']['form']) # Set optimization solver and options. First, Scipy's SLSQP if opt_options['driver']['solver'] == 'SLSQP': wt_opt.driver = om.ScipyOptimizeDriver() wt_opt.driver.options['optimizer'] = opt_options['driver'][ 'solver'] wt_opt.driver.options['tol'] = opt_options['driver']['tol'] wt_opt.driver.options['maxiter'] = opt_options['driver'][ 'max_iter'] # The next two optimization methods require pyOptSparse. elif opt_options['driver']['solver'] == 'CONMIN': try: from openmdao.api import pyOptSparseDriver except: exit( 'You requested the optimization solver CONMIN, but you have not installed the pyOptSparseDriver. Please do so and rerun.' ) wt_opt.driver = pyOptSparseDriver() wt_opt.driver.options['optimizer'] = opt_options['driver'][ 'solver'] wt_opt.driver.opt_settings['ITMAX'] = opt_options['driver'][ 'max_iter'] elif opt_options['driver']['solver'] == 'SNOPT': try: from openmdao.api import pyOptSparseDriver except: exit( 'You requested the optimization solver SNOPT, but you have not installed the pyOptSparseDriver. Please do so and rerun.' ) wt_opt.driver = pyOptSparseDriver() try: wt_opt.driver.options['optimizer'] = opt_options['driver'][ 'solver'] except: exit( 'You requested the optimization solver SNOPT, but you have not installed it within the pyOptSparseDriver. Please do so and rerun.' ) wt_opt.driver.opt_settings[ 'Major optimality tolerance'] = float( opt_options['driver']['tol']) wt_opt.driver.opt_settings['Major iterations limit'] = int( opt_options['driver']['max_major_iter']) wt_opt.driver.opt_settings['Iterations limit'] = int( opt_options['driver']['max_minor_iter']) wt_opt.driver.opt_settings[ 'Major feasibility tolerance'] = float( opt_options['driver']['tol']) wt_opt.driver.opt_settings['Summary file'] = os.path.join( folder_output, 'SNOPT_Summary_file.txt') wt_opt.driver.opt_settings['Print file'] = os.path.join( folder_output, 'SNOPT_Print_file.txt') if 'hist_file_name' in opt_options['driver']: wt_opt.driver.hist_file = opt_options['driver'][ 'hist_file_name'] if 'verify_level' in opt_options['driver']: wt_opt.driver.opt_settings['Verify level'] = opt_options[ 'driver']['verify_level'] # wt_opt.driver.declare_coloring() if 'hotstart_file' in opt_options['driver']: wt_opt.driver.hotstart_file = opt_options['driver'][ 'hotstart_file'] else: exit('The optimizer ' + opt_options['driver']['solver'] + 'is not yet supported!') # Set merit figure. Each objective has its own scaling. if opt_options['merit_figure'] == 'AEP': wt_opt.model.add_objective('sse.AEP', ref=-1.e6) elif opt_options['merit_figure'] == 'blade_mass': wt_opt.model.add_objective('elastic.precomp.blade_mass', ref=1.e4) elif opt_options['merit_figure'] == 'LCOE': wt_opt.model.add_objective('financese.lcoe', ref=0.1) elif opt_options['merit_figure'] == 'blade_tip_deflection': wt_opt.model.add_objective('tcons.tip_deflection_ratio') elif opt_options['merit_figure'] == 'tower_mass': wt_opt.model.add_objective('towerse.tower_mass') elif opt_options['merit_figure'] == 'tower_cost': wt_opt.model.add_objective('tcc.tower_cost') elif opt_options['merit_figure'] == 'Cp': if modeling_options['Analysis_Flags']['ServoSE']: wt_opt.model.add_objective('sse.powercurve.Cp_regII', ref=-1.) else: wt_opt.model.add_objective('ccblade.CP', ref=-1.) elif opt_options[ 'merit_figure'] == 'My_std': # for DAC optimization on root-flap-bending moments wt_opt.model.add_objective('aeroelastic.My_std', ref=1.e6) elif opt_options[ 'merit_figure'] == 'DEL_RootMyb': # for DAC optimization on root-flap-bending moments wt_opt.model.add_objective('aeroelastic.DEL_RootMyb', ref=1.e5) elif opt_options[ 'merit_figure'] == 'flp1_std': # for DAC optimization on flap angles - TORQUE 2020 paper (need to define time constant in ROSCO) wt_opt.model.add_objective('aeroelastic.flp1_std') #1.e-8) else: exit('The merit figure ' + opt_options['merit_figure'] + ' is not supported.') # Set optimization design variables. if blade_opt_options['aero_shape']['twist']['flag']: indices = range( 2, blade_opt_options['aero_shape']['twist']['n_opt']) wt_opt.model.add_design_var('blade.opt_var.twist_opt_gain', indices=indices, lower=0., upper=1.) chord_options = blade_opt_options['aero_shape']['chord'] if chord_options['flag']: indices = range(3, chord_options['n_opt'] - 1) wt_opt.model.add_design_var('blade.opt_var.chord_opt_gain', indices=indices, lower=chord_options['min_gain'], upper=chord_options['max_gain']) if blade_opt_options['aero_shape']['af_positions']['flag']: n_af = modeling_options['blade']['n_af_span'] indices = range( blade_opt_options['aero_shape']['af_positions'] ['af_start'], n_af - 1) af_pos_init = wt_init['components']['blade'][ 'outer_shape_bem']['airfoil_position']['grid'] lb_af = np.zeros(n_af) ub_af = np.zeros(n_af) for i in range(1, indices[0]): lb_af[i] = ub_af[i] = af_pos_init[i] for i in indices: lb_af[i] = 0.5 * (af_pos_init[i - 1] + af_pos_init[i]) + step_size ub_af[i] = 0.5 * (af_pos_init[i + 1] + af_pos_init[i]) - step_size lb_af[-1] = ub_af[-1] = 1. wt_opt.model.add_design_var('blade.opt_var.af_position', indices=indices, lower=lb_af[indices], upper=ub_af[indices]) spar_cap_ss_options = blade_opt_options['structure']['spar_cap_ss'] if spar_cap_ss_options['flag']: indices = range(1, spar_cap_ss_options['n_opt'] - 1) wt_opt.model.add_design_var( 'blade.opt_var.spar_cap_ss_opt_gain', indices=indices, lower=spar_cap_ss_options['min_gain'], upper=spar_cap_ss_options['max_gain']) # Only add the pressure side design variables if we do set # `equal_to_suction` as False in the optimization yaml. spar_cap_ps_options = blade_opt_options['structure']['spar_cap_ps'] if spar_cap_ps_options[ 'flag'] and not spar_cap_ps_options['equal_to_suction']: indices = range(1, spar_cap_ps_options['n_opt'] - 1) wt_opt.model.add_design_var( 'blade.opt_var.spar_cap_ps_opt_gain', indices=indices, lower=spar_cap_ps_options['min_gain'], upper=spar_cap_ps_options['max_gain']) if 'dac' in blade_opt_options: if blade_opt_options['dac']['te_flap_end']['flag']: wt_opt.model.add_design_var('blade.opt_var.te_flap_end', lower=blade_opt_options['dac'] ['te_flap_end']['min_end'], upper=blade_opt_options['dac'] ['te_flap_end']['max_end']) if blade_opt_options['dac']['te_flap_ext']['flag']: wt_opt.model.add_design_var('blade.opt_var.te_flap_ext', lower=blade_opt_options['dac'] ['te_flap_ext']['min_ext'], upper=blade_opt_options['dac'] ['te_flap_ext']['max_ext']) if tower_opt_options['outer_diameter']['flag']: wt_opt.model.add_design_var( 'tower.diameter', lower=tower_opt_options['outer_diameter']['lower_bound'], upper=tower_opt_options['outer_diameter']['upper_bound'], ref=5.) if tower_opt_options['layer_thickness']['flag']: wt_opt.model.add_design_var( 'tower.layer_thickness', lower=tower_opt_options['layer_thickness']['lower_bound'], upper=tower_opt_options['layer_thickness']['upper_bound'], ref=1e-2) # -- Control -- if control_opt_options['tsr']['flag']: wt_opt.model.add_design_var( 'opt_var.tsr_opt_gain', lower=control_opt_options['tsr']['min_gain'], upper=control_opt_options['tsr']['max_gain']) if control_opt_options['servo']['pitch_control']['flag']: wt_opt.model.add_design_var('control.PC_omega', lower=control_opt_options['servo'] ['pitch_control']['omega_min'], upper=control_opt_options['servo'] ['pitch_control']['omega_max']) wt_opt.model.add_design_var('control.PC_zeta', lower=control_opt_options['servo'] ['pitch_control']['zeta_min'], upper=control_opt_options['servo'] ['pitch_control']['zeta_max']) if control_opt_options['servo']['torque_control']['flag']: wt_opt.model.add_design_var('control.VS_omega', lower=control_opt_options['servo'] ['torque_control']['omega_min'], upper=control_opt_options['servo'] ['torque_control']['omega_max']) wt_opt.model.add_design_var('control.VS_zeta', lower=control_opt_options['servo'] ['torque_control']['zeta_min'], upper=control_opt_options['servo'] ['torque_control']['zeta_max']) if 'flap_control' in control_opt_options['servo']: if control_opt_options['servo']['flap_control']['flag']: wt_opt.model.add_design_var( 'control.Flp_omega', lower=control_opt_options['servo']['flap_control'] ['omega_min'], upper=control_opt_options['servo']['flap_control'] ['omega_max']) wt_opt.model.add_design_var( 'control.Flp_zeta', lower=control_opt_options['servo']['flap_control'] ['zeta_min'], upper=control_opt_options['servo']['flap_control'] ['zeta_max']) # Set non-linear constraints blade_constraints = opt_options['constraints']['blade'] if blade_constraints['strains_spar_cap_ss']['flag']: if blade_opt_options['structure']['spar_cap_ss']['flag']: wt_opt.model.add_constraint( 'rlds.constr.constr_max_strainU_spar', upper=1.0) else: print( 'WARNING: the strains of the suction-side spar cap are set to be constrained, but spar cap thickness is not an active design variable. The constraint is not enforced.' ) if blade_constraints['strains_spar_cap_ps']['flag']: if blade_opt_options['structure']['spar_cap_ps'][ 'flag'] or blade_opt_options['structure'][ 'spar_cap_ps']['equal_to_suction']: wt_opt.model.add_constraint( 'rlds.constr.constr_max_strainL_spar', upper=1.0) else: print( 'WARNING: the strains of the pressure-side spar cap are set to be constrained, but spar cap thickness is not an active design variable. The constraint is not enforced.' ) if blade_constraints['stall']['flag']: if blade_opt_options['aero_shape']['twist']['flag']: wt_opt.model.add_constraint( 'stall_check.no_stall_constraint', upper=1.0) else: print( 'WARNING: the margin to stall is set to be constrained, but twist is not an active design variable. The constraint is not enforced.' ) if blade_constraints['tip_deflection']['flag']: if blade_opt_options['structure']['spar_cap_ss'][ 'flag'] or blade_opt_options['structure'][ 'spar_cap_ps']['flag']: wt_opt.model.add_constraint('tcons.tip_deflection_ratio', upper=1.0) else: print( 'WARNING: the tip deflection is set to be constrained, but spar caps thickness is not an active design variable. The constraint is not enforced.' ) if blade_constraints['chord']['flag']: if blade_opt_options['aero_shape']['chord']['flag']: wt_opt.model.add_constraint('blade.pa.max_chord_constr', upper=1.0) else: print( 'WARNING: the max chord is set to be constrained, but chord is not an active design variable. The constraint is not enforced.' ) if blade_constraints['frequency']['flap_above_3P']: if blade_opt_options['structure']['spar_cap_ss'][ 'flag'] or blade_opt_options['structure'][ 'spar_cap_ps']['flag']: wt_opt.model.add_constraint( 'rlds.constr.constr_flap_f_margin', upper=0.0) else: print( 'WARNING: the blade flap frequencies are set to be constrained, but spar caps thickness is not an active design variable. The constraint is not enforced.' ) if blade_constraints['frequency']['edge_above_3P']: wt_opt.model.add_constraint('rlds.constr.constr_edge_f_margin', upper=0.0) # if blade_constraints['frequency']['flap_below_3P']: # wt_opt.model.add_constraint('rlds.constr.constr_flap_f_below_3P', upper= 1.0) # if blade_constraints['frequency']['edge_below_3P']: # wt_opt.model.add_constraint('rlds.constr.constr_edge_f_below_3P', upper= 1.0) # if blade_constraints['frequency']['flap_above_3P'] and blade_constraints['frequency']['flap_below_3P']: # exit('The blade flap frequency is constrained to be both above and below 3P. Please check the constraint flags.') # if blade_constraints['frequency']['edge_above_3P'] and blade_constraints['frequency']['edge_below_3P']: # exit('The blade edge frequency is constrained to be both above and below 3P. Please check the constraint flags.') if blade_constraints['rail_transport']['flag']: if blade_constraints['rail_transport']['8_axle']: wt_opt.model.add_constraint( 'elastic.rail.constr_LV_8axle_horiz', lower=0.8, upper=1.0) wt_opt.model.add_constraint('elastic.rail.constr_strainPS', upper=1.0) wt_opt.model.add_constraint('elastic.rail.constr_strainSS', upper=1.0) elif blade_constraints['rail_transport']['4_axle']: wt_opt.model.add_constraint( 'elastic.rail.constr_LV_4axle_horiz', upper=1.0) else: exit( 'You have activated the rail transport constraint module. Please define whether you want to model 4- or 8-axle flatcars.' ) if opt_options['constraints']['blade']['moment_coefficient'][ 'flag']: wt_opt.model.add_constraint( 'ccblade.CM', lower=opt_options['constraints']['blade'] ['moment_coefficient']['min'], upper=opt_options['constraints']['blade'] ['moment_coefficient']['max']) if opt_options['constraints']['blade']['match_cl_cd'][ 'flag_cl'] or opt_options['constraints']['blade'][ 'match_cl_cd']['flag_cd']: data_target = np.loadtxt(opt_options['constraints']['blade'] ['match_cl_cd']['filename']) eta_opt = np.linspace( 0., 1., opt_options['optimization_variables']['blade'] ['aero_shape']['twist']['n_opt']) target_cl = np.interp(eta_opt, data_target[:, 0], data_target[:, 3]) target_cd = np.interp(eta_opt, data_target[:, 0], data_target[:, 4]) eps_cl = 1.e-2 if opt_options['constraints']['blade']['match_cl_cd'][ 'flag_cl']: wt_opt.model.add_constraint('ccblade.cl_n_opt', lower=target_cl - eps_cl, upper=target_cl + eps_cl) if opt_options['constraints']['blade']['match_cl_cd'][ 'flag_cd']: wt_opt.model.add_constraint('ccblade.cd_n_opt', lower=target_cd - eps_cl, upper=target_cd + eps_cl) if opt_options['constraints']['blade']['match_L_D'][ 'flag_L'] or opt_options['constraints']['blade'][ 'match_L_D']['flag_D']: data_target = np.loadtxt(opt_options['constraints']['blade'] ['match_L_D']['filename']) eta_opt = np.linspace( 0., 1., opt_options['optimization_variables']['blade'] ['aero_shape']['twist']['n_opt']) target_L = np.interp(eta_opt, data_target[:, 0], data_target[:, 7]) target_D = np.interp(eta_opt, data_target[:, 0], data_target[:, 8]) eps_L = 1.e+2 if opt_options['constraints']['blade']['match_L_D']['flag_L']: wt_opt.model.add_constraint('ccblade.L_n_opt', lower=target_L - eps_L, upper=target_L + eps_L) if opt_options['constraints']['blade']['match_L_D']['flag_D']: wt_opt.model.add_constraint('ccblade.D_n_opt', lower=target_D - eps_L, upper=target_D + eps_L) tower_constraints = opt_options['constraints']['tower'] if tower_constraints['height_constraint']['flag']: wt_opt.model.add_constraint( 'towerse.height_constraint', lower=tower_constraints['height_constraint'] ['lower_bound'], upper=tower_constraints['height_constraint'] ['upper_bound']) if tower_constraints['stress']['flag']: wt_opt.model.add_constraint('towerse.post.stress', upper=1.0) if tower_constraints['global_buckling']['flag']: wt_opt.model.add_constraint('towerse.post.global_buckling', upper=1.0) if tower_constraints['shell_buckling']['flag']: wt_opt.model.add_constraint('towerse.post.shell_buckling', upper=1.0) if tower_constraints['constr_d_to_t']['flag']: wt_opt.model.add_constraint('towerse.constr_d_to_t', upper=0.0) if tower_constraints['constr_taper']['flag']: wt_opt.model.add_constraint('towerse.constr_taper', lower=0.0) if tower_constraints['slope']['flag']: wt_opt.model.add_constraint('towerse.slope', upper=1.0) if tower_constraints['frequency_1']['flag']: wt_opt.model.add_constraint( 'towerse.tower.f1', lower=tower_constraints['frequency_1']['lower_bound'], upper=tower_constraints['frequency_1']['upper_bound']) control_constraints = opt_options['constraints']['control'] if control_constraints['flap_control']['flag']: if modeling_options['Analysis_Flags']['OpenFAST'] != True: exit( 'Please turn on the call to OpenFAST if you are trying to optimize trailing edge flaps.' ) wt_opt.model.add_constraint( 'sse_tune.tune_rosco.Flp_Kp', lower=control_constraints['flap_control']['min'], upper=control_constraints['flap_control']['max']) wt_opt.model.add_constraint( 'sse_tune.tune_rosco.Flp_Ki', lower=control_constraints['flap_control']['min'], upper=control_constraints['flap_control']['max']) # Set recorder on the OpenMDAO driver level using the `optimization_log` # filename supplied in the optimization yaml if opt_options['recorder']['flag']: recorder = om.SqliteRecorder( os.path.join(folder_output, opt_options['recorder']['file_name'])) wt_opt.driver.add_recorder(recorder) wt_opt.add_recorder(recorder) wt_opt.driver.recording_options['excludes'] = ['*_df'] wt_opt.driver.recording_options['record_constraints'] = True wt_opt.driver.recording_options['record_desvars'] = True wt_opt.driver.recording_options['record_objectives'] = True # Setup openmdao problem wt_opt.setup() # Load initial wind turbine data from wt_initial to the openmdao problem wt_opt = yaml2openmdao(wt_opt, modeling_options, wt_init) wt_opt['blade.opt_var.s_opt_twist'] = np.linspace( 0., 1., blade_opt_options['aero_shape']['twist']['n_opt']) if blade_opt_options['aero_shape']['twist']['flag']: init_twist_opt = np.interp( wt_opt['blade.opt_var.s_opt_twist'], wt_init['components'] ['blade']['outer_shape_bem']['twist']['grid'], wt_init['components']['blade']['outer_shape_bem']['twist'] ['values']) lb_twist = np.array( blade_opt_options['aero_shape']['twist']['lower_bound']) ub_twist = np.array( blade_opt_options['aero_shape']['twist']['upper_bound']) wt_opt['blade.opt_var.twist_opt_gain'] = ( init_twist_opt - lb_twist) / (ub_twist - lb_twist) if max(wt_opt['blade.opt_var.twist_opt_gain']) > 1. or min( wt_opt['blade.opt_var.twist_opt_gain']) < 0.: print( 'Warning: the initial twist violates the upper or lower bounds of the twist design variables.' ) blade_constraints = opt_options['constraints']['blade'] wt_opt['blade.opt_var.s_opt_chord'] = np.linspace( 0., 1., blade_opt_options['aero_shape']['chord']['n_opt']) wt_opt['blade.ps.s_opt_spar_cap_ss'] = np.linspace( 0., 1., blade_opt_options['structure']['spar_cap_ss']['n_opt']) wt_opt['blade.ps.s_opt_spar_cap_ps'] = np.linspace( 0., 1., blade_opt_options['structure']['spar_cap_ps']['n_opt']) wt_opt['rlds.constr.max_strainU_spar'] = blade_constraints[ 'strains_spar_cap_ss']['max'] wt_opt['rlds.constr.max_strainL_spar'] = blade_constraints[ 'strains_spar_cap_ps']['max'] wt_opt['stall_check.stall_margin'] = blade_constraints['stall'][ 'margin'] * 180. / np.pi # Place the last design variables from a previous run into the problem. # This needs to occur after the above setup() and yaml2openmdao() calls # so these values are correctly placed in the problem. if 'warmstart_file' in opt_options['driver']: # Directly read the pyoptsparse sqlite db file from pyoptsparse import SqliteDict db = SqliteDict(opt_options['driver']['warmstart_file']) # Grab the last iteration's design variables last_key = db['last'] desvars = db[last_key]['xuser'] # Obtain the already-setup OM problem's design variables if wt_opt.model._static_mode: design_vars = wt_opt.model._static_design_vars else: design_vars = wt_opt.model._design_vars # Get the absolute names from the promoted names within the OM model. # We need this because the pyoptsparse db has the absolute names for # variables but the OM model uses the promoted names. prom2abs = wt_opt.model._var_allprocs_prom2abs_list['output'] abs2prom = {} for key in design_vars: abs2prom[prom2abs[key][0]] = key # Loop through each design variable for key in desvars: prom_key = abs2prom[key] # Scale each DV based on the OM scaling from the problem. # This assumes we're running the same problem with the same scaling scaler = design_vars[prom_key]['scaler'] adder = design_vars[prom_key]['adder'] if scaler is None: scaler = 1.0 if adder is None: adder = 0.0 scaled_dv = desvars[key] / scaler - adder # Special handling for blade twist as we only have the # last few control points as design variables if 'twist_opt_gain' in key: wt_opt[key][2:] = scaled_dv else: wt_opt[key][:] = scaled_dv if 'check_totals' in opt_options['driver']: if opt_options['driver']['check_totals']: wt_opt.run_model() totals = wt_opt.compute_totals() if 'check_partials' in opt_options['driver']: if opt_options['driver']['check_partials']: wt_opt.run_model() checks = wt_opt.check_partials(compact_print=True) sys.stdout.flush() # Run openmdao problem if opt_options['opt_flag']: wt_opt.run_driver() else: wt_opt.run_model() if (not MPI) or (MPI and rank == 0): # Save data coming from openmdao to an output yaml file froot_out = os.path.join(folder_output, opt_options['general']['fname_output']) wt_initial.write_ontology(wt_opt, froot_out) # Save data to numpy and matlab arrays fileIO.save_data(froot_out, wt_opt) if MPI and modeling_options['Analysis_Flags']['OpenFAST']: # subprocessor ranks spin, waiting for FAST simulations to run sys.stdout.flush() if rank in comm_map_up.keys(): subprocessor_loop(comm_map_up) sys.stdout.flush() # close signal to subprocessors if rank == 0: subprocessor_stop(comm_map_down) sys.stdout.flush() if rank == 0: return wt_opt, modeling_options, opt_options else: return [], [], []
def setup_problem( trans=dm.GaussLobatto(num_segments=10), polynomial_control=False): from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE from dymos.transcriptions.runge_kutta.runge_kutta import RungeKutta p = om.Problem(model=om.Group()) if isinstance(trans, RungeKutta): p.driver = om.pyOptSparseDriver() else: p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=trans) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=not isinstance(trans, RungeKutta), rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units']) phase.add_state('y', fix_initial=True, fix_final=not isinstance(trans, RungeKutta), rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units']) phase.add_state('v', fix_initial=True, rate_source=BrachistochroneODE.states['v']['rate_source'], units=BrachistochroneODE.states['v']['units']) if not polynomial_control: phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9) else: phase.add_polynomial_control('theta', order=1, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665) if isinstance(trans, RungeKutta): phase.add_timeseries_output('check', units='m/s', shape=(1, )) phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') if not polynomial_control: p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') else: p['phase0.polynomial_controls:theta'][:] = 5.0 return p
def test_brachistochrone_base_phase_class_gl(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_rel_error import dymos as dm class BrachistochronePhase(dm.Phase): def setup(self): self.options['ode_class'] = BrachistochroneODE self.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') self.add_state('x', fix_initial=True, rate_source='xdot', units='m') self.add_state('y', fix_initial=True, rate_source='ydot', units='m') self.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') self.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9, targets=['theta']) self.add_design_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) super(BrachistochronePhase, self).setup() p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = BrachistochronePhase( transcription=dm.GaussLobatto(num_segments=20, order=3)) p.model.add_subsystem('phase0', phase) phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) exp_out = phase.simulate() assert_rel_error(self, exp_out.get_val('phase0.timeseries.states:x')[-1], 10, tolerance=1.0E-3) assert_rel_error(self, exp_out.get_val('phase0.timeseries.states:y')[-1], 5, tolerance=1.0E-3)
def test_brachistochrone_forward_shooting_path_constrained_time(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.RungeKutta(num_segments=20)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(0.5, 2.0)) phase.add_state( 'x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state( 'y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_state( 'v', rate_source=BrachistochroneODE.states['v']['rate_source'], targets=BrachistochroneODE.states['v']['targets'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control( 'theta', targets=BrachistochroneODE.parameters['theta']['targets'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_design_parameter( 'g', targets=BrachistochroneODE.parameters['g']['targets'], units='m/s**2', opt=False, val=9.80665) # Final state values can't be controlled with simple bounds in ExplicitPhase, # so use nonlinear boundary constraints instead. phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) phase.add_path_constraint('time', lower=0.0, upper=2.0) phase.add_path_constraint('time_phase', lower=0.0, upper=2.0) # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = 0 p['phase0.states:y'] = 10 p['phase0.states:v'] = 0 p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_near_equal(p['phase0.time'][-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = phase.simulate() assert_near_equal(exp_out.get_val('phase0.timeseries.states:x')[-1, 0], 10, tolerance=1.0E-3) assert_near_equal(exp_out.get_val('phase0.timeseries.states:y')[-1, 0], 5, tolerance=1.0E-3)
def test_brachistochrone_undecorated_ode_gl(self): import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import openmdao.api as om from openmdao.utils.assert_utils import assert_rel_error import dymos as dm p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10), units='s') phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') phase.add_control('theta', units='deg', rate_continuity=False, lower=0.01, upper=179.9, targets=['theta']) phase.add_design_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100.5], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_rel_error(self, p.get_val('phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3)
def test_simulate_array_param(self): # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state('x', fix_initial=True, fix_final=True, rate_source='xdot') phase.add_state('y', fix_initial=True, fix_final=True, rate_source='ydot') phase.add_state('v', fix_initial=True, fix_final=False, rate_source='vdot') phase.add_control('theta', continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', units='m/s**2', val=9.80665) phase.add_parameter('array', units=None, shape=(10, ), static_target=True) # dummy array of data indeps = p.model.add_subsystem('indeps', om.IndepVarComp(), promotes=['*']) indeps.add_output('array', np.linspace(1, 10, 10), units=None) # add dummy array as a parameter and connect it p.model.connect('array', 'traj.phase0.parameters:array') # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() # # Setup the Problem # p.setup() # # Set the initial values # p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 2.0 p['traj.phase0.states:x'] = phase.interp('x', [0, 10]) p['traj.phase0.states:y'] = phase.interp('y', [10, 5]) p['traj.phase0.states:v'] = phase.interp('v', [0, 9.9]) p['traj.phase0.controls:theta'] = phase.interp('theta', [5, 100.5]) # # Solve for the optimal trajectory # dm.run_problem(p, simulate=True) # Test the results sol_results = om.CaseReader('dymos_solution.db').get_case('final') sim_results = om.CaseReader('dymos_solution.db').get_case('final') sol = sol_results.get_val('traj.phase0.timeseries.parameters:array') sim = sim_results.get_val('traj.phase0.timeseries.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol)) # Test that the parameter is available in the solution and simulation files sol = sol_results.get_val('traj.phase0.parameters:array') sim = sim_results.get_val('traj.phase0.parameters:array') assert_near_equal(sol - sim, np.zeros_like(sol))
def test_parameter_no_timeseries(self): import openmdao.api as om import dymos as dm from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() phase = dm.Phase(ode_class=BrachistochroneODE, transcription=dm.GaussLobatto(num_segments=8, order=3, compressed=True)) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) phase.add_state( 'x', rate_source=BrachistochroneODE.states['x']['rate_source'], units=BrachistochroneODE.states['x']['units'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state( 'y', rate_source=BrachistochroneODE.states['y']['rate_source'], units=BrachistochroneODE.states['y']['units'], fix_initial=True, fix_final=True, solve_segments=False) phase.add_state( 'v', rate_source=BrachistochroneODE.states['v']['rate_source'], units=BrachistochroneODE.states['v']['units'], fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', continuity=True, rate_continuity=True, opt=True, units='deg', lower=0.01, upper=179.9, ref=1, ref0=0) # upgrade_doc: begin parameter_no_timeseries phase.add_parameter('g', opt=False, units='m/s**2', include_timeseries=False) # upgrade_doc: end parameter_no_timeseries # Minimize time at the end of the phase phase.add_objective('time_phase', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 2.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 10], nodes='state_input') p['phase0.states:y'] = phase.interpolate(ys=[10, 5], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 9.9], nodes='state_input') p['phase0.controls:theta'] = phase.interpolate(ys=[5, 100], nodes='control_input') p['phase0.parameters:g'] = 9.80665 p.run_driver() with self.assertRaises(KeyError): p.get_val('phase0.timeseries.parameters:g}')
def _make_problem(self, transcription, num_seg, transcription_order=3, input_initial=False, input_duration=False): p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() # Compute sparsity/coloring when run_driver is called p.driver.declare_coloring() t = { 'gauss-lobatto': dm.GaussLobatto(num_segments=num_seg, order=transcription_order), 'radau-ps': dm.Radau(num_segments=num_seg, order=transcription_order) } phase = dm.Phase(ode_class=_BrachistochroneTestODE, transcription=t[transcription]) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(1, 1), duration_bounds=(.5, 10), units='s', time_phase_targets=['time_phase'], t_duration_targets=['t_duration'], t_initial_targets=['t_initial'], targets=['time'], input_initial=input_initial, input_duration=input_duration) phase.add_state('x', fix_initial=True, rate_source='xdot', units='m') phase.add_state('y', fix_initial=True, rate_source='ydot', units='m') phase.add_state('v', fix_initial=True, rate_source='vdot', targets=['v'], units='m/s') phase.add_control('theta', units='deg', rate_continuity=True, lower=0.01, upper=179.9, targets=['theta']) phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665, targets=['g']) phase.add_boundary_constraint('x', loc='final', equals=10) phase.add_boundary_constraint('y', loc='final', equals=5) # Minimize time at the end of the phase phase.add_objective('time', loc='final', scaler=10) p.model.linear_solver = om.DirectSolver() if input_duration: p.model.add_design_var('phase0.t_duration', lower=0, upper=3, scaler=1.0) p.setup(check=True) p['phase0.t_initial'] = 0.5 p['phase0.t_duration'] = 5.0 p['phase0.states:x'] = phase.interp('x', [0, 10]) p['phase0.states:y'] = phase.interp('y', [10, 5]) p['phase0.states:v'] = phase.interp('v', [0, 9.9]) p['phase0.controls:theta'] = phase.interp('theta', [5, 100.5]) return p
def test_simplified_ode_timeseries_output(self): """ # upgrade_doc: begin simplified_ode_output_timeseries phase.add_timeseries_output('tas_comp.TAS', shape=(1,), units='m/s') # upgrade_doc: end simplified_ode_output_timeseries """ import openmdao.api as om import dymos as dm from dymos.examples.aircraft_steady_flight.aircraft_ode import AircraftODE p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() transcription = dm.GaussLobatto(num_segments=1, order=13, compressed=False) phase = dm.Phase(ode_class=AircraftODE, transcription=transcription) p.model.add_subsystem('phase0', phase) # Pass Reference Area from an external source assumptions = p.model.add_subsystem('assumptions', om.IndepVarComp()) assumptions.add_output('S', val=427.8, units='m**2') assumptions.add_output('mass_empty', val=1.0, units='kg') assumptions.add_output('mass_payload', val=1.0, units='kg') phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(3600, 3600), duration_ref=3600) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(3600, 3600), duration_ref=3600) phase.add_state('range', units='km', fix_initial=True, fix_final=False, scaler=0.01, rate_source='range_rate_comp.dXdt:range', defect_scaler=0.01) phase.add_state('mass_fuel', units='kg', fix_final=True, upper=20000.0, lower=0.0, rate_source='propulsion.dXdt:mass_fuel', scaler=1.0E-4, defect_scaler=1.0E-2) phase.add_state('alt', rate_source='climb_rate', units='km', fix_initial=True) phase.add_control('mach', targets=['tas_comp.mach', 'aero.mach'], units=None, opt=False) phase.add_control('climb_rate', targets=['gam_comp.climb_rate'], units='m/s', opt=False) phase.add_parameter( 'S', targets=['aero.S', 'flight_equilibrium.S', 'propulsion.S'], units='m**2') phase.add_parameter('mass_empty', targets=['mass_comp.mass_empty'], units='kg') phase.add_parameter('mass_payload', targets=['mass_comp.mass_payload'], units='kg') phase.add_path_constraint('propulsion.tau', lower=0.01, upper=1.0, shape=(1, )) # upgrade_doc: begin simplified_ode_output_timeseries phase.add_timeseries_output('tas_comp.TAS') # upgrade_doc: end simplified_ode_output_timeseries p.model.connect('assumptions.S', 'phase0.parameters:S') p.model.connect('assumptions.mass_empty', 'phase0.parameters:mass_empty') p.model.connect('assumptions.mass_payload', 'phase0.parameters:mass_payload') phase.add_objective('time', loc='final', ref=3600) p.model.linear_solver = om.DirectSolver() p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 1.515132 * 3600.0 p['phase0.states:range'] = phase.interpolate(ys=(0, 1296.4), nodes='state_input') p['phase0.states:mass_fuel'] = phase.interpolate(ys=(12236.594555, 0), nodes='state_input') p['phase0.states:alt'] = 5.0 p['phase0.controls:mach'] = 0.8 p['phase0.controls:climb_rate'] = 0.0 p['assumptions.S'] = 427.8 p['assumptions.mass_empty'] = 0.15E6 p['assumptions.mass_payload'] = 84.02869 * 400 dm.run_problem(p) time = p.get_val('phase0.timeseries.time') tas = p.get_val('phase0.timeseries.TAS', units='km/s') range = p.get_val('phase0.timeseries.states:range') assert_near_equal(range, tas * time, tolerance=1.0E-4) exp_out = phase.simulate() time = exp_out.get_val('phase0.timeseries.time') tas = exp_out.get_val('phase0.timeseries.TAS', units='km/s') range = exp_out.get_val('phase0.timeseries.states:range') assert_near_equal(range, tas * time, tolerance=1.0E-4)
# build the model prob = om.Problem() prob.model.add_subsystem('parab', Paraboloid(), promotes_inputs=['x', 'y']) # define the component whose output will be constrained prob.model.add_subsystem('const', om.ExecComp('g = x + y'), promotes_inputs=['x', 'y']) # Design variables 'x' and 'y' span components, so we need to provide a common initial value for them prob.model.set_input_defaults('x', 3.0) prob.model.set_input_defaults('y', -4.0) # setup the optimization prob.driver = om.ScipyOptimizeDriver() prob.driver.options['optimizer'] = 'COBYLA' prob.model.add_design_var('x', lower=-50, upper=50) prob.model.add_design_var('y', lower=-50, upper=50) prob.model.add_objective('parab.f_xy') # to add the constraint to the model prob.model.add_constraint('const.g', lower=0, upper=10.) prob.setup() prob.run_driver() # minimum value print(prob.get_val('parab.f_xy'))
def test_brachistochrone_static_gravity(self): import openmdao.api as om from openmdao.utils.assert_utils import assert_near_equal import dymos as dm import matplotlib.pyplot as plt from dymos.examples.brachistochrone.brachistochrone_ode import BrachistochroneODE # # Initialize the Problem and the optimization driver # p = om.Problem(model=om.Group()) p.driver = om.ScipyOptimizeDriver() p.driver.declare_coloring() # # Create a trajectory and add a phase to it # traj = p.model.add_subsystem('traj', dm.Trajectory()) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=BrachistochroneODE, ode_init_kwargs={'static_gravity': True}, transcription=dm.GaussLobatto(num_segments=10))) # # Set the variables # phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.add_state('x', rate_source='xdot', targets=None, units='m', fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('y', rate_source='ydot', targets=None, units='m', fix_initial=True, fix_final=True, solve_segments=False) phase.add_state('v', rate_source='vdot', targets=['v'], units='m/s', fix_initial=True, fix_final=False, solve_segments=False) phase.add_control('theta', targets=['theta'], continuity=True, rate_continuity=True, units='deg', lower=0.01, upper=179.9) phase.add_parameter('g', targets=['g'], dynamic=False, opt=False) # # Minimize time at the end of the phase # phase.add_objective('time', loc='final', scaler=10) # # Setup the Problem # p.setup() # # Set the initial values # The initial time is fixed, and we set that fixed value here. # The optimizer is allowed to modify t_duration, but an initial guess is provided here. # p.set_val('traj.phase0.t_initial', 0.0) p.set_val('traj.phase0.t_duration', 2.0) # Guesses for states are provided at all state_input nodes. # We use the phase.interpolate method to linearly interpolate values onto the state input nodes. # Since fix_initial=True for all states and fix_final=True for x and y, the initial or final # values of the interpolation provided here will not be changed by the optimizer. p.set_val('traj.phase0.states:x', phase.interpolate(ys=[0, 10], nodes='state_input')) p.set_val('traj.phase0.states:y', phase.interpolate(ys=[10, 5], nodes='state_input')) p.set_val('traj.phase0.states:v', phase.interpolate(ys=[0, 9.9], nodes='state_input')) # Guesses for controls are provided at all control_input node. # Here phase.interpolate is used to linearly interpolate values onto the control input nodes. p.set_val('traj.phase0.controls:theta', phase.interpolate(ys=[5, 100.5], nodes='control_input')) # Set the value for gravitational acceleration. p.set_val('traj.phase0.parameters:g', 9.80665) # # Solve for the optimal trajectory # dm.run_problem(p) # Test the results assert_near_equal(p.get_val('traj.phase0.timeseries.time')[-1], 1.8016, tolerance=1.0E-3) # Generate the explicitly simulated trajectory exp_out = traj.simulate() # Extract the timeseries from the implicit solution and the explicit simulation x = p.get_val('traj.phase0.timeseries.states:x') y = p.get_val('traj.phase0.timeseries.states:y') t = p.get_val('traj.phase0.timeseries.time') theta = p.get_val('traj.phase0.timeseries.controls:theta') x_exp = exp_out.get_val('traj.phase0.timeseries.states:x') y_exp = exp_out.get_val('traj.phase0.timeseries.states:y') t_exp = exp_out.get_val('traj.phase0.timeseries.time') theta_exp = exp_out.get_val('traj.phase0.timeseries.controls:theta') fig, axes = plt.subplots(nrows=2, ncols=1) axes[0].plot(x, y, 'o') axes[0].plot(x_exp, y_exp, '-') axes[0].set_xlabel('x (m)') axes[0].set_ylabel('y (m)') axes[1].plot(t, theta, 'o') axes[1].plot(t_exp, theta_exp, '-') axes[1].set_xlabel('time (s)') axes[1].set_ylabel(r'$\theta$ (deg)') plt.show()
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 '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, '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, 'distributed_fuel_weight': True, # Constraints 'exact_failure_constraint': False, # if false, use KS function '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 = om.Problem() # Add problem information as an independent variables component indep_var_comp = om.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('Mach_number', 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=148000 + surf_dict['Wf_reserve'], units='kg') indep_var_comp.add_output('speed_of_sound', 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') 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 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('Mach_number', point_name + '.Mach_number') 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('speed_of_sound', point_name + '.speed_of_sound') 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', point_name + '.coupled.load_factor') 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') 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') #======================================================================================= # 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.struct_setup.fuel_vols', 'fuel_vol_delta.fuel_vols') 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') comp = om.ExecComp('fuel_diff = (fuel_mass - fuelburn) / fuelburn', fuel_mass={ 'value': 1.0, 'units': 'kg' }, fuelburn={ 'value': 1.0, 'units': 'kg' }) 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') #======================================================================================= #======================================================================================= prob.driver = om.ScipyOptimizeDriver() prob.driver.options['tol'] = 1e-9 prob.driver.options['maxiter'] = 2 # prob.driver = om.pyOptSparseDriver() # prob.driver.add_recorder(om.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_design_var('fuel_mass', lower=0., upper=2e5, scaler=1e-5) prob.model.add_constraint('AS_point_0.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.) prob.model.add_constraint('fuel_diff', equals=0.) #======================================================================================= #======================================================================================= # Set up the problem prob.setup() # om.view_model(prob) prob.run_model() # Check the partials at the initial point in the design space, # only care about relative error data = prob.check_partials(compact_print=True, out_stream=None, method='cs', step=1e-40) assert_check_partials(data, atol=1e20, rtol=1e-6) # Run the optimizer for 2 iterations prob.run_driver() # Check the partials at this point in the design space data = prob.check_partials(compact_print=True, out_stream=None, method='cs', step=1e-40) assert_check_partials(data, atol=1e20, rtol=1e-6)
def test(self): import numpy as np import openmdao.api as om from openaerostruct.geometry.utils import generate_mesh from openaerostruct.geometry.geometry_group import Geometry from openaerostruct.aerodynamics.aero_groups import AeroPoint from openaerostruct.common.atmos_group import AtmosGroup # Create a dictionary to store options about the mesh mesh_dict = { 'num_y': 7, 'num_x': 2, 'wing_type': 'CRM', 'symmetry': True, 'num_twist_cp': 5 } # Generate the aerodynamic mesh based on the previous dictionary mesh, twist_cp = generate_mesh(mesh_dict) # Create a dictionary with info and options about the aerodynamic # lifting surface surface = { # Wing definition 'name': 'wing', # name of the surface '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': 'tube', 'twist_cp': twist_cp, 'mesh': mesh, 'num_x': mesh.shape[0], 'num_y': mesh.shape[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.015, # 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.15]), # thickness over chord ratio (NACA0015) 'c_max_t': .303, # chordwise location of maximum (NACA0015) # thickness 'with_viscous': True, # if true, compute viscous drag 'with_wave': False, # if true, compute wave drag } # Create the OpenMDAO problem prob = om.Problem() # Create an independent variable component that will supply the flow # conditions to the problem. indep_var_comp = om.IndepVarComp() indep_var_comp.add_output('alpha', val=5., units='deg') indep_var_comp.add_output('Mach_number', val=0.84) indep_var_comp.add_output('altitude', val=35000., units='ft') # Add this IndepVarComp to the problem model prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) # Add this IndepVarComp to the problem model prob.model.add_subsystem('atmos', AtmosGroup(), promotes=['*']) # Create and add a group that handles the geometry for the # aerodynamic lifting surface geom_group = Geometry(surface=surface) prob.model.add_subsystem(surface['name'], geom_group) # Create the aero point group, which contains the actual aerodynamic # analyses aero_group = AeroPoint(surfaces=[surface]) point_name = 'aero_point_0' prob.model.add_subsystem( point_name, aero_group, promotes_inputs=['v', 'alpha', 'Mach_number', 're', 'rho', 'cg']) name = surface['name'] # Connect the mesh from the geometry component to the analysis point prob.model.connect(name + '.mesh', point_name + '.' + name + '.def_mesh') # Perform the connections with the modified names within the # 'aero_states' group. prob.model.connect(name + '.mesh', point_name + '.aero_states.' + name + '_def_mesh') prob.model.connect(name + '.t_over_c', point_name + '.' + name + '_perf.' + 't_over_c') # Import the Scipy Optimizer and set the driver of the problem to use # it, which defaults to an SLSQP optimization method prob.driver = om.ScipyOptimizeDriver() prob.driver.options['tol'] = 1e-9 # Setup problem and add design variables, constraint, and objective prob.model.add_design_var('wing.twist_cp', lower=-10., upper=15.) prob.model.add_constraint(point_name + '.wing_perf.CL', equals=0.5) prob.model.add_objective(point_name + '.wing_perf.CD', scaler=1e4) # Set up and run the optimization problem prob.setup() # prob.check_partials(compact_print=True) # exit() prob.run_driver() assert_rel_error(self, prob['aero_point_0.wing_perf.CD'][0], 0.030471796067577953, 1e-6) assert_rel_error(self, prob['aero_point_0.wing_perf.CL'][0], 0.5, 1e-6) assert_rel_error(self, prob['aero_point_0.CM'][1], -1.7331840488188963, 1e-6)
def test_ivp_driver_10_segs(self): import openmdao.api as om import dymos as dm import matplotlib.pyplot as plt plt.switch_backend('Agg') # disable plotting to the screen from dymos.examples.oscillator.doc.oscillator_ode import OscillatorODE # Instantiate an OpenMDAO Problem instance. prob = om.Problem() # We need an optimization driver. To solve this simple problem ScipyOptimizerDriver will work. prob.driver = om.ScipyOptimizeDriver() # Instantiate a Dymos Trajectory and add it to the Problem model. traj = dm.Trajectory() prob.model.add_subsystem('traj', traj) # Instantiate a Phase and add it to the Trajectory. phase = dm.Phase(ode_class=OscillatorODE, transcription=dm.Radau(num_segments=10)) traj.add_phase('phase0', phase) # Tell Dymos that the duration of the phase is bounded. phase.set_time_options(fix_initial=True, fix_duration=True) # Tell Dymos the states to be propagated using the given ODE. phase.add_state('x', fix_initial=True, rate_source='v', targets=['x'], units='m') phase.add_state('v', fix_initial=True, rate_source='v_dot', targets=['v'], units='m/s') # The spring constant, damping coefficient, and mass are inputs to the system that # are constant throughout the phase. phase.add_parameter('k', units='N/m', targets=['k']) phase.add_parameter('c', units='N*s/m', targets=['c']) phase.add_parameter('m', units='kg', targets=['m']) # Since we're using an optimization driver, an objective is required. We'll minimize # the final time in this case. phase.add_objective('time', loc='final') # Setup the OpenMDAO problem prob.setup() # Assign values to the times and states prob.set_val('traj.phase0.t_initial', 0.0) prob.set_val('traj.phase0.t_duration', 15.0) prob.set_val('traj.phase0.states:x', 10.0) prob.set_val('traj.phase0.states:v', 0.0) prob.set_val('traj.phase0.parameters:k', 1.0) prob.set_val('traj.phase0.parameters:c', 0.5) prob.set_val('traj.phase0.parameters:m', 1.0) # Now we're using the optimization driver to iteratively run the model and vary the # phase duration until the final y value is 0. prob.run_driver() # Perform an explicit simulation of our ODE from the initial conditions. sim_out = traj.simulate(times_per_seg=50) # Plot the state values obtained from the phase timeseries objects in the simulation output. t_sol = prob.get_val('traj.phase0.timeseries.time') t_sim = sim_out.get_val('traj.phase0.timeseries.time') states = ['x', 'v'] fig, axes = plt.subplots(len(states), 1) for i, state in enumerate(states): sol = axes[i].plot( t_sol, prob.get_val(f'traj.phase0.timeseries.states:{state}'), 'o') sim = axes[i].plot( t_sim, sim_out.get_val(f'traj.phase0.timeseries.states:{state}'), '-') axes[i].set_ylabel(state) axes[-1].set_xlabel('time (s)') fig.legend((sol[0], sim[0]), ('solution', 'simulation'), 'lower right', ncol=2) plt.tight_layout() plt.show()
def test_recording_remote_voi(self): # Create a parallel model model = om.Group() model.add_subsystem('par', om.ParallelGroup()) model.par.add_subsystem('G1', Mygroup()) model.par.add_subsystem('G2', Mygroup()) model.connect('par.G1.y', 'Obj.y1') model.connect('par.G2.y', 'Obj.y2') model.add_subsystem('Obj', om.ExecComp('obj=y1+y2')) model.add_objective('Obj.obj') # Configure driver to record VOIs on both procs driver = om.ScipyOptimizeDriver(disp=False) driver.recording_options['record_desvars'] = True driver.recording_options['record_objectives'] = True driver.recording_options['record_constraints'] = True driver.recording_options['includes'] = ['par.G1.y', 'par.G2.y'] driver.add_recorder(self.recorder) # Create problem and run driver prob = om.Problem(model, driver) prob.add_recorder(self.recorder) prob.setup(mode='fwd') t0, t1 = run_driver(prob) prob.record('final') t2 = time() prob.cleanup() # Since the test will compare the last case recorded, just check the # current values in the problem. This next section is about getting those values # These involve collective gathers so all ranks need to run this expected_outputs = driver.get_design_var_values(get_remote=True) expected_outputs.update(driver.get_objective_values()) expected_outputs.update(driver.get_constraint_values()) # includes for outputs are specified as promoted names but we need absolute names prom2abs = model._var_allprocs_prom2abs_list['output'] abs_includes = [ prom2abs[n][0] for n in prob.driver.recording_options['includes'] ] # Absolute path names of includes on this rank rrank = model.comm.rank rowned = model._owning_rank local_includes = [n for n in abs_includes if rrank == rowned[n]] # Get values for all vars on this rank inputs, outputs, residuals = model.get_nonlinear_vectors() # Get values for includes on this rank local_vars = {n: outputs[n] for n in local_includes} # Gather values for includes on all ranks all_vars = model.comm.gather(local_vars, root=0) if prob.comm.rank == 0: # Only on rank 0 do we have all the values. The all_vars variable is a list of # dicts from all ranks 0,1,... In this case, just ranks 0 and 1 dct = {} for d in all_vars: dct.update(d) expected_includes = { 'par.G1.Cy.y': dct['par.G1.Cy.y'], 'par.G2.Cy.y': dct['par.G2.Cy.y'], } expected_outputs.update(expected_includes) coordinate = [0, 'ScipyOptimize_SLSQP', (driver.iter_count - 1, )] expected_data = ((coordinate, (t0, t1), expected_outputs, None, None), ) assertDriverIterDataRecorded(self, expected_data, self.eps) expected_data = (('final', (t1, t2), expected_outputs), ) assertProblemDataRecorded(self, expected_data, self.eps)