Esempio n. 1
0
            def patched_function(inputs_dict: dict) -> dict:
                """
                The patched function perform a run of an openmdao component or group applying FASTOAD formalism.

                @param inputs_dict: dictionary of input (values, units) saved with their key name,
                as an example: inputs_dict = {'in1': (3.0, "m")}.
                @return: dictionary of the component/group outputs saving names as keys and (value, units) as tuple.
                """


                # Read .xml file and construct Independent Variable Component excluding outputs
                reader.path_separator = ":"
                ivc_local = reader.to_ivc()
                for name, value in inputs_dict.items():
                    ivc_local.add_output(name, value[0], units=value[1])
                group_local = AutoUnitsDefaultGroup()
                group_local.add_subsystem('system', system, promotes=["*"])
                group_local.add_subsystem('ivc', ivc_local, promotes=["*"])
                problem_local = FASTOADProblem(group_local)
                problem_local.setup()
                problem_local.run_model()
                if overwrite:
                    problem_local.output_file_path = xml_file_path
                    problem_local.write_outputs()
                # Get output names from component/group and construct dictionary
                outputs_units = [var.units for var in variables if not var.is_input]
                outputs_dict = {}
                for idx in range(len(outputs_names)):
                    value = problem_local.get_val(outputs_names[idx], outputs_units[idx])
                    outputs_dict[outputs_names[idx]] = (value, outputs_units[idx])
                return outputs_dict
Esempio n. 2
0
    def get_problem(self,
                    read_inputs: bool = False,
                    auto_scaling: bool = False) -> FASTOADProblem:
        """
        Builds the OpenMDAO problem from current configuration.

        :param read_inputs: if True, the created problem will already be fed
                            with variables from the input file
        :param auto_scaling: if True, automatic scaling is performed for design
                             variables and constraints
        :return: the problem instance
        """
        if not self._conf_dict:
            raise RuntimeError("read configuration file first")

        problem = FASTOADProblem(self._build_model())

        problem.input_file_path = self.input_file_path
        problem.output_file_path = self.output_file_path

        driver = self._conf_dict.get(KEY_DRIVER, "")
        if driver:
            problem.driver = _om_eval(driver)

        if self.get_optimization_definition():
            self._add_constraints(problem.model, auto_scaling)
            self._add_objectives(problem.model)

        if read_inputs:
            problem.read_inputs()
            self._add_design_vars(problem.model, auto_scaling)

        return problem
def test_problem_read_inputs_with_nan_inputs(cleanup):
    """Tests that when reading inputs using existing XML with some nan values an exception is raised"""

    problem = FASTOADProblem()
    problem.model.add_subsystem("sellar", Sellar(), promotes=["*"])

    input_data_path = pth.join(DATA_FOLDER_PATH, "nan_inputs.xml")

    problem.input_file_path = pth.join(DATA_FOLDER_PATH, "nan_inputs.xml")

    with pytest.raises(FASTOpenMDAONanInInputFile) as exc_info:
        problem.read_inputs()
        assert exc_info.value.input_file_path == input_data_path
        assert exc_info.value.nan_variable_names == ["x"]

    problem.setup()

    with pytest.raises(FASTOpenMDAONanInInputFile) as exc_info:
        problem.read_inputs()
        assert exc_info.value.input_file_path == input_data_path
        assert exc_info.value.nan_variable_names == ["x"]
Esempio n. 4
0
    def get_problem(self,
                    read_inputs: bool = False,
                    auto_scaling: bool = False) -> FASTOADProblem:
        """
        Builds the OpenMDAO problem from current configuration.

        :param read_inputs: if True, the created problem will already be fed
                            with variables from the input file
        :param auto_scaling: if True, automatic scaling is performed for design
                             variables and constraints
        :return: the problem instance
        """
        if self._serializer.data is None:
            raise RuntimeError("read configuration file first")

        if read_inputs:
            problem_with_no_inputs = self.get_problem(
                auto_scaling=auto_scaling)
            problem_with_no_inputs.setup()
            input_ivc, unused_variables = self._get_problem_inputs(
                problem_with_no_inputs)
        else:
            input_ivc = unused_variables = None

        problem = FASTOADProblem(self._build_model(input_ivc))
        problem.input_file_path = self.input_file_path
        problem.output_file_path = self.output_file_path
        problem.additional_variables = unused_variables

        driver = self._serializer.data.get(KEY_DRIVER, "")
        if driver:
            problem.driver = _om_eval(driver)

        if self.get_optimization_definition():
            self._add_constraints(problem.model, auto_scaling)
            self._add_objectives(problem.model)

        if read_inputs:
            self._add_design_vars(problem.model, auto_scaling)

        if self._configuration_modifier:
            self._configuration_modifier.modify(problem)

        return problem
