def test_problem_definition_no_model(cleanup):
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator()
        with pytest.raises(ValidationError) as exc_info:
            conf.load(pth.join(DATA_FOLDER_PATH, "missing_model.%s" % extension))
        assert exc_info.value.message == "'model' is a required property"
Example #2
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)
def test_problem_definition_correct_configuration(cleanup):
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator()
        conf.load(pth.join(DATA_FOLDER_PATH, "valid_sellar.%s" % extension))
        assert conf.input_file_path == pth.join(RESULTS_FOLDER_PATH, "inputs.xml")
        assert conf.output_file_path == pth.join(RESULTS_FOLDER_PATH, "outputs.xml")
def test_problem_definition_incorrect_attribute(cleanup):
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator()
        conf.load(pth.join(DATA_FOLDER_PATH, "invalid_attribute.%s" % extension))
        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"
Example #5
0
def test_problem_definition_with_nan_inputs(cleanup):
    """ Tests what happens when writing inputs using existing XML with some unwanted var"""
    conf = FASTOADProblemConfigurator(
        pth.join(DATA_FOLDER_PATH, "valid_sellar.toml"))

    input_data_path = pth.join(DATA_FOLDER_PATH, "nan_inputs.xml")
    os.makedirs(RESULTS_FOLDER_PATH, exist_ok=True)
    shutil.copy(input_data_path, conf.input_file_path)

    with pytest.raises(FASTConfigurationNanInInputFile) as exc:
        problem = conf.get_problem(read_inputs=True, auto_scaling=True)
        assert exc.input_file_path == input_data_path
        assert exc.nan_variable_names == ["x"]
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()
Example #8
0
def test_optimization_viewer_save(cleanup):
    """
    Basic tests for testing the OptimizationViewer save method.
    """
    filename = pth.join(DATA_FOLDER_PATH, "valid_sellar.toml")
    new_filename = pth.join(RESULTS_FOLDER_PATH, "new_valid_sellar.toml")
    copyfile(filename, new_filename)

    # Loading new file
    problem_configuration = FASTOADProblemConfigurator(filename)

    optim_viewer = OptimizationViewer()

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

    # Load new file
    optim_viewer.load(problem_configuration)
    optim_viewer.save()
    optim_viewer.load(problem_configuration)

    # We run the problem
    api.optimize_problem(new_filename, overwrite=True)
    optim_viewer.load(problem_configuration)
    optim_viewer.save()
    optim_viewer.load(problem_configuration)
Example #9
0
def test_optimization_viewer_load(cleanup):
    """
    Basic tests for testing the OptimizationViewer load method.
    """
    filename = pth.join(DATA_FOLDER_PATH, "valid_sellar.toml")

    # The problem has not yet been run
    problem_configuration = FASTOADProblemConfigurator(filename)

    optim_viewer = OptimizationViewer()

    # No input file exists
    with pytest.raises(FastMissingFile):
        optim_viewer.load(problem_configuration)

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

    # Input file exist
    optim_viewer.load(problem_configuration)

    # We run the problem
    api.optimize_problem(filename, overwrite=True)

    # Load the results
    optim_viewer.load(problem_configuration)
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()
def test_problem_definition_with_custom_xml(cleanup):
    """Tests what happens when writing inputs using existing XML with some unwanted var"""
    conf = FASTOADProblemConfigurator(pth.join(DATA_FOLDER_PATH, "valid_sellar.toml"))

    result_folder_path = pth.join(RESULTS_FOLDER_PATH, "problem_definition_with_custom_xml")
    conf.input_file_path = pth.join(result_folder_path, "inputs.xml")
    conf.output_file_path = pth.join(result_folder_path, "outputs.xml")

    input_data = pth.join(DATA_FOLDER_PATH, "ref_inputs.xml")
    os.makedirs(result_folder_path, exist_ok=True)
    shutil.copy(input_data, conf.input_file_path)

    problem = conf.get_problem(read_inputs=True, auto_scaling=True)
    problem.setup()
    problem.run_model()

    assert problem["f"] == pytest.approx(28.58830817, abs=1e-6)
    problem.write_outputs()
