DPsim
Loading...
Searching...
No Matches
EMT_Ph3_SynchronGeneratorTrStab.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_SynchronGeneratorTrStab.h>
10using namespace CPS;
11
12Matrix EMT::Ph3::SynchronGeneratorTrStab::parkTransformPowerInvariant(
13 Real theta, const Matrix &fabc) {
14 // Calculates fdq = Tdq * fabc
15 // Assumes that d-axis starts aligned with phase a
16 Matrix Tdq = getParkTransformMatrixPowerInvariant(theta);
17 Matrix dqvector = Tdq * fabc;
18 return dqvector;
19}
20
21Matrix EMT::Ph3::SynchronGeneratorTrStab::getParkTransformMatrixPowerInvariant(
22 Real theta) {
23 // Return park matrix for theta
24 // Assumes that d-axis starts aligned with phase a
25 Matrix Tdq = Matrix::Zero(2, 3);
26 Real k = sqrt(2. / 3.);
27 Tdq << k * cos(theta), k * cos(theta - 2. * M_PI / 3.),
28 k * cos(theta + 2. * M_PI / 3.), -k * sin(theta),
29 -k * sin(theta - 2. * M_PI / 3.), -k * sin(theta + 2. * M_PI / 3.);
30 return Tdq;
31}
32
33EMT::Ph3::SynchronGeneratorTrStab::SynchronGeneratorTrStab(
34 String uid, String name, Logger::Level logLevel)
35 : Base::SynchronGenerator(mAttributes),
36 CompositePowerComp<Real>(uid, name, true, true, logLevel),
37 mEp(mAttributes->create<Complex>("Ep")),
38 mEp_abs(mAttributes->create<Real>("Ep_mag")),
39 mEp_phase(mAttributes->create<Real>("Ep_phase")),
40 mDelta_p(mAttributes->create<Real>("delta_r")),
41 mRefOmega(mAttributes->createDynamic<Real>("w_ref")),
42 mRefDelta(mAttributes->createDynamic<Real>("delta_ref")) {
43 setVirtualNodeNumber(2);
44 setTerminalNumber(1);
45 **mIntfVoltage = Matrix::Zero(3, 1);
46 **mIntfCurrent = Matrix::Zero(3, 1);
47
48 mStates = Matrix::Zero(10, 1);
49}
50
51SimPowerComp<Real>::Ptr EMT::Ph3::SynchronGeneratorTrStab::clone(String name) {
52 auto copy = SynchronGeneratorTrStab::make(name, mLogLevel);
53 copy->setStandardParametersPU(mNomPower, mNomVolt, mNomFreq, mXpd / mBase_Z,
54 **mInertia, **mRs, mKd);
55 return copy;
56}
57
59 Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd,
60 Real inertia, Real D) {
61 setBaseParameters(nomPower, nomVolt, nomFreq);
62
63 // Input is in per unit but all values are converted to absolute values.
64 mParameterType = ParameterType::statorReferred;
65 mStateType = StateType::statorReferred;
66
67 **mLl = Ll;
68 mLmd = Lmd;
69 **mLd = **mLl + mLmd;
70 mLlfd = Llfd;
71 mLfd = mLlfd + mLmd;
72 // M = 2*H where H = inertia
73 **mInertia = inertia;
74 // X'd in absolute values
75 mXpd = mNomOmega * (**mLd - mLmd * mLmd / mLfd) * mBase_L;
76 mLpd = (**mLd - mLmd * mLmd / mLfd) * mBase_L;
77
78 SPDLOG_LOGGER_INFO(mSLog,
79 "\n--- Parameters ---"
80 "\nimpedance: {:f}"
81 "\ninductance: {:f}",
82 mXpd, mLpd);
83}
84
86 Real nomPower, Real nomVolt, Real nomFreq, Int polePairNumber, Real Rs,
87 Real Lpd, Real inertiaJ, Real Kd) {
88 setBaseParameters(nomPower, nomVolt, nomFreq);
89
90 mParameterType = ParameterType::statorReferred;
91 mStateType = StateType::statorReferred;
92
93 // M = 2*H where H = inertia
94 // H = J * 0.5 * omegaNom^2 / polePairNumber
95 **mInertia = calcHfromJ(inertiaJ, 2 * PI * nomFreq, polePairNumber);
96 // X'd in absolute values
97 mXpd = mNomOmega * Lpd;
98 mLpd = Lpd;
99
100 SPDLOG_LOGGER_INFO(mSLog,
101 "\n--- Parameters ---"
102 "\nimpedance: {:f}"
103 "\ninductance: {:f}",
104 mXpd, mLpd);
105}
106
108 Real nomPower, Real nomVolt, Real nomFreq, Real Xpd, Real inertia, Real Rs,
109 Real D) {
110 setBaseParameters(nomPower, nomVolt, nomFreq);
111
112 // Input is in per unit but all values are converted to absolute values.
113 mParameterType = ParameterType::statorReferred;
114 mStateType = StateType::statorReferred;
115
116 // M = 2*H where H = inertia
117 **mInertia = inertia;
118 // X'd in absolute values
119 mXpd = Xpd * mBase_Z;
120 mLpd = Xpd * mBase_L;
121
122 **mRs = Rs;
123 //The units of D are per unit power divided by per unit speed deviation.
124 // D is transformed to an absolute value to obtain Kd, which will be used in the swing equation
125 mKd = D * mNomPower / mNomOmega;
126
127 SPDLOG_LOGGER_INFO(mSLog,
128 "\n--- Parameters ---"
129 "\nimpedance: {:f}"
130 "\ninductance: {:f}",
131 mXpd, mLpd);
132}
133
134void EMT::Ph3::SynchronGeneratorTrStab::setInitialValues(Complex elecPower,
135 Real mechPower) {
136 mInitElecPower = elecPower;
137 mInitMechPower = mechPower;
138}
139
141 if (mSubCompCreated)
142 return;
143 mSubCompCreated = true;
144
146 EMT::Ph3::VoltageSource::make(**mName + "_src", mLogLevel);
147 mSubVoltageSource->connect({SimNode::GND, mVirtualNodes[0]});
148 mSubVoltageSource->setVirtualNodeAt(mVirtualNodes[1], 0);
150 MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
151 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
152
153 mSubInductor = EMT::Ph3::Inductor::make(**mName + "_ind", mLogLevel);
154 mSubInductor->setParameters(
156 mSubInductor->connect({mVirtualNodes[0], terminal(0)->node()});
157 addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
158 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
159}
160
162 Real frequency) {
163 // Initialize omega mech with nominal system frequency
164 **mOmMech = mNomOmega;
165
166 mInitElecPower = (mInitElecPower == Complex(0, 0))
167 ? -terminal(0)->singlePower()
168 : mInitElecPower;
169 mInitMechPower = (mInitElecPower == Complex(0, 0)) ? mInitElecPower.real()
170 : mInitMechPower;
171
172 // use complex interface quantities for initialization calculations
173 MatrixComp intfVoltageComplex = MatrixComp::Zero(3, 1);
174 MatrixComp intfCurrentComplex = MatrixComp::Zero(3, 1);
175 // // derive complex threephase initialization from single phase initial values (only valid for balanced systems)
176 // intfVoltageComplex(0, 0) = RMS3PH_TO_PEAK1PH * initialSingleVoltage(0);
177 intfVoltageComplex(0, 0) = initialSingleVoltage(0);
178 intfVoltageComplex(1, 0) = intfVoltageComplex(0, 0) * SHIFT_TO_PHASE_B;
179 intfVoltageComplex(2, 0) = intfVoltageComplex(0, 0) * SHIFT_TO_PHASE_C;
180 intfCurrentComplex(0, 0) =
181 std::conj(-2. / 3. * mInitElecPower / intfVoltageComplex(0, 0));
182 intfCurrentComplex(1, 0) = intfCurrentComplex(0, 0) * SHIFT_TO_PHASE_B;
183 intfCurrentComplex(2, 0) = intfCurrentComplex(0, 0) * SHIFT_TO_PHASE_C;
184
185 //save real interface quantities calculated from complex ones
186 **mIntfVoltage = intfVoltageComplex.real();
187 **mIntfCurrent = intfCurrentComplex.real();
188
189 mImpedance = Complex(**mRs, mXpd);
190
191 // Calculate initial emf behind reactance from power flow results
192 **mEp = intfVoltageComplex(0, 0) - mImpedance * intfCurrentComplex(0, 0);
193
194 // The absolute value of Ep is constant, only delta_p changes every step
195 **mEp_abs = Math::abs(**mEp);
196 // Delta_p is the angular position of mEp with respect to the synchronously rotating reference
197 **mDelta_p = Math::phase(**mEp);
198
199 // // Update active electrical power that is compared with the mechanical power
200 **mElecActivePower = (3. / 2. * intfVoltageComplex(0, 0) *
201 std::conj(-intfCurrentComplex(0, 0)))
202 .real();
203 // mElecActivePower = ( (mEp - (**mIntfVoltage)(0,0)) / mImpedance * (**mIntfVoltage)(0,0) ).real();
204 // For infinite power bus
205 // mElecActivePower = (Math::abs(mEp) * Math::abs((**mIntfVoltage)(0,0)) / mXpd) * sin(mDelta_p);
206
207 // Start in steady state so that electrical and mech. power are the same
208 // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
210
211 // Initialize node between X'd and Ep
212 mVirtualNodes[0]->setInitialVoltage(PEAK1PH_TO_RMS3PH * **mEp);
213
214 MatrixComp vref = MatrixComp::Zero(3, 1);
216
217 // Set emf on the already-created voltage source; the framework's generic
218 // sub-init loop will initialize it after this hook returns.
219 mSubVoltageSource->setParameters(vref, frequency);
220
221 SPDLOG_LOGGER_INFO(mSLog,
222 "\n--- Initialize according to powerflow ---"
223 "\nTerminal 0 voltage: {:e}<{:e}"
224 "\nVoltage behind reactance: {:e}<{:e}"
225 "\ninitial electrical power: {:e}+j{:e}"
226 "\nactive electrical power: {:e}"
227 "\nmechanical power: {:e}"
228 "\n--- End of powerflow initialization ---",
229 Math::abs((**mIntfVoltage)(0, 0)),
230 Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
231 Math::phaseDeg(**mEp), mInitElecPower.real(),
232 mInitElecPower.imag(), **mElecActivePower, **mMechPower);
233}
234
235void EMT::Ph3::SynchronGeneratorTrStab::step(Real time) {
236 // #### Calculations on input of time step k ####
237 // Transform interface quantities to synchronously rotating DQ reference frame
238 Matrix intfVoltageDQ = parkTransformPowerInvariant(mThetaN, **mIntfVoltage);
239 Matrix intfCurrentDQ = parkTransformPowerInvariant(mThetaN, **mIntfCurrent);
240 // Update electrical power (minus sign to calculate generated power from consumed current)
241 **mElecActivePower = -1. * (intfVoltageDQ(0, 0) * intfCurrentDQ(0, 0) +
242 intfVoltageDQ(1, 0) * intfCurrentDQ(1, 0));
243
244 // The damping factor Kd is adjusted to obtain a damping ratio of 0.3
245 // Real MaxElecActivePower= Math::abs(mEp) * Math::abs((**mIntfVoltage)(0,0)) / mXpd;
246 // mKd=4*0.3*sqrt(mNomOmega*mInertia*MaxElecActivePower*0.5);
247 mKd = 1 * mNomPower;
248
249 // #### Calculate state for time step k+1 ####
250 // semi-implicit Euler or symplectic Euler method for mechanical equations
251 Real dOmMech =
252 mNomOmega / (2. * **mInertia * mNomPower) *
253 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
254 if (mBehaviour == Behaviour::MNASimulation)
255 **mOmMech = **mOmMech + mTimeStep * dOmMech;
256 Real dDelta_p = **mOmMech - mNomOmega;
257 if (mBehaviour == Behaviour::MNASimulation)
258 **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
259 // Update emf - only phase changes
260 if (mBehaviour == Behaviour::MNASimulation)
261 **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
262
263 // Update nominal system angle
264 mThetaN = mThetaN + mTimeStep * mNomOmega;
265
266 // mStates << Math::abs(mEp), Math::phaseDeg(mEp), mElecActivePower, mMechPower,
267 // mDelta_p, mOmMech, dOmMech, dDelta_p, (**mIntfVoltage)(0,0).real(), (**mIntfVoltage)(0,0).imag();
268 // SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time, Logger::matrixToString(mStates));
269}
270
272 Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
273 mTimeStep = timeStep;
274 mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
275}
276
277void EMT::Ph3::SynchronGeneratorTrStab::mnaParentAddPreStepDependencies(
278 AttributeBase::List &prevStepDependencies,
279 AttributeBase::List &attributeDependencies,
280 AttributeBase::List &modifiedAttributes) {
281 prevStepDependencies.push_back(mIntfVoltage);
282};
283
284void EMT::Ph3::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
285 AttributeBase::List &prevStepDependencies,
286 AttributeBase::List &attributeDependencies,
287 AttributeBase::List &modifiedAttributes,
288 Attribute<Matrix>::Ptr &leftVector) {
289 attributeDependencies.push_back(leftVector);
290 modifiedAttributes.push_back(mIntfVoltage);
291};
292
294 Int timeStepCount) {
295 step(time);
296 //change magnitude of subvoltage source
297 MatrixComp vref = MatrixComp::Zero(3, 1);
298 vref = CPS::Math::singlePhaseVariableToThreePhase(PEAK1PH_TO_RMS3PH * **mEp);
299 mSubVoltageSource->mVoltageRef->set(vref);
300}
301
302void EMT::Ph3::SynchronGeneratorTrStab::AddBStep::execute(Real time,
303 Int timeStepCount) {
304 **mGenerator.mRightVector = **mGenerator.mSubInductor->mRightVector +
305 **mGenerator.mSubVoltageSource->mRightVector;
306}
307
309 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
310 mnaCompUpdateVoltage(**leftVector);
311 mnaCompUpdateCurrent(**leftVector);
312}
313
314void EMT::Ph3::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
315 const Matrix &leftVector) {
316 SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
317 (**mIntfVoltage)(0, 0) =
318 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0));
319 (**mIntfVoltage)(1, 0) =
320 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1));
321 (**mIntfVoltage)(2, 0) =
322 Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2));
323}
324
325void EMT::Ph3::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
326 const Matrix &leftVector) {
327 SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
328
329 **mIntfCurrent = **mSubInductor->mIntfCurrent;
330}
Real mNomFreq
nominal frequency fn [Hz]
Real mTimeStep
Simulation time step.
Real mLmd
d-axis mutual inductance Lmd [H]
Real mLlfd
field leakage inductance Llfd [H]
const Attribute< Real >::Ptr mMechPower
mechanical Power Pm [W]
Real mNomVolt
nominal voltage Vn [V] (phase-to-phase RMS)
Real mBase_L
base stator inductance
Real mBase_Z
base stator impedance
const Attribute< Real >::Ptr mRs
stator resistance Rs [Ohm]
const Attribute< Real >::Ptr mElecActivePower
Active part of the electrical power.
StateType mStateType
specifies if the machine parameters are transformed to per unit
Real mNomOmega
nominal angular frequency wn [Hz]
const Attribute< Real >::Ptr mLl
leakage inductance Ll [H]
Real mLfd
field inductance Lfd [H]
const Attribute< Real >::Ptr mLd
d-axis inductance Ld [H]
Real mNomPower
nominal power Pn [VA]
const Attribute< Real >::Ptr mOmMech
rotor speed omega_r
const Attribute< Real >::Ptr mInertia
inertia constant H [s] for per unit or moment of inertia J [kg*m^2]
void addMNASubComponent(typename SimPowerComp< Real >::Ptr subc, MNA_SUBCOMP_TASK_ORDER preStepOrder, MNA_SUBCOMP_TASK_ORDER postStepOrder, Bool contributeToRightVector)
void setStandardParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Xpd, Real inertia, Real Rs=0, Real D=0)
Initializes the machine parameters.
void mnaParentInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
Initializes variables of component.
void setStandardParametersSI(Real nomPower, Real nomVolt, Real nomFreq, Int polePairNumber, Real Rs, Real Lpd, Real inertiaJ, Real Kd=0)
Initializes the machine parameters.
std::shared_ptr< VoltageSource > mSubVoltageSource
Inner voltage source that represents the generator.
SimPowerComp< Real >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
void mnaParentPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
Retrieves calculated voltage from simulation for next step.
const Attribute< Complex >::Ptr mEp
True after createSubComponents() runs; prevents double-construction.
void mnaParentPreStep(Real time, Int timeStepCount) override
Complex mImpedance
Equivalent impedance for loadflow calculation.
std::shared_ptr< Inductor > mSubInductor
Inner inductor that represents the generator impedance.
void initializeParentFromNodesAndTerminals(Real frequency) override
const Attribute< Real >::Ptr mDelta_p
Angle by which the emf Ep is leading the terminal voltage.
void createSubComponents() override
Constructs and registers MNA subcomponents without emf value; idempotent.
void setFundamentalParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd, Real inertia, Real D=0)
Initializes the machine parameters.
Real mLpd
Absolute d-axis transient inductance.
Real mXpd
Absolute d-axis transient reactance X'd.
const Attribute< String >::Ptr mName
Human readable name.
static Matrix singlePhaseParameterToThreePhase(Real parameter)
To convert single phase parameters to symmetrical three phase ones.
static MatrixComp singlePhaseVariableToThreePhase(Complex var_1ph)
To convert single phase complex variables (voltages, currents) to symmetrical three phase ones.
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
SimTerminal< Real >::Ptr terminal(UInt index)
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
SimNode< Real >::List mVirtualNodes
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.