Power System Platform  2026w34a-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, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
40{
41 double swLineSize = 20.0;
42
43 double dist = std::sqrt(
44 (secondPoint.m_x - parentPoint.m_x) *
45 (secondPoint.m_x - parentPoint.m_x) +
46 (secondPoint.m_y - parentPoint.m_y) *
47 (secondPoint.m_y - parentPoint.m_y));
48
49 if (dist > 0.0 && dist < swLineSize) {
50 swLineSize = dist / 2.0;
51 }
52
53 double angle = parent->GetAngle();
54
55 // Transform secondPoint to the local coordinate system of the parent.
56 double rad = wxDegToRad(-angle);
57 double c = std::cos(rad);
58 double s = std::sin(rad);
59
60 double dx = secondPoint.m_x - parentPoint.m_x;
61 double dy = secondPoint.m_y - parentPoint.m_y;
62
63 double localX = c * dx - s * dy;
64 double localY = s * dx + c * dy;
65
66 // Keep the original behavior:
67 // the switch is always perpendicular to the bus.
68 //
69 // The local Y coordinate only determines which side of the bus the switch should be placed.
70 if (localY > 0.0) {
71 angle -= 180.0;
72 }
73
74 // The switch starts above the parent in its local coordinate system and is rotated according to the parent angle.
75 wxPoint2DDouble swPoint(parentPoint.m_x, parentPoint.m_y - swLineSize);
76
77 double backRad = wxDegToRad(angle);
78 double bc = std::cos(backRad);
79 double bs = std::sin(backRad);
80
81 wxPoint2DDouble pt(bc * (swPoint.m_x - parentPoint.m_x) - bs * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_x,
82 bs * (swPoint.m_x - parentPoint.m_x) + bc * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_y);
83
84 if (std::abs(pt.m_x - std::round(pt.m_x)) < 1e-4)
85 pt.m_x = std::round(pt.m_x);
86
87 if (std::abs(pt.m_y - std::round(pt.m_y)) < 1e-4)
88 pt.m_y = std::round(pt.m_y);
89
90 return pt;
91}
92
93bool PowerElement::SwitchesContains(wxPoint2DDouble position) const
94{
95 for (int i = 0; i < (int)m_switchRect.size(); i++) {
96 if (m_parentList[i]) {
97 if (m_switchRect[i].Contains(position)) return true;
98 }
99 }
100 return false;
101}
102
104{
105 // General method, to one switch only.
106 wxPoint2DDouble swCenter = wxPoint2DDouble((m_pointList[0].m_x + m_pointList[1].m_x) / 2.0,
107 (m_pointList[0].m_y + m_pointList[1].m_y) / 2.0);
108 m_switchRect[0] = wxRect2DDouble(swCenter.m_x - m_switchSize / 2.0, swCenter.m_y - m_switchSize / 2.0, m_switchSize,
109 m_switchSize);
110}
111
112//void PowerElement::DrawSwitches() const
113//{
114// int i = 0;
115// for(auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
116// Element* parent = *it;
117// if(parent) {
118// if(m_online) {
119// glColor4dv(m_closedSwitchColour);
120// } else {
121// glColor4dv(m_openedSwitchColour);
122// }
123//
124// glPushMatrix();
125// glTranslated(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
126// m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0, 0.0);
127// glRotated(parent->GetAngle(), 0.0, 0.0, 1.0);
128// glTranslated(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
129// -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0, 0.0);
130//
131// DrawRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
132// m_switchSize, m_switchSize);
133//
134// glPopMatrix();
135// }
136// i++;
137// }
138//}
139
140void PowerElement::DrawDCSwitches(GUIColour* guiColour, wxGraphicsContext* gc) const
141{
142 gc->SetPen(*wxTRANSPARENT_PEN);
143
144 int i = 0;
145 for (auto parent : m_parentList) {
146 if (parent) {
147 if (m_online) {
148 gc->SetBrush(wxBrush(guiColour->swClose));
149 }
150 else {
151 gc->SetBrush(wxBrush(guiColour->swOpen));
152 }
153
154 gc->PushState();
155 gc->Translate(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
156 m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0);
157 gc->Rotate(wxDegToRad(parent->GetAngle()));
158 gc->Translate(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
159 -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0);
160
161 wxPoint2DDouble switchPos = m_switchRect[i].GetPosition();
162 gc->DrawRectangle(switchPos.m_x, switchPos.m_y, m_switchSize, m_switchSize);
163
164 gc->PopState();
165 }
166 i++;
167 }
168}
169
170void PowerElement::DrawDCSwitches(GUIColour* guiColour, wxDC& dc) const
171{
172 dc.SetPen(*wxTRANSPARENT_PEN);
173
174 int i = 0;
175 for (auto parent : m_parentList) {
176 if (parent) {
177 if (m_online) {
178 dc.SetBrush(wxBrush(guiColour->swClose));
179 }
180 else {
181 dc.SetBrush(wxBrush(guiColour->swOpen));
182 }
183
184 DrawDCRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
185 m_switchSize, m_switchSize, parent->GetAngle(), dc);
186 }
187 i++;
188 }
189}
190
191void PowerElement::CalculatePowerFlowPts(std::vector<wxPoint2DDouble> edges)
192{
193 double arrowRate = 100.0; // One arrow to each "arrowRate" distance in pixels.
194
195 if (edges.size() < 2) return;
196
197 // Clear all power flow points
198 for (int i = 0; i < (int)m_powerFlowArrow.size(); i++) m_powerFlowArrow[i].clear();
199 m_powerFlowArrow.clear();
200
201 for (int i = 1; i < (int)edges.size(); i++) {
202 wxPoint2DDouble pt1 = edges[i - 1];
203 wxPoint2DDouble pt2 = edges[i];
204
205 double angle = std::atan2(pt2.m_y - pt1.m_y, pt2.m_x - pt1.m_x);
206
207 wxPoint2DDouble rotPt2(
208 std::cos(-angle) * (pt2.m_x - pt1.m_x) - std::sin(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_x,
209 std::sin(-angle) * (pt2.m_x - pt1.m_x) + std::cos(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_y);
210
211 int numArrows = std::abs(pt1.m_x - rotPt2.m_x) / arrowRate;
212 if (numArrows == 0) numArrows = 1;
213
214 for (int i = 0; i < numArrows; i++) {
215 wxPoint2DDouble arrowCenter(pt1.m_x + ((rotPt2.m_x - pt1.m_x) / double(numArrows + 1)) * double(i + 1),
216 pt1.m_y + ((rotPt2.m_y - pt1.m_y) / double(numArrows + 1)) * double(i + 1));
217
218 std::vector<wxPoint2DDouble> triPts;
219 triPts.push_back(arrowCenter + wxPoint2DDouble(5.0, 0.0));
220 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, 5.0));
221 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, -5.0));
222
223 // Rotate back.
224 for (int i = 0; i < 3; i++) {
225 triPts[i] = wxPoint2DDouble(
226 std::cos(angle) * (triPts[i].m_x - pt1.m_x) - std::sin(angle) * (triPts[i].m_y - pt1.m_y) + pt1.m_x,
227 std::sin(angle) * (triPts[i].m_x - pt1.m_x) + std::cos(angle) * (triPts[i].m_y - pt1.m_y) +
228 pt1.m_y);
229 }
230 m_powerFlowArrow.push_back(triPts);
231 }
232 }
233}
234
235//void PowerElement::DrawPowerFlowPts() const
236//{
237// if(m_online) {
238// glColor4dv(m_powerFlowArrowColour.GetRGBA());
239// for(int i = 0; i < (int)m_powerFlowArrow.size(); i++) { DrawTriangle(m_powerFlowArrow[i]); }
240// }
241//}
242
243void PowerElement::DrawDCPowerFlowPts(GUIColour* guiColour, wxGraphicsContext* gc) const
244{
245 gc->SetPen(*wxTRANSPARENT_PEN);
246 if (m_online) {
247 gc->SetBrush(wxBrush(guiColour->pfArrow));
248 for (auto arrow : m_powerFlowArrow) { DrawDCTriangle(arrow, gc); }
249 }
250}
251
252void PowerElement::DrawDCPowerFlowPts(GUIColour* guiColour, wxDC& dc) const
253{
254 dc.SetPen(*wxTRANSPARENT_PEN);
255 if (m_online) {
256 dc.SetBrush(wxBrush(guiColour->pfArrow));
257 for (auto arrow : m_powerFlowArrow) {
258 std::vector<wxPoint> arrowPts;
259 for (auto& pt : arrow) {
260 arrowPts.emplace_back(static_cast<int>(pt.m_x), static_cast<int>(pt.m_y));
261 }
262 DrawDCTriangle(arrowPts, dc);
263 }
264 }
265}
266
267double PowerElement::GetValueFromUnit(double value, ElectricalUnit valueUnit) const
268{
269 switch (valueUnit) {
270 case ElectricalUnit::UNIT_kV:
271 case ElectricalUnit::UNIT_kA:
272 case ElectricalUnit::UNIT_kW:
273 case ElectricalUnit::UNIT_kVA:
274 case ElectricalUnit::UNIT_kvar: {
275 return value * 1e3;
276 } break;
277 case ElectricalUnit::UNIT_MW:
278 case ElectricalUnit::UNIT_MVA:
279 case ElectricalUnit::UNIT_Mvar: {
280 return value * 1e6;
281 }
282 default:
283 break;
284 }
285 return value;
286}
287
288bool PowerElement::OpenCADProperties(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
289{
290 auto cadPropNode = elementNode->first_node("CADProperties");
291 if (!cadPropNode) return false;
292
293 auto position = cadPropNode->first_node("Position");
294 double posX = XMLParser::GetNodeValueDouble(position, "X");
295 double posY = XMLParser::GetNodeValueDouble(position, "Y");
296 auto size = cadPropNode->first_node("Size");
297 m_width = XMLParser::GetNodeValueDouble(size, "Width");
298 m_height = XMLParser::GetNodeValueDouble(size, "Height");
299 double angle = XMLParser::GetNodeValueDouble(cadPropNode, "Angle");
300 SetPosition(wxPoint2DDouble(posX, posY));
301
302 auto nodePosition = cadPropNode->first_node("NodePosition");
303 double nodePosX = XMLParser::GetNodeValueDouble(nodePosition, "X");
304 double nodePosY = XMLParser::GetNodeValueDouble(nodePosition, "Y");
305
306 int parentID = XMLParser::GetNodeValueInt(cadPropNode, "ParentID");
307 // If the opened power element has no parent, set up the basics CAD properties of the element manually, otherwise
308 // just class method AddParent to calculate properly.
309 if (parentID == -1) {
310 m_parentList.push_back(nullptr);
311 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
312 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
313 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0 - 10.0));
314 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0));
315
316 wxRect2DDouble genRect(0, 0, 0, 0);
317 m_switchRect.push_back(genRect); // Push a general rectangle.
319
320 m_online = false; // Not connected elements are always offline.
321 }
322 else {
323 AddParent(parentList[parentID], wxPoint2DDouble(nodePosX, nodePosY), true);
324 }
325
326 // Set up the points properly.
327 StartMove(m_position);
328 Move(wxPoint2DDouble(posX, posY));
329
330 // Set the rotation properly.
331 int numRot = angle / m_rotationAngle;
332 bool clockwise = true;
333 if (numRot < 0) {
334 numRot = std::abs(numRot);
335 clockwise = false;
336 }
337 for (int i = 0; i < numRot; i++) Rotate(clockwise);
338
339 return true;
340}
341
342void PowerElement::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
343{
344 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
345 auto position = XMLParser::AppendNode(doc, cadProp, "Position");
346 auto posX = XMLParser::AppendNode(doc, position, "X");
347 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
348 auto posY = XMLParser::AppendNode(doc, position, "Y");
349 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
350 auto size = XMLParser::AppendNode(doc, cadProp, "Size");
351 auto width = XMLParser::AppendNode(doc, size, "Width");
352 XMLParser::SetNodeValue(doc, width, m_width);
353 auto height = XMLParser::AppendNode(doc, size, "Height");
354 XMLParser::SetNodeValue(doc, height, m_height);
355 auto angle = XMLParser::AppendNode(doc, cadProp, "Angle");
356 XMLParser::SetNodeValue(doc, angle, m_angle);
357 auto nodePos = XMLParser::AppendNode(doc, cadProp, "NodePosition");
358 auto nodePosX = XMLParser::AppendNode(doc, nodePos, "X");
359 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[0].m_x);
360 auto nodePosY = XMLParser::AppendNode(doc, nodePos, "Y");
361 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[0].m_y);
362 auto parentID = XMLParser::AppendNode(doc, cadProp, "ParentID");
363 Element* parent = m_parentList[0];
364 if (parent) XMLParser::SetNodeValue(doc, parentID, parent->GetID());
365}
366
367void PowerElement::SaveSwitchingData(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* electricalNode)
368{
369 auto switchingList = XMLParser::AppendNode(doc, electricalNode, "SwitchingList");
370 for (int i = 0; i < static_cast<int>(m_swData.swType.size()); i++) {
371 auto switching = XMLParser::AppendNode(doc, switchingList, "Switching");
372 XMLParser::SetNodeAttribute(doc, switching, "ID", i);
373 auto swType = XMLParser::AppendNode(doc, switching, "Type");
374 XMLParser::SetNodeValue(doc, swType, static_cast<int>(m_swData.swType[i]));
375 auto swTime = XMLParser::AppendNode(doc, switching, "Time");
376 XMLParser::SetNodeValue(doc, swTime, m_swData.swTime[i]);
377 }
378}
379
380bool PowerElement::OpenSwitchingData(rapidxml::xml_node<>* electricalNode)
381{
382 auto switchingList = electricalNode->first_node("SwitchingList");
383 if (!switchingList) return false;
384 auto swNode = switchingList->first_node("Switching");
385 while (swNode) {
386 m_swData.swType.push_back((SwitchingType)XMLParser::GetNodeValueInt(swNode, "Type"));
387 m_swData.swTime.push_back(XMLParser::GetNodeValueDouble(swNode, "Time"));
388 swNode = swNode->next_sibling("Switching");
389 }
390 return true;
391}
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:115
virtual int GetID() const
Get the element ID.
Definition Element.h:275
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:189
double GetAngle() const
Get the element angle.
Definition Element.h:214
virtual bool AddParent(Element *parent, wxPoint2DDouble position, bool isOpening=false)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
Definition Element.h:243
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:335
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:341
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:321
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