Example #12
0
def optimization_viewer(configuration_file_path: str):
    """
    Displays optimization information and enables its editing

    :param configuration_file_path: problem definition
    :return: display of the OptimizationViewer
    """
    problem_configuration = FASTOADProblemConfigurator(configuration_file_path)
    viewer = OptimizationViewer()
    viewer.load(problem_configuration)

    return viewer.display()
Example #13
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_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)
Example #15
0
def write_n2(configuration_file_path: str,
             n2_file_path: str = None,
             overwrite: bool = False):
    """
    Write the N2 diagram of the problem in file n2.html

    :param configuration_file_path:
    :param n2_file_path:
    :param overwrite:
    """

    if not n2_file_path:
        n2_file_path = pth.join(pth.dirname(configuration_file_path),
                                "n2.html")
    n2_file_path = pth.abspath(n2_file_path)

    if not overwrite and pth.exists(n2_file_path):
        raise FastFileExistsError(
            "N2-diagram file %s not written because it already exists. "
            "Use overwrite=True to bypass." % n2_file_path,
            n2_file_path,
        )

    make_parent_dir(n2_file_path)
    problem = FASTOADProblemConfigurator(configuration_file_path).get_problem()
    problem.setup()
    problem.final_setup()

    om.n2(problem, outfile=n2_file_path, show_browser=False)
    _LOGGER.info("N2 diagram written in %s", n2_file_path)
Example #16
0
def test_problem_definition_module_folder_as_one_string(cleanup):
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator()
        conf.load(
            pth.join(pth.dirname(__file__), "data",
                     "module_folder_as_one_string.%s" % extension))
        conf.get_problem()
def test_problem_definition_no_module_folder(cleanup):
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        conf = FASTOADProblemConfigurator()
        conf.load(pth.join(DATA_FOLDER_PATH, "no_module_folder.%s" % extension))
        with pytest.raises(FastBundleLoaderUnknownFactoryNameError) as exc_info:
            conf.get_problem()
        assert exc_info.value.factory_name == "configuration_test.sellar.functions"
Example #18
0
def test_optimization_viewer_display(cleanup):
    """
    Basic tests for testing the OptimizationViewer load method.
    """
    filename = pth.join(DATA_FOLDER_PATH, "valid_sellar.toml")

    # The problem has not yet been ran
    problem_configuration = FASTOADProblemConfigurator(filename)

    optim_viewer = OptimizationViewer()

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

    optim_viewer.load(problem_configuration)
    optim_viewer.display()
def test_set_optimization_definition(cleanup):
    """
    Tests the modification of the optimization definition in the configuration file
    """
    for extension in ["toml", "yml"]:
        clear_openmdao_registry()
        reference_file = pth.join(DATA_FOLDER_PATH, "valid_sellar.%s" % extension)
        editable_file = pth.join(RESULTS_FOLDER_PATH, "editable_valid_sellar.%s" % extension)

        conf = FASTOADProblemConfigurator(reference_file)

        optimization_def = {
            "design_variables": {
                "x": {"name": "x", "lower": 0, "upper": 20},
                "z": {"name": "z", "lower": 0, "upper": 10},
            },
            "constraints": {
                "gg1": {"name": "gg1", "upper": 10},
                "gg2": {"name": "gg2", "upper": 0},
            },
            "objective": {"f": {"name": "f"}},
        }

        optimization_conf = {
            "design_variables": [
                {"name": "x", "lower": 0, "upper": 20},
                {"name": "z", "lower": 0, "upper": 10},
            ],
            "constraints": [{"name": "gg1", "upper": 10}, {"name": "gg2", "upper": 0}],
            "objective": [{"name": "f"}],
        }

        read = tomlkit.loads if extension == "toml" else YAML(typ="safe").load

        with open(reference_file, "r") as file:
            d = file.read()
            conf_dict = read(d)
        conf_dict_opt = conf_dict["optimization"]
        # Should be different
        assert optimization_conf != conf_dict_opt

        conf.set_optimization_definition(optimization_def)
        conf.save(editable_file)
        with open(editable_file, "r") as file:
            d = file.read()
            conf_dict = read(d)
        conf_dict_opt = conf_dict["optimization"]
        # Should be equal
        assert optimization_conf == conf_dict_opt
