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

Restructure loggers and log calls #236

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
eeeeddb
log all components to the same file
JTS22 Jul 10, 2023
9b9c9e2
enable debug logging
JTS22 Jul 11, 2023
653f90c
set max log level according to build type
JTS22 Jul 12, 2023
115a266
introduce custom sink type
JTS22 Jul 14, 2023
0e530da
remove name argument for csvreader log
JTS22 Jul 16, 2023
85a5ab6
remove solver name variable
JTS22 Jul 16, 2023
8efb2bf
clean up scheduler and MNASolverPlugin loggers
JTS22 Jul 16, 2023
8af1b59
remove CIMReader name and add cli log level
JTS22 Jul 16, 2023
a265cdb
do not use logger registry to allow duplicates
JTS22 Jul 16, 2023
3c58a45
fix log file names
JTS22 Jul 16, 2023
4470443
demote all CIMReader info log to debug
JTS22 Jul 16, 2023
891cdb8
demote scheduler info log to debug
JTS22 Jul 16, 2023
d43597f
add separate cli log level for simulation and scheduler
JTS22 Jul 16, 2023
f7703c3
change solver logs to debug level
JTS22 Jul 17, 2023
0e1dd5d
add a new logger for interfaces
JTS22 Jul 17, 2023
b33f6b9
change some simulation logs to debug level
JTS22 Jul 18, 2023
83a3b69
update component log levels
JTS22 Jul 18, 2023
d60a9b6
allow multiple simulation loggers for different filepaths
JTS22 Jul 19, 2023
0adb4cc
propagate simulation cliLevel to solvers
JTS22 Jul 19, 2023
d81463a
set component cliLevel according to logLevel
JTS22 Jul 19, 2023
4a0c9e0
update log levels for component initialize methods
JTS22 Jul 24, 2023
83ce14f
log domain on simulation init
JTS22 Jul 24, 2023
7d4d5f4
add an internal logger to simulation for logging iteration counts
JTS22 Jul 25, 2023
5fad5b6
remove old python log_attribute methods
JTS22 Jul 26, 2023
0181cb0
allow overwriting the simulation default logger
JTS22 Jul 26, 2023
5772700
only create left/right-vector logs on trace level
JTS22 Jul 26, 2023
22ce8b9
manually open / close non-simulation data loggers
JTS22 Jul 26, 2023
d3e84de
change log level in notebooks using LeftVector log
JTS22 Jul 26, 2023
d5d6183
update some log levels
JTS22 Jul 27, 2023
88be7bd
update logging guidelines in documentation
JTS22 Jul 28, 2023
f66fe17
use the DataLogger for trace logging in DP_Ph1_Capacitor
JTS22 Jul 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ check_symbol_exists(getopt_long getopt.h HAVE_GETOPT)
include(GetVersion)
GetVersion(${PROJECT_SOURCE_DIR} "DPSIM")

if (CMAKE_BUILD_TYPE MATCHES Debug)
set(DEBUG_BUILD ON)
endif()

# Deprecated build options
# TODO: Remove deprecated options in future release
option(WITH_EIGEN_MODULE "Fetch Eigen3 as module (deprecated option)" OFF)
Expand Down
38 changes: 34 additions & 4 deletions docs/hugo/content/en/docs/Development/Guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,39 @@ This is a summary of general guidelines for the development of DPsim.

## Logging

Debug or trace should be the default log level for information that might be nice to have but not necessary for every simulation case.

Calls to the logger that might occur during simulation must use spdlog macros, like SPDLOG_LOGGER_INFO.
There are two ways to log information in DPsim: The `CPS::Logger` (called the **Text Logger**), which can write to `.log` files and to the CLI, as well as the `CPS::DataLogger` (called the **Data Logger**), which can write to `.csv` files.

