12#ifndef RESPOND_SIMULATION_HPP_
13#define RESPOND_SIMULATION_HPP_
15#include <respond/constants.hpp>
16#include <respond/eigen_config.hpp>
17#include <respond/history.hpp>
18#include <respond/logging.hpp>
19#include <respond/model.hpp>
20#include <respond/runtime_config.hpp>
45 : _owner(&owner), _idx(idx) {}
54 operator Model &() {
return _owner->GetModelRefOrThrow(_idx); }
58 return *
this =
static_cast<const Model &
>(
59 other._owner->GetModelRefOrThrow(other._idx));
64 _owner->GetModelRefOrThrow(_idx);
65 _owner->_models[_idx] = model.
clone();
73 LogError(_owner->_runtime_config.logging.logger_name,
74 "Cannot assign null model pointer to simulation "
76 throw std::invalid_argument(
77 "Error attempting to assign null model pointer.");
79 _owner->GetModelRefOrThrow(_idx);
80 _owner->_models[_idx] = model->clone();
101 [[deprecated(
"Use Simulation(RuntimeConfig) instead")]]
105 LoggingConfig{log_name, RESPOND_DEFAULT_LOG_FILE, false}}) {}
110 [[deprecated(
"Use Simulation(RuntimeConfig) instead")]]
111 Simulation(
const std::string &log_name,
const std::string &log_filepath)
120 [[deprecated(
"Use Simulation(RuntimeConfig) instead")]]
121 Simulation(
const std::string &log_name,
const std::string &log_filepath,
124 execution_config,
LoggingConfig{log_name, log_filepath, false}}) {
129 : _runtime_config(runtime_config) {
130 if (ConfigureLogger(_runtime_config.logging) ==
131 CreationStatus::kError) {
132 throw std::runtime_error(
133 "Error attempting to initialize simulation logger.");
145 _runtime_config = other._runtime_config;
146 for (
const auto &m : other._models) {
147 _models.push_back(m->clone());
149 _duration = other._duration;
150 _parameter_change_times = other._parameter_change_times;
151 _stratify_entering_cohort = other._stratify_entering_cohort;
152 _build_summary_stats = other._build_summary_stats;
153 _save_state_history = other._save_state_history;
154 _timesteps_to_report = other._timesteps_to_report;
155 _pivot_long = other._pivot_long;
162 if (
this != &other) {
163 std::vector<std::unique_ptr<Model>> models;
164 for (
const auto &m : other._models) {
165 models.push_back(m->clone());
167 _runtime_config = other._runtime_config;
168 _models = std::move(models);
169 _duration = other._duration;
170 _parameter_change_times = other._parameter_change_times;
171 _stratify_entering_cohort = other._stratify_entering_cohort;
172 _build_summary_stats = other._build_summary_stats;
173 _save_state_history = other._save_state_history;
174 _timesteps_to_report = other._timesteps_to_report;
175 _pivot_long = other._pivot_long;
183 : _runtime_config(std::move(other._runtime_config)),
184 _models(std::move(other._models)), _duration(other._duration),
185 _parameter_change_times(std::move(other._parameter_change_times)),
186 _stratify_entering_cohort(other._stratify_entering_cohort),
187 _build_summary_stats(other._build_summary_stats),
188 _save_state_history(other._save_state_history),
189 _timesteps_to_report(std::move(other._timesteps_to_report)),
190 _pivot_long(other._pivot_long) {}
196 if (
this != &other) {
197 _runtime_config = std::move(other._runtime_config);
198 _models = std::move(other._models);
199 _duration = other._duration;
200 _parameter_change_times = std::move(other._parameter_change_times);
201 _stratify_entering_cohort = other._stratify_entering_cohort;
202 _build_summary_stats = other._build_summary_stats;
203 _save_state_history = other._save_state_history;
204 _timesteps_to_report = std::move(other._timesteps_to_report);
205 _pivot_long = other._pivot_long;
224 _models.push_back(
Model::Create(model_name, _runtime_config));
225 return _models.back()->clone();
234 void AddModel(
const std::unique_ptr<Model> &model) {
236 LogError(_runtime_config.logging.logger_name,
237 "Cannot add a null model to the simulation.");
238 throw std::invalid_argument(
239 "Error attempting to add a null model to simulation.");
241 _models.push_back(model->clone());
253 void Run(
int duration = -1) {
254 if (duration == 0 || duration < -1) {
255 throw std::invalid_argument(
256 "Simulation duration must be positive or -1.");
258 if (_models.empty()) {
259 LogError(_runtime_config.logging.logger_name,
260 "Cannot run a simulation with no models.");
261 throw std::invalid_argument(
262 "Error attempting to run simulation with no models.");
264 std::lock_guard<std::mutex> execution_lock(
265 detail::GetEigenExecutionMutex());
267 _duration = duration;
269 const auto &execution = _runtime_config.execution;
270 const unsigned int thread_limit = std::thread::hardware_concurrency();
271 const unsigned int eigen_threads =
272 thread_limit == 0 ? execution.eigen_threads
273 : std::min(execution.eigen_threads, thread_limit);
274 Eigen::setNbThreads(eigen_threads);
276 LogInfo(_runtime_config.logging.logger_name,
277 "Running simulation for duration of " +
278 std::to_string(_duration) +
" timesteps.");
279 const auto run_model = [
this](
const std::unique_ptr<Model> &model) {
280 model->SetFinalTimestep(_duration);
281 model->RunTimesteps();
283 if (!execution.run_models_concurrently || _models.size() < 2) {
284 for (
const auto &model : _models) {
290 if (execution.eigen_threads > 1) {
291 throw std::invalid_argument(
292 "Concurrent model execution requires eigen_threads == 1.");
295 unsigned int worker_limit = execution.total_threads;
296 if (worker_limit == 0) {
297 worker_limit = std::thread::hardware_concurrency();
298 if (worker_limit == 0) {
302 const auto worker_count =
303 std::min<size_t>(worker_limit, _models.size());
304 if (worker_count <= 1) {
305 for (
const auto &model : _models) {
311 std::atomic<size_t> next_model{0};
312 std::exception_ptr first_exception;
313 std::mutex exception_mutex;
314 std::vector<std::thread> workers;
315 workers.reserve(worker_count);
317 for (
size_t worker = 0; worker < worker_count; ++worker) {
318 workers.emplace_back([&]() {
320 const size_t index = next_model.fetch_add(1);
321 if (index >= _models.size()) {
325 run_model(_models[index]);
327 std::lock_guard<std::mutex> lock(exception_mutex);
328 if (!first_exception) {
329 first_exception = std::current_exception();
337 for (
auto &worker : workers) {
340 if (first_exception) {
341 std::rethrow_exception(first_exception);
354 std::vector<std::unique_ptr<Model>> _models_copy;
355 for (
const auto &model : _models) {
356 _models_copy.push_back(model->clone());
368 GetModelRefOrThrow(idx);
377 return GetModelRefOrThrow(idx);
388 std::map<size_t, std::string> ret;
389 for (
size_t i = 0; i < _models.size(); ++i) {
390 ret[i] = _models[i]->GetName();
401 if (_models.empty()) {
402 LogError(_runtime_config.logging.logger_name,
403 "No models available in GetModel.");
404 throw std::out_of_range(
"Error attempting to GetModel: no models.");
406 if (idx < -1 || idx >=
static_cast<int>(_models.size())) {
407 LogError(_runtime_config.logging.logger_name,
408 "Index out of range in GetModel: " + std::to_string(idx));
409 throw std::out_of_range(
"Error attempting to GetModel by index.");
413 return _models.back()->clone();
415 return _models[idx]->clone();
421 std::vector<std::string> ret;
422 for (
auto &m : _models) {
423 ret.push_back(m->GetName());
433 if (idx >= _models.size()) {
434 LogError(_runtime_config.logging.logger_name,
435 "Index out of range in GetModelHistory: " +
436 std::to_string(idx));
437 throw std::out_of_range(
438 "Error attempting to GetModelHistory by index.");
440 return _models[idx]->GetHistories();
447 if (idx >= _models.size()) {
448 LogError(_runtime_config.logging.logger_name,
449 "Index out of range in GetModelHistoryNames: " +
450 std::to_string(idx));
451 throw std::out_of_range(
452 "Error attempting to GetModelHistoryNames by index.");
454 std::vector<std::string> ret;
455 for (
const auto &kv : _models[idx]->GetHistories()) {
456 ret.push_back(kv.first);
466 throw std::invalid_argument(
467 "Simulation duration must be positive.");
469 _duration = duration;
475 return _runtime_config.execution;
481 _runtime_config.execution = execution_config;
484 const RuntimeConfig &GetRuntimeConfig()
const {
return _runtime_config; }
486 void SetRuntimeConfig(
const RuntimeConfig &runtime_config) {
487 if (ConfigureLogger(runtime_config.logging) == CreationStatus::kError) {
488 LogError(_runtime_config.logging.logger_name,
489 "Unable to apply simulation runtime logging config.");
490 throw std::invalid_argument(
491 "Error attempting to apply simulation runtime logging "
494 _runtime_config = runtime_config;
498 Model &GetModelRefOrThrow(
size_t idx) {
499 if (idx >= _models.size()) {
500 LogError(_runtime_config.logging.logger_name,
501 "Index out of range in model access: " +
502 std::to_string(idx));
503 throw std::out_of_range(
"Error attempting to access model by "
506 return *_models[idx];
509 const Model &GetModelRefOrThrow(
size_t idx)
const {
510 if (idx >= _models.size()) {
511 LogError(_runtime_config.logging.logger_name,
512 "Index out of range in model access: " +
513 std::to_string(idx));
514 throw std::out_of_range(
"Error attempting to access model by "
517 return *_models[idx];
520 RuntimeConfig _runtime_config;
521 std::vector<std::unique_ptr<Model>> _models;
524 std::vector<int> _parameter_change_times;
525 bool _stratify_entering_cohort =
false;
527 bool _build_summary_stats =
false;
528 bool _save_state_history =
false;
529 std::vector<int> _timesteps_to_report;
530 bool _pivot_long =
false;
Abstract base class representing a state transition model. Models manage a state vector,...
Definition: model.hpp:31
virtual std::unique_ptr< Model > clone() const =0
Creates a deep copy of this model.
static std::unique_ptr< Model > Create(const std::string &name, const std::string &log_name=RESPOND_DEFAULT_LOG, const std::string &log_filepath=RESPOND_DEFAULT_LOG_FILE)
Factory method to create a Model instance.
Proxy for mutable model slot access with clone-based assignment.
Definition: simulation.hpp:42
Model * operator->()
Access the underlying model pointer for member access.
Definition: simulation.hpp:48
Model & operator*()
Dereference to the underlying model.
Definition: simulation.hpp:51
ModelSlotProxy & operator=(const ModelSlotProxy &other)
Replace this slot by cloning from another proxy's model.
Definition: simulation.hpp:57
ModelSlotProxy & operator=(const Model &model)
Replace this slot by cloning from a model reference.
Definition: simulation.hpp:63
ModelSlotProxy & operator=(const std::unique_ptr< Model > &model)
Replace this slot by cloning from a model unique_ptr.
Definition: simulation.hpp:71
Manages and executes multiple models in a coordinated simulation. A Simulation aggregates Model insta...
Definition: simulation.hpp:39
std::vector< std::string > GetModelNames() const
Retrieves the names of all models in the simulation.
Definition: simulation.hpp:420
void SetExecutionConfig(const ExecutionConfig &execution_config)
Sets the simulation execution settings.
Definition: simulation.hpp:480
Simulation(Simulation &&other) noexcept
Move constructor for transferring simulation ownership.
Definition: simulation.hpp:182
std::vector< std::unique_ptr< Model > > GetModels() const
Retrieves all models in the simulation.
Definition: simulation.hpp:353
~Simulation()=default
Virtual destructor for polymorphic cleanup.
std::map< size_t, std::string > GetModelIndexNameMap() const
Provide a mapping of model indices to their names for all models in the simulation.
Definition: simulation.hpp:387
void Run(int duration=-1)
Executes the simulation for all registered models. Models run sequentially by default....
Definition: simulation.hpp:253
Simulation(const std::string &log_name)
Constructs a Simulation with a specified logger.
Definition: simulation.hpp:102
void ClearModels()
Removes all models from the simulation.
Definition: simulation.hpp:229
Simulation(const RuntimeConfig &runtime_config)
Constructs a Simulation with shared runtime settings.
Definition: simulation.hpp:128
const std::map< std::string, History > & GetModelHistory(size_t idx) const
Retrieves the complete state histories for the model at the index.
Definition: simulation.hpp:432
Simulation(const Simulation &other)
Copy constructor creating an independent deep copy of the simulation. All models are cloned; modifica...
Definition: simulation.hpp:144
void SetDuration(int duration)
Sets the default duration used by Run().
Definition: simulation.hpp:464
Simulation(const std::string &log_name, const std::string &log_filepath, const ExecutionConfig &execution_config)
Constructs a Simulation with logger and execution settings.
Definition: simulation.hpp:121
std::unique_ptr< Model > GetModel(int idx) const
Retrieves a specific model by index in the simulation.
Definition: simulation.hpp:400
const ExecutionConfig & GetExecutionConfig() const
Retrieves the simulation execution settings.
Definition: simulation.hpp:474
std::unique_ptr< Model > CreateNewModel(const std::string &model_name)
Creates a new model instance and adds it to the simulation.
Definition: simulation.hpp:223
Simulation()
Default constructor for a Simulation instance. Initializes the simulation with the default logger.
Definition: simulation.hpp:97
Simulation & operator=(const Simulation &other)
Copy assignment operator for deep copying simulation state.
Definition: simulation.hpp:161
const Model & operator[](size_t idx) const
Const index-based model access.
Definition: simulation.hpp:376
Simulation(const std::string &log_name, const std::string &log_filepath)
Constructs a Simulation with a specified logger and log file.
Definition: simulation.hpp:111
Simulation & operator=(Simulation &&other) noexcept
Move assignment operator for transferring simulation ownership.
Definition: simulation.hpp:195
void AddModel(const std::unique_ptr< Model > &model)
Adds a model to the simulation. The model is cloned and managed by the simulation.
Definition: simulation.hpp:234
const std::vector< std::string > GetModelHistoryNames(size_t idx) const
Retrieves history names for the model at the specified index.
Definition: simulation.hpp:446
ModelSlotProxy operator[](size_t idx)
Mutable index-based model access.
Definition: simulation.hpp:367
Controls how a Simulation distributes execution resources.
Definition: execution_config.hpp:14
Describes the logging destination used by a library object.
Definition: logging_config.hpp:21
Groups execution and logging settings shared by runtime objects.
Definition: runtime_config.hpp:17