Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ if(CORAL_BUILD_BACKEND_DEALII AND NOT CORAL_BUILD_SHARED_CORE)
endif()

if(CORAL_BUILD_TESTS)
# We intentionally run GoogleTest executables directly (faster than ctest).
# Do not call enable_testing() so CMake doesn't generate its own `test` target.
add_custom_target(test)
# Enable CTest for comprehensive test discovery
enable_testing()
# Custom target for running all tests via custom targets (faster than ctest)
# Note: "test" is reserved by CTest, so we use "run_all_tests"
add_custom_target(run_all_tests)
endif()

add_subdirectory(core)
Expand All @@ -31,7 +33,7 @@ endif()

if(CORAL_BUILD_TESTS)
add_subdirectory(core/tests)
if(TARGET test AND TARGET run_coral_core_tests)
add_dependencies(test run_coral_core_tests)
if(TARGET run_all_tests AND TARGET run_coral_core_tests)
add_dependencies(run_all_tests run_coral_core_tests)
endif()
endif()
8 changes: 4 additions & 4 deletions backends/dealii/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ endif()

if(CORAL_BUILD_TESTS)
add_subdirectory(tests)
if(TARGET test AND TARGET run_dealii_backend_tests)
add_dependencies(test run_dealii_backend_tests)
if(TARGET run_all_tests AND TARGET run_dealii_backend_tests)
add_dependencies(run_all_tests run_dealii_backend_tests)
endif()
if(TARGET test AND TARGET run_dealii_plugin_tests)
add_dependencies(test run_dealii_plugin_tests)
if(TARGET run_all_tests AND TARGET run_dealii_plugin_tests)
add_dependencies(run_all_tests run_dealii_plugin_tests)
endif()
endif()
6 changes: 5 additions & 1 deletion backends/dealii/include/poisson.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class PoissonSolver
, neumann_boundary_ids(neumann_boundary_ids)
, neumann_function_expression(neumann_function_expression)
, dof_handler(triangulation)
{}

void
solve()
{
dof_handler.distribute_dofs(fe);

Expand Down Expand Up @@ -128,4 +132,4 @@ class PoissonSolver
};


#endif
#endif
27 changes: 27 additions & 0 deletions backends/dealii/include/register_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ namespace coral
inline void
register_dimensional_types()
{
// When dim == spacedim, create aliases to handle both forms:
// - Static linking produces: Type<2>
// - Shared library produces: Type<2, 2>
// This ensures JSON compatibility between plugin and test builds
if constexpr (dim == spacedim)
{
const std::string dims_short = std::to_string(dim);
const std::string dims_full = dims_short + ", " + std::to_string(spacedim);

// Alias for all dimensional types
coral::detail::set_type_alias<Triangulation<dim, spacedim>>(
"dealii::Triangulation<" + dims_full + ">");
coral::detail::set_type_alias<FiniteElement<dim, spacedim>>(
"dealii::FiniteElement<" + dims_full + ">");
coral::detail::set_type_alias<FE_Q<dim, spacedim>>(
"dealii::FE_Q<" + dims_full + ">");
coral::detail::set_type_alias<DoFHandler<dim, spacedim>>(
"dealii::DoFHandler<" + dims_full + ">");
coral::detail::set_type_alias<PoissonSolver<dim, spacedim>>(
"PoissonSolver<" + dims_full + ">");
}

NodeObject::register_type<Triangulation<dim, spacedim>>();
NodeObject::register_type<DoFHandler<dim, spacedim>,
Triangulation<dim, spacedim>>("triangulation");
Expand Down Expand Up @@ -136,6 +158,11 @@ namespace coral
"dirichlet_function_expression",
"neumann_boundary_ids",
"neumann_function_expression"}});

NodeObject::register_method<PoissonSolver<dim, spacedim>, void>(
&PoissonSolver<dim, spacedim>::solve,
{"PoissonSolver::solve<" + Utilities::dim_string(dim, spacedim) + ">",
"poisson_solver"});
}


