def test_result(self): import numpy as np from numpy.testing import assert_almost_equal from openmdao.api import Problem, Group, IndepVarComp, BalanceComp, \ ExecComp, DirectSolver, NewtonSolver prob = Problem() ivc = IndepVarComp() ivc.add_output(name='M', val=0.0, units='deg', desc='Mean anomaly') ivc.add_output(name='ecc', val=0.0, units=None, desc='orbit eccentricity') bal = BalanceComp() bal.add_balance(name='E', val=0.0, units='rad', eq_units='rad', rhs_name='M') # Use M (mean anomaly) as the initial guess for E (eccentric anomaly) def guess_function(inputs, outputs, residuals): outputs['E'] = inputs['M'] bal.options['guess_func'] = guess_function # ExecComp used to compute the LHS of Kepler's equation. lhs_comp = ExecComp('lhs=E - ecc * sin(E)', lhs={ 'value': 0.0, 'units': 'rad' }, E={ 'value': 0.0, 'units': 'rad' }, ecc={'value': 0.0}) prob.model.add_subsystem(name='ivc', subsys=ivc, promotes_outputs=['M', 'ecc']) prob.model.add_subsystem(name='balance', subsys=bal, promotes_inputs=['M'], promotes_outputs=['E']) prob.model.add_subsystem(name='lhs_comp', subsys=lhs_comp, promotes_inputs=['E', 'ecc']) # Explicit connections prob.model.connect('lhs_comp.lhs', 'balance.lhs:E') # Set up solvers prob.model.linear_solver = DirectSolver() prob.model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) prob.setup() prob['M'] = 85.0 prob['ecc'] = 0.6 prob.run_model() assert_almost_equal(np.degrees(prob['E']), 115.9, decimal=1)
def test_min_time_climb_for_docs_gauss_lobatto(self): import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from openmdao.api import Problem, Group, pyOptSparseDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase from dymos.examples.min_time_climb.min_time_climb_ode import MinTimeClimbODE p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' # Compute sparsity/coloring when run_driver is called p.driver.options['dynamic_simul_derivs'] = True phase = Phase('gauss-lobatto', ode_class=MinTimeClimbODE, num_segments=12, compressed=True, transcription_order=3) p.model.add_subsystem('phase0', phase) phase.set_time_options(fix_initial=True, duration_bounds=(50, 400), duration_ref=100.0) phase.set_state_options('r', fix_initial=True, lower=0, upper=1.0E6, ref=1.0E3, defect_ref=1000.0, units='m') phase.set_state_options('h', fix_initial=True, lower=0, upper=20000.0, ref=1.0E2, defect_ref=100.0, units='m') phase.set_state_options('v', fix_initial=True, lower=10.0, ref=1.0E2, defect_ref=0.1, units='m/s') phase.set_state_options('gam', fix_initial=True, lower=-1.5, upper=1.5, ref=1.0, defect_scaler=1.0, units='rad') phase.set_state_options('m', fix_initial=True, lower=10.0, upper=1.0E5, ref=1.0E3, defect_ref=0.1) rate_continuity = True phase.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=rate_continuity, rate_continuity_scaler=100.0, rate2_continuity=False) phase.add_design_parameter('S', val=49.2386, units='m**2', opt=False) phase.add_design_parameter('Isp', val=1600.0, units='s', opt=False) phase.add_design_parameter('throttle', val=1.0, opt=False) phase.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m') phase.add_boundary_constraint('aero.mach', loc='final', equals=1.0, units=None) phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') phase.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000) phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8) phase.add_path_constraint(name='alpha', lower=-8, upper=8) # Minimize time at the end of the phase phase.add_objective('time', loc='final') p.driver.options['dynamic_simul_derivs'] = True p.model.options['assembled_jac_type'] = 'csc' p.model.linear_solver = DirectSolver(assemble_jac=True) p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 500 p['phase0.states:r'] = phase.interpolate(ys=[0.0, 50000.0], nodes='state_input') p['phase0.states:h'] = phase.interpolate(ys=[100.0, 20000.0], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[135.964, 283.159], nodes='state_input') p['phase0.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='state_input') p['phase0.states:m'] = phase.interpolate(ys=[19030.468, 10000.], nodes='state_input') p['phase0.controls:alpha'] = phase.interpolate(ys=[0.0, 0.0], nodes='control_input') # Solve for the optimal trajectory p.run_driver() # Test the results assert_rel_error(self, p.model.phase0.get_values('time')[-1], 321.0, tolerance=2) exp_out = phase.simulate(times=50) fig, axes = plt.subplots(2, 1, sharex=True) axes[0].plot(phase.get_values('time'), phase.get_values('h'), 'ro') axes[0].plot(exp_out.get_values('time'), exp_out.get_values('h'), 'b-') axes[0].set_xlabel('time (s)') axes[0].set_ylabel('altitude (m)') axes[1].plot(phase.get_values('time'), phase.get_values('alpha', units='deg'), 'ro') axes[1].plot(exp_out.get_values('time'), exp_out.get_values('alpha', units='deg'), 'b-') axes[1].set_xlabel('time (s)') axes[1].set_ylabel('alpha (deg)') plt.show()
def test_group_assembled_jac_with_ext_mat(self): class TwoSellarDis1(ExplicitComponent): """ Component containing Discipline 1 -- no derivatives version. """ def setup(self): self.add_input('z', val=np.zeros(2)) self.add_input('x', val=np.zeros(2)) self.add_input('y2', val=np.ones(2)) self.add_output('y1', val=np.ones(2)) self.declare_partials(of='*', wrt='*') def compute(self, inputs, outputs): z1 = inputs['z'][0] z2 = inputs['z'][1] x1 = inputs['x'] y2 = inputs['y2'] outputs['y1'][0] = z1**2 + z2 + x1[0] - 0.2 * y2[0] outputs['y1'][1] = z1**2 + z2 + x1[0] - 0.2 * y2[0] def compute_partials(self, inputs, partials): """ Jacobian for Sellar discipline 1. """ partials['y1', 'y2'] = np.array([[-0.2, 0.], [0., -0.2]]) partials['y1', 'z'] = np.array([[2.0 * inputs['z'][0], 1.0], [2.0 * inputs['z'][0], 1.0]]) partials['y1', 'x'] = np.eye(2) class TwoSellarDis2(ExplicitComponent): def setup(self): self.add_input('z', val=np.zeros(2)) self.add_input('y1', val=np.ones(2)) self.add_output('y2', val=np.ones(2)) self.declare_partials('*', '*', method='fd') def compute(self, inputs, outputs): z1 = inputs['z'][0] z2 = inputs['z'][1] y1 = inputs['y1'] # Note: this may cause some issues. However, y1 is constrained to be # above 3.16, so lets just let it converge, and the optimizer will # throw it out if y1[0].real < 0.0: y1[0] *= -1 if y1[1].real < 0.0: y1[1] *= -1 outputs['y2'][0] = y1[0]**.5 + z1 + z2 outputs['y2'][1] = y1[1]**.5 + z1 + z2 def compute_partials(self, inputs, J): y1 = inputs['y1'] if y1[0].real < 0.0: y1[0] *= -1 if y1[1].real < 0.0: y1[1] *= -1 J['y2', 'y1'] = np.array([[.5 * y1[0]**-.5, 0.], [0., .5 * y1[1]**-.5]]) J['y2', 'z'] = np.array([[1.0, 1.0], [1.0, 1.0]]) prob = Problem() model = prob.model model.add_subsystem('px', IndepVarComp('x', np.array([1.0, 1.0])), promotes=['x']) model.add_subsystem('pz', IndepVarComp('z', np.array([5.0, 2.0])), promotes=['z']) sup = model.add_subsystem('sup', Group(), promotes=['*']) sub1 = sup.add_subsystem('sub1', Group(), promotes=['*']) sub2 = sup.add_subsystem('sub2', Group(), promotes=['*']) d1 = sub1.add_subsystem('d1', TwoSellarDis1(), promotes=['x', 'z', 'y1', 'y2']) sub2.add_subsystem('d2', TwoSellarDis2(), promotes=['z', 'y1', 'y2']) model.add_subsystem('con_cmp1', ExecComp('con1 = 3.16 - y1[0] - y1[1]', y1=np.array([0.0, 0.0])), promotes=['con1', 'y1']) model.add_subsystem('con_cmp2', ExecComp('con2 = y2[0] + y2[1] - 24.0', y2=np.array([0.0, 0.0])), promotes=['con2', 'y2']) model.linear_solver = LinearBlockGS() sup.linear_solver = LinearBlockGS() sub1.linear_solver = DirectSolver(assemble_jac=True) sub2.linear_solver = DirectSolver(assemble_jac=True) prob.set_solver_print(level=0) prob.setup(check=False, mode='rev') prob.run_model() of = ['con1', 'con2'] wrt = ['x', 'z'] # Make sure we don't get a size mismatch. derivs = prob.compute_totals(of=of, wrt=wrt)
def setup(self): design = self.options['design'] thermo_data = self.options['thermo_data'] flow1_elements = self.options['Fl_I1_elements'] flow1_thermo = Thermo(thermo_data, init_reacts=flow1_elements) n_flow1_prods = len(flow1_thermo.products) in_flow = FlowIn(fl_name='Fl_I1', num_prods=n_flow1_prods) self.add_subsystem('in_flow1', in_flow, promotes=['Fl_I1:*']) flow2_elements = self.options['Fl_I2_elements'] flow2_thermo = Thermo(thermo_data, init_reacts=flow2_elements) n_flow2_prods = len(flow2_thermo.products) in_flow = FlowIn(fl_name='Fl_I2', num_prods=n_flow2_prods) self.add_subsystem('in_flow2', in_flow, promotes=['Fl_I2:*']) if design: # internal flow station to compute the area that is needed to match the static pressures if self.options['designed_stream'] == 1: Fl1_stat = SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=flow1_elements, fl_name="Fl_I1_calc:stat") self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[('init_prod_amounts', 'Fl_I1:stat:n'), ('S', 'Fl_I1:tot:S'), ('ht', 'Fl_I1:tot:h'), ('W', 'Fl_I1:stat:W'), ('Ps', 'Fl_I2:stat:P')], promotes_outputs=['Fl_I1_calc:stat*']) self.add_subsystem('area_calc', AreaSum(), promotes_inputs=['Fl_I2:stat:area'], promotes_outputs=[('area_sum', 'area')]) self.connect('Fl_I1_calc:stat:area', 'area_calc.Fl_I1:stat:area') else: Fl2_stat = SetStatic(mode="Ps", thermo_data=thermo_data, init_reacts=flow2_elements, fl_name="Fl_I2_calc:stat") self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[('init_prod_amounts', 'Fl_I2:tot:n'), ('S', 'Fl_I2:tot:S'), ('ht', 'Fl_I2:tot:h'), ('W', 'Fl_I2:stat:W'), ('Ps', 'Fl_I1:stat:P')], promotes_outputs=['Fl_I2_calc:stat:*']) self.add_subsystem('area_calc', AreaSum(), promotes_inputs=['Fl_I1:stat:area'], promotes_outputs=[('area_sum', 'area')]) self.connect('Fl_I2_calc:stat:area', 'area_calc.Fl_I2:stat:area') else: if self.options['designed_stream'] == 1: Fl1_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=flow1_elements, fl_name="Fl_I1_calc:stat") self.add_subsystem('Fl_I1_stat_calc', Fl1_stat, promotes_inputs=[ ('init_prod_amounts', 'Fl_I1:tot:n'), ('S', 'Fl_I1:tot:S'), ('ht', 'Fl_I1:tot:h'), ('W', 'Fl_I1:stat:W'), ('guess:Pt', 'Fl_I1:tot:P'), ('guess:gamt', 'Fl_I1:tot:gamma') ], promotes_outputs=['Fl_I1_calc:stat*']) else: Fl2_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=flow2_elements, fl_name="Fl_I2_calc:stat") self.add_subsystem('Fl_I2_stat_calc', Fl2_stat, promotes_inputs=[ ('init_prod_amounts', 'Fl_I2:tot:n'), ('S', 'Fl_I2:tot:S'), ('ht', 'Fl_I2:tot:h'), ('W', 'Fl_I2:stat:W'), ('guess:Pt', 'Fl_I2:tot:P'), ('guess:gamt', 'Fl_I2:tot:gamma') ], promotes_outputs=['Fl_I2_calc:stat*']) self.add_subsystem('extraction_ratio', ExecComp('ER=Pt1/Pt2', Pt1={'units': 'Pa'}, Pt2={'units': 'Pa'}), promotes_inputs=[('Pt1', 'Fl_I1:tot:P'), ('Pt2', 'Fl_I2:tot:P')], promotes_outputs=['ER']) mix_flow = MixFlow(thermo_data=thermo_data, Fl_I1_elements=self.options['Fl_I1_elements'], Fl_I2_elements=self.options['Fl_I2_elements']) if self.options['designed_stream'] == 1: self.add_subsystem('mix_flow', mix_flow, promotes_inputs=[ 'Fl_I1:tot:h', 'Fl_I1:tot:n', ('Fl_I1:stat:W', 'Fl_I1_calc:stat:W'), ('Fl_I1:stat:P', 'Fl_I1_calc:stat:P'), ('Fl_I1:stat:V', 'Fl_I1_calc:stat:V'), ('Fl_I1:stat:area', 'Fl_I1_calc:stat:area'), 'Fl_I2:tot:h', 'Fl_I2:tot:n', 'Fl_I2:stat:W', 'Fl_I2:stat:P', 'Fl_I2:stat:V', 'Fl_I2:stat:area' ]) else: self.add_subsystem('mix_flow', mix_flow, promotes_inputs=[ 'Fl_I1:tot:h', 'Fl_I1:tot:n', 'Fl_I1:stat:W', 'Fl_I1:stat:P', 'Fl_I1:stat:V', 'Fl_I1:stat:area', 'Fl_I2:tot:h', 'Fl_I2:tot:n', ('Fl_I2:stat:W', 'Fl_I2_calc:stat:W'), ('Fl_I2:stat:P', 'Fl_I2_calc:stat:P'), ('Fl_I2:stat:V', 'Fl_I2_calc:stat:V'), ('Fl_I2:stat:area', 'Fl_I2_calc:stat:area') ]) # group to converge for the impulse balance conv = self.add_subsystem('impulse_converge', Group(), promotes=['*']) if self.options['internal_solver']: newton = conv.nonlinear_solver = NewtonSolver() newton.options['maxiter'] = 30 newton.options['atol'] = 1e-2 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 20 newton.linesearch = BoundsEnforceLS() newton.linesearch.options['bound_enforcement'] = 'scalar' newton.linesearch.options['iprint'] = -1 conv.linear_solver = DirectSolver(assemble_jac=True) out_tot = SetTotal(thermo_data=thermo_data, mode='h', init_reacts=self.options['Fl_I1_elements'], fl_name="Fl_O:tot") conv.add_subsystem('out_tot', out_tot, promotes_outputs=['Fl_O:tot:*']) self.connect('mix_flow.n_mix', 'out_tot.init_prod_amounts') self.connect('mix_flow.ht_mix', 'out_tot.h') # note: gets Pt from the balance comp out_stat = SetStatic(mode="area", thermo_data=thermo_data, init_reacts=self.options['Fl_I1_elements'], fl_name="Fl_O:stat") conv.add_subsystem('out_stat', out_stat, promotes_outputs=['Fl_O:stat:*'], promotes_inputs=[ 'area', ]) self.connect('mix_flow.n_mix', 'out_stat.init_prod_amounts') self.connect('mix_flow.W_mix', 'out_stat.W') conv.connect('Fl_O:tot:S', 'out_stat.S') self.connect('mix_flow.ht_mix', 'out_stat.ht') conv.connect('Fl_O:tot:P', 'out_stat.guess:Pt') conv.connect('Fl_O:tot:gamma', 'out_stat.guess:gamt') conv.add_subsystem('imp_out', Impulse()) conv.connect('Fl_O:stat:P', 'imp_out.P') conv.connect('Fl_O:stat:area', 'imp_out.area') conv.connect('Fl_O:stat:V', 'imp_out.V') conv.connect('Fl_O:stat:W', 'imp_out.W') balance = conv.add_subsystem('balance', BalanceComp()) balance.add_balance('P_tot', val=100, units='psi', eq_units='N', lower=1e-3, upper=10000) conv.connect('balance.P_tot', 'out_tot.P') conv.connect('imp_out.impulse', 'balance.lhs:P_tot') self.connect( 'mix_flow.impulse_mix', 'balance.rhs:P_tot' ) #note that this connection comes from outside the convergence group
def test_no_promotion_errors(self): """ Tests for error-handling for invalid variable names and keys. """ g = Group(assembled_jac_type='dense') g.linear_solver = DirectSolver(assemble_jac=True) g.add_subsystem('c', ExecComp('y=2*x')) p = Problem() model = p.model model.add_subsystem('g', g) p.setup() # ------------------------------------------------------------------- msg = '\'Group (<model>): Variable "{}" not found.\'' # inputs with self.assertRaises(KeyError) as ctx: p['x'] = 5.0 self.assertEqual(str(ctx.exception), msg.format('x')) p._initial_condition_cache = {} with self.assertRaises(KeyError) as ctx: p['x'] self.assertEqual(str(ctx.exception), msg.format('x')) # outputs with self.assertRaises(KeyError) as ctx: p['y'] = 5.0 self.assertEqual(str(ctx.exception), msg.format('y')) p._initial_condition_cache = {} with self.assertRaises(KeyError) as ctx: p['y'] self.assertEqual(str(ctx.exception), msg.format('y')) p.final_setup() msg = "Group (g): Variable name '{}' not found." inputs, outputs, residuals = g.get_nonlinear_vectors() # inputs for vname in ['x', 'g.c.x']: with self.assertRaises(KeyError) as cm: inputs[vname] = 5.0 self.assertEqual(cm.exception.args[0], f"Group (g): Variable name '{vname}' not found.") with self.assertRaises(KeyError) as cm: inputs[vname] self.assertEqual(cm.exception.args[0], f"Group (g): Variable name '{vname}' not found.") # outputs for vname in ['y', 'g.c.y']: with self.assertRaises(KeyError) as cm: outputs[vname] = 5.0 self.assertEqual(cm.exception.args[0], f"Group (g): Variable name '{vname}' not found.") with self.assertRaises(KeyError) as cm: outputs[vname] self.assertEqual(cm.exception.args[0], f"Group (g): Variable name '{vname}' not found.") msg = r'Variable name pair \("{}", "{}"\) not found.' jac = g.linear_solver._assembled_jac # d(output)/d(input) with self.assertRaisesRegex(KeyError, msg.format('y', 'x')): jac['y', 'x'] = 5.0 with self.assertRaisesRegex(KeyError, msg.format('y', 'x')): jac['y', 'x'] # allow absolute keys now # with self.assertRaisesRegex(KeyError, msg.format('g.c.y', 'g.c.x')): # jac['g.c.y', 'g.c.x'] = 5.0 # with self.assertRaisesRegex(KeyError, msg.format('g.c.y', 'g.c.x')): # deriv = jac['g.c.y', 'g.c.x'] # d(output)/d(output) with self.assertRaisesRegex(KeyError, msg.format('y', 'y')): jac['y', 'y'] = 5.0 with self.assertRaisesRegex(KeyError, msg.format('y', 'y')): jac['y', 'y']
def test_circuit_voltage_source(self): from openmdao.api import ArmijoGoldsteinLS, Problem, IndepVarComp, BalanceComp, ExecComp from openmdao.api import NewtonSolver, DirectSolver, NonlinearRunOnce, LinearRunOnce from openmdao.test_suite.test_examples.test_circuit_analysis import Circuit p = Problem() model = p.model model.add_subsystem('ground', IndepVarComp('V', 0., units='V')) # replacing the fixed current source with a BalanceComp to represent a fixed Voltage source # model.add_subsystem('source', IndepVarComp('I', 0.1, units='A')) model.add_subsystem('batt', IndepVarComp('V', 1.5, units='V')) bal = model.add_subsystem('batt_balance', BalanceComp()) bal.add_balance('I', units='A', eq_units='V') model.add_subsystem('circuit', Circuit()) model.add_subsystem( 'batt_deltaV', ExecComp('dV = V1 - V2', V1={'units': 'V'}, V2={'units': 'V'}, dV={'units': 'V'})) # current into the circuit is now the output state from the batt_balance comp model.connect('batt_balance.I', 'circuit.I_in') model.connect('ground.V', ['circuit.Vg', 'batt_deltaV.V2']) model.connect('circuit.n1.V', 'batt_deltaV.V1') # set the lhs and rhs for the battery residual model.connect('batt.V', 'batt_balance.rhs:I') model.connect('batt_deltaV.dV', 'batt_balance.lhs:I') p.setup() ################### # Solver Setup ################### # change the circuit solver to RunOnce because we're # going to converge at the top level of the model with newton instead p.model.circuit.nonlinear_solver = NonlinearRunOnce() p.model.circuit.linear_solver = LinearRunOnce() # Put Newton at the top so it can also converge the new BalanceComp residual newton = p.model.nonlinear_solver = NewtonSolver() p.model.linear_solver = DirectSolver() newton.options['iprint'] = 2 newton.options['maxiter'] = 20 newton.options['solve_subsystems'] = True newton.linesearch = ArmijoGoldsteinLS() newton.linesearch.options['maxiter'] = 10 newton.linesearch.options['iprint'] = 2 # set initial guesses from the current source problem p['circuit.n1.V'] = 9.8 p['circuit.n2.V'] = .7 p.run_model() assert_rel_error(self, p['circuit.n1.V'], 1.5, 1e-5) assert_rel_error(self, p['circuit.n2.V'], 0.676232, 1e-5) assert_rel_error(self, p['circuit.R1.I'], 0.015, 1e-5) assert_rel_error(self, p['circuit.R2.I'], 8.23767999e-05, 1e-5) assert_rel_error(self, p['circuit.D1.I'], 8.23767999e-05, 1e-5)
def setup(self): F = 4 * 10**7 indeps = self.add_subsystem("indeps", IndepVarComp()) indeps.add_output( "n0_x_reaction_direction", 0, units="rad", desc= "Direction of horizontal reaction force of pinned joint on node 0") indeps.add_output( "n0_y_reaction_direction", math.pi / 2, units="rad", desc= "Direction of vertical reaction force of pinned joint on node 0") indeps.add_output( "n1_x_reaction_direction", 0, units="rad", desc="Direction of reaction force of roller joint on node 1") indeps.add_output("n0_beam0", 0, units="rad", desc="Direction of beam 0 at node 0") indeps.add_output("n0_beam1", math.pi * 3 / 2, units="rad", desc="Direction of beam 1 at node 0") indeps.add_output("n1_beam1", math.pi / 2, units="rad", desc="Direction of beam 1 at node 1") indeps.add_output("n1_beam4", math.pi * 5 / 3, units="rad", desc="Direction of beam 4 at node 1") indeps.add_output("n1_beam2", 0, units="rad", desc="Direction of beam 2 at node 1") indeps.add_output("n1_beam3", math.pi / 4, units="rad", desc="Direction of beam 3 at node 1") indeps.add_output("n2_beam2", math.pi, units="rad", desc="Direction of beam 2 at node 2") indeps.add_output("n2_beam5", math.pi * 4 / 3, units="rad", desc="Direction of beam 5 at node 2") indeps.add_output("n2_beam6", math.pi / 2, units="rad", desc="Direction of beam 6 at node 2") indeps.add_output("n3_beam4", math.pi * 2 / 3, units="rad", desc="Direction of beam 4 at node 3") indeps.add_output("n3_beam5", math.pi * 1 / 3, units="rad", desc="Direction of beam 5 at node 3") indeps.add_output("n4_beam0", math.pi, units="rad", desc="Direction of beam 0 at node 4") indeps.add_output("n4_beam3", math.pi * 5 / 4, units="rad", desc="Direction of beam 3 at node 4") indeps.add_output("n4_beam6", math.pi * 3 / 2, units="rad", desc="Direction of beam 6 at node 4") indeps.add_output("ext", F, units="N", desc="Force applied to beam structure") indeps.add_output("ext_direction", math.pi * 3 / 2, units="rad", desc="Direction of force applied to beam structure") indeps.add_output("A0", 1) indeps.add_output("A1", 1) indeps.add_output("A2", 1) indeps.add_output("A3", 1) indeps.add_output("A4", 1) indeps.add_output("A5", 1) indeps.add_output("A6", 1) indeps.add_output("L1") indeps.add_output("L2") cycle = self.add_subsystem("cycle", Group()) cycle.add_subsystem("node0", Node(n_loads=2, n_reactions=2)) cycle.add_subsystem("node1", Node(n_loads=4, n_reactions=1)) cycle.add_subsystem("node2", Node(n_loads=3)) cycle.add_subsystem("node3", Node(n_loads=2, n_external_forces=1)) cycle.add_subsystem("node4", Node(n_loads=3)) cycle.add_subsystem("beam0", Beam()) cycle.add_subsystem("beam1", Beam()) cycle.add_subsystem("beam2", Beam()) cycle.add_subsystem("beam3", Beam()) cycle.add_subsystem("beam4", Beam()) cycle.add_subsystem("beam5", Beam()) cycle.add_subsystem("beam6", Beam()) #Node 0 connections self.connect("indeps.n0_x_reaction_direction", "cycle.node0.direction0_reaction") self.connect("indeps.n0_y_reaction_direction", "cycle.node0.direction1_reaction") self.connect("indeps.n0_beam0", "cycle.node0.direction0_load") self.connect("indeps.n0_beam1", "cycle.node0.direction1_load") #Node 1 connections self.connect("indeps.n1_x_reaction_direction", "cycle.node1.direction0_reaction") self.connect("indeps.n1_beam1", "cycle.node1.direction0_load") self.connect("indeps.n1_beam2", "cycle.node1.direction1_load") self.connect("indeps.n1_beam3", "cycle.node1.direction2_load") self.connect("indeps.n1_beam4", "cycle.node1.direction3_load") #Node 2 connections self.connect("indeps.n2_beam2", "cycle.node2.direction0_load") self.connect("indeps.n2_beam5", "cycle.node2.direction1_load") self.connect("indeps.n2_beam6", "cycle.node2.direction2_load") #Node 3 connections self.connect("indeps.n3_beam4", "cycle.node3.direction0_load") self.connect("indeps.n3_beam5", "cycle.node3.direction1_load") self.connect("indeps.ext", "cycle.node3.force0_ext") self.connect("indeps.ext_direction", "cycle.node3.direction0_ext") #Node 4 connections self.connect("indeps.n4_beam0", "cycle.node4.direction0_load") self.connect("indeps.n4_beam3", "cycle.node4.direction1_load") self.connect("indeps.n4_beam6", "cycle.node4.direction2_load") #Inter-node connections self.connect("cycle.node0.load_out0", "cycle.beam0.force0") self.connect("cycle.node0.load_out1", "cycle.beam1.force0") self.connect("cycle.node1.load_out0", "cycle.beam1.force1") self.connect("cycle.node1.load_out1", "cycle.beam2.force0") self.connect("cycle.node1.load_out2", "cycle.beam3.force0") self.connect("cycle.node2.load_out0", "cycle.beam2.force1") self.connect("cycle.node2.load_out1", "cycle.beam5.force0") self.connect("cycle.node2.load_out2", "cycle.beam6.force0") self.connect("cycle.node3.load_out0", "cycle.beam4.force1") self.connect("cycle.node3.load_out1", "cycle.beam5.force1") self.connect("cycle.node4.load_out0", "cycle.beam0.force1") self.connect("cycle.node4.load_out1", "cycle.beam3.force1") self.connect("cycle.node4.load_out2", "cycle.beam6.force1") self.connect("cycle.node1.load_out3", "cycle.beam4.force0") self.connect("cycle.beam0.beam_force", ["cycle.node0.load_in0", "cycle.node4.load_in0"]) self.connect("cycle.beam1.beam_force", ["cycle.node0.load_in1", "cycle.node1.load_in0"]) self.connect("cycle.beam2.beam_force", ["cycle.node1.load_in1", "cycle.node2.load_in0"]) self.connect("cycle.beam3.beam_force", ["cycle.node1.load_in2", "cycle.node4.load_in1"]) self.connect("cycle.beam4.beam_force", ["cycle.node1.load_in3", "cycle.node3.load_in0"]) self.connect("cycle.beam5.beam_force", ["cycle.node2.load_in1", "cycle.node3.load_in1"]) self.connect("cycle.beam6.beam_force", ["cycle.node2.load_in2", "cycle.node4.load_in2"]) cycle.nonlinear_solver = NewtonSolver() cycle.nonlinear_solver.options['atol'] = 1e-7 cycle.nonlinear_solver.options['solve_subsystems'] = True cycle.nonlinear_solver.options["iprint"] = 2 cycle.linear_solver = DirectSolver() self.add_subsystem( "obj_cmp", ExecComp("obj = L1 * (A0 + A1 + A2 + A4 + A5 + A6) + L2 * A3")) self.add_subsystem("con0", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con1", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con2", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con3", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con4", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con5", ExecComp("con = 400 - abs(sigma)")) self.add_subsystem("con6", ExecComp("con = 400 - abs(sigma)")) self.connect("indeps.L1", ["obj_cmp.L1"]) self.connect("indeps.L2", ["obj_cmp.L2"]) self.connect("cycle.beam0.sigma", ["con0.sigma"]) self.connect("cycle.beam1.sigma", ["con1.sigma"]) self.connect("cycle.beam2.sigma", ["con2.sigma"]) self.connect("cycle.beam3.sigma", ["con3.sigma"]) self.connect("cycle.beam4.sigma", ["con4.sigma"]) self.connect("cycle.beam5.sigma", ["con5.sigma"]) self.connect("cycle.beam6.sigma", ["con6.sigma"]) self.connect("indeps.A0", ["cycle.beam0.A", "obj_cmp.A0"]) self.connect("indeps.A1", ["cycle.beam1.A", "obj_cmp.A1"]) self.connect("indeps.A2", ["cycle.beam2.A", "obj_cmp.A2"]) self.connect("indeps.A3", ["cycle.beam3.A", "obj_cmp.A3"]) self.connect("indeps.A4", ["cycle.beam4.A", "obj_cmp.A4"]) self.connect("indeps.A5", ["cycle.beam5.A", "obj_cmp.A5"]) self.connect("indeps.A6", ["cycle.beam6.A", "obj_cmp.A6"])
def setup(self): thermo_spec = species_data.janaf design = self.options['design'] statics = self.options['statics'] self.add_subsystem('fc', FlightConditions(thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('inlet', Inlet(design=design, thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('fan', Compressor(map_data=FanMap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, bleed_names=[], statics=statics, map_extrap=True), promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('splitter', Splitter(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('duct4', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('lpc', Compressor(map_data=LPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics, map_extrap=True),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('duct6', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('hpc', Compressor(map_data=HPCmap, design=design, thermo_data=thermo_spec, elements=AIR_MIX, bleed_names=['cool1','cool2','cust'], statics=statics, map_extrap=True),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('bld3', BleedOut(design=design, statics=statics, bleed_names=['cool3','cool4'])) self.add_subsystem('burner', Combustor(design=design,thermo_data=thermo_spec, inflow_elements=AIR_MIX, air_fuel_elements=AIR_FUEL_MIX, fuel_type='Jet-A(g)', statics=statics)) self.add_subsystem('hpt', Turbine(map_data=HPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool3','cool4'], statics=statics, map_extrap=True),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('duct11', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics)) self.add_subsystem('lpt', Turbine(map_data=LPTmap, design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, bleed_names=['cool1','cool2'], statics=statics, map_extrap=True),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('duct13', Duct(design=design, thermo_data=thermo_spec, elements=AIR_FUEL_MIX, statics=statics)) self.add_subsystem('core_nozz', Nozzle(nozzType='CV', lossCoef='Cv', thermo_data=thermo_spec, elements=AIR_FUEL_MIX)) self.add_subsystem('byp_bld', BleedOut(design=design, statics=statics, bleed_names=['bypBld'])) self.add_subsystem('duct15', Duct(design=design, thermo_data=thermo_spec, elements=AIR_MIX, statics=statics)) self.add_subsystem('byp_nozz', Nozzle(nozzType='CV', lossCoef='Cv', thermo_data=thermo_spec, elements=AIR_MIX)) self.add_subsystem('lp_shaft', Shaft(num_ports=3),promotes_inputs=[('Nmech','LP_Nmech')]) self.add_subsystem('hp_shaft', Shaft(num_ports=2),promotes_inputs=[('Nmech','HP_Nmech')]) self.add_subsystem('perf', Performance(num_nozzles=2, num_burners=1)) self.connect('inlet.Fl_O:tot:P', 'perf.Pt2') self.connect('hpc.Fl_O:tot:P', 'perf.Pt3') self.connect('burner.Wfuel', 'perf.Wfuel_0') self.connect('inlet.F_ram', 'perf.ram_drag') self.connect('core_nozz.Fg', 'perf.Fg_0') self.connect('byp_nozz.Fg', 'perf.Fg_1') self.connect('fan.trq', 'lp_shaft.trq_0') self.connect('lpc.trq', 'lp_shaft.trq_1') self.connect('lpt.trq', 'lp_shaft.trq_2') self.connect('hpc.trq', 'hp_shaft.trq_0') self.connect('hpt.trq', 'hp_shaft.trq_1') self.connect('fc.Fl_O:stat:P', 'core_nozz.Ps_exhaust') self.connect('fc.Fl_O:stat:P', 'byp_nozz.Ps_exhaust') balance = self.add_subsystem('balance', BalanceComp()) if design: balance.add_balance('W', units='lbm/s', eq_units='lbf') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('perf.Fn', 'balance.lhs:W') balance.add_balance('FAR', eq_units='degR', lower=1e-4, val=.017) self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR') balance.add_balance('lpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.lpt_PR', 'lpt.PR') self.connect('lp_shaft.pwr_in_real', 'balance.lhs:lpt_PR') self.connect('lp_shaft.pwr_out_real', 'balance.rhs:lpt_PR') balance.add_balance('hpt_PR', val=1.5, lower=1.001, upper=8, eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.hpt_PR', 'hpt.PR') self.connect('hp_shaft.pwr_in_real', 'balance.lhs:hpt_PR') self.connect('hp_shaft.pwr_out_real', 'balance.rhs:hpt_PR') else: balance.add_balance('FAR', val=0.017, lower=1e-4, eq_units='lbf') self.connect('balance.FAR', 'burner.Fl_I:FAR') self.connect('perf.Fn', 'balance.lhs:FAR') balance.add_balance('W', units='lbm/s', lower=10., upper=1000., eq_units='inch**2') self.connect('balance.W', 'inlet.Fl_I:stat:W') self.connect('core_nozz.Throat:stat:area', 'balance.lhs:W') balance.add_balance('BPR', lower=2., upper=10., eq_units='inch**2') self.connect('balance.BPR', 'splitter.BPR') self.connect('byp_nozz.Throat:stat:area', 'balance.lhs:BPR') balance.add_balance('lp_Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.lp_Nmech', 'LP_Nmech') self.connect('lp_shaft.pwr_in_real', 'balance.lhs:lp_Nmech') self.connect('lp_shaft.pwr_out_real', 'balance.rhs:lp_Nmech') balance.add_balance('hp_Nmech', val=1.5, units='rpm', lower=500., eq_units='hp', use_mult=True, mult_val=-1) self.connect('balance.hp_Nmech', 'HP_Nmech') self.connect('hp_shaft.pwr_in_real', 'balance.lhs:hp_Nmech') self.connect('hp_shaft.pwr_out_real', 'balance.rhs:hp_Nmech') self.set_order(['balance', 'fc', 'inlet', 'fan', 'splitter', 'duct4', 'lpc', 'duct6', 'hpc', 'bld3', 'burner', 'hpt', 'duct11', 'lpt', 'duct13', 'core_nozz', 'byp_bld', 'duct15', 'byp_nozz', 'lp_shaft', 'hp_shaft', 'perf']) connect_flow(self, 'fc.Fl_O', 'inlet.Fl_I', connect_w=False) connect_flow(self, 'inlet.Fl_O', 'fan.Fl_I') connect_flow(self, 'fan.Fl_O', 'splitter.Fl_I') connect_flow(self, 'splitter.Fl_O1', 'duct4.Fl_I') connect_flow(self, 'duct4.Fl_O', 'lpc.Fl_I') connect_flow(self, 'lpc.Fl_O', 'duct6.Fl_I') connect_flow(self, 'duct6.Fl_O', 'hpc.Fl_I') connect_flow(self, 'hpc.Fl_O', 'bld3.Fl_I') connect_flow(self, 'bld3.Fl_O', 'burner.Fl_I') connect_flow(self, 'burner.Fl_O', 'hpt.Fl_I') connect_flow(self, 'hpt.Fl_O', 'duct11.Fl_I') connect_flow(self, 'duct11.Fl_O', 'lpt.Fl_I') connect_flow(self, 'lpt.Fl_O', 'duct13.Fl_I') connect_flow(self, 'duct13.Fl_O','core_nozz.Fl_I') connect_flow(self, 'splitter.Fl_O2', 'byp_bld.Fl_I') connect_flow(self, 'byp_bld.Fl_O', 'duct15.Fl_I') connect_flow(self, 'duct15.Fl_O', 'byp_nozz.Fl_I') connect_flow(self, 'hpc.cool1', 'lpt.cool1', connect_stat=False) connect_flow(self, 'hpc.cool2', 'lpt.cool2', connect_stat=False) connect_flow(self, 'bld3.cool3', 'hpt.cool3', connect_stat=False) connect_flow(self, 'bld3.cool4', 'hpt.cool4', connect_stat=False) newton = self.nonlinear_solver = NewtonSolver() newton.options['atol'] = 1e-8 newton.options['rtol'] = 1e-8 newton.options['iprint'] = 2 newton.options['maxiter'] = 50 newton.options['solve_subsystems'] = True newton.options['max_sub_solves'] = 100 # ls = newton.linesearch = BoundsEnforceLS() ls = newton.linesearch = ArmijoGoldsteinLS() ls.options['maxiter'] = 3 ls.options['bound_enforcement'] = 'scalar' # ls.options['print_bound_enforce'] = True self.linear_solver = DirectSolver(assemble_jac=True)
def setup(self): super(ImplicitTMIntegrator, self).setup() ode_function = self.options['ode_function'] method = self.options['method'] starting_coeffs = self.options['starting_coeffs'] has_starting_method = method.starting_method is not None is_starting_method = starting_coeffs is not None states = ode_function._states static_parameters = ode_function._static_parameters dynamic_parameters = ode_function._dynamic_parameters time_units = ode_function._time_options['units'] starting_norm_times, my_norm_times = self._get_meta() glm_A, glm_B, glm_U, glm_V, num_stages, num_step_vars = self._get_method() num_times = len(my_norm_times) num_stages = method.num_stages num_step_vars = method.num_values glm_A = method.A glm_B = method.B glm_U = method.U glm_V = method.V # ------------------------------------------------------------------------------------ integration_group = Group() self.add_subsystem('integration_group', integration_group) for i_step in range(len(my_norm_times) - 1): group = Group(assembled_jac_type='dense') group_old_name = 'integration_group.step_%i' % (i_step - 1) group_new_name = 'integration_group.step_%i' % i_step integration_group.add_subsystem(group_new_name.split('.')[1], group) comp = self._create_ode(num_stages) group.add_subsystem('ode_comp', comp) if ode_function._time_options['targets']: self.connect('time_comp.stage_times', ['.'.join((group_new_name + '.ode_comp', t)) for t in ode_function._time_options['targets']], src_indices=i_step * (num_stages) + np.arange(num_stages)) if len(static_parameters) > 0: self._connect_multiple( self._get_static_parameter_names('static_parameter_comp', 'out'), self._get_static_parameter_names(group_new_name + '.ode_comp', 'targets'), src_indices_list=[[0] * num_stages for _ in range(len(static_parameters))] ) if len(dynamic_parameters) > 0: src_indices_list = [] for parameter_name, value in iteritems(dynamic_parameters): size = np.prod(value['shape']) shape = value['shape'] arange = np.arange(((len(my_norm_times) - 1) * num_stages * size)).reshape( ((len(my_norm_times) - 1, num_stages,) + shape)) src_indices = arange[i_step, :, :] src_indices_list.append(src_indices.flat) self._connect_multiple( self._get_dynamic_parameter_names('dynamic_parameter_comp', 'out'), self._get_dynamic_parameter_names(group_new_name + '.ode_comp', 'targets'), src_indices_list, ) comp = ImplicitTMStageComp( states=states, time_units=time_units, num_stages=num_stages, num_step_vars=num_step_vars, glm_A=glm_A, glm_U=glm_U, i_step=i_step, ) group.add_subsystem('stage_comp', comp) self.connect('time_comp.h_vec', group_new_name + '.stage_comp.h', src_indices=i_step) comp = ImplicitTMStepComp( states=states, time_units=time_units, num_stages=num_stages, num_step_vars=num_step_vars, glm_B=glm_B, glm_V=glm_V, i_step=i_step, ) group.add_subsystem('step_comp', comp) self.connect('time_comp.h_vec', group_new_name + '.step_comp.h', src_indices=i_step) self._connect_multiple( self._get_state_names(group_new_name + '.ode_comp', 'rate_source'), self._get_state_names(group_new_name + '.step_comp', 'F', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_new_name + '.ode_comp', 'rate_source'), self._get_state_names(group_new_name + '.stage_comp', 'F', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_new_name + '.stage_comp', 'Y', i_step=i_step), self._get_state_names(group_new_name + '.ode_comp', 'targets'), ) if i_step == 0: self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names(group_new_name + '.step_comp', 'y_old', i_step=i_step), ) self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names(group_new_name + '.stage_comp', 'y_old', i_step=i_step), ) else: self._connect_multiple( self._get_state_names(group_old_name + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names(group_new_name + '.step_comp', 'y_old', i_step=i_step), ) self._connect_multiple( self._get_state_names(group_old_name + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names(group_new_name + '.stage_comp', 'y_old', i_step=i_step), ) group.nonlinear_solver = NewtonSolver(iprint=2, maxiter=100) group.linear_solver = DirectSolver(assemble_jac=True) promotes = [] promotes.extend([get_name('state', state_name) for state_name in states]) if is_starting_method: promotes.extend([get_name('starting', state_name) for state_name in states]) comp = TMOutputComp( states=states, num_starting_times=len(starting_norm_times), num_my_times=len(my_norm_times), num_step_vars=num_step_vars, starting_coeffs=starting_coeffs) self.add_subsystem('output_comp', comp, promotes_outputs=promotes) if has_starting_method: self._connect_multiple( self._get_state_names('starting_system', 'state'), self._get_state_names('output_comp', 'starting_state'), ) for i_step in range(len(my_norm_times)): if i_step == 0: self._connect_multiple( self._get_state_names('starting_system', 'starting'), self._get_state_names('output_comp', 'y', i_step=i_step), ) else: self._connect_multiple( self._get_state_names('integration_group.step_%i' % (i_step - 1) + '.step_comp', 'y_new', i_step=i_step - 1), self._get_state_names('output_comp', 'y', i_step=i_step), )
def test_scalar_guess_func_using_outputs(self): model = Group() ind = IndepVarComp() ind.add_output('a', 1) ind.add_output('b', -4) ind.add_output('c', 3) lhs = ExecComp('lhs=-(a*x**2+b*x)') bal = BalanceComp(name='x', rhs_name='c') model.add_subsystem('ind_comp', ind, promotes_outputs=['a', 'b', 'c']) model.add_subsystem('lhs_comp', lhs, promotes_inputs=['a', 'b', 'x']) model.add_subsystem('bal_comp', bal, promotes_inputs=['c'], promotes_outputs=['x']) model.connect('lhs_comp.lhs', 'bal_comp.lhs:x') model.linear_solver = DirectSolver() model.nonlinear_solver = NewtonSolver(maxiter=100, iprint=0) # first verify behavior of the balance comp without the guess function # at initial conditions x=5, x=0 and x=-1 prob = Problem(model) prob.setup() # default solution with initial value of 5 is x=3. prob['x'] = 5 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7) # default solution with initial value of 0 is x=1. prob['x'] = 0 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # default solution with initial value of -1 is x=1. prob['x'] = -1 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # now use a guess function that steers us to the x=3 solution only # if the initial value of x is less than zero def guess_function(inputs, outputs, residuals): if outputs['x'] < 0: outputs['x'] = 3. bal.options['guess_func'] = guess_function # solution with initial value of 5 is still x=3. prob['x'] = 5 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7) # solution with initial value of 0 is still x=1. prob['x'] = 0 prob.run_model() assert_almost_equal(prob['x'], 1.0, decimal=7) # solution with initial value of -1 is now x=3. prob['x'] = -1 prob.run_model() assert_almost_equal(prob['x'], 3.0, decimal=7)
def test_two_phase_cannonball_for_docs(self): from openmdao.api import Problem, Group, IndepVarComp, DirectSolver, SqliteRecorder, \ pyOptSparseDriver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, Trajectory, Radau, GaussLobatto from dymos.examples.cannonball.cannonball_ode import CannonballODE from dymos.examples.cannonball.size_comp import CannonballSizeComp p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.options['dynamic_simul_derivs'] = True external_params = p.model.add_subsystem('external_params', IndepVarComp()) external_params.add_output('radius', val=0.10, units='m') external_params.add_output('dens', val=7.87, units='g/cm**3') external_params.add_design_var('radius', lower=0.01, upper=0.10, ref0=0.01, ref=0.10) p.model.add_subsystem('size_comp', CannonballSizeComp()) traj = p.model.add_subsystem('traj', Trajectory()) transcription = Radau(num_segments=5, order=3, compressed=True) ascent = Phase(ode_class=CannonballODE, transcription=transcription) ascent = traj.add_phase('ascent', ascent) # All initial states except flight path angle are fixed # Final flight path angle is fixed (we will set it to zero so that the phase ends at apogee) ascent.set_time_options(fix_initial=True, duration_bounds=(1, 100), duration_ref=100, units='s') ascent.set_state_options('r', fix_initial=True, fix_final=False) ascent.set_state_options('h', fix_initial=True, fix_final=False) ascent.set_state_options('gam', fix_initial=False, fix_final=True) ascent.set_state_options('v', fix_initial=False, fix_final=False) # Limit the muzzle energy ascent.add_boundary_constraint('kinetic_energy.ke', loc='initial', units='J', upper=400000, lower=0, ref=100000, shape=(1,)) # Second Phase (descent) transcription = GaussLobatto(num_segments=5, order=3, compressed=True) descent = Phase(ode_class=CannonballODE, transcription=transcription) traj.add_phase('descent', descent) # All initial states and time are free (they will be linked to the final states of ascent. # Final altitude is fixed (we will set it to zero so that the phase ends at ground impact) descent.set_time_options(initial_bounds=(.5, 100), duration_bounds=(.5, 100), duration_ref=100) descent.set_state_options('r', fix_initial=False, fix_final=False) descent.set_state_options('h', fix_initial=False, fix_final=True) descent.set_state_options('gam', fix_initial=False, fix_final=False) descent.set_state_options('v', fix_initial=False, fix_final=False) descent.add_objective('r', loc='final', scaler=-1.0) # Add internally-managed design parameters to the trajectory. traj.add_design_parameter('CD', val=0.5, units=None, opt=False) traj.add_design_parameter('CL', val=0.0, units=None, opt=False) traj.add_design_parameter('T', val=0.0, units='N', opt=False) traj.add_design_parameter('alpha', val=0.0, units='deg', opt=False) # Add externally-provided design parameters to the trajectory. traj.add_input_parameter('mass', target_params={'ascent': 'm', 'descent': 'm'}, val=1.0) traj.add_input_parameter('S', val=0.005) # Link Phases (link time and all state variables) traj.link_phases(phases=['ascent', 'descent'], vars=['*']) # Issue Connections p.model.connect('external_params.radius', 'size_comp.radius') p.model.connect('external_params.dens', 'size_comp.dens') p.model.connect('size_comp.mass', 'traj.input_parameters:mass') p.model.connect('size_comp.S', 'traj.input_parameters:S') # Finish Problem Setup p.model.linear_solver = DirectSolver() p.driver.add_recorder(SqliteRecorder('ex_two_phase_cannonball.db')) p.setup(check=True) # Set Initial Guesses p.set_val('external_params.radius', 0.05, units='m') p.set_val('external_params.dens', 7.87, units='g/cm**3') p.set_val('traj.design_parameters:CD', 0.5) p.set_val('traj.design_parameters:CL', 0.0) p.set_val('traj.design_parameters:T', 0.0) p.set_val('traj.ascent.t_initial', 0.0) p.set_val('traj.ascent.t_duration', 10.0) p.set_val('traj.ascent.states:r', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:h', ascent.interpolate(ys=[0, 100], nodes='state_input')) p.set_val('traj.ascent.states:v', ascent.interpolate(ys=[200, 150], nodes='state_input')) p.set_val('traj.ascent.states:gam', ascent.interpolate(ys=[25, 0], nodes='state_input'), units='deg') p.set_val('traj.descent.t_initial', 10.0) p.set_val('traj.descent.t_duration', 10.0) p.set_val('traj.descent.states:r', descent.interpolate(ys=[100, 200], nodes='state_input')) p.set_val('traj.descent.states:h', descent.interpolate(ys=[100, 0], nodes='state_input')) p.set_val('traj.descent.states:v', descent.interpolate(ys=[150, 200], nodes='state_input')) p.set_val('traj.descent.states:gam', descent.interpolate(ys=[0, -45], nodes='state_input'), units='deg') p.run_driver() assert_rel_error(self, p.get_val('traj.descent.states:r')[-1], 3183.25, tolerance=1.0E-2) exp_out = traj.simulate() print('optimal radius: {0:6.4f} m '.format(p.get_val('external_params.radius', units='m')[0])) print('cannonball mass: {0:6.4f} kg '.format(p.get_val('size_comp.mass', units='kg')[0])) print('launch angle: {0:6.4f} ' 'deg '.format(p.get_val('traj.ascent.timeseries.states:gam', units='deg')[0, 0])) print('maximum range: {0:6.4f} ' 'm '.format(p.get_val('traj.descent.timeseries.states:r')[-1, 0])) fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10, 6)) time_imp = {'ascent': p.get_val('traj.ascent.timeseries.time'), 'descent': p.get_val('traj.descent.timeseries.time')} time_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.time'), 'descent': exp_out.get_val('traj.descent.timeseries.time')} r_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:r'), 'descent': p.get_val('traj.descent.timeseries.states:r')} r_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:r'), 'descent': exp_out.get_val('traj.descent.timeseries.states:r')} h_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:h'), 'descent': p.get_val('traj.descent.timeseries.states:h')} h_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:h'), 'descent': exp_out.get_val('traj.descent.timeseries.states:h')} axes.plot(r_imp['ascent'], h_imp['ascent'], 'bo') axes.plot(r_imp['descent'], h_imp['descent'], 'ro') axes.plot(r_exp['ascent'], h_exp['ascent'], 'b--') axes.plot(r_exp['descent'], h_exp['descent'], 'r--') axes.set_xlabel('range (m)') axes.set_ylabel('altitude (m)') fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(10, 6)) states = ['r', 'h', 'v', 'gam'] for i, state in enumerate(states): x_imp = {'ascent': p.get_val('traj.ascent.timeseries.states:{0}'.format(state)), 'descent': p.get_val('traj.descent.timeseries.states:{0}'.format(state))} x_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.states:{0}'.format(state)), 'descent': exp_out.get_val('traj.descent.timeseries.states:{0}'.format(state))} axes[i].set_ylabel(state) axes[i].plot(time_imp['ascent'], x_imp['ascent'], 'bo') axes[i].plot(time_imp['descent'], x_imp['descent'], 'ro') axes[i].plot(time_exp['ascent'], x_exp['ascent'], 'b--') axes[i].plot(time_exp['descent'], x_exp['descent'], 'r--') params = ['CL', 'CD', 'T', 'alpha', 'm', 'S'] fig, axes = plt.subplots(nrows=6, ncols=1, figsize=(12, 6)) for i, param in enumerate(params): p_imp = { 'ascent': p.get_val('traj.ascent.timeseries.traj_parameters:{0}'.format(param)), 'descent': p.get_val('traj.descent.timeseries.traj_parameters:{0}'.format(param))} p_exp = {'ascent': exp_out.get_val('traj.ascent.timeseries.' 'traj_parameters:{0}'.format(param)), 'descent': exp_out.get_val('traj.descent.timeseries.' 'traj_parameters:{0}'.format(param))} axes[i].set_ylabel(param) axes[i].plot(time_imp['ascent'], p_imp['ascent'], 'bo') axes[i].plot(time_imp['descent'], p_imp['descent'], 'ro') axes[i].plot(time_exp['ascent'], p_exp['ascent'], 'b--') axes[i].plot(time_exp['descent'], p_exp['descent'], 'r--') plt.show()
phase.add_boundary_constraint('h', loc='final', equals=100., scaler=1.0E-3, units='m') phase.add_boundary_constraint('r', loc='final', equals=1500., units='km') # phase.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') phase.add_path_constraint(name='aero.mach', lower=0.01, upper=.9) # phase.add_path_constraint(name='prop.m_dot', upper=0.) # phase.add_path_constraint(name='flight_dynamics.r_dot', lower=0.) # phase.add_path_constraint(name='m', lower=1e4) phase.add_path_constraint(name='h', lower=0.) # phase.set_objective('time', loc='final', ref=10.0) phase.add_objective('m', loc='final', ref=-10000.0) # phase.set_objective('r', loc='final', ref=-100000.0) p.model.linear_solver = DirectSolver(assemble_jac=True) p.model.options['assembled_jac_type'] = 'csc' # p.driver.add_recorder(SqliteRecorder('out.db')) p.setup(mode='fwd', check=True) # from openmdao.api import view_model # view_model(p) # exit() p['phase.t_initial'] = 0.0 p['phase.t_duration'] = 500. p['phase.states:r'] = phase.interpolate(ys=[0.0, 150.], nodes='disc') p['phase.states:gam'] = phase.interpolate(ys=[0.0, 0.0], nodes='disc') p['phase.states:m'] = phase.interpolate(ys=[5e4, 4.9e4], nodes='disc') p['phase.states:h'][:] = 1e4
def escort_problem(optimizer='SLSQP', num_seg=3, transcription_order=5, transcription='gauss-lobatto', meeting_altitude=14000., climb_time=350., starting_mass=19030.468): p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = optimizer if optimizer == 'SNOPT': p.driver.opt_settings['Major iterations limit'] = 1000 p.driver.opt_settings['Iterations limit'] = 100000000 p.driver.opt_settings['iSumm'] = 6 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-5 p.driver.opt_settings['Verify level'] = -1 p.driver.opt_settings['Function precision'] = 1.0E-6 p.driver.opt_settings['Linesearch tolerance'] = 0.10 p.driver.opt_settings['Major step limit'] = 0.5 phase_class = _phase_map[transcription] climb = Phase('gauss-lobatto', ode_class=MinTimeClimbODE, num_segments=num_seg, transcription_order=transcription_order) climb.set_time_options(duration_bounds=(50, climb_time), duration_ref=100.0) climb.set_state_options('r', fix_initial=True, lower=0, upper=1.0E6, scaler=1.0E-3, defect_scaler=1.0E-2, units='m') climb.set_state_options('h', fix_initial=True, lower=0, upper=20000.0, scaler=1.0E-3, defect_scaler=1.0E-3, units='m') climb.set_state_options('v', fix_initial=True, lower=10.0, scaler=1.0E-2, defect_scaler=1.0E-2, units='m/s') climb.set_state_options('gam', fix_initial=True, lower=-1.5, upper=1.5, ref=1.0, defect_scaler=1.0, units='rad') climb.set_state_options('m', fix_initial=True, lower=10.0, upper=1.0E5, scaler=1.0E-3, defect_scaler=1.0E-3) climb.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=True) climb.add_control('S', val=49.2386, units='m**2', dynamic=False, opt=False) climb.add_control('Isp', val=5000.0, units='s', dynamic=False, opt=False) climb.add_control('throttle', val=1.0, dynamic=False, opt=False) climb.add_boundary_constraint('h', loc='final', equals=meeting_altitude, scaler=1.0E-3, units='m') climb.add_boundary_constraint('aero.mach', loc='final', equals=1.0, units=None) climb.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') # climb.add_boundary_constraint('time', loc='final', upper=climb_time, units='s') climb.add_path_constraint(name='h', lower=100.0, upper=20000, ref=20000) climb.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8) # Minimize time at the end of the climb climb.set_objective('time', loc='final', ref=100.0) p.model.add_subsystem('climb', climb) escort = Phase('gauss-lobatto', ode_class=MinTimeClimbODE, num_segments=num_seg * 2, transcription_order=transcription_order) escort.set_time_options(duration_bounds=(50, 10000), opt_initial=True, duration_ref=100.0) escort.set_state_options('r', lower=0, upper=1.0E6, scaler=1.0E-3, defect_scaler=1.0E-2, units='m') escort.set_state_options('h', lower=0, upper=20000.0, scaler=1.0E-3, defect_scaler=1.0E-3, units='m') escort.set_state_options('v', lower=10.0, scaler=1.0E-2, defect_scaler=1.0E-2, units='m/s') escort.set_state_options('gam', lower=-1.5, upper=1.5, ref=1.0, defect_scaler=1.0, units='rad') escort.set_state_options('m', lower=10.0, upper=1.0E5, scaler=1.0E-3, defect_scaler=1.0E-3) escort.add_control('alpha', units='deg', lower=-8.0, upper=8.0, scaler=1.0, rate_continuity=True) escort.add_control('S', val=49.2386, units='m**2', dynamic=False, opt=False) escort.add_control('Isp', val=1600.0, units='s', dynamic=False, opt=False) escort.add_control('throttle', val=1.0, lower=0., upper=1., opt=True) # escort.add_control('throttle', val=1.0, dynamic=False, opt=False) # escort.add_boundary_constraint('h', loc='final', equals=20000, scaler=1.0E-3, units='m') # escort.add_boundary_constraint('aero.mach', loc='final', equals=1.0, units=None) # escort.add_boundary_constraint('gam', loc='final', equals=0.0, units='rad') escort.add_boundary_constraint('m', loc='final', equals=15000.0, units='kg') escort.add_path_constraint(name='h', lower=meeting_altitude, upper=meeting_altitude, ref=meeting_altitude) escort.add_path_constraint(name='aero.mach', equals=1.0) # Maximize distance at the end of the escort # escort.set_objective('r', loc='final', ref=-1e5) p.model.add_subsystem('escort', escort) # Connect the phases linkage_comp = PhaseLinkageComp() linkage_comp.add_linkage(name='L01', vars=['t'], units='s', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['r'], units='m', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['h'], units='m', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['v'], units='m/s', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['gam'], units='rad', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['m'], units='kg', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['alpha'], units='rad', equals=0.0, linear=True) linkage_comp.add_linkage(name='L01', vars=['throttle'], equals=0.0, linear=True) p.model.connect('climb.time++', 'linkages.L01_t:lhs') p.model.connect('escort.time--', 'linkages.L01_t:rhs') p.model.connect('climb.states:r++', 'linkages.L01_r:lhs') p.model.connect('escort.states:r--', 'linkages.L01_r:rhs') p.model.connect('climb.states:h++', 'linkages.L01_h:lhs') p.model.connect('escort.states:h--', 'linkages.L01_h:rhs') # p.model.connect('climb.states:v++', 'linkages.L01_v:lhs') p.model.connect('escort.states:v--', 'linkages.L01_v:rhs') # p.model.connect('climb.states:gam++', 'linkages.L01_gam:lhs') p.model.connect('escort.states:gam--', 'linkages.L01_gam:rhs') p.model.connect('climb.states:m++', 'linkages.L01_m:lhs') p.model.connect('escort.states:m--', 'linkages.L01_m:rhs') p.model.connect('climb.controls:alpha++', 'linkages.L01_alpha:lhs') p.model.connect('escort.controls:alpha--', 'linkages.L01_alpha:rhs') p.model.connect('climb.controls:throttle++', 'linkages.L01_throttle:lhs') p.model.connect('escort.controls:throttle--', 'linkages.L01_throttle:rhs') p.model.add_subsystem('linkages', linkage_comp) p.model.linear_solver = DirectSolver(assemble_jac=True) p.model.options['assembled_jac_type'] = 'csc' # p.driver.add_recorder(SqliteRecorder('escort.db')) p.setup(mode='fwd', check=True) p['climb.t_initial'] = 0.0 p['climb.t_duration'] = 200. p['climb.states:r'] = climb.interpolate(ys=[0.0, 111319.54], nodes='disc') p['climb.states:h'] = climb.interpolate(ys=[100.0, 20000.0], nodes='disc') p['climb.states:v'] = climb.interpolate(ys=[135.964, 283.159], nodes='disc') p['climb.states:gam'] = climb.interpolate(ys=[0.0, 0.0], nodes='disc') p['climb.states:m'] = climb.interpolate(ys=[starting_mass, 16841.431], nodes='disc') p['climb.controls:alpha'] = climb.interpolate(ys=[0.0, 0.0], nodes='all') p['escort.t_initial'] = 200. p['escort.t_duration'] = 1000. p['escort.states:r'] = escort.interpolate(ys=[111319.54, 400000.], nodes='disc') p['escort.states:h'] = escort.interpolate( ys=[meeting_altitude, meeting_altitude], nodes='disc') p['escort.states:v'] = escort.interpolate(ys=[250., 250.], nodes='disc') p['escort.states:gam'] = escort.interpolate(ys=[0.0, 0.0], nodes='disc') p['escort.states:m'] = escort.interpolate(ys=[17000., 15000.], nodes='disc') p['escort.controls:alpha'] = escort.interpolate(ys=[0.0, 0.0], nodes='all') return p
def test_double_integrator_for_docs(self): import numpy as np import matplotlib.pyplot as plt from openmdao.api import Problem, Group, ScipyOptimizeDriver, DirectSolver from dymos import Phase from dymos.examples.double_integrator.double_integrator_ode import DoubleIntegratorODE p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() p.driver.options['dynamic_simul_derivs'] = True phase = Phase('gauss-lobatto', ode_class=DoubleIntegratorODE, num_segments=20, transcription_order=3, compressed=True) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(1.0, 1.0)) phase.set_state_options('x', fix_initial=True) phase.set_state_options('v', fix_initial=True, fix_final=True) phase.add_control('u', units='m/s**2', scaler=0.01, continuity=False, rate_continuity=False, rate2_continuity=False, lower=-1.0, upper=1.0) # Maximize distance travelled in one second. phase.add_objective('x', loc='final', scaler=-1) p.model.linear_solver = DirectSolver(assemble_jac=True) p.setup(check=True) p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 1.0 p['phase0.states:x'] = phase.interpolate(ys=[0, 0.25], nodes='state_input') p['phase0.states:v'] = phase.interpolate(ys=[0, 0], nodes='state_input') p['phase0.controls:u'] = phase.interpolate(ys=[1, -1], nodes='control_input') p.run_driver() exp_out = phase.simulate(times=np.linspace(p['phase0.t_initial'], p['phase0.t_duration'], 100), record=False) # Plot results fig, axes = plt.subplots(3, 1) fig.suptitle('Double Integrator Direct Collocation Solution') t_imp = phase.get_values('time', nodes='all') x_imp = phase.get_values('x', nodes='all') v_imp = phase.get_values('v', nodes='all') u_imp = phase.get_values('u', nodes='all') t_exp = exp_out.get_values('time') x_exp = exp_out.get_values('x') v_exp = exp_out.get_values('v') u_exp = exp_out.get_values('u') axes[0].plot(t_imp, x_imp, 'ro', label='implicit') axes[0].plot(t_exp, x_exp, 'b-', label='explicit') axes[0].set_xlabel('time (s)') axes[0].set_ylabel('x (m)') axes[0].grid(True) axes[0].legend(loc='best') axes[1].plot(t_imp, v_imp, 'ro', label='implicit') axes[1].plot(t_exp, v_exp, 'b-', label='explicit') axes[1].set_xlabel('time (s)') axes[1].set_ylabel('v (m/s)') axes[1].grid(True) axes[1].legend(loc='best') axes[2].plot(t_imp, u_imp, 'ro', label='implicit') axes[2].plot(t_exp, u_exp, 'b-', label='explicit') axes[2].set_xlabel('time (s)') axes[2].set_ylabel('u (m/s**2)') axes[2].grid(True) axes[2].legend(loc='best') plt.show()
def compute_drag_polar(Mach, alphas, surfaces, trimmed=False): if isinstance(surfaces, dict): surfaces = [surfaces,] # Create the OpenMDAO problem prob = Problem() # Create an independent variable component that will supply the flow # conditions to the problem. indep_var_comp = IndepVarComp() indep_var_comp.add_output('v', val=248.136, units='m/s') indep_var_comp.add_output('alpha', val=0., units = 'deg') indep_var_comp.add_output('Mach_number', val=Mach) 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('cg', val=np.zeros((3)), units='m') # Add this IndepVarComp to the problem model prob.model.add_subsystem('prob_vars', indep_var_comp, promotes=['*']) for surface in surfaces: name = surface['name'] # Create and add a group that handles the geometry for the # aerodynamic lifting surface geom_group = Geometry(surface=surface) prob.model.add_subsystem(name, geom_group) # Connect the mesh from the geometry component to the analysis point prob.model.connect(name + '.mesh', 'aero.' + name + '.def_mesh') # Perform the connections with the modified names within the # 'aero_states' group. prob.model.connect(name + '.mesh', 'aero.aero_states.' + name + '_def_mesh') # Create the aero point group, which contains the actual aerodynamic # analyses point_name = 'aero' aero_group = AeroPoint(surfaces=surfaces) prob.model.add_subsystem(point_name, aero_group, promotes_inputs=['v', 'alpha', 'Mach_number', 're', 'rho', 'cg']) # For trimmed polar, setup balance component if trimmed == True: bal = BalanceComp() bal.add_balance(name='tail_rotation', rhs_val = 0., units = 'deg') prob.model.add_subsystem('balance', bal, promotes_outputs = ['tail_rotation']) prob.model.connect('aero.CM', 'balance.lhs:tail_rotation', src_indices = [1]) prob.model.connect('tail_rotation', 'tail.twist_cp', src_indices = np.zeros((1,5), dtype = int)) # Use Newton Solver # prob.model.nonlinear_solver = NewtonSolver() # prob.model.nonlinear_solver.options['solve_subsystems'] = True # Use Broyden Solver prob.model.nonlinear_solver = BroydenSolver() prob.model.nonlinear_solver.options['state_vars'] = ['tail_rotation'] # prob.model.nonlinear_solver.linesearch = ArmijoGoldsteinLS() prob.model.nonlinear_solver.options['iprint'] = 2 prob.model.nonlinear_solver.options['maxiter'] = 20 prob.model.linear_solver = DirectSolver() prob.setup() #prob['tail_rotation'] = -0.75 prob.run_model() #prob.check_partials(compact_print = True) #prob.model.list_outputs(prom_name = True) prob.model.list_outputs(residuals = True) CLs = [] CDs = [] CMs = [] for a in alphas: prob['alpha'] = a prob.run_model() CLs.append(prob['aero.CL'][0]) CDs.append(prob['aero.CD'][0]) CMs.append(prob['aero.CM'][1]) # Take only the longitudinal CM #print(a, prob['aero.CL'], prob['aero.CD'], prob['aero.CM'][1]) # Plot CL vs alpha and drag polar fig,axes = plt.subplots(nrows=3) axes[0].plot(alphas, CLs) axes[1].plot(alphas, CMs) axes[2].plot(CLs, CDs) fig.savefig('drag_polar.pdf') #plt.show() return CLs, CDs, CMs
def test_brachistochrone_integrated_control_radau_ps(self): import numpy as np from openmdao.api import Problem, Group, ScipyOptimizeDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error from dymos import Phase, Radau p = Problem(model=Group()) p.driver = ScipyOptimizeDriver() phase = Phase(ode_class=BrachistochroneODE, transcription=Radau(num_segments=10)) p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(.5, 10)) phase.set_state_options('x', fix_initial=True, fix_final=True) phase.set_state_options('y', fix_initial=True, fix_final=True) phase.set_state_options('v', fix_initial=True) phase.set_state_options('theta', targets='theta', fix_initial=False) phase.add_control('theta_dot', units='deg/s', rate_continuity=True, lower=0, upper=60) phase.add_design_parameter('g', 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) p.model.linear_solver = 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_rel_error(self, 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_rel_error(self, x_interp(time_sol), x_sol, tolerance=1.0E-5) assert_rel_error(self, y_interp(time_sol), y_sol, tolerance=1.0E-5) assert_rel_error(self, v_interp(time_sol), v_sol, tolerance=1.0E-5) assert_rel_error(self, theta_interp(time_sol), theta_sol, tolerance=1.0E-5) assert_rel_error(self, theta_dot_interp(time_sol), theta_dot_sol, tolerance=1.0E-5)
def test_simple_external_code_implicit_comp(self): from openmdao.api import Group, NewtonSolver, Problem, IndepVarComp, DirectSolver, \ ExternalCodeImplicitComp class MachExternalCodeComp(ExternalCodeImplicitComp): def initialize(self): self.options.declare('super_sonic', types=bool) def setup(self): self.add_input('area_ratio', val=1.0, units=None) self.add_output('mach', val=1., units=None) self.declare_partials(of='mach', wrt='area_ratio', method='fd') self.input_file = 'mach_input.dat' self.output_file = 'mach_output.dat' # providing these are optional; the component will verify that any input # files exist before execution and that the output files exist after. self.options['external_input_files'] = [self.input_file] self.options['external_output_files'] = [self.output_file] self.options['command_apply'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] self.options['command_solve'] = [ 'python', 'extcode_mach.py', self.input_file, self.output_file, ] def apply_nonlinear(self, inputs, outputs, residuals): with open(self.input_file, 'w') as input_file: input_file.write('residuals\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format(outputs['mach'][0])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).apply_nonlinear(inputs, outputs, residuals) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) residuals['mach'] = mach def solve_nonlinear(self, inputs, outputs): with open(self.input_file, 'w') as input_file: input_file.write('outputs\n') input_file.write('{}\n'.format(inputs['area_ratio'][0])) input_file.write('{}\n'.format( self.options['super_sonic'])) # the parent apply_nonlinear function actually runs the external code super(MachExternalCodeComp, self).solve_nonlinear(inputs, outputs) # parse the output file from the external code and set the value of mach with open(self.output_file, 'r') as output_file: mach = float(output_file.read()) outputs['mach'] = mach group = Group() group.add_subsystem('ar', IndepVarComp('area_ratio', 0.5)) mach_comp = group.add_subsystem('comp', MachExternalCodeComp(), promotes=['*']) prob = Problem(model=group) group.nonlinear_solver = NewtonSolver() group.nonlinear_solver.options['solve_subsystems'] = True group.nonlinear_solver.options['iprint'] = 0 group.nonlinear_solver.options['maxiter'] = 20 group.linear_solver = DirectSolver() prob.setup(check=False) area_ratio = 1.3 super_sonic = False prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8) area_ratio = 1.3 super_sonic = True prob['area_ratio'] = area_ratio mach_comp.options['super_sonic'] = super_sonic prob.run_model() assert_rel_error(self, prob['mach'], mach_solve(area_ratio, super_sonic=super_sonic), 1e-8)
def setup(self): nn = self.options['num_nodes'] engine_heat_coeff = self.options['engine_heat_coeff'] pump_heat_coeff = self.options['pump_heat_coeff'] # Aero and mission self.add_subsystem(name='atmos', subsys=StandardAtmosphereGroup(num_nodes=nn), promotes_inputs=['h']) # self.add_subsystem(name='aero', # subsys=AeroGroup(num_nodes=nn), # promotes_inputs=['v', 'alpha', 'S']) self.add_subsystem(name='aero', subsys=AeroSMTGroup(num_nodes=nn), promotes_inputs=['v', 'alpha', 'S', 'h']) self.connect('atmos.sos', 'aero.sos') self.connect('atmos.rho', 'aero.rho') self.add_subsystem(name='prop', subsys=PropGroup(num_nodes=nn), promotes_inputs=['h', 'throttle'], promotes_outputs=['m_dot']) self.connect('aero.mach', 'prop.mach') self.add_subsystem(name='flight_dynamics', subsys=FlightPathEOM2D(num_nodes=nn), promotes_inputs=['m', 'v', 'gam', 'alpha']) self.connect('aero.f_drag', 'flight_dynamics.D') self.connect('aero.f_lift', 'flight_dynamics.L') self.connect('prop.thrust', 'flight_dynamics.T') # Thermal self.add_subsystem( 'm_burn_comp', ExecComp('m_burn = - m_dot', m_burn=np.zeros(nn), m_dot=np.zeros(nn)), promotes=['*'], ) self.add_subsystem( 'm_fuel_comp', ExecComp('m_fuel = m - W0', m_fuel=np.zeros(nn), m=np.zeros(nn), W0=np.zeros(nn)), promotes=['*'], ) self.add_subsystem( 'm_flow_comp', ExecComp('m_flow = m_burn + m_recirculated', m_flow=np.zeros(nn), m_burn=np.zeros(nn), m_recirculated=np.zeros(nn)), promotes=['*'], ) self.add_subsystem(name='pump_heating_comp', subsys=PumpHeatingComp(num_nodes=nn, heat_coeff=pump_heat_coeff), promotes_inputs=['m_flow'], promotes_outputs=['Q_pump']) self.add_subsystem(name='engine_heating_comp', subsys=EngineHeatingComp( num_nodes=nn, heat_coeff=engine_heat_coeff), promotes_inputs=['throttle'], promotes_outputs=['Q_engine']) self.add_subsystem( 'Q_env_tot_comp', ExecComp('Q_env_tot = Q_env + Q_pump + Q_engine', Q_env_tot=np.zeros(nn), Q_env=np.zeros(nn), Q_pump=np.zeros(nn), Q_engine=np.zeros(nn)), promotes=['*'], ) self.add_subsystem(name='cv', subsys=CvComp(num_nodes=nn), promotes_inputs=['T'], promotes_outputs=['Cv']) self.add_subsystem(name='tank', subsys=TankMissionComp(num_nodes=nn), promotes_inputs=[ 'm_fuel', 'm_flow', 'm_burn', 'T', 'Q_env_tot', 'Q_sink', 'Q_out', 'Cv' ], promotes_outputs=['T_dot', 'T_o']) self.add_subsystem(name='power', subsys=PowerComp(num_nodes=nn), promotes=['m_flow', 'power']) # Set solvers self.linear_solver = DirectSolver(assemble_jac=True) self.options['assembled_jac_type'] = 'csc'
def ex_aircraft_steady_flight(optimizer='SLSQP', transcription='gauss-lobatto'): p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.options['dynamic_simul_derivs'] = True if optimizer == 'SNOPT': p.driver.opt_settings['Major iterations limit'] = 1000 p.driver.opt_settings['Major feasibility tolerance'] = 1.0E-6 p.driver.opt_settings['Major optimality tolerance'] = 1.0E-6 p.driver.opt_settings["Linesearch tolerance"] = 0.10 p.driver.opt_settings['iSumm'] = 6 num_seg = 15 seg_ends, _ = lgl(num_seg + 1) phase = Phase(transcription, ode_class=AircraftODE, num_segments=num_seg, segment_ends=seg_ends, transcription_order=5, compressed=False) # Pass Reference Area from an external source assumptions = p.model.add_subsystem('assumptions', 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') p.model.add_subsystem('phase0', phase) phase.set_time_options(initial_bounds=(0, 0), duration_bounds=(300, 10000), duration_ref=3600) phase.set_state_options('range', units='NM', fix_initial=True, fix_final=False, scaler=0.001, defect_scaler=1.0E-2) phase.set_state_options('mass_fuel', units='lbm', fix_initial=True, fix_final=True, upper=1.5E5, lower=0.0, scaler=1.0E-5, defect_scaler=1.0E-1) phase.add_control('alt', units='kft', opt=True, lower=0.0, upper=50.0, rate_param='climb_rate', rate_continuity=True, rate_continuity_scaler=1.0, rate2_continuity=True, rate2_continuity_scaler=1.0, ref=1.0, fix_initial=True, fix_final=True) phase.add_control('mach', units=None, opt=False) phase.add_input_parameter('S', units='m**2') phase.add_input_parameter('mass_empty', units='kg') phase.add_input_parameter('mass_payload', units='kg') phase.add_path_constraint('propulsion.tau', lower=0.01, upper=1.0) phase.add_path_constraint('alt_rate', units='ft/min', lower=-3000, upper=3000, ref=3000) p.model.connect('assumptions.S', 'phase0.input_parameters:S') p.model.connect('assumptions.mass_empty', 'phase0.input_parameters:mass_empty') p.model.connect('assumptions.mass_payload', 'phase0.input_parameters:mass_payload') phase.add_objective('range', loc='final', ref=-1.0) p.model.linear_solver = DirectSolver(assemble_jac=True) p.setup() p['phase0.t_initial'] = 0.0 p['phase0.t_duration'] = 3600.0 p['phase0.states:range'] = phase.interpolate(ys=(0, 1000.0), nodes='state_input') p['phase0.states:mass_fuel'] = phase.interpolate(ys=(30000, 0), nodes='state_input') p['phase0.controls:mach'][:] = 0.8 p['phase0.controls:alt'][:] = 10.0 p['assumptions.S'] = 427.8 p['assumptions.mass_empty'] = 0.15E6 p['assumptions.mass_payload'] = 84.02869 * 400 p.run_driver() exp_out = phase.simulate( times=np.linspace(0, p['phase0.t_duration'], 500), record=True, record_file='test_ex_aircraft_steady_flight_rec.db') plt.plot(phase.get_values('time', nodes='all'), phase.get_values('alt', nodes='all'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('alt'), 'b-') plt.suptitle('altitude vs time') plt.figure() plt.plot(phase.get_values('time', nodes='all'), phase.get_values('alt_rate', nodes='all', units='ft/min'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('alt_rate', units='ft/min'), 'b-') plt.suptitle('altitude rate vs time') plt.figure() plt.plot(phase.get_values('time', nodes='all'), phase.get_values('mass_fuel', nodes='all'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('mass_fuel'), 'b-') plt.suptitle('fuel mass vs time') plt.figure() plt.plot(phase.get_values('time', nodes='all'), phase.get_values('propulsion.dXdt:mass_fuel', nodes='all'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('propulsion.dXdt:mass_fuel'), 'b-') plt.suptitle('fuel mass flow rate vs time') plt.figure() plt.plot(phase.get_values('time', nodes='all'), phase.get_values('mach', nodes='all'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('mach'), 'b-') plt.suptitle('mach vs time') plt.figure() plt.plot(phase.get_values('time', nodes='all'), phase.get_values('mach_rate', nodes='all'), 'ro') plt.plot(exp_out.get_values('time'), exp_out.get_values('mach_rate'), 'b-') plt.suptitle('mach rate vs time') print('time') print(phase.get_values('time', nodes='all').T) print('alt') print(phase.get_values('alt', nodes='all').T) print('alt_rate') print(phase.get_values('alt_rate', nodes='all').T) print('alt_rate2') print(phase.get_values('alt_rate2', nodes='all').T) print('range') print(phase.get_values('range', nodes='all').T) print('flight path angle') print(phase.get_values('gam_comp.gam').T) print('true airspeed') print(phase.get_values('tas_comp.TAS', units='m/s').T) print('coef of lift') print(phase.get_values('aero.CL').T) print('coef of drag') print(phase.get_values('aero.CD').T) print('atmos density') print(phase.get_values('atmos.rho').T) print('alpha') print(phase.get_values('flight_equilibrium.alpha', units='rad').T) print('coef of thrust') print(phase.get_values('flight_equilibrium.CT').T) print('fuel flow rate') print(phase.get_values('propulsion.dXdt:mass_fuel').T) print('max_thrust') print(phase.get_values('propulsion.max_thrust', units='N').T) print('tau') print(phase.get_values('propulsion.tau').T) print('dynamic pressure') print(phase.get_values('q_comp.q', units='Pa').T) print('S') print(phase.get_values('S', units='m**2').T) plt.show() return p
def main(): num_nodes = 1 num_blades = 10 num_radial = 15 num_cp = 6 af_filename = 'airfoils/mh117.dat' chord = 20. theta = 5.0*np.pi/180.0 pitch = 0. # Numbers taken from the Aviary group's study of the RVLT tiltwing # turboelectric concept vehicle. n_props = 4 hub_diameter = 30. # cm prop_diameter = 15*30.48 # 15 ft in cm c0 = np.sqrt(1.4*287.058*300.) # meters/second rho0 = 1.4*98600./(c0*c0) # kg/m^3 omega = 236. # rad/s # Find the thrust per rotor from the vehicle's mass. m_full = 6367 # kg g = 9.81 # m/s**2 thrust_vtol = m_full*g/n_props prob = Problem() comp = IndepVarComp() comp.add_output('rho', val=rho0, shape=num_nodes, units='kg/m**3') comp.add_output('mu', val=1., shape=num_nodes, units='N/m**2*s') comp.add_output('asound', val=c0, shape=num_nodes, units='m/s') comp.add_output('v', val=2., shape=num_nodes, units='m/s') comp.add_output('alpha', val=0., shape=num_nodes, units='rad') comp.add_output('incidence', val=0., shape=num_nodes, units='rad') comp.add_output('precone', val=0., units='deg') comp.add_output('hub_diameter', val=hub_diameter, shape=num_nodes, units='cm') comp.add_output('prop_diameter', val=prop_diameter, shape=num_nodes, units='cm') comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad') comp.add_output('chord_dv', val=chord, shape=num_cp, units='cm') comp.add_output('theta_dv', val=theta, shape=num_cp, units='rad') comp.add_output('thrust_vtol', val=thrust_vtol, shape=num_nodes, units='N') prob.model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = GeometryGroup(num_nodes=num_nodes, num_cp=num_cp, num_radial=num_radial) prob.model.add_subsystem( 'geometry_group', comp, promotes_inputs=['hub_diameter', 'prop_diameter', 'chord_dv', 'theta_dv', 'pitch'], promotes_outputs=['radii', 'dradii', 'chord', 'theta']) balance_group = Group() comp = CCBladeGroup(num_nodes=num_nodes, num_radial=num_radial, num_blades=num_blades, af_filename=af_filename, turbine=False) balance_group.add_subsystem( 'ccblade_group', comp, promotes_inputs=['radii', 'dradii', 'chord', 'theta', 'rho', 'mu', 'asound', 'v', 'precone', 'omega', 'hub_diameter', 'prop_diameter'], promotes_outputs=['thrust', 'torque', 'efficiency']) comp = BalanceComp() comp.add_balance( name='omega', eq_units='N', lhs_name='thrust', rhs_name='thrust_vtol', val=omega, units='rad/s', lower=0.) balance_group.add_subsystem('thrust_balance_comp', comp, promotes=['*']) balance_group.linear_solver = DirectSolver(assemble_jac=True) # balance_group.nonlinear_solver = NewtonSolver(maxiter=20, iprint=2) # balance_group.nonlinear_solver.options['solve_subsystems'] = True # balance_group.nonlinear_solver.options['atol'] = 1e-9 prob.model.add_subsystem('thrust_balance_group', balance_group, promotes=['*']) prob.model.nonlinear_solver = NewtonSolver(maxiter=20, iprint=2) prob.model.nonlinear_solver.options['solve_subsystems'] = True prob.model.nonlinear_solver.options['atol'] = 1e-9 prob.model.nonlinear_solver.linesearch = BoundsEnforceLS() prob.model.nonlinear_solver.linesearch.options['iprint'] = 2 prob.setup() prob.final_setup() # Calculate the induced axial velocity at the rotor for hover, used for # non-diminsionalation. rho = prob.get_val('rho', units='kg/m**3')[0] hub_diameter = prob.get_val('hub_diameter', units='m')[0] prop_diameter = prob.get_val('prop_diameter', units='m')[0] thrust_vtol = prob.get_val('thrust_vtol', units='N')[0] A_rotor = 0.25*np.pi*(prop_diameter**2 - hub_diameter**2) v_h = np.sqrt(thrust_vtol/(2*rho*A_rotor)) # Climb: climb_velocity_nondim = np.linspace(0.1, 2., 10) induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) for vc, vi in np.nditer( [climb_velocity_nondim, induced_velocity_nondim], op_flags=[['readonly'], ['writeonly']]): # Run the model with the requested climb velocity. prob.set_val('v', vc*v_h, units='m/s') print(f"v = {prob.get_val('v', units='m/s')}") prob.run_model() # Calculate the area-weighted average induced velocity at the rotor. # Need the area of each blade section. radii = prob.get_val('radii', units='m') dradii = prob.get_val('dradii', units='m') dArea = 2*np.pi*radii*dradii # Get the induced velocity at the rotor plane for each blade section. Vx = prob.get_val('ccblade_group.Vx', units='m/s') a = prob.get_val('ccblade_group.ccblade_comp.a') # Get the area-weighted average of the induced velocity. vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # Induced velocity from plain old momentum theory (for climb). induced_velocity_mt = ( -0.5*climb_velocity_nondim + np.sqrt((0.5*climb_velocity_nondim)**2 + 1.)) fig, ax = plt.subplots() ax.plot(climb_velocity_nondim, -induced_velocity_nondim, label='CCBlade.jl (climb)') ax.plot(climb_velocity_nondim, induced_velocity_mt, label='Momentum Theory (climb)') # Descent: climb_velocity_nondim = np.linspace(-4., -2.5, 10) induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) for vc, vi in np.nditer( [climb_velocity_nondim, induced_velocity_nondim], op_flags=[['readonly'], ['writeonly']]): # Run the model with the requested climb velocity. prob.set_val('v', vc*v_h, units='m/s') print(f"vc = {vc}, v = {prob.get_val('v', units='m/s')}") prob.run_model() # Calculate the area-weighted average induced velocity at the rotor. # Need the area of each blade section. radii = prob.get_val('radii', units='m') dradii = prob.get_val('dradii', units='m') dArea = 2*np.pi*radii*dradii # Get the induced velocity at the rotor plane for each blade section. Vx = prob.get_val('ccblade_group.Vx', units='m/s') a = prob.get_val('ccblade_group.ccblade_comp.a') # Get the area-weighted average of the induced velocity. vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # Induced velocity from plain old momentum theory (for descent). induced_velocity_mt = ( -0.5*climb_velocity_nondim - np.sqrt((0.5*climb_velocity_nondim)**2 - 1.)) # Plot the induced velocity for descent. ax.plot(climb_velocity_nondim, -induced_velocity_nondim, label='CCBlade.jl (descent)') ax.plot(climb_velocity_nondim, induced_velocity_mt, label='Momentum Theory (descent)') # # Empirical region: # climb_velocity_nondim = np.linspace(-1.9, -1.5, 5) # induced_velocity_nondim = np.zeros_like(climb_velocity_nondim) # for vc, vi in np.nditer( # [climb_velocity_nondim, induced_velocity_nondim], # op_flags=[['readonly'], ['writeonly']]): # # Run the model with the requested climb velocity. # prob.set_val('v', vc*v_h, units='m/s') # print(f"vc = {vc}, v = {prob.get_val('v', units='m/s')}") # prob.run_model() # # Calculate the area-weighted average induced velocity at the rotor. # # Need the area of each blade section. # radii = prob.get_val('radii', # units='m') # dradii = prob.get_val('dradii', # units='m') # dArea = 2*np.pi*radii*dradii # # Get the induced velocity at the rotor plane for each blade section. # Vx = prob.get_val('ccblade_group.Vx', units='m/s') # a = prob.get_val('ccblade_group.ccblade_comp.a') # # Get the area-weighted average of the induced velocity. # vi[...] = np.sum(a*Vx*dArea/A_rotor)/v_h # # Plot the induced velocity for the empirical region. # ax.plot(climb_velocity_nondim, -induced_velocity_nondim, # label='CCBlade.jl (empirical region)') ax.set_xlabel('Vc/vh') ax.set_ylabel('Vi/vh') ax.legend() fig.savefig('induced_velocity.png')
def setup(self): surfaces = self.options['surfaces'] coupled = Group() for surface in surfaces: name = surface['name'] # Connect the output of the loads component with the FEM # displacement parameter. This links the coupling within the coupled # group that necessitates the subgroup solver. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.normals', 'aero_states.' + name + '_normals') coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # coupled.connect(name + '.b_pts', 'aero_states.' + name + '_b_pts') # coupled.connect(name + '.c_pts', 'aero_states.' + name + '_c_pts') # coupled.connect(name + '.cos_sweep', 'aero_states.' + name + '_cos_sweep') # coupled.connect(name + '.widths', 'aero_states.' + name + '_widths') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = CoupledAS(surface=surface) coupled.add_subsystem(name, coupled_AS_group) # TODO: add this info to the options # prob.model.add_options(surface['name'] + 'yield_stress', surface['yield']) # prob.model.add_options(surface['name'] + 'fem_origin', surface['fem_origin']) # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', VLMStates(surfaces=surfaces), promotes_inputs=['v', 'alpha', 'rho']) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Add a loads component to the coupled group coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyIterativeSolver() # coupled.linear_solver.precon = LinearRunOnce() coupled.nonlinear_solver = NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['atol'] = 5e-6 coupled.nonlinear_solver.options['rtol'] = 1e-12 # coupled.jacobian = DenseJacobian() coupled.linear_solver = DirectSolver() # coupled.nonlinear_solver = NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 coupled.nonlinear_solver.options['iprint'] = 0 """ ### End change of solver settings ### """ # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=['v', 'alpha', 'rho']) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem( name + '_perf', perf_group, promotes_inputs=["rho", "v", "alpha", "re", "M"]) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. self.add_subsystem( 'total_perf', TotalPerformance(surfaces=surfaces), promotes_inputs=[ 'CL', 'CD', 'v', 'rho', 'empty_cg', 'total_weight', 'CT', 'a', 'R', 'M', 'W0', 'load_factor' ], promotes_outputs=['L_equals_W', 'fuelburn', 'CM', 'cg'])
def test_csc_masking(self): class CCBladeResidualComp(ImplicitComponent): def initialize(self): self.options.declare('num_nodes', types=int) self.options.declare('num_radial', types=int) def setup(self): num_nodes = self.options['num_nodes'] num_radial = self.options['num_radial'] self.add_input('chord', shape=(1, num_radial)) self.add_input('theta', shape=(1, num_radial)) self.add_output('phi', lower=-0.5 * np.pi, upper=0.0, shape=(num_nodes, num_radial)) self.add_output('Tp', shape=(num_nodes, num_radial)) of_names = ('phi', 'Tp') row_col = np.arange(num_radial) for name in of_names: self.declare_partials(name, 'chord', rows=row_col, cols=row_col) self.declare_partials(name, 'theta', rows=row_col, cols=row_col, val=0.0) self.declare_partials(name, 'phi', rows=row_col, cols=row_col) self.declare_partials('Tp', 'Tp', rows=row_col, cols=row_col, val=1.) def linearize(self, inputs, outputs, partials): partials['phi', 'chord'] = np.array([1., 2, 3, 4]) partials['phi', 'phi'] = np.array([5., 6, 7, 8]) partials['Tp', 'chord'] = np.array([9., 10, 11, 12]) partials['Tp', 'phi'] = np.array([13., 14, 15, 16]) prob = Problem() model = prob.model comp = IndepVarComp() comp.add_output('chord', val=np.ones((4, ))) model.add_subsystem('indep_var_comp', comp, promotes=['*']) comp = CCBladeResidualComp(num_nodes=1, num_radial=4, assembled_jac_type='csc') comp.linear_solver = DirectSolver(assemble_jac=True) model.add_subsystem('ccblade_comp', comp, promotes_inputs=['chord'], promotes_outputs=['Tp']) prob.setup(mode='fwd') prob.run_model() totals = prob.compute_totals(of=['Tp'], wrt=['chord'], return_format='array') expected = np.array([[-6.4, 0., 0., 0.], [0., -5.33333333, 0., 0.], [0., 0., -4.57142857, 0.], [0., 0., 0., -4.]]) np.testing.assert_allclose(totals, expected)
def test_solve_linear_ksp_precon_left(self): """Solve implicit system with PETScKrylov using a preconditioner.""" group = TestImplicitGroup(lnSolverClass=PETScKrylov) precon = group.linear_solver.precon = DirectSolver() group.linear_solver.options['precon_side'] = 'left' group.linear_solver.options['ksp_type'] = 'richardson' p = Problem(group) p.setup(check=False) p.set_solver_print(level=0) # Conclude setup but don't run model. p.final_setup() d_inputs, d_outputs, d_residuals = group.get_linear_vectors() # forward d_residuals.set_const(1.0) d_outputs.set_const(0.0) group.run_linearize() group.run_solve_linear(['linear'], 'fwd') output = d_outputs._data assert_rel_error(self, output, group.expected_solution, 1e-15) # reverse d_outputs.set_const(1.0) d_residuals.set_const(0.0) group.run_linearize() group.run_solve_linear(['linear'], 'rev') output = d_residuals._data assert_rel_error(self, output, group.expected_solution, 3e-15) # test the direct solver and make sure KSP correctly recurses for _linearize precon = group.linear_solver.precon = DirectSolver() group.linear_solver.options['precon_side'] = 'left' group.linear_solver.options['ksp_type'] = 'richardson' p.setup(check=False) # Conclude setup but don't run model. p.final_setup() d_inputs, d_outputs, d_residuals = group.get_linear_vectors() # forward d_residuals.set_const(1.0) d_outputs.set_const(0.0) group.linear_solver._linearize() group.run_solve_linear(['linear'], 'fwd') output = d_outputs._data assert_rel_error(self, output, group.expected_solution, 1e-15) # reverse d_outputs.set_const(1.0) d_residuals.set_const(0.0) group.linear_solver._linearize() group.run_solve_linear(['linear'], 'rev') output = d_residuals._data assert_rel_error(self, output, group.expected_solution, 3e-15)
def test_nested_promotion_errors(self): """ Tests for error-handling for promoted input variable names. """ c1 = IndepVarComp('x') c2 = ExecComp('y=2*x') c3 = ExecComp('z=3*x') g = Group(assembled_jac_type='dense') g.add_subsystem('c2', c2, promotes=['*']) g.add_subsystem('c3', c3, promotes=['*']) g.linear_solver = DirectSolver(assemble_jac=True) model = Group() model.add_subsystem('c1', c1, promotes=['*']) model.add_subsystem('g', g) p = Problem(model) p.setup() # ------------------------------------------------------------------- msg1 = "The promoted name g.x is invalid because it refers to multiple inputs: " \ "[g.c2.x, g.c3.x] that are not connected to an output variable." # inputs (g.x is not connected) # with assertRaisesRegex(self, RuntimeError, msg1.format('g.x')): with self.assertRaises(Exception) as context: p['g.x'] = 5.0 p.final_setup() self.assertEqual(str(context.exception), msg1) # Repeat test for post final_setup when vectors are allocated. p = Problem(model) p.setup() p.final_setup() # ------------------------------------------------------------------- # inputs (g.x is not connected) with self.assertRaises(Exception) as context: p['g.x'] = 5.0 p.final_setup() self.assertEqual(str(context.exception), msg1) # Start from a clean state again p = Problem(model) p.setup() with self.assertRaises(Exception) as context: self.assertEqual(p['g.x'], 5.0) self.assertEqual(str(context.exception), msg1) msg2 = "The promoted name x is invalid because it refers to multiple inputs: " \ "[g.c2.x, g.c3.x] that are not connected to an output variable." jac = g.linear_solver._assembled_jac # d(outputs)/d(inputs) with self.assertRaises(Exception) as context: jac['y', 'x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(jac['y', 'x'], 5.0) self.assertEqual(str(context.exception), msg2) # ------------------------------------------------------------------- # Repeat test for post final_setup when vectors are allocated. p = Problem(model) p.setup() p.final_setup() with self.assertRaises(Exception) as context: self.assertEqual(p['g.x'], 5.0) self.assertEqual(str(context.exception), msg1) # d(outputs)/d(inputs) with self.assertRaises(Exception) as context: jac['y', 'x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(jac['y', 'x'], 5.0) self.assertEqual(str(context.exception), msg2) # ------------------------------------------------------------------- msg1 = "The promoted name g.x is invalid because it refers to multiple inputs: " \ "[g.c2.x ,g.c3.x]. Access the value from the connected output variable x instead." # From here, 'g.x' has a valid source. model.connect('x', 'g.x') p = Problem(model) p.setup() # inputs (g.x is connected to x) p['g.x'] = 5.0 with self.assertRaises(Exception) as context: p.final_setup() self.assertEqual(str(context.exception), msg1) # Repeat test for post final_setup when vectors are allocated. p = Problem(model) p.setup() p.final_setup() # inputs (g.x is connected to x) with self.assertRaises(Exception) as context: p['g.x'] = 5.0 self.assertEqual(str(context.exception), msg1) # Final test, the getitem p = Problem(model) p.setup() with self.assertRaises(Exception) as context: self.assertEqual(p['g.x'], 5.0) self.assertEqual(str(context.exception), msg1) # d(outputs)/d(inputs) with self.assertRaises(Exception) as context: jac['y', 'x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(jac['y', 'x'], 5.0) # Start from a clean state again self.assertEqual(str(context.exception), msg2) # Repeat test for post final_setup when vectors are allocated. p = Problem(model) p.setup() p.final_setup() with self.assertRaises(Exception) as context: self.assertEqual(p['g.x'], 5.0) self.assertEqual(str(context.exception), msg1) # d(outputs)/d(inputs) with self.assertRaises(Exception) as context: jac['y', 'x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(jac['y', 'x'], 5.0) self.assertEqual(str(context.exception), msg2)
def test_with_promotion_errors(self): """ Tests for error-handling for invalid variable names and keys. """ c1 = IndepVarComp('x') c2 = ExecComp('y=2*x') c3 = ExecComp('z=3*x') g = Group(assembled_jac_type='dense') g.add_subsystem('c1', c1, promotes=['*']) g.add_subsystem('c2', c2, promotes=['*']) g.add_subsystem('c3', c3, promotes=['*']) g.linear_solver = DirectSolver(assemble_jac=True) model = Group() model.add_subsystem('g', g, promotes=['*']) p = Problem(model) p.setup() # Conclude setup but don't run model. p.final_setup() # ------------------------------------------------------------------- msg1 = "Group (g): Variable name '{}' not found." msg2 = "The promoted name x is invalid because it refers to multiple inputs: " \ "[g.c2.x ,g.c3.x]. Access the value from the connected output variable x instead." inputs, outputs, residuals = g.get_nonlinear_vectors() # inputs with self.assertRaises(Exception) as context: inputs['x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(inputs['x'], 5.0) self.assertEqual(str(context.exception), msg2) with self.assertRaises(KeyError) as cm: inputs['g.c2.x'] = 5.0 self.assertEqual(cm.exception.args[0], msg1.format('g.c2.x')) with self.assertRaises(KeyError) as cm: inputs['g.c2.x'] self.assertEqual(cm.exception.args[0], msg1.format('g.c2.x')) # outputs with self.assertRaises(KeyError) as cm: outputs['g.c2.y'] = 5.0 self.assertEqual(cm.exception.args[0], msg1.format('g.c2.y')) with self.assertRaises(KeyError) as cm: outputs['g.c2.y'] self.assertEqual(cm.exception.args[0], msg1.format('g.c2.y')) msg1 = r'Variable name pair \("{}", "{}"\) not found.' jac = g.linear_solver._assembled_jac # d(outputs)/d(inputs) with self.assertRaises(Exception) as context: jac['y', 'x'] = 5.0 self.assertEqual(str(context.exception), msg2) with self.assertRaises(Exception) as context: self.assertEqual(jac['y', 'x'], 5.0) self.assertEqual(str(context.exception), msg2)
def two_burn_orbit_raise_problem(transcription='gauss-lobatto', optimizer='SNOPT', transcription_order=3, compressed=True, show_plots=False): traj = Trajectory() p = Problem(model=traj) if optimizer == 'SNOPT': p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = optimizer p.driver.options['dynamic_simul_derivs'] = True 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 = pyOptSparseDriver() p.driver.options['dynamic_simul_derivs'] = True traj.add_design_parameter('c', opt=False, val=1.5, units='DU/TU') # First Phase (burn) burn1 = Phase(transcription, ode_class=FiniteBurnODE, num_segments=10, transcription_order=transcription_order, compressed=compressed) burn1 = traj.add_phase('burn1', burn1) burn1.set_time_options(fix_initial=True, duration_bounds=(.5, 10)) burn1.set_state_options('r', fix_initial=True, fix_final=False, defect_scaler=100.0) burn1.set_state_options('theta', fix_initial=True, fix_final=False, defect_scaler=100.0) burn1.set_state_options('vr', fix_initial=True, fix_final=False, defect_scaler=100.0) burn1.set_state_options('vt', fix_initial=True, fix_final=False, defect_scaler=100.0) burn1.set_state_options('accel', fix_initial=True, fix_final=False) burn1.set_state_options('deltav', fix_initial=True, fix_final=False) burn1.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, rate_continuity_scaler=0.001, rate2_continuity_scaler=0.001, lower=-30, upper=30) # Second Phase (Coast) coast = Phase(transcription, ode_class=FiniteBurnODE, num_segments=10, transcription_order=transcription_order, compressed=compressed) traj.add_phase('coast', coast) coast.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 10), duration_ref=10) coast.set_state_options('r', fix_initial=False, fix_final=False, defect_scaler=100.0) coast.set_state_options('theta', fix_initial=False, fix_final=False, defect_scaler=100.0) coast.set_state_options('vr', fix_initial=False, fix_final=False, defect_scaler=100.0) coast.set_state_options('vt', fix_initial=False, fix_final=False, defect_scaler=100.0) coast.set_state_options('accel', fix_initial=True, fix_final=True) coast.set_state_options('deltav', fix_initial=False, fix_final=False) coast.add_control('u1', opt=False, val=0.0, units='deg') # Third Phase (burn) burn2 = Phase(transcription, ode_class=FiniteBurnODE, num_segments=10, transcription_order=transcription_order, compressed=compressed) traj.add_phase('burn2', burn2) burn2.set_time_options(initial_bounds=(0.5, 20), duration_bounds=(.5, 10), initial_ref=10) burn2.set_state_options('r', fix_initial=False, fix_final=True, defect_scaler=100.0) burn2.set_state_options('theta', fix_initial=False, fix_final=False, defect_scaler=100.0) burn2.set_state_options('vr', fix_initial=False, fix_final=True, defect_scaler=100.0) burn2.set_state_options('vt', fix_initial=False, fix_final=True, defect_scaler=100.0) burn2.set_state_options('accel', fix_initial=False, fix_final=False, defect_scaler=1.0) burn2.set_state_options('deltav', fix_initial=False, fix_final=False, defect_scaler=1.0) burn2.add_control('u1', rate_continuity=True, rate2_continuity=True, units='deg', scaler=0.01, rate_continuity_scaler=0.001, rate2_continuity_scaler=0.001, lower=-10, upper=10) burn2.add_objective('deltav', loc='final', scaler=100.0) # Link Phases traj.link_phases(phases=['burn1', 'coast', 'burn2'], vars=['time', 'r', 'theta', 'vr', 'vt', 'deltav']) traj.link_phases(phases=['burn1', 'burn2'], vars=['accel']) # Finish Problem Setup p.model.options['assembled_jac_type'] = 'csc' p.model.linear_solver = DirectSolver(assemble_jac=True) p.driver.add_recorder(SqliteRecorder('two_burn_orbit_raise_example.db')) p.setup(check=True) # Set Initial Guesses p.set_val('design_parameters:c', value=1.5) p.set_val('burn1.t_initial', value=0.0) p.set_val('burn1.t_duration', value=2.25) p.set_val('burn1.states:r', value=burn1.interpolate(ys=[1, 1.5], nodes='state_input')) p.set_val('burn1.states:theta', value=burn1.interpolate(ys=[0, 1.7], nodes='state_input')) p.set_val('burn1.states:vr', value=burn1.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('burn1.states:vt', value=burn1.interpolate(ys=[1, 1], nodes='state_input')) p.set_val('burn1.states:accel', value=burn1.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val( 'burn1.states:deltav', value=burn1.interpolate(ys=[0, 0.1], nodes='state_input'), ) p.set_val('burn1.controls:u1', value=burn1.interpolate(ys=[-3.5, 13.0], nodes='control_input')) p.set_val('coast.t_initial', value=2.25) p.set_val('coast.t_duration', value=3.0) p.set_val('coast.states:r', value=coast.interpolate(ys=[1.3, 1.5], nodes='state_input')) p.set_val('coast.states:theta', value=coast.interpolate(ys=[2.1767, 1.7], nodes='state_input')) p.set_val('coast.states:vr', value=coast.interpolate(ys=[0.3285, 0], nodes='state_input')) p.set_val('coast.states:vt', value=coast.interpolate(ys=[0.97, 1], nodes='state_input')) p.set_val('coast.states:accel', value=coast.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('coast.controls:u1', value=coast.interpolate(ys=[0, 0], nodes='control_input')) p.set_val('burn2.t_initial', value=5.25) p.set_val('burn2.t_duration', value=1.75) p.set_val('burn2.states:r', value=burn2.interpolate(ys=[1, 3], nodes='state_input')) p.set_val('burn2.states:theta', value=burn2.interpolate(ys=[0, 4.0], nodes='state_input')) p.set_val('burn2.states:vr', value=burn2.interpolate(ys=[0, 0], nodes='state_input')) p.set_val('burn2.states:vt', value=burn2.interpolate(ys=[1, np.sqrt(1 / 3)], nodes='state_input')) p.set_val('burn2.states:accel', value=burn2.interpolate(ys=[0.1, 0], nodes='state_input')) p.set_val('burn2.states:deltav', value=burn2.interpolate(ys=[0.1, 0.2], nodes='state_input')) p.set_val('burn2.controls:u1', value=burn2.interpolate(ys=[1, 1], nodes='control_input')) p.run_driver() # Plot results exp_out = traj.simulate(times=50, num_procs=3) fig = plt.figure(figsize=(8, 4)) fig.suptitle('Two Burn Orbit Raise Solution') ax_u1 = plt.subplot2grid((2, 2), (0, 0)) ax_deltav = plt.subplot2grid((2, 2), (1, 0)) ax_xy = plt.subplot2grid((2, 2), (0, 1), rowspan=2) span = np.linspace(0, 2 * np.pi, 100) ax_xy.plot(np.cos(span), np.sin(span), 'k--', lw=1) ax_xy.plot(3 * np.cos(span), 3 * np.sin(span), 'k--', lw=1) ax_xy.set_xlim(-4.5, 4.5) ax_xy.set_ylim(-4.5, 4.5) ax_xy.set_xlabel('x ($R_e$)') ax_xy.set_ylabel('y ($R_e$)') ax_u1.set_xlabel('time ($TU$)') ax_u1.set_ylabel('$u_1$ ($deg$)') ax_u1.grid(True) ax_deltav.set_xlabel('time ($TU$)') ax_deltav.set_ylabel('${\Delta}v$ ($DU/TU$)') ax_deltav.grid(True) t_sol = traj.get_values('time', flat=True) x_sol = traj.get_values('pos_x', flat=True) y_sol = traj.get_values('pos_y', flat=True) dv_sol = traj.get_values('deltav', flat=True) u1_sol = traj.get_values('u1', units='deg', flat=True) t_exp = exp_out.get_values('time', flat=True) x_exp = exp_out.get_values('pos_x', flat=True) y_exp = exp_out.get_values('pos_y', flat=True) dv_exp = exp_out.get_values('deltav', flat=True) u1_exp = exp_out.get_values('u1', units='deg', flat=True) ax_u1.plot(t_sol, u1_sol, 'ro', ms=3) ax_u1.plot(t_exp, u1_exp, 'b-') ax_deltav.plot(t_sol, dv_sol, 'ro', ms=3) ax_deltav.plot(t_exp, dv_exp, 'b-') ax_xy.plot(x_sol, y_sol, 'ro', ms=3, label='implicit') ax_xy.plot(x_exp, y_exp, 'b-', label='explicit') if show_plots: plt.show() return p
def setup(self): surfaces = self.options['surfaces'] rotational = self.options['rotational'] coupled = Group() for surface in surfaces: name = surface['name'] # Connect the output of the loads component with the FEM # displacement parameter. This links the coupling within the coupled # group that necessitates the subgroup solver. coupled.connect(name + '_loads.loads', name + '.loads') # Perform the connections with the modified names within the # 'aero_states' group. coupled.connect(name + '.normals', 'aero_states.' + name + '_normals') coupled.connect(name + '.def_mesh', 'aero_states.' + name + '_def_mesh') # Connect the results from 'coupled' to the performance groups coupled.connect(name + '.def_mesh', name + '_loads.def_mesh') coupled.connect('aero_states.' + name + '_sec_forces', name + '_loads.sec_forces') # Connect the results from 'aero_states' to the performance groups self.connect('coupled.aero_states.' + name + '_sec_forces', name + '_perf' + '.sec_forces') # Connection performance functional variables self.connect(name + '_perf.CL', 'total_perf.' + name + '_CL') self.connect(name + '_perf.CD', 'total_perf.' + name + '_CD') self.connect('coupled.aero_states.' + name + '_sec_forces', 'total_perf.' + name + '_sec_forces') self.connect('coupled.' + name + '.chords', name + '_perf.aero_funcs.chords') # Connect parameters from the 'coupled' group to the performance # groups for the individual surfaces. self.connect('coupled.' + name + '.disp', name + '_perf.disp') self.connect('coupled.' + name + '.S_ref', name + '_perf.S_ref') self.connect('coupled.' + name + '.widths', name + '_perf.widths') # self.connect('coupled.' + name + '.chords', name + '_perf.chords') self.connect('coupled.' + name + '.lengths', name + '_perf.lengths') self.connect('coupled.' + name + '.cos_sweep', name + '_perf.cos_sweep') # Connect parameters from the 'coupled' group to the total performance group. self.connect('coupled.' + name + '.S_ref', 'total_perf.' + name + '_S_ref') self.connect('coupled.' + name + '.widths', 'total_perf.' + name + '_widths') self.connect('coupled.' + name + '.chords', 'total_perf.' + name + '_chords') self.connect('coupled.' + name + '.b_pts', 'total_perf.' + name + '_b_pts') # Add components to the 'coupled' group for each surface. # The 'coupled' group must contain all components and parameters # needed to converge the aerostructural system. coupled_AS_group = CoupledAS(surface=surface) if surface['distributed_fuel_weight'] or 'n_point_masses' in surface.keys() or surface['struct_weight_relief']: prom_in = ['load_factor'] else: prom_in = [] coupled.add_subsystem(name, coupled_AS_group, promotes_inputs=prom_in) if self.options['compressible'] == True: aero_states = CompressibleVLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho', 'Mach_number'] else: aero_states = VLMStates(surfaces=surfaces, rotational=rotational) prom_in = ['v', 'alpha', 'beta', 'rho'] # Add a single 'aero_states' component for the whole system within the # coupled group. coupled.add_subsystem('aero_states', aero_states, promotes_inputs=prom_in) # Explicitly connect parameters from each surface's group and the common # 'aero_states' group. for surface in surfaces: name = surface['name'] # Add a loads component to the coupled group coupled.add_subsystem(name + '_loads', LoadTransfer(surface=surface)) """ ### Change the solver settings here ### """ # Set solver properties for the coupled group # coupled.linear_solver = ScipyKrylov() # coupled.linear_solver.precon = LinearRunOnce() coupled.nonlinear_solver = NonlinearBlockGS(use_aitken=True) coupled.nonlinear_solver.options['maxiter'] = 100 coupled.nonlinear_solver.options['atol'] = 1e-7 coupled.nonlinear_solver.options['rtol'] = 1e-30 coupled.nonlinear_solver.options['iprint'] = 2 coupled.nonlinear_solver.options['err_on_maxiter'] = True # coupled.linear_solver = DirectSolver() coupled.linear_solver = DirectSolver(assemble_jac=True) coupled.options['assembled_jac_type'] = 'csc' # coupled.nonlinear_solver = NewtonSolver(solve_subsystems=True) # coupled.nonlinear_solver.options['maxiter'] = 50 """ ### End change of solver settings ### """ prom_in = ['v', 'alpha', 'rho'] if self.options['compressible'] == True: prom_in.append('Mach_number') # Add the coupled group to the model problem self.add_subsystem('coupled', coupled, promotes_inputs=prom_in) for surface in surfaces: name = surface['name'] # Add a performance group which evaluates the data after solving # the coupled system perf_group = CoupledPerformance(surface=surface) self.add_subsystem(name + '_perf', perf_group, promotes_inputs=['rho', 'v', 'alpha', 're', 'Mach_number']) # Add functionals to evaluate performance of the system. # Note that only the interesting results are promoted here; not all # of the parameters. self.add_subsystem('total_perf', TotalPerformance(surfaces=surfaces, user_specified_Sref=self.options['user_specified_Sref'], internally_connect_fuelburn=self.options['internally_connect_fuelburn']), promotes_inputs=['v', 'rho', 'empty_cg', 'total_weight', 'CT', 'speed_of_sound', 'R', 'Mach_number', 'W0', 'load_factor', 'S_ref_total'], promotes_outputs=['L_equals_W', 'fuelburn', 'CL', 'CD', 'CM', 'cg'])
def test_double_integrator_for_docs(self): import matplotlib.pyplot as plt from openmdao.api import Problem, Group, pyOptSparseDriver, DirectSolver from openmdao.utils.assert_utils import assert_rel_error import dymos as dm from dymos.examples.plotting import plot_results from dymos.examples.double_integrator.double_integrator_ode import DoubleIntegratorODE # Initialize the problem and assign the driver p = Problem(model=Group()) p.driver = pyOptSparseDriver() p.driver.options['optimizer'] = 'SLSQP' p.driver.options['dynamic_simul_derivs'] = True # Setup the trajectory and its phase traj = p.model.add_subsystem('traj', dm.Trajectory()) transcription = dm.Radau(num_segments=30, order=3, compressed=False) phase = traj.add_phase( 'phase0', dm.Phase(ode_class=DoubleIntegratorODE, transcription=transcription)) # # Set the options for our variables. # phase.set_time_options(fix_initial=True, fix_duration=True, units='s') phase.set_state_options('x', fix_initial=True, rate_source='v', units='m') phase.set_state_options('v', fix_initial=True, fix_final=True, rate_source='u', units='m/s') phase.add_control('u', units='m/s**2', scaler=0.01, continuity=False, rate_continuity=False, rate2_continuity=False, lower=-1.0, upper=1.0) # # Maximize distance travelled. # phase.add_objective('x', loc='final', scaler=-1) p.model.linear_solver = DirectSolver() # # Setup the problem and set our initial values. # p.setup(check=True) p['traj.phase0.t_initial'] = 0.0 p['traj.phase0.t_duration'] = 1.0 p['traj.phase0.states:x'] = phase.interpolate(ys=[0, 0.25], nodes='state_input') p['traj.phase0.states:v'] = phase.interpolate(ys=[0, 0], nodes='state_input') p['traj.phase0.controls:u'] = phase.interpolate(ys=[1, -1], nodes='control_input') # # Solve the problem. # p.run_driver() # # Verify that the results are correct. # x = p.get_val('traj.phase0.timeseries.states:x') v = p.get_val('traj.phase0.timeseries.states:v') assert_rel_error(self, x[0], 0.0, tolerance=1.0E-4) assert_rel_error(self, x[-1], 0.25, tolerance=1.0E-4) assert_rel_error(self, v[0], 0.0, tolerance=1.0E-4) assert_rel_error(self, v[-1], 0.0, tolerance=1.0E-4) # # Simulate the explicit solution and plot the results. # exp_out = traj.simulate() plot_results( [('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:x', 'time (s)', 'x $(m)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.states:v', 'time (s)', 'v $(m/s)$'), ('traj.phase0.timeseries.time', 'traj.phase0.timeseries.controls:u', 'time (s)', 'u $(m/s^2)$')], title='Double Integrator Solution\nRadau Pseudospectral Method', p_sol=p, p_sim=exp_out) plt.show()
def test_assembled_jacobian_unsupported_cases(self): class ParaboloidApply(ImplicitComponent): def setup(self): self.add_input('x', val=0.0) self.add_input('y', val=0.0) self.add_output('f_xy', val=0.0) def linearize(self, inputs, outputs, jacobian): return def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): d_residuals['x'] += ( np.exp(outputs['x']) - 2 * inputs['a']**2 * outputs['x']) * d_outputs['x'] d_residuals['x'] += (-2 * inputs['a'] * outputs['x']**2) * d_inputs['a'] # One level deep prob = Problem() model = prob.model = Group(assembled_jac_type='dense') model.linear_solver = DirectSolver(assemble_jac=True) model.add_subsystem('p1', IndepVarComp('x', val=1.0)) model.add_subsystem('p2', IndepVarComp('y', val=1.0)) model.add_subsystem('comp', ParaboloidApply()) model.connect('p1.x', 'comp.x') model.connect('p2.y', 'comp.y') prob.setup() msg = "AssembledJacobian not supported for matrix-free subcomponent." with assertRaisesRegex(self, Exception, msg): prob.run_model() # Nested prob = Problem() model = prob.model = Group(assembled_jac_type='dense') model.linear_solver = DirectSolver(assemble_jac=True) sub = model.add_subsystem('sub', Group()) model.add_subsystem('p1', IndepVarComp('x', val=1.0)) model.add_subsystem('p2', IndepVarComp('y', val=1.0)) sub.add_subsystem('comp', ParaboloidApply()) model.connect('p1.x', 'sub.comp.x') model.connect('p2.y', 'sub.comp.y') prob.setup() msg = "AssembledJacobian not supported for matrix-free subcomponent." with assertRaisesRegex(self, Exception, msg): prob.run_model() # Try a component that is derived from a matrix-free one class FurtherDerived(ParaboloidApply): def do_nothing(self): pass prob = Problem() model = prob.model = Group(assembled_jac_type='dense') model.linear_solver = DirectSolver(assemble_jac=True) model.add_subsystem('p1', IndepVarComp('x', val=1.0)) model.add_subsystem('p2', IndepVarComp('y', val=1.0)) model.add_subsystem('comp', FurtherDerived()) model.connect('p1.x', 'comp.x') model.connect('p2.y', 'comp.y') prob.setup() msg = "AssembledJacobian not supported for matrix-free subcomponent." with assertRaisesRegex(self, Exception, msg): prob.run_model() # Make sure regular comps don't give an error. prob = Problem() model = prob.model = Group(assembled_jac_type='dense') model.linear_solver = DirectSolver(assemble_jac=True) model.add_subsystem('p1', IndepVarComp('x', val=1.0)) model.add_subsystem('p2', IndepVarComp('y', val=1.0)) model.add_subsystem('comp', Paraboloid()) model.connect('p1.x', 'comp.x') model.connect('p2.y', 'comp.y') prob.setup() prob.final_setup() class ParaboloidJacVec(Paraboloid): def linearize(self, inputs, outputs, jacobian): return def compute_jacvec_product(self, inputs, d_inputs, d_outputs, d_residuals, mode): d_residuals['x'] += ( np.exp(outputs['x']) - 2 * inputs['a']**2 * outputs['x']) * d_outputs['x'] d_residuals['x'] += (-2 * inputs['a'] * outputs['x']**2) * d_inputs['a'] # One level deep prob = Problem() model = prob.model = Group(assembled_jac_type='dense') model.linear_solver = DirectSolver(assemble_jac=True) model.add_subsystem('p1', IndepVarComp('x', val=1.0)) model.add_subsystem('p2', IndepVarComp('y', val=1.0)) model.add_subsystem('comp', ParaboloidJacVec()) model.connect('p1.x', 'comp.x') model.connect('p2.y', 'comp.y') prob.setup() msg = "AssembledJacobian not supported for matrix-free subcomponent." with assertRaisesRegex(self, Exception, msg): prob.run_model()
def test_solve_subsystems_internals(self): # Here we test that this feature is doing what it should do by counting the # number of calls in various places. class CountNewton(NewtonSolver): """ This version of Newton also counts how many times it runs in total.""" def __init__(self, **kwargs): super(CountNewton, self).__init__(**kwargs) self.total_count = 0 def _iter_execute(self): super(CountNewton, self)._iter_execute() self.total_count += 1 class CountDS(DirectSolver): """ This version of Newton also counts how many times it linearizes""" def __init__(self, **kwargs): super(DirectSolver, self).__init__(**kwargs) self.lin_count = 0 def _linearize(self): super(CountDS, self)._linearize() self.lin_count += 1 prob = Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = NewtonSolver() model.linear_solver = ScipyKrylov() # Enfore behavior: max_sub_solves = 0 means we run once during init model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 0 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 2) self.assertEqual(g2.nonlinear_solver.total_count, 2) self.assertEqual(g1.linear_solver.lin_count, 2) prob = Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = NewtonSolver() model.linear_solver = ScipyKrylov() # Enforce Behavior: baseline model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 5 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 5) self.assertEqual(g2.nonlinear_solver.total_count, 5) self.assertEqual(g1.linear_solver.lin_count, 5) prob = Problem(model=DoubleSellar()) model = prob.model # each SubSellar group converges itself g1 = model.g1 g1.nonlinear_solver = CountNewton() g1.nonlinear_solver.options['rtol'] = 1.0e-5 g1.linear_solver = CountDS() # used for derivatives g2 = model.g2 g2.nonlinear_solver = CountNewton() g2.nonlinear_solver.options['rtol'] = 1.0e-5 g2.linear_solver = DirectSolver() # Converge the outer loop with Gauss Seidel, with a looser tolerance. model.nonlinear_solver = NewtonSolver() model.linear_solver = ScipyKrylov() # Enfore behavior: max_sub_solves = 1 means we run during init and first iteration of iter_execute model.nonlinear_solver.options['maxiter'] = 5 model.nonlinear_solver.options['solve_subsystems'] = True model.nonlinear_solver.options['max_sub_solves'] = 1 prob.set_solver_print(level=0) prob.setup() prob.run_model() # Verifying subsolvers ran self.assertEqual(g1.nonlinear_solver.total_count, 4) self.assertEqual(g2.nonlinear_solver.total_count, 4) self.assertEqual(g1.linear_solver.lin_count, 4)