From 9db3352d1c590b02c09fbe22ffdcb66c267d0a6a Mon Sep 17 00:00:00 2001 From: Matthew Carroll <28577806+MJC598@users.noreply.github.com> Date: Thu, 2 Jul 2026 10:10:16 -0400 Subject: [PATCH 1/3] uml and some basic constructors --- docs/src/index.md | 1 + docs/src/uml.md | 348 +++++++++++++++++++++ extras/benchmark/src/benchmark_respond.cpp | 2 +- include/respond/history.hpp | 8 +- include/respond/model.hpp | 12 +- include/respond/simulation.hpp | 33 +- include/respond/timestep.hpp | 90 ++++++ src/internals/markov.hpp | 13 +- src/logging.cpp | 6 +- tests/integration/respond_test.cpp | 49 ++- 10 files changed, 544 insertions(+), 18 deletions(-) create mode 100644 docs/src/uml.md create mode 100644 include/respond/timestep.hpp diff --git a/docs/src/index.md b/docs/src/index.md index b69f8562..5776959d 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -58,6 +58,7 @@ This documentation covers both user and developer perspectives: - **[Installation](installation.md)** - Build and installation instructions - **[Motivation](motivation.md)** - Model design and goals - **[Architecture and Design](architecture.md)** - Design patterns, component architecture, and extensibility +- **[UML Diagrams](uml.md)** - Editable class diagrams for the core library structure - **[C++ API Guide](api-guide.md)** - Developer guide for using RESPOND as a library - **[Data Management](data.md)** - Configuration and data requirements - **[Running the Model](run.md)** - Executing simulations diff --git a/docs/src/uml.md b/docs/src/uml.md new file mode 100644 index 00000000..faa2187e --- /dev/null +++ b/docs/src/uml.md @@ -0,0 +1,348 @@ +# UML Diagrams + +This page uses Mermaid so diagrams remain human-editable in plain text. + +## Public API Diagram + +This diagram is the external library view and reflects the target workflow: +one Simulation owns many Models, each Model owns many Timesteps, each +Timestep owns many Transitions. + +```mermaid +classDiagram + direction LR + + class Simulation { + +CreateNewModel(model_name) string + +AddModel(model) + +Run() + +GetModels() vector~unique_ptr~Model~~ + +GetModelNames() vector~string~ + +GetModelHistories() + +GetModelSparseHistories() + } + + class Model { + <> + +SetState(state) * + +GetState() VectorXd * + +AddTimestep(timestep) * + +GetTimesteps() + +RunTransitions() * + +GetHistories() map~string, History~ * + +SetHistories(h) * + +CreateDefaultHistories() * + +SetFinalTimestep(final_timestep) * + +clone() unique_ptr~Model~ * + +Create(name, log_name) unique_ptr~Model~ + } + + class Timestep { + +AddTransition(t) + +GetTransitions() vector~unique_ptr~Transition~~~ + +GetTransitionNames() vector~string~ + } + + class Transition { + <> + +Execute(state, histories) VectorXd * + +AddTransitionMatrix(matrix) * + +GetTransitionName() string * + +ClearTransitionMatrices() * + +clone() unique_ptr~Transition~ * + } + + class TransitionFactory { + +CreateTransition(type, log_name) unique_ptr~Transition~ + } + + class History { + +AddState(state, timestep) + +RecordSnapshot(state, timestep) + +AccumulateState(state) + +FlushPendingState(timestep, state_size) + +GetStateMap() map~int, VectorXd~ + +GetStateAsVector() vector~VectorXd~ + } + + class HistoryMode { + <> + Snapshot + Accumulated + } + + class LoggingAPI { + <> + +CreateFileLogger(name, filepath) + +CreateSharedFileSink(filepath) + +CreateSharedLogger(name) + +SetLogPattern(pattern) + +GetLogPattern() + +SetFlushInterval(seconds) + +FlushAllLoggers() + +LogInfo(name, message) + +LogWarning(name, message) + +LogError(name, message) + +LogDebug(name, message) + +CheckLoggerExists(name) + +GetLoggerInfo(name) + +SetLoggerLevel(name, level) + } + + class LogType { + <> + kInfo + kWarn + kError + kDebug + } + + class CreationStatus { + <> + kError + kSuccess + kExists + kNotCreated + } + + class LogPattern { + <> + kSimple + kStandard + kDetailed + kThreadSafe + } + + class CostEffectiveness { + <> + +Discount(data, discount_rate, week, is_discrete, total_weeks) + +CwiseProduct(state, multiplier) + +CwiseMin(state, multiplier) + +CalculateLifeYears(history, discount, discount_rate, total_weeks) + } + + class Version { + <> + RESPOND_VER_MAJOR + RESPOND_VER_MINOR + RESPOND_VER_PATCH + RESPOND_VERSION + } + + Simulation *-- "1..*" Model : owns + Model *-- "1..*" Timestep : owns (target API) + Timestep *-- "1..*" Transition : owns + + Model o-- "0..*" History : records + Transition ..> History : updates + History --> HistoryMode : mode + + TransitionFactory ..> Transition : creates + + LoggingAPI ..> LogType : uses + LoggingAPI ..> CreationStatus : returns + LoggingAPI ..> LogPattern : configures + + CostEffectiveness ..> History : consumes +``` + +## Internals Diagram + +This diagram is implementation-focused and intentionally verbose. + +```mermaid +classDiagram + direction LR + + class Model { + <> + +SetState(state) * + +GetState() VectorXd * + +RunTransitions() * + +AddTimestep(transitions) * + +GetTransitionNames() vector~string~ * + +GetHistories() map~string, History~ * + +CreateDefaultHistories() * + +SetHistories(h) * + +SetFinalTimestep(final_timestep) * + +clone() unique_ptr~Model~ * + +Create(name, log_name) unique_ptr~Model~ + } + + class Markov { + -vector~unique_ptr~Transition~~ _transition_vector + -VectorXd _state + -string _name + -string _log_name + -map~string, History~ _histories + -int _current_timestep + -int _history_capture_interval + -int _final_timestep + -bool _initial_history_recorded + +RunTransitions() + +AddTimestep(transitions) + +GetTransitionNames() vector~string~ + +GetHistories() map~string, History~ + +clone() unique_ptr~Model~ + } + + class Transition { + <> + +Execute(state, histories) VectorXd * + +AddTransitionMatrix(matrix) * + +GetTransitionName() string * + +ClearTransitionMatrices() * + +GetLogName() string * + +clone() unique_ptr~Transition~ * + } + + class TransitionBase { + <> + -string _name + -string _log_name + -vector~MatrixXd~ _transition_matrices + +AddTransitionMatrix(matrix) + +GetTransitionName() string + +ClearTransitionMatrices() + +GetLogName() string + } + + class Migration { + +Execute(state, histories) VectorXd + +Create(name, log_name) unique_ptr~Transition~ + } + + class Behavior { + +Execute(state, histories) VectorXd + +Create(name, log_name) unique_ptr~Transition~ + } + + class Intervention { + +Execute(state, histories) VectorXd + +Create(name, log_name) unique_ptr~Transition~ + } + + class Overdose { + +Execute(state, histories) VectorXd + +Create(name, log_name) unique_ptr~Transition~ + } + + class BackgroundDeath { + +Execute(state, histories) VectorXd + +Create(name, log_name) unique_ptr~Transition~ + } + + class TransitionFactory { + +CreateTransition(type, log_name) unique_ptr~Transition~ + } + + class History { + -string _name + -string _log_name + -HistoryMode _mode + -vector~int~ _timesteps + -vector~VectorXd~ _states + -VectorXd _pending_state + +AddState(state, timestep) + +RecordSnapshot(state, timestep) + +AccumulateState(state) + +FlushPendingState(timestep, state_size) + +GetLatestRecordedTimestep() int + +GetStateMap() map~int, VectorXd~ + +GetStateAsVector() vector~VectorXd~ + } + + class HistoryMode { + <> + Snapshot + Accumulated + } + + class LoggingConfig { + <> + +GetInstance() LoggingConfig + +GetSharedSink(filepath) + +GetPattern() + +SetPattern(pattern) + +GetFlushInterval() + +SetFlushInterval(seconds) + } + + class LogPattern { + <> + kSimple + kStandard + kDetailed + kThreadSafe + } + + Model <|-- Markov + + Transition <|-- TransitionBase + TransitionBase <|-- Migration + TransitionBase <|-- Behavior + TransitionBase <|-- Intervention + TransitionBase <|-- Overdose + TransitionBase <|-- BackgroundDeath + + Markov *-- "0..*" Transition : executes + Markov *-- "0..*" History : stores + Transition ..> History : mutates + History --> HistoryMode : mode + + TransitionFactory ..> Migration : creates + TransitionFactory ..> Behavior : creates + TransitionFactory ..> Intervention : creates + TransitionFactory ..> Overdose : creates + TransitionFactory ..> BackgroundDeath : creates + + LoggingConfig ..> LogPattern : stores +``` + +## Execution Flow Diagram + +This sequence follows the requested external flow and execution semantics. + +```mermaid +sequenceDiagram + autonumber + actor User + participant Sim as Simulation + participant Mod as Model[i] + participant Step as Timestep[j] + participant Tr as Transition[k] + + User->>Sim: Create Simulation + loop For each model to add + User->>Sim: Create/Add Model + loop For each timestep in model + User->>Mod: Create/Add Timestep + loop For each transition in timestep + User->>Step: Create/Add Transition + end + end + end + + User->>Sim: Run() + + par For each Model in Simulation (parallel) + Sim->>Mod: Run + loop For each Timestep in Model (series) + Mod->>Step: Execute + loop For each Transition in Timestep (series) + Step->>Tr: Execute(state, histories) + Tr-->>Step: updated state/history contribution + end + Step-->>Mod: updated state/history + end + Mod-->>Sim: model complete + end + + Sim-->>User: simulation complete +``` + +## Notes + +- Public API diagram includes every component from headers in include/respond: simulation, model, timestep, transition, transition_factory, history, logging, cost_effectiveness, and version. +- Current implementation detail: Model::Create currently returns a Markov instance. +- Current implementation detail: TransitionFactory::CreateTransition resolves migration, behavior, intervention, overdose, and background_death. diff --git a/extras/benchmark/src/benchmark_respond.cpp b/extras/benchmark/src/benchmark_respond.cpp index e484bb67..deb95a89 100644 --- a/extras/benchmark/src/benchmark_respond.cpp +++ b/extras/benchmark/src/benchmark_respond.cpp @@ -4,7 +4,7 @@ // Created Date: 2026-04-27 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-04-27 // +// Last Modified: 2026-06-29 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // diff --git a/include/respond/history.hpp b/include/respond/history.hpp index e92598d7..34eaedf3 100644 --- a/include/respond/history.hpp +++ b/include/respond/history.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-06-25 // +// Last Modified: 2026-06-30 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -36,12 +36,14 @@ inline HistoryMode GetDefaultHistoryMode(const std::string &name) { /// are filled with zero vectors). class History { public: + History() : History("state") {} + + History(const std::string &name) : History(name, "console") {} /// @brief Constructs a History tracker. /// @param name The identifier for this history (default: "state"). /// @param log_name The logger name for error reporting (default: /// "console"). - History(const std::string &name = "state", - const std::string &log_name = "console") + History(const std::string &name, const std::string &log_name) : History(name, log_name, GetDefaultHistoryMode(name)) {} /// @brief Constructs a History tracker with an explicit recording mode. diff --git a/include/respond/model.hpp b/include/respond/model.hpp index ee12217f..2d254c4c 100644 --- a/include/respond/model.hpp +++ b/include/respond/model.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-06-25 // +// Last Modified: 2026-06-30 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -44,10 +44,12 @@ class Model { /// history. virtual void RunTransitions() = 0; - /// @brief Adds a transition to the model. - /// @param t A unique_ptr to a Transition object. The model assumes - /// ownership. - virtual void AddTransition(const std::unique_ptr &t) = 0; + /// @brief Adds a transition to the model. The model takes ownership of the + /// transition via cloning. + /// @param transitions A vector of unique_ptrs to a Transition instances to + /// add. + virtual void AddTimestep( + const std::vector> &transitions) = 0; /// @brief Retrieves the names of all registered transitions. /// @return Vector of transition names in the order they were added. diff --git a/include/respond/simulation.hpp b/include/respond/simulation.hpp index b9b7fb55..a3800f15 100644 --- a/include/respond/simulation.hpp +++ b/include/respond/simulation.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-02-12 // +// Last Modified: 2026-06-30 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -21,6 +21,7 @@ #include #include +#include #include namespace respond { @@ -30,20 +31,37 @@ namespace respond { class Simulation { public: /// @brief Default constructor initializing with "console" logger. - Simulation() : Simulation("console") {} + Simulation() : Simulation("respond") {} /// @brief Constructs a Simulation with a specified logger. /// @param log_name Name of the logger for this simulation (default: /// "console"). - Simulation(const std::string &log_name) : _log_name(log_name) {} + Simulation(const std::string &log_name) + : Simulation(log_name, log_name + ".log") {} + + /// @brief Constructs a Simulation with a specified logger and log file + /// path. + /// @param log_name + /// @param log_filepath + Simulation(const std::string &log_name, const std::string &log_filepath) + : _log_name(log_name) { + CreateFileLogger(log_name, log_filepath); + } /// @brief Virtual destructor for polymorphic cleanup. ~Simulation() = default; + const std::string CreateNewModel(const std::string &model_name) { + _models.push_back(Model::Create(model_name, _log_name)); + return std::to_string(_models.size()) + "_" + + _models.back()->GetModelName(); + } + /// @brief Executes one step of the simulation for all models. /// Calls RunTransitions() on each registered model in sequence. void Run() { for (const auto &model : _models) { + model->SetFinalTimestep(_duration); model->RunTransitions(); } } @@ -152,6 +170,15 @@ class Simulation { private: std::string _log_name; std::vector> _models; + + int _duration = 1; // Default simulation duration in timesteps + std::vector _parameter_change_times; + bool _stratify_entering_cohort; + + bool _build_summary_stats; + bool _save_state_history; + std::vector _timesteps_to_report; + bool _pivot_long; }; } // namespace respond diff --git a/include/respond/timestep.hpp b/include/respond/timestep.hpp new file mode 100644 index 00000000..a26097a3 --- /dev/null +++ b/include/respond/timestep.hpp @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// File: timestep.hpp // +// Project: respond // +// Created Date: 2026-06-30 // +// Author: Matthew Carroll // +// ----- // +// Last Modified: 2026-06-30 // +// Modified By: Matthew Carroll // +// ----- // +// Copyright (c) 2026 Syndemics Lab at Boston Medical Center // +//////////////////////////////////////////////////////////////////////////////// +#ifndef RESPOND_TIMESTEP_HPP_ +#define RESPOND_TIMESTEP_HPP_ + +#include +#include + +#include + +namespace respond { +class Timestep { +public: + Timestep() { _transitions = {}; } + ~Timestep() = default; + + std::vector> GetTransitions() const { + return _transitions; + } + + const std::unique_ptr & + GetTransitionAtIndex(size_t index) const { + if (index >= _transitions.size()) { + throw std::out_of_range( + "Index out of range in GetTransitionAtIndex"); + } + return _transitions[index]; + } + + std::vector GetTransitionNames() const { + std::vector names; + for (const auto &t : _transitions) { + names.push_back(t->GetTransitionName()); + } + return names; + } + + void AddTransition(const std::unique_ptr &t) { + _transitions.push_back(t->clone()); + } + + // Copy Constructor and Assignment + + Timestep(const Timestep &other) { + _transitions.clear(); + for (const auto &t : other._transitions) { + _transitions.push_back(t->clone()); + } + } + + Timestep &operator=(const Timestep &other) { + if (this != &other) { + _transitions.clear(); + for (const auto &t : other._transitions) { + _transitions.push_back(t->clone()); + } + } + return *this; + } + + // Move Constructor and Assignment + + Timestep(Timestep &&other) noexcept + : _transitions(std::move(other._transitions)) { + other._transitions.clear(); + } + + Timestep &operator=(Timestep &&other) noexcept { + if (this != &other) { + _transitions = std::move(other._transitions); + other._transitions.clear(); + } + return *this; + } + +private: + std::vector> _transitions; +}; +} // namespace respond + +#endif // RESPOND_TIMESTEP_HPP_ \ No newline at end of file diff --git a/src/internals/markov.hpp b/src/internals/markov.hpp index a3f03cad..e2e7d780 100644 --- a/src/internals/markov.hpp +++ b/src/internals/markov.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-06-25 // +// Last Modified: 2026-06-29 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -115,15 +115,22 @@ class Markov : public virtual Model { if (!_initial_history_recorded) { RecordHistoryAtCurrentTimestep(); } + int transitions_per_timestep = + static_cast(_transition_vector.size()) / _final_timestep; for (const auto &t : _transition_vector) { _state = t->Execute(_state, _histories); } _current_timestep++; RecordHistoryAtCurrentTimestep(); } + // assume ownership of the Transition - void AddTransition(const std::unique_ptr &t) override { - _transition_vector.push_back(t->clone()); + void AddTimestep( + const std::vector> &transitions) override { + for (const auto &t : transitions) { + _transition_vector.push_back(t->clone()); + } + _final_timestep++; } // get the names of each transition we own std::vector GetTransitionNames() const override { diff --git a/src/logging.cpp b/src/logging.cpp index 032364ca..f3da2a3b 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -4,10 +4,10 @@ // Created Date: 2025-06-06 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2025-07-30 // +// Last Modified: 2026-06-30 // // Modified By: Matthew Carroll // // ----- // -// Copyright (c) 2025 Syndemics Lab at Boston Medical Center // +// Copyright (c) 2025-2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// #include @@ -36,6 +36,8 @@ CreationStatus CreateFileLogger(const std::string &logger_name, std::cerr << error_msg << std::endl; return CreationStatus::kError; } + std::cout << "Initialized logger (" << logger_name << ") to log file (" + << filepath << ")" << std::endl; return CreationStatus::kSuccess; } diff --git a/tests/integration/respond_test.cpp b/tests/integration/respond_test.cpp index ce576615..33b9602e 100644 --- a/tests/integration/respond_test.cpp +++ b/tests/integration/respond_test.cpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-06 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-02-13 // +// Last Modified: 2026-06-29 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -128,6 +128,53 @@ TEST_F(RespondTest, RunSimulationOneStep) { ASSERT_TRUE(state_history[1].isApprox(final_state)); } +TEST_F(RespondTest, RunSimulationTwoStep) { + markov->CreateDefaultHistories(); + + markov->SetState(init_state); + + auto migr = MakeTestTransition("migration", migration_pop); + auto beha = MakeTestTransition("behavior", behavior_trans); + auto inte = MakeTestTransition("intervention", intervention_trans); + auto over = MakeTestTransition("overdose", overdose_prob); + over->AddTransitionMatrix(fod_prob); + + auto back = MakeTestTransition("background_death", background_death_prob); + + markov->AddTransition(migr); + markov->AddTransition(beha); + markov->AddTransition(inte); + markov->AddTransition(over); + markov->AddTransition(back); + + markov->AddTransition(migr); + markov->AddTransition(beha); + markov->AddTransition(inte); + markov->AddTransition(over); + markov->AddTransition(back); + + Simulation sim("test_logger"); + sim.AddModel(markov); + sim.Run(); + + auto histories = sim.GetModelHistories(); + ASSERT_EQ(histories.size(), 1); + + auto mm_histories = histories[0]; + if (mm_histories.find("state") == mm_histories.end()) { + FAIL() << "Unable to find the 'state' history."; + } + + auto state_history = mm_histories.at("state"); + // 2 because it carries the initial state and 1 step + ASSERT_EQ(state_history.size(), 3); + + Eigen::Vector3d final_state; + ASSERT_TRUE(state_history[0].isApprox(init_state)); + final_state << 0.76715528791564891, 0.72320370216816077, 1.037712429738102; + ASSERT_TRUE(state_history[1].isApprox(final_state)); +} + TEST_F(RespondTest, CreateDefaultHistories) { std::vector expected = { "state", "total_overdose", "fatal_overdose", "intervention_admission", From 13b44a11bab8f94417426590dc7b880b1c0b2afd Mon Sep 17 00:00:00 2001 From: Matthew Carroll <28577806+MJC598@users.noreply.github.com> Date: Thu, 2 Jul 2026 11:25:08 -0400 Subject: [PATCH 2/3] Updating public diagram --- docs/src/uml.md | 56 ++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/docs/src/uml.md b/docs/src/uml.md index faa2187e..01c0feeb 100644 --- a/docs/src/uml.md +++ b/docs/src/uml.md @@ -13,20 +13,19 @@ classDiagram direction LR class Simulation { - +CreateNewModel(model_name) string - +AddModel(model) + +CreateNewModel(type) shared_ptr~Model~ + +AddNewModel(shared_ptr~Model~) bool +Run() - +GetModels() vector~unique_ptr~Model~~ + +GetModel(size_t model_idx) const Model & +GetModelNames() vector~string~ - +GetModelHistories() - +GetModelSparseHistories() + +GetModelHistory(size_t model_idx) map~string, History~ } class Model { <> +SetState(state) * +GetState() VectorXd * - +AddTimestep(timestep) * + +AddTimestep(shared_ptr~timestep~) * +GetTimesteps() +RunTransitions() * +GetHistories() map~string, History~ * @@ -38,22 +37,24 @@ classDiagram } class Timestep { - +AddTransition(t) - +GetTransitions() vector~unique_ptr~Transition~~~ - +GetTransitionNames() vector~string~ + +CreateTransition(type) const Transition & + +AddMatrixToTransition(size_t index, MatrixXd mat) + +GetTransition(size_t idx) const Transition & + +GetTransition(string name) const Transition & + +GetTransitions() const vector~const Transition &~ + +GetTransitionNames() vector~const string~ + +clone() unique_ptr~Timestep~ } class Transition { <> +Execute(state, histories) VectorXd * - +AddTransitionMatrix(matrix) * - +GetTransitionName() string * - +ClearTransitionMatrices() * + +AddMatrix(matrix, size_t idx = -1) * + +GetMatrix(size_t idx) Eigen::Ref~MatrixXd~ * + +GetName() string * + +ClearMatrices() * +clone() unique_ptr~Transition~ * - } - - class TransitionFactory { - +CreateTransition(type, log_name) unique_ptr~Transition~ + +Create(type, log_name) unique_ptr~Transition~ } class History { @@ -121,28 +122,17 @@ classDiagram +CalculateLifeYears(history, discount, discount_rate, total_weeks) } - class Version { - <> - RESPOND_VER_MAJOR - RESPOND_VER_MINOR - RESPOND_VER_PATCH - RESPOND_VERSION - } - - Simulation *-- "1..*" Model : owns - Model *-- "1..*" Timestep : owns (target API) - Timestep *-- "1..*" Transition : owns + Simulation *-- "0..*" Model : owns + Model *-- "0..*" Timestep : owns + Timestep *-- "0..*" Transition : owns - Model o-- "0..*" History : records - Transition ..> History : updates - History --> HistoryMode : mode - - TransitionFactory ..> Transition : creates + Model *-- "0..*" History : owns + Simulation ..> LoggingAPI: uses LoggingAPI ..> LogType : uses LoggingAPI ..> CreationStatus : returns LoggingAPI ..> LogPattern : configures - + History ..> HistoryMode : uses CostEffectiveness ..> History : consumes ``` From a87db997b29eb394c02cadd97c4390707fc065cc Mon Sep 17 00:00:00 2001 From: Matthew Carroll <28577806+MJC598@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:31:14 -0400 Subject: [PATCH 3/3] updating UML --- docs/src/uml.md | 155 ++++++++++++------------- include/respond/simulation.hpp | 2 +- include/respond/transition.hpp | 16 ++- include/respond/transition_factory.hpp | 41 ------- src/{markov.cpp => model_factory.cpp} | 0 src/transition_factory.cpp | 9 +- 6 files changed, 93 insertions(+), 130 deletions(-) delete mode 100644 include/respond/transition_factory.hpp rename src/{markov.cpp => model_factory.cpp} (100%) diff --git a/docs/src/uml.md b/docs/src/uml.md index 01c0feeb..91869cf8 100644 --- a/docs/src/uml.md +++ b/docs/src/uml.md @@ -19,6 +19,7 @@ classDiagram +GetModel(size_t model_idx) const Model & +GetModelNames() vector~string~ +GetModelHistory(size_t model_idx) map~string, History~ + +operator<<(ostream &os, const Simulation &obj) ostream & } class Model { @@ -26,14 +27,16 @@ classDiagram +SetState(state) * +GetState() VectorXd * +AddTimestep(shared_ptr~timestep~) * - +GetTimesteps() + +GetTimesteps() * +RunTransitions() * + +ClearTimesteps() * +GetHistories() map~string, History~ * - +SetHistories(h) * + +ClearHistories() * +CreateDefaultHistories() * +SetFinalTimestep(final_timestep) * +clone() unique_ptr~Model~ * +Create(name, log_name) unique_ptr~Model~ + +operator<<(ostream &os, const Model &obj) ostream & } class Timestep { @@ -44,17 +47,19 @@ classDiagram +GetTransitions() const vector~const Transition &~ +GetTransitionNames() vector~const string~ +clone() unique_ptr~Timestep~ + +operator<<(ostream &os, const Timestep &obj) ostream & } class Transition { <> +Execute(state, histories) VectorXd * - +AddMatrix(matrix, size_t idx = -1) * + +AddMatrix(const Eigen::Ref~const MatrixXd~ &matrix, size_t idx) * +GetMatrix(size_t idx) Eigen::Ref~MatrixXd~ * +GetName() string * +ClearMatrices() * +clone() unique_ptr~Transition~ * +Create(type, log_name) unique_ptr~Transition~ + +operator<<(ostream &os, const Transition &obj) ostream & } class History { @@ -64,6 +69,7 @@ classDiagram +FlushPendingState(timestep, state_size) +GetStateMap() map~int, VectorXd~ +GetStateAsVector() vector~VectorXd~ + +operator<<(ostream &os, const History &obj) ostream & } class HistoryMode { @@ -138,29 +144,39 @@ classDiagram ## Internals Diagram -This diagram is implementation-focused and intentionally verbose. +This diagram is implementation-focused and shows the relationship between +abstract classes and how it impacts the stored components for ownership +purposes. ```mermaid classDiagram direction LR + class Simulation { + -string _log_name + -vector~shared_ptr~Model~~ _models + -size_t _duration + -vector~size_t~ _parameter_change_times + -bool _stratify_entering_cohort + -bool _build_summary_stats + -bool _save_state_history + -vector~size_t~ _timesteps_to_report + -bool _pivot_long + +Simulation() + +Simulation(const string log_name) + +Simulation(const string log_name, const string log_filepath) + +Simulation(const Simulation &other) + +operator=(const Simulation &other) Simulation & + +Simulation(const Simulation &&other) + +operator=(const Simulation &&other) Simulation & + } + class Model { <> - +SetState(state) * - +GetState() VectorXd * - +RunTransitions() * - +AddTimestep(transitions) * - +GetTransitionNames() vector~string~ * - +GetHistories() map~string, History~ * - +CreateDefaultHistories() * - +SetHistories(h) * - +SetFinalTimestep(final_timestep) * - +clone() unique_ptr~Model~ * - +Create(name, log_name) unique_ptr~Model~ } class Markov { - -vector~unique_ptr~Transition~~ _transition_vector + -vector~shared_ptr~Transition~~ _transition_vector -VectorXd _state -string _name -string _log_name @@ -169,21 +185,32 @@ classDiagram -int _history_capture_interval -int _final_timestep -bool _initial_history_recorded - +RunTransitions() - +AddTimestep(transitions) - +GetTransitionNames() vector~string~ - +GetHistories() map~string, History~ - +clone() unique_ptr~Model~ + -ResetHistoryTracking() + -GetLatestRecordedTimestep() + -ShouldRecordHistoryAtTimestep(int timestep) + -RecordHistoryAtCurrentTimestep() + -SetupHistory() + +Markov() + +Markov(const string &name, const string log_name) + +Markov(Markov &other) + +operator=(Markov &other) Markov & + +Markov(Markov &&other) + +operator=(Markov &&other) Markov & + +SetState(state) override + +GetState() VectorXd override + +AddTimestep(shared_ptr~timestep~) override + +GetTimesteps() override + +ClearTimesteps() override + +RunTransitions() override + +GetHistories() map~string, History~ override + +ClearHistories() override + +CreateDefaultHistories() override + +SetFinalTimestep(final_timestep) override + +clone() unique_ptr~Model~ override } class Transition { <> - +Execute(state, histories) VectorXd * - +AddTransitionMatrix(matrix) * - +GetTransitionName() string * - +ClearTransitionMatrices() * - +GetLogName() string * - +clone() unique_ptr~Transition~ * } class TransitionBase { @@ -191,10 +218,11 @@ classDiagram -string _name -string _log_name -vector~MatrixXd~ _transition_matrices - +AddTransitionMatrix(matrix) - +GetTransitionName() string - +ClearTransitionMatrices() - +GetLogName() string + -GetMatrices() const vector & + +AddMatrix(const Eigen::Ref~const MatrixXd~ &matrix, size_t idx) override + +GetTransitionName() string override + +ClearTransitionMatrices() override + +GetLogName() string override } class Migration { @@ -222,10 +250,6 @@ classDiagram +Create(name, log_name) unique_ptr~Transition~ } - class TransitionFactory { - +CreateTransition(type, log_name) unique_ptr~Transition~ - } - class History { -string _name -string _log_name @@ -242,51 +266,26 @@ classDiagram +GetStateAsVector() vector~VectorXd~ } - class HistoryMode { - <> - Snapshot - Accumulated - } - - class LoggingConfig { - <> - +GetInstance() LoggingConfig - +GetSharedSink(filepath) - +GetPattern() - +SetPattern(pattern) - +GetFlushInterval() - +SetFlushInterval(seconds) - } - - class LogPattern { - <> - kSimple - kStandard - kDetailed - kThreadSafe - } + Simulation *-- "0..*" Model : owns + Model <|-- Markov : implements - Model <|-- Markov + Transition <|-- TransitionBase : extends + TransitionBase <|-- Migration : implements + TransitionBase <|-- Behavior : implements + TransitionBase <|-- Intervention : implements + TransitionBase <|-- Overdose : implements + TransitionBase <|-- BackgroundDeath : implements - Transition <|-- TransitionBase - TransitionBase <|-- Migration - TransitionBase <|-- Behavior - TransitionBase <|-- Intervention - TransitionBase <|-- Overdose - TransitionBase <|-- BackgroundDeath + Markov *-- "0..*" Transition : owns + Markov *-- "0..*" History : owns + Migration <.. History : uses + Behavior <.. History : uses + Intervention <..> History : uses and updates + Overdose <..> History : uses and updates + BackgroundDeath <..> History : uses and updates - Markov *-- "0..*" Transition : executes - Markov *-- "0..*" History : stores - Transition ..> History : mutates - History --> HistoryMode : mode - TransitionFactory ..> Migration : creates - TransitionFactory ..> Behavior : creates - TransitionFactory ..> Intervention : creates - TransitionFactory ..> Overdose : creates - TransitionFactory ..> BackgroundDeath : creates - LoggingConfig ..> LogPattern : stores ``` ## Execution Flow Diagram @@ -330,9 +329,3 @@ sequenceDiagram Sim-->>User: simulation complete ``` - -## Notes - -- Public API diagram includes every component from headers in include/respond: simulation, model, timestep, transition, transition_factory, history, logging, cost_effectiveness, and version. -- Current implementation detail: Model::Create currently returns a Markov instance. -- Current implementation detail: TransitionFactory::CreateTransition resolves migration, behavior, intervention, overdose, and background_death. diff --git a/include/respond/simulation.hpp b/include/respond/simulation.hpp index a3800f15..d6c2fbb9 100644 --- a/include/respond/simulation.hpp +++ b/include/respond/simulation.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-06-30 // +// Last Modified: 2026-07-02 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // diff --git a/include/respond/transition.hpp b/include/respond/transition.hpp index 9c2fd0dd..0faeabc0 100644 --- a/include/respond/transition.hpp +++ b/include/respond/transition.hpp @@ -4,7 +4,7 @@ // Created Date: 2026-02-02 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-06-25 // +// Last Modified: 2026-07-02 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // @@ -71,6 +71,20 @@ class Transition { /// @return A unique_ptr to an independent copy of this transition. virtual std::unique_ptr clone() const = 0; + /// @brief Creates a transition of the specified type. + /// @param type The type of transition to create. Supported types + /// (case-insensitive): + /// - "migration": Population migration transitions + /// - "behavior": Behavioral state transitions + /// - "intervention": Intervention-driven transitions + /// - "overdose": Overdose-related transitions + /// - "background_death": Background mortality transitions + /// @param log_name The logger name for error reporting (e.g., "console"). + /// @return A unique_ptr to the created Transition, or nullptr if type is + /// unsupported. + static std::unique_ptr Create(const std::string &type, + const std::string &log_name); + protected: /// @brief Protected default constructor for subclass initialization. /// Not intended for direct public use. diff --git a/include/respond/transition_factory.hpp b/include/respond/transition_factory.hpp deleted file mode 100644 index 87fd8fc8..00000000 --- a/include/respond/transition_factory.hpp +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// File: transition_factory.hpp // -// Project: respond // -// Created Date: 2026-02-05 // -// Author: Matthew Carroll // -// ----- // -// Last Modified: 2026-02-06 // -// Modified By: Matthew Carroll // -// ----- // -// Copyright (c) 2026 Syndemics Lab at Boston Medical Center // -//////////////////////////////////////////////////////////////////////////////// -#ifndef RESPOND_TRANSITION_FACTORY_HPP_ -#define RESPOND_TRANSITION_FACTORY_HPP_ - -#include - -#include - -namespace respond { -/// @brief Factory for creating concrete Transition instances. -/// This factory supports creation of various transition types used in the -/// RESPOND model. -class TransitionFactory { -public: - /// @brief Creates a transition of the specified type. - /// @param type The type of transition to create. Supported types - /// (case-insensitive): - /// - "migration": Population migration transitions - /// - "behavior": Behavioral state transitions - /// - "intervention": Intervention-driven transitions - /// - "overdose": Overdose-related transitions - /// - "background_death": Background mortality transitions - /// @param log_name The logger name for error reporting (e.g., "console"). - /// @return A unique_ptr to the created Transition, or nullptr if type is - /// unsupported. - static std::unique_ptr - CreateTransition(const std::string &type, const std::string &log_name); -}; -} // namespace respond - -#endif // RESPOND_TRANSITION_FACTORY_HPP_ \ No newline at end of file diff --git a/src/markov.cpp b/src/model_factory.cpp similarity index 100% rename from src/markov.cpp rename to src/model_factory.cpp diff --git a/src/transition_factory.cpp b/src/transition_factory.cpp index 267d383e..9df2078c 100644 --- a/src/transition_factory.cpp +++ b/src/transition_factory.cpp @@ -4,14 +4,12 @@ // Created Date: 2026-02-05 // // Author: Matthew Carroll // // ----- // -// Last Modified: 2026-02-05 // +// Last Modified: 2026-07-02 // // Modified By: Matthew Carroll // // ----- // // Copyright (c) 2026 Syndemics Lab at Boston Medical Center // //////////////////////////////////////////////////////////////////////////////// -#include - #include #include #include @@ -26,9 +24,8 @@ #include "internals/overdose.hpp" namespace respond { -std::unique_ptr -TransitionFactory::CreateTransition(const std::string &type, - const std::string &log_name) { +std::unique_ptr Transition::Create(const std::string &type, + const std::string &log_name) { std::string type_copy = type; std::transform(type_copy.begin(), type_copy.end(), type_copy.begin(), [](unsigned char c) { return std::tolower(c); });