43 resetZeroCrossingTime();
56 copy->setSwitchingMode(mSwitchingMode);
57 copy->setZeroCrossingTolerance(mZeroCrossingTolerance);
58 copy->setExponentialSwitchingTime(mExponentialSwitchingTime);
63 mSwitchingMode = mode;
67 resetZeroCrossingTime();
69 mPreviousCurrentValid =
false;
70 mResetZeroCrossingHistory =
false;
72 resetExponentialTransition();
73 synchronizeEffectiveResistance(**
mIsClosed);
77 if (tolerance < 0.0) {
78 throw std::invalid_argument(
79 "DP::Ph1::Switch zero-crossing tolerance must be non-negative.");
82 mZeroCrossingTolerance = tolerance;
86 if (switchingTime <= 0.0) {
87 throw std::invalid_argument(
88 "DP::Ph1::Switch exponential switching time must be positive.");
91 mExponentialSwitchingTime = switchingTime;
100 resetExponentialTransition();
102 synchronizeEffectiveResistance(
false);
105 SPDLOG_LOGGER_INFO(
mSLog,
106 "DP opening command received during an exponential "
107 "closing transition. Transition abandoned.");
114 resetZeroCrossingTime();
115 resetExponentialTransition();
116 synchronizeEffectiveResistance(
false);
127 resetZeroCrossingTime();
128 resetExponentialTransition();
129 mResetZeroCrossingHistory =
true;
131 SPDLOG_LOGGER_INFO(
mSLog,
132 "DP current-zero opening command received. "
133 "Waiting for the reconstructed physical current zero.");
137 validateExponentialResistanceParameters();
139 resetZeroCrossingTime();
140 resetExponentialTransition();
142 synchronizeEffectiveResistance(
true);
145 SPDLOG_LOGGER_INFO(
mSLog,
146 "DP exponential ZCS-emulation opening command received. "
147 "Switching duration={:.6e}s.",
148 mExponentialSwitchingTime);
155 resetZeroCrossingTime();
156 mResetZeroCrossingHistory =
false;
160 resetExponentialTransition();
161 synchronizeEffectiveResistance(
true);
163 SPDLOG_LOGGER_INFO(
mSLog,
"DP switch closing command: pole closed.");
168 resetExponentialTransition();
169 synchronizeEffectiveResistance(
true);
177 validateExponentialResistanceParameters();
178 resetExponentialTransition();
182 synchronizeEffectiveResistance(
false);
185 SPDLOG_LOGGER_INFO(
mSLog,
186 "DP exponential ZCS-emulation closing command received. "
187 "Switching duration={:.6e}s.",
188 mExponentialSwitchingTime);
195 (**mIntfCurrent)(0, 0) = (**
mIntfVoltage)(0, 0) / impedance;
200 resetZeroCrossingTime();
201 resetExponentialTransition();
202 synchronizeEffectiveResistance(**
mIsClosed);
204 SPDLOG_LOGGER_INFO(
mSLog,
205 "\n--- Initialization from powerflow ---"
206 "\nVoltage across: {:s}"
208 "\nTerminal 0 voltage: {:s}"
209 "\nTerminal 1 voltage: {:s}"
210 "\nSwitching mode: {:s}"
211 "\nInitial breaker state: {:s}"
212 "\n--- Initialization from powerflow finished ---",
221 :
"ExponentialZCSEmulation"),
228 mTimeStep = timeStep;
236 synchronizeEffectiveResistance(**
mIsClosed);
237 resetExponentialTransition();
247 mPreviousCurrentTime = 0.0;
248 mPreviousCurrentValid =
true;
249 mResetZeroCrossingHistory =
false;
287 (**mIntfVoltage)(0, 0) = 0;
289 (**mIntfVoltage)(0, 0) =
292 (**mIntfVoltage)(0, 0) =
298 (**mIntfCurrent)(0, 0) = currentConductance() * (**mIntfVoltage)(0, 0);
307 attributeDependencies.push_back(leftVector);
333 updateZeroCrossingState(time);
335 updateExponentialTransition(time);
367void DP::Ph1::Switch::setPoleClosed(
Bool closed) {
368 **mPoleClosed = closed;
372 if (mSwitchingMode == SwitchingMode::CurrentZero)
373 synchronizeEffectiveResistance(closed);
376void DP::Ph1::Switch::resetZeroCrossingTime() { **mZeroCrossingTime = -1.0; }
378void DP::Ph1::Switch::synchronizeEffectiveResistance(
Bool closed) {
379 **mEffectiveResistance = closed ? **mClosedResistance : **mOpenResistance;
382void DP::Ph1::Switch::resetExponentialTransition() {
383 **mExponentialTransitionActive =
false;
384 **mExponentialTransitionClosing =
false;
385 **mExponentialProgress = 0.0;
386 **mExponentialTransitionStartTime = -1.0;
387 **mExponentialTransitionEndTime = -1.0;
388 mExponentialTransitionStarted =
false;
391void DP::Ph1::Switch::validateExponentialResistanceParameters()
const {
392 if (mExponentialSwitchingTime <= 0.0) {
393 throw std::invalid_argument(
394 "DP::Ph1::Switch exponential switching time must be positive.");
397 if (**mClosedResistance <= 0.0 || **mOpenResistance <= 0.0) {
398 throw std::invalid_argument(
"DP::Ph1::Switch exponential mode requires "
399 "positive open and closed resistances.");
403Real DP::Ph1::Switch::exponentialResistance(
Real alpha)
const {
404 const Real rClosed = **mClosedResistance;
405 const Real rOpen = **mOpenResistance;
406 const Real boundedAlpha = std::max(0.0, std::min(1.0, alpha));
408 return std::exp(std::log(rClosed) +
409 boundedAlpha * (std::log(rOpen) - std::log(rClosed)));
412void DP::Ph1::Switch::updateExponentialTransition(
Real time) {
413 if (!**mExponentialTransitionActive)
416 if (!mExponentialTransitionStarted) {
417 mExponentialTransitionStarted =
true;
418 **mExponentialTransitionStartTime = time;
419 **mExponentialTransitionEndTime = time + mExponentialSwitchingTime;
422 const Real targetTime = time + mTimeStep;
423 const Real elapsed = (targetTime - **mExponentialTransitionStartTime) /
424 mExponentialSwitchingTime;
425 const Real boundedElapsed = std::max(0.0, std::min(1.0, elapsed));
427 const Bool closing = **mExponentialTransitionClosing;
430 const Real alpha = closing ? 1.0 - boundedElapsed : boundedElapsed;
432 **mExponentialProgress = alpha;
433 **mEffectiveResistance = exponentialResistance(alpha);
435 if (boundedElapsed >= 1.0) {
436 synchronizeEffectiveResistance(closing);
437 **mPoleClosed = closing;
438 **mOpeningRequested =
false;
439 **mExponentialTransitionActive =
false;
440 **mExponentialTransitionClosing =
false;
444 "DP exponential ZCS-emulation {:s} completed: start={:.9f}s, "
445 "end={:.9f}s, duration={:.6e}s.",
446 closing ?
"closing" :
"opening", **mExponentialTransitionStartTime,
447 **mExponentialTransitionEndTime, mExponentialSwitchingTime);
451Complex DP::Ph1::Switch::currentConductance()
const {
452 if (mSwitchingMode == SwitchingMode::Ideal) {
453 return (**mIsClosed) ?
Complex(1. / **mClosedResistance, 0)
454 :
Complex(1. / **mOpenResistance, 0);
457 if (mSwitchingMode == SwitchingMode::CurrentZero) {
458 return (**mPoleClosed) ?
Complex(1. / **mClosedResistance, 0)
459 :
Complex(1. / **mOpenResistance, 0);
462 return Complex(1. / **mEffectiveResistance, 0);
465Real DP::Ph1::Switch::reconstructInstantaneousCurrent(
Real time)
const {
469 const Complex carrier = std::polar<Real>(1.0, mShiftOmega * time);
471 return std::real((**mIntfCurrent)(0, 0) * carrier);
474Bool DP::Ph1::Switch::currentCrossedZero(
Real previousCurrent,
475 Real current)
const {
476 if (std::abs(current) <= mZeroCrossingTolerance)
479 return (previousCurrent > mZeroCrossingTolerance &&
480 current < -mZeroCrossingTolerance) ||
481 (previousCurrent < -mZeroCrossingTolerance &&
482 current > mZeroCrossingTolerance);
485Real DP::Ph1::Switch::interpolateZeroCrossingTime(
Real previousCurrent,
488 Real currentTime)
const {
489 const Real denominator = std::abs(previousCurrent) + std::abs(current);
491 if (denominator <= mZeroCrossingTolerance)
494 const Real fraction = std::abs(previousCurrent) / denominator;
496 return previousTime + fraction * (currentTime - previousTime);
499void DP::Ph1::Switch::updateZeroCrossingState(
Real time) {
500 const Real current = **mInstantCurrent;
503 if (!**mOpeningRequested) {
504 mPreviousInstantaneousCurrent = current;
505 mPreviousCurrentTime = time;
506 mPreviousCurrentValid =
true;
511 if (mResetZeroCrossingHistory || !mPreviousCurrentValid) {
512 if (**mPoleClosed && std::abs(current) <= mZeroCrossingTolerance) {
513 setPoleClosed(
false);
514 **mZeroCrossingTime = time;
516 SPDLOG_LOGGER_INFO(mSLog,
517 "DP pole opened at sampled physical current zero: "
518 "t={:.9f}s, i={:.6e}A",
522 mPreviousInstantaneousCurrent = current;
523 mPreviousCurrentTime = time;
524 mPreviousCurrentValid =
true;
525 mResetZeroCrossingHistory =
false;
528 **mOpeningRequested =
false;
534 currentCrossedZero(mPreviousInstantaneousCurrent, current)) {
535 const Real zeroTime = interpolateZeroCrossingTime(
536 mPreviousInstantaneousCurrent, current, mPreviousCurrentTime, time);
538 setPoleClosed(
false);
539 **mZeroCrossingTime = zeroTime;
541 SPDLOG_LOGGER_INFO(mSLog,
542 "DP physical current zero detected: "
543 "t_z={:.9f}s, i_prev={:.6e}A, i={:.6e}A. Pole opened.",
544 zeroTime, mPreviousInstantaneousCurrent, current);
547 mPreviousInstantaneousCurrent = current;
548 mPreviousCurrentTime = time;
549 mPreviousCurrentValid =
true;
551 if (!**mPoleClosed) {
552 **mOpeningRequested =
false;
554 SPDLOG_LOGGER_INFO(mSLog,
"DP current-zero interruption completed: "
555 "the breaker pole is open.");
AttributePointer< Attribute< T > > Ptr
const Attribute< Real >::Ptr mClosedResistance
Resistance if switch is closed [ohm].
const Attribute< Real >::Ptr mOpenResistance
Resistance if switch is open [ohm].
virtual void closeSwitch()
virtual void openSwitch()
const Attribute< Bool >::Ptr mIsClosed
Defines if Switch is open or closed.
SimPowerComp< Complex >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
const Attribute< Real >::Ptr mInstantCurrent
Reconstructed physical instantaneous current [A].
void setSwitchingMode(SwitchingMode mode)
void initializeFromNodesAndTerminals(Real frequency) override
Initializes component from power flow data.
const Attribute< Bool >::Ptr mPoleClosed
void closeSwitch() override
Bool mnaIsClosed() override
Check if switch is closed.
void setExponentialSwitchingTime(Real switchingTime)
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
Stamps system matrix.
@ ExponentialZCSEmulation
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override
Stamps right side (source) vector.
Bool hasParameterChanged() override
Returns true if one of the element paramters has changed.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
Add MNA post step dependencies.
const Attribute< Real >::Ptr mEffectiveResistance
Effective resistance currently stamped into the MNA matrix [ohm].
Switch(String uid, String name, Logger::Level loglevel=Logger::Level::off)
Defines UID, name, component parameters and logging level.
const Attribute< Real >::Ptr mZeroCrossingTime
const Attribute< Bool >::Ptr mExponentialTransitionActive
Exponential-transition diagnostics.
const Attribute< Bool >::Ptr mOpeningRequested
void mnaCompUpdateCurrent(const Matrix &leftVector) override
Update interface current from MNA system result.
const Attribute< Bool >::Ptr mExponentialTransitionClosing
True while the active exponential transition is a closing transition.
void setZeroCrossingTolerance(Real tolerance)
const Attribute< Real >::Ptr mExponentialTransitionEndTime
void mnaCompApplySwitchSystemMatrixStamp(Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) override
Stamps system matrix considering the defined switch position.
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
void mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
MNA post step operations.
void openSwitch() override
void mnaCompUpdateVoltage(const Matrix &leftVector) override
Update interface voltage from MNA system result.
const Attribute< Real >::Ptr mExponentialProgress
const Attribute< Real >::Ptr mExponentialTransitionStartTime
Bool supportsPrecomputedSystemMatrices() const override
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
static String phasorToString(const Complex &num)
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
static void stampAdmittance(Complex admittance, SparseMatrixRow &mat, UInt node1Index, UInt node2Index, Bool isTerminal1NotGrounded, Bool isTerminal2NotGrounded, const Logger::Log &mSLog, Int maxFreq=1, Int freqIdx=0)
static Complex complexFromVectorElement(const Matrix &mat, Matrix::Index row, Int maxFreq=1, Int freqIdx=0)
UInt matrixNodeIndex(UInt nodeIndex)
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
std::shared_ptr< SimPowerComp< VarType > > Ptr
Bool terminalNotGrounded(UInt index)
Complex initialSingleVoltage(UInt index)
void updateMatrixNodeIndices()
void setTerminalNumber(UInt num)
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.
static std::shared_ptr< Switch > make(Args &&...args)
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
std::complex< Real > Complex
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).