Example #1
0
    def save(self):
        """
        Save the optimization to the files.
        Possible files modified are:
            - the .yml configuration file
            - the input file (initial values)
            - the output file (values)
        """
        conf = self.problem_configuration
        input_variables = DataFile(self.problem_configuration.input_file_path,
                                   None)
        output_variables = DataFile(
            self.problem_configuration.output_file_path, None)
        opt_def = conf.get_optimization_definition()

        variables = self.get_variables()
        for variable in variables:
            name = variable.name
            if name in input_variables.names():
                input_variables[name].value = variable.metadata[
                    "initial_value"]
            if name in output_variables.names():
                output_variables[name].value = variable.metadata["value"]
            self._update_optim_variable(variable, opt_def)

        # Saving modifications
        # Initial values
        input_variables.save()
        # Values
        output_variables.save()

        # Optimization definition
        conf.set_optimization_definition(opt_def)
        conf.save()
Example #2
0
    def _get_problem_inputs(
            self,
            problem: FASTOADProblem) -> Tuple[om.IndepVarComp, VariableList]:
        """
        Reads input file for the configure problem.

        Needed variables are returned as an IndepVarComp instance while unused variables are
        returned as a VariableList instance.

        :param problem: problem with missing inputs. setup() must have been run.
        :return: IVC of needed input variables, VariableList with unused variables.
        """
        mandatory, optional = get_unconnected_input_names(problem,
                                                          promoted_names=True)
        needed_variable_names = mandatory + optional

        input_variables = DataFile(self.input_file_path)

        unused_variables = VariableList([
            var for var in input_variables
            if var.name not in needed_variable_names
        ])
        for name in unused_variables.names():
            del input_variables[name]

        nan_variable_names = [
            var.name for var in input_variables if np.all(np.isnan(var.value))
        ]
        if nan_variable_names:
            raise FASTConfigurationNanInInputFile(self.input_file_path,
                                                  nan_variable_names)

        input_ivc = input_variables.to_ivc()
        return input_ivc, unused_variables
def test_problem_definition_with_xml_ref_with_indep(cleanup):
    """Tests what happens when writing inputs of a problem with indeps using data from existing XML file"""
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator(
            pth.join(DATA_FOLDER_PATH, "valid_sellar_with_indep.%s" % extension)
        )

        result_folder_path = pth.join(
            RESULTS_FOLDER_PATH, "problem_definition_with_xml_ref_with_indep"
        )
        conf.input_file_path = pth.join(result_folder_path, "inputs.xml")
        conf.output_file_path = pth.join(result_folder_path, "outputs.xml")
        ref_input_data_path = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
        conf.write_needed_inputs(ref_input_data_path)
        input_data = DataFile(conf.input_file_path)
        assert len(input_data) == 2
        assert "system:x" in input_data.names()
        assert "z" in input_data.names()

        problem = conf.get_problem(read_inputs=True, auto_scaling=True)
        # runs evaluation without optimization loop to check that inputs are taken into account
        problem.setup()
        # system:x is not in ref_inputs.xml
        problem["system:x"] = 1.0
        problem.run_model()
        assert problem["f"] == pytest.approx(28.58830817, abs=1e-6)
        problem.write_outputs()
Example #4
0
    def write_needed_inputs(self,
                            source_file_path: str = None,
                            source_formatter: IVariableIOFormatter = None):
        """
        Writes the input file of the problem with unconnected inputs of the
        configured problem.

        Written value of each variable will be taken:

            1. from input_data if it contains the variable
            2. from defined default values in component definitions

        :param source_file_path: if provided, variable values will be read from it
        :param source_formatter: the class that defines format of input file. if
                                 not provided, expected format will be the default one.
        """
        problem = self.get_problem(read_inputs=False)
        problem.setup()
        variables = DataFile(self.input_file_path, load_data=False)
        variables.update(
            VariableList.from_unconnected_inputs(problem,
                                                 with_optional_inputs=True),
            add_variables=True,
        )
        if source_file_path:
            ref_vars = DataFile(source_file_path, formatter=source_formatter)
            variables.update(ref_vars, add_variables=False)
            for var in variables:
                var.is_input = True
        variables.save()
