DPsim
Loading...
Searching...
No Matches
EMT_Ph3_RXLoad.cpp
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <dpsim-models/EMT/EMT_Ph3_RXLoad.h>
10
11using namespace CPS;
12
13EMT::Ph3::RXLoad::RXLoad(String uid, String name, Logger::Level logLevel)
14 : CompositePowerComp<Real>(uid, name, true, true, Logger::Level::trace),
15 mActivePower(mAttributes->create<Matrix>("P")),
16 mReactivePower(mAttributes->create<Matrix>("Q")),
17 mNomVoltage(mAttributes->create<Real>("V_nom")),
18 mReactanceInSeries(false) {
19 mPhaseType = PhaseType::ABC;
20 setTerminalNumber(1);
21
22 SPDLOG_LOGGER_INFO(mSLog, "Create {} {}", this->type(), name);
23 **mIntfVoltage = Matrix::Zero(3, 1);
24 **mIntfCurrent = Matrix::Zero(3, 1);
25 mSLog->flush();
26}
27
28EMT::Ph3::RXLoad::RXLoad(String name, Logger::Level logLevel)
29 : RXLoad(name, name, logLevel) {}
30
31EMT::Ph3::RXLoad::RXLoad(String name, Matrix activePower, Matrix reactivePower,
32 Real volt, Logger::Level logLevel)
33 : RXLoad(name, logLevel) {
34 **mActivePower = activePower;
35 **mReactivePower = reactivePower;
36 mPower = MatrixComp::Zero(3, 3);
37 mPower << Complex((**mActivePower)(0, 0), (**mReactivePower)(0, 0)),
38 Complex((**mActivePower)(0, 1), (**mReactivePower)(0, 1)),
39 Complex((**mActivePower)(0, 2), (**mReactivePower)(0, 2)),
40 Complex((**mActivePower)(1, 0), (**mReactivePower)(1, 0)),
41 Complex((**mActivePower)(1, 1), (**mReactivePower)(1, 1)),
42 Complex((**mActivePower)(1, 2), (**mReactivePower)(1, 2)),
43 Complex((**mActivePower)(2, 0), (**mReactivePower)(2, 0)),
44 Complex((**mActivePower)(2, 1), (**mReactivePower)(2, 1)),
45 Complex((**mActivePower)(2, 2), (**mReactivePower)(2, 2));
46
47 **mNomVoltage = volt;
48 initPowerFromTerminal = false;
49}
50
51void EMT::Ph3::RXLoad::setParameters(Matrix activePower, Matrix reactivePower,
52 Real volt, bool reactanceInSeries) {
53 **mActivePower = activePower;
54 **mReactivePower = reactivePower;
55 mReactanceInSeries = reactanceInSeries;
56
57 if (mReactanceInSeries) {
58 setVirtualNodeNumber(1);
59 }
60 // complex power
61 mPower = MatrixComp::Zero(3, 3);
62 mPower(0, 0) = {(**mActivePower)(0, 0), (**mReactivePower)(0, 0)};
63 mPower(1, 1) = {(**mActivePower)(1, 1), (**mReactivePower)(1, 1)};
64 mPower(2, 2) = {(**mActivePower)(2, 2), (**mReactivePower)(2, 2)};
65
66 **mNomVoltage = volt;
67
68 SPDLOG_LOGGER_INFO(mSLog,
69 "\nActive Power [W]: {}"
70 "\nReactive Power [VAr]: {}",
71 Logger::matrixToString(**mActivePower),
72 Logger::matrixToString(**mReactivePower));
73 SPDLOG_LOGGER_INFO(mSLog, "Nominal Voltage={} [V]", **mNomVoltage);
74
75 initPowerFromTerminal = false;
76}
77
80 return;
81 mSubCompCreated = true;
82
83 // Intentionally empty: which of R/L/C exist depends on the load power sign, known only in
84 // initializeParentFromNodesAndTerminals(), where the sub-components are created. Safe: any
85 // series-mode virtual node is already declared in setParameters().
86}
87
89 Real omega = 2. * PI * frequency;
90
91 if (initPowerFromTerminal) {
92 **mActivePower = Matrix::Zero(3, 3);
93 (**mActivePower)(0, 0) = mTerminals[0]->singleActivePower() / 3.;
94 (**mActivePower)(1, 1) = mTerminals[0]->singleActivePower() / 3.;
95 (**mActivePower)(2, 2) = mTerminals[0]->singleActivePower() / 3.;
96
97 **mReactivePower = Matrix::Zero(3, 3);
98 (**mReactivePower)(0, 0) = mTerminals[0]->singleReactivePower() / 3.;
99 (**mReactivePower)(1, 1) = mTerminals[0]->singleReactivePower() / 3.;
100 (**mReactivePower)(2, 2) = mTerminals[0]->singleReactivePower() / 3.;
101
102 // complex power
103 mPower = MatrixComp::Zero(3, 3);
104 mPower(0, 0) = {(**mActivePower)(0, 0), (**mReactivePower)(0, 0)};
105 mPower(1, 1) = {(**mActivePower)(1, 1), (**mReactivePower)(1, 1)};
106 mPower(2, 2) = {(**mActivePower)(2, 2), (**mReactivePower)(2, 2)};
107
108 **mNomVoltage = std::abs(mTerminals[0]->initialSingleVoltage());
109
110 SPDLOG_LOGGER_INFO(mSLog,
111 "\nActive Power [W]: {}"
112 "\nReactive Power [VAr]: {}",
113 Logger::matrixToString(**mActivePower),
114 Logger::matrixToString(**mReactivePower));
115 SPDLOG_LOGGER_INFO(mSLog, "Nominal Voltage={} [V]", **mNomVoltage);
116 }
117
118 // Compute derived impedance values and create+parametrize sub-components
119 // now that power and voltage are guaranteed to be known.
120 if ((**mActivePower)(0, 0) != 0) {
122 std::pow(**mNomVoltage / sqrt(3), 2) * (**mActivePower).inverse();
123 }
124 if ((**mReactivePower)(0, 0) != 0)
125 mReactance =
126 std::pow(**mNomVoltage / sqrt(3), 2) * (**mReactivePower).inverse();
127 else
128 mReactance = Matrix::Zero(3, 3);
129
130 if ((**mActivePower)(0, 0) != 0) {
132 std::make_shared<EMT::Ph3::Resistor>(**mName + "_res", mLogLevel);
133 mSubResistor->setParameters(mResistance);
134 if (mReactanceInSeries) {
135 mSubResistor->connect({mTerminals[0]->node(), mVirtualNodes[0]});
136 } else {
137 mSubResistor->connect({SimNode::GND, mTerminals[0]->node()});
138 }
139 addMNASubComponent(mSubResistor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
140 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
141 }
142
143 if (mReactance(0, 0) > 0) {
144 mInductance = mReactance / omega;
145
147 std::make_shared<EMT::Ph3::Inductor>(**mName + "_ind", mLogLevel);
148 mSubInductor->setParameters(mInductance);
149 if (mReactanceInSeries) {
150 mSubInductor->connect({SimNode::GND, mVirtualNodes[0]});
151 } else {
152 mSubInductor->connect({SimNode::GND, mTerminals[0]->node()});
153 }
154 addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
155 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
156 } else if (mReactance(0, 0) < 0) {
157 mCapacitance = -1. / omega * mReactance.inverse();
158
160 std::make_shared<EMT::Ph3::Capacitor>(**mName + "_cap", mLogLevel);
161 mSubCapacitor->setParameters(mCapacitance);
162 if (mReactanceInSeries) {
163 mSubCapacitor->connect({SimNode::GND, mVirtualNodes[0]});
164 } else {
165 mSubCapacitor->connect({SimNode::GND, mTerminals[0]->node()});
166 }
168 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
169 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
170 }
171
172 MatrixComp vInitABC = MatrixComp::Zero(3, 1);
173 vInitABC(0, 0) = RMS3PH_TO_PEAK1PH * mTerminals[0]->initialSingleVoltage();
174 vInitABC(1, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_B;
175 vInitABC(2, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_C;
176 **mIntfVoltage = vInitABC.real();
177
178 if ((**mActivePower)(0, 0) != 0) {
180 std::pow(**mNomVoltage / sqrt(3), 2) * (**mActivePower).inverse();
181 }
182
183 if ((**mReactivePower)(0, 0) != 0)
184 mReactance =
185 std::pow(**mNomVoltage / sqrt(3), 2) * (**mReactivePower).inverse();
186 else
187 mReactance = Matrix::Zero(1, 1);
188
189 if (mReactanceInSeries) {
190 MatrixComp impedance = MatrixComp::Zero(3, 3);
191 impedance << Complex(mResistance(0, 0), mReactance(0, 0)),
192 Complex(mResistance(0, 1), mReactance(0, 1)),
193 Complex(mResistance(0, 2), mReactance(0, 2)),
194 Complex(mResistance(1, 0), mReactance(1, 0)),
195 Complex(mResistance(1, 1), mReactance(1, 1)),
196 Complex(mResistance(1, 2), mReactance(1, 2)),
197 Complex(mResistance(2, 0), mReactance(2, 0)),
198 Complex(mResistance(2, 1), mReactance(2, 1)),
199 Complex(mResistance(2, 2), mReactance(2, 2));
200 **mIntfCurrent = (impedance.inverse() * vInitABC).real();
201
202 // Initialization of virtual node
203 // Initial voltage of phase B,C is set after A
204 MatrixComp vInitTerm0 = MatrixComp::Zero(3, 1);
205 vInitTerm0(0, 0) = initialSingleVoltage(0);
206 vInitTerm0(1, 0) = vInitTerm0(0, 0) * SHIFT_TO_PHASE_B;
207 vInitTerm0(2, 0) = vInitTerm0(0, 0) * SHIFT_TO_PHASE_C;
208 mVirtualNodes[0]->setInitialVoltage(vInitTerm0 +
210 }
211
212 // Sub-components were just created above; initialize them now so their
213 // post-init current can be read back before this hook returns (the
214 // framework's generic sub-init loop would otherwise run too late for
215 // that).
216 if ((**mActivePower)(0, 0) != 0) {
217 mSubResistor->initialize(mFrequencies);
218 mSubResistor->initializeFromNodesAndTerminals(frequency);
219 if (!mReactanceInSeries) {
220 **mIntfCurrent += mSubResistor->intfCurrent();
221 }
222 }
223
224 if (mReactance(0, 0) > 0) {
225 mSubInductor->initialize(mFrequencies);
226 mSubInductor->initializeFromNodesAndTerminals(frequency);
227 if (!mReactanceInSeries) {
228 **mIntfCurrent += mSubInductor->intfCurrent();
229 }
230 } else if (mReactance(0, 0) < 0) {
231 mSubCapacitor->initialize(mFrequencies);
232 mSubCapacitor->initializeFromNodesAndTerminals(frequency);
233 if (!mReactanceInSeries) {
234 **mIntfCurrent += mSubCapacitor->intfCurrent();
235 }
236 }
237
238 SPDLOG_LOGGER_INFO(
239 mSLog,
240 "\n--- Initialization from powerflow ---"
241 "\nVoltage across: {:s}"
242 "\nCurrent: {:s}"
243 "\nTerminal 0 voltage: {:s}"
244 "\nActive Power: {:s}"
245 "\nReactive Power: {:s}"
246 "\nResistance: {:s}"
247 "\nReactance: {:s}"
248 "\n--- Initialization from powerflow finished ---",
249 Logger::matrixToString(**mIntfVoltage),
250 Logger::matrixToString(**mIntfCurrent),
251 Logger::phasorToString(RMS3PH_TO_PEAK1PH * initialSingleVoltage(0)),
252 Logger::matrixToString(**mActivePower),
253 Logger::matrixToString(**mReactivePower),
254 Logger::matrixToString(mResistance), Logger::matrixToString(mReactance));
255 mSLog->flush();
256}
257
258void EMT::Ph3::RXLoad::mnaParentAddPreStepDependencies(
259 AttributeBase::List &prevStepDependencies,
260 AttributeBase::List &attributeDependencies,
261 AttributeBase::List &modifiedAttributes) {
262 modifiedAttributes.push_back(mRightVector);
263};
264
265void EMT::Ph3::RXLoad::mnaParentAddPostStepDependencies(
266 AttributeBase::List &prevStepDependencies,
267 AttributeBase::List &attributeDependencies,
268 AttributeBase::List &modifiedAttributes,
269 Attribute<Matrix>::Ptr &leftVector) {
270 attributeDependencies.push_back(leftVector);
271 modifiedAttributes.push_back(mIntfCurrent);
272 modifiedAttributes.push_back(mIntfVoltage);
273};
274
278
279void EMT::Ph3::RXLoad::mnaParentPostStep(Real time, Int timeStepCount,
280 Attribute<Matrix>::Ptr &leftVector) {
281 mnaCompUpdateVoltage(**leftVector);
282 mnaCompUpdateCurrent(**leftVector);
283}
284
285void EMT::Ph3::RXLoad::mnaCompUpdateVoltage(const Matrix &leftVector) {
286 **mIntfVoltage = Matrix::Zero(3, 1);
287 (**mIntfVoltage)(0, 0) =
288 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0));
289 (**mIntfVoltage)(1, 0) =
290 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1));
291 (**mIntfVoltage)(2, 0) =
292 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2));
293}
294
295void EMT::Ph3::RXLoad::mnaCompUpdateCurrent(const Matrix &leftVector) {
296 if (mReactanceInSeries) {
297 **mIntfCurrent = mSubInductor->intfCurrent();
298 } else {
299 **mIntfCurrent = Matrix::Zero(3, 1);
300 for (auto &subc : mSubComponents) {
301 **mIntfCurrent += subc->intfCurrent();
302 }
303 }
304}
void addMNASubComponent(typename SimPowerComp< Real >::Ptr subc, MNA_SUBCOMP_TASK_ORDER preStepOrder, MNA_SUBCOMP_TASK_ORDER postStepOrder, Bool contributeToRightVector)
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override
CompositePowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
const Attribute< Matrix >::Ptr mActivePower
Active power [Watt].
Bool mReactanceInSeries
If set to true, the reactance is in series with the resistor. Otherwise it is parallel to the resisto...
RXLoad(String uid, String name, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and logging level.
std::shared_ptr< EMT::Ph3::Resistor > mSubResistor
Internal resistance.
std::shared_ptr< EMT::Ph3::Inductor > mSubInductor
Internal inductor.
void createSubComponents() override
Constructs and registers MNA subcomponents; idempotent.
std::shared_ptr< EMT::Ph3::Capacitor > mSubCapacitor
Internal capacitor.
Matrix mResistance
Resistance [Ohm].
void initializeParentFromNodesAndTerminals(Real frequency) override
Derives values from power flow data and pushes them to subcomponents.
Matrix mReactance
Reactance [Ohm].
const Attribute< Real >::Ptr mNomVoltage
Nominal voltage [V].
void mnaParentPreStep(Real time, Int timeStepCount) override
MNA pre and post step operations.
Matrix mCapacitance
Capacitance [F].
const Attribute< Matrix >::Ptr mReactivePower
Reactive power [VAr].
Matrix mInductance
Inductance [H].
const Attribute< String >::Ptr mName
Human readable name.
String uid()
Returns unique id.
String type()
Get component type (cross-platform)
AttributeList::Ptr mAttributes
Attribute List.
Attribute< Matrix >::Ptr mRightVector
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
SimTerminal< Real >::List mTerminals
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
SimNode< Real >::List mVirtualNodes
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.