Simulation.cpp 17.1 KB
Newer Older
1
2
/* Copyright 2017-2020 Institute for Automation of Complex Power Systems,
 *                     EONERC, RWTH Aachen University
Steffen Vogel's avatar
Steffen Vogel committed
3
4
5
6
7
8
9
10
 * DPsim
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Steffen Vogel's avatar
Steffen Vogel committed
12
13
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Steffen Vogel's avatar
Steffen Vogel committed
15
 *********************************************************************************/
16

17
#include <chrono>
Markus Mirz's avatar
Markus Mirz committed
18
19
20
#include <iomanip>
#include <algorithm>
#include <typeindex>
21

22
#include <dpsim/SequentialScheduler.h>
23
#include <dpsim/Simulation.h>
Markus Mirz's avatar
Markus Mirz committed
24
25
#include <dpsim/Utils.h>
#include <cps/Utils.h>
Philipp Fensch's avatar
Philipp Fensch committed
26
#include <dpsim/MNASolver.h>
27
#include <dpsim/MNASolverSysRecomp.h>
28
#include <dpsim/PFSolverPowerPolar.h>
Markus Mirz's avatar
Markus Mirz committed
29
#include <dpsim/DiakopticsSolver.h>
30

31
32
#include <spdlog/sinks/stdout_color_sinks.h>

33
#ifdef WITH_CIM
34
  #include <cps/CIM/Reader.h>
35
#endif
36

37
#ifdef WITH_SUNDIALS
38
  #include <cps/Solver/ODEInterface.h>
Steffen Vogel's avatar
Steffen Vogel committed
39
  #include <dpsim/DAESolver.h>
40
  #include <dpsim/ODESolver.h>
41
42
#endif

Philipp Fensch's avatar
Philipp Fensch committed
43
#ifdef WITH_CUDA
Philipp Fensch's avatar
Philipp Fensch committed
44
45
46
47
48
#ifdef WITH_SPARSE
	#include <dpsim/MNASolverGpuSparse.h>
#else
	#include <dpsim/MNASolverGpuDense.h>
#endif
Philipp Fensch's avatar
Philipp Fensch committed
49
50
#endif

Steffen Vogel's avatar
Steffen Vogel committed
51
using namespace CPS;
52
using namespace DPsim;
53

54
55
56
Simulation::Simulation(String name,	Logger::Level logLevel) :
	mName(name), mLogLevel(logLevel) {

57
	addAttribute<String>("name", &mName, Flags::read);
58
	addAttribute<Real>("time_step", &mTimeStep, Flags::read);
59
	addAttribute<Real>("final_time", &mFinalTime, Flags::read|Flags::write);
60
61
	addAttribute<Bool>("steady_state_init", &mSteadyStateInit, Flags::read|Flags::write);
	addAttribute<Bool>("split_subnets", &mSplitSubnets, Flags::read|Flags::write);
Markus Mirz's avatar
Markus Mirz committed
62
63
	addAttribute<Real>("time_step", &mTimeStep, Flags::read);

64
	Eigen::setNbThreads(1);
65
66

	// Logging
67
	mLog = Logger::get(name, logLevel, std::max(Logger::Level::info, logLevel));
68

69
	mInitialized = false;
70
}
Markus Mirz's avatar
Markus Mirz committed
71

Markus Mirz's avatar
Markus Mirz committed
72
73
Simulation::Simulation(String name, SystemTopology system,
	Real timeStep, Real finalTime,
74
	Domain domain, Solver::Type solverType,
75
	Logger::Level logLevel,
Markus Mirz's avatar
Markus Mirz committed
76
	Bool initFromNodesAndTerminals,
77
	Bool steadyStateInit,
78
	Bool splitSubnets,
79
	IdentifiedObject::List tearComponents) :