Example #5
0
def test_mission_group_without_loop(cleanup, with_dummy_plugin_2):
    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    ivc = DataFile(input_file_path).to_ivc()

    with pytest.raises(FastMissionFileMissingMissionNameError):
        run_system(
            Mission(
                propulsion_id="test.wrapper.propulsion.dummy_engine",
                out_file=pth.join(RESULTS_FOLDER_PATH, "unlooped_mission_group.csv"),
                use_initializer_iteration=False,
                mission_file_path=pth.join(DATA_FOLDER_PATH, "test_mission.yml"),
                adjust_fuel=False,
                reference_area_variable="data:geometry:aircraft:reference_area",
            ),
            ivc,
        )

    problem = run_system(
        Mission(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH, "unlooped_mission_group.csv"),
            use_initializer_iteration=False,
            mission_file_path=pth.join(DATA_FOLDER_PATH, "test_mission.yml"),
            mission_name="operational",
            adjust_fuel=False,
            reference_area_variable="data:geometry:aircraft:reference_area",
        ),
        ivc,
    )
    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6589.0, atol=1.0)
    assert_allclose(problem["data:mission:operational:block_fuel"], 15195.0, atol=1.0)
Example #6
0
    def _get_problem_inputs(self) -> Tuple[VariableList, VariableList]:
        """
        Reads input file for the configured problem.

        Needed variables and unused variables are
        returned as a VariableList instance.

        :return: VariableList of needed input variables, VariableList with unused variables.
        """

        problem_variables = VariableList().from_problem(self)
        problem_inputs_names = [var.name for var in problem_variables if var.is_input]

        input_variables = DataFile(self.input_file_path)

        unused_variables = VariableList(
            [var for var in input_variables if var.name not in problem_inputs_names]
        )
        for name in unused_variables.names():
            del input_variables[name]

        nan_variable_names = [var.name for var in input_variables if np.all(np.isnan(var.value))]
        if nan_variable_names:
            raise FASTOpenMDAONanInInputFile(self.input_file_path, nan_variable_names)

        return input_variables, unused_variables
Example #7
0
def test_mission_group_with_loop(cleanup):

    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    vars = DataFile(input_file_path)
    del vars["data:mission:operational:TOW"]
    ivc = vars.to_ivc()

    problem = run_system(
        Mission(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH,
                              "test_looped_mission_group.csv"),
            use_initializer_iteration=True,
            mission_file_path=pth.join(DATA_FOLDER_PATH, "test_mission.yml"),
            mission_name="operational",
            add_solver=True,
        ),
        ivc,
    )

    # check loop
    assert_allclose(
        problem["data:mission:operational:TOW"],
        problem["data:mission:operational:OWE"] +
        problem["data:mission:operational:payload"] +
        problem["data:mission:operational:onboard_fuel_at_takeoff"],
        atol=1.0,
    )
    assert_allclose(
        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"],
        problem["data:mission:operational:onboard_fuel_at_takeoff"],
        atol=1.0,
    )
    assert_allclose(
        problem["data:mission:operational:needed_block_fuel"],
        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"] +
        problem["data:mission:operational:taxi_out:fuel"] +
        problem["data:mission:operational:takeoff:fuel"],
        atol=1.0,
    )
    assert_allclose(
        problem["data:mission:operational:needed_block_fuel"],
        5682.0,
        atol=1.0,
    )
Example #8
0
def test_mission_group_breguet_with_loop(cleanup, with_dummy_plugin_2):

    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    vars = DataFile(input_file_path)
    del vars["data:mission:operational:TOW"]
    ivc = vars.to_ivc()

    problem = run_system(
        Mission(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH, "looped_breguet_mission_group.csv"),
            use_initializer_iteration=True,
            mission_file_path=pth.join(DATA_FOLDER_PATH, "test_breguet.yml"),
            add_solver=True,
            reference_area_variable="data:geometry:aircraft:reference_area",
        ),
        ivc,
    )

    # check loop
    assert_allclose(
        problem["data:mission:operational:TOW"],
        problem["data:mission:operational:OWE"]
        + problem["data:mission:operational:payload"]
        + problem["data:mission:operational:onboard_fuel_at_takeoff"],
        atol=1.0,
    )
    assert_allclose(
        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"],
        problem["data:mission:operational:onboard_fuel_at_takeoff"],
        atol=1.0,
    )
    assert_allclose(
        problem["data:mission:operational:needed_block_fuel"],
        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"]
        + problem["data:mission:operational:taxi_out:fuel"]
        + problem["data:mission:operational:takeoff:fuel"],
        atol=1.0,
    )
    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 5626.0, atol=1.0)
    assert_allclose(
        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"], 5430.0, atol=1.0
    )
