def test_problem_definition_with_xml_ref_run_optim(cleanup):
    """
    Tests what happens when writing inputs using data from existing XML file
    and running an optimization problem
    """
    conf = FASTOADProblemConfigurator(
        pth.join(DATA_FOLDER_PATH, "valid_sellar.toml"))

    input_data = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
    conf.write_needed_inputs(input_data)

    # Runs optimization problem with semi-analytic FD
    problem1 = conf.get_problem(read_inputs=True)
    problem1.setup()
    problem1.run_model()
    assert problem1["f"] == pytest.approx(28.58830817, abs=1e-6)
    problem1.run_driver()
    assert problem1["f"] == pytest.approx(3.18339395, abs=1e-6)

    # Runs optimization problem with monolithic FD
    problem2 = conf.get_problem(read_inputs=True)
    problem2.model.approx_totals()
    problem2.setup()
    problem2.run_model()  # checks problem has been reset
    assert problem2["f"] == pytest.approx(28.58830817, abs=1e-6)
    problem2.run_driver()
    assert problem2["f"] == pytest.approx(3.18339395, abs=1e-6)
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 #3
0
def test_oad_process(cleanup):
    """
    Test for the overall aircraft design process.
    """

    configurator = FASTOADProblemConfigurator(pth.join(DATA_FOLDER_PATH, "oad_process.yml"))

    # Create inputs
    ref_inputs = pth.join(DATA_FOLDER_PATH, "CeRAS01_legacy.xml")
    configurator.write_needed_inputs(ref_inputs)

    # Create problems with inputs
    problem = configurator.get_problem(read_inputs=True)
    problem.setup()
    problem.run_model()
    problem.write_outputs()

    if not pth.exists(RESULTS_FOLDER_PATH):
        os.mkdir(RESULTS_FOLDER_PATH)
    om.view_connections(
        problem, outfile=pth.join(RESULTS_FOLDER_PATH, "connections.html"), show_browser=False
    )
    om.n2(problem, outfile=pth.join(RESULTS_FOLDER_PATH, "n2.html"), show_browser=False)

    # Check that weight-performances loop correctly converged
    _check_weight_performance_loop(problem)
Example #4
0
def generate_inputs(
    configuration_file_path: str,
    source_path: str = None,
    source_path_schema="native",
    overwrite: bool = False,
):
    """
    Generates input file for the :class:`FASTOADProblem` specified in configuration_file_path.

    :param configuration_file_path: where the path of input file to write is set
    :param source_path: path of file data will be taken from
    :param source_path_schema: set to 'legacy' if the source file come from legacy FAST
    :param overwrite: if True, file will be written even if one already exists
    :raise FastFileExistsError: if overwrite==False and configuration_file_path already exists
    """
    conf = FASTOADProblemConfigurator(configuration_file_path)

    input_file_path = conf.input_file_path
    if not overwrite and pth.exists(conf.input_file_path):
        raise FastFileExistsError(
            "Input file %s not written because it already exists. "
            "Use overwrite=True to bypass." % input_file_path,
            input_file_path,
        )

    if source_path_schema == "legacy":
        conf.write_needed_inputs(source_path, VariableLegacy1XmlFormatter())
    else:
        conf.write_needed_inputs(source_path)

    _LOGGER.info("Problem inputs written in %s", input_file_path)
def test_oad_process(cleanup):
    """
    Test for the overall aircraft design process.
    """

    configurator = FASTOADProblemConfigurator(
        pth.join(DATA_FOLDER_PATH, "oad_process.toml"))

    # Create inputs
    ref_inputs = pth.join(DATA_FOLDER_PATH, "CeRAS01_legacy.xml")
    configurator.write_needed_inputs(ref_inputs)

    # Create problems with inputs
    problem = configurator.get_problem(read_inputs=True)
    problem.setup()
    problem.run_model()
    problem.write_outputs()

    if not pth.exists(RESULTS_FOLDER_PATH):
        os.mkdir(RESULTS_FOLDER_PATH)
    om.view_connections(problem,
                        outfile=pth.join(RESULTS_FOLDER_PATH,
                                         "connections.html"),
                        show_browser=False)
    om.n2(problem,
          outfile=pth.join(RESULTS_FOLDER_PATH, "n2.html"),
          show_browser=False)

    # Check that weight-performances loop correctly converged
    assert_allclose(
        problem["data:weight:aircraft:OWE"],
        problem["data:weight:airframe:mass"] +
        problem["data:weight:propulsion:mass"] +
        problem["data:weight:systems:mass"] +
        problem["data:weight:furniture:mass"] +
        problem["data:weight:crew:mass"],
        atol=1,
    )
    assert_allclose(
        problem["data:weight:aircraft:MZFW"],
        problem["data:weight:aircraft:OWE"] +
        problem["data:weight:aircraft:max_payload"],
        atol=1,
    )
    assert_allclose(
        problem["data:weight:aircraft:MTOW"],
        problem["data:weight:aircraft:OWE"] +
        problem["data:weight:aircraft:payload"] +
        problem["data:mission:sizing:needed_block_fuel"],
        atol=1,
    )
