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()
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 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.
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"])
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)
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)