Skip to content

Commit

Permalink
VILLASfpga cosimulation development (#325)
Browse files Browse the repository at this point in the history
- add a real time data logger
This writes simulation results to memory and only dumps to a file when
the simulation ends. It allocates a buffer that is attributes*simulation
duration/time step long - so this only works for relatively short
simulations.
- adds `EMT::Ph3::ControlledVoltageSource` and
`EMT::Ph3::ControlledCurrentSource`. The existing sources
`EMT::Ph3::VoltageSource` and `EMT::Ph3::CurrentSource` only allow
setting RMS voltages and derives the individual phase voltages from
that. This cannot be used for PHIL or Co-Simulations where we need the
set each phase voltage individually. The existing voltage source can
also not be easily adapted to support both, so I created a new model
(RTDS and Simulink also have a separate model for this so it makes
sense).
- modifies `EMT::Ph3::RXLoad` to be able to implement a series R-L load
instead of only a parallel R-L load. The series load is needed for the
WSCC 9 bus model.
- adds `FPGACosimulation`, an example of using dpsim-villas to build a
co-simulation where DPsim simulates load connected to bus 5 in a WSCC 9
bus model.
- add `FPGACosim3PhInfiniteBus`, an example that implements an infinite
bus to be connected with a load in another simulator.
- make logging step times optional, as this slows down the simulation
quite a bit when running in real time.
- None of the changes should break existing code, because everything is
again optional and the default behavior is the old behavior.
  • Loading branch information
m-mirz authored Jan 22, 2025
2 parents 4a511b7 + f6054a9 commit c070bc7
Show file tree
Hide file tree
Showing 38 changed files with 1,617 additions and 252 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_test_linux_fedora.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ jobs:
continue-on-error: true

- name: Archive cppcheck output
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ always() && steps.cppcheck.outcome == 'failure' }}
with:
name: cppcheck-output
Expand Down
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ endif()
include(CheckSymbolExists)
check_symbol_exists(timerfd_create sys/timerfd.h HAVE_TIMERFD)
check_symbol_exists(getopt_long getopt.h HAVE_GETOPT)
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
add_compile_options(-flto -march=native -Ofast)
endif()

# Get version info and buildid from Git
include(GetVersion)
Expand Down Expand Up @@ -168,6 +171,7 @@ endif()

if(WITH_PROFILING)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
list(APPEND DPSIM_CXX_FLAGS "-pg")
endif()