80
81
82
83
84
	Simulation(name, logLevel) {

	mTimeStep = timeStep;
	mFinalTime = finalTime;
	mDomain = domain;
85
86
	mSystem = system;
	mSolverType = solverType;
Markus Mirz's avatar
Markus Mirz committed
87
	mInitFromNodesAndTerminals = initFromNodesAndTerminals;
88
89
90
	mSteadyStateInit = steadyStateInit;
	mSplitSubnets = splitSubnets;
	mTearComponents = tearComponents;
91

92
	mInitialized = false;
93
94
}

95
void Simulation::initialize() {
96
97
98
99
	if (mInitialized)
		return;

	mSolvers.clear();
100
101

	switch (mDomain) {
102
103
	case Domain::SP:
		// Treat SP as DP
104
	case Domain::DP:
105
		createSolvers<Complex>();
106
107
		break;
	case Domain::EMT:
108
		createSolvers<Real>();
109
110
		break;
	}
111

112
113
114
115
116
	mTime = 0;
	mTimeStepCount = 0;

	schedule();

117
	mInitialized = true;
118
}
119

120
template <typename VarType>
121
122
123
124
125
126
127
128
129
130
131
132
133
134
void Simulation::createSolvers() {
	Solver::Ptr solver;
	switch (mSolverType) {
		case Solver::Type::MNA:
			createMNASolver<VarType>();
			break;
#ifdef WITH_SUNDIALS
		case Solver::Type::DAE:
			solver = std::make_shared<DAESolver>(mName, mSystem, mTimeStep, 0.0);
			mSolvers.push_back(solver);
			break;
#endif /* WITH_SUNDIALS */
		case Solver::Type::NRP:
			solver = std::make_shared<PFSolverPowerPolar>(mName, mSystem, mTimeStep, mLogLevel);
Markus Mirz's avatar
Markus Mirz committed
135
			solver->doInitFromNodesAndTerminals(mInitFromNodesAndTerminals);
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
			solver->initialize();
			mSolvers.push_back(solver);
			break;
		default:
			throw UnsupportedSolverException();
	}

	// Some components require a dedicated ODE solver.
	// This solver is independent of the system solver.
#ifdef WITH_SUNDIALS
	for (auto comp : mSystem.mComponents) {
		auto odeComp = std::dynamic_pointer_cast<ODEInterface>(comp);
		if (odeComp) {
			// TODO explicit / implicit integration
			auto odeSolver = std::make_shared<ODESolver>(
				odeComp->attribute<String>("name")->get() + "_ODE", odeComp, false, mTimeStep);
			mSolvers.push_back(odeSolver);
		}
	}
#endif /* WITH_SUNDIALS */
}
157