### Log Levels
The log levels that can be used with the Text Logger are as follows:
- `off` - no Text Logger output at all, recommended to use e.g. when running case studies where exclusively the simulation results should be logged
- `error` - all errors
- `warn` - all warnings
- `info` - logging of information that is basic and that is logged only once before/after the simulation
- `debug` - logging of information for debugging (extended static information, e.g. initialization values, matrix stamps, subcomponents)
- `trace` - logging of information in each simulation step

While the Data Logger does not have any log levels (just a boolean flag to enable or disable the logger),
there are some Data Loggers that are only activated when the object owning the Data Logger is also configured to use a certain log level.
E.g., the `<SimName>_LeftVector.csv` and `<SimName>_RightVector.csv` will only be generated when the Simulation Text Logger is set to `trace` level.

Logging with the Text Logger should always happen using the `SPDLOG_LOGGER_<LEVEL>` macros.

### Examples
The Text Loggers in DPsim are grouped into three categories:
- `Simulation`: All the Text Loggers logging into `Simulation.log`
- `Component`: All the Text Loggers logging into `Components.log`
- `Debug`: Text Loggers used for debugging purposes, each creating their own log files

The following table provides an overview on what output is produced in each category on different log levels:

| Log Level | `Simulation` Text Logger | `Component` Text Logger | `Debug` Text Loggers | Data Loggers |
|--------------|---------------------------|-------------------------|----------------------|--------------|
| `off` | - | - | - |Only log that is explicitly requested by the user, e.g. through `DataLogger::log_attribute` or `Simulation::logStepTimes`|
| `error` | Errors that will lead to a program crash or invalidate the simulation results | Errors that will lead to a program crash or invalidate the simulation results | Errors that will lead to a program crash or invalidate the simulation results | Only log that is explicitly requested by the user, e.g. through `DataLogger::log_attribute` or `Simulation::logStepTimes`|
| `warn` | Problems that might lead to invalid results, but will likely not lead to a crash | Problems that might lead to invalid results, but will likely not lead to a crash | Problems that might lead to invalid results, but will likely not lead to a crash | Only log that is explicitly requested by the user, e.g. through `DataLogger::log_attribute` or `Simulation::logStepTimes`|
| `info` | e.g. Summary of number of network and virtual nodes, Simulation configuration / domain| e.g. Parameters set through `setParameters`, connections to network nodes | e.g. `CSVReader.log`, whenever a `CSVReader` is used | Only log that is explicitly requested by the user, e.g. through `DataLogger::log_attribute` or `Simulation::logStepTimes`|
| `debug` | e.g. System matrices, Scheduler log, More details about the used solver | e.g. Component initialization, subcomponents, snubber components | e.g. `CIMReader.log`, whenever a `CIMReader` is used| e.g. iteration count for iterative solvers in `<SimName>.csv`|
| `trace` | Information that changes on each simulation step | e.g. voltages / currents calculated in `mnaUpdateCurrent`/`mnaUpdateVoltage`|-|e.g. `<SimName>_LeftVector.csv`, `<SimName>_RightVector.csv`, `<SimName>_InitLeftVector.csv`, `<SimName>_InitRightVector.csv`|

## Creating New Releases

Expand All @@ -23,4 +53,4 @@ A new version of DPsim has to be indicated as follows:
- Update sonar-project.properties

Due to the creation of a new tag, a new PyPi package will be deployed automatically.
To release an updated Docker image, the container workflow needs to be triggered manually.
To release an updated Docker image, the container workflow needs to be triggered manually.
14 changes: 7 additions & 7 deletions docs/hugo/content/en/docs/Overview/Attributes/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ In general, attributes are instances of the `Attribute<T>` class, but they are u

