Skip to content

Commit

Permalink
dpsim-villas/models: add co-simulation example
Browse files Browse the repository at this point in the history
This adds FpgaCosimulation.cpp, an example for a cosimulation with another simulator running a WSCC 9 bus and DPsim running the load connected to bus 5.
This also modifies EMT::Ph3::RXLoad to allow it to represent an R-L series load rather than only a parallel load.
It also adds EMT::Ph3::ControlledVoltageSource, a voltage source with EMT setpoints for feeding interface data into the DPsim simulation.

Signed-off-by: Niklas Eiling <[email protected]>
  • Loading branch information
n-eiling committed Oct 31, 2024
1 parent 6aac265 commit b6143b0
Show file tree
Hide file tree
Showing 7 changed files with 481 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
* EONERC, RWTH Aachen University
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*********************************************************************************/
#pragma once

#include <dpsim-models/MNASimPowerComp.h>
#include <dpsim-models/Signal/CosineFMGenerator.h>
#include <dpsim-models/Signal/FrequencyRampGenerator.h>
#include <dpsim-models/Signal/SignalGenerator.h>
#include <dpsim-models/Signal/SineWaveGenerator.h>
#include <dpsim-models/Solver/MNAInterface.h>

namespace CPS {
namespace EMT {
namespace Ph3 {
/// \brief Controlled Ideal Voltage source model
///
/// This model uses modified nodal analysis to represent an ideal voltage source.
/// This voltage source derives it's output purely from attributes rather than an internal signal generator.
class ControlledVoltageSource : public MNASimPowerComp<Real>, public SharedFactory<ControlledVoltageSource> {
protected:
// Updates voltage according to reference phasor and frequency
void updateVoltage(Real time);

public:
const CPS::Attribute<Matrix>::Ptr mVoltageRef;

/// Defines UID, name and logging level
ControlledVoltageSource(String uid, String name, Logger::Level logLevel = Logger::Level::off);
///
ControlledVoltageSource(String name, Logger::Level logLevel = Logger::Level::off) : ControlledVoltageSource(name, name, logLevel) {}

SimPowerComp<Real>::Ptr clone(String name) override;
// #### General ####
/// Initializes component from power flow data
void initializeFromNodesAndTerminals(Real frequency) override;
/// Setter for reference voltage with a real valued generator
/// This will initialize the values of mVoltageRef to match the given parameters
/// However, the attributes can be modified during the simulation to dynamically change the value of the output voltage.
void setParameters(Matrix voltageRef);

// #### MNA section ####
/// Initializes internal variables of the component
void mnaCompInitialize(Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) override;
/// Stamps system matrix
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override;
/// Stamps right side (source) vector
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override;
/// Returns current through the component
void mnaCompUpdateCurrent(const Matrix &leftVector) override;
/// MNA pre step operations
void mnaCompPreStep(Real time, Int timeStepCount) override;
/// MNA post step operations
void mnaCompPostStep(Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) override;
/// Add MNA pre step dependencies
void mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override;
/// Add MNA post step dependencies
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes,
Attribute<Matrix>::Ptr &leftVector) override;
};
} // namespace Ph3
} // namespace EMT
} // namespace CPS
4 changes: 3 additions & 1 deletion dpsim-models/include/dpsim-models/EMT/EMT_Ph3_RXLoad.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class RXLoad : public CompositePowerComp<Real>, public SharedFactory<RXLoad> {
Matrix mCapacitance;
///
Bool initPowerFromTerminal = true;
/// If set to true, the reactance is in series with the resistor. Otherwise it is parallel to the resistor.
Bool mReactanceInSeries;
/// Internal inductor
std::shared_ptr<EMT::Ph3::Inductor> mSubInductor;
/// Internal capacitor
Expand All @@ -58,7 +60,7 @@ class RXLoad : public CompositePowerComp<Real>, public SharedFactory<RXLoad> {

// #### General ####
///
void setParameters(Matrix activePower, Matrix reactivePower, Real volt);
void setParameters(Matrix activePower, Matrix reactivePower, Real volt, bool reactanceInSeries = false);
/// Initializes component from power flow data
void initializeFromNodesAndTerminals(Real frequency) override;

Expand Down
1 change: 1 addition & 0 deletions dpsim-models/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ list(APPEND MODELS_SOURCES

EMT/EMT_Ph3_CurrentSource.cpp
EMT/EMT_Ph3_VoltageSource.cpp
EMT/EMT_Ph3_ControlledVoltageSource.cpp
EMT/EMT_Ph3_Inductor.cpp
EMT/EMT_Ph3_Capacitor.cpp
EMT/EMT_Ph3_AvVoltageSourceInverterDQ.cpp
Expand Down
115 changes: 115 additions & 0 deletions dpsim-models/src/EMT/EMT_Ph3_ControlledVoltageSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
* EONERC, RWTH Aachen University
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*********************************************************************************/

#include <dpsim-models/EMT/EMT_Ph3_ControlledVoltageSource.h>

using namespace CPS;

EMT::Ph3::ControlledVoltageSource::ControlledVoltageSource(String uid, String name, Logger::Level logLevel)
: MNASimPowerComp<Real>(uid, name, true, true, logLevel), mVoltageRef(mAttributes->createDynamic<Matrix>("V_ref")) // rms-value, phase-to-phase
{
mPhaseType = PhaseType::ABC;
setVirtualNodeNumber(1);
setTerminalNumber(2);
**mIntfVoltage = Matrix::Zero(3, 1);
**mIntfCurrent = Matrix::Zero(3, 1);
}

void EMT::Ph3::ControlledVoltageSource::setParameters(Matrix voltageRef) {
**mVoltageRef = voltageRef;

mParametersSet = true;
}

void EMT::Ph3::ControlledVoltageSource::initializeFromNodesAndTerminals(Real frequency) {
SPDLOG_LOGGER_INFO(mSLog, "\n--- Initialization from node voltages ---");
// TODO: this approach currently overwrites voltage reference set from outside, when not using setParameters
if (!mParametersSet) {
// initialize voltage reference as zeroes
SPDLOG_LOGGER_INFO(mSLog,
"\nReference voltage: {:s}"
"\nTerminal 0 voltage: {:s}"
"\nTerminal 1 voltage: {:s}",
Logger::matrixCompToString(**mVoltageRef), Logger::phasorToString(initialSingleVoltage(0)), Logger::phasorToString(initialSingleVoltage(1)));
} else {
SPDLOG_LOGGER_INFO(mSLog,
"\nInitialization from node voltages omitted (parameter already set)."
"\nReference voltage: {:s}",
Logger::matrixCompToString(**mVoltageRef));
}
SPDLOG_LOGGER_INFO(mSLog, "\n--- Initialization from node voltages ---");
mSLog->flush();
}

SimPowerComp<Real>::Ptr EMT::Ph3::ControlledVoltageSource::clone(String name) {
auto copy = ControlledVoltageSource::make(name, mLogLevel);
copy->setParameters(**mVoltageRef);
return copy;
}

void EMT::Ph3::ControlledVoltageSource::mnaCompInitialize(Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) { updateMatrixNodeIndices(); }

void EMT::Ph3::ControlledVoltageSource::mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) {
if (terminalNotGrounded(0)) {
Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), mVirtualNodes[0]->matrixNodeIndex(PhaseType::A), -1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::A), matrixNodeIndex(0, 0), -1);

Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 1), mVirtualNodes[0]->matrixNodeIndex(PhaseType::B), -1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::B), matrixNodeIndex(0, 1), -1);

Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 2), mVirtualNodes[0]->matrixNodeIndex(PhaseType::C), -1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::C), matrixNodeIndex(0, 2), -1);
}
if (terminalNotGrounded(1)) {
Math::addToMatrixElement(systemMatrix, matrixNodeIndex(1, 0), mVirtualNodes[0]->matrixNodeIndex(PhaseType::A), 1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::A), matrixNodeIndex(1, 0), 1);