158
159
160
template <typename VarType>
void Simulation::createMNASolver() {
	Solver::Ptr solver;
161
	std::vector<SystemTopology> subnets;
162
163
	// The Diakoptics solver splits the system at a later point.
	// That is why the system is not split here if tear components exist.
164
165
	if (mSplitSubnets && mTearComponents.size() == 0)
		mSystem.splitSubnets<VarType>(subnets);
166
	else
167
		subnets.push_back(mSystem);
168

169
	for (UInt net = 0; net < subnets.size(); ++net) {
170
171
172
173
		String copySuffix;
	   	if (subnets.size() > 1)
			copySuffix = "_" + std::to_string(net);

174
		// TODO: In the future, here we could possibly even use different
175
		// solvers for different subnets if deemed useful
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
		if (mTearComponents.size() > 0) {
			// Tear components available, use diakoptics
			solver = std::make_shared<DiakopticsSolver<VarType>>(mName,
				subnets[net], mTearComponents, mTimeStep, mLogLevel);
		}
		else if (mSystemMatrixRecomputation) {
			// Recompute system matrix if switches or other components change
			solver = std::make_shared<MnaSolverSysRecomp<VarType>>(
				mName + copySuffix, mDomain, mLogLevel);
			solver->setTimeStep(mTimeStep);
			solver->doSteadyStateInit(mSteadyStateInit);
			solver->setSteadStIniTimeLimit(mSteadStIniTimeLimit);
			solver->setSteadStIniAccLimit(mSteadStIniAccLimit);
			solver->setSystem(subnets[net]);
			solver->initialize();
		}
		else {
			// Default case with precomputed system matrices for different configurations
194
#ifdef WITH_CUDA
Philipp Fensch's avatar
Philipp Fensch committed
195
196
#ifdef WITH_SPARSE
			solver = std::make_shared<MnaSolverGpuSparse<VarType>>(
197
				mName + copySuffix, mDomain, mLogLevel);
Philipp Fensch's avatar
Philipp Fensch committed
198
199
200
201
#else
			solver = std::make_shared<MnaSolverGpuDense<VarType>>(
				mName + copySuffix, mDomain, mLogLevel);
#endif
202
#else
203
204
			solver = std::make_shared<MnaSolver<VarType>>(
				mName + copySuffix, mDomain, mLogLevel);
205
#endif /* WITH_CUDA */
206
207
208
209
210
211
212
			solver->setTimeStep(mTimeStep);
			solver->doSteadyStateInit(mSteadyStateInit);
			solver->doFrequencyParallelization(mFreqParallel);
			solver->setSteadStIniTimeLimit(mSteadStIniTimeLimit);
			solver->setSteadStIniAccLimit(mSteadStIniAccLimit);
			solver->setSystem(subnets[net]);
			solver->initialize();
213
		}
214
		mSolvers.push_back(solver);
215
	}
216
}
217

218
void Simulation::sync() {
219
220
#ifdef WITH_SHMEM

221
222
	int numOfSyncInterfaces = std::count_if(mInterfaces.begin(), mInterfaces.end(), [](InterfaceMapping ifm) {return ifm.syncStart;});
	mLog->info("Start synchronization with remotes on {} interfaces", numOfSyncInterfaces);
223

224
	for (auto ifm : mInterfaces) {
225
226
227
		if(ifm.syncStart) {
			// Send initial state over interface
			ifm.interface->writeValues();
228

229
230
231
232
233
			// Blocking wait for interface
			ifm.interface->readValues(ifm.syncStart);

			ifm.interface->writeValues();
		}
Steffen Vogel's avatar
Steffen Vogel committed
234
235
	}

236
	mLog->info("Synchronized simulation start with remotes");
237
#endif
238
239
}

240
void Simulation::prepSchedule() {
241
242
243
	mTasks.clear();
	mTaskOutEdges.clear();
	mTaskInEdges.clear();
244
245
246
247
248
	for (auto solver : mSolvers) {
		for (auto t : solver->getTasks()) {
			mTasks.push_back(t);
		}
	}
249
250
251
#ifdef WITH_SHMEM
	for (auto intfm : mInterfaces) {
		for (auto t : intfm.interface->getTasks()) {
252
			mTasks.push_back(t);
253
254
255
		}
	}
#endif
256
	for (auto logger : mLoggers) {
257
258
		mTasks.push_back(logger->getTask());
	}
259
260
261
262
	if (!mScheduler) {
		mScheduler = std::make_shared<SequentialScheduler>();
	}
	mScheduler->resolveDeps(mTasks, mTaskInEdges, mTaskOutEdges);
263
264
265
}

void Simulation::schedule() {
266
	mLog->info("Scheduling tasks.");
267
	prepSchedule();
268
	mScheduler->createSchedule(mTasks, mTaskInEdges, mTaskOutEdges);
269
	mLog->info("Scheduling done.");
270
271
}

