DPsim
Loading...
Searching...
No Matches
DP_Ph1_SVC.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/DP/DP_Ph1_SVC.h>
10
11using namespace CPS;
12
13DP::Ph1::SVC::SVC(String uid, String name, Logger::Level logLevel)
14 : MNASimPowerComp<Complex>(uid, name, true, true, logLevel),
15 mVpcc(mAttributes->create<Real>("Vpcc", 0)),
16 mVmeasPrev(mAttributes->create<Real>("Vmeas", 0)) {
17 setTerminalNumber(1);
18 setVirtualNodeNumber(2);
19 **mIntfVoltage = MatrixComp::Zero(1, 1);
20 **mIntfCurrent = MatrixComp::Zero(1, 1);
21
22 mDeltaV = mAttributes->create<Real>("DeltaV", 0);
23 mBPrev = mAttributes->create<Real>("B");
24 mViolationCounter = mAttributes->create<Real>("ViolationCounter", 0);
25}
26
27Bool DP::Ph1::SVC::ValueChanged() { return mValueChange; }
28
30 if (mSubCompCreated)
31 return;
32 mSubCompCreated = true;
33
34 // Inductor/capacitor values depend on omega, which is computed from the
35 // simulation frequency in initializeFromNodesAndTerminals() rather than
36 // mFrequencies here, since the latter is not guaranteed to be populated
37 // yet during this pre-pass. They are created here (existence doesn't
38 // depend on that value) but parametrized there, before their own
39 // initializeFromNodesAndTerminals() runs.
40
41 // Inductor with Switch
43 std::make_shared<DP::Ph1::Inductor>(**mName + "_ind", mLogLevel);
44 mSubInductor->connect({SimNode::GND, mVirtualNodes[0]});
45 mSubInductor->initialize(mFrequencies);
46
47 mSubInductorSwitch =
48 std::make_shared<DP::Ph1::Switch>(**mName + "_Lswitch", mLogLevel);
49 mSubInductorSwitch->setParameters(mSwitchROpen, mSwitchRClosed, false);
50 mSubInductorSwitch->connect({mVirtualNodes[0], mTerminals[0]->node()});
51 mSubInductorSwitch->initialize(mFrequencies);
52
53 // Capacitor with Switch
55 std::make_shared<DP::Ph1::Capacitor>(**mName + "_cap", mLogLevel);
56 mSubCapacitor->connect({SimNode::GND, mVirtualNodes[1]});
57 mSubCapacitor->initialize(mFrequencies);
58
59 mSubCapacitorSwitch =
60 std::make_shared<DP::Ph1::Switch>(**mName + "_Cswitch", mLogLevel);
61 mSubCapacitorSwitch->setParameters(mSwitchROpen, mSwitchRClosed, false);
62 mSubCapacitorSwitch->connect({mVirtualNodes[1], mTerminals[0]->node()});
63 mSubCapacitorSwitch->initialize(mFrequencies);
64}
65
67 // initial state is both switches are open
68 Real omega = 2. * PI * frequency;
69 // init L and C with small/high values (both have high impedance)
70 Real LInit = 1e6 / omega;
71 Real CInit = 1e-6 / omega;
72 mLPrev = LInit;
73 mCPrev = CInit;
74
75 // impedances of both branches
76 Complex LImpedance = {mSwitchROpen, omega * LInit};
77 Complex CImpedance = {mSwitchROpen, -1 / (omega * CInit)};
78 Complex impedance = LImpedance * CImpedance / (LImpedance + CImpedance);
79
80 (**mIntfVoltage)(0, 0) = initialSingleVoltage(0);
81 (**mIntfCurrent)(0, 0) = (**mIntfVoltage)(0, 0) / impedance;
82
83 **mBPrev = 0;
84 mPrevVoltage = (**mIntfVoltage)(0, 0).real();
85 **mVmeasPrev = mPrevVoltage;
86
87 if (mMechMode) {
88 SPDLOG_LOGGER_INFO(mSLog, "Using Mechanical Model");
89 }
90
91 SPDLOG_LOGGER_INFO(mSLog,
92 "\n --- Parameters ---"
93 "\n Controller: T = {} K = {}"
94 "\n Reference Voltage {} [kV]"
95 "\n Qmax = {} [var] -> BN = {} [S]"
96 "\n Bmax = {} Bmin = {} [p.u.]"
97 "\n Initial B: {}",
98 mTr, mKr, mRefVolt, mQN, mBN, mBMax, mBMin, **mBPrev);
99
100 // set voltages at virtual nodes
101 Complex VLSwitch =
102 (**mIntfVoltage)(0, 0) - LImpedance * (**mIntfCurrent)(0, 0);
103 mVirtualNodes[0]->setInitialVoltage(VLSwitch);
104 Complex VCSwitch =
105 (**mIntfVoltage)(0, 0) - CImpedance * (**mIntfCurrent)(0, 0);
106 mVirtualNodes[1]->setInitialVoltage(VCSwitch);
107
109 mSubInductor->setParameters(LInit);
110 mSubCapacitor->setParameters(CInit);
111
112 mSubInductor->initializeFromNodesAndTerminals(frequency);
113 mSubInductorSwitch->initializeFromNodesAndTerminals(frequency);
114 mSubCapacitor->initializeFromNodesAndTerminals(frequency);
115 mSubCapacitorSwitch->initializeFromNodesAndTerminals(frequency);
116
117 SPDLOG_LOGGER_INFO(mSLog,
118 "\n--- Initialization from powerflow ---"
119 "\nImpedance: {}"
120 "\nVoltage across: {:s}"
121 "\nCurrent: {:s}"
122 "\nTerminal 0 voltage: {:s}"
123 "\n--- Initialization from powerflow finished ---",
124 impedance, Logger::phasorToString((**mIntfVoltage)(0, 0)),
125 Logger::phasorToString((**mIntfCurrent)(0, 0)),
126 Logger::phasorToString(initialSingleVoltage(0)));
127}
128
129// #### MNA functions ####
130
131void DP::Ph1::SVC::mnaCompInitialize(Real omega, Real timeStep,
132 Attribute<Matrix>::Ptr leftVector) {
134
135 SPDLOG_LOGGER_INFO(mSLog, "\nTerminal 0 connected to {:s} = sim node {:d}",
136 mTerminals[0]->node()->name(),
137 mTerminals[0]->node()->matrixNodeIndex());
138
139 mSubInductor->mnaInitialize(omega, timeStep, leftVector);
140 mRightVectorStamps.push_back(
141 &mSubInductor->attributeTyped<Matrix>("right_vector")->get());
142
143 mSubInductorSwitch->mnaInitialize(omega, timeStep, leftVector);
144 mRighteVctorStamps.push_back(
145 &mSubInductorSwitch->attributeTyped<Matrix>("right_vector")->get());
146
147 mSubCapacitor->mnaInitialize(omega, timeStep, leftVector);
148 mRightVectorStamps.push_back(
149 &mSubCapacitor->attributeTyped<Matrix>("right_vector")->get());
150
151 mSubCapacitorSwitch->mnaInitialize(omega, timeStep, leftVector);
152 mRightVectorStamps.push_back(
153 &mSubCapacitorSwitch->attributeTyped<Matrix>("right_vector")->get());
154}
155
157 SparseMatrixRow &systemMatrix) {
158 mSubInductor->mnaApplySystemMatrixStamp(systemMatrix);
159 mSubCapacitor->mnaApplySystemMatrixStamp(systemMatrix);
160 mSubCapacitorSwitch->mnaApplySystemMatrixStamp(systemMatrix);
161 mSubInductorSwitch->mnaApplySystemMatrixStamp(systemMatrix);
162}
163
165 mSubInductor->mnaApplyRightSideVectorStamp(rightVector);
166 mSubCapacitor->mnaApplyRightSideVectorStamp(rightVector);
167 mSubCapacitorSwitch->mnaApplyRightSideVectorStamp(rightVector);
168 mSubInductorSwitch->mnaApplyRightSideVectorStamp(rightVector);
169}
170
172 AttributeBase::List &prevStepDependencies,
173 AttributeBase::List &attributeDependencies,
174 AttributeBase::List &modifiedAttributes) {
175 // add pre-step dependencies of subcomponents
176 mSubInductor->mnaAddPreStepDependencies(
177 prevStepDependencies, attributeDependencies, modifiedAttributes);
178 mSubInductorSwitch->mnaAddPreStepDependencies(
179 prevStepDependencies, attributeDependencies, modifiedAttributes);
180 mSubCapacitor->mnaAddPreStepDependencies(
181 prevStepDependencies, attributeDependencies, modifiedAttributes);
182 mSubCapacitorSwitch->mnaAddPreStepDependencies(
183 prevStepDependencies, attributeDependencies, modifiedAttributes);
184
185 // add pre-step dependencies of component itself
186 modifiedAttributes.push_back(mRightVector);
187}
188
189void DP::Ph1::SVC::mnaCompPreStep(Real time, Int timeStepCount) {
190 mSubInductor->mnaPreStep(time, timeStepCount);
191 mSubInductorSwitch->mnaPreStep(time, timeStepCount);
192 mSubCapacitor->mnaPreStep(time, timeStepCount);
193 mSubCapacitorSwitch->mnaPreStep(time, timeStepCount);
194
196
197 if (time > 0.1 && !mDisconnect) {
198 if (mMechMode) {
199 mechanicalModelUpdateSusceptance(time);
200 } else {
201 updateSusceptance();
202 }
203 checkProtection(time);
204 }
205}
206
208 AttributeBase::List &prevStepDependencies,
209 AttributeBase::List &attributeDependencies,
210 AttributeBase::List &modifiedAttributes,
211 Attribute<Matrix>::Ptr &leftVector) {
212 // add post-step dependencies of subcomponents
213 mSubInductor->mnaAddPostStepDependencies(prevStepDependencies,
214 attributeDependencies,
215 modifiedAttributes, leftVector);
216 mSubInductorSwitch->mnaAddPostStepDependencies(
217 prevStepDependencies, attributeDependencies, modifiedAttributes,
218 leftVector);
219 mSubCapacitor->mnaAddPostStepDependencies(prevStepDependencies,
220 attributeDependencies,
221 modifiedAttributes, leftVector);
222 mSubCapacitorSwitch->mnaAddPostStepDependencies(
223 prevStepDependencies, attributeDependencies, modifiedAttributes,
224 leftVector);
225
226 // add post-step dependencies of component itself
227 attributeDependencies.push_back(leftVector);
228 modifiedAttributes.push_back(mIntfVoltage);
229 modifiedAttributes.push_back(mIntfCurrent);
230}
231
232void DP::Ph1::SVC::mnaCompPostStep(Real time, Int timeStepCount) {
233 mSubInductor->mnaPostStep(time, timeStepCount, leftVector);
234 mSubInductorSwitch->mnaPostStep(time, timeStepCount, leftVector);
235 mSubCapacitor->mnaPostStep(time, timeStepCount, leftVector);
236 mSubCapacitorSwitch->mnaPostStep(time, timeStepCount, leftVector);
237
238 mnaCompUpdateVoltage(**mLeftVector);
239 mnaCompUpdateCurrent(**mLeftVector);
240
241 mDeltaT = time - mPrevTimeStep;
242 mPrevTimeStep = time;
243 mValueChange = false;
244}
245
246void DP::Ph1::SVC::mnaCompUpdateVoltage(const Matrix &leftVector) {
247 **mVpcc = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0),
248 mNumFreqs, 0)
249 .real();
250 (**mIntfVoltage)(0, 0) =
251 Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
252}
253
254void DP::Ph1::SVC::mnaCompUpdateCurrent(const Matrix &leftVector) {
255 (**mIntfCurrent)(0, 0) = 0;
256 (**mIntfCurrent)(0, 0) += mSubInductor->intfCurrent()(0, 0);
257 (**mIntfCurrent)(0, 0) += mSubCapacitor->intfCurrent()(0, 0);
258}
259
260void DP::Ph1::SVC::checkProtection(Real time) {
261 // check states for violation of protection values
262 // get inverse protection curve value (time delay value)
263
264 Real Vpu = **mVmeasPrev / mNomVolt;
265 if (Vpu > 1.4) {
266 mProtCount1 = mProtCount1 + mDeltaT;
267 if (mProtCount1 > 0.1) {
268 mDisconnect = true;
269 }
270 } else {
271 mProtCount1 = 0;
272 }
273 if (Vpu > 1.25) {
274 mProtCount2 = mProtCount2 + mDeltaT;
275 if (mProtCount2 > 1) {
276 mDisconnect = true;
277 }
278 } else {
279 mProtCount2 = 0;
280 }
281 if (Vpu > 1.15) {
282 mProtCount3 = mProtCount3 + mDeltaT;
283 if (mProtCount3 > 5) {
284 mDisconnect = true;
285 }
286 } else {
287 mProtCount3 = 0;
288 }
289
290 if (mDisconnect) {
291 SPDLOG_LOGGER_INFO(mSLog, "Disconnect SVC because of overvoltage at {}",
292 time);
293 mSubCapacitorSwitch->open();
294 mSubInductorSwitch->open();
295 mValueChange = true;
296 }
297}
298
299void DP::Ph1::SVC::updateSusceptance() {
300 // calculate new B value
301 // summarize some constants
302 Real Fac1 = mDeltaT / (2 * mTr);
303 Real Fac2 = mDeltaT * mKr / (2 * mTr);
304
305 Complex vintf = (**mIntfVoltage)(0, 0);
306 Real V = Math::abs((**mIntfVoltage)(0, 0).real());
307
308 // Pt1 with trapez rule for voltage measurement
309 Real Fac3 = mDeltaT / (2 * mTm);
310 Real Vmeas = (1 / (1 + Fac3)) * (V + mPrevVoltage - **mVmeasPrev);
311
312 **mDeltaV = (Vmeas - mRefVolt) / mNomVolt;
313 Real deltaVPrev = (**mVmeasPrev - mRefVolt) / mNomVolt;
314
315 // calc new B with trapezoidal rule
316 //Real B = (1/(1+Fac1)) * (Fac2 * (mDeltaV + deltaVPrev) + (1-Fac1) * mBPrev);
317 Real B = (1 / (1 + Fac1)) *
318 (Fac2 * (**mDeltaV + deltaVPrev) + (1 - Fac1) * **mBPrev);
319 //SPDLOG_LOGGER_INFO(mSLog, "New B value: percent={}, absolute={}", 100 * B, B * mBN);
320
321 // check bounds
322 if (B > mBMax) {
323 B = mBMax;
324 //SPDLOG_LOGGER_DEBUG(mSLog, "New B value exceeds Bmax");
325 } else if (B < mBMin) {
326 B = mBMin;
327 //SPDLOG_LOGGER_DEBUG(mSLog, "New B value exceeds Bmin");
328 }
329
330 // set new B if it has a new value and difference is big enough
331 if (B != **mBPrev) {
332 //if (B != mBPrev && mBSetCounter > 0.001){
333 //mValueChange = true;
334 //mBSetCounter = 0;
335 Real omega = 2 * M_PI * mFrequencies(0, 0);
336
337 if (B > 0) {
338 // model inductive behaviour (decrease voltage)
339 Real inductance = 1 / (omega * B * mBN);
340 //check if change in reactance is sufficient to trigger a change
341 if (Math::abs(1 - inductance / mLPrev) > 0.01) {
342 mInductiveMode = true;
343 mSubInductor->updateInductance(inductance, mDeltaT);
344 //SPDLOG_LOGGER_DEBUG(mSLog, "Inductive Mode: New Inductance: L = {} [H]", inductance);
345 mLPrev = inductance;
346
347 mValueChange = true;
348 mBSetCounter = 0;
349 }
350 } else {
351 // model capacitive behaviour (increase voltage)
352 Real capacitance = B * mBN / (-omega);
353 //check if change in reactance is sufficient to trigger a change
354 if (Math::abs(1 - capacitance / mCPrev) > 0.01) {
355 mInductiveMode = false;
356 mSubCapacitor->updateCapacitance(capacitance, mDeltaT);
357 //SPDLOG_LOGGER_DEBUG(mSLog, "Capacitive Mode: New Capacitance: C = {} [F]", capacitance);
358 mCPrev = capacitance;
359
360 mValueChange = true;
361 mBSetCounter = 0;
362 }
363 }
364
365 // update inductance model
366 setSwitchState();
367 } else {
368 mBSetCounter = mBSetCounter + mDeltaT;
369 }
370
371 // save values
372 **mBPrev = B;
373 mPrevVoltage = V;
374 **mVmeasPrev = Vmeas;
375}
376
377// model SVC with a mechanical component and discrete
378void DP::Ph1::SVC::mechanicalModelUpdateSusceptance(Real time) {
379 // current voltage
380 Real V = Math::abs((**mIntfVoltage)(0, 0).real());
381 Real omega = 2 * M_PI * mFrequencies(0, 0);
382
383 // Pt1 with trapez rule for voltage measurement
384 Real Fac3 = mDeltaT / (2 * mTm);
385 Real Vmeas = (1 / (1 + Fac3)) * (V + mPrevVoltage - **mVmeasPrev);
386
387 // V diff in pu
388 Real deltaV = (mRefVolt - Vmeas) / mRefVolt;
389
390 if (Math::abs(deltaV) > mDeadband) {
391 if (**mViolationCounter > mMechSwitchDelay) {
392 // change suszeptance one step
393 if (deltaV > 0 && (mTapPos > mMinPos)) {
394 // undervoltage
395
396 mTapPos = mTapPos - 1;
397 mTapPos = (mTapPos < mMinPos) ? mMinPos : mTapPos;
398 **mViolationCounter = 0;
399 SPDLOG_LOGGER_INFO(mSLog,
400 "Time: {}"
401 "\nDecreasing Tap. Reason: Undervoltage"
402 "\nNew Tap Position: {}",
403 time, mTapPos);
404 } else if (deltaV < 0 && (mTapPos < mMaxPos)) {
405 // overvoltage
406 mTapPos = mTapPos + 1;
407 mTapPos = (mTapPos > mMaxPos) ? mMaxPos : mTapPos;
408 **mViolationCounter = 0;
409 SPDLOG_LOGGER_INFO(mSLog,
410 "Time: {}"
411 "\nIncreasing Tap. Reason: Overvoltag"
412 "\nNew Tap Position: {}",
413 time, mTapPos);
414 }
415
416 if (**mViolationCounter == 0) {
417 // new value for suszeptance
418 if (mTapPos > 0) {
419 // inductor is active
420 mInductiveMode = true;
421 Real inductance = 1 / ((mTapPos / mMaxPos) * mBN * omega);
422 SPDLOG_LOGGER_INFO(mSLog, "New inductance: {}", inductance);
423 mSubInductor->updateInductance(inductance, mDeltaT);
424 mValueChange = true;
425 setSwitchState();
426 } else if (mTapPos < 0) {
427 // capacitor is active
428 mInductiveMode = false;
429 Real capacitance = ((mTapPos / mMinPos) * mBN) / omega;
430 SPDLOG_LOGGER_INFO(mSLog, "New capacitance: {}", capacitance);
431 mSubCapacitor->updateCapacitance(capacitance, mDeltaT);
432 mValueChange = true;
433 setSwitchState();
434
435 } else if (mTapPos = 0) {
436 // open both
437 SPDLOG_LOGGER_INFO(mSLog,
438 "Time: {}"
439 "Tap Position: 0. Open both elements",
440 time);
441 mSubInductorSwitch->open();
442 mSubCapacitorSwitch->open();
443 }
444 }
445 } else {
446 // increase counter
447 **mViolationCounter = **mViolationCounter + mDeltaT;
448 }
449 } else {
450 // reset counter
451 **mViolationCounter = 0;
452 }
453
454 // save states
455 mPrevVoltage = V;
456 **mVmeasPrev = Vmeas;
457}
458
459void DP::Ph1::SVC::setSwitchState() {
460 // set switches according to current mode of svc
461 if (mInductiveMode) {
462 if (!mSubInductorSwitch->mnaIsClosed()) {
463 SPDLOG_LOGGER_INFO(mSLog, "Inductive Mode: Closed Inductor Switch");
464 mSubInductorSwitch->close();
465 }
466 if (mSubCapacitorSwitch->mnaIsClosed()) {
467 mSubCapacitorSwitch->open();
468 SPDLOG_LOGGER_INFO(mSLog, "Inductive Mode: Opened Capacitor Switch");
469 }
470 } else {
471 if (mSubInductorSwitch->mnaIsClosed()) {
472 mSubInductorSwitch->open();
473 SPDLOG_LOGGER_INFO(mSLog, "Capacitive Mode: Openend Inductor Switch");
474 }
475 if (!mSubCapacitorSwitch->mnaIsClosed()) {
476 mSubCapacitorSwitch->close();
477 SPDLOG_LOGGER_INFO(mSLog, "Capacitive Mode: Closed Capcitor Switch");
478 }
479 }
480}
Real mTr
Time Constant.
Real mBMax
Maximium susceptance [p.u.].
Real mBN
rated B [S]
Real mBMin
Minimium susceptance [p.u.].
Real mRefVolt
Reference Voltage.
Real mQN
maximum Q
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
Stamps system matrix.
void mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
MNA post step operations.
void mnaCompUpdateCurrent(const Matrix &leftVector) override
Update interface current from MNA system results.
void createSubComponents() override
Constructs subcomponents; idempotent.
std::shared_ptr< DP::Ph1::Capacitor > mSubCapacitor
Internal capacitor.
Definition DP_Ph1_SVC.h:40
void mnaCompUpdateVoltage(const Matrix &leftVector) override
Update interface voltage from MNA system results.
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
Initializes MNA specific variables.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
add MNA post step dependencies
std::shared_ptr< DP::Ph1::Inductor > mSubInductor
Definition DP_Ph1_SVC.h:37
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override
Stamps right side (source) vector.
void initializeFromNodesAndTerminals(Real frequency) override
Initializes states from power flow data.
void mnaCompPreStep(Real time, Int timeStepCount) override
MNA pre step operations.
void mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
add MNA pre step dependencies
SVC(String uid, String name, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and log level.
const Attribute< String >::Ptr mName
Human readable name.
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
SimTerminal< Complex >::List mTerminals
SimNode< Complex >::Ptr node(UInt index)
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
SimNode< Complex >::List mVirtualNodes
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.