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 : CompositePowerComp<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
31 return;
32 mSubCompCreated = true;
33
34 // Inductor/capacitor values depend on omega computed from the simulation
35 // frequency in initializeParentFromNodesAndTerminals(). They are created
36 // here (topology) and parametrized there (values).
37
38 // Inductor with Switch
40 std::make_shared<DP::Ph1::Inductor>(**mName + "_ind", mLogLevel);
41 mSubInductor->connect({SimNode::GND, mVirtualNodes[0]});
42 addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
43 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
44
45 mSubInductorSwitch =
46 std::make_shared<DP::Ph1::Switch>(**mName + "_Lswitch", mLogLevel);
47 mSubInductorSwitch->setParameters(mSwitchROpen, mSwitchRClosed, false);
48 mSubInductorSwitch->connect({mVirtualNodes[0], mTerminals[0]->node()});
49 addMNASubComponent(mSubInductorSwitch,
50 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
51 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
52
53 // Capacitor with Switch
55 std::make_shared<DP::Ph1::Capacitor>(**mName + "_cap", mLogLevel);
56 mSubCapacitor->connect({SimNode::GND, mVirtualNodes[1]});
57 addMNASubComponent(mSubCapacitor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
58 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
59
60 mSubCapacitorSwitch =
61 std::make_shared<DP::Ph1::Switch>(**mName + "_Cswitch", mLogLevel);
62 mSubCapacitorSwitch->setParameters(mSwitchROpen, mSwitchRClosed, false);
63 mSubCapacitorSwitch->connect({mVirtualNodes[1], mTerminals[0]->node()});
64 addMNASubComponent(mSubCapacitorSwitch,
65 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
66 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
67}
68
70 // initial state is both switches are open
71 Real omega = 2. * PI * frequency;
72 // init L and C with small/high values (both have high impedance)
73 Real LInit = 1e6 / omega;
74 Real CInit = 1e-6 / omega;
75 mLPrev = LInit;
76 mCPrev = CInit;
77
78 // impedances of both branches
79 Complex LImpedance = {mSwitchROpen, omega * LInit};
80 Complex CImpedance = {mSwitchROpen, -1 / (omega * CInit)};
81 Complex impedance = LImpedance * CImpedance / (LImpedance + CImpedance);
82
83 (**mIntfVoltage)(0, 0) = initialSingleVoltage(0);
84 (**mIntfCurrent)(0, 0) = (**mIntfVoltage)(0, 0) / impedance;
85
86 **mBPrev = 0;
87 mPrevVoltage = (**mIntfVoltage)(0, 0).real();
88 **mVmeasPrev = mPrevVoltage;
89
90 if (mMechMode) {
91 SPDLOG_LOGGER_INFO(mSLog, "Using Mechanical Model");
92 }
93
94 SPDLOG_LOGGER_INFO(mSLog,
95 "\n --- Parameters ---"
96 "\n Controller: T = {} K = {}"
97 "\n Reference Voltage {} [kV]"
98 "\n Qmax = {} [var] -> BN = {} [S]"
99 "\n Bmax = {} Bmin = {} [p.u.]"
100 "\n Initial B: {}",
101 mTr, mKr, mRefVolt, mQN, mBN, mBMax, mBMin, **mBPrev);
102
103 // set voltages at virtual nodes
104 Complex VLSwitch =
105 (**mIntfVoltage)(0, 0) - LImpedance * (**mIntfCurrent)(0, 0);
106 mVirtualNodes[0]->setInitialVoltage(VLSwitch);
107 Complex VCSwitch =
108 (**mIntfVoltage)(0, 0) - CImpedance * (**mIntfCurrent)(0, 0);
109 mVirtualNodes[1]->setInitialVoltage(VCSwitch);
110
111 mSubInductor->setParameters(LInit);
112 mSubCapacitor->setParameters(CInit);
113
114 SPDLOG_LOGGER_INFO(mSLog,
115 "\n--- Initialization from powerflow ---"
116 "\nImpedance: {}"
117 "\nVoltage across: {:s}"
118 "\nCurrent: {:s}"
119 "\nTerminal 0 voltage: {:s}"
120 "\n--- Initialization from powerflow finished ---",
121 impedance, Logger::phasorToString((**mIntfVoltage)(0, 0)),
122 Logger::phasorToString((**mIntfCurrent)(0, 0)),
123 Logger::phasorToString(initialSingleVoltage(0)));
124}
125
126// #### MNA functions ####
127
129 AttributeBase::List &prevStepDependencies,
130 AttributeBase::List &attributeDependencies,
131 AttributeBase::List &modifiedAttributes) {
132 modifiedAttributes.push_back(mRightVector);
133}
134
135void DP::Ph1::SVC::mnaParentPreStep(Real time, Int timeStepCount) {
137
138 if (time > 0.1 && !mDisconnect) {
139 if (mMechMode) {
140 mechanicalModelUpdateSusceptance(time);
141 } else {
142 updateSusceptance();
143 }
144 checkProtection(time);
145 }
146}
147
149 AttributeBase::List &prevStepDependencies,
150 AttributeBase::List &attributeDependencies,
151 AttributeBase::List &modifiedAttributes,
152 Attribute<Matrix>::Ptr &leftVector) {
153 attributeDependencies.push_back(leftVector);
154 modifiedAttributes.push_back(mIntfVoltage);
155 modifiedAttributes.push_back(mIntfCurrent);
156}
157
158void DP::Ph1::SVC::mnaParentPostStep(Real time, Int timeStepCount,
159 Attribute<Matrix>::Ptr &leftVector) {
160 mnaCompUpdateVoltage(**leftVector);
161 mnaCompUpdateCurrent(**leftVector);
162
163 mDeltaT = time - mPrevTimeStep;
164 mPrevTimeStep = time;
165 mValueChange = false;
166}
167
168void DP::Ph1::SVC::mnaCompUpdateVoltage(const Matrix &leftVector) {
169 **mVpcc = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0),
170 mNumFreqs, 0)
171 .real();
172 (**mIntfVoltage)(0, 0) =
173 Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
174}
175
176void DP::Ph1::SVC::mnaCompUpdateCurrent(const Matrix &leftVector) {
177 (**mIntfCurrent)(0, 0) = 0;
178 (**mIntfCurrent)(0, 0) += mSubInductor->intfCurrent()(0, 0);
179 (**mIntfCurrent)(0, 0) += mSubCapacitor->intfCurrent()(0, 0);
180}
181
182void DP::Ph1::SVC::checkProtection(Real time) {
183 // check states for violation of protection values
184 // get inverse protection curve value (time delay value)
185
186 Real Vpu = **mVmeasPrev / mNomVolt;
187 if (Vpu > 1.4) {
188 mProtCount1 = mProtCount1 + mDeltaT;
189 if (mProtCount1 > 0.1) {
190 mDisconnect = true;
191 }
192 } else {
193 mProtCount1 = 0;
194 }
195 if (Vpu > 1.25) {
196 mProtCount2 = mProtCount2 + mDeltaT;
197 if (mProtCount2 > 1) {
198 mDisconnect = true;
199 }
200 } else {
201 mProtCount2 = 0;
202 }
203 if (Vpu > 1.15) {
204 mProtCount3 = mProtCount3 + mDeltaT;
205 if (mProtCount3 > 5) {
206 mDisconnect = true;
207 }
208 } else {
209 mProtCount3 = 0;
210 }
211
212 if (mDisconnect) {
213 SPDLOG_LOGGER_INFO(mSLog, "Disconnect SVC because of overvoltage at {}",
214 time);
215 mSubCapacitorSwitch->open();
216 mSubInductorSwitch->open();
217 mValueChange = true;
218 }
219}
220
221void DP::Ph1::SVC::updateSusceptance() {
222 // calculate new B value
223 // summarize some constants
224 Real Fac1 = mDeltaT / (2 * mTr);
225 Real Fac2 = mDeltaT * mKr / (2 * mTr);
226
227 Real V = Math::abs((**mIntfVoltage)(0, 0).real());
228
229 // Pt1 with trapez rule for voltage measurement
230 Real Fac3 = mDeltaT / (2 * mTm);
231 Real Vmeas = (1 / (1 + Fac3)) * (V + mPrevVoltage - **mVmeasPrev);
232
233 **mDeltaV = (Vmeas - mRefVolt) / mNomVolt;
234 Real deltaVPrev = (**mVmeasPrev - mRefVolt) / mNomVolt;
235
236 // calc new B with trapezoidal rule
237 Real B = (1 / (1 + Fac1)) *
238 (Fac2 * (**mDeltaV + deltaVPrev) + (1 - Fac1) * **mBPrev);
239
240 // check bounds
241 if (B > mBMax) {
242 B = mBMax;
243 } else if (B < mBMin) {
244 B = mBMin;
245 }
246
247 // set new B if it has a new value and difference is big enough
248 if (B != **mBPrev) {
249 Real omega = 2 * M_PI * mFrequencies(0, 0);
250
251 if (B > 0) {
252 // model inductive behaviour (decrease voltage)
253 Real inductance = 1 / (omega * B * mBN);
254 //check if change in reactance is sufficient to trigger a change
255 if (Math::abs(1 - inductance / mLPrev) > 0.01) {
256 mInductiveMode = true;
257 mSubInductor->updateInductance(inductance, mDeltaT);
258 mLPrev = inductance;
259
260 mValueChange = true;
261 mBSetCounter = 0;
262 }
263 } else {
264 // model capacitive behaviour (increase voltage)
265 Real capacitance = B * mBN / (-omega);
266 //check if change in reactance is sufficient to trigger a change
267 if (Math::abs(1 - capacitance / mCPrev) > 0.01) {
268 mInductiveMode = false;
269 mSubCapacitor->updateCapacitance(capacitance, mDeltaT);
270 mCPrev = capacitance;
271
272 mValueChange = true;
273 mBSetCounter = 0;
274 }
275 }
276
277 // update inductance model
278 setSwitchState();
279 } else {
280 mBSetCounter = mBSetCounter + mDeltaT;
281 }
282
283 // save values
284 **mBPrev = B;
285 mPrevVoltage = V;
286 **mVmeasPrev = Vmeas;
287}
288
289// model SVC with a mechanical component and discrete
290void DP::Ph1::SVC::mechanicalModelUpdateSusceptance(Real time) {
291 // current voltage
292 Real V = Math::abs((**mIntfVoltage)(0, 0).real());
293 Real omega = 2 * M_PI * mFrequencies(0, 0);
294
295 // Pt1 with trapez rule for voltage measurement
296 Real Fac3 = mDeltaT / (2 * mTm);
297 Real Vmeas = (1 / (1 + Fac3)) * (V + mPrevVoltage - **mVmeasPrev);
298
299 // V diff in pu
300 Real deltaV = (mRefVolt - Vmeas) / mRefVolt;
301
302 if (Math::abs(deltaV) > mDeadband) {
303 if (**mViolationCounter > mMechSwitchDelay) {
304 // change suszeptance one step
305 if (deltaV > 0 && (mTapPos > mMinPos)) {
306 // undervoltage
307
308 mTapPos = mTapPos - 1;
309 mTapPos = (mTapPos < mMinPos) ? mMinPos : mTapPos;
310 **mViolationCounter = 0;
311 SPDLOG_LOGGER_INFO(mSLog,
312 "Time: {}"
313 "\nDecreasing Tap. Reason: Undervoltage"
314 "\nNew Tap Position: {}",
315 time, mTapPos);
316 } else if (deltaV < 0 && (mTapPos < mMaxPos)) {
317 // overvoltage
318 mTapPos = mTapPos + 1;
319 mTapPos = (mTapPos > mMaxPos) ? mMaxPos : mTapPos;
320 **mViolationCounter = 0;
321 SPDLOG_LOGGER_INFO(mSLog,
322 "Time: {}"
323 "\nIncreasing Tap. Reason: Overvoltag"
324 "\nNew Tap Position: {}",
325 time, mTapPos);
326 }
327
328 if (**mViolationCounter == 0) {
329 // new value for suszeptance
330 if (mTapPos > 0) {
331 // inductor is active
332 mInductiveMode = true;
333 Real inductance = 1 / ((mTapPos / mMaxPos) * mBN * omega);
334 SPDLOG_LOGGER_INFO(mSLog, "New inductance: {}", inductance);
335 mSubInductor->updateInductance(inductance, mDeltaT);
336 mValueChange = true;
337 setSwitchState();
338 } else if (mTapPos < 0) {
339 // capacitor is active
340 mInductiveMode = false;
341 Real capacitance = ((mTapPos / mMinPos) * mBN) / omega;
342 SPDLOG_LOGGER_INFO(mSLog, "New capacitance: {}", capacitance);
343 mSubCapacitor->updateCapacitance(capacitance, mDeltaT);
344 mValueChange = true;
345 setSwitchState();
346
347 } else if (mTapPos = 0) {
348 // open both
349 SPDLOG_LOGGER_INFO(mSLog,
350 "Time: {}"
351 "Tap Position: 0. Open both elements",
352 time);
353 mSubInductorSwitch->open();
354 mSubCapacitorSwitch->open();
355 }
356 }
357 } else {
358 // increase counter
359 **mViolationCounter = **mViolationCounter + mDeltaT;
360 }
361 } else {
362 // reset counter
363 **mViolationCounter = 0;
364 }
365
366 // save states
367 mPrevVoltage = V;
368 **mVmeasPrev = Vmeas;
369}
370
371void DP::Ph1::SVC::setSwitchState() {
372 // set switches according to current mode of svc
373 if (mInductiveMode) {
374 if (!mSubInductorSwitch->mnaIsClosed()) {
375 SPDLOG_LOGGER_INFO(mSLog, "Inductive Mode: Closed Inductor Switch");
376 mSubInductorSwitch->close();
377 }
378 if (mSubCapacitorSwitch->mnaIsClosed()) {
379 mSubCapacitorSwitch->open();
380 SPDLOG_LOGGER_INFO(mSLog, "Inductive Mode: Opened Capacitor Switch");
381 }
382 } else {
383 if (mSubInductorSwitch->mnaIsClosed()) {
384 mSubInductorSwitch->open();
385 SPDLOG_LOGGER_INFO(mSLog, "Capacitive Mode: Openend Inductor Switch");
386 }
387 if (!mSubCapacitorSwitch->mnaIsClosed()) {
388 mSubCapacitorSwitch->close();
389 SPDLOG_LOGGER_INFO(mSLog, "Capacitive Mode: Closed Capcitor Switch");
390 }
391 }
392}
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 addMNASubComponent(typename SimPowerComp< Complex >::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)
void initializeParentFromNodesAndTerminals(Real frequency) override
Initializes states from power flow data (parent-specific part).
void mnaParentPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
MNA post step operations (parent-specific)
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:38
void mnaCompUpdateVoltage(const Matrix &leftVector) override
Update interface voltage from MNA system results.
std::shared_ptr< DP::Ph1::Inductor > mSubInductor
Internal inductor.
Definition DP_Ph1_SVC.h:35
void mnaParentAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
add MNA post step dependencies (parent-specific)
void mnaParentPreStep(Real time, Int timeStepCount) override
MNA pre step operations (parent-specific)
SVC(String uid, String name, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and log level.
void mnaParentAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
add MNA pre step dependencies (parent-specific)
const Attribute< String >::Ptr mName
Human readable name.
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
Attribute< Matrix >::Ptr mRightVector
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
SimTerminal< Complex >::List mTerminals
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
SimNode< Complex >::List mVirtualNodes
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.