/* SPDX-FileCopyrightText: Copyright (c) 2021 - 2026, the adamantine authors. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #define BOOST_TEST_MODULE validate_input_database #include #include "main.cc" namespace adamantine { BOOST_AUTO_TEST_CASE(expected_passes) { boost::property_tree::ptree database; // Bare-bones required inputs database.put("boundary.type", "adiabatic"); database.put("discretization.thermal.fe_degree", "1"); database.put("geometry.dim", "3"); database.put("discretization.thermal.quadrature", "gauss"); database.put("geometry.import_mesh", "false"); database.put("geometry.length", 1.0); database.put("geometry.height", 1.0); database.put("geometry.width", 1.0); database.put("materials.n_materials", 1); database.put("materials.property_format", "polynomial"); database.put("materials.material_0.solid.thermal_conductivity_x", 10.); database.put("materials.material_0.solid.thermal_conductivity_y", 10.); database.put("materials.material_0.solid.thermal_conductivity_z", 10.); database.put("materials.material_0.solid.density", 10.); database.put("materials.material_0.solid.specific_heat", 10.); database.put("physics.thermal", true); database.put("physics.mechanical", false); database.put("post_processor.filename_prefix", "output"); database.put("refinement.n_heat_refinements", 0); database.put("sources.n_beams", 1); database.put("sources.beam_0.type", "goldak"); database.put("sources.beam_0.scan_path_file", "sp.txt"); database.put("sources.beam_0.scan_path_file_format", "segment"); database.put("sources.beam_0.depth", 0.1); database.put("sources.beam_0.absorption_efficiency", 0.1); database.put("time_stepping.method", "forward_euler"); database.put("time_stepping.duration", 1.0); database.put("time_stepping.time_step", 0.1); validate_input_database(database); database.put("sources.beam_0.type", "gaussian"); database.put("sources.beam_0.A", 1.0); database.put("sources.beam_0.B", 1.0); validate_input_database(database); } BOOST_AUTO_TEST_CASE(parse_test_input_files) { boost::property_tree::ptree database; std::string filename = "bare_plate_L_da.info"; boost::property_tree::info_parser::read_info(filename, database); validate_input_database(database); filename = "bare_plate_L_ensemble.info"; boost::property_tree::info_parser::read_info(filename, database); validate_input_database(database); filename = "demo_316_short.info"; boost::property_tree::info_parser::read_info(filename, database); validate_input_database(database); filename = "integration_2d.info"; boost::property_tree::info_parser::read_info(filename, database); validate_input_database(database); } BOOST_AUTO_TEST_CASE(expected_failures) { boost::property_tree::ptree database; // Start with a valid set of bare-bones inputs database.put("boundary.type", "adiabatic,clamped"); database.put("discretization.thermal.fe_degree", "1"); database.put("geometry.dim", "3"); database.put("discretization.thermal.quadrature", "gauss"); database.put("geometry.import_mesh", "false"); database.put("geometry.length", 1.0); database.put("geometry.height", 1.0); database.put("geometry.width", 1.0); database.put("materials.n_materials", 1); database.put("materials.property_format", "polynomial"); database.put("materials.material_0.solid.thermal_conductivity_x", 10.); database.put("materials.material_0.solid.thermal_conductivity_y", 10.); database.put("materials.material_0.solid.thermal_conductivity_z", 10.); database.put("materials.material_0.solid.density", 10.); database.put("materials.material_0.solid.specific_heat", 10.); database.put("physics.thermal", true); database.put("physics.mechanical", true); database.put("post_processor.filename_prefix", "output"); database.put("refinement.n_heat_refinements", 0); database.put("sources.n_beams", 1); database.put("sources.beam_0.type", "goldak"); database.put("sources.beam_0.scan_path_file", "sp.txt"); database.put("sources.beam_0.scan_path_file_format", "segment"); database.put("sources.beam_0.depth", 0.1); database.put("sources.beam_0.absorption_efficiency", 0.1); database.put("time_stepping.method", "forward_euler"); database.put("time_stepping.duration", 1.0); database.put("time_stepping.time_step", 0.1); // The base database (this one should be valid) validate_input_database(database); // We purposefully reset the database fully after each check to make the // checks independent. Even though carrying some state between checks might // reduce the number of lines, the increase in complexity isn't worth it. // Invalid BC combination database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic,convective,traction_free"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic,clamped,traction_free"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic,traction_free"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("boundary.boundary_1.type", "adiabatic,clamped"); validate_input_database(database); // Invalid fe degree database.get_child("discretization").erase("thermal.fe_degree"); database.put("discretization.thermal.fe_degree", 11); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("discretization").erase("thermal.fe_degree"); database.put("discretization.thermal.fe_degree", 1); // Invalid quadrature type database.get_child("discretization").erase("thermal.quadrature"); database.put("discretization.thermal.quadrature", "gass"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("discretization").erase("thermal.quadrature"); database.put("discretization.thermal.quadrature", "gauss"); // Invalid number of dimensions database.get_child("geometry").erase("dim"); database.put("geometry.dim", 1); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("dim"); database.put("geometry.dim", 11); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("dim"); database.put("geometry.dim", 3); // 'use_powder' true, but no 'powder_layer' database.get_child("geometry").erase("use_powder"); database.put("geometry.use_powder", true); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("use_powder"); database.put("geometry.use_powder", false); // 'material_deposition' true, but invalid 'material_deposition_method' database.get_child("geometry").erase("material_deposition"); database.put("geometry.material_deposition", true); database.put("geometry.material_deposition_method", "ffile"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("material_deposition"); database.get_child("geometry").erase("material_deposition_method"); // 'material_deposition' true, but missing deposition inputs database.get_child("geometry").erase("material_deposition_method"); database.put("geometry.material_deposition", true); database.put("geometry.material_deposition_method", "scan_paths"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.deposition_length", 0.1); database.put("geometry.deposition_height", 0.1); database.put("geometry.deposition_width", 0.1); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("deposition_length"); database.put("geometry.lead_time", 0.1); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("deposition_height"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.height", 0.1); database.get_child("geometry").erase("deposition_width"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("material_deposition"); database.get_child("geometry").erase("material_deposition_method"); database.get_child("geometry").erase("deposition_length"); database.get_child("geometry").erase("deposition_height"); database.get_child("geometry").erase("deposition_width"); database.get_child("geometry").erase("lead_time"); // 'import_mesh' true, but missing mesh reading inputs database.get_child("geometry").erase("import_mesh"); database.put("geometry.import_mesh", true); database.put("geometry.mesh_file", "mesh.vtk"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("mesh_file"); database.put("geometry.mesh_format", "vtk"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("import_mesh"); database.put("geometry.import_mesh", false); database.get_child("geometry").erase("mesh_file"); database.get_child("geometry").erase("mesh_format"); // 'import_mesh' false, missing inputs database.get_child("geometry").erase("length"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.length", 10.0); database.get_child("geometry").erase("height"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.height", 10.0); database.get_child("geometry").erase("width"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.width", 10.0); // 'n_materials' missing database.get_child("materials").erase("n_materials"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.n_materials", 1); // Invalid material properties format database.get_child("materials").erase("property_format"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.property_format", "ppoly"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.property_format", "polynomial"); // Non-consecutive materials database.get_child("materials").erase("n_materials"); database.put("materials.n_materials", 2); database.put("materials.material_2.solid.thermal_conductivity_x", 10.); database.put("materials.material_2.solid.thermal_conductivity_y", 10.); database.put("materials.material_2.solid.thermal_conductivity_z", 10.); database.put("materials.material_2.solid.density", 10.); database.put("materials.material_2.solid.specific_heat", 10.); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("materials").erase("n_materials"); database.put("materials.n_materials", 1); database.get_child("materials") .get_child("material_2") .get_child("solid") .erase("thermal_conductivity_x"); database.get_child("materials") .get_child("material_2") .get_child("solid") .erase("thermal_conductivity_y"); database.get_child("materials") .get_child("material_2") .get_child("solid") .erase("thermal_conductivity_z"); database.get_child("materials") .get_child("material_2") .get_child("solid") .erase("density"); database.get_child("materials") .get_child("material_2") .get_child("solid") .erase("specific_heat"); // Check 13: Material without any phases database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_x"); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_y"); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_z"); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("density"); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("specific_heat"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.thermal_conductivity_x", 10.); database.put("materials.material_0.solid.thermal_conductivity_y", 10.); database.put("materials.material_0.solid.thermal_conductivity_z", 10.); database.put("materials.material_0.solid.density", 10.); database.put("materials.material_0.solid.specific_heat", 10.); // Missing always required material properties database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_x"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.thermal_conductivity_x", 10.); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_y"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.thermal_conductivity_y", 10.); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("thermal_conductivity_z"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.thermal_conductivity_z", 10.); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("density"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.density", 10.); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("specific_heat"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("materials.material_0.solid.specific_heat", 10.); // Missing required material properties with convective BCs database.get_child("boundary").erase("type"); database.put("boundary.type", "convective,traction_free"); database.put("materials.material_0.solid.convection_heat_transfer_coef", 10.); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("convection_heat_transfer_coef"); database.put("materials.material_0.convection_temperature_infty", 10.); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("convection_temperature_infty"); database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic,traction_free"); // Missing required material properties with radiative BCs database.get_child("boundary").erase("type"); database.put("boundary.type", "radiative,traction_free"); database.put("materials.material_0.solid.emissivity", 10.); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("emissivity"); database.put("materials.material_0.radiation_temperature_infty", 10.); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("materials") .get_child("material_0") .get_child("solid") .erase("radiation_temperature_infty"); database.get_child("boundary").erase("type"); database.put("boundary.type", "adiabatic,clamped"); // Invalid memory space database.erase("memory_space"); database.put("memory_space", "hhost"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("memory_space"); // Missing thermal physics database.get_child("physics").erase("thermal"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("physics.thermal", false); // Missing mechanical physics database.get_child("physics").erase("mechanical"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("physics.mechanical", false); // No physics is enabled BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("physics").erase("thermal"); database.put("physics.thermal", true); database.get_child("physics").erase("mechanical"); database.put("physics.mechanical", true); // Invalid units database.put("units.mesh", "light-year"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("units"); database.put("units.heat_source.power", "BTU/hour"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("units"); database.put("units.heat_source.velocity", "inch/second"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("units"); database.put("units.heat_source.dimension", "angstrom"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("units"); database.put("units.heat_source.scan_path", "rod"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.erase("units"); // Missing postprocessor filename database.get_child("post_processor").erase("filename_prefix"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("post_processor.filename_prefix", "output"); // Missing refinement block database.get_child("refinement").erase("n_heat_refinements"); database.erase("refinement"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("refinement.n_heat_refinements", 0); // Missing 'n_beams' database.get_child("sources").erase("n_beams"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.n_beams", 1); // Missing heat source type database.get_child("sources").get_child("beam_0").erase("type"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.type", "goldak"); // Invalid heat source type database.get_child("sources").get_child("beam_0").erase("type"); database.put("sources.beam_0.type", "gold"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.type", "goldak"); // Missing scan path file database.get_child("sources").get_child("beam_0").erase("scan_path_file"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.scan_path_file", "sp.txt"); // Missing scan path file format database.get_child("sources").get_child("beam_0").erase( "scan_path_file_format"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.scan_path_file_format", "segment"); // Invalid scan path file format database.get_child("sources").get_child("beam_0").erase( "scan_path_file_format"); database.put("sources.beam_0.scan_path_file_format", "seg"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.scan_path_file_format", "segment"); // Missing beam depth database.get_child("sources").get_child("beam_0").erase("depth"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.depth", 0.1); // Missing beam absorption efficiency database.get_child("sources").get_child("beam_0").erase( "absorption_efficiency"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("sources.beam_0.absorption_efficiency", 0.1); // Missing time stepping method database.get_child("time_stepping").erase("method"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("time_stepping.method", "forward_euler"); // Invalid time stepping method database.get_child("time_stepping").erase("method"); database.put("time_stepping.method", "4_step_jump"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("time_stepping.method", "forward_euler"); // Missing experimental inputs database.put("experiment.read_in_experimental_data", true); database.put("experiment.file", "file.csv"); database.put("experiment.last_frame", 1); database.put("experiment.first_camera_id", 0); database.put("experiment.last_camera_id", 1); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("experiment").erase("file"); database.put("experiment.log_filename", "log.txt"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("experiment").erase("last_frame"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("experiment.last_frame", 1); database.get_child("experiment").erase("first_camera_id"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("experiment.first_camera_id", 0); database.get_child("experiment").erase("last_camera_id"); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("experiment.first_camera_id", 0); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("experiment").erase("file"); database.get_child("experiment").erase("last_frame"); database.get_child("experiment").erase("first_camera_id"); database.get_child("experiment").erase("last_camera_id"); database.get_child("experiment").erase("read_in_experimental_data"); // Last experimental frame index smaller than first frame database.put("experiment.read_in_experimental_data", true); database.put("experiment.first_frame", 4); database.put("experiment.last_frame", 3); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("experiment").erase("last_frame"); database.get_child("experiment").erase("first_frame"); database.get_child("experiment").erase("read_in_experimental_data"); // Last experimental camera id smaller than first camera id database.put("experiment.read_in_experimental_data", true); database.put("experiment.first_camera_id", 4); database.put("experiment.last_camera_id", 3); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("experiment").erase("first_camera_id"); database.get_child("experiment").erase("last_camera_id"); database.get_child("experiment").erase("read_in_experimental_data"); // Incorrect number of entries for the experimental data column indices database.put("experiment.read_in_experimental_data", true); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.put("geometry.dim", 2); BOOST_CHECK_THROW(validate_input_database(database), std::runtime_error); database.get_child("geometry").erase("dim"); database.put("geometry.dim", 3); database.get_child("experiment").erase("read_in_experimental_data"); // This should be back to the base database (this should be valid) validate_input_database(database); } } // namespace adamantine