Beispiel #1
0
    def test_betz_derivatives(self):
        import openmdao.api as om

        from openmdao.test_suite.test_examples.test_betz_limit import ActuatorDisc

        prob = om.Problem()

        prob.model.add_subsystem('a_disk', ActuatorDisc())

        prob.setup()
        prob.check_partials(compact_print=True)
def problem_init():
    prob = Problem()
    indeps = prob.model.add_subsystem("indeps", IndepVarComp(), promotes=["*"])
    indeps.add_output("a", 0.5)
    indeps.add_output("Area", 10.0, units="m**2")
    indeps.add_output("rho", 1.225, units="kg/m**3")
    indeps.add_output("Vu", 10.0, units="m/s")

    prob.model.add_subsystem("a_disk", ActuatorDisc(), promotes=["*"])

    # setup the optimization
    prob.driver = ScipyOptimizeDriver()
    prob.driver.options["optimizer"] = "SLSQP"

    prob.model.add_design_var("a", lower=0.0, upper=1.0)
    prob.model.add_design_var("Area", lower=0.0, upper=1.0)

    # negative one so we maximize the objective
    prob.model.add_objective("Cp", scaler=-1)

    prob.setup()
    prob.final_setup()
    return prob
    def test_model_viewer_has_correct_data_from_optimization_problem(self):
        """
        Verify that the correct model structure data exists when stored as compared
        to the expected structure, using the ActuatorDisc model.
        """

        # build the model
        prob = Problem()
        indeps = prob.model.add_subsystem('indeps',
                                          IndepVarComp(),
                                          promotes=['*'])
        indeps.add_output('a', .5)
        indeps.add_output('Area', 10.0, units='m**2')
        indeps.add_output('rho', 1.225, units='kg/m**3')
        indeps.add_output('Vu', 10.0, units='m/s')

        prob.model.add_subsystem('a_disk',
                                 ActuatorDisc(),
                                 promotes_inputs=['a', 'Area', 'rho', 'Vu'])

        # setup the optimization
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['optimizer'] = 'SLSQP'

        prob.model.add_design_var('a', lower=0., upper=1.)
        prob.model.add_design_var('Area', lower=0., upper=1.)

        # negative one so we maximize the objective
        prob.model.add_objective('a_disk.Cp', scaler=-1)

        prob.setup()
        prob.final_setup()

        model_viewer_data = _get_viewer_data(prob)

        expected_tree_betz = json.loads("""
                    {"name": "root", "type": "root", "class": "Group", "component_type": null,
                     "subsystem_type": "group", "is_parallel": false, "linear_solver": "LN: RUNONCE",
                     "nonlinear_solver": "NL: RUNONCE", "children": [
                        {"name": "indeps", "type": "subsystem", "class": "IndepVarComp",
                         "subsystem_type": "component", "is_parallel": false, "component_type": "indep",
                         "linear_solver": "", "nonlinear_solver": "", "children": [
                            {"name": "a", "type": "unknown", "implicit": false, "dtype": "ndarray"},
                            {"name": "Area", "type": "unknown", "implicit": false, "dtype": "ndarray"},
                            {"name": "rho", "type": "unknown", "implicit": false, "dtype": "ndarray"},
                            {"name": "Vu", "type": "unknown", "implicit": false, "dtype": "ndarray"}]},
                        {"name": "a_disk", "type": "subsystem", "class": "ActuatorDisc",
                         "subsystem_type": "component", "is_parallel": false, "component_type": "explicit",
                         "linear_solver": "", "nonlinear_solver": "",
                         "children": [{"name": "a", "type": "param", "dtype": "ndarray"},
                                      {"name": "Area", "type": "param", "dtype": "ndarray"},
                                      {"name": "rho", "type": "param", "dtype": "ndarray"},
                                      {"name": "Vu", "type": "param", "dtype": "ndarray"},
                                      {"name": "Vr", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"},
                                      {"name": "Vd", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"},
                                      {"name": "Ct", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"},
                                      {"name": "thrust", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"},
                                      {"name": "Cp", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"},
                                      {"name": "power", "type": "unknown", "implicit": false,
                                       "dtype": "ndarray"}]}]}
                    """)
        expected_pathnames_betz = json.loads('[]')
        expected_conns_betz = json.loads("""
                [{"src": "indeps.a", "tgt": "a_disk.a"}, {"src": "indeps.Area", "tgt": "a_disk.Area"},
                 {"src": "indeps.rho", "tgt": "a_disk.rho"}, {"src": "indeps.Vu", "tgt": "a_disk.Vu"}]
                """)
        expected_abs2prom_betz = json.loads("""
                    {"input": {"a_disk.a": "a", "a_disk.Area": "Area", "a_disk.rho": "rho", "a_disk.Vu": "Vu"},
                    "output": {"indeps.a": "a", "indeps.Area": "Area", "indeps.rho": "rho", "indeps.Vu": "Vu",
                    "a_disk.Vr": "a_disk.Vr", "a_disk.Vd": "a_disk.Vd", "a_disk.Ct": "a_disk.Ct",
                    "a_disk.thrust": "a_disk.thrust", "a_disk.Cp": "a_disk.Cp",
                    "a_disk.power": "a_disk.power"}}
                    """)
        expected_declare_partials_betz = json.loads("""
                ["indeps.a > indeps.a", "indeps.Area > indeps.Area", "indeps.rho > indeps.rho", "indeps.Vu > indeps.Vu", "a_disk.Vr > a_disk.a", "a_disk.Vr > a_disk.Vu", "a_disk.Vd > a_disk.a", "a_disk.Ct > a_disk.a", "a_disk.thrust > a_disk.a", "a_disk.thrust > a_disk.Area", "a_disk.thrust > a_disk.rho", "a_disk.thrust > a_disk.Vu", "a_disk.Cp > a_disk.a", "a_disk.power > a_disk.a", "a_disk.power > a_disk.Area", "a_disk.power > a_disk.rho", "a_disk.power > a_disk.Vu", "a_disk.Vr > a_disk.Vr", "a_disk.Vd > a_disk.Vd", "a_disk.Ct > a_disk.Ct", "a_disk.thrust > a_disk.thrust", "a_disk.Cp > a_disk.Cp", "a_disk.power > a_disk.power"]
        """)

        expected_driver_name = 'ScipyOptimizeDriver'
        expected_design_vars_names = ['indeps.a', 'indeps.Area']
        expected_responses_names = [
            'a_disk.Cp',
        ]

        # check expected model tree
        self.check_model_viewer_data(
            model_viewer_data,
            expected_tree_betz,
            expected_pathnames_betz,
            expected_conns_betz,
            expected_abs2prom_betz,
            expected_declare_partials_betz,
            expected_driver_name,
            expected_design_vars_names,
            expected_responses_names,
        )
    def test_betz(self):
        from openmdao.api import Problem, ScipyOptimizeDriver, IndepVarComp, ExplicitComponent

        class ActuatorDisc(ExplicitComponent):
            """Simple wind turbine model based on actuator disc theory"""

            def setup(self):

                # Inputs
                self.add_input('a', 0.5, desc="Induced Velocity Factor")
                self.add_input('Area', 10.0, units="m**2", desc="Rotor disc area")
                self.add_input('rho', 1.225, units="kg/m**3", desc="air density")
                self.add_input('Vu', 10.0, units="m/s", desc="Freestream air velocity, upstream of rotor")

                # Outputs
                self.add_output('Vr', 0.0, units="m/s",
                                desc="Air velocity at rotor exit plane")
                self.add_output('Vd', 0.0, units="m/s",
                                desc="Slipstream air velocity, downstream of rotor")
                self.add_output('Ct', 0.0, desc="Thrust Coefficient")
                self.add_output('thrust', 0.0, units="N",
                                desc="Thrust produced by the rotor")
                self.add_output('Cp', 0.0, desc="Power Coefficient")
                self.add_output('power', 0.0, units="W", desc="Power produced by the rotor")

                self.declare_partials('Vr', ['a', 'Vu'])
                self.declare_partials('Vd', 'a')
                self.declare_partials('Ct', 'a')
                self.declare_partials('thrust', ['a', 'Area', 'rho', 'Vu'])
                self.declare_partials('Cp', 'a')
                self.declare_partials('power', ['a', 'Area', 'rho', 'Vu'])

            def compute(self, inputs, outputs):
                """ Considering the entire rotor as a single disc that extracts
                velocity uniformly from the incoming flow and converts it to
                power."""

                a = inputs['a']
                Vu = inputs['Vu']

                qA = .5 * inputs['rho'] * inputs['Area'] * Vu ** 2

                outputs['Vd'] = Vd = Vu * (1 - 2 * a)
                outputs['Vr'] = .5 * (Vu + Vd)

                outputs['Ct'] = Ct = 4 * a * (1 - a)
                outputs['thrust'] = Ct * qA

                outputs['Cp'] = Cp = Ct * (1 - a)
                outputs['power'] = Cp * qA * Vu

            def compute_partials(self, inputs, J):
                """ Jacobian of partial derivatives."""

                a = inputs['a']
                Vu = inputs['Vu']
                Area = inputs['Area']
                rho = inputs['rho']

                # pre-compute commonly needed quantities
                a_times_area = a * Area
                one_minus_a = 1.0 - a
                a_area_rho_vu = a_times_area * rho * Vu

                J['Vr', 'a'] = -Vu
                J['Vr', 'Vu'] = one_minus_a

                J['Vd', 'a'] = -2.0 * Vu

                J['Ct', 'a'] = 4.0 - 8.0 * a

                J['thrust', 'a'] = .5 * rho * Vu**2 * Area * J['Ct', 'a']
                J['thrust', 'Area'] = 2.0 * Vu**2 * a * rho * one_minus_a
                J['thrust', 'rho'] = 2.0 * a_times_area * Vu ** 2 * (one_minus_a)
                J['thrust', 'Vu'] = 4.0 * a_area_rho_vu * (one_minus_a)

                J['Cp', 'a'] = 4.0 * a * (2.0 * a - 2.0) + 4.0 * (one_minus_a)**2

                J['power', 'a'] = 2.0 * Area * Vu**3 * a * rho * (
                2.0 * a - 2.0) + 2.0 * Area * Vu**3 * rho * one_minus_a ** 2
                J['power', 'Area'] = 2.0 * Vu**3 * a * rho * one_minus_a ** 2
                J['power', 'rho'] = 2.0 * a_times_area * Vu ** 3 * (one_minus_a)**2
                J['power', 'Vu'] = 6.0 * Area * Vu**2 * a * rho * one_minus_a**2


        # build the model
        prob = Problem()
        indeps = prob.model.add_subsystem('indeps', IndepVarComp(), promotes=['*'])
        indeps.add_output('a', .5)
        indeps.add_output('Area', 10.0, units='m**2')
        indeps.add_output('rho', 1.225, units='kg/m**3')
        indeps.add_output('Vu', 10.0, units='m/s')

        prob.model.add_subsystem('a_disk', ActuatorDisc(),
                                promotes_inputs=['a', 'Area', 'rho', 'Vu'])

        # setup the optimization
        prob.driver = ScipyOptimizeDriver()
        prob.driver.options['optimizer'] = 'SLSQP'

        prob.model.add_design_var('a', lower=0., upper=1.)
        prob.model.add_design_var('Area', lower=0., upper=1.)
        # negative one so we maximize the objective
        prob.model.add_objective('a_disk.Cp', scaler=-1)

        prob.setup()
        prob.run_driver()

        # minimum value
        assert_rel_error(self, prob['a_disk.Cp'], 16./27., 1e-4)
        assert_rel_error(self, prob['a'], 0.33333, 1e-4)
    def test_model_viewer_has_correct_data_from_optimization_problem(self):
        """
        Verify that the correct model structure data exists when stored as compared
        to the expected structure, using the ActuatorDisc model.
        """

        # build the model
        prob = om.Problem()
        indeps = prob.model.add_subsystem('indeps',
                                          om.IndepVarComp(),
                                          promotes=['*'])
        indeps.add_output('a', .5)
        indeps.add_output('Area', 10.0, units='m**2')
        indeps.add_output('rho', 1.225, units='kg/m**3')
        indeps.add_output('Vu', 10.0, units='m/s')

        prob.model.add_subsystem('a_disk',
                                 ActuatorDisc(),
                                 promotes_inputs=['a', 'Area', 'rho', 'Vu'])

        # setup the optimization
        prob.driver = om.ScipyOptimizeDriver()
        prob.driver.options['optimizer'] = 'SLSQP'

        prob.model.add_design_var('a', lower=0., upper=1.)
        prob.model.add_design_var('Area', lower=0., upper=1.)

        # negative one so we maximize the objective
        prob.model.add_objective('a_disk.Cp', scaler=-1)

        prob.setup()
        prob.final_setup()

        model_viewer_data = _get_viewer_data(prob)

        # To generate the reference JSON file, use this code
        # from openmdao.utils.testing_utils import _ModelViewerDataTreeEncoder
        # with open('betz_tree_new.json', 'w') as outfile:
        #    json.dump(model_viewer_data['tree'], outfile,cls=_ModelViewerDataTreeEncoder, indent=4)

        with open(os.path.join(self.parent_dir,
                               'betz_tree.json')) as json_file:
            expected_tree_betz = json.load(json_file)

        expected_pathnames_betz = json.loads('[]')
        expected_conns_betz = json.loads("""
                [{"src": "indeps.a", "tgt": "a_disk.a"}, {"src": "indeps.Area", "tgt": "a_disk.Area"},
                 {"src": "indeps.rho", "tgt": "a_disk.rho"}, {"src": "indeps.Vu", "tgt": "a_disk.Vu"}]
                """)
        expected_abs2prom_betz = json.loads("""
                    {"input": {"a_disk.a": "a", "a_disk.Area": "Area", "a_disk.rho": "rho", "a_disk.Vu": "Vu"},
                    "output": {"indeps.a": "a", "indeps.Area": "Area", "indeps.rho": "rho", "indeps.Vu": "Vu",
                    "a_disk.Vr": "a_disk.Vr", "a_disk.Vd": "a_disk.Vd", "a_disk.Ct": "a_disk.Ct",
                    "a_disk.thrust": "a_disk.thrust", "a_disk.Cp": "a_disk.Cp",
                    "a_disk.power": "a_disk.power"}}
                    """)
        expected_declare_partials_betz = json.loads("""
                ["a_disk.Vr > a_disk.a", "a_disk.Vr > a_disk.Vu", "a_disk.Vd > a_disk.a", "a_disk.Ct > a_disk.a", "a_disk.thrust > a_disk.a", "a_disk.thrust > a_disk.Area", "a_disk.thrust > a_disk.rho", "a_disk.thrust > a_disk.Vu", "a_disk.Cp > a_disk.a", "a_disk.power > a_disk.a", "a_disk.power > a_disk.Area", "a_disk.power > a_disk.rho", "a_disk.power > a_disk.Vu"]
        """)

        expected_driver_name = 'ScipyOptimizeDriver'
        expected_design_vars_names = ['indeps.a', 'indeps.Area']
        expected_responses_names = [
            'a_disk.Cp',
        ]

        # check expected model tree
        self.check_model_viewer_data(
            model_viewer_data,
            expected_tree_betz,
            expected_pathnames_betz,
            expected_conns_betz,
            expected_abs2prom_betz,
            expected_declare_partials_betz,
            expected_driver_name,
            expected_design_vars_names,
            expected_responses_names,
        )