def test_problem_definition_with_xml_ref(cleanup):
    """Tests what happens when writing inputs using data from existing XML file"""
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator(pth.join(DATA_FOLDER_PATH, "valid_sellar.%s" % extension))

        result_folder_path = pth.join(RESULTS_FOLDER_PATH, "problem_definition_with_xml_ref")
        conf.input_file_path = pth.join(result_folder_path, "inputs.xml")
        conf.output_file_path = pth.join(result_folder_path, "outputs.xml")
        ref_input_data_path = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
        conf.write_needed_inputs(ref_input_data_path)
        input_data = DataFile(conf.input_file_path)
        assert len(input_data) == 2
        assert "x" in input_data.names()
        assert "z" in input_data.names()

        problem = conf.get_problem(read_inputs=True, auto_scaling=True)
        # runs evaluation without optimization loop to check that inputs are taken into account
        problem.setup()
        problem.run_model()
        assert problem["f"] == pytest.approx(28.58830817, abs=1e-6)
        problem.write_outputs()

        # Test with alternate submodel #########################################
        alt_conf = FASTOADProblemConfigurator(
            pth.join(DATA_FOLDER_PATH, "valid_sellar_alternate.%s" % extension)
        )
        alt_conf.input_file_path = pth.join(result_folder_path, "inputs.xml")
        alt_conf.output_file_path = pth.join(result_folder_path, "outputs_alt.xml")
        alt_problem = alt_conf.get_problem(read_inputs=True, auto_scaling=True)
        # runs evaluation without optimization loop to check that inputs are taken into account
        alt_problem.setup()
        alt_problem.run_model()
        alt_problem.write_outputs()

        assert alt_problem["f"] == pytest.approx(0.58830817, abs=1e-6)
        assert alt_problem["g2"] == pytest.approx(-11.94151185, abs=1e-6)
        with pytest.raises(KeyError):
            alt_problem["g1"]  # submodel for g1 computation has been deactivated.
Example #10
0
def test_mission_component(cleanup, with_dummy_plugin_2):

    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    ivc = DataFile(input_file_path).to_ivc()

    problem = run_system(
        MissionComponent(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH, "mission.csv"),
            use_initializer_iteration=False,
            mission_wrapper=MissionWrapper(pth.join(DATA_FOLDER_PATH, "test_mission.yml")),
            mission_name="operational",
            reference_area_variable="data:geometry:aircraft:reference_area",
        ),
        ivc,
    )
    # plot_flight(problem.model.component.flight_points, "test_mission.png")
    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6590.0, atol=1.0)

    assert_allclose(
        problem["data:mission:operational:main_route:initial_climb:duration"], 34.0, atol=1.0
    )
    assert_allclose(
        problem["data:mission:operational:main_route:initial_climb:fuel"], 121.0, atol=1.0
    )
    assert_allclose(
        problem["data:mission:operational:main_route:initial_climb:distance"], 3600.0, atol=1.0
    )

    assert_allclose(problem["data:mission:operational:main_route:climb:duration"], 236.0, atol=1.0)
    assert_allclose(problem["data:mission:operational:main_route:climb:fuel"], 727.0, atol=1.0)
    assert_allclose(
        problem["data:mission:operational:main_route:climb:distance"], 43065.0, atol=1.0
    )

    assert_allclose(
        problem["data:mission:operational:main_route:cruise:duration"], 14734.0, atol=1.0
    )
    assert_allclose(problem["data:mission:operational:main_route:cruise:fuel"], 5167.0, atol=1.0)
    assert_allclose(
        problem["data:mission:operational:main_route:cruise:distance"], 3392590.0, atol=1.0
    )

    assert_allclose(
        problem["data:mission:operational:main_route:descent:duration"], 1424.0, atol=1.0
    )
    assert_allclose(problem["data:mission:operational:main_route:descent:fuel"], 192.0, atol=1.0)
    assert_allclose(
        problem["data:mission:operational:main_route:descent:distance"], 264451.0, atol=1.0
    )