def test_problem_definition_with_xml_ref(cleanup):
    """ Tests what happens when writing inputs using data from existing XML file"""
    conf = FASTOADProblemConfigurator(
        pth.join(DATA_FOLDER_PATH, "valid_sellar.toml"))

    input_data = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
    conf.write_needed_inputs(input_data)

    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()
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 #8
0
def test_problem_definition_with_xml_ref_run_optim(cleanup):
    """
    Tests what happens when writing inputs using data from existing XML file
    and running an optimization problem
    """
    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_run_optim")
        conf.input_file_path = pth.join(result_folder_path, "inputs.xml")
        input_data = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
        conf.write_needed_inputs(input_data)

        # Runs optimization problem with semi-analytic FD
        problem1 = conf.get_problem(read_inputs=True)
        problem1.setup()
        problem1.run_model()
        assert problem1["f"] == pytest.approx(28.58830817, abs=1e-6)
        problem1.run_driver()
        assert problem1["f"] == pytest.approx(3.18339395, abs=1e-6)
        problem1.output_file_path = pth.join(result_folder_path,
                                             "outputs_1.xml")
        problem1.write_outputs()

        # Runs optimization problem with monolithic FD
        problem2 = conf.get_problem(read_inputs=True)
        problem2.model.approx_totals()
        problem2.setup()
        problem2.run_model()  # checks problem has been reset
        assert problem2["f"] == pytest.approx(28.58830817, abs=1e-6)
        problem2.run_driver()
        assert problem2["f"] == pytest.approx(3.18339395, abs=1e-6)
        problem2.output_file_path = pth.join(result_folder_path,
                                             "outputs_2.xml")
        problem2.write_outputs()
def run_non_regression_test(
    conf_file,
    legacy_result_file,
    result_dir,
    use_xfoil=False,
    vars_to_check=None,
    tolerance=5.0e-3,
    check_weight_perfo_loop=True,
):
    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 that weight-performances loop correctly converged
        assert_allclose(
            problem["data:weight:aircraft:OWE"],
            problem["data:weight:airframe:mass"] +
            problem["data:weight:propulsion:mass"] +
            problem["data:weight:systems:mass"] +
            problem["data:weight:furniture:mass"] +
            problem["data:weight:crew:mass"],
            atol=1,
        )
        assert_allclose(
            problem["data:weight:aircraft:MZFW"],
            problem["data:weight:aircraft:OWE"] +
            problem["data:weight:aircraft:max_payload"],
            atol=1,
        )
        assert_allclose(
            problem["data:weight:aircraft:MTOW"],
            problem["data:weight:aircraft:OWE"] +
            problem["data:weight:aircraft:payload"] +
            problem["data:mission:sizing:needed_block_fuel"],
            atol=1,
        )

    ref_var_list = VariableIO(pth.join(DATA_FOLDER_PATH,
                                       legacy_result_file), ).read()

    row_list = []
    for ref_var in ref_var_list:
        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:
            row = df.loc[df.name == name]
            assert_allclose(row.ref_value, row.value, rtol=tolerance)
            # assert np.all(df.abs_rel_delta.loc[df.name == name] < tolerance)
    else:
        assert np.all(df.abs_rel_delta < tolerance)
