19#include "../../utils/Path.h"
23 m_elementType = TYPE_LINE;
24 for (
int i = 0; i < 2; i++) {
25 for (
int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
29Line::Line(wxString name) :
Branch()
31 m_elementType = TYPE_LINE;
32 for (
int i = 0; i < 2; i++) {
33 for (
int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
35 m_electricalData.name = name;
112 wxGraphicsMatrix identityMatrix = gc->GetTransform();
113 identityMatrix.Set();
115 wxColour elementColour;
120 elementColour = guiColour->enabled;
123 elementColour = guiColour->disable;
125 std::vector<wxPoint2DDouble> pointList = m_pointList;
126 if (!m_inserted && pointList.size() > 0) {
127 wxPoint2DDouble secondPoint = m_position;
128 if (pointList.size() > 2) { secondPoint = pointList[2]; }
129 pointList[1] =
GetSwitchPoint(m_parentList[0], pointList[0], secondPoint);
130 pointList.push_back(m_position);
135 gc->SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
136 gc->SetBrush(*wxTRANSPARENT_BRUSH);
137 if (pointList.size() > 0)
138 gc->StrokeLines(pointList.size(), &pointList[0]);
141 gc->SetPen(*wxTRANSPARENT_PEN);
142 gc->SetBrush(wxBrush(guiColour->selection));
143 if (pointList.size() > 0) {
144 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, 10, gc);
145 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, 10, gc); }
150 gc->SetPen(wxPen(elementColour, 2));
151 gc->SetBrush(*wxTRANSPARENT_BRUSH);
152 if (pointList.size() > 0)
153 gc->StrokeLines(pointList.size(), &pointList[0]);
161 gc->SetPen(*wxTRANSPARENT_PEN);
162 gc->SetBrush(wxBrush(elementColour));
163 if (pointList.size() > 0) {
165 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0, 10, gc); }
171 gc->SetTransform(identityMatrix);
173 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
180 if (m_dynEvent && m_pointList.size() >= 3) {
181 auto pt = m_pointList[1] / 2.0 + m_pointList[2] / 2.0;
182 DrawStabilityEventGC(gc, translation, scale, guiColour,
false, pt);
189 wxColour elementColour;
192 elementColour = guiColour->eventElement;
194 elementColour = guiColour->enabled;
197 elementColour = guiColour->disable;
199 std::vector<wxPoint> pointList;
200 for (
auto pt : m_pointList) {
201 pointList.emplace_back(pt.m_x, pt.m_y);
206 dc.SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
207 dc.SetBrush(*wxTRANSPARENT_BRUSH);
208 if (pointList.size() > 0)
209 dc.DrawLines(pointList.size(), &pointList[0]);
212 dc.SetPen(*wxTRANSPARENT_PEN);
213 dc.SetBrush(wxBrush(guiColour->selection));
214 if (pointList.size() > 0) {
215 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, dc);
216 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, dc); }
221 dc.SetPen(wxPen(elementColour, 2));
222 dc.SetBrush(*wxTRANSPARENT_BRUSH);
223 if (pointList.size() > 0)
224 dc.DrawLines(pointList.size(), &pointList[0]);
232 dc.SetPen(*wxTRANSPARENT_PEN);
233 dc.SetBrush(wxBrush(elementColour));
234 if (pointList.size() > 0) {
236 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0, dc); }
242 if (!m_parentList[0]) {
243 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
244 UpdateSwitchesPosition();
245 UpdatePowerFlowArrowsPosition();
247 if (!m_parentList[1]) {
248 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
249 UpdateSwitchesPosition();
250 UpdatePowerFlowArrowsPosition();
253 if (!m_parentList[0] && !m_parentList[1]) {
254 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
255 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
260void Line::AlignToGrid(
double gridSize)
262 if (gridSize <= 0.0) gridSize = 20.0;
263 if (m_pointList.empty())
return;
266 for (
size_t i = 2; i + 2 < m_pointList.size(); ++i) {
267 m_pointList[i].m_x = std::round(m_pointList[i].m_x / gridSize) * gridSize;
268 m_pointList[i].m_y = std::round(m_pointList[i].m_y / gridSize) * gridSize;
272 if (m_parentList.size() > 0 && m_parentList[0]) {
273 Element* bus1 = m_parentList[0];
276 localPt.m_x = std::round(localPt.m_x / gridSize) * gridSize;
277 double halfW = bus1->
GetWidth() / 2.0;
282 else if (!m_pointList.empty()) {
283 m_pointList[0].m_x = std::round(m_pointList[0].m_x / gridSize) * gridSize;
284 m_pointList[0].m_y = std::round(m_pointList[0].m_y / gridSize) * gridSize;
287 if (m_parentList.size() > 1 && m_parentList[1] && m_pointList.size() > 1) {
288 Element* bus2 = m_parentList[1];
291 localPt.m_x = std::round(localPt.m_x / gridSize) * gridSize;
292 double halfW = bus2->
GetWidth() / 2.0;
297 else if (m_pointList.size() > 1) {
298 m_pointList.back().m_x = std::round(m_pointList.back().m_x / gridSize) * gridSize;
299 m_pointList.back().m_y = std::round(m_pointList.back().m_y / gridSize) * gridSize;
302 UpdateSwitchesPosition();
303 UpdatePowerFlowArrowsPosition();
310 if (m_parentList.size() == 0) {
311 m_position = position;
312 m_parentList.push_back(parent);
314 wxPoint2DDouble parentPt =
318 m_pointList.push_back(parentPt);
319 m_pointList.push_back(
GetSwitchPoint(parent, parentPt, m_position));
321 wxRect2DDouble genRect(0, 0, 0, 0);
322 m_switchRect.push_back(genRect);
325 Bus* parentBus =
static_cast<Bus*
>(parent);
326 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
327 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
332 else if (parent != m_parentList[0]) {
333 Bus* parentBus =
static_cast<Bus*
>(parent);
334 if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
335 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
336 wxMessageDialog msgDialog(
nullptr,
337 _(
"Unable to connect two buses with different nominal voltages.\n"
338 "Use a transformer or edit the bus properties."),
339 _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
340 msgDialog.ShowModal();
344 m_parentList.push_back(parent);
346 wxPoint2DDouble parentPt =
352 wxPoint2DDouble secondPoint = parentPt;
353 if (m_pointList.size() > 2) { secondPoint = m_pointList[2]; }
354 m_pointList[1] =
GetSwitchPoint(m_parentList[0], m_pointList[0], secondPoint);
357 m_pointList.push_back(
GetSwitchPoint(parent, parentPt, m_pointList[m_pointList.size() - 1]));
359 m_pointList.push_back(parentPt);
361 wxRect2DDouble genRect(0, 0, 0, 0);
362 m_switchRect.push_back(genRect);
366 UpdatePowerFlowArrowsPosition();
374 for (
auto it = m_pointList.begin(); it != m_pointList.end(); ++it) {
375 if (rect.Contains(*it))
return true;
383 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
384 if (m_activePickboxID == i) {
385 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
386 UpdateSwitchesPosition();
387 UpdatePowerFlowArrowsPosition();
393 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
394 wxRect2DDouble rect(m_pointList[i].m_x - 5.0, m_pointList[i].m_y - 5.0, 10.0, 10.0);
395 if (rect.Contains(position)) {
396 m_activePickboxID = i;
405 if (m_parentList.size() != 0) { m_pointList.push_back(point); }
410 m_moveStartPt = position;
411 m_movePts = m_pointList;
418 if (parent == m_parentList[0]) {
419 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
422 else if (parent == m_parentList[1]) {
423 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
428 for (
int i = 2; i < (int)m_pointList.size() - 1; i++) {
429 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
435 if (m_activeNodeID == 1) {
436 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
437 if (m_parentList[0]) {
438 m_parentList[0]->RemoveChild(
this);
439 m_parentList[0] =
nullptr;
443 else if (m_activeNodeID == 2) {
444 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
445 if (m_parentList[1]) {
446 m_parentList[1]->RemoveChild(
this);
447 m_parentList[1] =
nullptr;
454 UpdateSwitchesPosition();
455 UpdatePowerFlowArrowsPosition();
465 wxString busName[2] = {
"?",
"?" };
466 if (m_parentList.size() == 2) {
468 for (
Element* element : m_parentList) {
470 Bus* bus =
static_cast<Bus*
>(element);
471 busName[i] = bus->GetElectricalData().name;
477 wxMenu* textMenu =
new wxMenu();
479 textMenu->Append(ID_TXT_NAME, _(
"Name"));
480 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_1_2, _(
"Active power (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
481 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_2_1, _(
"Active power (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
482 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_1_2, _(
"Reactive power (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
483 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_2_1, _(
"Reactive power (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
484 textMenu->Append(ID_TXT_BRANCH_LOSSES, _(
"Losses"));
485 textMenu->Append(ID_TXT_BRANCH_CURRENT_1_2, _(
"Current (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
486 textMenu->Append(ID_TXT_BRANCH_CURRENT_2_1, _(
"Current (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
487 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_1_2, _(
"Fault current (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
488 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_2_1, _(
"Fault current (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
489 textMenu->SetClientData(menu.GetClientData());
490 menu.AppendSubMenu(textMenu, _(
"Add text"));
493 wxMenuItem* addNodeItem =
new wxMenuItem(&menu,
ID_LINE_ADD_NODE, _(
"Insert node"));
494 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/addNode16.png"));
495 menu.Append(addNodeItem);
499 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/removeNode16.png"));
500 menu.Append(addNodeItem);
502 wxMenuItem* deleteItem =
new wxMenuItem(&menu,
ID_DELETE, _(
"Delete"));
503 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/delete16.png"));
504 menu.Append(deleteItem);
508void Line::RemoveNode(wxPoint2DDouble point)
511 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
512 if (m_activePickboxID == i) {
513 m_pointList.erase(m_pointList.begin() + i);
518 UpdateSwitchesPosition();
519 UpdatePowerFlowArrowsPosition();
522void Line::AddNode(wxPoint2DDouble point)
524 int segmentNumber = 0;
526 if (segmentNumber > 0 && segmentNumber < (
int)m_pointList.size() - 2) {
527 m_pointList.insert(m_pointList.begin() + segmentNumber + 1, point);
529 UpdateSwitchesPosition();
530 UpdatePowerFlowArrowsPosition();
535 if (m_pointList.size() > 0) {
537 leftUp = m_pointList[0];
538 rightBottom = m_pointList[0];
539 for (
int i = 1; i < (int)m_pointList.size(); i++) {
540 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
541 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
542 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
543 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
551 lineForm.CenterOnParent();
552 if (lineForm.ShowModal() == wxID_OK) {
560 if (nominalVoltage.size() > 0) {
561 m_electricalData.nominalVoltage = nominalVoltage[0];
562 m_electricalData.nominalVoltageUnit = nominalVoltageUnit[0];
568 if (m_activeNodeID == 1 && parent == m_parentList[0])
return false;
569 if (m_activeNodeID == 2 && parent == m_parentList[1])
return false;
571 if (parent && m_activeNodeID != 0) {
572 wxRect2DDouble nodeRect(0, 0, 0, 0);
573 if (m_activeNodeID == 1) {
574 nodeRect = wxRect2DDouble(m_pointList[0].m_x - 5.0 - m_borderSize, m_pointList[0].m_y - 5.0 - m_borderSize,
575 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
577 if (m_activeNodeID == 2) {
578 nodeRect = wxRect2DDouble(m_pointList[m_pointList.size() - 1].m_x - 5.0 - m_borderSize,
579 m_pointList[m_pointList.size() - 1].m_y - 5.0 - m_borderSize,
580 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
586 Bus* parentBus =
static_cast<Bus*
>(parent);
587 if (!m_parentList[0] && !m_parentList[1]) {
588 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
589 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
591 else if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
592 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
593 wxMessageDialog msgDialog(
nullptr,
594 _(
"Unable to connect two buses with different nominal voltages.\n"
595 "Use a transformer or edit the bus properties."),
596 _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
597 msgDialog.ShowModal();
602 if (m_activeNodeID == 1) {
604 if (m_parentList[1] == parent) {
609 m_parentList[0] = parent;
613 m_pointList[0], -parent->
GetAngle());
616 m_pointList[0] = parentPt;
618 UpdateSwitchesPosition();
619 UpdatePowerFlowArrowsPosition();
622 if (m_activeNodeID == 2) {
623 if (m_parentList[0] == parent) {
628 m_parentList[1] = parent;
630 wxPoint2DDouble parentPt =
634 m_pointList[m_pointList.size() - 1] = parentPt;
636 UpdateSwitchesPosition();
637 UpdatePowerFlowArrowsPosition();
642 if (m_activeNodeID == 1) m_parentList[0] =
nullptr;
643 if (m_activeNodeID == 2) m_parentList[1] =
nullptr;
651 m_pfDirection = pfDirection;
652 UpdatePowerFlowArrowsPosition();
655void Line::UpdatePowerFlowArrowsPosition()
657 std::vector<wxPoint2DDouble> edges;
658 switch (m_pfDirection) {
660 m_powerFlowArrow.clear();
663 for (
int i = 1; i < (int)m_pointList.size() - 1; i++) { edges.push_back(m_pointList[i]); }
666 for (
int i = (
int)m_pointList.size() - 2; i > 0; i--) { edges.push_back(m_pointList[i]); }
676 double rotAngle = m_rotationAngle;
677 if (!clockwise) rotAngle = -m_rotationAngle;
679 if (parent == m_parentList[0]) {
682 else if (parent == m_parentList[1]) {
683 m_pointList[m_pointList.size() - 1] = parent->
RotateAtPosition(m_pointList[m_pointList.size() - 1], rotAngle);
685 UpdateSwitchesPosition();
686 UpdatePowerFlowArrowsPosition();
691 m_pointList = pointList;
692 UpdateSwitchesPosition();
693 UpdatePowerFlowArrowsPosition();
705 wxString tipText = m_electricalData.name;
710 busNumber[0] =
static_cast<Bus*
>(m_parentList[0])->GetElectricalData().number + 1;
711 busNumber[1] =
static_cast<Bus*
>(m_parentList[1])->GetElectricalData().number + 1;
713 tipText += _(
"\nP") + wxString::Format(
"(%d-%d) = ", busNumber[0], busNumber[1]) +
714 wxString::FromDouble(m_electricalData.powerFlow[0].real(), 5) + _(
" p.u.");
715 tipText += _(
"\nQ") + wxString::Format(
"(%d-%d) = ", busNumber[0], busNumber[1]) +
716 wxString::FromDouble(m_electricalData.powerFlow[0].imag(), 5) + _(
" p.u.");
717 tipText += _(
"\nP") + wxString::Format(
"(%d-%d) = ", busNumber[1], busNumber[0]) +
718 wxString::FromDouble(m_electricalData.powerFlow[1].real(), 5) + _(
" p.u.");
719 tipText += _(
"\nQ") + wxString::Format(
"(%d-%d) = ", busNumber[1], busNumber[0]) +
720 wxString::FromDouble(m_electricalData.powerFlow[1].imag(), 5) + _(
" p.u.");
722 if (!m_electricalData.harmonicOrder.empty()) {
723 tipText += _(
"\n\nHarmonic currents:");
725 for (
auto& hCurrent1 : m_electricalData.harmonicCurrent[0]) {
726 auto& hCurrent2 = m_electricalData.harmonicCurrent[1][i];
728 i1.Printf(_(
"\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[0], busNumber[1], std::abs(hCurrent1), wxString(L
'\u2220'), wxRadToDeg(std::arg(hCurrent1)), wxString(L
'\u00B0'));
729 i2.Printf(_(
"\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[1], busNumber[0], std::abs(hCurrent2), wxString(L
'\u2220'), wxRadToDeg(std::arg(hCurrent2)), wxString(L
'\u00B0'));
744 data.name = m_electricalData.name;
746 data.nominalPower = m_electricalData.nominalPower;
747 data.nominalPowerUnit = m_electricalData.nominalPowerUnit;
749 data.nominalVoltage = m_electricalData.nominalVoltage;
750 data.nominalVoltageUnit = m_electricalData.nominalVoltageUnit;
752 data.useLinePower = m_electricalData.useLinePower;
754 data.lineSize = m_electricalData.lineSize;
756 data.resistance = m_electricalData.resistance;
757 data.resistanceUnit = m_electricalData.resistanceUnit;
759 data.indReactance = m_electricalData.indReactance;
760 data.indReactanceUnit = m_electricalData.indReactanceUnit;
762 data.capSusceptance = m_electricalData.capSusceptance;
763 data.capSusceptanceUnit = m_electricalData.capSusceptanceUnit;
765 data.zeroResistance = m_electricalData.zeroResistance;
766 data.zeroIndReactance = m_electricalData.zeroIndReactance;
767 data.zeroCapSusceptance = m_electricalData.zeroCapSusceptance;
769 data.powerFlow[0] = m_electricalData.powerFlow[0];
770 data.powerFlow[1] = m_electricalData.powerFlow[1];
772 data.faultCurrent[0][0] = m_electricalData.faultCurrent[0][0];
773 data.faultCurrent[0][1] = m_electricalData.faultCurrent[0][1];
774 data.faultCurrent[0][2] = m_electricalData.faultCurrent[0][2];
775 data.faultCurrent[1][0] = m_electricalData.faultCurrent[1][0];
776 data.faultCurrent[1][1] = m_electricalData.faultCurrent[1][1];
777 data.faultCurrent[1][2] = m_electricalData.faultCurrent[1][2];
779 data.harmonicOrder = m_electricalData.harmonicOrder;
780 data.harmonicCurrent[0] = m_electricalData.harmonicCurrent[0];
781 data.harmonicCurrent[1] = m_electricalData.harmonicCurrent[1];
784 double lineBasePower = GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
785 double baseVoltage = GetValueFromUnit(data.nominalVoltage, data.nominalVoltageUnit);
786 double systemBaseImpedance = (baseVoltage * baseVoltage) / systemBasePower;
787 double lineBaseImpedance = (baseVoltage * baseVoltage) / lineBasePower;
790 double r = data.resistance;
791 if (data.resistanceUnit == ElectricalUnit::UNIT_OHM_km) data.resistance = r * data.lineSize / systemBaseImpedance;
792 else if (data.resistanceUnit == ElectricalUnit::UNIT_PU) {
793 if (data.useLinePower) data.resistance = (r * lineBaseImpedance) / systemBaseImpedance;
796 data.resistance = r / systemBaseImpedance;
798 data.resistanceUnit = ElectricalUnit::UNIT_PU;
801 double x = data.indReactance;
802 if (data.indReactanceUnit == ElectricalUnit::UNIT_OHM_km) data.indReactance = x * data.lineSize / systemBaseImpedance;
803 else if (data.indReactanceUnit == ElectricalUnit::UNIT_PU) {
804 if (data.useLinePower) data.indReactance = (x * lineBaseImpedance) / systemBaseImpedance;
807 data.indReactance = x / systemBaseImpedance;
809 data.indReactanceUnit = ElectricalUnit::UNIT_PU;
812 double b = data.capSusceptance;
813 if (data.capSusceptanceUnit == ElectricalUnit::UNIT_S_km) data.capSusceptance = b * data.lineSize * systemBaseImpedance;
814 else if (data.capSusceptanceUnit == ElectricalUnit::UNIT_PU) {
815 if (data.useLinePower) data.capSusceptance = (b / lineBaseImpedance) * systemBaseImpedance;
818 data.capSusceptance = b * systemBaseImpedance;
820 data.capSusceptanceUnit = ElectricalUnit::UNIT_PU;
825 double r0 = data.zeroResistance;
826 if (data.useLinePower) data.zeroResistance = (r0 * lineBaseImpedance) / systemBaseImpedance;
829 double x0 = data.zeroIndReactance;
830 if (data.useLinePower) data.zeroIndReactance = (x0 * lineBaseImpedance) / systemBaseImpedance;
833 double b0 = data.zeroCapSusceptance;
834 if (data.useLinePower) data.zeroCapSusceptance = (b0 / lineBaseImpedance) * systemBaseImpedance;
837 data.powerFlow[0] = std::complex<double>(0, 0);
838 data.powerFlow[1] = std::complex<double>(0, 0);
839 data.faultCurrent[0][0] = std::complex<double>(0, 0);
840 data.faultCurrent[0][1] = std::complex<double>(0, 0);
841 data.faultCurrent[0][2] = std::complex<double>(0, 0);
842 data.faultCurrent[1][0] = std::complex<double>(0, 0);
843 data.faultCurrent[1][1] = std::complex<double>(0, 0);
844 data.faultCurrent[1][2] = std::complex<double>(0, 0);
850rapidxml::xml_node<>* Line::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
852 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"Line");
853 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
854 auto cadProp = XMLParser::AppendNode(doc, elementNode,
"CADProperties");
855 auto nodeList = XMLParser::AppendNode(doc, cadProp,
"NodeList");
858 for (
unsigned int i = 0; i < m_pointList.size(); i++) {
861 if ((i != 1) && (i != m_pointList.size() - 2)) {
862 auto nodePos = XMLParser::AppendNode(doc, nodeList,
"Node");
863 XMLParser::SetNodeAttribute(doc, nodePos,
"ID", nodeID);
864 auto nodePosX = XMLParser::AppendNode(doc, nodePos,
"X");
865 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[i].m_x);
866 auto nodePosY = XMLParser::AppendNode(doc, nodePos,
"Y");
867 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[i].m_y);
872 auto parentIDList = XMLParser::AppendNode(doc, cadProp,
"ParentIDList");
873 for (
unsigned int i = 0; i < m_parentList.size(); i++) {
874 if (m_parentList[i]) {
875 auto parentID = XMLParser::AppendNode(doc, parentIDList,
"ParentID");
876 XMLParser::SetNodeAttribute(doc, parentID,
"ID",
static_cast<int>(i));
877 XMLParser::SetNodeValue(doc, parentID, m_parentList[i]->
GetID());
881 auto electricalProp = XMLParser::AppendNode(doc, elementNode,
"ElectricalProperties");
882 auto isOnline = XMLParser::AppendNode(doc, electricalProp,
"IsOnline");
883 XMLParser::SetNodeValue(doc, isOnline, m_online);
884 auto name = XMLParser::AppendNode(doc, electricalProp,
"Name");
885 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
886 auto nominalVoltage = XMLParser::AppendNode(doc, electricalProp,
"NominalVoltage");
887 XMLParser::SetNodeValue(doc, nominalVoltage, m_electricalData.nominalVoltage);
888 XMLParser::SetNodeAttribute(doc, nominalVoltage,
"UnitID",
static_cast<int>(m_electricalData.nominalVoltageUnit));
889 auto nominalPower = XMLParser::AppendNode(doc, electricalProp,
"NominalPower");
890 XMLParser::SetNodeValue(doc, nominalPower, m_electricalData.nominalPower);
891 XMLParser::SetNodeAttribute(doc, nominalPower,
"UnitID",
static_cast<int>(m_electricalData.nominalPowerUnit));
892 auto resistance = XMLParser::AppendNode(doc, electricalProp,
"Resistance");
893 XMLParser::SetNodeValue(doc, resistance, m_electricalData.resistance);
894 XMLParser::SetNodeAttribute(doc, resistance,
"UnitID",
static_cast<int>(m_electricalData.resistanceUnit));
895 auto indReactance = XMLParser::AppendNode(doc, electricalProp,
"IndReactance");
896 XMLParser::SetNodeValue(doc, indReactance, m_electricalData.indReactance);
897 XMLParser::SetNodeAttribute(doc, indReactance,
"UnitID",
static_cast<int>(m_electricalData.indReactanceUnit));
898 auto capSusceptance = XMLParser::AppendNode(doc, electricalProp,
"CapSusceptance");
899 XMLParser::SetNodeValue(doc, capSusceptance, m_electricalData.capSusceptance);
900 XMLParser::SetNodeAttribute(doc, capSusceptance,
"UnitID",
static_cast<int>(m_electricalData.capSusceptanceUnit));
901 auto lineSize = XMLParser::AppendNode(doc, electricalProp,
"LineSize");
902 XMLParser::SetNodeValue(doc, lineSize, m_electricalData.lineSize);
903 auto useLinePower = XMLParser::AppendNode(doc, electricalProp,
"UseLinePower");
904 XMLParser::SetNodeValue(doc, useLinePower, m_electricalData.useLinePower);
906 auto fault = XMLParser::AppendNode(doc, electricalProp,
"Fault");
907 auto zeroResistance = XMLParser::AppendNode(doc, fault,
"ZeroResistance");
908 XMLParser::SetNodeValue(doc, zeroResistance, m_electricalData.zeroResistance);
909 auto zeroIndReactance = XMLParser::AppendNode(doc, fault,
"ZeroIndReactance");
910 XMLParser::SetNodeValue(doc, zeroIndReactance, m_electricalData.zeroIndReactance);
911 auto zeroCapSusceptance = XMLParser::AppendNode(doc, fault,
"ZeroCapSusceptance");
912 XMLParser::SetNodeValue(doc, zeroCapSusceptance, m_electricalData.zeroCapSusceptance);
914 SaveSwitchingData(doc, electricalProp);
919bool Line::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
921 auto cadPropNode = elementNode->first_node(
"CADProperties");
922 if (!cadPropNode)
return false;
925 std::vector<wxPoint2DDouble> ptsList;
926 auto nodePosList = cadPropNode->first_node(
"NodeList");
927 if (!nodePosList)
return false;
928 auto nodePos = nodePosList->first_node(
"Node");
930 double nodePosX = XMLParser::GetNodeValueDouble(nodePos,
"X");
931 double nodePosY = XMLParser::GetNodeValueDouble(nodePos,
"Y");
932 ptsList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
933 nodePos = nodePos->next_sibling(
"Node");
937 auto parentIDList = cadPropNode->first_node(
"ParentIDList");
938 if (!parentIDList)
return false;
939 auto parentNode = parentIDList->first_node(
"ParentID");
940 long parentID[2] = { -1, -1 };
943 wxString(parentNode->first_attribute(
"ID")->value()).ToCLong(&index);
944 wxString(parentNode->value()).ToCLong(&parentID[index]);
945 parentNode = parentNode->next_sibling(
"ParentID");
948 std::vector<wxPoint2DDouble> nodePtsList;
949 nodePtsList.push_back(ptsList[0]);
950 nodePtsList.push_back(ptsList[ptsList.size() - 1]);
953 std::vector<Bus*> dummyBusList;
954 for (
unsigned int i = 0; i < nodePtsList.size(); ++i) {
955 if (parentID[i] == -1)
957 Bus* dummyBus =
new Bus(nodePtsList[i]);
958 dummyBusList.push_back(dummyBus);
962 AddParent(parentList[parentID[i]], nodePtsList[i]);
967 std::vector<wxPoint2DDouble> midPts;
968 for (
unsigned int i = 1; i < ptsList.size() - 1; i++) midPts.push_back(ptsList[i]);
969 m_pointList.insert(m_pointList.begin() + 2, midPts.begin(), midPts.end());
973 for (
auto it = dummyBusList.begin(), itEnd = dummyBusList.end(); it != itEnd; ++it) {
977 dummyBusList.clear();
979 auto electricalProp = elementNode->first_node(
"ElectricalProperties");
980 if (!electricalProp)
return false;
982 SetOnline(XMLParser::GetNodeValueInt(electricalProp,
"IsOnline"));
983 m_electricalData.name = electricalProp->first_node(
"Name")->value();
984 m_electricalData.nominalVoltage = XMLParser::GetNodeValueDouble(electricalProp,
"NominalVoltage");
985 m_electricalData.nominalVoltageUnit =
986 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"NominalVoltage",
"UnitID"));
987 m_electricalData.nominalPower = XMLParser::GetNodeValueDouble(electricalProp,
"NominalPower");
988 m_electricalData.nominalPowerUnit =
989 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"NominalPower",
"UnitID"));
990 m_electricalData.resistance = XMLParser::GetNodeValueDouble(electricalProp,
"Resistance");
991 m_electricalData.resistanceUnit =
992 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"Resistance",
"UnitID"));
993 m_electricalData.indReactance = XMLParser::GetNodeValueDouble(electricalProp,
"IndReactance");
994 m_electricalData.indReactanceUnit =
995 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"IndReactance",
"UnitID"));
996 m_electricalData.capSusceptance = XMLParser::GetNodeValueDouble(electricalProp,
"CapSusceptance");
997 m_electricalData.capSusceptanceUnit =
998 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"CapSusceptance",
"UnitID"));
999 m_electricalData.lineSize = XMLParser::GetNodeValueDouble(electricalProp,
"LineSize");
1000 m_electricalData.useLinePower = XMLParser::GetNodeValueInt(electricalProp,
"UseLinePower");
1002 auto fault = electricalProp->first_node(
"Fault");
1003 m_electricalData.zeroResistance = XMLParser::GetNodeValueDouble(fault,
"ZeroResistance");
1004 m_electricalData.zeroIndReactance = XMLParser::GetNodeValueDouble(fault,
"ZeroIndReactance");
1005 m_electricalData.zeroCapSusceptance = XMLParser::GetNodeValueDouble(fault,
"ZeroCapSusceptance");
1007 if (!OpenSwitchingData(electricalProp))
return false;
PowerFlowDirection
Direction of power flow arrows.
Abstract class for branch power elements.
virtual void UpdateSwitches()
Update the switch position.
virtual void RemoveParent(Element *parent)
Remove a parent.
Node for power elements. All others power elements are connected through this.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual bool Intersects(wxRect2DDouble rect) const =0
Check if the element's rect intersects other rect.
virtual int GetID() const
Get the element ID.
double GetWidth() const
Get the element width.
virtual double PointToLineDistance(wxPoint2DDouble point, int *segmentNumber=nullptr) const
Calculate the distance between a line (formed by point list) and a point.
wxPoint2DDouble GetPosition() const
Get the element position.
double GetAngle() const
Get the element angle.
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
virtual void AddChild(Element *child)
Add a child to the child list.
bool SetOnline(bool online=true)
Set if the element is online or offline.
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
virtual void AddPoint(wxPoint2DDouble point)
Add point to the list of points that connect the element to the bus.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
virtual void Move(wxPoint2DDouble position)
Move the element other position.
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
virtual wxString GetTipText() const
Get the tip text.
virtual bool SetNodeParent(Element *parent)
Set a perent to the node. If all conditions are met, a new parent are added to the element and the po...
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
virtual void RotateNode(Element *parent, bool clockwise=true)
Rotate a node.
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
virtual bool PickboxContains(wxPoint2DDouble position)
Check if a pickbox contains a point. If contains the attributes related to pickbox movement will be c...
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,...
virtual void SetPointList(std::vector< wxPoint2DDouble > pointList)
Set the list of points that connect the element to the bus.
virtual void MovePickbox(wxPoint2DDouble position)
Move the pickbox.
virtual Element * GetCopy()
Get a the element copy.
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.
virtual void CalculatePowerFlowPts(std::vector< wxPoint2DDouble > edges)
Calculate the points of the power flow arrows.
virtual void DrawDCPowerFlowPts(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw power flow arrows.
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