Through the template parameter `T` of the `Attribute<T>` class, attributes can have different value types, most commonly `Real`, `Complex`, `Matrix`, or `MatrixComp`. Additionally, attributes can fall into one of two categories:
**Static** attributes have a fixed value which can only be changed explicitly through the attribute's `set`-method or through a mutable reference obtained through `get`.
**Dynamic** attributes on the other hand can dynamically re-compute their value from other attributes every time they are read. This can for example be used to create a scalar attribute of type `Real` whose value always contains the magnitude of another, different attribute of type `Complex`.
**Dynamic** attributes on the other hand can dynamically re-compute their value from other attributes every time they are read. This can for example be used to create a scalar attribute of type `Real` whose value always contains the magnitude of another, different attribute of type `Complex`.

Any simulation component or class which inherits from `IdentifiedObject` contains an instance of an `AttributeList`.
This list can be used to store all the attributes present in this component and later access them via a `String` instead of having to use the member variable directly.
For reasons of code clarity and runtime safety, the member variables should still be used whenever possible.
For reasons of code clarity and runtime safety, the member variables should still be used whenever possible.

## Creating and Storing Attributes
Normally, a new attribute is created by using the `create` or `createDynamic` method of an `AttributeList` object.
These two methods will create a new attribute of the given type and insert it into the `AttributeList` under the given name. After the name, `create` can take an additional parameter of type `T` which will be used as the initial value for this attribute.
Afterwards, a pointer to the attribute is returned which can then be stored in a component's member variable. Usually this is done in the
component's constructor in an initialization list:

```cpp
```cpp
/// Component class Base::Ph1::PiLine

