This guide provides an overview of the RESPOND C++ API for developers wishing to use the library in their own projects.
Overview
The RESPOND library provides a flexible framework for building opioid use disorder models through composition of models, transitions, and history tracking. The core components are:
- Model: Abstract base class representing a state transition system
- Simulation: Aggregates and coordinates multiple models
- Timestep: Owns and sequences transitions for one simulation step
- Transition: Abstract base for specific transition types
- History: Tracks state vectors over time
Runtime Configuration
RuntimeConfig groups the logging and execution settings used by Simulation and Model. Use the configuration-based constructors and factory overloads for new code.
#include <respond/respond.hpp>
runtime_config.logging.logger_name = "respond";
runtime_config.logging.file_path = "respond.log";
runtime_config.logging.use_shared_sink = true;
unsigned int total_threads
Total worker threads available for concurrent model execution. A value of 0 selects the implementatio...
Definition: execution_config.hpp:17
bool run_models_concurrently
Whether models may be executed concurrently by the simulation. When multiple models actually run in p...
Definition: execution_config.hpp:26
unsigned int eigen_threads
Eigen worker threads used within a model. Use 1 when models are executed concurrently to avoid oversu...
Definition: execution_config.hpp:21
Groups execution and logging settings shared by runtime objects.
Definition: runtime_config.hpp:17
When multiple models run concurrently, keep eigen_threads at 1 because Eigen's worker setting is process-global. For sequential execution, including a single-model simulation, eigen_threads may be greater than 1.
Migrating from Legacy Constructors
The string-based Model::Create, Simulation, Timestep, and transition factory overloads are deprecated. Replace separate logger-name and file-path arguments with LoggingConfig, and pass it through RuntimeConfig when both logging and execution settings are needed:
config.logging = {"app", "simulation.log", false};
Manages and executes multiple models in a coordinated simulation. A Simulation aggregates Model insta...
Definition: simulation.hpp:39
Core Concepts
State Vectors
Models operate on state vectors (Eigen::VectorXd) representing the population distribution across model states. A state vector element at index i represents the count of individuals in state i.
Transitions
Transitions apply transformations to state vectors using transition matrices or vectors. The RESPOND model supports several transition types:
- Migration: Population movement between states
- Behavior: Behavioral state changes
- Intervention: Intervention-driven state changes
- Overdose: Overdose-related transitions
- BackgroundDeath: Background mortality transitions
History Tracking
History objects record state vectors at timesteps, enabling analysis of state trajectories over time. Histories support sparse timesteps and can return contiguous vectors with zero-filled gaps.
Model Class
The Model class is the abstract base for all models in RESPOND.
#include <respond/respond.hpp>
runtime_config.logging.logger_name = "model_logger";
runtime_config.logging.file_path = "model.log";
Eigen::VectorXd initial_state(50);
initial_state.setZero();
model->SetState(initial_state);
auto &behavior_transition = step.CreateTransition("behavior");
behavior_transition->AddMatrix(some_matrix);
"migration", "migration", runtime_config.logging);
step.AddTransition(migration_transition);
step[0]->AddMatrix(some_other_matrix);
model->AddTimestep(step);
model->RunTimestep();
Eigen::VectorXd current_state = model->GetState();
auto histories = model->GetHistories();
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.
Represents a single timestep in a simulation, managing a collection of transitions....
Definition: timestep.hpp:31
static std::unique_ptr< Transition > Create(const std::string &type, const std::string &name=RESPOND_DEFAULT_TRANSITION_NAME, const std::string &log_name=RESPOND_DEFAULT_LOG, const std::string &log_file=RESPOND_DEFAULT_LOG_FILE)
Creates a transition of the specified type.
Model::Create() currently creates the Markov implementation. Its string argument is an instance name, not a model-type selector, and any string is accepted as that name. Additional model implementations may be exposed by the factory in the future.
Key Methods
SetState(const Eigen::Ref<const Eigen::VectorXd> &state): Sets the model's state vector
GetState() const: Returns a const Eigen ref to the current state
AddTimestep(const Timestep ×tep): Adds a timestep (deep-copied)
RunTimestep(): Runs the current timestep and advances time
RunTimestep(size_t idx): Runs a specific timestep index
RunTimesteps(): Runs all registered timesteps (bounded by final timestep when set)
ClearTimesteps(): Removes all timesteps
GetHistories() const: Returns map of history name to History objects
CreateDefaultHistories(): Initializes default history tracking
ClearHistories(): Clears history records and resets history tracking
GetName() const: Returns model name
clone() const: Creates a deep copy of the model
Simulation Class
The Simulation class manages multiple models and coordinates their execution.
#include <respond/respond.hpp>
runtime_config.logging.logger_name = "simulation_logger";
runtime_config.logging.file_path = "simulation.log";
runtime_config.logging.use_shared_sink = true;
sim.AddModel(model1);
sim.AddModel(model2);
sim[0]->CreateDefaultHistories();
sim[1]->CreateDefaultHistories();
sim.Run(52);
auto model_0_histories = sim.GetModelHistory(0);
auto model_names = sim.GetModelNames();
auto history_names = sim.GetModelHistoryNames(0);
Key Methods
Run(int duration = -1): Runs all models; -1 uses the configured duration and positive values override it
SetDuration(int duration): Sets a positive default duration used by Run() when no argument is provided
CreateNewModel(const std::string &name): Creates and manages a model, then returns an editable deep copy; assign the edited copy through sim[idx]
AddModel(const std::unique_ptr<Model> &model): Adds a model (cloned internally)
operator[](size_t idx): Mutable index access to owned model slot (sim[idx]->Method())
operator[](size_t idx) const: Const index access to owned model
GetModels() const: Returns a deep-copied vector of models
GetModel(int idx) const: Returns one deep-copied model by index (-1 returns last)
GetModelIndexNameMap() const: Returns map of model index to model name
GetModelNames() const: Returns all model names
ClearModels(): Removes all models
GetModelHistory(size_t idx) const: Returns one model's history map
GetModelHistoryNames(size_t idx) const: Returns history names for one model
Timestep Class
The Timestep class owns transitions for one model step and supports both transition creation and clone-based insertion.
#include <respond/respond.hpp>
"timestep_logger", "timestep.log", false};
auto &behavior = step.CreateTransition("behavior");
behavior->AddMatrix(behavior_matrix);
"migration", "migration", logging_config);
step.AddTransition(migration);
step[0]->AddMatrix(another_behavior_matrix);
step[1] = step[0];
step[1] = migration;
Abstract base class representing a state transition operation. Transitions apply transformation matri...
Definition: transition.hpp:33
Describes the logging destination used by a library object.
Definition: logging_config.hpp:21
Key Methods
CreateTransition(const std::string &transition_name): Creates and stores a transition by type
AddTransition(const std::unique_ptr<Transition> &transition): Clones and stores caller-provided transition
operator[](size_t idx): Mutable slot access for transition mutation/replacement
operator[](size_t idx) const: Const transition reference by index
GetTransition(const size_t &idx) const: Gets transition pointer by index
GetTransition(const std::string &transition_name) const: Gets transition pointer by name
GetTransitionNames() const: Returns transition names in execution order
RemoveTransition(size_t idx): Removes and returns transition at index
Model Access Semantics
sim[idx] accesses the model owned by Simulation and can be used for in-place mutation.
sim[idx] = *other_model replaces the model at idx by cloning other_model.
sim[idx] = other_model_ptr replaces the model at idx by cloning the pointee (caller retains ownership).
GetModels() and GetModel(...) return clones for safe detached access.
- Name-based retrieval is not provided; use
GetModelIndexNameMap() to resolve names to indices.
History Class
The History class records and manages state vectors across timesteps.
#include <respond/history.hpp>
hist.AddState(state_vector_0, 0);
hist.AddState(state_vector_1, 1);
hist.AddState(state_vector_2, 2);
hist.AddState(another_state);
auto state_at_t0 = hist.GetStateMap()[0];
auto all_states = hist.GetStateAsVector();
std::string name = hist.GetName();
respond::HistoryMode mode = hist.GetHistoryMode();
hist.Clear();
Tracks and manages state vector history over time. History records state snapshots at discrete timest...
Definition: history.hpp:54
Key Methods
AddState(const Eigen::VectorXd &state, int timestep = -1): Records a state
- If timestep < 0, automatically assigns next available timestep
- If timestep already exists, currently overwrites
GetStateMap() const: Returns map of timestep → state vector
GetRecordedTimesteps() const: Returns stored timesteps without densifying
GetRecordedStates() const: Returns stored states without densifying
GetStateAsVector() const: Returns contiguous vector of states (fills gaps with zeros)
GetName() const: Returns history identifier
GetLatestRecordedTimestep() const: Returns latest recorded timestep
GetPendingState() const: Returns pending aggregate for accumulated histories
HasPendingState() const: Indicates pending aggregate state
Clear(): Removes all recorded states
operator==, operator!=: Comparison operators
Transition Class
The Transition class is abstract; use Transition::Create(...) to create concrete instances.
#include <respond/respond.hpp>
"transition_logger", "transitions.log", false};
"behavior",
"behavior_name",
logging_config
);
Eigen::MatrixXd trans_matrix = ...;
transition->AddMatrix(trans_matrix);
auto histories_map = ...;
Eigen::VectorXd result = transition->Execute(current_state, histories_map);
std::string name = transition->GetName();
transition->ClearMatrices();
Supported Transition Types
| Type | Description |
| "migration" | Population migration transitions |
| "behavior" | Behavioral state changes |
| "intervention" | Intervention-driven transitions |
| "overdose" | Overdose-related transitions |
| "background_death" | Background mortality transitions |
Logging Integration
RESPOND uses the spdlog library for logging. Models, timesteps, and transitions accept LoggingConfig, which specifies the logger name, file path, and whether the logger uses a shared sink:
"my_logger", "path/to/logfile.log", false};
respond::ConfigureLogger(logging_config);
Complete Example
#include <respond/respond.hpp>
int main() {
config.logging = {"app", "simulation.log", false};
Eigen::VectorXd initial_state = Eigen::VectorXd::Zero(50);
initial_state(0) = 1000;
model->SetState(initial_state);
auto &behavior_transition = step.CreateTransition("behavior");
auto &migration_transition = step.CreateTransition("migration");
for (int t = 0; t < 52; ++t) {
model->AddTimestep(step);
}
sim.AddModel(model);
sim[0]->CreateDefaultHistories();
sim.Run(52);
auto histories = sim.GetModelHistory(0);
auto history_names = sim.GetModelHistoryNames(0);
return 0;
}
Memory Management
RESPOND uses std::unique_ptr for ownership management:
- Models and Transitions are typically managed by Simulation or parent objects
- History objects are copyable and can be freely copied
- All models are cloned when added to a Simulation
- Clearing containers (for example
ClearModels, ClearTimesteps) deletes contained objects
Best Practices
- Use
Transition::Create to create transitions by type.
- Build timesteps explicitly and add them to models in execution order.
- Set simulation duration intentionally (
SetDuration or Run(duration)) to match timestep plans.
- Initialize loggers early before creating models and transitions.
- Validate matrix dimensions and ranges before adding matrices.
Common Patterns
Running Multiple Independent Simulations
for (int run = 0; run < num_runs; ++run) {
config.logging.logger_name = "logger_" + std::to_string(run);
config.logging.file_path = "run_" + std::to_string(run) + ".log";
sim.AddModel(model);
sim.Run(duration);
}
Resetting Model State
Eigen::VectorXd initial_state = ...;
model->SetState(initial_state);
model->ClearTimesteps();
model->CreateDefaultHistories();
Copying Simulations
config.logging.logger_name = "logger";
Parallel Execution with Shared Logging
When running multiple models in parallel, all loggers can safely write to the same file using RESPOND's shared sink functionality. This ensures thread-safe logging without file corruption.
Basic Parallel Logging Setup
#include <respond/respond.hpp>
#include <thread>
#include <vector>
int main() {
respond::ConfigureLogger({"model_1", "unified.log", true});
respond::SetLogPattern(respond::LogPattern::kThreadSafe);
respond::SetFlushInterval(0);
respond::ConfigureLogger({"model_2", "unified.log", true});
respond::ConfigureLogger({"model_3", "unified.log", true});
return 0;
}
Running Models in Parallel with Unified Logging
#include <respond/respond.hpp>
#include <thread>
#include <vector>
void RunSimulation(int id, const std::string& log_file) {
std::string logger_name = "model_" + std::to_string(id);
config.logging = {logger_name, log_file, true};
Eigen::VectorXd initial_state = Eigen::VectorXd::Zero(50);
initial_state(0) = 1000;
model->SetState(initial_state);
sim.AddModel(model);
sim.Run(52);
respond::FlushAllLoggers();
}
int main() {
respond::SetLogPattern(respond::LogPattern::kThreadSafe);
respond::SetFlushInterval(0);
const int num_threads = 4;
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back(RunSimulation, i, "unified.log");
}
for (auto& t : threads) {
t.join();
}
return 0;
}
Shared Logger Pattern Options
The LogPattern enum controls log format for all shared loggers:
- **
kSimple**: Minimal format [logger_name] message
- **
kStandard**: Includes time and thread ID (default)
- **
kDetailed**: Full timestamp with milliseconds; best for debugging
- **
kThreadSafe**: Optimized for concurrent writes with sequence numbers
respond::SetLogPattern(respond::LogPattern::kDetailed);
auto current = respond::GetLogPattern();
Monitoring Shared Loggers
bool exists = (respond::CheckLoggerExists("model_1") == respond::CreationStatus::kExists);
std::string info = respond::GetLoggerInfo("model_1");
respond::SetLoggerLevel("model_1", 2);
respond::FlushAllLoggers();
Thread-Safe File Sink Management
Shared sinks are automatically cached and reused by ConfigureLogger when LoggingConfig::use_shared_sink is true:
respond::ConfigureLogger({"logger_1", "logs/simulation.log", true});
respond::ConfigureLogger({"logger_2", "logs/simulation.log", true});
The legacy CreateSharedFileSink and CreateSharedLogger functions remain available for existing code. New code should use ConfigureLogger so the destination is explicit. Reusing a logger name with the same destination returns CreationStatus::kExists; using a different destination returns CreationStatus::kError.
Best Practices for Parallel Logging
- Call
SetLogPattern() once at program startup, before creating any loggers
- Set
LoggingConfig::use_shared_sink to true when parallel loggers should write to one file
- Use
kThreadSafe pattern when logs will have high concurrent write volume
- Set
FlushInterval(0) when each message must be flushed immediately; positive values are currently reserved and do not enable periodic flushing
- Call
FlushAllLoggers() at end of main before exit to ensure all writes complete
- Monitor logger levels with
GetLoggerInfo() when debugging multi-model runs
Troubleshooting
- Assertion failures: Ensure matrix dimensions match state vector size before adding to transitions
- Empty histories: Call
CreateDefaultHistories() after model setup or manually add histories
- Logger errors: Ensure each logger name has one consistent destination; use
ConfigureLogger with the intended LoggingConfig
- Memory issues: Verify no circular unique_ptr references; models own transitions
For more information, see the Doxygen-generated API documentation or the Architecture and Design guide.
Previous: Architecture and Design
Next: Data Guide