Math::addToMatrixElement(systemMatrix, matrixNodeIndex(1, 1), mVirtualNodes[0]->matrixNodeIndex(PhaseType::B), 1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::B), matrixNodeIndex(1, 1), 1);

Math::addToMatrixElement(systemMatrix, matrixNodeIndex(1, 2), mVirtualNodes[0]->matrixNodeIndex(PhaseType::C), 1);
Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(PhaseType::C), matrixNodeIndex(1, 2), 1);
}
}

void EMT::Ph3::ControlledVoltageSource::mnaCompApplyRightSideVectorStamp(Matrix &rightVector) {
Math::setVectorElement(rightVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::A), (**mIntfVoltage)(0, 0));
Math::setVectorElement(rightVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::B), (**mIntfVoltage)(1, 0));
Math::setVectorElement(rightVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::C), (**mIntfVoltage)(2, 0));
}

void EMT::Ph3::ControlledVoltageSource::updateVoltage(Real time) {
**mIntfVoltage = **mVoltageRef;

SPDLOG_LOGGER_DEBUG(mSLog, "\nUpdate Voltage: {:s}", Logger::matrixToString(**mIntfVoltage));
}

void EMT::Ph3::ControlledVoltageSource::mnaCompAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) {
attributeDependencies.push_back(mVoltageRef);
modifiedAttributes.push_back(mRightVector);
modifiedAttributes.push_back(mIntfVoltage);
}

void EMT::Ph3::ControlledVoltageSource::mnaCompPreStep(Real time, Int timeStepCount) {
updateVoltage(time);
mnaCompApplyRightSideVectorStamp(**mRightVector);
}