272
#ifdef WITH_GRAPHVIZ
Markus Mirz's avatar
Markus Mirz committed
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
Graph::Graph Simulation::dependencyGraph() {
	if (!mInitialized)
		initialize();

	std::map<CPS::Task::Ptr, Scheduler::TaskTime> avgTimes;
	std::map<CPS::Task::Ptr, String> fillColors;

	/* The main SolveTasks of each Solver usually takes the
	 * largest amount of computing time. We exclude it from
	 * coloring for improving the spread of the color range
	 * for the remaining tasks.
	 */
// 	auto isSolveTask = [](Task::Ptr task) -> Bool {
// 		const std::vector<std::type_index> ignoreTasks = {
// #ifdef WITH_SUNDIALS
// 			std::type_index(typeid(ODESolver::SolveTask)),
// #endif
// 			std::type_index(typeid(MnaSolver<Real>::SolveTask)),
// 			std::type_index(typeid(MnaSolver<Complex>::SolveTask)),
// 			std::type_index(typeid(DiakopticsSolver<Real>::SubnetSolveTask)),
// 			std::type_index(typeid(DiakopticsSolver<Complex>::SubnetSolveTask)),
// 			std::type_index(typeid(DiakopticsSolver<Real>::SolveTask)),
// 			std::type_index(typeid(DiakopticsSolver<Complex>::SolveTask)),
// 			std::type_index(typeid(NRpolarSolver::SolveTask))
// 		};

// 		return std::find(ignoreTasks.begin(), ignoreTasks.end(), std::type_index(typeid(*task.get()))) != ignoreTasks.end();
// 	};

	// auto isIgnoredTask = [&isSolveTask](Task::Ptr task) -> Bool {
	// 	return typeid(*task.get()) == typeid(Interface::PreStep) || sSolveTask(task);
	// };

	// auto isRootTask = [](Task::Ptr task) -> Bool {
	// 	return typeid(*task.get()) == typeid(Scheduler::Root);
	// };

	auto isScheduled = [this](Task::Ptr task) -> Bool {
		return ! mTaskOutEdges[task].empty();
	};

	auto getColor = [](Task::Ptr task) -> String {
		static std::map<std::type_index, String> colorMap;
		auto tid = std::type_index(typeid(*task.get()));

		if (colorMap.find(tid) != colorMap.end()) {
			colorMap[tid] = String("/paired9/") + std::to_string(1 + colorMap.size() % 9);
		}

		return colorMap[tid];
	};

	auto avgTimeWorst = Scheduler::TaskTime::min();
	for (auto task : mTasks) {
		avgTimes[task] = mScheduler->getAveragedMeasurement(task);

		if (avgTimes[task] > avgTimeWorst)
			avgTimeWorst = avgTimes[task];
	}

	// TODO: For level-based Scheduler's we might want to
	//       maintain the level structure by setting the respective
	//       Graphviz 'rank' attributes and group each level into sub-graph
336
337
338

	Graph::Graph g("dependencies", Graph::Type::directed);
	for (auto task : mTasks) {
Markus Mirz's avatar
Markus Mirz committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
		String name = task->toString();
		String type = CPS::Utils::className(task.get(), "DPsim::");

		auto *n = g.addNode(name);

		std::stringstream label;

		label << "<B>" << Utils::encodeXml(name) << "</B><BR/>";
		label << "<FONT POINT-SIZE=\"10\" COLOR=\"gray28\">"
		      << Utils::encodeXml(type) << "<BR/>";

		if (isScheduled(task))
			label << "Avg. time: " << avgTimes[task].count() << "ns<BR/>";
		else
			label << "Unscheduled" << "<BR/>";

		label <<"</FONT>";

		n->set("color", getColor(task));
		n->set("label", label.str(), true);
		n->set("style", "rounded,filled,bold");
		n->set("shape", "rectangle");

		// if (isSolveTask(task)) {
		// 	n->set("fillcolor", "orange");
		// }
		// if (isRootTask(task)) {
		// 	n->set("fillcolor", "springgreen1");
		// }
		if (isScheduled(task)) {
			if (avgTimeWorst > Scheduler::TaskTime(0)) {
				auto grad = (float) avgTimes[task].count() / avgTimeWorst.count();
				n->set("fillcolor", CPS::Utils::Rgb::gradient(grad).hex());
372
				mLog->info("{} {}", task->toString(), CPS::Utils::Rgb::gradient(grad).hex());
Markus Mirz's avatar
Markus Mirz committed
373
374
375
376
377
378
			}
		}
		else {
			n->set("fillcolor", "white");
		}

379
380
381
382
383
384
385
	}
	for (auto from : mTasks) {
		for (auto to : mTaskOutEdges[from]) {
			g.addEdge("", g.node(from->toString()), g.node(to->toString()));
		}
	}

Markus Mirz's avatar
Markus Mirz committed
386
387
	g.set("splines", "ortho");
	return g;
388
}
389
#endif
390