def test_problem_definition(cleanup):
    """ Test conf definition from configuration files """

    # no input file
    conf = FASTOADProblemConfigurator()
    with pytest.raises(FASTConfigurationError) as exc_info:
        conf.load(
            pth.join(pth.dirname(__file__), "data", "missing_input_file.toml"))
    assert exc_info.value.missing_key == KEY_INPUT_FILE

    # no output file
    conf = FASTOADProblemConfigurator()
    with pytest.raises(FASTConfigurationError) as exc_info:
        conf.load(
            pth.join(pth.dirname(__file__), "data",
                     "missing_output_file.toml"))
    assert exc_info.value.missing_key == KEY_OUTPUT_FILE

    # Missing model definition
    conf = FASTOADProblemConfigurator()
    with pytest.raises(FASTConfigurationError) as exc_info:
        conf.load(pth.join(pth.dirname(__file__), "data",
                           "missing_model.toml"))
    assert exc_info.value.missing_section == TABLE_MODEL

    # Incorrect attribute
    conf = FASTOADProblemConfigurator()
    conf.load(pth.join(pth.dirname(__file__), "data",
                       "invalid_attribute.toml"))
    with pytest.raises(
            FASTConfigurationBadOpenMDAOInstructionError) as exc_info:
        problem = conf.get_problem(read_inputs=False)
    assert exc_info.value.key == "model.cycle.other_group.nonlinear_solver"

    # Reading of a minimal conf (model = ExplicitComponent)
    conf = FASTOADProblemConfigurator()
    conf.load(pth.join(pth.dirname(__file__), "data", "disc1.toml"))
    problem = conf.get_problem(read_inputs=False)
    assert isinstance(problem.model.system, om.ExplicitComponent)

    # Reading of correct conf definition
    conf = FASTOADProblemConfigurator()
    conf.load(pth.join(pth.dirname(__file__), "data", "valid_sellar.toml"))
    assert conf.input_file_path == pth.join(RESULTS_FOLDER_PATH, "inputs.xml")
    assert conf.output_file_path == pth.join(RESULTS_FOLDER_PATH,
                                             "outputs.xml")

    # Just running these methods to check there is no crash. As simple assemblies of
    # other methods, their results should already be unit-tested.
    conf.write_needed_inputs()
    problem = conf.get_problem(read_inputs=True)

    problem.setup()
    assert isinstance(problem.model.cycle, om.Group)
    assert isinstance(problem.model.cycle.disc1, om.ExplicitComponent)
    assert isinstance(problem.model.cycle.disc2, om.ExplicitComponent)
    assert isinstance(problem.model.functions, om.ExplicitComponent)

    assert isinstance(problem.driver, om.ScipyOptimizeDriver)
    assert problem.driver.options["optimizer"] == "SLSQP"
    assert isinstance(problem.model.cycle.nonlinear_solver,
                      om.NonlinearBlockGS)

    problem.run_driver()

    problem.run_model()
    assert np.isnan(problem["f"])
Example #11
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)
Example #12
0
def run_non_regression_test(
    conf_file,
    legacy_result_file,
    result_dir,
    use_xfoil=False,
    check_only_mtow=False,
    tolerance=5.0e-3,
):
    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)
    problem = FASTOADProblemConfigurator(configuration_file_path).get_problem()

    # Next trick is needed for overloading option setting from TOML file
    if use_xfoil and (system() == "Windows" or xfoil_path):
        problem.model.aerodynamics_landing._OPTIONS["use_xfoil"] = True
        if system() != "Windows":
            problem.model.aerodynamics_landing._OPTIONS[
                "xfoil_exe_path"] = xfoil_path
        # BTW we narrow computed alpha range for sake of CPU time
        problem.model.aerodynamics_landing._OPTIONS["xfoil_alpha_min"] = 18.0
        problem.model.aerodynamics_landing._OPTIONS["xfoil_alpha_max"] = 22.0

    # Generation and reading of inputs ----------------------------------------
    ref_inputs = pth.join(DATA_FOLDER_PATH, legacy_result_file)
    problem.write_needed_inputs(ref_inputs, VariableLegacy1XmlFormatter())
    problem.read_inputs()
    problem.setup()

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

    try:
        problem.model.performance.flight_points.to_csv(
            pth.join(results_folder_path, "flight_points.csv"),
            sep="\t",
            decimal=",",
        )
    except AttributeError:
        pass
    om.view_connections(problem,
                        outfile=pth.join(results_folder_path,
                                         "connections.html"),
                        show_browser=False)

    # Check that weight-performances loop correctly converged
    assert_allclose(
        problem["data:weight:aircraft:OWE"],
        problem["data:weight:airframe:mass"] +
        problem["data:weight:propulsion:mass"] +
        problem["data:weight:systems:mass"] +
        problem["data:weight:furniture:mass"] +
        problem["data:weight:crew:mass"],
        atol=1,
    )
    assert_allclose(
        problem["data:weight:aircraft:MZFW"],
        problem["data:weight:aircraft:OWE"] +
        problem["data:weight:aircraft:max_payload"],
        atol=1,
    )
    # assert_allclose(
    #     problem["data:weight:aircraft:MTOW"],
    #     problem["data:weight:aircraft:OWE"]
    #     + problem["data:weight:aircraft:payload"]
    #     + problem["data:mission:sizing:fuel"],
    #     atol=1,
    # )

    ref_var_list = VariableIO(
        pth.join(DATA_FOLDER_PATH, legacy_result_file),
        formatter=VariableLegacy1XmlFormatter(),
    ).read()

    row_list = []
    for ref_var in ref_var_list:
        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 check_only_mtow:
        assert np.all(df.abs_rel_delta.loc[
            df.name == "data:weight:aircraft:MTOW"] < tolerance)
    else:
        assert np.all(df.abs_rel_delta < tolerance)