void EMT::Ph3::ControlledVoltageSource::mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes,
Attribute<Matrix>::Ptr &leftVector) {
attributeDependencies.push_back(leftVector);
modifiedAttributes.push_back(mIntfCurrent);
};

void EMT::Ph3::ControlledVoltageSource::mnaCompPostStep(Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) { mnaCompUpdateCurrent(**leftVector); }

void EMT::Ph3::ControlledVoltageSource::mnaCompUpdateCurrent(const Matrix &leftVector) {
(**mIntfCurrent)(0, 0) = Math::realFromVectorElement(leftVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::A));
(**mIntfCurrent)(1, 0) = Math::realFromVectorElement(leftVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::B));
(**mIntfCurrent)(2, 0) = Math::realFromVectorElement(leftVector, mVirtualNodes[0]->matrixNodeIndex(PhaseType::C));
}
104 changes: 72 additions & 32 deletions dpsim-models/src/EMT/EMT_Ph3_RXLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
using namespace CPS;

EMT::Ph3::RXLoad::RXLoad(String uid, String name, Logger::Level logLevel)
: CompositePowerComp<Real>(uid, name, true, true, logLevel),
mActivePower(mAttributes->create<Matrix>("P")),
mReactivePower(mAttributes->create<Matrix>("Q")),
mNomVoltage(mAttributes->create<Real>("V_nom")) {
: CompositePowerComp<Real>(uid, name, true, true, Logger::Level::trace), mActivePower(mAttributes->create<Matrix>("P")), mReactivePower(mAttributes->create<Matrix>("Q")),
mNomVoltage(mAttributes->create<Real>("V_nom")), mReactanceInSeries(false) {
mPhaseType = PhaseType::ABC;
setTerminalNumber(1);

Expand Down Expand Up @@ -47,11 +45,14 @@ EMT::Ph3::RXLoad::RXLoad(String name, Matrix activePower, Matrix reactivePower,
initPowerFromTerminal = false;
}

void EMT::Ph3::RXLoad::setParameters(Matrix activePower, Matrix reactivePower,
Real volt) {
void EMT::Ph3::RXLoad::setParameters(Matrix activePower, Matrix reactivePower, Real volt, bool reactanceInSeries) {
**mActivePower = activePower;
**mReactivePower = reactivePower;
mReactanceInSeries = reactanceInSeries;

if (mReactanceInSeries) {
setVirtualNodeNumber(1);
}
// complex power
mPower = MatrixComp::Zero(3, 3);
mPower(0, 0) = {(**mActivePower)(0, 0), (**mReactivePower)(0, 0)};
Expand Down Expand Up @@ -99,59 +100,94 @@ void EMT::Ph3::RXLoad::initializeFromNodesAndTerminals(Real frequency) {
SPDLOG_LOGGER_INFO(mSLog, "Nominal Voltage={} [V]", **mNomVoltage);
}

MatrixComp vInitABC = MatrixComp::Zero(3, 1);
vInitABC(0, 0) = RMS3PH_TO_PEAK1PH * mTerminals[0]->initialSingleVoltage();
vInitABC(1, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_B;
vInitABC(2, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_C;
**mIntfVoltage = vInitABC.real();

if ((**mActivePower)(0, 0) != 0) {
mResistance =
std::pow(**mNomVoltage / sqrt(3), 2) * (**mActivePower).inverse();
mSubResistor =
std::make_shared<EMT::Ph3::Resistor>(**mName + "_res", mLogLevel);
mSubResistor->setParameters(mResistance);
mSubResistor->connect({SimNode::GND, mTerminals[0]->node()});
mSubResistor->initialize(mFrequencies);
mSubResistor->initializeFromNodesAndTerminals(frequency);
addMNASubComponent(mSubResistor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
**mIntfCurrent += mSubResistor->intfCurrent();
mResistance = std::pow(**mNomVoltage / sqrt(3), 2) * (**mActivePower).inverse();
}

if ((**mReactivePower)(0, 0) != 0)
mReactance =
std::pow(**mNomVoltage / sqrt(3), 2) * (**mReactivePower).inverse();
mReactance = std::pow(**mNomVoltage / sqrt(3), 2) * (**mReactivePower).inverse();
else
mReactance = Matrix::Zero(1, 1);

if (mReactanceInSeries) {
MatrixComp impedance = MatrixComp::Zero(3, 3);
impedance << Complex(mResistance(0, 0), mReactance(0, 0)), Complex(mResistance(0, 1), mReactance(0, 1)), Complex(mResistance(0, 2), mReactance(0, 2)), Complex(mResistance(1, 0), mReactance(1, 0)),
Complex(mResistance(1, 1), mReactance(1, 1)), Complex(mResistance(1, 2), mReactance(1, 2)), Complex(mResistance(2, 0), mReactance(2, 0)), Complex(mResistance(2, 1), mReactance(2, 1)),
Complex(mResistance(2, 2), mReactance(2, 2));
**mIntfCurrent = (impedance.inverse() * vInitABC).real();

// Initialization of virtual node
// Initial voltage of phase B,C is set after A
MatrixComp vInitTerm0 = MatrixComp::Zero(3, 1);
vInitTerm0(0, 0) = initialSingleVoltage(0);
vInitTerm0(1, 0) = vInitTerm0(0, 0) * SHIFT_TO_PHASE_B;
vInitTerm0(2, 0) = vInitTerm0(0, 0) * SHIFT_TO_PHASE_C;
mVirtualNodes[0]->setInitialVoltage(vInitTerm0 + mResistance * **mIntfCurrent);
}

if ((**mActivePower)(0, 0) != 0) {
mSubResistor = std::make_shared<EMT::Ph3::Resistor>(**mName + "_res", mLogLevel);
mSubResistor->setParameters(mResistance);
if (mReactanceInSeries) {
mSubResistor->connect({mTerminals[0]->node(), mVirtualNodes[0]});
} else {
mSubResistor->connect({SimNode::GND, mTerminals[0]->node()});
}
mSubResistor->initialize(mFrequencies);
mSubResistor->initializeFromNodesAndTerminals(frequency);
addMNASubComponent(mSubResistor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);

if (!mReactanceInSeries) {
**mIntfCurrent += mSubResistor->intfCurrent();
}
}

if (mReactance(0, 0) > 0) {
mInductance = mReactance / (2 * PI * frequency);

mSubInductor =
std::make_shared<EMT::Ph3::Inductor>(**mName + "_ind", mLogLevel);
mSubInductor->setParameters(mInductance);
mSubInductor->connect({SimNode::GND, mTerminals[0]->node()});
if (mReactanceInSeries) {
mSubInductor->connect({SimNode::GND, mVirtualNodes[0]});
} else {
mSubInductor->connect({SimNode::GND, mTerminals[0]->node()});
}
mSubInductor->initialize(mFrequencies);
mSubInductor->initializeFromNodesAndTerminals(frequency);
addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
**mIntfCurrent += mSubInductor->intfCurrent();

if (!mReactanceInSeries) {
**mIntfCurrent += mSubInductor->intfCurrent();
}
} else if (mReactance(0, 0) < 0) {
mCapacitance = -1 / (2 * PI * frequency) * mReactance.inverse();

mSubCapacitor =
std::make_shared<EMT::Ph3::Capacitor>(**mName + "_cap", mLogLevel);
mSubCapacitor->setParameters(mCapacitance);
mSubCapacitor->connect({SimNode::GND, mTerminals[0]->node()});
if (mReactanceInSeries) {
mSubCapacitor->connect({SimNode::GND, mVirtualNodes[0]});
} else {
mSubCapacitor->connect({SimNode::GND, mTerminals[0]->node()});
}
mSubCapacitor->initialize(mFrequencies);
mSubCapacitor->initializeFromNodesAndTerminals(frequency);
addMNASubComponent(mSubCapacitor,
MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
**mIntfCurrent += mSubCapacitor->intfCurrent();
if (!mReactanceInSeries) {
**mIntfCurrent += mSubCapacitor->intfCurrent();
}
}

MatrixComp vInitABC = MatrixComp::Zero(3, 1);
vInitABC(0, 0) = RMS3PH_TO_PEAK1PH * mTerminals[0]->initialSingleVoltage();
vInitABC(1, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_B;
vInitABC(2, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_C;
**mIntfVoltage = vInitABC.real();

SPDLOG_LOGGER_INFO(
mSLog,
"\n--- Initialization from powerflow ---"
Expand Down Expand Up @@ -210,8 +246,12 @@ void EMT::Ph3::RXLoad::mnaCompUpdateVoltage(const Matrix &leftVector) {
}

void EMT::Ph3::RXLoad::mnaCompUpdateCurrent(const Matrix &leftVector) {
**mIntfCurrent = Matrix::Zero(3, 1);
for (auto &subc : mSubComponents) {
**mIntfCurrent += subc->intfCurrent();
if (mReactanceInSeries) {
**mIntfCurrent = mSubInductor->intfCurrent();
} else {
**mIntfCurrent = Matrix::Zero(3, 1);
for (auto &subc : mSubComponents) {
**mIntfCurrent += subc->intfCurrent();
}
}
}
1 change: 1 addition & 0 deletions dpsim-villas/examples/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(SHMEM_SOURCES
FileExample.cpp
MqttExample.cpp
FpgaExample.cpp
FpgaCosimulation.cpp
SharedMemExample.cpp
#ShmemExample.cpp
#ShmemDistributedReference.cpp
Expand Down
Loading

0 comments on commit b6143b0

Please sign in to comment.