Example #11
0
def test_mission_component_breguet(cleanup):

    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    ivc = DataFile(input_file_path).to_ivc()

    problem = run_system(
        MissionComponent(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH, "test_breguet.csv"),
            use_initializer_iteration=False,
            mission_wrapper=MissionWrapper(
                pth.join(DATA_FOLDER_PATH, "test_breguet.yml")),
            mission_name="operational",
        ),
        ivc,
    )
    # plot_flight(problem.model.component.flight_points, "test_mission.png")
    assert_allclose(problem["data:mission:operational:needed_block_fuel"],
                    6353.0,
                    atol=1.0)
Example #12
0
def test_mission_group_breguet_without_loop(cleanup):
    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
    ivc = DataFile(input_file_path).to_ivc()

    problem = run_system(
        Mission(
            propulsion_id="test.wrapper.propulsion.dummy_engine",
            out_file=pth.join(RESULTS_FOLDER_PATH,
                              "test_unlooped_mission_group.csv"),
            use_initializer_iteration=False,
            mission_file_path=pth.join(DATA_FOLDER_PATH, "test_breguet.yml"),
            adjust_fuel=False,
        ),
        ivc,
    )
    assert_allclose(problem["data:mission:operational:needed_block_fuel"],
                    6353.0,
                    atol=1.0)
    assert_allclose(problem["data:mission:operational:block_fuel"],
                    15195.0,
                    atol=1.0)
Example #13
0
    def load(
        self,
        problem_configuration: FASTOADProblemConfigurator,
    ):
        """
        Loads the FAST-OAD problem and stores its data.

        :param problem_configuration: the FASTOADProblem instance.
        """

        self.problem_configuration = problem_configuration

        if pth.isfile(self.problem_configuration.input_file_path):
            input_variables = DataFile(
                self.problem_configuration.input_file_path)
        else:
            # TODO: generate the input file by default ?
            raise FastMissingFile(
                "Please generate input file before using the optimization viewer"
            )

        if pth.isfile(self.problem_configuration.output_file_path):
            output_variables = DataFile(
                self.problem_configuration.output_file_path)
        else:
            problem = self.problem_configuration.get_problem()
            problem.setup()
            output_variables = VariableList.from_problem(problem)

        optimization_variables = VariableList()
        opt_def = problem_configuration.get_optimization_definition()
        # Design Variables
        if KEY_DESIGN_VARIABLES in opt_def:
            for name, design_var in opt_def[KEY_DESIGN_VARIABLES].items():
                metadata = {
                    "type": "design_var",
                    "initial_value": input_variables[name].value,
                    "lower": design_var.get("lower"),
                    "value": output_variables[name].value,
                    "upper": design_var.get("upper"),
                    "units": input_variables[name].units,
                    "desc": input_variables[name].description,
                }
                optimization_variables[name] = metadata

        # Constraints
        if KEY_CONSTRAINTS in opt_def:
            for name, constr in opt_def[KEY_CONSTRAINTS].items():
                metadata = {
                    "type": "constraint",
                    "initial_value": None,
                    "lower": constr.get("lower"),
                    "value": output_variables[name].value,
                    "upper": constr.get("upper"),
                    "units": output_variables[name].units,
                    "desc": output_variables[name].description,
                }
                optimization_variables[name] = metadata

        # Objectives
        if KEY_OBJECTIVE in opt_def:
            for name in opt_def[KEY_OBJECTIVE]:
                metadata = {
                    "type": "objective",
                    "initial_value": None,
                    "lower": None,
                    "value": output_variables[name].value,
                    "upper": None,
                    "units": output_variables[name].units,
                    "desc": output_variables[name].description,
                }
                optimization_variables[name] = metadata

        self.load_variables(optimization_variables)