Esempio n. 5
0
def test_write_outputs():
    problem = FASTOADProblem()
    problem.model.add_subsystem("sellar", Sellar(), promotes=["*"])
    problem.output_file_path = pth.join(RESULTS_FOLDER_PATH, "output.xml")
    problem.setup()

    problem.write_outputs()
    variables = VariableIO(problem.output_file_path).read()
    assert variables == [
        Variable(name="f", value=1.0),
        Variable(name="g1", value=1.0),
        Variable(name="g2", value=1.0),
        Variable(name="x", value=2),
        Variable(name="y2", value=1.0),
        Variable(name="z", value=[5.0, 2.0], units="m**2"),
    ]

    problem.run_model()
    problem.write_outputs()
    variables = VariableIO(problem.output_file_path).read()
    assert variables == [
        Variable(name="f", value=32.569100892077444),
        Variable(name="g1", value=-23.409095627564167),
        Variable(name="g2", value=-11.845478137832359),
        Variable(name="x", value=2),
        Variable(name="y2", value=12.154521862167641),
        Variable(name="z", value=[5.0, 2.0], units="m**2"),
    ]
Esempio n. 6
0
def generate_block_analysis(
        system: Union[ExplicitComponent, ImplicitComponent, Group],
        var_inputs: List,
        xml_file_path: str,
        overwrite: bool = False,
):

    # Search what are the component/group outputs
    variables = list_variables(system)
    inputs_names = [var.name for var in variables if var.is_input]
    outputs_names = [var.name for var in variables if not var.is_input]

    # Check that variable inputs are in the group/component list
    if not(set(var_inputs) == set(inputs_names).intersection(set(var_inputs))):
        raise Exception('The input list contains name(s) out of component/group input list!')

    # Perform some tests on the .xml availability and completeness
    if not(os.path.exists(xml_file_path)) and not(set(var_inputs) == set(inputs_names)):
        # If no input file and some inputs are missing, generate it and return None
        if isinstance(system, Group):
            problem = FASTOADProblem(system)
        else:
            group = AutoUnitsDefaultGroup()
            group.add_subsystem('system', system, promotes=["*"])
            problem = FASTOADProblem(group)
        problem.input_file_path = xml_file_path
        problem.setup()
        problem.write_needed_inputs(None, VariableXmlStandardFormatter())
        raise Exception('Input .xml file not found, a default file has been created with default NaN values, '
                        'but no function is returned!\nConsider defining proper values before second execution!')

    elif os.path.exists(xml_file_path):

        reader = VariableIO(xml_file_path, VariableXmlStandardFormatter()).read(ignore=(var_inputs + outputs_names))
        xml_inputs = reader.names()
        if not(set(xml_inputs + var_inputs).intersection(set(inputs_names)) == set(inputs_names)):
            # If some inputs are missing write an error message and add them to the problem if authorized
            missing_inputs = list(
                set(inputs_names).difference(set(xml_inputs + var_inputs).intersection(set(inputs_names)))
            )
            message = 'The following inputs are missing in .xml file:'
            for item in missing_inputs:
                message += ' [' + item + '],'
            message = message[:-1] + '.\n'
            if overwrite:
                reader.path_separator = ":"
                ivc = reader.to_ivc()
                group = AutoUnitsDefaultGroup()
                group.add_subsystem('system', system, promotes=["*"])
                group.add_subsystem('ivc', ivc, promotes=["*"])
                problem = FASTOADProblem(group)
                problem.input_file_path = xml_file_path
                problem.output_file_path = xml_file_path
                problem.setup()
                problem.write_outputs()
                message += 'Default values have been added to {} file. ' \
                           'Consider modifying them for a second run!'.format(xml_file_path)
                raise Exception(message)
            else:
                raise Exception(message)
        else:
            # If all inputs addressed either by .xml or var_inputs, construct the function
            def patched_function(inputs_dict: dict) -> dict:
                """
                The patched function perform a run of an openmdao component or group applying FASTOAD formalism.

                @param inputs_dict: dictionary of input (values, units) saved with their key name,
                as an example: inputs_dict = {'in1': (3.0, "m")}.
                @return: dictionary of the component/group outputs saving names as keys and (value, units) as tuple.
                """


                # Read .xml file and construct Independent Variable Component excluding outputs
                reader.path_separator = ":"
                ivc_local = reader.to_ivc()
                for name, value in inputs_dict.items():
                    ivc_local.add_output(name, value[0], units=value[1])
                group_local = AutoUnitsDefaultGroup()
                group_local.add_subsystem('system', system, promotes=["*"])
                group_local.add_subsystem('ivc', ivc_local, promotes=["*"])
                problem_local = FASTOADProblem(group_local)
                problem_local.setup()
                problem_local.run_model()
                if overwrite:
                    problem_local.output_file_path = xml_file_path
                    problem_local.write_outputs()
                # Get output names from component/group and construct dictionary
                outputs_units = [var.units for var in variables if not var.is_input]
                outputs_dict = {}
                for idx in range(len(outputs_names)):
                    value = problem_local.get_val(outputs_names[idx], outputs_units[idx])
                    outputs_dict[outputs_names[idx]] = (value, outputs_units[idx])
                return outputs_dict
            return patched_function
