Power System Platform  2026w23a-beta
Loading...
Searching...
No Matches
PowerElement.cpp
1/*
2 * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "PowerElement.h"
19#ifdef USING_WX_3_0_X
20#include "DegreesAndRadians.h"
21#endif
22#include <wx/brush.h>
23
25{
26 m_busColour.Set(0, 78, 255, 255);
27 m_onlineElementColour.Set(51, 51, 51, 255);
28 m_offlineElementColour.Set(128, 128, 128, 255);
29 m_closedSwitchColour.Set(0, 102, 0, 255);
30 m_openedSwitchColour.Set(255, 26, 26, 255);
31 m_powerFlowArrowColour.Set(255, 130, 0, 255);
32 m_dynamicEventColour.Set(255, 130, 0, 255);
33}
34
35void PowerElement::SetNominalVoltage(std::vector<double> nominalVoltage, std::vector<ElectricalUnit> nominalVoltageUnit)
36{
37}
38
39wxPoint2DDouble PowerElement::GetSwitchPoint(Element* parent,
40 wxPoint2DDouble parentPoint,
41 wxPoint2DDouble secondPoint) const
42{
43 double swLineSize = 25.0;
44 wxPoint2DDouble swPoint = wxPoint2DDouble(parentPoint.m_x, parentPoint.m_y - swLineSize);
45
46 // Rotate the second point (to compare).
47 double angle = parent->GetAngle();
48
49 secondPoint =
50 wxPoint2DDouble(std::cos(wxDegToRad(-angle)) * (secondPoint.m_x - parentPoint.m_x) -
51 std::sin(wxDegToRad(-angle)) * (secondPoint.m_y - parentPoint.m_y) + parentPoint.m_x,
52 std::sin(wxDegToRad(-angle)) * (secondPoint.m_x - parentPoint.m_x) +
53 std::cos(wxDegToRad(-angle)) * (secondPoint.m_y - parentPoint.m_y) + parentPoint.m_y);
54
55 // Rotate
56 if(secondPoint.m_y > parentPoint.m_y) angle -= 180.0;
57 return wxPoint2DDouble(std::cos(wxDegToRad(angle)) * (swPoint.m_x - parentPoint.m_x) -
58 std::sin(wxDegToRad(angle)) * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_x,
59 std::sin(wxDegToRad(angle)) * (swPoint.m_x - parentPoint.m_x) +
60 std::cos(wxDegToRad(angle)) * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_y);
61}
62
63bool PowerElement::SwitchesContains(wxPoint2DDouble position) const
64{
65 for(int i = 0; i < (int)m_switchRect.size(); i++) {
66 if(m_parentList[i]) {
67 if(m_switchRect[i].Contains(position)) return true;
68 }
69 }
70 return false;
71}
72
74{
75 // General method, to one switch only.
76 wxPoint2DDouble swCenter = wxPoint2DDouble((m_pointList[0].m_x + m_pointList[1].m_x) / 2.0,
77 (m_pointList[0].m_y + m_pointList[1].m_y) / 2.0);
78 m_switchRect[0] = wxRect2DDouble(swCenter.m_x - m_switchSize / 2.0, swCenter.m_y - m_switchSize / 2.0, m_switchSize,
79 m_switchSize);
80}
81
82//void PowerElement::DrawSwitches() const
83//{
84// int i = 0;
85// for(auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
86// Element* parent = *it;
87// if(parent) {
88// if(m_online) {
89// glColor4dv(m_closedSwitchColour);
90// } else {
91// glColor4dv(m_openedSwitchColour);
92// }
93//
94// glPushMatrix();
95// glTranslated(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
96// m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0, 0.0);
97// glRotated(parent->GetAngle(), 0.0, 0.0, 1.0);
98// glTranslated(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
99// -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0, 0.0);
100//
101// DrawRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
102// m_switchSize, m_switchSize);
103//
104// glPopMatrix();
105// }
106// i++;
107// }
108//}
109
110void PowerElement::DrawDCSwitches(GUIColour* guiColour, wxGraphicsContext* gc) const
111{
112 gc->SetPen(*wxTRANSPARENT_PEN);
113
114 int i = 0;
115 for (auto parent : m_parentList) {
116 if (parent) {
117 if (m_online) {
118 gc->SetBrush(wxBrush(guiColour->swClose));
119 }
120 else {
121 gc->SetBrush(wxBrush(guiColour->swOpen));
122 }
123
124 gc->PushState();
125 gc->Translate(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
126 m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0);
127 gc->Rotate(wxDegToRad(parent->GetAngle()));
128 gc->Translate(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
129 -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0);
130
131 wxPoint2DDouble switchPos = m_switchRect[i].GetPosition();
132 gc->DrawRectangle(switchPos.m_x, switchPos.m_y, m_switchSize, m_switchSize);
133
134 gc->PopState();
135 }
136 i++;
137 }
138}
139
140void PowerElement::DrawDCSwitches(GUIColour* guiColour, wxDC& dc) const
141{
142 dc.SetPen(*wxTRANSPARENT_PEN);
143
144 int i = 0;
145 for (auto parent : m_parentList) {
146 if (parent) {
147 if (m_online) {
148 dc.SetBrush(wxBrush(guiColour->swClose));
149 }
150 else {
151 dc.SetBrush(wxBrush(guiColour->swOpen));
152 }
153
154 DrawDCRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
155 m_switchSize, m_switchSize, parent->GetAngle(), dc);
156 }
157 i++;
158 }
159}
160
161void PowerElement::CalculatePowerFlowPts(std::vector<wxPoint2DDouble> edges)
162{
163 double arrowRate = 100.0; // One arrow to each "arrowRate" distance in pixels.
164
165 if(edges.size() < 2) return;
166
167 // Clear all power flow points
168 for(int i = 0; i < (int)m_powerFlowArrow.size(); i++) m_powerFlowArrow[i].clear();
169 m_powerFlowArrow.clear();
170
171 for(int i = 1; i < (int)edges.size(); i++) {
172 wxPoint2DDouble pt1 = edges[i - 1];
173 wxPoint2DDouble pt2 = edges[i];
174
175 double angle = std::atan2(pt2.m_y - pt1.m_y, pt2.m_x - pt1.m_x);
176
177 wxPoint2DDouble rotPt2(
178 std::cos(-angle) * (pt2.m_x - pt1.m_x) - std::sin(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_x,
179 std::sin(-angle) * (pt2.m_x - pt1.m_x) + std::cos(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_y);
180
181 int numArrows = std::abs(pt1.m_x - rotPt2.m_x) / arrowRate;
182 if(numArrows == 0) numArrows = 1;
183
184 for(int i = 0; i < numArrows; i++) {
185 wxPoint2DDouble arrowCenter(pt1.m_x + ((rotPt2.m_x - pt1.m_x) / double(numArrows + 1)) * double(i + 1),
186 pt1.m_y + ((rotPt2.m_y - pt1.m_y) / double(numArrows + 1)) * double(i + 1));
187
188 std::vector<wxPoint2DDouble> triPts;
189 triPts.push_back(arrowCenter + wxPoint2DDouble(5.0, 0.0));
190 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, 5.0));
191 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, -5.0));
192
193 // Rotate back.
194 for(int i = 0; i < 3; i++) {
195 triPts[i] = wxPoint2DDouble(
196 std::cos(angle) * (triPts[i].m_x - pt1.m_x) - std::sin(angle) * (triPts[i].m_y - pt1.m_y) + pt1.m_x,
197 std::sin(angle) * (triPts[i].m_x - pt1.m_x) + std::cos(angle) * (triPts[i].m_y - pt1.m_y) +
198 pt1.m_y);
199 }
200 m_powerFlowArrow.push_back(triPts);
201 }
202 }
203}
204
205//void PowerElement::DrawPowerFlowPts() const
206//{
207// if(m_online) {
208// glColor4dv(m_powerFlowArrowColour.GetRGBA());
209// for(int i = 0; i < (int)m_powerFlowArrow.size(); i++) { DrawTriangle(m_powerFlowArrow[i]); }
210// }
211//}
212
213void PowerElement::DrawDCPowerFlowPts(GUIColour* guiColour, wxGraphicsContext* gc) const
214{
215 gc->SetPen(*wxTRANSPARENT_PEN);
216 if (m_online) {
217 gc->SetBrush(wxBrush(guiColour->pfArrow));
218 for (auto arrow : m_powerFlowArrow) { DrawDCTriangle(arrow, gc); }
219 }
220}
221
222void PowerElement::DrawDCPowerFlowPts(GUIColour* guiColour, wxDC& dc) const
223{
224 dc.SetPen(*wxTRANSPARENT_PEN);
225 if (m_online) {
226 dc.SetBrush(wxBrush(guiColour->pfArrow));
227 for (auto arrow : m_powerFlowArrow) {
228 std::vector<wxPoint> arrowPts;
229 for (auto& pt : arrow) {
230 arrowPts.emplace_back(static_cast<int>(pt.m_x), static_cast<int>(pt.m_y));
231 }
232 DrawDCTriangle(arrowPts, dc);
233 }
234 }
235}
236
237double PowerElement::GetValueFromUnit(double value, ElectricalUnit valueUnit) const
238{
239 switch(valueUnit) {
240 case ElectricalUnit::UNIT_kV:
241 case ElectricalUnit::UNIT_kA:
242 case ElectricalUnit::UNIT_kW:
243 case ElectricalUnit::UNIT_kVA:
244 case ElectricalUnit::UNIT_kvar: {
245 return value * 1e3;
246 } break;
247 case ElectricalUnit::UNIT_MW:
248 case ElectricalUnit::UNIT_MVA:
249 case ElectricalUnit::UNIT_Mvar: {
250 return value * 1e6;
251 }
252 default:
253 break;
254 }
255 return value;
256}
257
258bool PowerElement::OpenCADProperties(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
259{
260 auto cadPropNode = elementNode->first_node("CADProperties");
261 if(!cadPropNode) return false;
262
263 auto position = cadPropNode->first_node("Position");
264 double posX = XMLParser::GetNodeValueDouble(position, "X");
265 double posY = XMLParser::GetNodeValueDouble(position, "Y");
266 auto size = cadPropNode->first_node("Size");
267 m_width = XMLParser::GetNodeValueDouble(size, "Width");
268 m_height = XMLParser::GetNodeValueDouble(size, "Height");
269 double angle = XMLParser::GetNodeValueDouble(cadPropNode, "Angle");
270 SetPosition(wxPoint2DDouble(posX, posY));
271
272 auto nodePosition = cadPropNode->first_node("NodePosition");
273 double nodePosX = XMLParser::GetNodeValueDouble(nodePosition, "X");
274 double nodePosY = XMLParser::GetNodeValueDouble(nodePosition, "Y");
275
276 int parentID = XMLParser::GetNodeValueInt(cadPropNode, "ParentID");
277 // If the opened power element has no parent, set up the basics CAD properties of the element manually, otherwise
278 // just class method AddParent to calculate properly.
279 if(parentID == -1) {
280 m_parentList.push_back(nullptr);
281 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
282 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
283 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0 - 10.0));
284 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0));
285
286 wxRect2DDouble genRect(0, 0, 0, 0);
287 m_switchRect.push_back(genRect); // Push a general rectangle.
289
290 m_online = false; // Not connected elements are always offline.
291 } else {
292 AddParent(parentList[parentID], wxPoint2DDouble(nodePosX, nodePosY));
293 }
294
295 // Set up the points properly.
296 StartMove(m_position);
297 Move(wxPoint2DDouble(posX, posY));
298
299 // Set the rotation properly.
300 int numRot = angle / m_rotationAngle;
301 bool clockwise = true;
302 if(numRot < 0) {
303 numRot = std::abs(numRot);
304 clockwise = false;
305 }
306 for(int i = 0; i < numRot; i++) Rotate(clockwise);
307
308 return true;
309}
310
311void PowerElement::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
312{
313 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
314 auto position = XMLParser::AppendNode(doc, cadProp, "Position");
315 auto posX = XMLParser::AppendNode(doc, position, "X");
316 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
317 auto posY = XMLParser::AppendNode(doc, position, "Y");
318 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
319 auto size = XMLParser::AppendNode(doc, cadProp, "Size");
320 auto width = XMLParser::AppendNode(doc, size, "Width");
321 XMLParser::SetNodeValue(doc, width, m_width);
322 auto height = XMLParser::AppendNode(doc, size, "Height");
323 XMLParser::SetNodeValue(doc, height, m_height);
324 auto angle = XMLParser::AppendNode(doc, cadProp, "Angle");
325 XMLParser::SetNodeValue(doc, angle, m_angle);
326 auto nodePos = XMLParser::AppendNode(doc, cadProp, "NodePosition");
327 auto nodePosX = XMLParser::AppendNode(doc, nodePos, "X");
328 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[0].m_x);
329 auto nodePosY = XMLParser::AppendNode(doc, nodePos, "Y");
330 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[0].m_y);
331 auto parentID = XMLParser::AppendNode(doc, cadProp, "ParentID");
332 Element* parent = m_parentList[0];
333 if(parent) XMLParser::SetNodeValue(doc, parentID, parent->GetID());
334}
335
336void PowerElement::SaveSwitchingData(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* electricalNode)
337{
338 auto switchingList = XMLParser::AppendNode(doc, electricalNode, "SwitchingList");
339 for(int i = 0; i < static_cast<int>(m_swData.swType.size()); i++) {
340 auto switching = XMLParser::AppendNode(doc, switchingList, "Switching");
341 XMLParser::SetNodeAttribute(doc, switching, "ID", i);
342 auto swType = XMLParser::AppendNode(doc, switching, "Type");
343 XMLParser::SetNodeValue(doc, swType, static_cast<int>(m_swData.swType[i]));
344 auto swTime = XMLParser::AppendNode(doc, switching, "Time");
345 XMLParser::SetNodeValue(doc, swTime, m_swData.swTime[i]);
346 }
347}
348
349bool PowerElement::OpenSwitchingData(rapidxml::xml_node<>* electricalNode)
350{
351 auto switchingList = electricalNode->first_node("SwitchingList");
352 if(!switchingList) return false;
353 auto swNode = switchingList->first_node("Switching");
354 while(swNode) {
355 m_swData.swType.push_back((SwitchingType)XMLParser::GetNodeValueInt(swNode, "Type"));
356 m_swData.swTime.push_back(XMLParser::GetNodeValueDouble(swNode, "Time"));
357 swNode = swNode->next_sibling("Switching");
358 }
359 return true;
360}
SwitchingType
Type of switching.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:114
virtual int GetID() const
Get the element ID.
Definition Element.h:273
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:188
virtual bool AddParent(Element *parent, wxPoint2DDouble position)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
Definition Element.h:241
double GetAngle() const
Get the element angle.
Definition Element.h:213
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
Definition Element.cpp:41
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Definition Element.cpp:265
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:33
virtual void DrawDCTriangle(std::vector< wxPoint2DDouble > points, wxGraphicsContext *gc) const
Draw rectangle.
Definition Element.cpp:209
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Element.cpp:271
virtual bool Contains(wxPoint2DDouble position) const =0
Checks if the element contains a position.
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Element.h:319
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
PowerElement()
Constructor.
virtual void CalculatePowerFlowPts(std::vector< wxPoint2DDouble > edges)
Calculate the points of the power flow arrows.
virtual bool SwitchesContains(wxPoint2DDouble position) const
Check if switch contains position.
virtual void DrawDCPowerFlowPts(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw power flow arrows.
virtual void UpdateSwitches()
Update the switch position.
virtual void DrawDCSwitches(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw switch.
virtual wxPoint2DDouble GetSwitchPoint(Element *parent, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
Get the correct switch position.
std::vector< double > swTime
std::vector< SwitchingType > swType