DPsim
Loading...
Searching...
No Matches
Simulation.cpp
1// SPDX-License-Identifier: Apache-2.0
2
3#include <algorithm>
4#include <chrono>
5#include <iomanip>
6#include <typeindex>
7
8#include <dpsim-models/Utils.h>
9#include <dpsim/DiakopticsSolver.h>
10#include <dpsim/MNASolverFactory.h>
11#include <dpsim/PFSolverPowerPolar.h>
12#include <dpsim/SequentialScheduler.h>
13#include <dpsim/Simulation.h>
14#include <dpsim/Utils.h>
15
16#include <spdlog/sinks/stdout_color_sinks.h>
17
18#ifdef WITH_CIM
19#include <dpsim-models/CIM/Reader.h>
20#endif
21
22#ifdef WITH_SUNDIALS
23#include <dpsim-models/Solver/ODEInterface.h>
24#include <dpsim/DAESolver.h>
25#include <dpsim/ODESolver.h>
26#endif
27
28using namespace CPS;
29using namespace DPsim;
30
31Simulation::Simulation(String name, Logger::Level logLevel)
32 : mName(AttributeStatic<String>::make(name)),
33 mFinalTime(AttributeStatic<Real>::make(0.001)),
34 mTimeStep(AttributeStatic<Real>::make(0.001)),
35 mSplitSubnets(AttributeStatic<Bool>::make(true)),
36 mSteadyStateInit(AttributeStatic<Bool>::make(false)),
37 mLogLevel(logLevel) {
38 create();
39}
40
42 : mName(AttributeStatic<String>::make(name)),
43 mSolverPluginName(args.solverPluginName),
44 mFinalTime(AttributeStatic<Real>::make(args.duration)),
45 mTimeStep(AttributeStatic<Real>::make(args.timeStep)),
46 mSplitSubnets(AttributeStatic<Bool>::make(true)),
47 mSteadyStateInit(AttributeStatic<Bool>::make(false)),
48 mLogLevel(args.logLevel), mDomain(args.solver.domain),
49 mSolverType(args.solver.type), mDirectImpl(args.directImpl) {
50 create();
51}
52
54 // Logging
55 mLog =
56 Logger::get(**mName, mLogLevel, std::max(Logger::Level::info, mLogLevel));
57
58 Eigen::setNbThreads(1);
59
60 mInitialized = false;
61}
62
64 if (mInitialized)
65 return;
66
67 mSolvers.clear();
68
69 switch (mDomain) {
70 case Domain::SP:
71 // Treat SP as DP
72 case Domain::DP:
74 break;
75 case Domain::EMT:
77 break;
78 }
79
80 mTime = 0;
82
83 schedule();
84
85 mInitialized = true;
86}
87
88template <typename VarType> void Simulation::createSolvers() {
89 Solver::Ptr solver;
90 switch (mSolverType) {
91 case Solver::Type::MNA:
93 break;
94#ifdef WITH_SUNDIALS
95 case Solver::Type::DAE:
96 solver = std::make_shared<DAESolver>(**mName, mSystem, **mTimeStep, 0.0);
97 mSolvers.push_back(solver);
98 break;
99#endif /* WITH_SUNDIALS */
100 case Solver::Type::NRP:
101 solver = std::make_shared<PFSolverPowerPolar>(**mName, mSystem, **mTimeStep,
102 mLogLevel);
103 solver->doInitFromNodesAndTerminals(mInitFromNodesAndTerminals);
104 solver->setSolverAndComponentBehaviour(mSolverBehaviour);
105 solver->initialize();
106 mSolvers.push_back(solver);
107 break;
108 default:
110 }
111
112 // Some components require a dedicated ODE solver.
113 // This solver is independent of the system solver.
114#ifdef WITH_SUNDIALS
115 for (auto comp : mSystem.mComponents) {
116 auto odeComp = std::dynamic_pointer_cast<ODEInterface>(comp);
117 if (odeComp) {
118 // TODO explicit / implicit integration
119 auto odeSolver = std::make_shared<ODESolver>(
120 odeComp->mAttributeList->attributeTyped<String>("name")->get() +
121 "_ODE",
122 odeComp, false, **mTimeStep);
123 mSolvers.push_back(odeSolver);
124 }
125 }
126#endif /* WITH_SUNDIALS */
127}
128
129template <typename VarType> void Simulation::createMNASolver() {
130 Solver::Ptr solver;
131 std::vector<SystemTopology> subnets;
132 // The Diakoptics solver splits the system at a later point.
133 // That is why the system is not split here if tear components exist.
134 if (**mSplitSubnets && mTearComponents.size() == 0)
135 mSystem.splitSubnets<VarType>(subnets);
136 else
137 subnets.push_back(mSystem);
138
139 for (UInt net = 0; net < subnets.size(); ++net) {
140 String copySuffix;
141 if (subnets.size() > 1)
142 copySuffix = "_" + std::to_string(net);
143
144 // TODO: In the future, here we could possibly even use different
145 // solvers for different subnets if deemed useful
146 if (mTearComponents.size() > 0) {
147 // Tear components available, use diakoptics
148 solver = std::make_shared<DiakopticsSolver<VarType>>(
149 **mName, subnets[net], mTearComponents, **mTimeStep, mLogLevel);
150 } else {
151 // Default case with lu decomposition from mna factory
152 solver = MnaSolverFactory::factory<VarType>(**mName + copySuffix, mDomain,
153 mLogLevel, mDirectImpl,
155 solver->setTimeStep(**mTimeStep);
156 solver->setLogSolveTimes(mLogStepTimes);
157 solver->doSteadyStateInit(**mSteadyStateInit);
158 solver->doFrequencyParallelization(mFreqParallel);
159 solver->setSteadStIniTimeLimit(mSteadStIniTimeLimit);
160 solver->setSteadStIniAccLimit(mSteadStIniAccLimit);
161 solver->setSystem(subnets[net]);
162 solver->setSolverAndComponentBehaviour(mSolverBehaviour);
163 solver->doInitFromNodesAndTerminals(mInitFromNodesAndTerminals);
164 solver->doSystemMatrixRecomputation(mSystemMatrixRecomputation);
165 solver->setDirectLinearSolverConfiguration(
166 mDirectLinearSolverConfiguration);
167 solver->initialize();
168 solver->setMaxNumberOfIterations(mMaxIterations);
169 }
170 mSolvers.push_back(solver);
171 }
172}
173
174void Simulation::sync() const {
175 SPDLOG_LOGGER_INFO(mLog, "Start synchronization with remotes on interfaces");
176
177 for (auto intf : mInterfaces) {
178 intf->syncExports();
179 intf->syncImports();
180 intf->syncExports();
181 }
182
183 SPDLOG_LOGGER_INFO(mLog, "Synchronized simulation start with remotes");
184}
185
187 mTasks.clear();
188 mTaskOutEdges.clear();
189 mTaskInEdges.clear();
190 for (auto solver : mSolvers) {
191 for (auto t : solver->getTasks()) {
192 mTasks.push_back(t);
193 }
194 }
195
196 for (auto intf : mInterfaces) {
197 for (auto t : intf->getTasks()) {
198 mTasks.push_back(t);
199 }
200 }
201
202 for (auto logger : mLoggers) {
203 mTasks.push_back(logger->getTask());
204 }
205 if (!mScheduler) {
206 mScheduler = std::make_shared<SequentialScheduler>();
207 }
208 mScheduler->resolveDeps(mTasks, mTaskInEdges, mTaskOutEdges);
209}
210
212 SPDLOG_LOGGER_INFO(mLog, "Scheduling tasks.");
213 prepSchedule();
214 mScheduler->createSchedule(mTasks, mTaskInEdges, mTaskOutEdges);
215 SPDLOG_LOGGER_INFO(mLog, "Scheduling done.");
216}
217
218#ifdef WITH_GRAPHVIZ
219Graph::Graph Simulation::dependencyGraph() {
220 if (!mInitialized)
221 initialize();
222
223 std::map<CPS::Task::Ptr, Scheduler::TaskTime> avgTimes;
224 std::map<CPS::Task::Ptr, String> fillColors;
225
226 auto isScheduled = [this](Task::Ptr task) -> Bool {
227 return !mTaskOutEdges[task].empty();
228 };
229
230 auto getColor = [](Task::Ptr task) -> String {
231 static std::map<std::type_index, String> colorMap;
232 auto tid = std::type_index(typeid(task.get()));
233
234 if (colorMap.find(tid) != colorMap.end()) {
235 colorMap[tid] =
236 String("/paired9/") + std::to_string(1 + colorMap.size() % 9);
237 }
238
239 return colorMap[tid];
240 };
241
242 auto avgTimeWorst = Scheduler::TaskTime::min();
243 for (auto task : mTasks) {
244 avgTimes[task] = mScheduler->getAveragedMeasurement(task);
245
246 if (avgTimes[task] > avgTimeWorst)
247 avgTimeWorst = avgTimes[task];
248 }
249
250 // TODO: For level-based Scheduler's we might want to
251 // maintain the level structure by setting the respective
252 // Graphviz 'rank' attributes and group each level into sub-graph
253
254 Graph::Graph g("dependencies", Graph::Type::directed);
255 for (auto task : mTasks) {
256 String name = task->toString();
257 String type = CPS::Utils::className(task.get(), "DPsim::");
258
259 auto *n = g.addNode(name);
260
261 std::stringstream label;
262
263 label << "<B>" << Utils::encodeXml(name) << "</B><BR/>";
264 label << "<FONT POINT-SIZE=\"10\" COLOR=\"gray28\">"
265 << Utils::encodeXml(type) << "<BR/>";
266
267 if (isScheduled(task))
268 label << "Avg. time: " << avgTimes[task].count() << "ns<BR/>";
269 else
270 label << "Unscheduled"
271 << "<BR/>";
272
273 label << "</FONT>";
274
275 n->set("color", getColor(task));
276 n->set("label", label.str(), true);
277 n->set("style", "rounded,filled,bold");
278 n->set("shape", "rectangle");
279
280 if (isScheduled(task)) {
281 if (avgTimeWorst > Scheduler::TaskTime(0)) {
282 auto grad = (float)avgTimes[task].count() / avgTimeWorst.count();
283 n->set("fillcolor", CPS::Utils::Rgb::gradient(grad).hex());
284 SPDLOG_LOGGER_INFO(mLog, "{} {}", task->toString(),
285 CPS::Utils::Rgb::gradient(grad).hex());
286 }
287 } else {
288 n->set("fillcolor", "white");
289 }
290 }
291 for (auto from : mTasks) {
292 for (auto to : mTaskOutEdges[from]) {
293 g.addEdge("", g.node(from->toString()), g.node(to->toString()));
294 }
295 }
296
297 g.set("splines", "ortho");
298 return g;
299}
300#endif
301
303 SPDLOG_LOGGER_INFO(mLog, "Initialize simulation: {}", **mName);
304 if (!mInitialized)
305 initialize();
306
307 for (auto lg : mLoggers)
308 lg->start();
309
310 SPDLOG_LOGGER_INFO(mLog, "Opening interfaces.");
311
312 for (auto intf : mInterfaces)
313 intf->open();
314
315 sync();
316
317 SPDLOG_LOGGER_INFO(mLog, "Start simulation: {}", **mName);
318 SPDLOG_LOGGER_INFO(mLog, "Time step: {:e}", **mTimeStep);
319 SPDLOG_LOGGER_INFO(mLog, "Final time: {:e}", **mFinalTime);
320
321 // In PF we dont log the initial conditions of the componentes because they are not calculated
322 // In dynamic simulations log initial values of attributes (t=0)
323 if (mSolverType != Solver::Type::NRP) {
324 if (mLoggers.size() > 0)
325 mLoggers[0]->log(0, 0);
326
327 // In dynamic simulations increase simulation time to calculate first results at t=timestep
328 mTime += **mTimeStep;
329 }
330
331 mSimulationStartTimePoint = std::chrono::steady_clock::now();
332}
333
335
336 mSimulationEndTimePoint = std::chrono::steady_clock::now();
339 SPDLOG_LOGGER_INFO(mLog, "Simulation calculation time: {:.6f}",
341
342 mScheduler->stop();
343
344 for (auto intf : mInterfaces)
345 intf->close();
346
347 for (auto lg : mLoggers)
348 lg->stop();
349
350 SPDLOG_LOGGER_INFO(mLog, "Simulation finished.");
351 mLog->flush();
352}
353
355 if (mTime < **mFinalTime + DOUBLE_EPSILON)
356 step();
357 else
358 stop();
359
360 return mTime;
361}
362
364 start();
365
366 while (mTime < **mFinalTime + DOUBLE_EPSILON) {
367 step();
368 }
369
370 stop();
371}
372
374 std::chrono::steady_clock::time_point start;
375 if (mLogStepTimes) {
376 start = std::chrono::steady_clock::now();
377 }
378
379 mEvents.handleEvents(mTime);
381
382 mTime += **mTimeStep;
384
385 if (mLogStepTimes) {
386 auto end = std::chrono::steady_clock::now();
387 std::chrono::duration<double> diff = end - start;
388 mStepTimes.push_back(diff.count());
389 }
390 return mTime;
391}
392
393void Simulation::logStepTimes(String logName) {
394 auto stepTimeLog = Logger::get(logName, Logger::Level::info);
395 if (!mLogStepTimes) {
396 SPDLOG_LOGGER_WARN(mLog, "Collection of step times has been disabled.");
397 return;
398 }
399 Logger::setLogPattern(stepTimeLog, "%v");
400 stepTimeLog->info("step_time");
401
402 Real stepTimeSum = 0;
403 for (auto meas : mStepTimes) {
404 stepTimeSum += meas;
405 stepTimeLog->info("{:.9f}", meas);
406 }
407 SPDLOG_LOGGER_INFO(mLog, "Average step time: {:.9f}",
408 stepTimeSum / mStepTimes.size());
409}
410
411void Simulation::checkForOverruns(String logName) {
412 auto stepTimeLog = Logger::get(logName, Logger::Level::info);
413 Logger::setLogPattern(stepTimeLog, "%v");
414 stepTimeLog->info("overruns");
415
416 int overruns = 0;
417 for (auto meas : mStepTimes) {
418 if (meas > **mTimeStep) {
419 overruns++;
420 SPDLOG_LOGGER_INFO(mLog, "overrun detected {}: {:.9f}", overruns, meas);
421 }
422 }
423 SPDLOG_LOGGER_INFO(mLog, "Detected {} overruns.", overruns);
424}
425
427 for (auto solver : mSolvers) {
428 solver->logLUTimes();
429 }
430}
431
432CPS::AttributeBase::Ptr Simulation::getIdObjAttribute(const String &comp,
433 const String &attr) {
434 IdentifiedObject::Ptr idObj = mSystem.component<IdentifiedObject>(comp);
435 if (!idObj) {
436 idObj = mSystem.node<TopologicalNode>(comp);
437 }
438
439 if (idObj) {
440 try {
441 CPS::AttributeBase::Ptr attrPtr = idObj->attribute(attr);
442 return attrPtr;
443 } catch (InvalidAttributeException &e) {
444 SPDLOG_LOGGER_ERROR(
445 mLog, "Attribute with name {} not found on component {}", attr, comp);
447 }
448 } else {
449 SPDLOG_LOGGER_ERROR(mLog, "Component or node with name {} not found", comp);
451 }
452}
453
454void Simulation::logIdObjAttribute(const String &comp, const String &attr) {
455 CPS::AttributeBase::Ptr attrPtr = getIdObjAttribute(comp, attr);
456 String name = comp + "." + attr;
457 logAttribute(name, attrPtr);
458}
459
460void Simulation::logAttribute(String name, CPS::AttributeBase::Ptr attr) {
461 if (mLoggers.size() > 0) {
462 mLoggers[0]->logAttribute(name, attr);
463 } else {
464 throw SystemError("Cannot log attributes when no logger is configured for "
465 "this simulation!");
466 }
467}
static std::shared_ptr< MnaSolver< VarType > > factory(String name, CPS::Domain domain=CPS::Domain::DP, CPS::Logger::Level logLevel=CPS::Logger::Level::info, DirectLinearSolverImpl implementation=mSupportedSolverImpls().back(), String pluginName="plugin.so")
sovlerImpl: choose the most advanced solver implementation available by default
std::chrono::steady_clock::duration TaskTime
Time measurement for the task execution.
Definition Scheduler.h:33
std::chrono::duration< double > mSimulationCalculationTime
Measured calculation time for simulation.
Definition Simulation.h:70
void logLUTimes()
Write LU decomposition times measurements to log file.
void sync() const
Synchronize simulation with remotes by exchanging intial state over interfaces.
CPS::IdentifiedObject::List mTearComponents
Definition Simulation.h:100
CPS::Logger::Level mLogLevel
Simulation log level.
Definition Simulation.h:74
virtual Real step()
Solve system A * x = z for x and current time.
Scheduler::Edges mTaskInEdges
Task dependencies as incoming / outgoing edges.
Definition Simulation.h:122
const CPS::Attribute< Real >::Ptr mTimeStep
Simulation timestep.
Definition Simulation.h:42
const CPS::Attribute< Bool >::Ptr mSteadyStateInit
Definition Simulation.h:53
Real next()
Run until next time step.
void logStepTimes(String logName)
Write step time measurements to log file.
String mSolverPluginName
If there we use a solver plugin, this specifies its name (excluding .so)
Definition Simulation.h:38
void initialize()
Create solver instances etc.
std::vector< Real > mStepTimes
(Real) time needed for the timesteps
Definition Simulation.h:76
const CPS::Attribute< Real >::Ptr mFinalTime
Final time of the simulation.
Definition Simulation.h:40
void prepSchedule()
Prepare schedule for simulation.
Int mTimeStepCount
Number of step which have been executed for this simulation.
Definition Simulation.h:59
Real mSteadStIniTimeLimit
steady state initialization time limit
Definition Simulation.h:112
CPS::Task::List mTasks
List of all tasks to be scheduled.
Definition Simulation.h:120
const CPS::Attribute< Bool >::Ptr mSplitSubnets
Definition Simulation.h:48
std::shared_ptr< Scheduler > mScheduler
Scheduler used for task scheduling.
Definition Simulation.h:118
std::vector< Interface::Ptr > mInterfaces
Vector of Interfaces.
Definition Simulation.h:125
Bool mLogStepTimes
activate collection of step times
Definition Simulation.h:78
void create()
Helper function for constructors.
CPS::SystemTopology mSystem
System list.
Definition Simulation.h:63
void checkForOverruns(String logName)
Check for overruns.
Simulation(String name, CommandLineArgs &args)
Creates simulation with name and CommandLineArgs.
CPS::AttributeBase::Ptr getIdObjAttribute(const String &comp, const String &attr)
CHECK: Can these be deleted? getIdObjAttribute + "**attr =" should suffice.
void start()
Start simulation without advancing in time.
EventQueue mEvents
The simulation event queue.
Definition Simulation.h:61
void schedule()
Create the schedule for the independent tasks.
std::chrono::time_point< std::chrono::steady_clock > mSimulationStartTimePoint
Start time point to measure calculation time.
Definition Simulation.h:66
Real mTime
Time variable that is incremented at every step.
Definition Simulation.h:57
Bool mSystemMatrixRecomputation
Enable recomputation of system matrix during simulation.
Definition Simulation.h:96
Real mSteadStIniAccLimit
steady state initialization accuracy limit
Definition Simulation.h:114
void createMNASolver()
Subroutine for MNA only because there are many MNA options.
void run()
Run simulation until total time is elapsed.
void logAttribute(String name, CPS::AttributeBase::Ptr attr)
CHECK: Can we store the attribute name / UID intrinsically inside the attribute?
DataLoggerInterface::List mLoggers
The data loggers.
Definition Simulation.h:135
void createSolvers()
Create solvers depending on simulation settings.
const CPS::Attribute< String >::Ptr mName
Simulation name.
Definition Simulation.h:36
CPS::Logger::Log mLog
Simulation logger.
Definition Simulation.h:151
void stop()
Stop simulation including scheduler and interfaces.
std::chrono::time_point< std::chrono::steady_clock > mSimulationEndTimePoint
End time point to measure calculation time.
Definition Simulation.h:68