#ifndef GRIDOXIDE_HPP
#define GRIDOXIDE_HPP
#include "gridoxide.h"
#include <cstddef>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace gridoxide {
class Error : public std::runtime_error {
public:
Error(gridoxide_status status, const std::string &what)
: std::runtime_error(what), status_(status) {}
gridoxide_status status() const noexcept { return status_; }
private:
gridoxide_status status_;
};
namespace detail {
inline std::string last_error(gridoxide_status status) {
const char *message = gridoxide_last_error_message();
if (message != nullptr) {
return std::string(message);
}
return "gridoxide call failed with status " + std::to_string(static_cast<int>(status));
}
inline void check(gridoxide_status status) {
if (status != GRIDOXIDE_STATUS_OK) {
throw Error(status, last_error(status));
}
}
}
enum class Terminal : int { From = 0, To = 1 };
class PowerFlow {
public:
static gridoxide_options default_options() noexcept {
gridoxide_options options{};
gridoxide_options_default(&options);
return options;
}
static PowerFlow from_pgm_file(const std::string &path,
const gridoxide_options &options = default_options()) {
gridoxide_powerflow *handle = nullptr;
detail::check(gridoxide_powerflow_from_pgm_file(path.c_str(), &options, &handle));
return PowerFlow(handle);
}
static PowerFlow from_pgm_string(const std::string &json,
const gridoxide_options &options = default_options()) {
gridoxide_powerflow *handle = nullptr;
detail::check(gridoxide_powerflow_from_pgm_string(json.data(), json.size(), &options,
&handle));
return PowerFlow(handle);
}
static PowerFlow from_arrays(const std::vector<gridoxide_bus> &buses,
const std::vector<gridoxide_line> &lines = {},
const std::vector<gridoxide_transformer> &transformers = {},
const std::vector<gridoxide_shunt> &shunts = {},
const gridoxide_options &options = default_options()) {
gridoxide_powerflow *handle = nullptr;
detail::check(gridoxide_powerflow_from_arrays(
buses.data(), buses.size(), lines.data(), lines.size(), transformers.data(),
transformers.size(), shunts.data(), shunts.size(), &options, &handle));
return PowerFlow(handle);
}
~PowerFlow() { gridoxide_powerflow_free(handle_); }
PowerFlow(const PowerFlow &) = delete;
PowerFlow &operator=(const PowerFlow &) = delete;
PowerFlow(PowerFlow &&other) noexcept : handle_(other.handle_) { other.handle_ = nullptr; }
PowerFlow &operator=(PowerFlow &&other) noexcept {
if (this != &other) {
gridoxide_powerflow_free(handle_);
handle_ = other.handle_;
other.handle_ = nullptr;
}
return *this;
}
void solve() { detail::check(gridoxide_powerflow_solve(handle_)); }
void solve_enforcing_q_limits(std::size_t max_outer_iterations = 10) {
detail::check(gridoxide_powerflow_solve_enforcing_q_limits(handle_, max_outer_iterations));
}
void solve_distributing_slack(const std::vector<double> &factors = {}) {
const double *data = factors.empty() ? nullptr : factors.data();
detail::check(
gridoxide_powerflow_solve_distributing_slack(handle_, data, factors.size()));
}
std::size_t bus_count() const noexcept { return gridoxide_powerflow_bus_count(handle_); }
std::size_t branch_count() const noexcept {
return gridoxide_powerflow_branch_count(handle_);
}
std::vector<double> voltage_magnitude() const {
std::vector<double> out(bus_count());
detail::check(gridoxide_powerflow_voltage_magnitude(handle_, out.data(), out.size()));
return out;
}
std::vector<double> voltage_angle() const {
std::vector<double> out(bus_count());
detail::check(gridoxide_powerflow_voltage_angle(handle_, out.data(), out.size()));
return out;
}
std::pair<std::vector<double>, std::vector<double>>
branch_flow(Terminal terminal = Terminal::From) const {
std::vector<double> p(branch_count());
std::vector<double> q(branch_count());
detail::check(gridoxide_powerflow_branch_flow(handle_, static_cast<int>(terminal),
p.data(), q.data(), p.size()));
return {std::move(p), std::move(q)};
}
std::vector<double> slack_shift() const {
std::vector<double> out(bus_count());
gridoxide_status status =
gridoxide_powerflow_slack_shift(handle_, out.data(), out.size());
if (status == GRIDOXIDE_STATUS_NO_ANSWER) {
return {};
}
detail::check(status);
return out;
}
std::size_t iterations() const noexcept {
return gridoxide_powerflow_iterations(handle_);
}
double max_mismatch() const noexcept {
return gridoxide_powerflow_max_mismatch(handle_);
}
std::size_t island_count() const noexcept {
return gridoxide_powerflow_island_count(handle_);
}
gridoxide_island_status island_status(std::size_t island) const {
gridoxide_island_status out{};
detail::check(gridoxide_powerflow_island_status(handle_, island, &out));
return out;
}
std::vector<std::size_t> island_buses(std::size_t island) const {
std::vector<std::size_t> out(gridoxide_powerflow_island_bus_count(handle_, island));
detail::check(gridoxide_powerflow_island_buses(handle_, island, out.data(), out.size()));
return out;
}
gridoxide_powerflow *handle() const noexcept { return handle_; }
private:
explicit PowerFlow(gridoxide_powerflow *handle) noexcept : handle_(handle) {}
gridoxide_powerflow *handle_;
};
inline std::string version() { return std::string(gridoxide_version()); }
inline void check_abi_compatibility() {
const uint32_t linked = gridoxide_abi_version();
if (linked != GRIDOXIDE_ABI_VERSION) {
throw Error(GRIDOXIDE_STATUS_INTERNAL,
"gridoxide ABI mismatch: this header describes version " +
std::to_string(GRIDOXIDE_ABI_VERSION) +
", but the linked library speaks version " + std::to_string(linked));
}
}
}
#endif