diff --git a/config.cmake.in b/config.cmake.in index bf8b4882..b9b8fc91 100644 --- a/config.cmake.in +++ b/config.cmake.in @@ -30,6 +30,7 @@ if(@PCMS_ENABLE_OMEGA_H@) endif() include("${CMAKE_CURRENT_LIST_DIR}/pcms_utility-targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/pcms_transient-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/pcms_discretization-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/pcms_localization-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/pcms_field-targets.cmake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 143889fe..3ca83730 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ configure_file(pcms/configuration.h.in pcms/configuration.h) list(APPEND PCMS_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/pcms/version.h ${CMAKE_CURRENT_BINARY_DIR}/pcms/configuration.h) add_subdirectory(pcms/utility) +add_subdirectory(pcms/transient) add_subdirectory(pcms/discretization) add_subdirectory(pcms/localization) add_subdirectory(pcms/field) @@ -87,7 +88,7 @@ install( ) add_library(pcms_pcms INTERFACE) -target_link_libraries(pcms_pcms INTERFACE pcms::core) +target_link_libraries(pcms_pcms INTERFACE pcms::core pcms::transient) set_target_properties(pcms_pcms PROPERTIES EXPORT_NAME pcms) if (PCMS_ENABLE_Python) # Disable LTO/IPO before adding Python subdirectory to avoid fatbinData conflicts diff --git a/src/pcms/transient/CMakeLists.txt b/src/pcms/transient/CMakeLists.txt new file mode 100644 index 00000000..c64c9e36 --- /dev/null +++ b/src/pcms/transient/CMakeLists.txt @@ -0,0 +1,33 @@ +set(PCMS_TRANSIENT_HEADERS timestepper.hpp) +set(PCMS_TRANSIENT_SOURCES timestepper.cpp) + +add_library(pcms_transient ${PCMS_TRANSIENT_SOURCES}) +set_target_properties(pcms_transient PROPERTIES EXPORT_NAME transient) +add_library(pcms::transient ALIAS pcms_transient) + +target_sources( + pcms_transient + PUBLIC FILE_SET transient + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/.. + FILES ${PCMS_TRANSIENT_HEADERS}) +target_compile_features(pcms_transient PUBLIC cxx_std_20) +target_link_libraries(pcms_transient PUBLIC pcms::utility) +target_include_directories( + pcms_transient + PUBLIC $ + $) + +install( + TARGETS pcms_transient + EXPORT pcms_transient-targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pcms/transient + FILE_SET transient DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pcms) + +install( + EXPORT pcms_transient-targets + NAMESPACE pcms:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pcms) diff --git a/src/pcms/transient/timestepper.cpp b/src/pcms/transient/timestepper.cpp new file mode 100644 index 00000000..14755b9a --- /dev/null +++ b/src/pcms/transient/timestepper.cpp @@ -0,0 +1,18 @@ +#include "pcms/transient/timestepper.hpp" + +namespace pcms::transient +{ + +FixedTimestepper::FixedTimestepper(Real dt) : dt_(dt) {} + +Real FixedTimestepper::InitialStep() const +{ + return dt_; +} + +std::pair FixedTimestepper::Update(Real, Real) +{ + return {true, dt_}; +} + +} // namespace pcms::transient diff --git a/src/pcms/transient/timestepper.hpp b/src/pcms/transient/timestepper.hpp new file mode 100644 index 00000000..8ef47ca7 --- /dev/null +++ b/src/pcms/transient/timestepper.hpp @@ -0,0 +1,43 @@ +#ifndef PCMS_TRANSIENT_TIMESTEPPER_HPP +#define PCMS_TRANSIENT_TIMESTEPPER_HPP + +#include "pcms/utility/types.h" +#include + +namespace pcms::transient +{ + +// Decides whether to accept a completed time window and selects the next step. +class Timestepper +{ +public: + // Return the positive step size used for the first coupling window. + [[nodiscard]] virtual Real InitialStep() const = 0; + + // Given the current time step that was actually completed and its normalized + // error, return whether to accept it and the positive step size to try next. + virtual std::pair Update(Real dt, Real err) = 0; + + virtual ~Timestepper() = default; +}; + +// Always accepts and keeps a constant time step. +class FixedTimestepper : public Timestepper +{ +public: + // Store the positive step size used for every window. + explicit FixedTimestepper(Real dt); + + // Return the configured fixed step. + Real InitialStep() const override; + + // Keep the configured fixed time step. + std::pair Update(Real dt, Real err) override; + +private: + Real dt_; +}; + +} // namespace pcms::transient + +#endif // PCMS_TRANSIENT_TIMESTEPPER_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9a7eb845..482e582a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -399,6 +399,10 @@ find_package(Catch2 3) if(Catch2_FOUND) message( STATUS "Found Catch2: ${Catch2_DIR} (found version ${Catch2_VERSION})") + add_executable(test_transient_timestepper test_transient_timestepper.cpp) + target_link_libraries(test_transient_timestepper + PRIVATE Catch2::Catch2WithMain pcms::transient) + set(PCMS_UNIT_TEST_SOURCES unit_test_main.cpp test_coordinate_transform.cpp test_coordinate.cpp test_bounding_box.cpp) if(PCMS_ENABLE_XGC) @@ -474,6 +478,7 @@ if(Catch2_FOUND) include(Catch) Catch_discover_tests(unit_tests) + Catch_discover_tests(test_transient_timestepper) else() message(WARNING "Catch2 not found. Disabling Unit Tests") endif() diff --git a/test/test_transient_timestepper.cpp b/test/test_transient_timestepper.cpp new file mode 100644 index 00000000..8b2bad5b --- /dev/null +++ b/test/test_transient_timestepper.cpp @@ -0,0 +1,21 @@ +#include +#include + +#include "pcms/transient/timestepper.hpp" + +namespace tr = pcms::transient; + +TEST_CASE("FixedTimestepper always accepts its configured step", "[transient]") +{ + // 0.25 represents a positive fixed window size. + tr::FixedTimestepper timestepper(0.25); + + // The completed step (0.1) and large normalized error (100). + // A fixed controller ignores both. + const auto [accepted, next_step] = timestepper.Update(0.1, 100.0); + + // The first window must use the user-configured size. + REQUIRE(timestepper.InitialStep() == Catch::Approx(0.25)); + REQUIRE(accepted); + REQUIRE(next_step == Catch::Approx(0.25)); +}