391
void Simulation::start() {
392
	mLog->info("Initialize simulation: {}", mName);
Markus Mirz's avatar
Markus Mirz committed
393
394
	if (!mInitialized)
		initialize();
395

Markus Mirz's avatar
Markus Mirz committed
396
#ifdef WITH_SHMEM
397
	mLog->info("Opening interfaces.");
398

399
	for (auto ifm : mInterfaces)
400
		ifm.interface->open(mLog);
401
402

	sync();
403
#endif
404

405
	mLog->info("Start simulation: {}", mName);
406
}
407

408
void Simulation::stop() {
409
410
	mScheduler->stop();

Markus Mirz's avatar
Markus Mirz committed
411
#ifdef WITH_SHMEM
412
413
	for (auto ifm : mInterfaces)
		ifm.interface->close();
Markus Mirz's avatar
Markus Mirz committed
414
#endif
415

416
	for (auto lg : mLoggers)
417
		lg->close();
418

419
	mLog->info("Simulation finished.");
420
421
}

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
Real Simulation::next() {
	if (mTime < mFinalTime)
		step();
	else
		stop();

	return mTime;
}


void Simulation::run() {
	start();

	while (mTime < mFinalTime) {
		step();
	}

	stop();
}

442
Real Simulation::step() {
443
	auto start = std::chrono::steady_clock::now();
444
445
	mEvents.handleEvents(mTime);

446
	mScheduler->step(mTime, mTimeStepCount);
447

448
	mTime += mTimeStep;
449
	++mTimeStepCount;
450

451
452
	auto end = std::chrono::steady_clock::now();
	std::chrono::duration<double> diff = end-start;
453
	mStepTimes.push_back(diff.count());
454
	return mTime;
Steffen Vogel's avatar
Steffen Vogel committed
455
}
456
457
458
459
460
461
462
463
464
465
466
467

void Simulation::reset() {

	// Resets component states
	mSystem.reset();

	for (auto l : mLoggers)
		l->reopen();

	// Force reinitialization for next run
	mInitialized = false;
}
Markus Mirz's avatar
Markus Mirz committed
468
469
470
471
472
473
474
475
476
477
478

void Simulation::logStepTimes(String logName) {
	auto stepTimeLog = Logger::get(logName, Logger::Level::info);
	Logger::setLogPattern(stepTimeLog, "%v");
	stepTimeLog->info("step_time");

	Real stepTimeSum = 0;
	for (auto meas : mStepTimes) {
		stepTimeSum += meas;
		stepTimeLog->info("{:f}", meas);
	}
479
	mLog->info("Average step time: {:.6f}", stepTimeSum / mStepTimes.size());
Markus Mirz's avatar
Markus Mirz committed
480
}
481

482
void Simulation::setIdObjAttr(const String &comp, const String &attr, Real value) {
483
484
485
486
487
488
489
490
491
492
493
494
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);
	if (compObj) {
		try {
			compObj->attribute<Real>(attr)->set(value);
		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}			
	}
	else
		mLog->error("Component not found");
}
		