Example #20
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()
Example #21
0
def test_oad_process(cleanup):
    """
    Test for the overall aircraft design process.
    """

    problem = FASTOADProblemConfigurator(pth.join(INPUT_FOLDER_PATH, "oad_process.toml")).get_problem()
    problem.model.aicraft.set_input_defaults('data:geometry:horizontal_tail:sweep_25', val=10., units='deg')
    ref_inputs = pth.join(INPUT_FOLDER_PATH, XML_NAME)
    get_problem_after_setup(problem).write_needed_inputs(ref_inputs, VariableXmlStandardFormatter())
    problem.read_inputs()
    print('\n')
    problem.setup(check=True)
    problem.set_solver_print(level=2)
    with Timer(name="Mass-performance loop:"):
        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"],
        rtol=5e-2,
    )
    assert_allclose(
        problem["data:weight:aircraft:MZFW"],
        problem["data:weight:aircraft:OWE"] + problem["data:weight:aircraft:max_payload"],
        rtol=5e-2,
    )
    assert_allclose(
        problem["data:weight:aircraft:MTOW"],
        problem["data:weight:aircraft:OWE"]
        + problem["data:weight:aircraft:max_payload"]
        + problem["data:mission:sizing:fuel"],
        rtol=5e-2,
    )

    assert_allclose(problem.get_val("data:mission:sizing:fuel", units="kg"), 203.65, atol=1)
    # noinspection PyTypeChecker
    assert_allclose(problem.get_val("data:weight:aircraft:max_payload", units="kg"), 400.0, atol=1)
    # noinspection PyTypeChecker
    assert_allclose(problem["data:handling_qualities:static_margin"], 0.08515, atol=1e-2)
    # noinspection PyTypeChecker
    assert_allclose(problem.get_val("data:weight:aircraft:MTOW", units="kg"), 1650.24, atol=1)
    # noinspection PyTypeChecker
    assert_allclose(problem.get_val("data:weight:aircraft:payload", units="kg"), 360., atol=1)
    # noinspection PyTypeChecker
    assert_allclose(problem.get_val("data:weight:aircraft:OWE", units="kg"), 1046.61, atol=1)
    # noinspection PyTypeChecker
    assert_allclose(problem.get_val("data:mission:sizing:main_route:cruise:fuel", units="kg"), 166.405, atol=1)
def test_set_optimization_definition(cleanup):
    """
    Tests the modification of the optimization definition in the .toml
    configuration file
    """
    reference_file = pth.join(DATA_FOLDER_PATH, "valid_sellar.toml")
    editable_file = pth.join(RESULTS_FOLDER_PATH, "editable_valid_sellar.toml")

    # copy(reference_file, editable_file)

    conf = FASTOADProblemConfigurator(reference_file)

    optimization_def = {
        "design_var": {
            "x": {
                "name": "x",
                "lower": 0,
                "upper": 20
            },
            "z": {
                "name": "z",
                "lower": 0,
                "upper": 10
            },
        },
        "constraint": {
            "gg1": {
                "name": "gg1",
                "upper": 10
            },
            "gg2": {
                "name": "gg2",
                "upper": 0
            },
        },
        "objective": {
            "f": {
                "name": "f"
            }
        },
    }

    optimization_conf = {
        "design_var": [
            {
                "name": "x",
                "lower": 0,
                "upper": 20
            },
            {
                "name": "z",
                "lower": 0,
                "upper": 10
            },
        ],
        "constraint": [{
            "name": "gg1",
            "upper": 10
        }, {
            "name": "gg2",
            "upper": 0
        }],
        "objective": [{
            "name": "f"
        }],
    }

    with open(reference_file, "r") as file:
        d = file.read()
        conf_dict = tomlkit.loads(d)
    conf_dict_opt = conf_dict["optimization"]
    # Should be different
    assert optimization_conf != conf_dict_opt

    conf.set_optimization_definition(optimization_def)
    conf.save(editable_file)
    with open(editable_file, "r") as file:
        d = file.read()
        conf_dict = tomlkit.loads(d)
    conf_dict_opt = conf_dict["optimization"]
    # Should be equal
    assert optimization_conf == conf_dict_opt
