DPsim
RealTimeDataLogger.cpp
1 /* A data logger for real-time simulation data logging.
2  *
3  * Author: Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
4  * SPDX-FileCopyrightText: 2024 Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 #include "dpsim-models/Attribute.h"
8 #include <iomanip>
9 
10 #include <dpsim-models/Logger.h>
11 #include <dpsim/RealTimeDataLogger.h>
12 #include <memory>
13 
14 using namespace DPsim;
15 
16 RealTimeDataLogger::RealTimeDataLogger(std::filesystem::path &filename, size_t rowNumber)
17  : DataLoggerInterface(), mFilename(filename), mRowNumber(rowNumber), mCurrentRow(0), mCurrentAttribute(0), mAttributeData() {}
18 
19 RealTimeDataLogger::RealTimeDataLogger(std::filesystem::path &filename, Real finalTime, Real timeStep)
20  : DataLoggerInterface(), mFilename(filename), mRowNumber((finalTime / timeStep + 0.5)), mCurrentRow(0), mCurrentAttribute(0), mAttributeData() {}
21 
22 void RealTimeDataLogger::start() {
23  double mb_size = static_cast<double>(mRowNumber) * (mAttributes.size() + 1) * sizeof(Real);
24  auto log = CPS::Logger::get("RealTimeDataLogger", CPS::Logger::Level::off, CPS::Logger::Level::info);
25  log->info("Preallocating memory for real-time data logger: {} rows for {} attributes ({} MB)", mRowNumber, mAttributes.size(), mb_size / (1024 * 1024));
26  // We are doing real time so preallocate everything
27  mAttributeData.resize(mRowNumber);
28  for (auto &it : mAttributeData) {
29  // We have to add one to the size because we also log the time
30  it.resize(mAttributes.size() + 1);
31  }
32 }
33 
34 void RealTimeDataLogger::stop() {
35  auto mLogFile = std::ofstream(mFilename, std::ios_base::out | std::ios_base::trunc);
36  if (!mLogFile.is_open()) {
37  throw std::runtime_error("Cannot open log file " + mFilename.string());
38  }
39 
40  mLogFile << std::right << std::setw(14) << "time";
41  for (auto it : mAttributes)
42  mLogFile << ", " << std::right << std::setw(13) << it.first;
43  mLogFile << '\n';
44 
45  for (auto row : mAttributeData) {
46  mLogFile << std::scientific << std::right << std::setw(14) << row[0];
47  for (size_t i = 1; i < row.size(); ++i)
48  mLogFile << ", " << std::right << std::setw(13) << row[i];
49  mLogFile << '\n';
50  }
51  mLogFile.close();
52 }
53 
54 void RealTimeDataLogger::log(Real time, Int timeStepCount) {
55  mCurrentRow = timeStepCount;
56  if (timeStepCount < 0 || static_cast<size_t>(timeStepCount) >= mRowNumber) {
57  throw std::runtime_error("RealTimeDataLogger: timeStepCount out of bounds. Please verify the logger was initialized correctly.");
58  }
59  if (mAttributeData.size() != mRowNumber || mAttributeData[mCurrentRow].size() != mAttributes.size() + 1) {
60  throw std::runtime_error("RealTimeDataLogger: Attribute data size mismatch");
61  }
62  mAttributeData[mCurrentRow][0] = time;
63  mCurrentAttribute = 1;
64 
65  for (auto it : mAttributes) {
66  if (it.second->getType() == typeid(Real)) {
67  mAttributeData[mCurrentRow][mCurrentAttribute++] = **std::dynamic_pointer_cast<std::shared_ptr<CPS::Attribute<Real>>>(it.second.getPtr());
68  } else if (it.second->getType() == typeid(Int)) {
69  mAttributeData[mCurrentRow][mCurrentAttribute++] = **std::dynamic_pointer_cast<std::shared_ptr<CPS::Attribute<Int>>>(it.second.getPtr());
70  }
71  }
72 }
73 
74 void RealTimeDataLogger::Step::execute(Real time, Int timeStepCount) { mLogger.log(time, timeStepCount); }
75 
76 CPS::Task::Ptr RealTimeDataLogger::getTask() { return std::make_shared<RealTimeDataLogger::Step>(*this); }