53 resetZeroCrossingTimes();
68 copy->setSwitchingMode(mSwitchingMode);
69 copy->setZeroCrossingTolerance(mZeroCrossingTolerance);
70 copy->setExponentialSwitchingTime(mExponentialSwitchingTime);
75 mSwitchingMode = mode;
79 resetZeroCrossingTimes();
81 mPreviousCurrentValid =
false;
82 mResetZeroCrossingHistory =
false;
84 resetExponentialTransition();
85 synchronizeEffectiveResistance(**
mIsClosed);
89 if (tolerance < 0.0) {
90 throw std::invalid_argument(
91 "DP::Ph3::Switch zero-crossing tolerance must be non-negative.");
94 mZeroCrossingTolerance = tolerance;
98 if (switchingTime <= 0.0) {
99 throw std::invalid_argument(
100 "DP::Ph3::Switch exponential switching time must be positive.");
103 mExponentialSwitchingTime = switchingTime;
112 resetExponentialTransition();
114 synchronizeEffectiveResistance(
false);
117 SPDLOG_LOGGER_INFO(
mSLog,
118 "DP opening command received during an exponential "
119 "closing transition. Transition abandoned.");
126 resetZeroCrossingTimes();
127 resetExponentialTransition();
128 synchronizeEffectiveResistance(
false);
132 if (allPolesOpen()) {
139 resetZeroCrossingTimes();
140 resetExponentialTransition();
141 mResetZeroCrossingHistory =
true;
144 mSLog,
"DP current-zero opening command received. "
145 "Waiting for reconstructed physical phase-current zeros.");
149 validateExponentialResistanceParameters();
151 resetZeroCrossingTimes();
152 resetExponentialTransition();
154 synchronizeEffectiveResistance(
true);
157 SPDLOG_LOGGER_INFO(
mSLog,
158 "DP exponential ZCS-emulation opening command received. "
159 "Switching duration={:.6e}s.",
160 mExponentialSwitchingTime);
167 resetZeroCrossingTimes();
168 mResetZeroCrossingHistory =
false;
172 resetExponentialTransition();
173 synchronizeEffectiveResistance(
true);
176 mSLog,
"DP switch closing command: all three physical poles closed.");
181 resetExponentialTransition();
182 synchronizeEffectiveResistance(
true);
190 validateExponentialResistanceParameters();
191 resetExponentialTransition();
195 synchronizeEffectiveResistance(
false);
198 SPDLOG_LOGGER_INFO(
mSLog,
199 "DP exponential ZCS-emulation closing command received. "
200 "Switching duration={:.6e}s.",
201 mExponentialSwitchingTime);
224 resetZeroCrossingTimes();
225 resetExponentialTransition();
226 synchronizeEffectiveResistance(**
mIsClosed);
228 SPDLOG_LOGGER_INFO(
mSLog,
229 "\n--- Initialization from powerflow ---"
230 "\nVoltage envelope:"
232 "\nCurrent envelope:"
234 "\nTerminal 0 voltage envelope:"
236 "\nTerminal 1 voltage envelope:"
238 "\nSwitching mode: {:s}"
239 "\nInitial breaker state: {:s}"
240 "\n--- Initialization from powerflow finished ---",
249 :
"ExponentialZCSEmulation"),
256 mTimeStep = timeStep;
264 synchronizeEffectiveResistance(**
mIsClosed);
265 resetExponentialTransition();
275 const Matrix instantaneous = reconstructInstantaneousCurrent(0.0);
277 setInstantaneousCurrentAttributes(instantaneous);
279 mPreviousInstantaneousCurrent = instantaneous;
280 mEffectiveResistancePrev = {{
286 mPreviousCurrentTime = 0.0;
287 mPreviousCurrentValid =
true;
288 mResetZeroCrossingHistory =
false;
295 return allPolesClosed();
318 closed ? (**mClosedResistance).inverse() : (**mOpenResistance).inverse();
330 attributeDependencies.push_back(leftVector);
363 const Matrix instantaneous = reconstructInstantaneousCurrent(time);
365 setInstantaneousCurrentAttributes(instantaneous);
368 updateZeroCrossingState(time);
370 updateExponentialTransition(time);
378 (**mIntfVoltage)(0, 0) =
380 (**mIntfVoltage)(1, 0) =
382 (**mIntfVoltage)(2, 0) =
387 (**mIntfVoltage)(0, 0) -=
389 (**mIntfVoltage)(1, 0) -=
391 (**mIntfVoltage)(2, 0) -=
411 const std::array<Real, 3> resistanceNow{{
417 Bool changed =
false;
418 for (
UInt phase = 0; phase < 3; ++phase) {
419 if (resistanceNow[phase] != mEffectiveResistancePrev[phase]) {
420 mEffectiveResistancePrev[phase] = resistanceNow[phase];
427 const std::array<Bool, 3> poleClosedNow{{
433 Bool changed =
false;
435 for (
UInt phase = 0; phase < 3; ++phase) {
436 if (poleClosedNow[phase] != mPoleClosedPrev[phase]) {
437 mPoleClosedPrev[phase] = poleClosedNow[phase];
445Bool DP::Ph3::Switch::poleClosed(
UInt phase)
const {
448 return **mPoleClosedA;
450 return **mPoleClosedB;
452 return **mPoleClosedC;
454 throw std::out_of_range(
"DP::Ph3::Switch phase index must be 0, 1, or 2.");
458void DP::Ph3::Switch::setPoleClosed(
UInt phase,
Bool closed) {
461 **mPoleClosedA = closed;
464 **mPoleClosedB = closed;
467 **mPoleClosedC = closed;
470 throw std::out_of_range(
"DP::Ph3::Switch phase index must be 0, 1, or 2.");
475 if (mSwitchingMode == SwitchingMode::CurrentZero &&
476 (**mClosedResistance).rows() >= 3 && (**mOpenResistance).rows() >= 3) {
477 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
478 : (**mOpenResistance)(phase, phase));
482void DP::Ph3::Switch::setAllPoles(
Bool closed) {
483 **mPoleClosedA = closed;
484 **mPoleClosedB = closed;
485 **mPoleClosedC = closed;
488Bool DP::Ph3::Switch::allPolesClosed()
const {
489 return **mPoleClosedA && **mPoleClosedB && **mPoleClosedC;
492Bool DP::Ph3::Switch::allPolesOpen()
const {
493 return !**mPoleClosedA && !**mPoleClosedB && !**mPoleClosedC;
496void DP::Ph3::Switch::resetZeroCrossingTimes() {
497 **mZeroCrossingTimeA = -1.0;
498 **mZeroCrossingTimeB = -1.0;
499 **mZeroCrossingTimeC = -1.0;
502Real DP::Ph3::Switch::effectiveResistance(
UInt phase)
const {
505 return **mEffectiveResistanceA;
507 return **mEffectiveResistanceB;
509 return **mEffectiveResistanceC;
511 throw std::out_of_range(
"DP::Ph3::Switch phase index must be 0, 1, or 2.");
515void DP::Ph3::Switch::setEffectiveResistance(
UInt phase,
Real resistance) {
518 **mEffectiveResistanceA = resistance;
521 **mEffectiveResistanceB = resistance;
524 **mEffectiveResistanceC = resistance;
527 throw std::out_of_range(
"DP::Ph3::Switch phase index must be 0, 1, or 2.");
531void DP::Ph3::Switch::synchronizeEffectiveResistance(
Bool closed) {
532 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3)
535 for (
UInt phase = 0; phase < 3; ++phase) {
536 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
537 : (**mOpenResistance)(phase, phase));
541void DP::Ph3::Switch::resetExponentialTransition() {
542 **mExponentialTransitionActive =
false;
543 **mExponentialTransitionClosing =
false;
544 **mExponentialProgress = 0.0;
545 **mExponentialTransitionStartTime = -1.0;
546 **mExponentialTransitionEndTime = -1.0;
547 mExponentialTransitionStarted =
false;
550void DP::Ph3::Switch::validateExponentialResistanceParameters()
const {
551 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3) {
552 throw std::invalid_argument(
553 "DP::Ph3::Switch exponential mode requires 3x3 resistance matrices.");
556 if (mExponentialSwitchingTime <= 0.0) {
557 throw std::invalid_argument(
558 "DP::Ph3::Switch exponential switching time must be positive.");
561 for (
UInt phase = 0; phase < 3; ++phase) {
562 const Real rClosed = (**mClosedResistance)(phase, phase);
563 const Real rOpen = (**mOpenResistance)(phase, phase);
565 if (rClosed <= 0.0 || rOpen <= 0.0) {
566 throw std::invalid_argument(
567 "DP::Ph3::Switch exponential mode requires positive diagonal "
568 "open and closed resistances.");
573Real DP::Ph3::Switch::exponentialResistance(
UInt phase,
Real alpha)
const {
574 const Real rClosed = (**mClosedResistance)(phase, phase);
575 const Real rOpen = (**mOpenResistance)(phase, phase);
576 const Real boundedAlpha = std::max(0.0, std::min(1.0, alpha));
578 return std::exp(std::log(rClosed) +
579 boundedAlpha * (std::log(rOpen) - std::log(rClosed)));
582void DP::Ph3::Switch::updateExponentialTransition(
Real time) {
583 if (!**mExponentialTransitionActive)
586 if (!mExponentialTransitionStarted) {
587 mExponentialTransitionStarted =
true;
588 **mExponentialTransitionStartTime = time;
589 **mExponentialTransitionEndTime = time + mExponentialSwitchingTime;
592 const Real targetTime = time + mTimeStep;
593 const Real elapsed = (targetTime - **mExponentialTransitionStartTime) /
594 mExponentialSwitchingTime;
595 const Real boundedElapsed = std::max(0.0, std::min(1.0, elapsed));
597 const Bool closing = **mExponentialTransitionClosing;
600 const Real alpha = closing ? 1.0 - boundedElapsed : boundedElapsed;
602 **mExponentialProgress = alpha;
604 for (
UInt phase = 0; phase < 3; ++phase)
605 setEffectiveResistance(phase, exponentialResistance(phase, alpha));
607 if (boundedElapsed >= 1.0) {
608 synchronizeEffectiveResistance(closing);
609 setAllPoles(closing);
610 **mOpeningRequested =
false;
611 **mExponentialTransitionActive =
false;
612 **mExponentialTransitionClosing =
false;
616 "DP exponential ZCS-emulation {:s} completed: start={:.9f}s, "
617 "end={:.9f}s, duration={:.6e}s.",
618 closing ?
"closing" :
"opening", **mExponentialTransitionStartTime,
619 **mExponentialTransitionEndTime, mExponentialSwitchingTime);
626 if (mSwitchingMode == SwitchingMode::Ideal) {
627 conductance.real() = (**mIsClosed) ? (**mClosedResistance).inverse()
628 : (**mOpenResistance).inverse();
632 for (
UInt phase = 0; phase < 3; ++phase) {
633 Real resistance = 0.0;
635 if (mSwitchingMode == SwitchingMode::CurrentZero) {
636 resistance = poleClosed(phase) ? (**mClosedResistance)(phase, phase)
637 : (**mOpenResistance)(phase, phase);
639 resistance = effectiveResistance(phase);
642 conductance(phase, phase) =
Complex(1.0 / resistance, 0.0);
648Matrix DP::Ph3::Switch::reconstructInstantaneousCurrent(
Real time)
const {
649 Matrix instantaneous = Matrix::Zero(3, 1);
653 const Complex carrier = std::polar<Real>(1.0, mShiftOmega * time);
655 for (
UInt phase = 0; phase < 3; ++phase) {
656 instantaneous(phase, 0) = std::real((**mIntfCurrent)(phase, 0) * carrier);
659 return instantaneous;
662void DP::Ph3::Switch::setInstantaneousCurrentAttributes(
const Matrix ¤t) {
663 **mInstantCurrentA = current(0, 0);
664 **mInstantCurrentB = current(1, 0);
665 **mInstantCurrentC = current(2, 0);
668Bool DP::Ph3::Switch::currentCrossedZero(
Real previousCurrent,
669 Real current)
const {
670 if (std::abs(current) <= mZeroCrossingTolerance)
673 return (previousCurrent > mZeroCrossingTolerance &&
674 current < -mZeroCrossingTolerance) ||
675 (previousCurrent < -mZeroCrossingTolerance &&
676 current > mZeroCrossingTolerance);
679Real DP::Ph3::Switch::interpolateZeroCrossingTime(
Real previousCurrent,
682 Real currentTime)
const {
683 const Real denominator = std::abs(previousCurrent) + std::abs(current);
685 if (denominator <= mZeroCrossingTolerance)
688 const Real fraction = std::abs(previousCurrent) / denominator;
690 return previousTime + fraction * (currentTime - previousTime);
693void DP::Ph3::Switch::setZeroCrossingTime(
UInt phase,
Real time) {
696 **mZeroCrossingTimeA = time;
699 **mZeroCrossingTimeB = time;
702 **mZeroCrossingTimeC = time;
705 throw std::out_of_range(
"DP::Ph3::Switch phase index must be 0, 1, or 2.");
709void DP::Ph3::Switch::updateZeroCrossingState(
Real time) {
710 const Matrix current = reconstructInstantaneousCurrent(time);
713 if (!**mOpeningRequested) {
714 mPreviousInstantaneousCurrent = current;
715 mPreviousCurrentTime = time;
716 mPreviousCurrentValid =
true;
721 if (mResetZeroCrossingHistory || !mPreviousCurrentValid) {
722 for (
UInt phase = 0; phase < 3; ++phase) {
723 if (poleClosed(phase) &&
724 std::abs(current(phase, 0)) <= mZeroCrossingTolerance) {
725 setPoleClosed(phase,
false);
726 setZeroCrossingTime(phase, time);
730 "DP phase {} opened at sampled physical current zero: "
731 "t={:.9f}s, i={:.6e}A",
732 phase, time, current(phase, 0));
736 mPreviousInstantaneousCurrent = current;
737 mPreviousCurrentTime = time;
738 mPreviousCurrentValid =
true;
739 mResetZeroCrossingHistory =
false;
742 **mOpeningRequested =
false;
747 for (
UInt phase = 0; phase < 3; ++phase) {
748 if (!poleClosed(phase))
751 const Real previous = mPreviousInstantaneousCurrent(phase, 0);
752 const Real present = current(phase, 0);
754 if (!currentCrossedZero(previous, present))
757 const Real zeroTime = interpolateZeroCrossingTime(
758 previous, present, mPreviousCurrentTime, time);
760 setPoleClosed(phase,
false);
761 setZeroCrossingTime(phase, zeroTime);
763 SPDLOG_LOGGER_INFO(mSLog,
764 "DP phase {} physical current zero detected: "
765 "t_z={:.9f}s, i_prev={:.6e}A, i={:.6e}A. "
767 phase, zeroTime, previous, present);
770 mPreviousInstantaneousCurrent = current;
771 mPreviousCurrentTime = time;
772 mPreviousCurrentValid =
true;
774 if (allPolesOpen()) {
775 **mOpeningRequested =
false;
777 SPDLOG_LOGGER_INFO(mSLog,
"DP current-zero interruption completed: "
778 "all three physical breaker poles are open.");
AttributePointer< Attribute< T > > Ptr
const CPS::Attribute< Matrix >::Ptr mClosedResistance
Resistance if switch is closed [ohm].
virtual void closeSwitch()
const CPS::Attribute< Matrix >::Ptr mOpenResistance
Resistance if switch is open [ohm].
virtual void openSwitch()
const CPS::Attribute< Bool >::Ptr mIsClosed
Commanded/global switch state.
const Attribute< Bool >::Ptr mPoleClosedA
Bool hasParameterChanged() override
Returns true if one of the element paramters has changed.
void closeSwitch() override
const Attribute< Bool >::Ptr mPoleClosedB
const Attribute< Real >::Ptr mInstantCurrentA
Reconstructed physical instantaneous currents [A].
void mnaCompUpdateCurrent(const Matrix &leftVector) override
void setZeroCrossingTolerance(Real tolerance)
void setExponentialSwitchingTime(Real switchingTime)
const Attribute< Bool >::Ptr mExponentialTransitionActive
Exponential-transition diagnostics.
const Attribute< Real >::Ptr mEffectiveResistanceA
Effective phase resistances currently stamped into the MNA matrix [ohm].
const Attribute< Real >::Ptr mExponentialTransitionStartTime
@ ExponentialZCSEmulation
const Attribute< Bool >::Ptr mExponentialTransitionClosing
True while the active exponential transition is a closing transition.
void mnaCompUpdateVoltage(const Matrix &leftVector) override
Switch(String uid, String name, Logger::Level loglevel=Logger::Level::off)
const Attribute< Real >::Ptr mZeroCrossingTimeB
Bool mnaIsClosed() override
Check if switch is closed.
void setSwitchingMode(SwitchingMode mode)
const Attribute< Real >::Ptr mExponentialTransitionEndTime
const Attribute< Real >::Ptr mZeroCrossingTimeA
const Attribute< Bool >::Ptr mPoleClosedC
void openSwitch() override
Bool supportsPrecomputedSystemMatrices() const override
SimPowerComp< Complex >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
void mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
const Attribute< Real >::Ptr mInstantCurrentC
const Attribute< Real >::Ptr mEffectiveResistanceB
const Attribute< Real >::Ptr mInstantCurrentB
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
const Attribute< Bool >::Ptr mOpeningRequested
void mnaCompApplySwitchSystemMatrixStamp(Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) override
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
const Attribute< Real >::Ptr mEffectiveResistanceC
const Attribute< Real >::Ptr mExponentialProgress
const Attribute< Real >::Ptr mZeroCrossingTimeC
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
static String phasorMatrixToString(const MatrixComp &mat)
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
static void stampAdmittanceMatrix(const MatrixComp &admittanceMat, 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
SimTerminal< Complex >::List mTerminals
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
MatrixComp initialVoltage(UInt index)
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)
#define RMS3PH_TO_PEAK1PH
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
std::complex< Real > Complex
Eigen::Matrix< Complex, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > MatrixComp
Dense matrix for complex numbers.
Eigen::Matrix< Complex, rows, cols, Eigen::ColMajor > MatrixFixedSizeComp
Dense matrix for complex numbers with fixed dimension.
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).