Example #23
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 #24
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 #25
0
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)
    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)
    get_problem_after_setup(problem).write_needed_inputs(ref_inputs, VariableLegacy1XmlFormatter())
    problem.read_inputs()
    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: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 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)
Example #26
0
def test_oad_process(cleanup):
    """
    Test the overall aircraft design process without and with optimization.
    """
    test = FASTOADProblemConfigurator(
        pth.join(INPUT_FOLDER_PATH, "oad_process.toml"))
    problem = FASTOADProblemConfigurator(
        pth.join(INPUT_FOLDER_PATH, "oad_process.toml")).get_problem()
    recorder = om.SqliteRecorder("cases.sql")

    ref_inputs = pth.join(INPUT_FOLDER_PATH, XML_NAME)
    get_problem_after_setup(problem).write_needed_inputs(
        ref_inputs, VariableXmlStandardFormatter())
    problem.read_inputs()
    print('\n')
    problem.setup(check=True)
    solver = problem.model.nonlinear_solver
    solver.add_recorder(recorder)
    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"],
        rtol=5e-2,
    )
    assert_allclose(
        problem["data:weight:aircraft:MZFW"],
        problem["data:weight:aircraft:OWE"] +
        problem["data:weight:aircraft:max_payload"],
        rtol=5e-2,
    )
    assert_allclose(
        problem["data:weight:aircraft:MTOW"],
        problem["data:weight:aircraft:OWE"] +
        problem["data:weight:aircraft:payload"] +
        problem["data:mission:sizing:fuel"],
        rtol=5e-2,
    )

    if XML_NAME == "cirrus_sr22.xml":
        assert_allclose(problem.get_val("data:mission:sizing:fuel",
                                        units="kg"),
                        258.831,
                        atol=1)
        # noinspection PyTypeChecker
        assert_allclose(
            problem["data:handling_qualities:stick_fixed_static_margin"],
            0.0728,
            atol=1e-2)
        # noinspection PyTypeChecker
        assert_allclose(problem.get_val("data:weight:aircraft:MTOW",
                                        units="kg"),
                        1629.7406025,
                        atol=1)
        # noinspection PyTypeChecker
        assert_allclose(problem.get_val("data:weight:aircraft:OWE",
                                        units="kg"),
                        1030.9167,
                        atol=1)
    else:
        assert_allclose(problem.get_val("data:mission:sizing:fuel",
                                        units="kg"),
                        228.624,
                        atol=1)
        # noinspection PyTypeChecker
        assert_allclose(
            problem["data:handling_qualities:stick_fixed_static_margin"],
            0.0252,
            atol=1e-2)
        # noinspection PyTypeChecker
        assert_allclose(problem.get_val("data:weight:aircraft:MTOW",
                                        units="kg"),
                        1678.863295,
                        atol=1)
        # noinspection PyTypeChecker
        assert_allclose(problem.get_val("data:weight:aircraft:OWE",
                                        units="kg"),
                        1090.2524,
                        atol=1)
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 #28
0
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 load(
        self,
        problem_configuration: FASTOADProblemConfigurator,
    ):
        """
        Loads the FAST-OAD problem and stores its data.

        :param problem_configuration: the FASTOADProblem instance.
        :param file_formatter: the formatter that defines file format. If not provided,
               default format will be assumed.
        """

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

        if pth.isfile(problem.output_file_path):
            output_variables = VariableIO(problem.output_file_path).read()
        else:
            output_variables = VariableList.from_problem(problem)

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

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

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

        self.load_variables(optimization_variables)
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.