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
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/model.hpp>
#include <respond/timestep.hpp>
#include <respond/transition.hpp>
Eigen::VectorXd initial_state(50);
initial_state.setZero();
model->SetState(initial_state);
auto &behavior_transition = step.CreateTransition("behavior");
behavior_transition->AddMatrix(some_matrix);
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:30
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.
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/simulation.hpp>
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);
Manages and executes multiple models in a coordinated simulation. A Simulation aggregates Model insta...
Definition: simulation.hpp:32
Key Methods
Run(int duration = -1): Runs all models for the configured duration
SetDuration(int duration): Sets default duration used by Run() when no argument is provided
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/timestep.hpp>
#include <respond/transition.hpp>
auto &behavior = step.CreateTransition("behavior");
behavior->AddMatrix(behavior_matrix);
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:32
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:51
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/transition.hpp>
"behavior",
"behavior_name",
"my_logger"
);
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 and transitions accept a logger name:
respond::CreateFileLogger("my_logger", "path/to/logfile.log");
Complete Example
#include <respond/simulation.hpp>
#include <respond/model.hpp>
#include <respond/timestep.hpp>
#include <respond/logging.hpp>
int main() {
respond::CreateFileLogger("app", "simulation.log");
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) {
sim.AddModel(model);
sim.Run(duration);
}
Resetting Model State
Eigen::VectorXd initial_state = ...;
model->SetState(initial_state);
model->ClearTimesteps();
model->CreateDefaultHistories();
Copying Simulations
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/logging.hpp>
#include <respond/model.hpp>
#include <thread>
#include <vector>
int main() {
respond::SetLogPattern(respond::LogPattern::kThreadSafe);
respond::SetFlushInterval(3);
respond::CreateSharedLogger("model_1");
respond::CreateSharedLogger("model_2");
respond::CreateSharedLogger("model_3");
return 0;
}
Running Models in Parallel with Unified Logging
#include <respond/logging.hpp>
#include <respond/simulation.hpp>
#include <thread>
#include <vector>
void RunSimulation(int id, const std::string& log_file) {
std::string logger_name = "model_" + std::to_string(id);
respond::CreateSharedLogger(logger_name);
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();
std::string pattern_str = respond::LoggingConfig::GetPatternString(current);
Monitoring Shared Loggers
bool exists = (respond::CheckLoggerExists("model_1") == respond::CreationStatus::kExists);
std::string info = respond::GetLoggerInfo("model_1");
respond::SetLoggerLevel("model_1", spdlog::level::info);
respond::FlushAllLoggers();
Thread-Safe File Sink Management
The CreateSharedFileSink function creates file sinks that are automatically cached and reused:
auto sink = respond::CreateSharedFileSink("logs/simulation.log");
respond::CreateSharedLogger("logger_1");
respond::CreateSharedLogger("logger_2");
Best Practices for Parallel Logging
- Call
SetLogPattern() once at program startup, before creating any loggers
- Call
CreateSharedLogger() instead of CreateFileLogger() when using parallel execution
- Use
kThreadSafe pattern when logs will have high concurrent write volume
- Set
FlushInterval(0) for critical logging; use FlushInterval(3-5) for performance
- 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 logger names exist (create with
CreateFileLogger if needed)
- 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