diff --git a/include/respond/model.hpp b/include/respond/model.hpp index 6be742b7..1376a154 100644 --- a/include/respond/model.hpp +++ b/include/respond/model.hpp @@ -4,8 +4,8 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-07-14 // -// Modified By: Matthew Carroll // +// Last Modified: 2026-09-14 // +// Modified By: Dimitri Baptiste // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// @@ -49,6 +49,22 @@ class Model { const std::string &log_name = RESPOND_DEFAULT_LOG, const std::string &log_filepath = RESPOND_DEFAULT_LOG_FILE); + /// @brief Alternate factory method to create a Model instance. + /// @details This method creates a new instance of a Model subclass based on + /// the provided name. It sets the number of threads to be used by the + /// model, initializes logging for the model, and returns a unique_ptr to + /// the created instance. Throws an exception if the model name is + /// unsupported. + /// @param name The name identifier for the model to create. + /// @param processor_count The number of threads to be used by the model. + /// @param log_name Name of the logger for this model (default: "console"). + /// @param log_filepath File path for the log file (default: "respond.log"). + /// @return A unique_ptr to the newly created Model instance. + static std::unique_ptr + Create(const std::string &name, const unsigned int processor_count, + const std::string &log_name = RESPOND_DEFAULT_LOG, + const std::string &log_filepath = RESPOND_DEFAULT_LOG_FILE); + /// @brief Virtual destructor for proper polymorphic cleanup. virtual ~Model() = default; @@ -188,4 +204,4 @@ inline std::ostream &operator<<(std::ostream &os, const Model &model) { } // namespace respond -#endif // RESPOND_MODEL_HPP_ \ No newline at end of file +#endif // RESPOND_MODEL_HPP_ diff --git a/src/internals/markov.hpp b/src/internals/markov.hpp index 08e63993..ad5f1781 100644 --- a/src/internals/markov.hpp +++ b/src/internals/markov.hpp @@ -4,8 +4,8 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-07-14 // -// Modified By: Matthew Carroll // +// Last Modified: 2026-09-18 // +// Modified By: Dimitri Baptiste // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -48,14 +49,21 @@ class Markov : public virtual Model { /// @param log_name The logger name for error reporting. /// @param log_filepath The file path for the log file to be used by this /// model. + /// @param processor_count The number of threads to use when running this + /// model. Markov(const std::string &name, const std::string &log_name, - const std::string &log_filepath) + const std::string &log_filepath, + const unsigned int processor_count = + std::thread::hardware_concurrency()) : _name(name), _log_name(log_name), _current_timestep(0), _history_capture_interval(1), _final_timestep(-1), _initial_history_recorded(false) { CreateFileLogger(log_name, log_filepath); - const auto processor_count = std::thread::hardware_concurrency(); - Eigen::setNbThreads(processor_count); + // ensure that the number of threads cannot exceed the hardware capacity + const unsigned int thread_limit = std::thread::hardware_concurrency(); + const unsigned int threads = + processor_count > thread_limit ? thread_limit : processor_count; + Eigen::setNbThreads(threads); } /// @brief Destructor for Markov model. Default implementation. @@ -361,4 +369,4 @@ class Markov : public virtual Model { }; } // namespace respond -#endif // RESPOND_INTERNALS_MARKOV_HPP_ \ No newline at end of file +#endif // RESPOND_INTERNALS_MARKOV_HPP_ diff --git a/src/model_factory.cpp b/src/model_factory.cpp index d2a57d35..9d882f7b 100644 --- a/src/model_factory.cpp +++ b/src/model_factory.cpp @@ -4,8 +4,8 @@ // Created Date: 2025-07-07 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-07-07 // -// Modified By: Matthew Carroll // +// Last Modified: 2026-09-14 // +// Modified By: Dimitri Baptiste // // ----- // // Copyright (c) 2025-2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// @@ -25,4 +25,12 @@ std::unique_ptr Model::Create(const std::string &name, const std::string &log_filepath) { return std::make_unique(name, log_name, log_filepath); } + +std::unique_ptr Model::Create(const std::string &name, + const unsigned int processor_count, + const std::string &log_name, + const std::string &log_filepath) { + return std::make_unique(name, log_name, log_filepath, + processor_count); +} } // namespace respond diff --git a/tests/unit/markov_test.cpp b/tests/unit/markov_test.cpp index 02619a26..5e754b97 100644 --- a/tests/unit/markov_test.cpp +++ b/tests/unit/markov_test.cpp @@ -4,8 +4,8 @@ // Created Date: 2025-06-06 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-07-09 // -// Modified By: Matthew Carroll // +// Last Modified: 2026-09-14 // +// Modified By: Dimitri Baptiste // // ----- // // Copyright (c) 2025-2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -88,6 +89,17 @@ TEST_F(MarkovTest, CreateMarkovModel) { CreationStatus::kExists); } +TEST_F(MarkovTest, CreateMarkovModelProcessorCount) { + unsigned int processor_count = + std::thread::hardware_concurrency() > 1 + ? (std::thread::hardware_concurrency() / 2) + : 1; + auto markov = Model::Create("markov", processor_count); + ASSERT_NE(markov, nullptr); + ASSERT_EQ(CreateFileLogger(RESPOND_DEFAULT_LOG, ""), + CreationStatus::kExists); +} + TEST_F(MarkovTest, MoveConstructor) { Markov markov("markov_source", RESPOND_DEFAULT_LOG); markov.SetState(state);