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()
Example #3
0
    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)
Example #4
0
    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
Example #5
0
    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']
Example #6
0
    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)
Example #7
0
    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"])
Example #8
0
    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)
Example #9
0
    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),
                )
Example #10
0
    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
Example #13
0
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()
Example #15
0
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
Example #16
0
    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'
Example #19
0
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
Example #20
0
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'])
Example #22
0
    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)
Example #23
0
    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)
Example #25
0
    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)
Example #26
0
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'])
Example #28
0
    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()
Example #29
0
    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()
Example #30
0
    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)