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