gtopt::SDDPMethod class

Iterative SDDP solver for multi-phase power system planning.

Wraps a PlanningLP and adds Benders decomposition on top of the per-phase LP subproblems. Handles reservoir volumes, capacity expansion variables, and future state-variable types generically.

Supports multiple scenes (solved in parallel), optimality cut sharing between scenes, iterative feasibility backpropagation, and cut persistence for hot-start.

API for external monitoring / GUI integration

The solver exposes a rich API that enables GUI or monitoring tools to observe the iterative process and control execution:

  • Callback: register an SDDPIterationCallback via set_iteration_callback(). It is invoked after every iteration with the full SDDPIterationResult. Return true from the callback to request a stop.
  • Programmatic stop: call request_stop() from any thread; the solver checks this flag at the start of each iteration and exits gracefully, saving all accumulated cuts.
  • Live query: call current_iteration(), current_gap(), current_lower_bound(), current_upper_bound() at any time (they are atomic and thread-safe) to poll the solver's convergence state.
  • Sentinel file: same as PLP's userstop — check for a sentinel file on disk.
SDDPMethod sddp(planning_lp, SDDPOptions{.max_iterations = 100});

// Register a callback that prints progress and stops at gap < 1e-6
sddp.set_iteration_callback([](const SDDPIterationResult& r) {
    fmt::print("iter {} gap={:.6f}\n", r.iteration, r.gap);
    return r.gap < 1e-6;  // true → stop
});

// Start solving (blocks until done / stopped)
auto results = sddp.solve();

// Or stop programmatically from another thread:
sddp.request_stop();

Constructors, destructors, conversion operators

SDDPMethod(PlanningLP& planning_lp, SDDPOptions opts = {}) explicit noexcept

Public functions

auto clear_stop() -> void noexcept
Clear a previous stop request (e.g., before re-running solve()).
auto clear_stored_cuts() -> void noexcept
auto current_gap() const -> double noexcept
Current relative convergence gap.
auto current_iteration() const -> int noexcept
Current iteration number (0 before first iteration completes)
auto current_lower_bound() const -> double noexcept
Current lower bound (phase-0 objective including α)
auto current_pass() const -> int noexcept
Current pass: 0=idle, 1=forward, 2=backward.
auto current_upper_bound() const -> double noexcept
Current upper bound (sum of actual phase costs)
auto cut_store() -> SDDPCutStore& noexcept
Access the cut store (for cascade orchestration, etc.).
auto ensure_initialized() -> std::expected< void, Error > -> auto
auto forget_first_cuts(int count) -> void
auto global_max_kappa() const -> double noexcept
Get the global max kappa across all (scene, phase) LP solves.
auto has_converged() const -> bool noexcept
Whether the solver has converged.
auto is_stop_requested() const -> bool noexcept
Check whether a stop has been requested.
auto load_boundary_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error > -> auto
auto load_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error > -> auto
auto load_named_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error > -> auto
auto load_scene_cuts_from_directory(const std::string& directory) -> std::expected< CutLoadResult, Error > -> auto
auto load_state(const std::string& filepath) -> std::expected< void, Error > -> auto
auto mutable_options() -> constexpr auto& noexcept
Mutable options access (for cascade orchestration between solve() calls).
auto num_stored_cuts() const -> int noexcept
auto options() const -> constexpr auto& noexcept
SDDP options (const)
auto phase_states() const -> constexpr auto& noexcept
Legacy accessor for scene 0 (backward compatibility)
auto phase_states(SceneIndex scene) const -> constexpr auto& noexcept
Per-phase state for a given scene.
auto request_stop() -> void noexcept
auto save_all_scene_cuts(const std::string& directory) const -> std::expected< void, Error > -> auto
auto save_cuts(const std::string& filepath) const -> std::expected< void, Error > -> auto
Save accumulated cuts to a CSV file for hot-start.
auto save_scene_cuts(SceneIndex scene, const std::string& directory) const -> std::expected< void, Error > -> auto
auto save_state(const std::string& filepath) const -> std::expected< void, Error > -> auto
auto scenes_done() const -> int noexcept
Number of scenes completed in the current pass.
auto set_iteration_callback(SDDPIterationCallback cb) -> void noexcept
auto solve(const SolverOptions& lp_opts = {}) -> std::expected< std::vector< SDDPIterationResult >, Error > -> auto
Run the SDDP iterative solve.
auto stored_cuts() const -> const auto& noexcept
All stored cuts (for persistence / inspection)
auto update_stored_cut_duals() -> void

