DPsim
Loading...
Searching...
No Matches
CSVReader.cpp
1/* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2 * EONERC, RWTH Aachen University
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 *********************************************************************************/
8
9#include <dpsim-models/CSVReader.h>
10
11using namespace CPS;
12
13MatrixRow CSVReader::csv2Eigen(const String &path) {
14 std::ifstream inputFile;
15 inputFile.open(path);
16 String line;
17 std::vector<double> values;
18 UInt rows = 0;
19 while (std::getline(inputFile, line)) {
20 std::stringstream lineStream(line);
21 String cell;
22 while (std::getline(lineStream, cell, ',')) {
23 values.push_back(std::stod(cell));
24 }
25 ++rows;
26 }
27 UInt columns = values.size() / rows;
28 return Eigen::Map<const MatrixRow>(values.data(), rows, columns);
29}
30
31void CSVRow::readNextRow(std::istream &str) {
32 std::string line;
33 std::getline(str, line);
34
35 std::stringstream lineStream(line);
36 std::string cell;
37
38 m_data.clear();
39 while (std::getline(lineStream >> std::ws, cell, ',')) {
40 m_data.push_back(cell);
41 }
42 // This checks for a trailing comma with no data after it.
43 if (!lineStream && cell.empty()) {
44 // If there was a trailing comma then add an empty element.
45 m_data.push_back("");
46 }
47}
48
49int CSVRow::size() const { return m_data.size(); }
50
51CSVReaderIterator &CSVReaderIterator::next() {
52 if (m_str) {
53 m_row.readNextRow(*m_str);
54 if (!(*m_str)) {
55 m_str = NULL;
56 }
57 }
58 return *this;
59}
60
61CSVReaderIterator CSVReaderIterator::next(int) {
62 CSVReaderIterator tmp(*this);
63 this->next();
64 return tmp;
65}
66
67CSVReaderIterator &CSVReaderIterator::step(int time_step) {
68 while (time_step != 0) {
69 if (m_str) {
70 m_row.readNextRow(*m_str);
71 if (!(*m_str)) {
72 m_str = NULL;
73 }
74 }
75 time_step -= 1;
76 }
77 return *this;
78}
79
80CSVReaderIterator::CSVReaderIterator(std::istream &str)
81 : m_str(str.good() ? &str : NULL) {
82 this->next();
83}
84
85CSVReaderIterator::CSVReaderIterator() : m_str(NULL) {}
86
87CSVReader::CSVReader(CPS::String name, std::list<fs::path> paths,
88 CPS::Logger::Level logLevel) {
89 mSLog = Logger::get(name + "_csvReader", logLevel);
90 //mFileList = paths;
91 for (auto file : paths) {
92 if (file.string().find(".csv") != std::string::npos) {
93 mFileList.push_back(file);
94 std::cout << "add " << file << std::endl;
95 }
96 }
97}
98
99CSVReader::CSVReader(CPS::String name, CPS::String path,
100 CPS::Logger::Level logLevel) {
101 mSLog = Logger::get(name + "_csvReader", logLevel);
102
103 mPath = path;
104 for (const auto &entry : fs::directory_iterator(path)) {
105 mFileList.push_back(entry.path());
106 }
107}
108
109CSVReader::CSVReader(CPS::String name, CPS::String path,
110 std::map<String, String> &assignList,
111 CPS::Logger::Level logLevel)
112 : CSVReader(name, path, logLevel) {
113
114 mAssignPattern = assignList;
115}
116
117CSVReader::CSVReader(CPS::String name, std::list<fs::path> paths,
118 std::map<String, String> &assignList,
119 CPS::Logger::Level logLevel)
120 : CSVReader(name, paths, logLevel) {
121 mAssignPattern = assignList;
122}
123
124CPS::Real CSVReader::time_format_convert(const CPS::String &time) {
125 int hh, mm, ss = 0;
126 CPS::Real secs = 0;
127 if (sscanf(time.c_str(), "%d:%d:%d", &hh, &mm, &ss) >= 2) {
128 secs = hh * 3600 + mm * 60 + ss;
129 }
130 return secs;
131}
132
133std::vector<PQData> CSVReader::readLoadProfileDP(fs::path file, Real start_time,
134 Real time_step, Real end_time,
135 Real scale_factor,
136 CSVReader::DataFormat format) {
137
138 std::vector<PQData> load_profileDP;
139 std::ifstream csvfile(file);
140 CSVReaderIterator row_(csvfile);
141
142 // Ignore the first row if it is a title
143 if (mSkipFirstRow && !std::isdigit((*row_).get(0)[0])) {
144 row_.next();
145 }
146
147 /* Loop over rows of the csv file to find the entry point to read in.
148 * if start_time and end_time are negative (as default), it reads in all rows.
149 */
150 Real presentTime = 0;
151 for (; row_ != CSVReaderIterator(); row_.next()) {
152 if ((start_time < 0) | (Int(presentTime) + 1 > Int(start_time)))
153 break;
154 presentTime++;
155 }
156 // Reading data after entry point until end_time is reached.
157 for (; row_ != CSVReaderIterator(); row_.next()) {
158 // IMPORTANT: take care of units. assume kW
159 PQData pq;
160 // multiplied by 1000 due to unit conversion (kw to w)
161 pq.p = std::stod((*row_).get(1)) * 1000 * scale_factor;
162 pq.q = std::stod((*row_).get(2)) * 1000 * scale_factor;
163 load_profileDP.push_back(pq);
164 if (end_time > 0 && presentTime > end_time)
165 break;
166 presentTime = Int(presentTime) + 1;
167 }
168 std::cout << "CSV loaded." << std::endl;
169 return load_profileDP;
170}
171
172PowerProfile CSVReader::readLoadProfile(fs::path file, Real start_time,
173 Real time_step, Real end_time,
174 CSVReader::DataFormat format) {
175
176 PowerProfile load_profile;
177 std::ifstream csvfile(file);
178 bool need_that_conversion = (format == DataFormat::HHMMSS) ? true : false;
179 bool data_with_weighting_factor = false;
180
181 CSVReaderIterator loop(csvfile);
182
183 // ignore the first row if it is a title
184 if (mSkipFirstRow && !std::isdigit((*loop).get(0)[0])) {
185 loop.next();
186 }
187
188 /*
189 loop over rows of the csv file to find the entry point to read in.
190 and determine data type prior to read in. (assuming only time,p,q or time,weighting factor)
191 if start_time and end_time are negative (as default), it reads in all rows.
192 */
193 for (; loop != CSVReaderIterator(); loop.next()) {
194 CSVReaderIterator nextRow = loop;
195 nextRow.next();
196 CPS::Real nextTime = (need_that_conversion)
197 ? time_format_convert((*nextRow).get(0))
198 : std::stod((*nextRow).get(0));
199 if ((*nextRow).size() == 2) {
200 data_with_weighting_factor = true;
201 }
202 if ((start_time < 0) | (nextTime >= Int(start_time))) {
203 break;
204 }
205 }
206 /*
207 reading data after entry point until end_time is reached
208 */
209 for (; loop != CSVReaderIterator(); loop.next()) {
210 CPS::Real currentTime = (need_that_conversion)
211 ? time_format_convert((*loop).get(0))
212 : std::stod((*loop).get(0));
213 if (data_with_weighting_factor) {
214 Real wf = std::stod((*loop).get(1));
215 load_profile.weightingFactors.insert(
216 std::pair<Real, Real>(currentTime, wf));
217 } else {
218 PQData pq;
219 // multiplied by 1000 due to unit conversion (kw to w)
220 pq.p = std::stod((*loop).get(1)) * 1000;
221 pq.q = std::stod((*loop).get(2)) * 1000;
222 load_profile.pqData.insert(std::pair<Real, PQData>(currentTime, pq));
223 }
224
225 if (end_time > 0 && currentTime > end_time)
226 break;
227 }
228 std::vector<CPS::Real> times;
229 for (CPS::Real x_ = start_time; x_ <= end_time; x_ += time_step) {
230 times.push_back(x_);
231 }
232
233 for (auto x : times) {
234 if (load_profile.pqData.find(x) == load_profile.pqData.end()) {
235 if (data_with_weighting_factor) {
236 Real y = interpol_linear(load_profile.weightingFactors, x);
237 load_profile.weightingFactors.insert(std::pair<Real, Real>(x, y));
238 } else {
239 PQData y = interpol_linear(load_profile.pqData, x);
240 load_profile.pqData.insert(std::pair<Real, PQData>(x, y));
241 }
242 }
243 }
244
245 return load_profile;
246}
247
248// can only read one file for now
249std::vector<Real> CSVReader::readPQData(fs::path file, Real start_time,
250 Real time_step, Real end_time,
251 CSVReader::DataFormat format) {
252
253 std::vector<Real> p_data;
254 std::ifstream csvfile(file);
255 CSVReaderIterator row_(csvfile);
256
257 // ignore the first row if it is a title
258 if (mSkipFirstRow && !std::isdigit((*row_).get(0)[0])) {
259 row_.next();
260 }
261
262 /*
263 loop over rows of the csv file to find the entry point to read in.
264 if start_time and end_time are negative (as default), it reads in all rows.
265 */
266 Real presentTime = 0;
267 for (; row_ != CSVReaderIterator(); row_.next()) {
268 if ((start_time < 0) | (Int(presentTime) + 1 > Int(start_time)))
269 break;
270 presentTime++;
271 }
272 // Reading data after entry point until end_time is reached.
273 for (; row_ != CSVReaderIterator(); row_.next()) {
274 // IMPORTANT: take care of units. assume kW
275 p_data.push_back(std::stod((*row_).get(0)) * 1000);
276 if (end_time > 0 && presentTime > end_time)
277 break;
278 presentTime = Int(presentTime) + 1;
279 }
280 std::cout << "CSV loaded." << std::endl;
281 return p_data;
282}
283
285 Real time_step, Real end_time,
286 CSVReader::Mode mode,
287 CSVReader::DataFormat format) {
288
289 switch (mode) {
290 case CSVReader::Mode::AUTO: {
291 for (auto obj : sys.mComponents) {
292 if (std::shared_ptr<CPS::SP::Ph1::Load> load =
293 std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(obj)) {
294 SPDLOG_LOGGER_INFO(mSLog,
295 "Comparing csv file names with load mRIDs ...");
296 String load_name = load->name();
297 for (auto file : mFileList) {
298 String file_name = file.filename().string();
299 // Changing file name and load name to upper case for later matching
300 for (auto &c : load_name)
301 c = toupper(c);
302 for (auto &c : file_name)
303 c = toupper(c);
304 // Strip off all non-alphanumeric characters
305 load_name.erase(remove_if(load_name.begin(), load_name.end(),
306 [](char c) { return !isalnum(c); }),
307 load_name.end());
308 file_name.erase(remove_if(file_name.begin(), file_name.end(),
309 [](char c) { return !isalnum(c); }),
310 file_name.end());
311 if (std::string(file_name.begin(), file_name.end() - 3)
312 .compare(load_name) == 0) {
313 load->mLoadProfile =
314 readLoadProfile(file, start_time, time_step, end_time, format);
315 load->use_profile = true;
316 SPDLOG_LOGGER_INFO(mSLog, "Assigned {} to {}",
317 file.filename().string(), load->name());
318 }
319 }
320 }
321 }
322 break;
323 }
324 case CSVReader::Mode::MANUAL: {
325 Int LP_assigned_counter = 0;
326 Int LP_not_assigned_counter = 0;
327 SPDLOG_LOGGER_INFO(mSLog,
328 "Assigning load profiles with user defined pattern ...");
329 for (auto obj : sys.mComponents) {
330 if (std::shared_ptr<CPS::SP::Ph1::Load> load =
331 std::dynamic_pointer_cast<CPS::SP::Ph1::Load>(obj)) {
332 std::map<String, String>::iterator file =
333 mAssignPattern.find(load->name());
334 if (file == mAssignPattern.end()) {
335 std::cout << load->name() << " has no profile given." << std::endl;
336 SPDLOG_LOGGER_INFO(mSLog, "{} has no profile given.", load->name());
337 LP_not_assigned_counter++;
338 continue;
339 }
340 load->mLoadProfile =
341 readLoadProfile(fs::path(mPath + file->second + ".csv"), start_time,
342 time_step, end_time);
343 load->use_profile = true;
344 std::cout << " Assigned " << file->second << " to " << load->name()
345 << std::endl;
346 SPDLOG_LOGGER_INFO(mSLog, "Assigned {}.csv to {}", file->second,
347 load->name());
348 LP_assigned_counter++;
349 }
350 }
351 SPDLOG_LOGGER_INFO(mSLog,
352 "Assigned profiles for {} loads, {} not assigned.",
353 LP_assigned_counter, LP_not_assigned_counter);
354 break;
355 }
356 default: {
357 throw std::invalid_argument("Load profile assign mode error");
358 break;
359 }
360 }
361}
362
363CPS::PQData CSVReader::interpol_linear(std::map<CPS::Real, CPS::PQData> &pqData,
364 CPS::Real x) {
365 std::map<Real, PQData>::const_iterator entry = pqData.upper_bound(x);
366 PQData y;
367
368 if (entry == pqData.end()) {
369 return (--entry)->second;
370 }
371 if (entry == pqData.begin()) {
372 return entry->second;
373 }
374 std::map<Real, PQData>::const_iterator prev = entry;
375 --prev;
376
377 const CPS::Real delta = (x - prev->first) / (entry->first - prev->first);
378
379 y.p = delta * entry->second.p + (1 - delta) * prev->second.p;
380 y.q = delta * entry->second.q + (1 - delta) * prev->second.q;
381 return y;
382}
383
384CPS::Real
385CSVReader::interpol_linear(std::map<CPS::Real, CPS::Real> &weightingFactors,
386 CPS::Real x) {
387 std::map<Real, Real>::const_iterator entry = weightingFactors.upper_bound(x);
388 CPS::Real y;
389
390 if (entry == weightingFactors.end()) {
391 return (--entry)->second;
392 }
393 if (entry == weightingFactors.begin()) {
394 return entry->second;
395 }
396 std::map<Real, Real>::const_iterator prev = entry;
397 --prev;
398
399 const CPS::Real delta = (x - prev->first) / (entry->first - prev->first);
400
401 y = delta * entry->second + (1 - delta) * prev->second;
402 return y;
403}
reads load profiles (csv files only) and assign them to the corresponding load object
Definition CSVReader.h:28
PowerProfile readLoadProfile(fs::path file, Real start_time=-1, Real time_step=1, Real end_time=-1, CSVReader::DataFormat format=CSVReader::DataFormat::SECONDS)
PQData interpol_linear(std::map< Real, PQData > &data_PQ, Real x)
interpolation for PQ data points
void assignLoadProfile(SystemTopology &sys, Real start_time=-1, Real time_step=1, Real end_time=-1, CSVReader::Mode mode=CSVReader::Mode::AUTO, CSVReader::DataFormat format=CSVReader::DataFormat::SECONDS)
assign load profile to corresponding load object
Real time_format_convert(const String &time)
IdentifiedObject::List mComponents
List of network components.