Skip to content

Commit fdb2f09

Browse files
committed
expose values for gens and buses for later interop
1 parent de3fc2e commit fdb2f09

9 files changed

Lines changed: 110 additions & 23 deletions

File tree

‎GridKit/Model/OptimalPowerFlow/Bus/Bus.hpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ namespace GridKit
7575

7676
[[gnu::always_inline]] void internalConstraints(const ScalarT* x, const ScalarT* external, ScalarT* g) const override;
7777

78+
/// Real voltage [p.u.]
79+
const ScalarT& Vr() const
80+
{
81+
return x_.getData()[static_cast<size_t>(BusInternalVariables::VR)];
82+
}
83+
84+
/// Imaginary voltage [p.u.]
85+
const ScalarT& Vi() const
86+
{
87+
return x_.getData()[static_cast<size_t>(BusInternalVariables::VI)];
88+
}
89+
7890
private:
7991
void initializeParameters(const ModelDataT& data);
8092

‎GridKit/Model/OptimalPowerFlow/Generator/Generator.hpp‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace GridKit
4444
using ModelDataT = GeneratorData<RealT, IdxT>;
4545
using BusT = Bus<scalar_type, index_type>;
4646

47+
using ComponentT::x_;
4748
using ComponentT::x_lower_;
4849
using ComponentT::x_upper_;
4950

@@ -69,6 +70,18 @@ namespace GridKit
6970
[[gnu::always_inline]] ScalarT objective(const ScalarT* x, const ScalarT* external) const override;
7071
[[gnu::always_inline]] void externalConstraints(const ScalarT* x, const ScalarT* external, ScalarT* g) const override;
7172

73+
/// Active power into the bus [p.u.]
74+
const ScalarT& P() const
75+
{
76+
return x_.getData()[static_cast<size_t>(GeneratorInternalVariables::P)];
77+
}
78+
79+
/// Reactive power into the bus [p.u.]
80+
const ScalarT& Q() const
81+
{
82+
return x_.getData()[static_cast<size_t>(GeneratorInternalVariables::Q)];
83+
}
84+
7285
private:
7386
void initializeParameters(const ModelDataT& data);
7487
void setDerivedParameters();

‎GridKit/Model/OptimalPowerFlow/SystemModel.hpp‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77

88
#include <map>
99
#include <memory>
10+
#include <string>
1011
#include <vector>
1112

1213
#include <GridKit/Model/ConfigurationChecks.hpp>
14+
#include <GridKit/Model/OptimalPowerFlow/Bus/Bus.hpp>
1315
#include <GridKit/Model/OptimalPowerFlow/Component.hpp>
16+
#include <GridKit/Model/OptimalPowerFlow/Generator/Generator.hpp>
1417
#include <GridKit/Model/OptimalPowerFlow/SystemModelData.hpp>
1518
#include <GridKit/Model/OptimizationEvaluator.hpp>
1619