if(WITH_ASAN)
Expand Down
3 changes: 1 addition & 2 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@
/dpsim-models/src/SP/SP_Ph1_SynchronGenerator6bOrderVBR.cpp @martinmoraga
/dpsim-models/src/SP/SP_Ph1_SynchronGeneratorTrStab.cpp @gnakti
/dpsim-models/src/SP/SP_Ph1_varResSwitch.cpp @gnakti
/dpsim-villas/ @stv0g
/dpsim-villas/examples/cxx/FpgaExample.cpp @n-eiling
/dpsim-villas/ @n-eiling @stv0g
/dpsim/examples/cxx/CIM @gnakti
/dpsim/examples/cxx/Circuits @gnakti
/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp @martinmoraga
Expand Down
7 changes: 4 additions & 3 deletions dpsim-models/include/dpsim-models/Components.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,14 @@
#include <dpsim-models/EMT/EMT_Ph3_AvVoltSourceInverterStateSpace.h>
#include <dpsim-models/EMT/EMT_Ph3_AvVoltageSourceInverterDQ.h>
#include <dpsim-models/EMT/EMT_Ph3_Capacitor.h>
#include <dpsim-models/EMT/EMT_Ph3_ControlledVoltageSource.h>
#include <dpsim-models/EMT/EMT_Ph3_CurrentSource.h>
#include <dpsim-models/EMT/EMT_Ph3_Inductor.h>
#include <dpsim-models/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h>
#include <dpsim-models/EMT/EMT_Ph3_Resistor.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Capacitor.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Full_Serial_RLC.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Inductor.h>
#include <dpsim-models/EMT/EMT_Ph3_SeriesResistor.h>
#include <dpsim-models/EMT/EMT_Ph3_SeriesSwitch.h>
#include <dpsim-models/EMT/EMT_Ph3_SynchronGenerator3OrderVBR.h>
Expand All @@ -108,9 +112,6 @@
#include <dpsim-models/EMT/EMT_Ph3_SynchronGeneratorDQTrapez.h>
#include <dpsim-models/EMT/EMT_Ph3_VoltageSource.h>
#include <dpsim-models/EMT/EMT_Ph3_VoltageSourceNorton.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Capacitor.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Inductor.h>
#include <dpsim-models/EMT/EMT_Ph3_SSN_Full_Serial_RLC.h>
#ifdef WITH_SUNDIALS
#include <dpsim-models/EMT/EMT_Ph3_SynchronGeneratorDQODE.h>
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Copyright 2017-2020 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 Ideal current source model
///
/// This model uses modified nodal analysis to represent an ideal current source.
/// This involves the stamping of the current to the right side vector.
class ControlledCurrentSource : public MNASimPowerComp<Real>, public SharedFactory<ControlledCurrentSource> {
protected:
// Updates current according to reference phasor and frequency
void updateCurrent(Real time);

public:
const Attribute<Matrix>::Ptr mCurrentRef;

/// Defines UID, name and logging level
ControlledCurrentSource(String uid, String name, Logger::Level logLevel = Logger::Level::off);
///
ControlledCurrentSource(String name, Logger::Level logLevel = Logger::Level::off) : ControlledCurrentSource(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 current
void setParameters(Matrix currentRef);
// #### MNA section ####
/// Initializes internal variables of the component
void mnaCompInitialize(Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) override;
/// Stamps right side (source) vector
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override;
/// Returns voltage through the component
void mnaCompUpdateVoltage(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
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
5 changes: 4 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 @@ -57,8 +59,9 @@ class RXLoad : public CompositePowerComp<Real>, public SharedFactory<RXLoad> {
Logger::Level logLevel = Logger::Level::off);

// #### General ####
virtual String description() override { return fmt::format("Active: {}MW, Reactive: {}MVAr, Voltage: {}kV", (**mActivePower)(0, 0) / 1e6, (**mReactivePower)(0, 0) / 1e6, (**mNomVoltage) / 1e3); };
///
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
2 changes: 2 additions & 0 deletions dpsim-models/include/dpsim-models/IdentifiedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,7 @@ class IdentifiedObject {
String uid() { return **mUID; }
/// Get component type (cross-platform)
String type() { return Utils::className(this); }
// Returns a description of the object
virtual String description() { return ""; }
};
} // namespace CPS
4 changes: 4 additions & 0 deletions dpsim-models/src/CIM/Reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,10 @@ void Reader::processTopologicalNode(CIMPP::TopologicalNode *topNode) {
}

auto pfEquipment = mPowerflowEquipment.at(equipment->mRID);
if (pfEquipment == nullptr) {
SPDLOG_LOGGER_ERROR(mSLog, "Equipment {} is null in equipment list", equipment->mRID);
throw SystemError("Equipment is null in equipment list.");
}
std::dynamic_pointer_cast<SimPowerComp<VarType>>(pfEquipment)
->setTerminalAt(std::dynamic_pointer_cast<SimTerminal<VarType>>(
mPowerflowTerminals[term->mRID]),
Expand Down
2 changes: 2 additions & 0 deletions dpsim-models/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ list(APPEND MODELS_SOURCES

EMT/EMT_Ph3_CurrentSource.cpp
EMT/EMT_Ph3_VoltageSource.cpp
EMT/EMT_Ph3_ControlledCurrentSource.cpp
EMT/EMT_Ph3_ControlledVoltageSource.cpp
EMT/EMT_Ph3_Inductor.cpp
EMT/EMT_Ph3_Capacitor.cpp
EMT/EMT_Ph3_AvVoltageSourceInverterDQ.cpp
Expand Down
111 changes: 111 additions & 0 deletions dpsim-models/src/EMT/EMT_Ph3_ControlledCurrentSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* Copyright 2017-2020 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_ControlledCurrentSource.h>

using namespace CPS;

EMT::Ph3::ControlledCurrentSource::ControlledCurrentSource(String uid, String name, Logger::Level logLevel)
: MNASimPowerComp<Real>(uid, name, true, true, logLevel), mCurrentRef(mAttributes->create<Matrix>("I_ref")) {
mPhaseType = PhaseType::ABC;
setVirtualNodeNumber(0);
setTerminalNumber(2);
**mIntfVoltage = Matrix::Zero(3, 1);
**mIntfCurrent = Matrix::Zero(3, 1);
}
SimPowerComp<Real>::Ptr EMT::Ph3::ControlledCurrentSource::clone(String name) {
auto copy = ControlledCurrentSource::make(name, mLogLevel);
copy->setParameters(attributeTyped<Matrix>("I_ref")->get());
return copy;
}

void EMT::Ph3::ControlledCurrentSource::setParameters(Matrix currentRef) {
**mCurrentRef = currentRef;

mSLog->info("\nCurrent reference phasor [I]: {:s}", Logger::matrixCompToString(currentRef));

mParametersSet = true;
}

void EMT::Ph3::ControlledCurrentSource::initializeFromNodesAndTerminals(Real frequency) {
SPDLOG_LOGGER_INFO(mSLog, "\n--- Initialization from node voltages and terminal ---");
if (!mParametersSet) {
**mCurrentRef = Matrix::Zero(3, 1);

SPDLOG_LOGGER_INFO(mSLog,
"\nTerminal 0 voltage: {:s}"
"\nTerminal 1 voltage: {:s}"
"\nTerminal 0 power: {:s}"
"\nTerminal 1 power: {:s}",
Logger::phasorToString(initialSingleVoltage(0)), Logger::phasorToString(initialSingleVoltage(1)), Logger::complexToString(terminal(0)->singlePower()),
Logger::complexToString(terminal(1)->singlePower()));
} else {
SPDLOG_LOGGER_INFO(mSLog,
"\nInitialization from node voltages and terminal omitted (parameter "
"already set)."
"\nReference voltage: {:s}",
Logger::matrixToString(attributeTyped<Matrix>("I_ref")->get()));
}
SPDLOG_LOGGER_INFO(mSLog, "\n--- Initialization from node voltages and terminal ---");
mSLog->flush();
}

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

void EMT::Ph3::ControlledCurrentSource::mnaCompApplyRightSideVectorStamp(Matrix &rightVector) {
if (terminalNotGrounded(1)) {
Math::setVectorElement(rightVector, matrixNodeIndex(1, 0), -(**mIntfCurrent)(0, 0));
Math::setVectorElement(rightVector, matrixNodeIndex(1, 1), -(**mIntfCurrent)(1, 0));
Math::setVectorElement(rightVector, matrixNodeIndex(1, 2), -(**mIntfCurrent)(2, 0));
}
if (terminalNotGrounded(0)) {
Math::setVectorElement(rightVector, matrixNodeIndex(0, 0), (**mIntfCurrent)(0, 0));
Math::setVectorElement(rightVector, matrixNodeIndex(0, 1), (**mIntfCurrent)(1, 0));
Math::setVectorElement(rightVector, matrixNodeIndex(0, 2), (**mIntfCurrent)(2, 0));
}
}

void EMT::Ph3::ControlledCurrentSource::updateCurrent(Real time) {
**mIntfCurrent = **mCurrentRef;

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

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

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

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

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

void EMT::Ph3::ControlledCurrentSource::mnaCompUpdateVoltage(const Matrix &leftVector) {
// v1 - v0
**mIntfVoltage = Matrix::Zero(3, 1);
if (terminalNotGrounded(1)) {
(**mIntfVoltage)(0, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(1, 0));
(**mIntfVoltage)(1, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(1, 1));
(**mIntfVoltage)(2, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(1, 2));
}
if (terminalNotGrounded(0)) {
(**mIntfVoltage)(0, 0) = (**mIntfVoltage)(0, 0) - Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0));
(**mIntfVoltage)(1, 0) = (**mIntfVoltage)(1, 0) - Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1));
(**mIntfVoltage)(2, 0) = (**mIntfVoltage)(2, 0) - Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2));
}
}
Loading

0 comments on commit c070bc7

Please sign in to comment.