CIMReader.cpp 9.67 KB
Newer Older
1
2
3
4
#include "CIMReader.h"
#include "CIMModel.hpp"
#include "IEC61970.hpp"

5
#include "Components/PQLoad.h"
6
#include "Components/RxLine.h"
7
#include "Components/TwoWindingTransformer.h"
8
9
10

using namespace DPsim;
using namespace IEC61970::Base::Core;
11
12
using namespace IEC61970::Base::Domain;
using namespace IEC61970::Base::Equivalents;
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using namespace IEC61970::Base::Topology;
using namespace IEC61970::Base::Wires;

// TODO is UnitMulitplier actually used/set anywhere?
double CIMReader::unitValue(double value, UnitMultiplier mult) {
	switch (mult) {
	case UnitMultiplier::p:
		value *= 1e-12;
		break;
	case UnitMultiplier::n:
		value *= 1e-9;
		break;
	case UnitMultiplier::micro:
		value *= 1e-6;
		break;
	case UnitMultiplier::m:
		value *= 1e-3;
		break;
	case UnitMultiplier::c:
		value *= 1e-2;
		break;
	case UnitMultiplier::d:
		value *= 1e-1;
		break;
	case UnitMultiplier::k:
		value *= 1e3;
		break;
	case UnitMultiplier::M:
		value *= 1e6;
		break;
	case UnitMultiplier::G:
		value *= 1e9;
		break;
	case UnitMultiplier::T:
		value *= 1e12;
		break;
	default:
		break;
	}
	return value;
}

55
CIMReader::CIMReader(Real om) {
56
	mModel.setDependencyCheckOff();
57
	mNumVoltageSources = 0;
58
	mVoltages = nullptr;
59
	mFrequency = om;
60
61
62
}

CIMReader::~CIMReader() {
63
64
	if (mVoltages)
		delete[] mVoltages;
65
66
67
68
69
70
71
72
73
74
75
76
77
}

BaseComponent* CIMReader::mapACLineSegment(ACLineSegment* line) {
	std::vector<int> &nodes = mEqNodeMap.at(line->mRID); // TODO can fail
	if (nodes.size() != 2) {
		std::cerr << "ACLineSegment " << line->mRID << " has " << nodes.size() << " terminals, ignoring" << std::endl;
		// TODO better error handling (throw exception?)
		return nullptr;
	}
	Real r = line->r.value;
	Real x = line->x.value;
	std::cerr << "RxLine " << line->name << " rid=" << line->mRID << " node1=" << nodes[0] << " node2=" << nodes[1];
	std::cerr << " R=" << r << " X=" << x << std::endl;
78
	return new RxLine(line->name, nodes[0], nodes[1], r, x/mFrequency);
79
80
}

81
BaseComponent* CIMReader::mapAsynchronousMachine(AsynchronousMachine* machine) {
82
83
84
85
86
	return newFlowPQLoad(machine->mRID, machine->name);
}

BaseComponent* CIMReader::mapEnergyConsumer(EnergyConsumer* con) {
	return newFlowPQLoad(con->mRID, con->name);
87
88
}

89
BaseComponent* CIMReader::mapEquivalentInjection(EquivalentInjection* inj) {
90
	return newFlowPQLoad(inj->mRID, inj->name);
91
92
}

93
94
95
96
97
98
99
100
101
102
103
104
105
106
BaseComponent* CIMReader::mapExternalNetworkInjection(ExternalNetworkInjection* inj) {
	std::vector<int> &nodes = mEqNodeMap.at(inj->mRID);
	if (nodes.size() != 1) {
		std::cerr << "ExternalNetworkInjection " << inj->mRID << " has " << nodes.size() << " terminals, ignoring" << std::endl;
		return nullptr;
	}
	int node = nodes[0];
	SvVoltage *volt = mVoltages[node-1];
	if (!volt) {
		std::cerr << "ExternalNetworkInjection " << inj->mRID << " has no associated SvVoltage, ignoring" << std::endl;
		return nullptr;
	}
	std::cerr << "IdealVoltageSource " << inj->name << " rid=" << inj->mRID << " node1=" << node << " node2=0 ";
	std::cerr << " V=" << volt->v.value << "<" << volt->angle.value << std::endl;
107
	return new IdealVoltageSource(inj->name, node, 0, Complex(volt->v.value, volt->angle.value*PI/180), ++mNumVoltageSources);
108
109
}

