DPsim
Loading...
Searching...
No Matches
EMT_Ph3_PQLoad.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Institute for Automation of Complex Power Systems, EONERC, RWTH Aachen University
2// SPDX-License-Identifier: MPL-2.0
3
5
6#include <algorithm>
7#include <cmath>
8#include <stdexcept>
9
10using namespace CPS;
11
12namespace {
13
14bool isFiniteMatrix(const Matrix &matrix) {
15 for (Matrix::Index idx = 0; idx < matrix.size(); ++idx) {
16 if (!Math::isFinite(matrix.data()[idx]))
17 return false;
18 }
19 return true;
20}
21
22} // namespace
23
25 : MNASimPowerComp<Real>(uid, name, true, true, logLevel),
26 mActivePower(mAttributes->create<Real>("P", 0.0)),
27 mReactivePower(mAttributes->create<Real>("Q", 0.0)),
28 mNomVoltage(mAttributes->create<Real>("V_nom", 0.0)),
29 mMinimumVoltagePerUnit(mAttributes->create<Real>("V_min_pu", 0.1)),
30 mMeasuredActivePower(mAttributes->create<Real>("p_inst", 0.0)),
31 mMeasuredReactivePower(mAttributes->create<Real>("q_inst", 0.0)) {
34
35 **mIntfVoltage = Matrix::Zero(3, 1);
36 **mIntfCurrent = Matrix::Zero(3, 1);
37 mNumIter = mAttributes->create<Int>("NIterations", 0);
38
39 SPDLOG_LOGGER_INFO(mSLog, "Create {} {}", this->type(), name);
40}
41
44
45EMT::Ph3::PQLoad::PQLoad(String name, Real activePower, Real reactivePower,
46 Real nominalVoltage, Logger::Level logLevel)
47 : PQLoad(name, name, activePower, reactivePower, nominalVoltage, logLevel) {
48}
49
51 Real reactivePower, Real nominalVoltage,
52 Logger::Level logLevel)
53 : PQLoad(uid, name, logLevel) {
54 setParameters(activePower, reactivePower, nominalVoltage);
55}
56
57void EMT::Ph3::PQLoad::setParameters(Real activePower, Real reactivePower,
58 Real nominalVoltage,
59 Real minimumVoltagePerUnit) {
60 if (!Math::isFinite(activePower) || !Math::isFinite(reactivePower))
61 throw std::invalid_argument("PQLoad power set points must be finite.");
62 if (!Math::isFinite(nominalVoltage) || nominalVoltage <= 0.0)
63 throw std::invalid_argument("PQLoad nominal voltage must be positive.");
64 if (!Math::isFinite(minimumVoltagePerUnit) || minimumVoltagePerUnit <= 0.0)
65 throw std::invalid_argument(
66 "PQLoad minimum per-unit voltage must be positive.");
67
68 **mActivePower = activePower;
69 **mReactivePower = reactivePower;
70 **mNomVoltage = nominalVoltage;
71 **mMinimumVoltagePerUnit = minimumVoltagePerUnit;
72 mParametersSet = true;
73
74 SPDLOG_LOGGER_INFO(mSLog,
75 "\nActive power={} [W]"
76 "\nReactive power={} [var]"
77 "\nNominal line-to-line voltage={} [V]"
78 "\nMinimum voltage={} [pu]",
81}
82
84 auto copy = PQLoad::make(name, mLogLevel);
85 if (mParametersSet) {
86 copy->setParameters(**mActivePower, **mReactivePower, **mNomVoltage,
88 } else {
89 **copy->mMinimumVoltagePerUnit = **mMinimumVoltagePerUnit;
90 }
91 copy->setMaxIterations(mMaxIter);
92 copy->setTolerance(mTolerance);
93 return copy;
94}
95
97 Matrix voltageQuadrature = Matrix::Zero(3, 1);
98 voltageQuadrature << (voltage(1, 0) - voltage(2, 0)) / std::sqrt(3.0),
99 (voltage(2, 0) - voltage(0, 0)) / std::sqrt(3.0),
100 (voltage(0, 0) - voltage(1, 0)) / std::sqrt(3.0);
101 return voltageQuadrature;
102}
103
105 if (!isFiniteMatrix(voltage))
106 return Matrix::Zero(3, 1);
107
108 Matrix voltageNoZeroSequence = voltage;
109 voltageNoZeroSequence.array() -= voltageNoZeroSequence.mean();
110
111 const Matrix voltageQuadrature = quadratureVoltage(voltageNoZeroSequence);
112 const Real minimumVoltage = **mMinimumVoltagePerUnit * **mNomVoltage;
113 const Real denominator = std::max(voltageNoZeroSequence.squaredNorm(),
114 minimumVoltage * minimumVoltage);
115
116 return (**mActivePower * voltageNoZeroSequence +
117 **mReactivePower * voltageQuadrature) /
118 denominator;
119}
120
122 Matrix voltageNoZeroSequence = **mIntfVoltage;
123 voltageNoZeroSequence.array() -= voltageNoZeroSequence.mean();
124 const Matrix voltageQuadrature = quadratureVoltage(voltageNoZeroSequence);
125
127 voltageNoZeroSequence.cwiseProduct(**mIntfCurrent).sum();
129 voltageQuadrature.cwiseProduct(**mIntfCurrent).sum();
130}
131
133 if (!mParametersSet) {
134 **mActivePower = mTerminals[0]->singleActivePower();
135 **mReactivePower = mTerminals[0]->singleReactivePower();
136 **mNomVoltage = std::abs(mTerminals[0]->initialSingleVoltage());
137
138 if (**mNomVoltage <= 0.0) {
139 throw std::invalid_argument(
140 "PQLoad requires a positive nominal voltage when initialized from "
141 "terminal data.");
142 }
143 }
144
145 MatrixComp initialVoltageABC = MatrixComp::Zero(3, 1);
146 initialVoltageABC(0, 0) =
147 RMS3PH_TO_PEAK1PH * mTerminals[0]->initialSingleVoltage();
148 initialVoltageABC(1, 0) = initialVoltageABC(0, 0) * SHIFT_TO_PHASE_B;
149 initialVoltageABC(2, 0) = initialVoltageABC(0, 0) * SHIFT_TO_PHASE_C;
150
151 **mIntfVoltage = initialVoltageABC.real();
154
155 SPDLOG_LOGGER_INFO(mSLog,
156 "\n--- Initialization ---"
157 "\nVoltage: {:s}"
158 "\nCurrent: {:s}"
159 "\nActive-power set point: {} [W]"
160 "\nReactive-power set point: {} [var]"
161 "\n--- Initialization finished ---",
165}
166
171
173 SparseMatrixRow &systemMatrix) {
174 // The PQ load is represented by an ideal current sink and therefore has no
175 // system-matrix stamp.
176}
177
179 if (!terminalNotGrounded(0))
180 return;
181
182 for (UInt phase = 0; phase < 3; ++phase) {
183 // Positive interface current is consumed by the load and therefore is a
184 // negative injection into the network node.
185 Math::setVectorElement(rightVector, matrixNodeIndex(0, phase),
186 -(**mIntfCurrent)(phase, 0));
187 }
188}
189
191 **mIntfVoltage = Matrix::Zero(3, 1);
192 if (!terminalNotGrounded(0))
193 return;
194
195 for (UInt phase = 0; phase < 3; ++phase) {
196 (**mIntfVoltage)(phase, 0) =
197 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, phase));
198 }
199}
200
201void EMT::Ph3::PQLoad::mnaCompPreStep(Real time, Int timeStepCount) {
202 **mNumIter = 0;
204 (**mRightVector).setZero();
206}
207
209 Attribute<Matrix>::Ptr &leftVector) {
210 mnaCompUpdateVoltage(**leftVector);
212}
213
215 AttributeBase::List &prevStepDependencies,
216 AttributeBase::List &attributeDependencies,
217 AttributeBase::List &modifiedAttributes) {
218 prevStepDependencies.push_back(mIntfVoltage);
219 attributeDependencies.push_back(mActivePower);
220 attributeDependencies.push_back(mReactivePower);
221 attributeDependencies.push_back(mNomVoltage);
222 attributeDependencies.push_back(mMinimumVoltagePerUnit);
223 modifiedAttributes.push_back(mIntfCurrent);
224 modifiedAttributes.push_back(mNumIter);
225 modifiedAttributes.push_back(mRightVector);
226}
227
229 AttributeBase::List &prevStepDependencies,
230 AttributeBase::List &attributeDependencies,
231 AttributeBase::List &modifiedAttributes,
232 Attribute<Matrix>::Ptr &leftVector) {
233 attributeDependencies.push_back(leftVector);
234 attributeDependencies.push_back(mIntfCurrent);
235 modifiedAttributes.push_back(mIntfVoltage);
236 modifiedAttributes.push_back(mMeasuredActivePower);
237 modifiedAttributes.push_back(mMeasuredReactivePower);
238}
239
246
248 mnaCompUpdateVoltage(leftVector);
249}
250
252 if (!isFiniteMatrix(**mIntfVoltage) || !isFiniteMatrix(**mIntfCurrent))
253 return false;
254
255 const Matrix targetCurrent = calculateCurrent(**mIntfVoltage);
256 const Real currentBase = std::hypot(**mActivePower, **mReactivePower) /
257 std::max(**mNomVoltage, 1.0);
258 const Real currentScale =
259 std::max({targetCurrent.norm(), currentBase, 1e-12});
260 const Real relativeError =
261 (targetCurrent - **mIntfCurrent).norm() / currentScale;
262
263 if (relativeError <= mTolerance)
264 return false;
265
266 if (**mNumIter >= mMaxIter) {
267 SPDLOG_LOGGER_WARN(mSLog,
268 "PQLoad corrector reached its iteration limit ({}): "
269 "relative current residual={} (tolerance={}).",
270 mMaxIter, relativeError, mTolerance);
271 return false;
272 }
273
274 return true;
275}
std::vector< Ptr > List
Definition Attribute.h:123
AttributePointer< Attribute< T > > Ptr
Definition Attribute.h:249
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
const Attribute< Real >::Ptr mActivePower
Three-phase total active-power set point [W], positive for consumption.
void correctorStep() override
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override
void updateVoltage(const Matrix &leftVector) override
void mnaCompPreStep(Real time, Int timeStepCount) override
void updateMeasuredPower()
Updates the instantaneous measured powers.
void setParameters(Real activePower, Real reactivePower, Real nominalVoltage, Real minimumVoltagePerUnit=0.1)
Sets total three-phase P/Q, nominal line-to-line voltage and voltage floor.
const Attribute< Real >::Ptr mMeasuredReactivePower
Instantaneous measured three-phase reactive power [var].
PQLoad(String uid, String name, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and logging level.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
SimPowerComp< Real >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
static Matrix quadratureVoltage(const Matrix &voltage)
Calculates the voltage vector shifted by 90 degrees for positive sequence.
void mnaCompUpdateVoltage(const Matrix &leftVector) override
const Attribute< Real >::Ptr mReactivePower
bool requiresIteration() override
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
void initializeFromNodesAndTerminals(Real frequency) override
Initializes set points and instantaneous values from terminal data.
const Attribute< Real >::Ptr mNomVoltage
Nominal line-to-line RMS voltage [V].
const Attribute< Real >::Ptr mMeasuredActivePower
Instantaneous measured three-phase active power [W].
void mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
const Attribute< Real >::Ptr mMinimumVoltagePerUnit
Minimum line-to-line voltage in per unit of mNomVoltage.
void mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
Matrix calculateCurrent(const Matrix &voltage) const
Calculates the load current for a given instantaneous abc voltage.
String uid()
Returns unique id.
String type()
Get component type (cross-platform)
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
Definition Logger.h:33
static String matrixToString(const Matrix &mat)
Definition Logger.cpp:31
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
Attribute< Int >::Ptr mNumIter
static Real realFromVectorElement(const Matrix &mat, Matrix::Index row)
static void setVectorElement(Matrix &mat, Matrix::Index row, Complex value, Int maxFreq=1, Int freqIdx=0, Matrix::Index colOffset=0)
Definition MathUtils.cpp:73
static bool isFinite(Real value)
Definition MathUtils.cpp:63
UInt matrixNodeIndex(UInt nodeIndex)
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
SimTerminal< Real >::List mTerminals
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
std::shared_ptr< SimPowerComp< VarType > > Ptr
Bool terminalNotGrounded(UInt index)
Complex initialSingleVoltage(UInt index)
void setTerminalNumber(UInt num)
Logger::Level mLogLevel
Component logger control for internal variables.
bool mParametersSet
Flag indicating that parameters are set via setParameters() function.
Logger::Log mSLog
Component logger.
static std::shared_ptr< PQLoad > make(Args &&...args)
Definition PtrFactory.h:19
#define RMS3PH_TO_PEAK1PH
Definition Definitions.h:50
#define SHIFT_TO_PHASE_C
Definition Definitions.h:47
#define SHIFT_TO_PHASE_B
Definition Definitions.h:46
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
Definition Definitions.h:81
std::string String
Definition Definitions.h:65
double Real
Definition Definitions.h:62
int Int
Definition Definitions.h:61
Eigen::Matrix< Complex, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > MatrixComp
Dense matrix for complex numbers.
Definition Definitions.h:84
unsigned int UInt
Definition Definitions.h:60
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).
Definition Definitions.h:74