495
void Simulation::setIdObjAttr(const String &comp, const String &attr, Complex value){
496
497
498
499
500
501
502
503
504
505
506
507
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);	
	if (compObj) {
		try {
			compObj->attributeComplex(attr)->set(value);
		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}			
	}
	else
		mLog->error("Component not found");
}

508
Real Simulation::getRealIdObjAttr(const String &comp, const String &attr) {
509
510
511
512
513
514
515
516
517
518
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);
	if (compObj) {
		try {
			return compObj->attribute<Real>(attr)->getByValue();
		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}			
	}
	
	mLog->error("Component not found");
519
	return 0;	
520
521
}
		
522
Complex Simulation::getComplexIdObjAttr(const String &comp, const String &attr) {
523
524
525
526
527
528
529
530
531
532
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);
	if (compObj) {
		try {
			return compObj->attributeComplex(attr)->getByValue();
		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}			
	}
	
	mLog->error("Component not found");
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
	return 0;
}

void Simulation::exportIdObjAttr(const String &comp, const String &attr, UInt idx, AttributeBase::Modifier mod, UInt row, UInt col) {
	Bool found = false;
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);
	if (!compObj) compObj = mSystem.node<TopologicalNode>(comp);

	if (compObj) {
		try {
			auto v = compObj->attribute<Real>(attr);
			mInterfaces[0].interface->exportReal(v, idx);
			found = true;
		} catch (InvalidAttributeException &e) { }			

		try {			
			auto v = compObj->attributeComplex(attr);
			switch(mod) {
				case AttributeBase::Modifier::real :
					mInterfaces[0].interface->exportReal(v->real(), idx);
					found = true;
					break;
				case AttributeBase::Modifier::imag :
					mInterfaces[0].interface->exportReal(v->imag(), idx);
					found = true;
					break;
				case AttributeBase::Modifier::mag :
					mInterfaces[0].interface->exportReal(v->mag(), idx);
					found = true;
					break;
				case AttributeBase::Modifier::phase :
					mInterfaces[0].interface->exportReal(v->phase(), idx);
					found = true;
					break;
			}			
		} catch (InvalidAttributeException &e) { }		

		try {			
			auto v = compObj->attributeMatrixReal(attr)->coeff(row, col);
			found = true;			
		} catch (InvalidAttributeException &e) { }		

		try {			
			auto v = compObj->attributeMatrixComp(attr);
			switch(mod) {
				case AttributeBase::Modifier::real :
					mInterfaces[0].interface->exportReal(v->coeffReal(row, col), idx);
					found = true;
					break;
				case AttributeBase::Modifier::imag :
					mInterfaces[0].interface->exportReal(v->coeffImag(row, col), idx);
					found = true;
					break;
				case AttributeBase::Modifier::mag :
					mInterfaces[0].interface->exportReal(v->coeffMag(row, col), idx);
					found = true;
					break;
				case AttributeBase::Modifier::phase :
					mInterfaces[0].interface->exportReal(v->coeffPhase(row, col), idx);
					found = true;
					break;
			}			
		} catch (InvalidAttributeException &e) { }		

		if (!found) mLog->error("Attribute not found");
	}	
	else {
		mLog->error("Component not found");
	}	
}

void Simulation::logIdObjAttr(const String &comp, const String &attr) {
	IdentifiedObject::Ptr compObj = mSystem.component<IdentifiedObject>(comp);
	IdentifiedObject::Ptr nodeObj = mSystem.node<TopologicalNode>(comp);

	if (compObj) {
		try {
			auto name = compObj->name() + "." + attr;
			auto v = compObj->attribute(attr);
			mLoggers[0]->addAttribute(name, v);

		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}			
	} else if (nodeObj) {
		try {
			auto name = nodeObj->name() + "." + attr;
			auto v = nodeObj->attribute(attr);
			mLoggers[0]->addAttribute(name, v);

		} catch (InvalidAttributeException &e) {
			mLog->error("Attribute not found");
		}		
	}
	else {
		mLog->error("Component not found");
	}	
}