public:
Expand Down Expand Up @@ -53,7 +53,7 @@ Simulation::Simulation(String name, Logger::Level logLevel) :
mTimeStep(AttributeStatic<Real>::make(0.001)),
mSplitSubnets(AttributeStatic<Bool>::make(true)),
mSteadyStateInit(AttributeStatic<Bool>::make(false)),
//...
//...
{
// ...
}
Expand Down Expand Up @@ -83,7 +83,7 @@ Real read3 = **attr; //read3 = 0.003
```

## Working with Dynamic Attributes
In general, dynamic attributes can be accessed via the same `get` and `set`-methods described above for static attributes. However,
In general, dynamic attributes can be accessed via the same `get` and `set`-methods described above for static attributes. However,
dynamic attributes can additionally have **dependencies** on other attributes which affect the behavior of these methods.
Usually, this is used to dynamically compute the attribute's value from the value of another attribute. In the simplest case, a dynamic
attribute can be set to **reference** another (static or dynamic) attribute using the `setReference`-method. After this method has been called,
Expand Down Expand Up @@ -150,7 +150,7 @@ required attribute pointer, one can either directly access the public member var
auto r1 = DP::Ph1::Resistor::make("r_1");
r1->setParameters(5);

auto logger = DataLogger::make("simName");
auto logger = CPS::DataLogger::make("simName");
// Access the attribute through the member variable
logger->logAttribute("i12", r1->mIntfCurrent);

Expand Down Expand Up @@ -187,4 +187,4 @@ void DP::Ph1::Inductor::mnaAddPostStepDependencies(
}
```
Here, the MNA post step depends on the solution vector of the system, `leftVector`, and modifies `mIntfVoltage` and `mIntfCurrent`.
Therefore, this task needs to be scheduled after the system solution that computes `leftVector` and before tasks that require the voltage and current interface vectors of the inductance, e.g. the task logging these values.
Therefore, this task needs to be scheduled after the system solution that computes `leftVector` and before tasks that require the voltage and current interface vectors of the inductance, e.g. the task logging these values.
4 changes: 2 additions & 2 deletions dpsim-models/include/dpsim-models/CIM/Reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ namespace CIM {
public:
/// Creates new reader with a name for logging.
/// The first log level is for the reader and the second for the generated components.
Reader(String name,
Logger::Level logLevel = Logger::Level::info,
Reader(Logger::Level logLevel = Logger::Level::info,
Logger::Level cliLevel = Logger::Level::off,
Logger::Level componentLogLevel = Logger::Level::off);
///
virtual ~Reader();
Expand Down
8 changes: 4 additions & 4 deletions dpsim-models/include/dpsim-models/CSVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ namespace CPS {
enum class DataFormat { HHMMSS, SECONDS, HOURS, MINUTES };

///
CSVReader(String name, std::list<fs::path> path, Logger::Level logLevel);
CSVReader(std::list<fs::path> path, Logger::Level logLevel, Logger::Level cliLevel = Logger::Level::off);
///
CSVReader(String name, String path, Logger::Level logLevel);
CSVReader(String path, Logger::Level logLevel, Logger::Level cliLevel = Logger::Level::off);
///
CSVReader(String name, std::list<fs::path> path, std::map<String, String>& assignList, Logger::Level logLevel);
CSVReader(std::list<fs::path> path, std::map<String, String>& assignList, Logger::Level logLevel, Logger::Level cliLevel = Logger::Level::off);
///
CSVReader(String name, String path, std::map<String, String>& assignList, Logger::Level logLevel);
CSVReader(String path, std::map<String, String>& assignList, Logger::Level logLevel, Logger::Level cliLevel = Logger::Level::off);

/// convert HH:MM:SS format timestamp into total seconds.
/// e.g.: 00 : 01 : 00 -- > 60.
Expand Down
1 change: 1 addition & 0 deletions dpsim-models/include/dpsim-models/Config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
#cmakedefine WITH_NUMPY
#cmakedefine WITH_KLU
#cmakedefine CGMES_BUILD
#cmakedefine DEBUG_BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,50 @@
#include <iostream>
#include <fstream>

#include <dpsim/Definitions.h>
#include <dpsim/Scheduler.h>
#include <dpsim-models/Filesystem.h>
#include <dpsim-models/PtrFactory.h>
#include <dpsim-models/Attribute.h>
#include <dpsim-models/SimNode.h>
#include <dpsim-models/Task.h>

namespace DPsim {
namespace CPS {

class DataLogger : public SharedFactory<DataLogger> {

protected:
std::ofstream mLogFile;
String mName;
/// FIXME: The enabled-parameter cannot be changed midway through the simulation
/// Instead of setting this to false, the logger should not be created at all to reduce function call overhead
Bool mEnabled;
UInt mDownsampling;
fs::path mFilename;

std::map<String, CPS::AttributeBase::Ptr> mAttributes;

void logDataLine(Real time, Real data);
void logDataLine(Real time, const Matrix& data);
void logDataLine(Real time, const MatrixComp& data);

public:
typedef std::shared_ptr<DataLogger> Ptr;
typedef std::vector<DataLogger::Ptr> List;

DataLogger(Bool enabled = true);
DataLogger(String name, Bool enabled = true, UInt downsampling = 1);

void logDataLine(Real time, Real data);
void logDataLine(Real time, std::vector<Real> data);
void logDataLine(Real time, const Matrix& data);
void logDataLine(Real time, const MatrixComp& data);

void open();
void close();
void reopen() {
close();
open();
}

const String& name() const {
return mName;
}

void logPhasorNodeValues(Real time, const Matrix& data, Int freqNum = 1);
void logEMTNodeValues(Real time, const Matrix& data);

Expand All @@ -72,7 +77,7 @@ namespace DPsim {
for (auto attr : logger.mAttributes) {
mAttributeDependencies.push_back(attr.second);
}
mModifiedAttributes.push_back(Scheduler::external);
mModifiedAttributes.push_back(nullptr); //nullptr = Scheduler::external, but this cannot be used because the scheduler is not linked yet.
}

void execute(Real time, Int timeStepCount);
Expand Down
88 changes: 75 additions & 13 deletions dpsim-models/include/dpsim-models/Logger.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* 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/.
*********************************************************************************/
// SPDX-License-Identifier: Apache-2.0

#pragma once

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#include <dpsim-models/Config.h>

#if defined(DEBUG_BUILD)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#else
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif
#define SPDLOG_DISABLE_DEFAULT_LOGGER

#include <spdlog/spdlog.h>

#if defined(SPDLOG_VER_MAJOR) && SPDLOG_VER_MAJOR >= 1
Expand All @@ -18,6 +20,9 @@
#endif

#include <spdlog/fmt/ostr.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include "spdlog/sinks/base_sink.h"

#include <dpsim-models/Definitions.h>
#include <dpsim-models/MathUtils.h>
Expand All @@ -30,20 +35,33 @@ namespace CPS {
using Level = spdlog::level::level_enum;
using Log = std::shared_ptr<spdlog::logger>;

enum class LoggerType {
SIMULATION,
COMPONENT,
DEBUG
};

/// Holds the file sinks shared by all simulation loggers
static std::map<std::string, spdlog::sink_ptr, std::less<>> mFileSinkRegistry;
/// Holds the stdout cli sink shared by all loggers
static spdlog::sink_ptr mStdoutSink;
/// Holds the stderr cli sink shared by all loggers
static spdlog::sink_ptr mStderrSink;

private:
static Log create(const std::string &name, Level filelevel = Level::info, Level clilevel = Level::off);
static Log create(Logger::LoggerType type, const std::string &name, const std::string &fileName, Level filelevel, Level clilevel);

public:
Logger();
~Logger();
Logger() = default;
~Logger() = default;

static String prefix();
static String logDir();
static void setLogDir(String path);

// #### SPD log wrapper ####
///
static Log get(const std::string &name, Level filelevel = Level::info, Level clilevel = Level::off);
static Log get(LoggerType type, const std::string &name, Level filelevel = Level::info, Level clilevel = Level::off);
///
static void setLogLevel(std::shared_ptr<spdlog::logger> logger, Logger::Level level);
///
Expand All @@ -58,10 +76,54 @@ namespace CPS {
static String phasorToString(const Complex& num);
static String complexToString(const Complex& num);
static String realToString(const Real& num);

static String getCSVColumnNames(std::vector<String> names);
static String getCSVLineFromData(Real time, Real data);
static String getCSVLineFromData(Real time, const Matrix& data);
static String getCSVLineFromData(Real time, const MatrixComp& data);

class dpsim_sink : public spdlog::sinks::base_sink <std::mutex>
{
private:
spdlog::sink_ptr mFileSink;
spdlog::sink_ptr mStdoutSink;
spdlog::sink_ptr mStderrSink;
Level mFileLevel;
Level mCliLevel;
public:
dpsim_sink(spdlog::sink_ptr fileSink, spdlog::sink_ptr stdoutSink, spdlog::sink_ptr stderrSink, Level fileLevel, Level cliLevel) :
spdlog::sinks::base_sink<std::mutex>(),
mFileSink(fileSink),
mStdoutSink(stdoutSink),
mStderrSink(stderrSink),
mFileLevel(fileLevel),
mCliLevel(cliLevel) { };
protected:
void sink_it_(const spdlog::details::log_msg& msg) override
{
if (mFileSink && msg.level >= mFileLevel) {
mFileSink->log(msg);
}
if (mStdoutSink && msg.level >= mCliLevel && msg.level < Level::warn) {
mStdoutSink->log(msg);
}
if (mStderrSink && msg.level >= mCliLevel && msg.level >= Level::warn) {
mStderrSink->log(msg);
}
}

void flush_() override
{
if (mFileSink) {
mFileSink->flush();
}
if (mStdoutSink) {
mStdoutSink->flush();
}
if (mStderrSink) {
mStderrSink->flush();
}
}
};
};
}
3 changes: 3 additions & 0 deletions dpsim-models/include/dpsim-models/MNASimPowerComp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ namespace CPS {
Bool mHasPreStep;
Bool mHasPostStep;

protected:
Real mSimulationTime;

public:
using Type = VarType;
using Ptr = std::shared_ptr<MNASimPowerComp<VarType>>;
Expand Down
Loading