def test_problem_read_inputs_before_setup(cleanup):
    """Tests what happens when reading inputs using existing XML with correct var"""

    problem = FASTOADProblem()
    problem.model.add_subsystem("sellar", Sellar(), promotes=["*"])

    problem.input_file_path = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")

    problem.read_inputs()
    problem.setup()
    problem.run_model()

    assert_allclose(problem.get_val(name="x"), 1.0)
    assert_allclose(problem.get_val(name="z", units="m**2"), [4.0, 3.0])
    assert_allclose(problem["f"], 21.7572, atol=1.0e-4)
def test_problem_read_inputs_after_setup(cleanup):
    """Tests what happens when reading inputs using existing XML with correct var"""

    problem = FASTOADProblem()
    problem.model.add_subsystem("sellar", Sellar(), promotes=["*"])

    problem.input_file_path = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")

    problem.setup()

    assert problem.get_val(name="x") == [2.0]
    with pytest.raises(RuntimeError):
        # Several default values are defined for "z", thus OpenMDAO raises an error that
        # will be solved only after run_model() has been used.
        _ = problem.get_val(name="z", units="m**2")

    problem.read_inputs()

    problem.run_model()
    assert_allclose(problem.get_val(name="x"), 1.0)
    assert_allclose(problem.get_val(name="z", units="m**2"), [4.0, 3.0])
    assert_allclose(problem["f"], 21.7572, atol=1.0e-4)
def test_write_outputs():
    problem = FASTOADProblem()
    problem.model.add_subsystem("sellar", Sellar(), promotes=["*"])
    problem.output_file_path = pth.join(RESULTS_FOLDER_PATH, "output.xml")
    problem.setup()

    problem.write_outputs()
    variables = VariableIO(problem.output_file_path).read()
    assert variables == [
        Variable(name="f", val=1.0),
        Variable(name="g1", val=1.0),
        Variable(name="g2", val=1.0),
        Variable(name="x", val=2.0),
        Variable(name="y2", val=1.0),
        Variable(name="z", val=[np.nan, np.nan], units="m**2"),
    ]

    problem["x"] = 2.0
    problem["z"] = [
        5.0,
        2.0,
    ]  # Since version 3.17 of OpenMDAO, the np.nan input definition of z is chosen.

    problem.run_model()
    problem.write_outputs()
    variables = VariableIO(problem.output_file_path).read()
    assert variables == [
        Variable(name="f", val=32.569100892077444),
        Variable(name="g1", val=-23.409095627564167),
        Variable(name="g2", val=-11.845478137832359),
        Variable(name="x", val=2.0),
        Variable(name="y2", val=12.154521862167641),
        Variable(name="z", val=[5.0, 2.0], units="m**2"),
    ]
