Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voltage Profile for NetworkInjection with CSVreader #124

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.11...3.23)
set(CMAKE_SYSTEM_VERSION "10.0")
project(DPsim
VERSION 1.1.0
DESCRIPTION "C++ Power System Simulation Library"
Expand Down
14 changes: 14 additions & 0 deletions dpsim-models/include/dpsim-models/CSVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <dpsim-models/SystemTopology.h>
#include <dpsim-models/SP/SP_Ph1_Load.h>
#include <dpsim-models/DP/DP_Ph1_PQLoadCS.h>
#include <dpsim-models/DP/DP_Ph1_NetworkInjection.h>
#include <dpsim-models/DP/DP_Ph1_AvVoltageSourceInverterDQ.h>
#include <dpsim-models/SP/SP_Ph1_AvVoltageSourceInverterDQ.h>

Expand Down Expand Up @@ -97,6 +98,11 @@ namespace CPS {
std::vector<Real> readPQData (fs::path file,
Real start_time = -1, Real time_step = 1, Real end_time = -1,
CSVReader::DataFormat format = CSVReader::DataFormat::SECONDS);
///
VoltageProfile readVoltageProfile(fs::path file,
Real start_time = -1, Real time_step = 1, Real end_time = -1,
CSVReader::DataFormat format = CSVReader::DataFormat::SECONDS);

/// assign load profile to corresponding load object
void assignLoadProfile(SystemTopology& sys,
Real start_time = -1, Real time_step = 1, Real end_time = -1,
Expand All @@ -106,10 +112,18 @@ namespace CPS {
void assignPVGeneration(SystemTopology& sys,
Real start_time = -1, Real time_step = 1, Real end_time = -1,
CSVReader::Mode mode = CSVReader::Mode::AUTO);
/// assign voltage source profile to corresponding source object
void assignSourceProfile(std::vector<std::shared_ptr<CPS::DP::Ph1::NetworkInjection>>& sources,
Real start_time = -1, Real time_step = 1, Real end_time = -1,
CSVReader::Mode mode = CSVReader::Mode::AUTO,
CSVReader::DataFormat format = CSVReader::DataFormat::SECONDS);

/// interpolation for PQ data points
PQData interpol_linear(std::map<Real, PQData>& data_PQ, Real x);

/// interpolation for Voltage data points
VData interpol_linear(std::map<Real, VData>& vData, Real x);

/// interpolation for weighting factor data points
Real interpol_linear(std::map<Real, Real>& data_wf, Real x);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <dpsim-models/Solver/MNAInterface.h>
#include <dpsim-models/Solver/DAEInterface.h>
#include <dpsim-models/DP/DP_Ph1_VoltageSource.h>
#include <dpsim-models/PowerProfile.h>

namespace CPS {
namespace DP {
Expand All @@ -38,6 +39,12 @@ namespace Ph1 {
std::vector<const Matrix*> mRightVectorStamps;

public:
// ### Source profile as time series ###
/// Source profile data
VoltageProfile mSourceProfile;
/// Use the assigned load profile
bool use_profile = false;

const Attribute<Complex>::Ptr mVoltageRef;
const Attribute<Real>::Ptr mSrcFreq;

Expand Down Expand Up @@ -78,7 +85,7 @@ namespace Ph1 {
/// Returns current through the component
void mnaUpdateCurrent(const Matrix& leftVector);
/// Updates voltage across component
void mnaUpdateVoltage(const Matrix& leftVector);
void mnaUpdateVoltage(Real time, const Matrix& leftVector);
/// MNA pre step operations
void mnaPreStep(Real time, Int timeStepCount);
/// MNA post step operations
Expand Down
9 changes: 9 additions & 0 deletions dpsim-models/include/dpsim-models/PowerProfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ namespace CPS {
std::map<Real, PQData> pqData;
std::map<Real, Real> weightingFactors;
};

struct VData {
Complex v;
};

struct VoltageProfile {
std::map<Real, VData> vData;
std::map<Real, Real> weightingFactors;
};
}
163 changes: 162 additions & 1 deletion dpsim-models/src/CSVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


#include <dpsim-models/CSVReader.h>
#include <cctype> // we'll use the non-locale <cctype>'s std::isdigit

using namespace CPS;

Expand Down Expand Up @@ -137,7 +138,6 @@ std::vector<PQData> CSVReader::readLoadProfileDP(fs::path file,
std::ifstream csvfile(file);
CSVReaderIterator row_(csvfile);


// ignore the first row if it is a title
if (mSkipFirstRow && !std::isdigit((*row_).get(0)[0])) {
row_.next();
Expand Down Expand Up @@ -535,6 +535,167 @@ void CSVReader::assignLoadProfile(CPS::SystemTopology& sys, Real start_time, Rea
}
}

VoltageProfile CSVReader::readVoltageProfile(std::experimental::filesystem::path file,
Real start_time, Real time_step, Real end_time, CSVReader::DataFormat format) {

VoltageProfile source_profile;
std::ifstream csvfile(file);
bool need_that_conversion = (format == DataFormat::HHMMSS) ? true : false;
bool data_with_weighting_factor = false;

CSVReaderIterator loop(csvfile);

// ignore the first row if it is a title
if (!std::isdigit((*loop).get(0)[0])) {
loop.next();
}
/*
loop over rows of the csv file to find the entry point to read in.
and determine data type prior to read in. (assuming only time,p,q or time,weighting factor)
if start_time and end_time are negative (as default), it reads in all rows.
*/
for (; loop != CSVReaderIterator(); loop.next()) {
CSVReaderIterator nextRow = loop;
nextRow.next();
CPS::Real nextTime = (need_that_conversion) ? time_format_convert((*nextRow).get(0)) : std::stod((*nextRow).get(0));
if ((*nextRow).size() == 2) {
data_with_weighting_factor = true;
}
if ((start_time < 0) | (nextTime >= Int(start_time)))
{
break;
}
}
/*
reading data after entry point until end_time is reached
*/
for (; loop != CSVReaderIterator(); loop.next()) {
CPS::Real currentTime = (need_that_conversion) ? time_format_convert((*loop).get(0)) : std::stod((*loop).get(0));
if (data_with_weighting_factor) {
Real wf = std::stod((*loop).get(1));
source_profile.weightingFactors.insert(std::pair<Real, Real>(currentTime, wf));
}
else {
VData v;

v.v.real(std::stod((*loop).get(1)));
v.v.imag(std::stod((*loop).get(2)));
source_profile.vData.insert(std::pair<Real, VData>(currentTime, v));
}

if (end_time > 0 && currentTime > end_time)
break;
}
std::vector<CPS::Real> times;
for (CPS::Real x_ = start_time; x_ <= end_time; x_ += time_step) {
times.push_back(x_);
}

for (auto x : times) {
if (source_profile.vData.find(x) == source_profile.vData.end()) {
if (data_with_weighting_factor) {
Real y = interpol_linear(source_profile.weightingFactors, x);
source_profile.weightingFactors.insert(std::pair<Real, Real>(x, y));
}
else {
VData y = interpol_linear(source_profile.vData, x);
source_profile.vData.insert(std::pair<Real, VData>(x, y));
}
}
}

return source_profile;

}


void CSVReader::assignSourceProfile(std::vector<std::shared_ptr<CPS::DP::Ph1::NetworkInjection>>& sources,
Real start_time, Real time_step, Real end_time,
CSVReader::Mode mode, CSVReader::DataFormat format) {

switch (mode) {
case CSVReader::Mode::AUTO: {
mSLog->info("Assigning source profiles");
for (auto source : sources) {
String source_name = source->name();
String source_voltage = source->attribute("V_ref")->toString();
for (auto file : mFileList) {
String file_name = file.filename().string();
/// changing file name and load name to upper case for later matching
for (auto & c : source_name) c = toupper(c);
for (auto & c : file_name) c = toupper(c);
/// strip off all non-alphanumeric characters
source_name.erase(remove_if(source_name.begin(), source_name.end(), [](char c) { return !isalnum(c); }), source_name.end());
file_name.erase(remove_if(file_name.begin(), file_name.end(), [](char c) { return !isalnum(c); }), file_name.end());
if (file_name == (source_name + "CSV")) {
source->mSourceProfile = readVoltageProfile(file, start_time, time_step, end_time, format);
source->use_profile = true;
mSLog->info("Assigned {} to {}", file.filename().string(), source->name());
mSLog->info("Initial value = {}", source->mSourceProfile.vData.at(0).v);
mSLog->info("Format = {}", format);
}
}

}

break;
}
case CSVReader::Mode::MANUAL: {
Int LP_assigned_counter = 0;
Int LP_not_assigned_counter = 0;
mSLog->info("Assigning source profiles with user defined pattern ...");
for (auto source : sources) {
std::map<String, String>::iterator file = mAssignPattern.find(source->name());
if (file == mAssignPattern.end()) {

mSLog->info("{} has no profile given.", source->name());
LP_not_assigned_counter++;
continue;
}
for (auto path : mFileList) {
if (path.string().find(file->second) != std::string::npos) {
source->mSourceProfile = readVoltageProfile(path, start_time, time_step, end_time);
source->use_profile = true;
mSLog->info("Assigned {}.csv to {}", file->second, source->name());
LP_assigned_counter++;
}

}
}
mSLog->info("Assigned profiles for {} sources, {} not assigned.", LP_assigned_counter, LP_not_assigned_counter);
break;
}
default: {
throw std::invalid_argument(
"Source profile assign mode error");
break;
}
}
}


CPS::VData CSVReader::interpol_linear(std::map<CPS::Real, CPS::VData>& vData, CPS::Real x) {
std::map <Real, VData>::const_iterator entry = vData.upper_bound(x);
VData y;

if (entry == vData.end()) {
return (--entry)->second;
}
if (entry == vData.begin()) {
return entry->second;
}
std::map <Real, VData>::const_iterator prev = entry;
--prev;

const CPS::Real delta = (x - prev->first) / (entry->first - prev->first);

y.v.real(delta * entry->second.v.real() + (1 - delta)*prev->second.v.real());
y.v.imag(delta * entry->second.v.imag() + (1 - delta)*prev->second.v.imag());
return y;
}



CPS::PQData CSVReader::interpol_linear(std::map<CPS::Real, CPS::PQData>& pqData, CPS::Real x) {
std::map <Real, PQData>::const_iterator entry = pqData.upper_bound(x);
PQData y;
Expand Down
11 changes: 9 additions & 2 deletions dpsim-models/src/DP/DP_Ph1_NetworkInjection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,17 @@ void DP::Ph1::NetworkInjection::mnaPostStep(Real time, Int timeStepCount, Attrib
mnasubcomp->mnaPostStep(time, timeStepCount, leftVector);
// post-step of component itself
mnaUpdateCurrent(**leftVector);
mnaUpdateVoltage(**leftVector);
mnaUpdateVoltage(time, **leftVector);
}

void DP::Ph1::NetworkInjection::mnaUpdateVoltage(const Matrix& leftVector) {
void DP::Ph1::NetworkInjection::mnaUpdateVoltage(Real time, const Matrix& leftVector) {
if (mSourceProfile.vData.size() > 0) {
std::map<Real, VData>::iterator it;
it = mSourceProfile.vData.lower_bound(time);
if (it != mSourceProfile.vData.end()) {
mSubVoltageSource->setParameters(it->second.v);
}
}
**mIntfVoltage = **mSubVoltageSource->mIntfVoltage;
}

Expand Down
1 change: 1 addition & 0 deletions dpsim/examples/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(CIRCUIT_SOURCES
Circuits/DP_Circuits.cpp
Circuits/DP_Basics_DP_Sims.cpp
Circuits/DP_PiLine.cpp
Circuits/DP_PiLine_SourceProfile.cpp
Circuits/DP_DecouplingLine.cpp
Circuits/DP_Diakoptics.cpp
Circuits/DP_VSI.cpp
Expand Down
83 changes: 83 additions & 0 deletions dpsim/examples/cxx/Circuits/DP_PiLine_SourceProfile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* 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.h>
#include <dpsim-models/CSVReader.h>

using namespace DPsim;
using namespace CPS::DP;

void simPiLineSource() {

#ifdef _WIN32
String loadProfilePath("build\\_deps\\profile-data-src\\NetworkInjektion_Voltage\\");
#elif defined(__linux__) || defined(__APPLE__)
String loadProfilePath("build/_deps/profile-data-src/NetworkInjektion_Voltage/");
#endif

Real timeStep = 0.00005;
Real finalTime = 1;
String simName = "DP_PiLine_Source";
Logger::setLogDir("logs/"+simName);

// read csv
String ni_name = "vs_test";
CPS::CSVReader csvreader(ni_name + ".csv", loadProfilePath, Logger::Level::debug);

// Nodes
auto n1 = SimNode::make("n1");
auto n2 = SimNode::make("n2");

// Components
auto vs = Ph1::NetworkInjection::make(ni_name);
vs->setParameters(CPS::Math::polar(100000, 0));

// Parametrization of components
Real resistance = 5;
Real inductance = 0.16;
Real capacitance = 1.0e-6;
Real conductance = 1e-6;

auto line = Ph1::PiLine::make("Line");
line->setParameters(resistance, inductance, capacitance, conductance);

auto load = Ph1::Resistor::make("R_load");
load->setParameters(10000);

// Topology
vs->connect({ n1 });
line->connect({ n1, n2 });
load->connect({ n2, SimNode::GND });

auto sys = SystemTopology(50,
SystemNodeList{n1, n2},
SystemComponentList{vs, line, load});

// Logging
auto logger = DataLogger::make(simName);
logger->logAttribute("v1", n1->attribute("v"));
logger->logAttribute("v2", n2->attribute("v"));
logger->logAttribute("iline", line->attribute("i_intf"));

Simulation sim(simName);
sim.setSystem(sys);
sim.setTimeStep(timeStep);
sim.setFinalTime(finalTime);
sim.addLogger(logger);

std::vector<std::shared_ptr<CPS::DP::Ph1::NetworkInjection>> networkinjections;
networkinjections.push_back(vs);
csvreader.assignSourceProfile(networkinjections, sim.time(), sim.timeStep(), sim.finalTime(), CPS::CSVReader::Mode::AUTO);

sim.run();
}


int main(int argc, char* argv[]) {
simPiLineSource();
}