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 <stdexcept>
7#include <typeindex>
8
9#include <dpsim-models/Utils.h>
10#include <dpsim/DiakopticsSolver.h>
11#include <dpsim/MNASolver.h>
12#include <dpsim/MNASolverFactory.h>
13#include <dpsim/PFSolverPowerPolar.h>
14#include <dpsim/PFSolverPowerPolarSparse.h>
15#include <dpsim/SequentialScheduler.h>
16#include <dpsim/Simulation.h>
17#include <dpsim/Utils.h>
18
19#include <spdlog/sinks/stdout_color_sinks.h>
20
21#ifdef WITH_CIM
22#include <dpsim-models/CIM/Reader.h>
23#endif
24
25#ifdef WITH_SUNDIALS
26#include <dpsim-models/Solver/ODEInterface.h>
27#include <dpsim/DAESolver.h>
28#include <dpsim/ODESolver.h>
29#endif
30
31using namespace CPS;
32using namespace DPsim;
33
34Simulation::Simulation(String name, Logger::Level logLevel)
35 : mName(AttributeStatic<String>::make(name)),
36 mFinalTime(AttributeStatic<Real>::make(0.001)),
37 mTimeStep(AttributeStatic<Real>::make(0.001)),
38 mPFKeepLastSolution(CPS::AttributeStatic<Bool>::make(false)),
39 mPFSolverUseSparse(CPS::AttributeStatic<Bool>::make(false)),
40 mSplitSubnets(AttributeStatic<Bool>::make(true)),
41 mSteadyStateInit(AttributeStatic<Bool>::make(false)),
42 mLogLevel(logLevel) {
43 create();
44}
45
47 : mName(AttributeStatic<String>::make(name)),
48 mSolverPluginName(args.solverPluginName),
49 mFinalTime(AttributeStatic<Real>::make(args.duration)),
50 mTimeStep(AttributeStatic<Real>::make(args.timeStep)),
51 mPFKeepLastSolution(CPS::AttributeStatic<Bool>::make(false)),
52 mPFSolverUseSparse(CPS::AttributeStatic<Bool>::make(false)),
53 mSplitSubnets(AttributeStatic<Bool>::make(true)),
54 mSteadyStateInit(AttributeStatic<Bool>::make(false)),
55 mLogLevel(args.logLevel), mDomain(args.solver.domain),
56 mSolverType(args.solver.type), mDirectImpl(args.directImpl) {
57 create();
58}
59
61 // Logging
62 mLog =
63 Logger::get(**mName, mLogLevel, std::max(Logger::Level::info, mLogLevel));
64
65 Eigen::setNbThreads(1);
66
67 mInitialized = false;
68}
69
71 if (mInitialized)
72 return;
73
74 mSolvers.clear();
75
76 switch (mDomain) {
77 case Domain::SP:
78 // Treat SP as DP
79 case Domain::DP:
81 break;
82 case Domain::EMT:
84 break;
85 }
86
87 mTime = 0;
89
90 schedule();
91
92 mInitialized = true;
93}
94
95template <typename VarType> void Simulation::createSolvers() {
96 Solver::Ptr solver;
97
98 switch (mSolverType) {
99 case Solver::Type::MNA:
101 break;
102
103#ifdef WITH_SUNDIALS
104 case Solver::Type::DAE:
105 solver = std::make_shared<DAESolver>(**mName, mSystem, **mTimeStep, 0.0);
106 mSolvers.push_back(solver);
107 break;
108#endif /* WITH_SUNDIALS */
109
110 case Solver::Type::NRP: {
111 std::shared_ptr<PFSolver> pfSolver;
112#if defined(WITH_KLU) || defined(WITH_SPARSE)
113 if (**mPFSolverUseSparse)
114 pfSolver = std::make_shared<PFSolverPowerPolarSparse>(
116 else
117 pfSolver = std::make_shared<PFSolverPowerPolar>(**mName, mSystem,
119#else
120 // No sparse linear solver available: fall back to the dense solver.
121 pfSolver = std::make_shared<PFSolverPowerPolar>(**mName, mSystem,
123#endif
124
125 pfSolver->setKeepLastSolution(**mPFKeepLastSolution);
126
127 solver = pfSolver;
128
129 solver->doInitFromNodesAndTerminals(mInitFromNodesAndTerminals);
130 solver->setSolverAndComponentBehaviour(mSolverBehaviour);
131 solver->initialize();
132
133 mSolvers.push_back(solver);
134 break;
135 }
136
137 default:
139 }
140
141 // Some components require a dedicated ODE solver.
142 // This solver is independent of the system solver.
143#ifdef WITH_SUNDIALS
144 for (auto comp : mSystem.mComponents) {
145 auto odeComp = std::dynamic_pointer_cast<ODEInterface>(comp);
146 if (odeComp) {
147 // TODO explicit / implicit integration
148 auto odeSolver = std::make_shared<ODESolver>(
149 odeComp->mAttributeList->attributeTyped<String>("name")->get() +
150 "_ODE",
151 odeComp, false, **mTimeStep);
152 mSolvers.push_back(odeSolver);
153 }
154 }
155#endif /* WITH_SUNDIALS */
156}
157
158template <typename VarType> void Simulation::createMNASolver() {
159 Solver::Ptr solver;
160 std::vector<SystemTopology> subnets;
161 // The Diakoptics solver splits the system at a later point.
162 // That is why the system is not split here if tear components exist.
163 if (**mSplitSubnets && mTearComponents.size() == 0)
164 mSystem.splitSubnets<VarType>(subnets);
165 else
166 subnets.push_back(mSystem);
167
168 for (UInt net = 0; net < subnets.size(); ++net) {
169 String copySuffix;
170 if (subnets.size() > 1)
171 copySuffix = "_" + std::to_string(net);
172
173 // TODO: In the future, here we could possibly even use different
174 // solvers for different subnets if deemed useful
175 if (mTearComponents.size() > 0) {
177 throw std::logic_error("MNA state-space extraction does not support "
178 "Diakoptics/tearing.");
179 }
180 // Tear components available, use diakoptics
181 solver = std::make_shared<DiakopticsSolver<VarType>>(
182 **mName, subnets[net], mTearComponents, **mTimeStep, mLogLevel);
183 } else {
184 // Default case with lu decomposition from mna factory
185 auto mnaSolver = MnaSolverFactory::factory<VarType>(
186 **mName + copySuffix, mDomain, mLogLevel, mDirectImpl,
188
189 mnaSolver->doStateSpaceExtraction(mStateSpaceExtraction);
190
191 solver = mnaSolver;
192 solver->setTimeStep(**mTimeStep);
193 solver->setLogSolveTimes(mLogStepTimes);
194 solver->doSteadyStateInit(**mSteadyStateInit);
195 solver->doFrequencyParallelization(mFreqParallel);
196 solver->setSteadStIniTimeLimit(mSteadStIniTimeLimit);
197 solver->setSteadStIniAccLimit(mSteadStIniAccLimit);
198 solver->setSystem(subnets[net]);
199 solver->setSolverAndComponentBehaviour(mSolverBehaviour);
200 solver->doInitFromNodesAndTerminals(mInitFromNodesAndTerminals);
201 solver->doSystemMatrixRecomputation(mSystemMatrixRecomputation);
202 solver->setDirectLinearSolverConfiguration(
203 mDirectLinearSolverConfiguration);
204 solver->initialize();
205 solver->setMaxNumberOfIterations(mMaxIterations);
206 }
207 mSolvers.push_back(solver);
208 }
209}
210
211void Simulation::sync() const {
212 SPDLOG_LOGGER_INFO(mLog, "Start synchronization with remotes on interfaces");
213
214 for (auto intf : mInterfaces) {
215 intf->syncExports();
216 intf->syncImports();
217 intf->syncExports();
218 }
219
220 SPDLOG_LOGGER_INFO(mLog, "Synchronized simulation start with remotes");
221}
222
224 mTasks.clear();
225 mTaskOutEdges.clear();
226 mTaskInEdges.clear();
227 for (auto solver : mSolvers) {
228 for (auto t : solver->getTasks()) {
229 mTasks.push_back(t);
230 }
231 }
232
233 for (auto intf : mInterfaces) {
234 for (auto t : intf->getTasks()) {
235 mTasks.push_back(t);
236 }
237 }
238
239 for (auto logger : mLoggers) {
240 mTasks.push_back(logger->getTask());
241 }
242 if (!mScheduler) {
243 mScheduler = std::make_shared<SequentialScheduler>();
244 }
245 mScheduler->resolveDeps(mTasks, mTaskInEdges, mTaskOutEdges);
246}
247
249 SPDLOG_LOGGER_INFO(mLog, "Scheduling tasks.");
250 prepSchedule();
251 mScheduler->createSchedule(mTasks, mTaskInEdges, mTaskOutEdges);
252 SPDLOG_LOGGER_INFO(mLog, "Scheduling done.");
253}
254
255#ifdef WITH_GRAPHVIZ
256Graph::Graph Simulation::dependencyGraph() {
257 if (!mInitialized)
258 initialize();
259
260 std::map<CPS::Task::Ptr, Scheduler::TaskTime> avgTimes;
261 std::map<CPS::Task::Ptr, String> fillColors;
262
263 auto isScheduled = [this](Task::Ptr task) -> Bool {
264 return !mTaskOutEdges[task].empty();
265 };
266
267 auto getColor = [](Task::Ptr task) -> String {
268 static std::map<std::type_index, String> colorMap;
269 auto tid = std::type_index(typeid(task.get()));
270
271 if (colorMap.find(tid) != colorMap.end()) {
272 colorMap[tid] =
273 String("/paired9/") + std::to_string(1 + colorMap.size() % 9);
274 }
275
276 return colorMap[tid];
277 };
278
279 auto avgTimeWorst = Scheduler::TaskTime::min();
280 for (auto task : mTasks) {
281 avgTimes[task] = mScheduler->getAveragedMeasurement(task);
282
283 if (avgTimes[task] > avgTimeWorst)
284 avgTimeWorst = avgTimes[task];
285 }
286
287 // TODO: For level-based Scheduler's we might want to
288 // maintain the level structure by setting the respective
289 // Graphviz 'rank' attributes and group each level into sub-graph
290
291 Graph::Graph g("dependencies", Graph::Type::directed);
292 for (auto task : mTasks) {
293 String name = task->toString();
294 String type = CPS::Utils::className(task.get(), "DPsim::");
295
296 auto *n = g.addNode(name);
297
298 std::stringstream label;
299
300 label << "<B>" << Utils::encodeXml(name) << "</B><BR/>";
301 label << "<FONT POINT-SIZE=\"10\" COLOR=\"gray28\">"
302 << Utils::encodeXml(type) << "<BR/>";
303
304 if (isScheduled(task))
305 label << "Avg. time: " << avgTimes[task].count() << "ns<BR/>";
306 else
307 label << "Unscheduled"
308 << "<BR/>";
309
310 label << "</FONT>";
311
312 n->set("color", getColor(task));
313 n->set("label", label.str(), true);
314 n->set("style", "rounded,filled,bold");
315 n->set("shape", "rectangle");
316
317 if (isScheduled(task)) {
318 if (avgTimeWorst > Scheduler::TaskTime(0)) {
319 auto grad = (float)avgTimes[task].count() / avgTimeWorst.count();
320 n->set("fillcolor", CPS::Utils::Rgb::gradient(grad).hex());
321 SPDLOG_LOGGER_INFO(mLog, "{} {}", task->toString(),
322 CPS::Utils::Rgb::gradient(grad).hex());
323 }
324 } else {
325 n->set("fillcolor", "white");
326 }
327 }
328 for (auto from : mTasks) {
329 for (auto to : mTaskOutEdges[from]) {
330 g.addEdge("", g.node(from->toString()), g.node(to->toString()));
331 }
332 }
333
334 g.set("splines", "ortho");
335 return g;
336}
337#endif
338
339void Simulation::setPFKeepLastSolution(Bool value) {
340 **mPFKeepLastSolution = value;
341}
342
343Bool Simulation::getPFKeepLastSolution() const { return **mPFKeepLastSolution; }
344
345void Simulation::setPFSolverUseSparse(Bool value) {
346 **mPFSolverUseSparse = value;
347}
348
349Bool Simulation::getPFSolverUseSparse() const { return **mPFSolverUseSparse; }
350
352 SPDLOG_LOGGER_INFO(mLog, "Initialize simulation: {}", **mName);
353 if (!mInitialized)
354 initialize();
355
356 for (auto lg : mLoggers)
357 lg->start();
358
359 SPDLOG_LOGGER_INFO(mLog, "Opening interfaces.");
360
361 for (auto intf : mInterfaces)
362 intf->open();
363
364 sync();
365
366 SPDLOG_LOGGER_INFO(mLog, "Start simulation: {}", **mName);
367 SPDLOG_LOGGER_INFO(mLog, "Time step: {:e}", **mTimeStep);
368 SPDLOG_LOGGER_INFO(mLog, "Final time: {:e}", **mFinalTime);
369
370 // In PF we dont log the initial conditions of the componentes because they are not calculated
371 // In dynamic simulations log initial values of attributes (t=0)
372 if (mSolverType != Solver::Type::NRP) {
373 if (mLoggers.size() > 0)
374 mLoggers[0]->log(0, 0);
375
376 // In dynamic simulations increase simulation time to calculate first results at t=timestep
377 mTime += **mTimeStep;
378 }
379
380 mSimulationStartTimePoint = std::chrono::steady_clock::now();
381}
382
384
385 mSimulationEndTimePoint = std::chrono::steady_clock::now();
388 SPDLOG_LOGGER_INFO(mLog, "Simulation calculation time: {:.6f}",
390
391 mScheduler->stop();
392
393 for (auto intf : mInterfaces)
394 intf->close();
395
396 for (auto lg : mLoggers)
397 lg->stop();
398
399 SPDLOG_LOGGER_INFO(mLog, "Simulation finished.");
400 mLog->flush();
401}
402
404 if (mTime < **mFinalTime + DOUBLE_EPSILON)
405 step();
406 else
407 stop();
408
409 return mTime;
410}
411
413 start();
414
415 while (mTime < **mFinalTime + DOUBLE_EPSILON) {
416 step();
417 }
418
419 stop();
420}
421
423 std::chrono::steady_clock::time_point start;
424 if (mLogStepTimes) {
425 start = std::chrono::steady_clock::now();
426 }
427
428 mEvents.handleEvents(mTime);
430
431 mTime += **mTimeStep;
433
434 if (mLogStepTimes) {
435 auto end = std::chrono::steady_clock::now();
436 std::chrono::duration<double> diff = end - start;
437 mStepTimes.push_back(diff.count());
438 }
439 return mTime;
440}
441
442void Simulation::logStepTimes(String logName) {
443 auto stepTimeLog = Logger::get(logName, Logger::Level::info);
444 if (!mLogStepTimes) {
445 SPDLOG_LOGGER_WARN(mLog, "Collection of step times has been disabled.");
446 return;
447 }
448 Logger::setLogPattern(stepTimeLog, "%v");
449 SPDLOG_LOGGER_INFO(stepTimeLog, "step_time");
450
451 Real stepTimeSum = 0;
452 for (auto meas : mStepTimes) {
453 stepTimeSum += meas;
454 SPDLOG_LOGGER_INFO(stepTimeLog, "{:.9f}", meas);
455 }
456 SPDLOG_LOGGER_INFO(mLog, "Average step time: {:.9f}",
457 stepTimeSum / mStepTimes.size());
458}
459
460void Simulation::checkForOverruns(String logName) {
461 auto stepTimeLog = Logger::get(logName, Logger::Level::info);
462 Logger::setLogPattern(stepTimeLog, "%v");
463 SPDLOG_LOGGER_INFO(stepTimeLog, "overruns");
464
465 int overruns = 0;
466 for (auto meas : mStepTimes) {
467 if (meas > **mTimeStep) {
468 overruns++;
469 SPDLOG_LOGGER_INFO(mLog, "overrun detected {}: {:.9f}", overruns, meas);
470 }
471 }
472 SPDLOG_LOGGER_INFO(mLog, "Detected {} overruns.", overruns);
473}
474
476 for (auto solver : mSolvers) {
477 solver->logLUTimes();
478 }
479}
480
482Simulation::getStateSpaceExtractor(UInt solverIndex) const {
483 if (solverIndex >= mSolvers.size()) {
484 throw std::out_of_range(
485 "Simulation::getStateSpaceExtractor(): solver index out of range.");
486 }
487
488 const auto mnaSolver =
489 std::dynamic_pointer_cast<MnaSolver<Real>>(mSolvers[solverIndex]);
490
491 if (!mnaSolver) {
492 throw std::logic_error(
493 "Simulation::getStateSpaceExtractor(): selected solver is not a "
494 "real-valued MNA solver.");
495 }
496
497 return mnaSolver->getStateSpaceExtractor();
498}
499
500CPS::AttributeBase::Ptr Simulation::getIdObjAttribute(const String &comp,
501 const String &attr) {
502 IdentifiedObject::Ptr idObj = mSystem.component<IdentifiedObject>(comp);
503 if (!idObj) {
504 idObj = mSystem.node<TopologicalNode>(comp);
505 }
506
507 if (idObj) {
508 try {
509 CPS::AttributeBase::Ptr attrPtr = idObj->attribute(attr);
510 return attrPtr;
511 } catch (InvalidAttributeException &e) {
512 SPDLOG_LOGGER_ERROR(
513 mLog, "Attribute with name {} not found on component {}", attr, comp);
515 }
516 } else {
517 SPDLOG_LOGGER_ERROR(mLog, "Component or node with name {} not found", comp);
519 }
520}
521
522void Simulation::logIdObjAttribute(const String &comp, const String &attr) {
523 CPS::AttributeBase::Ptr attrPtr = getIdObjAttribute(comp, attr);
524 String name = comp + "." + attr;
525 logAttribute(name, attrPtr);
526}
527
528void Simulation::logAttribute(String name, CPS::AttributeBase::Ptr attr) {
529 if (mLoggers.size() > 0) {
530 mLoggers[0]->logAttribute(name, attr);
531 } else {
532 throw SystemError("Cannot log attributes when no logger is configured for "
533 "this simulation!");
534 }
535}
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
Bool mStateSpaceExtraction
Enable extraction of the MNA-coupled discrete-time state matrix.
Definition Simulation.h:105
std::chrono::duration< double > mSimulationCalculationTime
Measured calculation time for simulation.
Definition Simulation.h:77
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:109
CPS::Logger::Level mLogLevel
Simulation log level.
Definition Simulation.h:81
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:131
const CPS::Attribute< Real >::Ptr mTimeStep
Simulation timestep.
Definition Simulation.h:42
const CPS::Attribute< Bool >::Ptr mSteadyStateInit
Definition Simulation.h:60
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:83
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:66
Real mSteadStIniTimeLimit
steady state initialization time limit
Definition Simulation.h:121
CPS::Task::List mTasks
List of all tasks to be scheduled.
Definition Simulation.h:129
const CPS::Attribute< Bool >::Ptr mSplitSubnets
Definition Simulation.h:55
std::shared_ptr< Scheduler > mScheduler
Scheduler used for task scheduling.
Definition Simulation.h:127
std::vector< Interface::Ptr > mInterfaces
Vector of Interfaces.
Definition Simulation.h:134
Bool mLogStepTimes
activate collection of step times
Definition Simulation.h:85
void create()
Helper function for constructors.
CPS::SystemTopology mSystem
System list.
Definition Simulation.h:70
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:68
void schedule()
Create the schedule for the independent tasks.
const CPS::Attribute< Bool >::Ptr mPFSolverUseSparse
Definition Simulation.h:49
std::chrono::time_point< std::chrono::steady_clock > mSimulationStartTimePoint
Start time point to measure calculation time.
Definition Simulation.h:73
Real mTime
Time variable that is incremented at every step.
Definition Simulation.h:64
Bool mSystemMatrixRecomputation
Enable recomputation of system matrix during simulation.
Definition Simulation.h:103
Real mSteadStIniAccLimit
steady state initialization accuracy limit
Definition Simulation.h:123
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:144
const MNAStateSpaceExtractor & getStateSpaceExtractor(UInt solverIndex=0) const
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:160
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:75