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