Expand Down
10 changes: 10 additions & 0 deletions backends/dealii/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ add_custom_target(run_dealii_backend_tests
DEPENDS dealii_backend_tests
USES_TERMINAL)

# Register backend tests with CTest
gtest_discover_tests(dealii_backend_tests
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
PROPERTIES LABELS "dealii;backend")

add_executable(dealii_plugin_tests ${_plugin_test_file})
target_link_libraries(dealii_plugin_tests coral_core ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
if(GTEST_INCLUDE_DIRS)
Expand All @@ -64,3 +69,8 @@ add_custom_target(run_dealii_plugin_tests
COMMAND $<TARGET_FILE:dealii_plugin_tests>
DEPENDS dealii_plugin_tests coral_backend_dealii
USES_TERMINAL)

# Register plugin tests with CTest
gtest_discover_tests(dealii_plugin_tests
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
PROPERTIES LABELS "dealii;plugin")
130 changes: 130 additions & 0 deletions backends/dealii/tests/dealii_examples.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,80 @@ using namespace dealii;
using namespace coral;
using coral_test::ScopedTestOutputDir;

// Function to sum all numbers in a set
unsigned int
sum_set(const std::set<unsigned int> &input_set)
{
unsigned int sum = 0;
for (const auto &val : input_set)
sum += val;
return sum;
}

TEST(dealiiExamples, SumSetRegistry)
{
ScopedTestOutputDir output_dir("dealiiExamples_SumSetRegistry");

// Register the types
NodeObject::register_elementary_type<unsigned int>();
NodeObject::register_elementary_type<std::set<unsigned int>>();

// Register the function
NodeObject::register_function(sum_set, {"sum_set", "result", "input_set"});

// Dump the registry to a file
std::ofstream registry_file(output_dir.path() / "registry.json");
registry_file << NodeObject::get_registry().dump(2) << std::endl;
registry_file.close();

// Check that the file exists
std::ifstream check_file(output_dir.path() / "registry.json");
ASSERT_TRUE(check_file.good());
check_file.close();

// Load and execute the network from SetSum.json
Network network;
network.set_touch_file_base_path(output_dir);
network.clear_network();

std::ifstream json_file(SOURCE_DIR "/test_files/SetSum.json");
ASSERT_TRUE(json_file.is_open()) << "Failed to open SetSum.json file.";

nlohmann::json json_data;
json_file >> json_data;
json_file.close();

ASSERT_FALSE(json_data.empty()) << "JSON data is empty.";

// Load the network from JSON
network.from_json(json_data);

// Print debugging information
slog_debug("Network has %u nodes and %u connections",
network.n_nodes(),
network.n_connections());

// Run the network
network.run();

// Get the function node (node 1)
auto function_node = network.get_node(1);
ASSERT_TRUE(function_node != nullptr) << "Node 1 not found.";

// For non-void functions, the result is stored in the output of the function
// node
slog_debug("Function node has %zu outputs", function_node->n_outputs());

auto result_node = function_node->get_output(0);
ASSERT_TRUE(result_node != nullptr) << "Function output not found.";
ASSERT_TRUE(result_node->ready()) << "Result node not ready.";

// The result should be 15 (sum of 1 + 2 + 3 + 4 + 5)
unsigned int result = result_node->get<unsigned int>();
slog_debug("Function output is: %u.", result);
ASSERT_EQ(15u, result) << "Expected sum to be 15, got " << result;
}

// Void function test
TEST(dealiiExamples, step01)
{
Expand Down Expand Up @@ -321,8 +395,64 @@ TEST(dealiiExamples, PoissonSolver)

ASSERT_TRUE((*poisson)());
ASSERT_TRUE(poisson->ready());

// Create a method node for solve() and invoke it using coral machinery
auto solve_method = make_node("PoissonSolver::solve<2>");
solve_method->set_arguments({poisson});
(*solve_method)();

// check that the output file was created
std::ifstream file(output_dir.path() / "solution.vtu");
ASSERT_TRUE(file.good());
file.close();
}

TEST(dealiiExamples, NetworkFromJsonPoissonSolverSolution)
{
ScopedTestOutputDir output_dir(
"dealiiExamples_NetworkFromJsonPoissonSolverSolution");

register_non_dimensional_types();
register_dimensional_types<2, 2>();

Network network;
network.set_touch_file_base_path(output_dir);
network.clear_network();

std::ifstream file(SOURCE_DIR "/test_files/PoissonSolverSolution.json");
ASSERT_TRUE(file.is_open())
<< "Failed to open PoissonSolverSolution.json file.";

nlohmann::json json_data;
file >> json_data;
file.close();

ASSERT_FALSE(json_data.empty()) << "JSON data is empty.";

// Update the output file path to use the test output directory
// Node 9 contains the output filename
if (json_data["workflow"]["nodes"].contains("9") &&
json_data["workflow"]["nodes"]["9"].contains("value"))
{
std::string output_filename =
json_data["workflow"]["nodes"]["9"]["value"];
json_data["workflow"]["nodes"]["9"]["value"] =
(output_dir.path() / output_filename).string();
}

network.from_json(json_data);

// Print some debugging information
slog_debug("Network has %u nodes and %u connections",
network.n_nodes(),
network.n_connections());

// Run the network
network.run();

// Check that the output file was created (assuming the JSON specifies
// solution.vtu)
std::ifstream solution_file(output_dir.path() / "solution.vtu");
ASSERT_TRUE(solution_file.good()) << "Solution VTU file was not created.";
solution_file.close();
}
3 changes: 3 additions & 0 deletions backends/dealii/tests/network.cc
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,9 @@ TEST(Network, DuplicateQualifiedIdFromFile)
json network_json;
input >> network_json;

// Register types so the network can be deserialized
coral::register_all_types();

// Attempting to load this network should throw DuplicateQualifiedIdException
coral::Network network;
ASSERT_THROW(
Expand Down
20 changes: 12 additions & 8 deletions core/source/backend_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ namespace
#if defined(_WIN32)
handle.handle = LoadLibraryA(path.string().c_str());
if (!handle.handle)
throw std::runtime_error("LoadLibrary failed for: " +
path.string());
throw std::runtime_error("LoadLibrary failed for: " + path.string());
#else
handle.handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL);
if (!handle.handle)
Expand All @@ -103,8 +102,9 @@ namespace

BackendPlugin() = default;

BackendPlugin(const BackendPlugin &) = delete;
BackendPlugin &operator=(const BackendPlugin &) = delete;
BackendPlugin(const BackendPlugin &) = delete;
BackendPlugin &
operator=(const BackendPlugin &) = delete;

BackendPlugin(BackendPlugin &&other) noexcept
{
Expand Down Expand Up @@ -372,9 +372,10 @@ main(int argc, char *argv[])
SlogCliConfig slog_cli;

fs::path plugin_path;
app.add_option("-p,--plugin",
plugin_path,
"Backend plugin path (.so/.dylib/.dll)")
app
.add_option("-p,--plugin",
plugin_path,
"Backend plugin path (.so/.dylib/.dll)")
->required()
->check(CLI::ExistingPath.description(""))
->type_name("PATH");
Expand Down Expand Up @@ -549,7 +550,10 @@ main(int argc, char *argv[])

if (dump_reg)
{
dump_registry(register_path, backend.name);
// FIXME: we should switch back to `dump_registry(register_path,
// backend.name)`
// when the FE will validate the `__backend_name` field.
dump_registry(register_path, "");
slog_info("Dumped registered nodes to %s.", register_path.c_str());
}

Expand Down
6 changes: 6 additions & 0 deletions core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ add_custom_target(run_coral_core_tests
DEPENDS coral_core_tests
USES_TERMINAL)

# Register tests with CTest using GoogleTest discovery
include(GoogleTest)
gtest_discover_tests(coral_core_tests
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
PROPERTIES LABELS "core")

Loading