Function documentation

void gtopt::SDDPMethod::clear_stored_cuts() noexcept

Clear all stored cut metadata (combined + per-scene). Used by CascadePlanningMethod between cascade phases.

auto gtopt::SDDPMethod::ensure_initialized() -> std::expected< void, Error >

Ensure the solver is initialized (alpha variables, state links, etc.). Safe to call multiple times (guarded by m_initialized_ flag). Call before loading external cuts that reference alpha columns.

void gtopt::SDDPMethod::forget_first_cuts(int count)

Parameters
count Number of leading cuts to remove (clamped to size).

Remove the first count cuts from stored cuts and from the LP. This deletes the LP rows associated with those cuts (via delete_rows) and erases them from m_stored_cuts_. Used by the cascade solver's "forget inherited cuts" feature: the first N cuts loaded via hot-start are dropped so the solver continues with only self-generated cuts.

auto gtopt::SDDPMethod::load_boundary_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error >

Returns CutLoadResult with count and max iteration, or an error.

Load boundary (future-cost) cuts from a named-variable CSV file.

The CSV header names the state variables (e.g. reservoir or battery names); subsequent rows provide {name, scenario, rhs, coefficients}. Cuts are added only to the last phase, with an alpha column created if needed. This is analogous to PLP's "planos de embalse".

auto gtopt::SDDPMethod::load_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error >

Load cuts from a CSV file and add to all scenes' phase LPs. Cuts are broadcast to all scenes regardless of originating scene, since loaded cuts serve as warm-start approximations for the entire problem (analogous to PLP's cut sharing across scenarios).

auto gtopt::SDDPMethod::load_named_cuts(const std::string& filepath) -> std::expected< CutLoadResult, Error >

Returns CutLoadResult with count and max iteration, or an error.

Load named-variable cuts from a CSV file with a phase column.

Unlike load_boundary_cuts() (which loads into the last phase only), this method resolves named state-variable headers in each specified phase and adds the cuts to the corresponding phase LP. The CSV format is: name,iteration,scene,phase,rhs,StateVar1,StateVar2,...

This is used for hot-start from PLP planos data where cuts span multiple stages (mapped to gtopt phases).

auto gtopt::SDDPMethod::load_scene_cuts_from_directory(const std::string& directory) -> std::expected< CutLoadResult, Error >

Load all per-scene cut files from a directory. Files matching scene_<N>.csv are loaded; files with the error_ prefix (from infeasible scenes in a previous run) are skipped to prevent loading invalid cuts during hot-start.

auto gtopt::SDDPMethod::load_state(const std::string& filepath) -> std::expected< void, Error >

Load state variable column solutions from a CSV file. Sets the warm column solution on each phase's LinearInterface so that physical_eini/physical_efin return loaded values before the first solve.

int gtopt::SDDPMethod::num_stored_cuts() const noexcept

Number of stored cuts (thread-safe). In single_cut_storage mode, counts across all per-scene vectors.

void gtopt::SDDPMethod::request_stop() noexcept

Request the solver to stop gracefully after the current iteration. Thread-safe — may be called from any thread.

auto gtopt::SDDPMethod::save_all_scene_cuts(const std::string& directory) const -> std::expected< void, Error >

Save all scenes' cuts to per-scene files in the given directory. Each scene gets its own file: <directory>/scene_<N>.csv.

auto gtopt::SDDPMethod::save_scene_cuts(SceneIndex scene, const std::string& directory) const -> std::expected< void, Error >

Save cuts for a single scene to a per-scene file. Uses scene-specific storage, avoiding lock contention when called in parallel for different scenes.

auto gtopt::SDDPMethod::save_state(const std::string& filepath) const -> std::expected< void, Error >

Save state variable column solutions and reduced costs to a CSV file. Writes one row per column with its name, phase, scene, value, and reduced cost. Saved alongside cuts for hot-start state restoration.

void gtopt::SDDPMethod::set_iteration_callback(SDDPIterationCallback cb) noexcept

Register a callback invoked after each iteration. If the callback returns true, the solver stops after that iteration.

void gtopt::SDDPMethod::update_stored_cut_duals()

Update dual values of stored cuts from the current LP solution. Call after the solver finishes to populate the dual field in each StoredCut with the row dual from the last forward-pass solve.