1720
namespace GridKit
1821
{
1922
namespace OptimalPowerFlow
2023
{
21-
template <typename scalar_type, typename index_type>
22-
class Bus;
23-
2424
/**
2525
* @brief AC optimal power flow in Cartesian coordinates
2626
*
@@ -38,6 +38,7 @@ namespace GridKit
3838
using IndexVectorT = LinearAlgebra::Vector<IdxT, IdxT>;
3939
using CsrMatrixT = typename EvaluatorT::CsrMatrixT;
4040
using BusT = Bus<ScalarT, IdxT>;
41+
using GeneratorT = Generator<ScalarT, IdxT>;
4142
using Block = typename ComponentT::Block;
4243
using CooBufferT = typename ComponentT::CooBuffer;
4344

@@ -72,6 +73,12 @@ namespace GridKit
7273
int evaluateJacobian() override;
7374
int evaluateHessian(RealT sigma) override;
7475

76+
/// Bus by number, which views its solution after `allocate()`
77+
BusT* getBus(IdxT number);
78+
79+
/// Generator by case-file `id`, which views its solution after `allocate()`
80+
GeneratorT* getGenerator(const std::string& id);
81+
7582
private:
7683
/// Component entries of one derivative matrix and the CSR entry of each
7784
struct Assembly
@@ -83,7 +90,7 @@ namespace GridKit
8390
void addBus(const BusData<RealT, IdxT>& data);
8491

8592
template <typename ModelT, typename DataT>
86-
void addDevice(const DataT& data);
93+
ModelT* addDevice(const DataT& data);
8794

8895
int compose();
8996
int bind(ComponentT& component, const Block& offset);
@@ -98,6 +105,9 @@ namespace GridKit
98105
/// Buses by number
99106
std::map<IdxT, BusT*> buses_;
100107

108+
/// Generators by id
109+
std::map<std::string, GeneratorT*> generators_;
110+
101111
Assembly jacobian_assembly_;
102112
Assembly hessian_assembly_;
103113
};

‎GridKit/Model/OptimalPowerFlow/SystemModelImpl.hpp‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace GridKit
4545

4646
for (const auto& generator : data.generator)
4747
{
48-
addDevice<Generator<ScalarT, IdxT>>(generator);
48+
generators_.emplace(generator.id, addDevice<GeneratorT>(generator));
4949
}
5050

5151
for (const auto& load : data.load)
@@ -234,6 +234,21 @@ namespace GridKit
234234
return 0;
235235
}
236236

237+
/// @throw std::out_of_range for an unknown bus
238+
template <typename scalar_type, typename index_type>
239+
typename SystemModel<scalar_type, index_type>::BusT* SystemModel<scalar_type, index_type>::getBus(IdxT number)
240+
{
241+
return buses_.at(number);
242+
}
243+
244+
/// @throw std::out_of_range for an unknown generator
245+
template <typename scalar_type, typename index_type>
246+
typename SystemModel<scalar_type, index_type>::GeneratorT*
247+
SystemModel<scalar_type, index_type>::getGenerator(const std::string& id)
248+
{
249+
return generators_.at(id);
250+
}
251+
237252
template <typename scalar_type, typename index_type>
238253
void SystemModel<scalar_type, index_type>::addBus(const BusData<RealT, IdxT>& data)
239254
{
@@ -248,7 +263,7 @@ namespace GridKit
248263
/// Device from `data`, connected at each port to the bus it names
249264
template <typename scalar_type, typename index_type>
250265
template <typename ModelT, typename DataT>
251-
void SystemModel<scalar_type, index_type>::addDevice(const DataT& data)
266+
ModelT* SystemModel<scalar_type, index_type>::addDevice(const DataT& data)
252267
{
253268
using Ports = typename DataT::Ports;
254269
static_assert(ModelT::NUM_PORT_VARIABLES.size() == Utilities::enum_size<Ports>());
@@ -276,7 +291,9 @@ namespace GridKit
276291
}
277292
}
278293

294+
ModelT* const added = device.get();
279295
components_.push_back(std::move(device));
296+
return added;
280297
}
281298

282299
/// Size the system vectors and bind each component to its blocks

‎GridKit/Solver/Optimization/IpoptInterface.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ namespace AnalysisManager
207207
copyInto(model_->zLower(), z_L);
208208
copyInto(model_->zUpper(), z_U);
209209
model_->evaluateObjective();
210+
model_->evaluateConstraints();
210211
}
211212

212213
template <typename scalar_type, typename index_type>

‎GridKit/Solver/Optimization/IpoptInterface.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ namespace AnalysisManager
9191
ipindex* jCol,
9292
ipnumber* values) override;
9393

94-
/// Leave the final point, its multipliers, and its objective in the model
94+
/// Leave the final point, its multipliers, its objective, and its constraints in the model
9595
void finalize_solution(SolverReturn status,
9696
ipindex n,
9797
const ipnumber* x,

‎tests/UnitTests/OptimalPowerFlow/IpoptTests.hpp‎

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ namespace GridKit
2222
using SystemT = OPF::SystemModel<RealT, IdxT>;
2323

2424
public:
25-
// Ipopt stops with the constraints and the stationarity within its tolerance. They reach 5e-13 and 2e-13.
25+
// Ipopt stops with the constraints and the stationarity within its tolerance. They reach 5e-13 and 2e-13,
26+
// and the marginal prices 8e-13.
2627
static constexpr RealT kTol = static_cast<RealT>(1.0e-10);
2728

2829
TestOutcome reference()
@@ -41,10 +42,9 @@ namespace GridKit
4142
return success.report(__func__);
4243
}
4344

44-
const RealT* x = system.x().getData();
45-
46-
success *= isEqual(x[1], ZERO<RealT>, kTol);
47-
success *= !isEqual(x[3], ZERO<RealT>, kTol) && !isEqual(x[5], ZERO<RealT>, kTol);
45+
success *= isEqual(system.getBus(1)->Vi(), ZERO<RealT>, kTol);
46+
success *= !isEqual(system.getBus(2)->Vi(), ZERO<RealT>, kTol);
47+
success *= !isEqual(system.getBus(3)->Vi(), ZERO<RealT>, kTol);
4848

4949
return success.report(__func__);
5050
}
@@ -65,22 +65,50 @@ namespace GridKit
6565
return success.report(__func__);
6666
}
6767

68-
success *= system.evaluateConstraints() == 0;
69-
if (!success)
70-
{
71-
return success.report(__func__);
72-
}
7368
const RealT* g = system.g().getData();
7469
for (const IdxT i : SystemTestData::BALANCE_ROWS)
7570
{
7671
success *= isEqual(g[i], ZERO<RealT>, kTol);
7772
}
7873
success *= isEqual(g[SystemTestData::FLOW_ROWS[0]], SystemTestData::SMAX * SystemTestData::SMAX, kTol);
7974

80-
const RealT* x = system.x().getData();
75+
const RealT p1 = system.getGenerator("gen_1")->P();
76+
const RealT p3 = system.getGenerator("gen_3")->P();
77+
78+
success *= p1 > p3;
79+
success *= p3 > ZERO<RealT>;
8180

82-
success *= x[6] > x[8];
83-
success *= x[8] > ZERO<RealT>;
81+
return success.report(__func__);
82+
}
83+
84+
/// The negative of the active power multiplier of a bus is the marginal
85+
/// cost of a generator there within its limits
86+
TestOutcome marginalPrices()
87+
{
88+
TestStatus success = true;
89+
90+
SystemT system(SystemTestData::data());
91+
success *= system.allocate() == 0 && system.initialize() == 0;
92+
if (!success)
93+
{
94+
return success.report(__func__);
95+
}
96+
success *= optimize(system);
97+
if (!success)
98+
{
99+
return success.report(__func__);
100+
}
101+
102+
const auto price = [&system](IdxT number)
103+
{
104+
const auto P = static_cast<size_t>(OPF::BusInternalConstraints::P);
105+
return -system.getBus(number)->lambda().getData()[P];
106+
};
107+
const auto marginalCost = [&system](const char* id, RealT c1)
108+
{ return c1 + TWO<RealT> * SystemTestData::C2 * system.getGenerator(id)->P(); };
109+
110+
success *= isEqual(price(1), marginalCost("gen_1", SystemTestData::C1_GEN_1), kTol);
111+
success *= isEqual(price(3), marginalCost("gen_3", SystemTestData::C1_GEN_3), kTol);
84112

85113
return success.report(__func__);
86114
}

‎tests/UnitTests/OptimalPowerFlow/SystemTestData.hpp‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ namespace GridKit
2020
static constexpr RealT LOAD_P = 1.2;
2121
static constexpr RealT LOAD_Q = 0.3;
2222

23+
/// Linear costs of gen_1 and gen_3, and the quadratic cost of both
24+
static constexpr RealT C1_GEN_1 = 10.0;
25+
static constexpr RealT C1_GEN_3 = 20.0;
26+
static constexpr RealT C2 = 0.01;
27+
2328
/// Constraints of the buses in order, P, Q, and VM2 each, then S1 and S2
2429
/// of the rated branch, then of the unrated branches
2530
static constexpr std::array<IdxT, 6> BALANCE_ROWS{0, 1, 3, 4, 6, 7};
@@ -49,8 +54,8 @@ namespace GridKit
4954
addBranch(data, "branch_1_3", 1, 3, 0.02, 0.2, 0.0);
5055
data.branch[0].parameters[OPF::BranchParameters::Smax] = SMAX;
5156

52-
addGenerator(data, "gen_1", 1, 10.0);
53-
addGenerator(data, "gen_3", 3, 20.0);
57+
addGenerator(data, "gen_1", 1, C1_GEN_1);
58+
addGenerator(data, "gen_3", 3, C1_GEN_3);
5459

5560
auto& load = data.load.emplace_back();
5661
load.id = "load_2";
@@ -88,7 +93,7 @@ namespace GridKit
8893
generator.parameters[OPF::GeneratorParameters::Qmin] = -1.0;
8994
generator.parameters[OPF::GeneratorParameters::Qmax] = 1.0;
9095
generator.parameters[OPF::GeneratorParameters::c1] = c1;
91-
generator.parameters[OPF::GeneratorParameters::c2] = 0.01;
96+
generator.parameters[OPF::GeneratorParameters::c2] = C2;
9297
}
9398
};
9499
} // namespace Testing

‎tests/UnitTests/OptimalPowerFlow/runIpoptTests.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ int main()
88
result += test.reference();
99
result += test.solve();
1010
result += test.multipliers();
11+
result += test.marginalPrices();
1112

1213
return result.summary();
1314
}

0 commit comments

Comments
 (0)