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