def test_problem_with_dynamically_shaped_inputs(cleanup):
    class MyComp1(om.ExplicitComponent):
        def setup(self):
            self.add_input("x", shape_by_conn=True, copy_shape="y")
            self.add_output("y", shape_by_conn=True, copy_shape="x")

        def compute(self,
                    inputs,
                    outputs,
                    discrete_inputs=None,
                    discrete_outputs=None):
            outputs["y"] = 10 * inputs["x"]

    class MyComp2(om.ExplicitComponent):
        def setup(self):
            self.add_input("y", shape_by_conn=True, copy_shape="z")
            self.add_output("z", shape_by_conn=True, copy_shape="y")

        def compute(self,
                    inputs,
                    outputs,
                    discrete_inputs=None,
                    discrete_outputs=None):
            outputs["z"] = 0.1 * inputs["y"]

    # --------------------------------------------------------------------------
    # With these 2 components, an OpenMDAO problem won't pass the setup due to
    # the non-determined shapes
    vanilla_problem = om.Problem()
    vanilla_problem.model.add_subsystem("comp1", MyComp1(), promotes=["*"])
    vanilla_problem.model.add_subsystem("comp2", MyComp2(), promotes=["*"])
    with pytest.raises(RuntimeError):
        vanilla_problem.setup()

    # --------------------------------------------------------------------------
    # ... But fastoad problem will do the setup and provide dummy shapes
    # when needed
    fastoad_problem = FASTOADProblem()
    fastoad_problem.model.add_subsystem("comp1", MyComp1(), promotes=["*"])
    fastoad_problem.model.add_subsystem("comp2", MyComp2(), promotes=["*"])
    fastoad_problem.setup()
    assert (fastoad_problem["x"].shape == fastoad_problem["y"].shape ==
            fastoad_problem["z"].shape == (2, ))

    # In such case, reading inputs after the setup will make run_model fail, because dummy shapes
    # have already been provided, and will probably not match the ones in input file.
    fastoad_problem.input_file_path = pth.join(DATA_FOLDER_PATH,
                                               "dynamic_shape_inputs_1.xml")
    fastoad_problem.read_inputs()
    with pytest.raises(ValueError):
        fastoad_problem.run_model()

    # --------------------------------------------------------------------------
    # If input reading is done before setup, all is fine.
    fastoad_problem = FASTOADProblem()
    fastoad_problem.model.add_subsystem("comp1", MyComp1(), promotes=["*"])
    fastoad_problem.model.add_subsystem("comp2", MyComp2(), promotes=["*"])
    fastoad_problem.input_file_path = pth.join(DATA_FOLDER_PATH,
                                               "dynamic_shape_inputs_1.xml")
    fastoad_problem.read_inputs()
    fastoad_problem.setup()

    inputs = VariableList.from_problem(fastoad_problem, io_status="inputs")
    assert inputs.names() == ["x"]
    outputs = VariableList.from_problem(fastoad_problem, io_status="outputs")
    assert outputs.names() == ["y", "z"]
    variables = VariableList.from_problem(fastoad_problem)
    assert variables.names() == ["x", "y", "z"]

    fastoad_problem.run_model()

    assert_allclose(fastoad_problem["x"], [1.0, 2.0, 5.0])
    assert_allclose(fastoad_problem["y"], [10.0, 20.0, 50.0])
    assert_allclose(fastoad_problem["z"], [1.0, 2.0, 5.0])

    # --------------------------------------------------------------------------
    # In the case variables are shaped from "downstream", OpenMDAO works OK.
    class MyComp3(om.ExplicitComponent):
        def setup(self):
            self.add_input("z", shape=(3, ))
            self.add_output("a")

        def compute(self,
                    inputs,
                    outputs,
                    discrete_inputs=None,
                    discrete_outputs=None):
            outputs["a"] = np.sum(inputs["z"])

    fastoad_problem = FASTOADProblem()
    fastoad_problem.model.add_subsystem("comp1", MyComp1(), promotes=["*"])
    fastoad_problem.model.add_subsystem("comp2", MyComp2(), promotes=["*"])
    fastoad_problem.model.add_subsystem("comp3", MyComp3(), promotes=["*"])

    inputs = VariableList.from_problem(fastoad_problem, io_status="inputs")
    assert inputs.names() == ["x"]
    outputs = VariableList.from_problem(fastoad_problem, io_status="outputs")
    assert outputs.names() == ["y", "z", "a"]
    variables = VariableList.from_problem(fastoad_problem)
    assert variables.names() == ["x", "y", "z", "a"]

    fastoad_problem.setup()
    fastoad_problem.run_model()
def test_problem_with_case_recorder(cleanup):
    """Tests what happens when using a case recorder"""
    # Adding a case recorder may cause a crash in case of deepcopy.

    problem = FASTOADProblem()
    sellar = Sellar()
    sellar.nonlinear_solver = om.NonlinearBlockGS(
    )  # Solver that is compatible with deepcopy
    sellar.add_recorder(
        om.SqliteRecorder(pth.join(RESULTS_FOLDER_PATH, "cases.sql")))

    problem.model.add_subsystem("sellar", sellar, promotes=["*"])

    problem.input_file_path = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")

    problem.setup()
    problem.read_inputs()
    problem.run_model()

    assert_allclose(problem.get_val(name="x"), 1.0)
    assert_allclose(problem.get_val(name="z", units="m**2"), [4.0, 3.0])
    assert_allclose(problem["f"], 21.7572, atol=1.0e-4)