Example #14
0
def test_generate_inputs(cleanup):
    input_file_path = api.generate_inputs(CONFIGURATION_FILE_PATH,
                                          overwrite=False)
    assert input_file_path == pth.join(RESULTS_FOLDER_PATH, "inputs.xml")
    assert pth.exists(input_file_path)
    data = DataFile(input_file_path)
    assert len(data) == 2
    assert "x" in data.names() and "z" in data.names()

    # Let's add another variable to ensure overwrite is correctly done (issue #328)
    data["dummy_var"] = {"value": 0.0}
    data.save()

    # Generating again without forcing overwrite will make it fail
    with pytest.raises(FastPathExistsError):
        api.generate_inputs(CONFIGURATION_FILE_PATH, overwrite=False)

    input_file_path = api.generate_inputs(CONFIGURATION_FILE_PATH,
                                          pth.join(DATA_FOLDER_PATH,
                                                   "inputs.xml"),
                                          overwrite=True)

    assert input_file_path == pth.join(RESULTS_FOLDER_PATH, "inputs.xml")
    assert pth.exists(input_file_path)
    data = DataFile(input_file_path)
    assert len(data) == 2
    assert "x" in data.names() and "z" in data.names()

    # We test without source file to see if variable description in "desc" kwargs
    # is captured (issue #319)
    input_file_path = api.generate_inputs(CONFIGURATION_FILE_PATH,
                                          overwrite=True)
    assert input_file_path == pth.join(RESULTS_FOLDER_PATH, "inputs.xml")
    assert pth.exists(input_file_path)
    data = DataFile(input_file_path)
    assert len(data) == 2
    assert "x" in data.names() and "z" in data.names()
Example #15
0
def run_non_regression_test(
    conf_file,
    legacy_result_file,
    result_dir,
    use_xfoil=False,
    global_tolerance=1e-2,
    vars_to_check=None,
    specific_tolerance=5.0e-3,
    check_weight_perfo_loop=True,
):
    """
    Convenience function for non regression tests
    :param conf_file: FAST-OAD configuration file
    :param legacy_result_file: reference data for inputs and outputs
    :param result_dir: relative name, folder will be in RESULTS_FOLDER_PATH
    :param use_xfoil: if True, XFOIL computation will be activated
    :param vars_to_check: variables that will be concerned by specific_tolerance
    :param specific_tolerance: test will fail if absolute relative error between computed and
                               reference values is beyond this value for variables in vars_to_check
    :param global_tolerance: test will fail if absolute relative error between computed and
                             reference values is beyond this value for ANY variable
    :param check_weight_perfo_loop: if True, consistency of weights will be checked
    """
    results_folder_path = pth.join(RESULTS_FOLDER_PATH, result_dir)
    configuration_file_path = pth.join(results_folder_path, conf_file)

    # Copy of configuration file and generation of problem instance ------------------
    api.generate_configuration_file(configuration_file_path)  # just ensure folders are created...
    shutil.copy(pth.join(DATA_FOLDER_PATH, conf_file), configuration_file_path)
    configurator = FASTOADProblemConfigurator(configuration_file_path)
    configurator._set_configuration_modifier(XFOILConfigurator(use_xfoil))

    # Generation of inputs ----------------------------------------
    ref_inputs = pth.join(DATA_FOLDER_PATH, legacy_result_file)
    configurator.write_needed_inputs(ref_inputs)

    # Get problem with inputs -------------------------------------
    problem = configurator.get_problem(read_inputs=True)
    problem.setup()

    # Run model ---------------------------------------------------------------
    problem.run_model()
    problem.write_outputs()

    om.view_connections(
        problem, outfile=pth.join(results_folder_path, "connections.html"), show_browser=False
    )

    if check_weight_perfo_loop:
        _check_weight_performance_loop(problem)

    ref_data = DataFile(pth.join(DATA_FOLDER_PATH, legacy_result_file))

    row_list = []
    for ref_var in ref_data:
        try:
            value = problem.get_val(ref_var.name, units=ref_var.units)[0]
        except KeyError:
            continue
        row_list.append(
            {
                "name": ref_var.name,
                "units": ref_var.units,
                "ref_value": ref_var.value[0],
                "value": value,
            }
        )

    df = pd.DataFrame(row_list)
    df["rel_delta"] = (df.value - df.ref_value) / df.ref_value
    df["rel_delta"][(df.ref_value == 0) & (abs(df.value) <= 1e-10)] = 0.0
    df["abs_rel_delta"] = np.abs(df.rel_delta)

    pd.set_option("display.max_rows", None)
    pd.set_option("display.max_columns", None)
    pd.set_option("display.width", 1000)
    pd.set_option("display.max_colwidth", 120)
    print(df.sort_values(by=["abs_rel_delta"]))

    if vars_to_check is not None:
        for name in vars_to_check:
            assert_allclose(df.ref_value, df.value, rtol=global_tolerance)
            row = df.loc[df.name == name]
            assert_allclose(row.ref_value, row.value, rtol=specific_tolerance)
    else:
        assert np.all(df.abs_rel_delta < specific_tolerance)