Power System Platform  2026w34a-beta
Loading...
Searching...
No Matches
Capacitor.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 "Capacitor.h"
19#include "../../forms/ReactiveShuntElementForm.h"
20
21Capacitor::Capacitor() : Shunt() {
22 m_elementType = TYPE_CAPACITOR;
23}
24
25Capacitor::Capacitor(wxString name) : Shunt() {
26 m_elementType = TYPE_CAPACITOR;
27 m_electricalData.name = name;
28}
29
30Capacitor::~Capacitor() {}
31
32bool Capacitor::AddParent(Element* parent, wxPoint2DDouble position, bool isOpening)
33{
34 if (parent) {
35 m_parentList.push_back(parent);
36 parent->AddChild(this);
37 wxPoint2DDouble parentPt =
38 parent->RotateAtPosition(position, -parent->GetAngle()); // Rotate click to horizontal position.
39 parentPt.m_y = parent->GetPosition().m_y; // Centralize on bus.
40 parentPt = parent->RotateAtPosition(parentPt, parent->GetAngle()); // Rotate back.
41
42 m_position = parentPt + wxPoint2DDouble(0.0, 100.0); // Shifts the position to the down of the bus.
43 m_width = 40;
44 m_height = 30;
45 m_rect = wxRect2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0, m_width, m_height);
46
47 m_pointList.push_back(parentPt);
48 m_pointList.push_back(GetSwitchPoint(parent, parentPt, m_position));
49 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0 - 10.0));
50 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0));
51
52 m_inserted = true;
53
54 wxRect2DDouble genRect(0, 0, 0, 0);
55 m_switchRect.push_back(genRect); // Push a general rectangle.
57
58 return true;
59 }
60 return false;
61}
62
63//void Capacitor::Draw(wxPoint2DDouble translation, double scale) const
64//{
65// OpenGLColour elementColour;
66// if (m_online) {
67// if (m_dynEvent)
68// elementColour = m_dynamicEventColour;
69// else
70// elementColour = m_onlineElementColour;
71// }
72// else
73// elementColour = m_offlineElementColour;
74//
75// if (m_inserted) {
76// std::vector<wxPoint2DDouble> capPts;
77// capPts.push_back(wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0));
78// capPts.push_back(wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0));
79// capPts.push_back(wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0));
80// capPts.push_back(wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0));
81//
82// if (m_selected) {
83// glLineWidth(1.5 + m_borderSize * 2.0);
84// glColor4dv(m_selectionColour.GetRGBA());
85//
86// DrawLine(m_pointList);
87//
88// glPushMatrix();
89// glTranslated(m_position.m_x, m_position.m_y, 0.0);
90// glRotated(m_angle, 0.0, 0.0, 1.0);
91// glTranslated(-m_position.m_x, -m_position.m_y, 0.0);
92//
93// DrawLine(capPts, GL_LINES);
94//
95// DrawGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0));
96//
97// glPopMatrix();
98//
99// // Draw node selection.
100// DrawCircle(m_pointList[0], 5.0 + m_borderSize / scale, 10, GL_POLYGON);
101// }
102// // Draw Capacitor (layer 2).
103// glLineWidth(1.5);
104// glColor4dv(elementColour.GetRGBA());
105// DrawCircle(m_pointList[0], 5.0, 10, GL_POLYGON);
106// DrawLine(m_pointList);
107//
108// DrawSwitches();
109//
110// glPushMatrix();
111// glTranslated(m_position.m_x, m_position.m_y, 0.0);
112// glRotated(m_angle, 0.0, 0.0, 1.0);
113// glTranslated(-m_position.m_x, -m_position.m_y, 0.0);
114//
115// glColor4dv(elementColour.GetRGBA());
116// DrawLine(capPts, GL_LINES);
117//
118// DrawGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0));
119//
120// glPopMatrix();
121// }
122//}
123
124void Capacitor::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext* gc) const
125{
126 wxColour elementColour;
127 if (m_online) {
128 //if (m_dynEvent)
129 // elementColour = guiColour->eventElement;
130 //else
131 elementColour = guiColour->enabled;
132 }
133 else
134 elementColour = guiColour->disable;
135
136 if (m_inserted) {
137 std::vector<wxPoint2DDouble> capPts;
138 capPts.push_back(wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0));
139 capPts.push_back(wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0));
140 capPts.push_back(wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0));
141 capPts.push_back(wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0));
142
143 if (m_selected) {
144 gc->SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
145 gc->SetBrush(*wxTRANSPARENT_BRUSH);
146
147 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
148
149 // Push the current matrix on stack.
150 gc->PushState();
151 // Rotate the matrix around the object position.
152 gc->Translate(m_position.m_x, m_position.m_y);
153 gc->Rotate(wxDegToRad(m_angle));
154 gc->Translate(-m_position.m_x, -m_position.m_y);
155
156 gc->StrokeLines(2, &capPts[0]);
157 gc->StrokeLines(2, &capPts[2]);
158
159 DrawDCGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0), gc);
160
161 gc->PopState();
162
163 // Draw node selection.
164 gc->SetPen(*wxTRANSPARENT_PEN);
165 gc->SetBrush(wxBrush(guiColour->selection));
166 DrawDCCircle(m_pointList[0], 5.0 + m_borderSize / scale, 10, gc);
167 }
168 // Draw Capacitor (layer 2).
169 // Draw node.
170 gc->SetPen(*wxTRANSPARENT_PEN);
171 gc->SetBrush(wxBrush(elementColour));
172 DrawDCCircle(m_pointList[0], 5.0, 10, gc);
173
174 gc->SetPen(wxPen(elementColour, 2));
175 gc->SetBrush(*wxTRANSPARENT_BRUSH);
176 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
177
178 DrawDCSwitches(guiColour, gc);
179
180 // Push the current matrix on stack.
181 gc->PushState();
182 // Rotate the matrix around the object position.
183 gc->Translate(m_position.m_x, m_position.m_y);
184 gc->Rotate(wxDegToRad(m_angle));
185 gc->Translate(-m_position.m_x, -m_position.m_y);
186
187 gc->SetPen(wxPen(elementColour, 2));
188 gc->SetBrush(*wxTRANSPARENT_BRUSH);
189 gc->StrokeLines(2, &capPts[0]);
190 gc->StrokeLines(2, &capPts[2]);
191
192 DrawDCGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0), gc);
193
194 gc->PopState();
195
196 if (m_dynEvent) {
197 DrawStabilityEventGC(gc, translation, scale, guiColour);
198 }
199 }
200}
201
202void Capacitor::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxDC& dc) const
203{
204 wxColour elementColour;
205 if (m_online) {
206 if (m_dynEvent)
207 elementColour = guiColour->eventElement;
208 else
209 elementColour = guiColour->enabled;
210 }
211 else
212 elementColour = guiColour->disable;
213
214 if (m_inserted) {
215 wxPoint2DDouble p;
216 wxPoint capPts[4];
217 p = wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0);
218 capPts[0] = RotateAround(p, m_position, m_angle);
219
220 p = wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0);
221 capPts[1] = RotateAround(p, m_position, m_angle);
222
223 p = wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0);
224 capPts[2] = RotateAround(p, m_position, m_angle);
225 p = wxPoint2DDouble(m_position.m_x + m_width / 2.0, m_position.m_y - m_height / 2.0 + 10.0);
226 capPts[3] = RotateAround(p, m_position, m_angle);
227
228 std::vector<wxPoint> pointListInt;
229 for (auto& pt : m_pointList) {
230 pointListInt.emplace_back(static_cast<int>(pt.m_x), static_cast<int>(pt.m_y));
231 }
232
233 if (m_selected) {
234 dc.SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
235 dc.SetBrush(*wxTRANSPARENT_BRUSH);
236
237 dc.DrawLines(pointListInt.size(), &pointListInt[0]);
238
239
240 dc.DrawLines(2, &capPts[0]);
241 dc.DrawLines(2, &capPts[2]);
242
243 DrawDCGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0), dc);
244
245 // Draw node selection.
246 dc.SetPen(*wxTRANSPARENT_PEN);
247 dc.SetBrush(wxBrush(guiColour->selection));
248 DrawDCCircle(m_pointList[0], 5.0 + m_borderSize / scale, dc);
249 }
250 // Draw Capacitor (layer 2).
251 // Draw node.
252 dc.SetPen(*wxTRANSPARENT_PEN);
253 dc.SetBrush(wxBrush(elementColour));
254 DrawDCCircle(m_pointList[0], 5.0, dc);
255
256 dc.SetPen(wxPen(elementColour, 2));
257 dc.SetBrush(*wxTRANSPARENT_BRUSH);
258 dc.DrawLines(pointListInt.size(), &pointListInt[0]);
259
260 DrawDCSwitches(guiColour, dc);
261
262 dc.SetPen(wxPen(elementColour, 2));
263 dc.SetBrush(*wxTRANSPARENT_BRUSH);
264 dc.DrawLines(2, &capPts[0]);
265 dc.DrawLines(2, &capPts[2]);
266
267 DrawDCGround(m_position + wxPoint2DDouble(0, -m_height / 2.0 + 10.0), dc);
268 }
269}
270
271void Capacitor::Rotate(bool clockwise)
272{
273 double rotAngle = m_rotationAngle;
274 if (!clockwise) rotAngle = -m_rotationAngle;
275
276 m_angle += rotAngle;
277 if (m_angle >= 360 || m_angle <= -360) m_angle = 0.0;
278 m_pointList[2] = RotateAtPosition(m_pointList[2], rotAngle);
279 m_pointList[3] = RotateAtPosition(m_pointList[3], rotAngle);
280 UpdateSwitchesPosition();
281}
282
284{
285 menu.Append(ID_EDIT_ELEMENT, _("Edit Capacitor"));
286
287 wxMenu* textMenu = new wxMenu();
288
289 textMenu->Append(ID_TXT_NAME, _("Name"));
290 textMenu->Append(ID_TXT_REACTIVE_POWER, _("Reactive power"));
291 textMenu->SetClientData(menu.GetClientData());
292 menu.AppendSubMenu(textMenu, _("Add text"));
293
294 GeneralMenuItens(menu);
295 return true;
296}
297
298bool Capacitor::Contains(wxPoint2DDouble position) const
299{
300 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
301 return m_rect.Contains(ptR);
302}
303
304bool Capacitor::Intersects(wxRect2DDouble rect) const
305{
306 return RotatedRectanglesIntersects(m_rect, rect, m_angle, 0.0);
307}
308
309bool Capacitor::ShowForm(wxWindow* parent, Element* element, wxWindow* workspace)
310{
311 ReactiveShuntElementForm capacitorForm(parent, this);
312 capacitorForm.SetTitle(_("Capacitor"));
313 capacitorForm.CenterOnParent();
314 if (capacitorForm.ShowModal() == wxID_OK) {
315 return true;
316 }
317 return false;
318}
319
320CapacitorElectricalData Capacitor::GetPUElectricalData(double systemPowerBase)
321{
322 CapacitorElectricalData data = m_electricalData;
323 switch (data.reactivePowerUnit) {
324 case ElectricalUnit::UNIT_var: {
325 data.reactivePower = data.reactivePower / systemPowerBase;
326 data.reactivePowerUnit = ElectricalUnit::UNIT_PU;
327 } break;
328 case ElectricalUnit::UNIT_kvar: {
329 data.reactivePower = (data.reactivePower * 1e3) / systemPowerBase;
330 data.reactivePowerUnit = ElectricalUnit::UNIT_PU;
331 } break;
332 case ElectricalUnit::UNIT_Mvar: {
333 data.reactivePower = (data.reactivePower * 1e6) / systemPowerBase;
334 data.reactivePowerUnit = ElectricalUnit::UNIT_PU;
335 } break;
336 default:
337 break;
338 }
339
340 return data;
341}
342
344{
345 Capacitor* copy = new Capacitor();
346 *copy = *this;
347 return copy;
348}
349
350wxString Capacitor::GetTipText() const
351{
352 wxString tipText = m_electricalData.name;
353
354 // TODO: Avoid reactive power calculation.
355 double reactivePower = m_electricalData.reactivePower;
356 if (!m_online)
357 reactivePower = 0.0;
358 else {
359 std::complex<double> v = static_cast<Bus*>(m_parentList[0])->GetElectricalData().voltage;
360 reactivePower *= std::pow(std::abs(v), 2);
361 }
362 tipText += "\n";
363 tipText += _("\nQ = ") + wxString::FromDouble(reactivePower, 5);
364 switch (m_electricalData.reactivePowerUnit) {
365 case ElectricalUnit::UNIT_PU: {
366 tipText += _(" p.u.");
367 } break;
368 case ElectricalUnit::UNIT_var: {
369 tipText += _(" var");
370 } break;
371 case ElectricalUnit::UNIT_kvar: {
372 tipText += _(" kvar");
373 } break;
374 case ElectricalUnit::UNIT_Mvar: {
375 tipText += _(" Mvar");
376 } break;
377 default:
378 break;
379 }
380
381 return tipText;
382}
383
384rapidxml::xml_node<>* Capacitor::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
385{
386 auto elementNode = XMLParser::AppendNode(doc, elementListNode, "Capacitor");
387 XMLParser::SetNodeAttribute(doc, elementNode, "ID", m_elementID);
388
389 SaveCADProperties(doc, elementNode);
390
391 auto electricalProp = XMLParser::AppendNode(doc, elementNode, "ElectricalProperties");
392 auto isOnline = XMLParser::AppendNode(doc, electricalProp, "IsOnline");
393 XMLParser::SetNodeValue(doc, isOnline, m_online);
394 auto name = XMLParser::AppendNode(doc, electricalProp, "Name");
395 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
396 auto reactivePower = XMLParser::AppendNode(doc, electricalProp, "ReactivePower");
397 XMLParser::SetNodeValue(doc, reactivePower, m_electricalData.reactivePower);
398 XMLParser::SetNodeAttribute(doc, reactivePower, "UnitID", static_cast<int>(m_electricalData.reactivePowerUnit));
399
400 SaveSwitchingData(doc, electricalProp);
401
402 return elementNode;
403}
404
405bool Capacitor::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
406{
407 if (!OpenCADProperties(elementNode, parentList)) return false;
408
409 auto electricalProp = elementNode->first_node("ElectricalProperties");
410 if (!electricalProp) return false;
411
412 SetOnline(XMLParser::GetNodeValueInt(electricalProp, "IsOnline"));
413 m_electricalData.name = electricalProp->first_node("Name")->value();
414 m_electricalData.reactivePower = XMLParser::GetNodeValueDouble(electricalProp, "ReactivePower");
415 m_electricalData.reactivePowerUnit =
416 static_cast<ElectricalUnit>(XMLParser::GetAttributeValueInt(electricalProp, "ReactivePower", "UnitID"));
417
418 if (!OpenSwitchingData(electricalProp)) return false;
419 if (m_swData.swTime.size() != 0) SetDynamicEvent(true);
420
421 m_inserted = true;
422 return true;
423}
@ ID_EDIT_ELEMENT
Definition Element.h:77
Node for power elements. All others power elements are connected through this.
Definition Bus.h:87
Shunt capactior power element.
Definition Capacitor.h:39
virtual wxString GetTipText() const
Get the tip text.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
virtual void Rotate(bool clockwise=true)
Rotate the element.
virtual Element * GetCopy()
Get a the element copy.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
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 Capacitor.cpp:32
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:115
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
Definition Element.cpp:377
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
Definition Element.cpp:475
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:189
double GetAngle() const
Get the element angle.
Definition Element.h:214
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:298
virtual void AddChild(Element *child)
Add a child to the child list.
Definition Element.cpp:581
bool SetOnline(bool online=true)
Set if the element is online or offline.
Definition Element.cpp:465
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
Definition Element.cpp:173
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.
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.
Form to edit the reactive shunt element power data.
Abstract class for shunt power elements.
Definition Shunt.h:32
std::vector< double > swTime