110
111
112
113
114
115
116
117
118
119
120
121
122
123
BaseComponent* CIMReader::mapPowerTransformer(PowerTransformer* trans) {
	std::vector<int> &nodes = mEqNodeMap.at(trans->mRID);
	if (nodes.size() != trans->PowerTransformerEnd.size()) {
		std::cerr << "PowerTransformer " << trans->mRID << " has differing number of terminals and windings, ignoring" << std::endl;
		return nullptr;
	}
	if (nodes.size() != 2) {
		// TODO three windings also possible
		std::cerr << "PowerTransformer " << trans->mRID << " has " << nodes.size() << "terminals; ignoring" << std::endl;
		return nullptr;
	}
	for (PowerTransformerEnd *end : trans->PowerTransformerEnd) {
		if (end->endNumber == 1) {
			std::cerr << "PowerTransformer " << trans->name << " rid=" << trans->mRID << " node1=" << nodes[0] << "node2=" << nodes[1] << " R=" << end->r.value << " X=" << end->x.value << std::endl;
124
			return new TwoWindingTransformer(trans->name, nodes[0], nodes[1], end->r.value, end->x.value/mFrequency);
125
126
127
128
129
130
		}
	}
	std::cerr << "PowerTransformer " << trans->mRID << " has no primary End; ignoring" << std::endl;
	return nullptr;
}

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
BaseComponent* CIMReader::mapSynchronousMachine(SynchronousMachine* machine) {
	// TODO: don't use SvVoltage, but map to a SynchronGenerator instead?
	std::vector<int> &nodes = mEqNodeMap.at(machine->mRID);
	if (nodes.size() != 1) {
		// TODO check with the model if this assumption (only 1 terminal) is always true
		std::cerr << "SynchronousMachine " << machine->mRID << " has " << nodes.size() << " terminals, ignoring" << std::endl;
		return nullptr;
	}
	int node = nodes[0];
	SvVoltage *volt = mVoltages[node-1];
	if (!volt) {
		std::cerr << "SynchronousMachine " << machine->mRID << " has no associated SvVoltage, ignoring" << std::endl;
		return nullptr;
	}
	std::cerr << "VoltSourceRes " << machine->name << " rid=" << machine->mRID << " node1=" << node << " node2=0 ";
	std::cerr << " V=" << volt->v.value << "<" << volt->angle.value << " R=" << machine->r.value << std::endl;
	// TODO is it appropiate to use this resistance here
148
	return new VoltSourceRes(machine->name, node, 0, Complex(volt->v.value, volt->angle.value*PI/180), machine->r.value);
149
150
}

151
BaseComponent* CIMReader::mapComponent(BaseClass* obj) {
152
	if (ACLineSegment *line = dynamic_cast<ACLineSegment*>(obj))
153
		return mapACLineSegment(line);
154
155
	if (AsynchronousMachine *machine = dynamic_cast<AsynchronousMachine*>(obj))
		return mapAsynchronousMachine(machine);
156
157
	if (EnergyConsumer *con = dynamic_cast<EnergyConsumer*>(obj))
		return mapEnergyConsumer(con);
158
159
	if (EquivalentInjection *inj = dynamic_cast<EquivalentInjection*>(obj))
		return mapEquivalentInjection(inj);
160
161
	if (ExternalNetworkInjection *inj = dynamic_cast<ExternalNetworkInjection*>(obj))
		return mapExternalNetworkInjection(inj);
162
163
	if (PowerTransformer *trans = dynamic_cast<PowerTransformer*>(obj))
		return mapPowerTransformer(trans);
164
165
	if (SynchronousMachine *syncMachine = dynamic_cast<SynchronousMachine*>(obj))
		return mapSynchronousMachine(syncMachine);
166
167
168
	return nullptr;
}

