9#include <dpsim/PFSolverPowerPolar.h>
17 CPS::Logger::Level logLevel)
18 :
PFSolver(name, system, timeStep, logLevel) {}
21 bool keep_last_solution) {
22 const UInt n =
mSystem.mNodes.size();
24 bool can_keep = keep_last_solution && mHasLastConvergedSolution &&
25 mLastConvergedV.size() == n && mLastConvergedD.size() == n;
27 SPDLOG_LOGGER_INFO(
mSLog,
28 "PF initialization: keep_last_solution={}, can_keep={}",
29 keep_last_solution, can_keep);
35 sol_V = mLastConvergedV;
36 sol_D = mLastConvergedD;
40 for (
auto comp :
mSystem.mComponents) {
41 if (
auto load = std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
42 if (load->use_profile)
50 std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(comp)) {
57 UInt idx = pq->matrixNodeIndex();
67 for (
auto comp :
mSystem.mComponentsAtNode[pq]) {
68 if (
auto load = std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
69 sol_P(idx) -= load->attributeTyped<CPS::Real>(
"P_pu")->get();
70 sol_Q(idx) -= load->attributeTyped<CPS::Real>(
"Q_pu")->get();
71 }
else if (
auto sst = std::dynamic_pointer_cast<
73 sol_P(idx) -= sst->getNodalInjection(pq).real();
74 sol_Q(idx) -= sst->getNodalInjection(pq).imag();
75 }
else if (
auto vsi = std::dynamic_pointer_cast<
82 std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(
84 sol_P(idx) += gen->attributeTyped<CPS::Real>(
"P_set_pu")->get();
85 sol_Q(idx) += gen->attributeTyped<CPS::Real>(
"Q_set_pu")->get();
95 UInt idx = pv->matrixNodeIndex();
105 for (
auto comp :
mSystem.mComponentsAtNode[pv]) {
106 if (
auto gen = std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(
108 sol_P(idx) += gen->attributeTyped<CPS::Real>(
"P_set_pu")->get();
109 sol_V(idx) = gen->attributeTyped<CPS::Real>(
"V_set_pu")->get();
110 }
else if (
auto load =
111 std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
112 sol_P(idx) -= load->attributeTyped<CPS::Real>(
"P_pu")->get();
113 sol_Q(idx) -= load->attributeTyped<CPS::Real>(
"Q_pu")->get();
114 }
else if (
auto vsi = std::dynamic_pointer_cast<
118 }
else if (
auto extnet =
119 std::dynamic_pointer_cast<CPS::SP::Ph1::NetworkInjection>(
121 sol_P(idx) += extnet->attributeTyped<CPS::Real>(
"p_inj")->get() /
123 sol_V(idx) = extnet->attributeTyped<CPS::Real>(
"V_set_pu")->get();
133 UInt idx = vd->matrixNodeIndex();
140 for (
auto comp :
mSystem.mComponentsAtNode[vd]) {
142 std::dynamic_pointer_cast<CPS::SP::Ph1::NetworkInjection>(comp)) {
143 sol_V(idx) = extnet->attributeTyped<CPS::Real>(
"V_set_pu")->get();
144 }
else if (
auto load =
145 std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
146 sol_P(idx) -= load->attributeTyped<CPS::Real>(
"P_pu")->get();
147 sol_Q(idx) -= load->attributeTyped<CPS::Real>(
"Q_pu")->get();
148 }
else if (
auto gen =
149 std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(
151 sol_P(idx) += gen->attributeTyped<CPS::Real>(
"P_set_pu")->get();
152 sol_Q(idx) += gen->attributeTyped<CPS::Real>(
"Q_set_pu")->get();
153 sol_V(idx) = gen->attributeTyped<CPS::Real>(
"V_set_pu")->get();
173 for (UInt a = 0; a < npqpv; ++a) {
176 mF(a) = Pesp.coeff(k) -
P(k);
180 mF(a + npqpv) = Qesp.coeff(k) -
Q(k);
193 for (UInt a = 0; a < npqpv; ++a) {
196 mJ.coeffRef(a, a) = -
Q(k) -
B(k, k) *
sol_V.coeff(k) *
sol_V.coeff(k);
199 for (UInt b = 0; b < npqpv; ++b) {
206 mJ.coeffRef(a, b) = val;
214 for (UInt a = 0; a < npqpv; ++a) {
219 mJ.coeffRef(a + da, a + db) =
231 mJ.coeffRef(a + da, b + db) = val;
243 mJ.coeffRef(a + da, a + db) =
247 for (UInt b = 0; b < npqpv; ++b) {
255 mJ.coeffRef(a + da, b + db) = -val;
267 mJ.coeffRef(a + da, a + db) =
279 mJ.coeffRef(a + da, b + db) = val;
291 const double maxDVpu = 0.1;
292 const double maxDThetaRad = 0.2;
296 for (UInt a = 0; a < npqpv; ++a) {
297 double dTheta = std::abs(
mX.coeff(a));
298 if (dTheta > maxDThetaRad)
299 scale = std::min(scale, maxDThetaRad / dTheta);
302 double dVrel = std::abs(
mX.coeff(npqpv + b));
304 scale = std::min(scale, maxDVpu / dVrel);
307 for (UInt a = 0; a < npqpv; ++a) {
309 sol_D(k) += scale *
mX.coeff(a);
312 sol_V(k) *= (1.0 + scale *
mX.coeff(a + npqpv));
315 for (
auto node :
mSystem.mNodes) {
316 UInt idx = node->matrixNodeIndex();
324 SPDLOG_LOGGER_WARN(
mSLog,
"Not converged within {} iterations",
327 SPDLOG_LOGGER_WARN(
mSLog,
"Writing last iterate to result state "
328 "(not stored as warm-start solution).");
334 mLastConvergedV =
sol_V;
335 mLastConvergedD =
sol_D;
336 mHasLastConvergedSolution =
true;
341 SPDLOG_LOGGER_INFO(
mSLog,
"Solution written to result state:");
343 SPDLOG_LOGGER_INFO(
mSLog,
"Name\tP\t\tQ\t\tV\t\tD");
345 for (
auto node :
mSystem.mNodes) {
346 UInt idx = node->matrixNodeIndex();
349 mSLog,
"{}\t{}\t{}\t{}\t{}",
356 for (UInt i = 0; i <
mSystem.mNodes.size(); ++i) {
361 for (
auto node :
mSystem.mNodes) {
362 auto simNode = std::dynamic_pointer_cast<CPS::SimNode<CPS::Complex>>(node);
364 UInt idx = node->matrixNodeIndex();
376 for (
auto line :
mLines) {
378 v(0) =
sol_V_complex.coeff(line->node(0)->matrixNodeIndex());
379 v(1) =
sol_V_complex.coeff(line->node(1)->matrixNodeIndex());
381 VectorComp current = line->Y_element() * v;
383 VectorComp flow_on_branch = v.array() * current.conjugate().array();
384 line->updateBranchFlow(current, flow_on_branch);
388 v(0) =
sol_V_complex.coeff(trafo->node(0)->matrixNodeIndex());
389 v(1) =
sol_V_complex.coeff(trafo->node(1)->matrixNodeIndex());
391 VectorComp current = trafo->Y_element() * v;
393 VectorComp flow_on_branch = v.array() * current.conjugate().array();
394 trafo->updateBranchFlow(current, flow_on_branch);
399 for (
auto node :
mSystem.mNodes) {
400 std::list<std::shared_ptr<CPS::SP::Ph1::PiLine>> lines;
401 for (
auto comp :
mSystem.mComponentsAtNode[node]) {
402 if (std::shared_ptr<CPS::SP::Ph1::PiLine> line =
403 std::dynamic_pointer_cast<CPS::SP::Ph1::PiLine>(comp)) {
404 line->storeNodalInjection(
sol_S_complex.coeff(node->matrixNodeIndex()));
405 lines.push_back(line);
410 for (
auto comp :
mSystem.mComponentsAtNode[node]) {
411 if (std::shared_ptr<CPS::SP::Ph1::Transformer> trafo =
412 std::dynamic_pointer_cast<CPS::SP::Ph1::Transformer>(comp)) {
413 trafo->storeNodalInjection(
424 for (UInt j = 0; j <
mSystem.mNodes.size(); ++j) {
428 return sol_V.coeff(k) * val;
433 for (UInt j = 0; j <
mSystem.mNodes.size(); ++j) {
437 return sol_V.coeff(k) * val;
442 auto node_idx = topoNode->matrixNodeIndex();
445 CPS::Complex I(0.0, 0.0);
446 for (UInt j = 0; j <
mSystem.mNodes.size(); ++j)
449 CPS::Complex S =
sol_Vcx(node_idx) * conj(I);
452 CPS::Complex Sgen = S;
453 for (
auto comp :
mSystem.mComponentsAtNode[topoNode]) {
454 if (
auto loadPtr = std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
455 Sgen += CPS::Complex(**(loadPtr->mActivePowerPerUnit),
456 **(loadPtr->mReactivePowerPerUnit));
461 for (
auto comp :
mSystem.mComponentsAtNode[topoNode]) {
463 std::dynamic_pointer_cast<CPS::SP::Ph1::NetworkInjection>(comp)) {
469 std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(
477 sol_P(node_idx) = S.real();
478 sol_Q(node_idx) = S.imag();
483 auto node_idx = topoNode->matrixNodeIndex();
486 CPS::Complex I(0.0, 0.0);
487 for (UInt j = 0; j <
mSystem.mNodes.size(); ++j)
490 CPS::Complex S =
sol_Vcx(node_idx) * conj(I);
493 CPS::Complex Sgen = S;
494 for (
auto comp :
mSystem.mComponentsAtNode[topoNode]) {
495 if (
auto loadPtr = std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(comp)) {
496 Sgen += CPS::Complex(**(loadPtr->mActivePowerPerUnit),
497 **(loadPtr->mReactivePowerPerUnit));
502 for (
auto comp :
mSystem.mComponentsAtNode[topoNode]) {
504 std::dynamic_pointer_cast<CPS::SP::Ph1::SynchronGenerator>(
512 sol_Q(node_idx) = S.imag();
519 auto node_idx = topoNode->matrixNodeIndex();
522 CPS::Complex I(0.0, 0.0);
523 for (UInt j = 0; j <
mSystem.mNodes.size(); ++j)
525 CPS::Complex S =
sol_Vcx(node_idx) * conj(I);
528 CPS::Real V =
sol_V.coeff(node_idx);
529 for (
auto comp :
mSystem.mComponentsAtNode[topoNode])
530 if (
auto shuntPtr = std::dynamic_pointer_cast<CPS::SP::Ph1::Shunt>(comp))
532 S += std::pow(V, 2) * Complex(-**(shuntPtr->mConductancePerUnit),
533 **(shuntPtr->mSusceptancePerUnit));
536 sol_P(node_idx) = S.real();
537 sol_Q(node_idx) = S.imag();
542 sol_P = CPS::Vector(n);
543 sol_Q = CPS::Vector(n);
544 sol_V = CPS::Vector(n);
545 sol_D = CPS::Vector(n);
CPS::TopologicalNode::List mPQBuses
Vector of nodes characterized as PQ buses.
UInt mNumPQBuses
Number of PQ nodes.
std::vector< std::shared_ptr< CPS::SP::Ph1::PiLine > > mLines
Vector of line components.
std::vector< std::shared_ptr< CPS::SP::Ph1::Transformer > > mTransformers
Vector of transformer components.
CPS::Matrix mJ
Jacobian matrix.
CPS::Vector mX
Solution vector.
CPS::Bool solutionInitialized
Flag whether solution vectors are initialized.
CPS::Real mBaseApparentPower
Base power of per-unit system.
CPS::SparseMatrixCompRow mY
Admittance matrix.
CPS::Bool solutionComplexInitialized
Flag whether complex solution vectors are initialized.
UInt mNumPVBuses
Number of PV nodes.
CPS::Real B(int i, int j)
Gets the imaginary part of admittance matrix element.
CPS::Bool isConverged
Convergence flag.
CPS::Vector mF
Vector of mismatch values.
CPS::UInt mIterations
Actual number of iterations.
PFSolver(CPS::String name, CPS::SystemTopology system, Real timeStep, CPS::Logger::Level logLevel)
Constructor to be used in simulation examples.
CPS::SystemTopology mSystem
System list.
std::map< CPS::TopologicalNode::Ptr, CPS::Real > mBaseVoltageAtNode
Map providing determined base voltages for each node.
CPS::TopologicalNode::List mVDBuses
Vector of nodes characterized as VD buses.
CPS::TopologicalNode::List mPVBuses
Vector of nodes characterized as PV buses.
CPS::Real G(int i, int j)
Gets the real part of admittance matrix element.
std::vector< CPS::UInt > mPQPVBusIndices
Vector with indices of both PQ and PV buses.
CPS::Vector sol_P
Solution vector of active power.
void resize_complex_sol(CPS::Int n)
Resize complex solution vector.
void calculateBranchFlow()
Calculate branch flows from current solution and store them in line and transformer components.
void calculateQAtPVBuses()
Calculate the reactive power at all PV buses from current solution.
CPS::VectorComp sol_S_complex
Solution vector of representing sol_P and sol_Q as complex quantity.
PFSolverPowerPolar(CPS::String name, const CPS::SystemTopology &system, CPS::Real timeStep, CPS::Logger::Level logLevel)
Constructor to be used in simulation examples.
CPS::Vector sol_D
Solution vector of voltage angle.
CPS::Real sol_Vi(CPS::UInt k)
Calculate imaginary part of voltage from sol_V and sol_D.
void calculatePAndQAtSlackBus()
Calculate P and Q at slack bus from current solution.
CPS::Vector sol_Q
Solution vector of reactive power.
CPS::Real Q(CPS::UInt k)
Calculate the reactive power at a bus from current solution.
void calculateMismatch() override
Calculate mismatch.
CPS::Complex sol_Vcx(CPS::UInt k)
Calculate complex voltage from sol_V and sol_D.
void generateInitialSolution(Real time, bool keep_last_solution=false) override
Generate initial solution for current time step.
void calculateJacobian() override
Calculate the Jacobian.
void updateSolution() override
Update solution in each iteration.
void resize_sol(CPS::Int n)
Resize solution vector.
CPS::Real sol_Vr(CPS::UInt k)
Calculate real part of voltage from sol_V and sol_D.
CPS::Real P(CPS::UInt k)
Calculate active power at a bus from current solution.
void calculateNodalInjection()
Calculate nodal power injections and store them in first line or transformer (in case no line is conn...
CPS::Vector sol_V
Solution vector of voltage magnitude.
void calculatePAndQInjectionPQBuses()
Calculate complex power flowing from this node to the other nodes.
CPS::VectorComp sol_V_complex
Solution vector of representing sol_V and sol_D as complex quantity.
void setSolution() override
Set final solution.
CPS::Logger::Log mSLog
Logger.