DPsim
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>
10 using namespace CPS;
11 
12 Matrix 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 
21 Matrix 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 
33 EMT::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 
51 SimPowerComp<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 
134 void EMT::Ph3::SynchronGeneratorTrStab::setInitialValues(Complex elecPower,
135  Real mechPower) {
136  mInitElecPower = elecPower;
137  mInitMechPower = mechPower;
138 }
139 
141  Real frequency) {
142 
143  // Initialize omega mech with nominal system frequency
144  **mOmMech = mNomOmega;
145 
146  mInitElecPower = (mInitElecPower == Complex(0, 0))
147  ? -terminal(0)->singlePower()
148  : mInitElecPower;
149  mInitMechPower = (mInitElecPower == Complex(0, 0)) ? mInitElecPower.real()
150  : mInitMechPower;
151 
152  // use complex interface quantities for initialization calculations
153  MatrixComp intfVoltageComplex = MatrixComp::Zero(3, 1);
154  MatrixComp intfCurrentComplex = MatrixComp::Zero(3, 1);
155  // // derive complex threephase initialization from single phase initial values (only valid for balanced systems)
156  // intfVoltageComplex(0, 0) = RMS3PH_TO_PEAK1PH * initialSingleVoltage(0);
157  intfVoltageComplex(0, 0) = initialSingleVoltage(0);
158  intfVoltageComplex(1, 0) = intfVoltageComplex(0, 0) * SHIFT_TO_PHASE_B;
159  intfVoltageComplex(2, 0) = intfVoltageComplex(0, 0) * SHIFT_TO_PHASE_C;
160  intfCurrentComplex(0, 0) =
161  std::conj(-2. / 3. * mInitElecPower / intfVoltageComplex(0, 0));
162  intfCurrentComplex(1, 0) = intfCurrentComplex(0, 0) * SHIFT_TO_PHASE_B;
163  intfCurrentComplex(2, 0) = intfCurrentComplex(0, 0) * SHIFT_TO_PHASE_C;
164 
165  //save real interface quantities calculated from complex ones
166  **mIntfVoltage = intfVoltageComplex.real();
167  **mIntfCurrent = intfCurrentComplex.real();
168 
169  mImpedance = Complex(**mRs, mXpd);
170 
171  // Calculate initial emf behind reactance from power flow results
172  **mEp = intfVoltageComplex(0, 0) - mImpedance * intfCurrentComplex(0, 0);
173 
174  // The absolute value of Ep is constant, only delta_p changes every step
175  **mEp_abs = Math::abs(**mEp);
176  // Delta_p is the angular position of mEp with respect to the synchronously rotating reference
177  **mDelta_p = Math::phase(**mEp);
178 
179  // // Update active electrical power that is compared with the mechanical power
180  **mElecActivePower = (3. / 2. * intfVoltageComplex(0, 0) *
181  std::conj(-intfCurrentComplex(0, 0)))
182  .real();
183  // mElecActivePower = ( (mEp - (**mIntfVoltage)(0,0)) / mImpedance * (**mIntfVoltage)(0,0) ).real();
184  // For infinite power bus
185  // mElecActivePower = (Math::abs(mEp) * Math::abs((**mIntfVoltage)(0,0)) / mXpd) * sin(mDelta_p);
186 
187  // Start in steady state so that electrical and mech. power are the same
188  // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
189  **mMechPower = **mElecActivePower - mKd * (**mOmMech - mNomOmega);
190 
191  // Initialize node between X'd and Ep
192  mVirtualNodes[0]->setInitialVoltage(PEAK1PH_TO_RMS3PH * **mEp);
193 
194  MatrixComp vref = MatrixComp::Zero(3, 1);
196 
197  // Create sub voltage source for emf
198  mSubVoltageSource =
199  EMT::Ph3::VoltageSource::make(**mName + "_src", mLogLevel);
200  mSubVoltageSource->setParameters(vref, frequency);
201  mSubVoltageSource->connect({SimNode::GND, mVirtualNodes[0]});
202  mSubVoltageSource->setVirtualNodeAt(mVirtualNodes[1], 0);
203  mSubVoltageSource->initialize(mFrequencies);
204  mSubVoltageSource->initializeFromNodesAndTerminals(frequency);
205  addMNASubComponent(mSubVoltageSource,
206  MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
207  MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
208 
209  // Create sub inductor as Xpd
210  mSubInductor = EMT::Ph3::Inductor::make(**mName + "_ind", mLogLevel);
211  mSubInductor->setParameters(
213  mSubInductor->connect({mVirtualNodes[0], terminal(0)->node()});
214  mSubInductor->initialize(mFrequencies);
215  mSubInductor->initializeFromNodesAndTerminals(frequency);
216  addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
217  MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
218 
219  SPDLOG_LOGGER_INFO(mSLog,
220  "\n--- Initialize according to powerflow ---"
221  "\nTerminal 0 voltage: {:e}<{:e}"
222  "\nVoltage behind reactance: {:e}<{:e}"
223  "\ninitial electrical power: {:e}+j{:e}"
224  "\nactive electrical power: {:e}"
225  "\nmechanical power: {:e}"
226  "\n--- End of powerflow initialization ---",
227  Math::abs((**mIntfVoltage)(0, 0)),
228  Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
229  Math::phaseDeg(**mEp), mInitElecPower.real(),
230  mInitElecPower.imag(), **mElecActivePower, **mMechPower);
231 }
232 
233 void EMT::Ph3::SynchronGeneratorTrStab::step(Real time) {
234  // #### Calculations on input of time step k ####
235  // Transform interface quantities to synchronously rotating DQ reference frame
236  Matrix intfVoltageDQ = parkTransformPowerInvariant(mThetaN, **mIntfVoltage);
237  Matrix intfCurrentDQ = parkTransformPowerInvariant(mThetaN, **mIntfCurrent);
238  // Update electrical power (minus sign to calculate generated power from consumed current)
239  **mElecActivePower = -1. * (intfVoltageDQ(0, 0) * intfCurrentDQ(0, 0) +
240  intfVoltageDQ(1, 0) * intfCurrentDQ(1, 0));
241 
242  // The damping factor Kd is adjusted to obtain a damping ratio of 0.3
243  // Real MaxElecActivePower= Math::abs(mEp) * Math::abs((**mIntfVoltage)(0,0)) / mXpd;
244  // mKd=4*0.3*sqrt(mNomOmega*mInertia*MaxElecActivePower*0.5);
245  mKd = 1 * mNomPower;
246 
247  // #### Calculate state for time step k+1 ####
248  // semi-implicit Euler or symplectic Euler method for mechanical equations
249  Real dOmMech =
250  mNomOmega / (2. * **mInertia * mNomPower) *
251  (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
252  if (mBehaviour == Behaviour::MNASimulation)
253  **mOmMech = **mOmMech + mTimeStep * dOmMech;
254  Real dDelta_p = **mOmMech - mNomOmega;
255  if (mBehaviour == Behaviour::MNASimulation)
256  **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
257  // Update emf - only phase changes
258  if (mBehaviour == Behaviour::MNASimulation)
259  **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
260 
261  // Update nominal system angle
262  mThetaN = mThetaN + mTimeStep * mNomOmega;
263 
264  // mStates << Math::abs(mEp), Math::phaseDeg(mEp), mElecActivePower, mMechPower,
265  // mDelta_p, mOmMech, dOmMech, dDelta_p, (**mIntfVoltage)(0,0).real(), (**mIntfVoltage)(0,0).imag();
266  // SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time, Logger::matrixToString(mStates));
267 }
268 
270  Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
271  mTimeStep = timeStep;
272  mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
273 }
274 
275 void EMT::Ph3::SynchronGeneratorTrStab::mnaParentAddPreStepDependencies(
276  AttributeBase::List &prevStepDependencies,
277  AttributeBase::List &attributeDependencies,
278  AttributeBase::List &modifiedAttributes) {
279  prevStepDependencies.push_back(mIntfVoltage);
280 };
281 
282 void EMT::Ph3::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
283  AttributeBase::List &prevStepDependencies,
284  AttributeBase::List &attributeDependencies,
285  AttributeBase::List &modifiedAttributes,
286  Attribute<Matrix>::Ptr &leftVector) {
287  attributeDependencies.push_back(leftVector);
288  modifiedAttributes.push_back(mIntfVoltage);
289 };
290 
292  Int timeStepCount) {
293  step(time);
294  //change magnitude of subvoltage source
295  MatrixComp vref = MatrixComp::Zero(3, 1);
296  vref = CPS::Math::singlePhaseVariableToThreePhase(PEAK1PH_TO_RMS3PH * **mEp);
297  mSubVoltageSource->mVoltageRef->set(vref);
298 }
299 
300 void EMT::Ph3::SynchronGeneratorTrStab::AddBStep::execute(Real time,
301  Int timeStepCount) {
302  **mGenerator.mRightVector = **mGenerator.mSubInductor->mRightVector +
303  **mGenerator.mSubVoltageSource->mRightVector;
304 }
305 
307  Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
308  mnaCompUpdateVoltage(**leftVector);
309  mnaCompUpdateCurrent(**leftVector);
310 }
311 
312 void EMT::Ph3::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
313  const Matrix &leftVector) {
314  SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
315  (**mIntfVoltage)(0, 0) =
316  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0));
317  (**mIntfVoltage)(1, 0) =
318  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1));
319  (**mIntfVoltage)(2, 0) =
320  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2));
321 }
322 
323 void EMT::Ph3::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
324  const Matrix &leftVector) {
325  SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
326 
327  **mIntfCurrent = **mSubInductor->mIntfCurrent;
328 }
Real mNomFreq
nominal frequency fn [Hz]
Real mNomVolt
nominal voltage Vn [V] (phase-to-phase RMS)
Real mBase_Z
base stator impedance
const Attribute< Real >::Ptr mRs
stator resistance Rs [Ohm]
Real mNomPower
nominal power Pn [VA]
const Attribute< Real >::Ptr mInertia
inertia constant H [s] for per unit or moment of inertia J [kg*m^2]
Base class for composite power components.
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.
void mnaParentPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
Retrieves calculated voltage from simulation for next step.
void mnaParentPreStep(Real time, Int timeStepCount) override
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
void setFundamentalParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd, Real inertia, Real D=0)
Initializes the machine parameters.
Real mXpd
Absolute d-axis transient reactance X'd.
static Matrix singlePhaseParameterToThreePhase(Real parameter)
To convert single phase parameters to symmetrical three phase ones.
Definition: MathUtils.cpp:211
static MatrixComp singlePhaseVariableToThreePhase(Complex var_1ph)
To convert single phase complex variables (voltages, currents) to symmetrical three phase ones.
Definition: MathUtils.cpp:205
Base class for all components that are transmitting power.
Definition: SimPowerComp.h:17
Logger::Level mLogLevel
Component logger control for internal variables.