169
170
171
172
173
174
175
176
177
178
179
180
181
BaseComponent* CIMReader::newFlowPQLoad(std::string rid, std::string name) {
	std::vector<int> &nodes = mEqNodeMap.at(rid);
	if (nodes.size() != 1) {
		std::cerr << rid << " has " << nodes.size() << " terminals; ignoring" << std::endl;
		return nullptr;
	}
	auto search = mPowerFlows.find(rid);
	if (search == mPowerFlows.end()) {
		std::cerr << rid << " has no associated SvPowerFlow, ignoring" << std::endl;
		return nullptr;
	}
	SvPowerFlow* flow = search->second;
	int node = nodes[0];
182
183
184
185
186
187
188
189
	SvVoltage *volt = mVoltages[node-1];
	if (!volt) {
		std::cerr << rid << " has no associated SvVoltage, ignoring" << std::endl;
		return nullptr;
	}
	std::cerr << "PQLoad " << name << " rid=" << rid << " node1=" << node << " node2=0 P=" << flow->p.value << " Q=" << flow->q.value;
	std::cerr << " V=" << volt->v.value << "<" << volt->angle.value << std::endl;
	return new PQLoad(name, node, 0, flow->p.value, flow->q.value, volt->v.value, volt->angle.value*PI/180);
190
191
}

192
193
194
195
bool CIMReader::addFile(std::string filename) {
	return mModel.addCIMFile(filename);
}

196
void CIMReader::parseFiles() {
197
198
199
200
201
202
203
204
	mModel.parseFiles();
	// First, go through all topological nodes and collect them in a list.
	// Since all nodes have references to the equipment connected to them (via Terminals), but not
	// the other way around (which we need for instantiating the components), we collect that information here as well.
	for (BaseClass* obj : mModel.Objects) {
		TopologicalNode* topNode = dynamic_cast<TopologicalNode*>(obj);
		if (topNode) {
			std::cerr << "TopologicalNode " << mTopNodes.size()+1 << " rid=" << topNode->mRID << " Terminals:" << std::endl;
205
			mTopNodes[topNode->mRID] = mTopNodes.size()+1;
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
			for (Terminal* term : topNode->Terminal) {
				std::cerr << "    " << term->mRID << std::endl;
				ConductingEquipment *eq = term->ConductingEquipment;
				if (!eq) {
					std::cerr << "Terminal " << term->mRID << " has no Conducting Equipment, ignoring!" << std::endl;
				} else {
					std::cerr << "    eq " << eq->mRID << " sequenceNumber " << term->sequenceNumber << std::endl;
					std::vector<int> &nodesVec = mEqNodeMap[eq->mRID];
					if (nodesVec.size() < term->sequenceNumber)
						nodesVec.resize(term->sequenceNumber);
					nodesVec[term->sequenceNumber-1] = mTopNodes.size();
				}
			}
		}
	}
221
222
223
224
225
	// Collect voltage state variables associated to nodes that are used
	// for various components.
	mVoltages = new SvVoltage*[mTopNodes.size()];
	std::cerr << "Voltages" << std::endl;
	for (BaseClass* obj : mModel.Objects) {
226
		if (SvVoltage* volt = dynamic_cast<SvVoltage*>(obj)) {
227
228
229
230
231
232
233
234
235
236
237
238
			TopologicalNode* node = volt->TopologicalNode;
			if (!node) {
				std::cerr << "SvVoltage references missing Topological Node, ignoring" << std::endl;
				continue;
			}
			auto search = mTopNodes.find(node->mRID);
			if (search == mTopNodes.end()) {
				std::cerr << "SvVoltage references Topological Node " << node->mRID << " missing from mTopNodes, ignoring" << std::endl;
				continue;
			}
			mVoltages[search->second-1] = volt;
			std::cerr << volt->v.value << "<" << volt->angle.value << " at " << search->second << std::endl;
239
240
241
242
		} else if (SvPowerFlow* flow = dynamic_cast<SvPowerFlow*>(obj)) {
			// TODO could there be more than one power flow per equipment?
			Terminal* term = flow->Terminal;
			mPowerFlows[term->ConductingEquipment->mRID] = flow;
243
244
		}
	}
245
246
247
	for (BaseClass* obj : mModel.Objects) {
		BaseComponent* comp = mapComponent(obj);
		if (comp)
248
			mComponents.push_back(comp);
249
	}
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
}

std::vector<BaseComponent*>& CIMReader::getComponents() {
	return mComponents;
}

int CIMReader::mapTopologicalNode(std::string mrid) {
	auto search = mTopNodes.find(mrid);
	if (search == mTopNodes.end())
		return -1;
	return search->second;
}

int CIMReader::getNumVoltageSources() {
	return mNumVoltageSources;
265
}