3#include "../../forms/EMTElementForm.h"
4#include "../../utils/PropertiesData.h"
5#include "../../extLibs/fftw/fftw3.h"
6#include "../../elements/GCText.h"
9#include <wx/textfile.h>
11#include <wx/process.h>
12#include <wx/wfstream.h>
13#include <wx/datstrm.h>
15EMTElement::EMTElement()
19EMTElement::EMTElement(wxString name)
24EMTElement::~EMTElement()
38 m_parentList.push_back(parent);
40 wxPoint2DDouble parentPt =
45 m_position = parentPt + wxPoint2DDouble(0.0, 100.0);
48 m_rect = wxRect2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0, m_width, m_height);
50 m_pointList.push_back(parentPt);
51 m_pointList.push_back(
GetSwitchPoint(parent, parentPt, m_position));
52 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0 - 10.0));
53 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0));
57 wxRect2DDouble genRect(0, 0, 0, 0);
58 m_switchRect.push_back(genRect);
62 auto data =
static_cast<Bus*
>(parent)->GetElectricalData();
63 m_data.puVoltage = data.voltage;
64 m_data.baseVoltage = GetValueFromUnit(data.nominalVoltage, data.nominalVoltageUnit);
65 m_data.frequency = data.stabFreq;
74 wxColour elementColour;
77 elementColour = m_dynamicEventColour;
79 elementColour = m_onlineElementColour;
82 elementColour = m_offlineElementColour;
87 gc->SetPen(wxPen(wxColour(m_selectionColour), 2 + m_borderSize * 2.0));
88 gc->SetBrush(*wxTRANSPARENT_BRUSH);
90 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
95 gc->Translate(m_position.m_x, m_position.m_y);
96 gc->Rotate(wxDegToRad(m_angle));
97 gc->Translate(-m_position.m_x, -m_position.m_y);
100 gc->DrawRoundedRectangle(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0, m_width, m_height, 10.0);
105 gc->SetPen(*wxTRANSPARENT_PEN);
106 gc->SetBrush(wxBrush(wxColour(m_selectionColour)));
107 DrawDCCircle(m_pointList[0], 5.0 + m_borderSize / scale, 10, gc);
111 gc->SetPen(*wxTRANSPARENT_PEN);
112 gc->SetBrush(wxBrush(wxColour(elementColour)));
115 gc->SetPen(wxPen(wxColour(elementColour), 2));
116 gc->SetBrush(*wxTRANSPARENT_BRUSH);
117 gc->StrokeLines(m_pointList.size(), &m_pointList[0]);
124 gc->Translate(m_position.m_x, m_position.m_y);
125 gc->Rotate(wxDegToRad(m_angle));
126 gc->Translate(-m_position.m_x, -m_position.m_y);
128 gc->SetPen(wxPen(wxColour(elementColour), 2));
129 gc->SetBrush(*wxWHITE_BRUSH);
131 gc->DrawRoundedRectangle(m_position.m_x - m_width / 2.0, m_position.m_y - m_height / 2.0, m_width, m_height, 10.0);
136 font.SetFaceName(wxT(
"CMU Serif"));
137 font.SetPointSize(10);
139 gc->SetFont(font, m_online ? wxColour(255, 60, 0) : m_offlineElementColour);
140 double textWidth, textHeight;
141 gc->GetTextExtent(_(
"EMT"), &textWidth, &textHeight);
142 gc->DrawText(_(
"EMT"), m_position.m_x - textWidth / 2.0, m_position.m_y - textHeight / 2.0 + 20.0);
144 wxGCDC* gcdc =
new wxGCDC(gc);
145 gcdc->SetPen(wxPen(m_online ? wxColour(0, 60, 255) : m_offlineElementColour, 2));
146 std::vector<wxPoint> ptList;
147 for (
double x = m_position.m_x - m_width / 2.0 + 2; x < (m_position.m_x + m_width / 2.0); x += (m_width - 4.0) / 6.0) {
148 ptList.emplace_back(x, m_position.m_y + std::sin((x - (m_position.m_x - m_width / 2.0 + 2)) / m_width * 6 * M_PI) * 20.0);
150 gcdc->DrawSpline(ptList.size(), ptList.data());
158 wxColour elementColour;
161 elementColour = m_dynamicEventColour;
163 elementColour = m_onlineElementColour;
166 elementColour = m_offlineElementColour;
168 std::vector<wxPoint> pointListInt;
169 for (
auto& pt : m_pointList) {
170 pointListInt.emplace_back(
static_cast<int>(pt.m_x),
static_cast<int>(pt.m_y));
176 dc.SetPen(wxPen(wxColour(m_selectionColour), 2 + m_borderSize * 2.0));
177 dc.SetBrush(*wxTRANSPARENT_BRUSH);
179 dc.DrawLines(pointListInt.size(), &pointListInt[0]);
181 DrawDCRoundedRectRotated(dc, m_position, m_width, m_height, 10.0, m_angle);
184 dc.SetPen(*wxTRANSPARENT_PEN);
185 dc.SetBrush(wxBrush(wxColour(m_selectionColour)));
186 DrawDCCircle(m_pointList[0], 5.0 + m_borderSize / scale, dc);
190 dc.SetPen(*wxTRANSPARENT_PEN);
191 dc.SetBrush(wxBrush(wxColour(elementColour)));
194 dc.SetPen(wxPen(wxColour(elementColour), 2));
195 dc.SetBrush(*wxTRANSPARENT_BRUSH);
196 dc.DrawLines(pointListInt.size(), &pointListInt[0]);
200 dc.SetPen(wxPen(wxColour(elementColour), 2));
201 dc.SetBrush(*wxWHITE_BRUSH);
202 DrawDCRoundedRectRotated(dc, m_position, m_width, m_height, 10.0, m_angle);
205 font.SetFaceName(wxT(
"CMU Serif"));
206 font.SetPointSize(10);
209 gcText.SetFont(font);
211 wxPoint pt = RotateAround(m_position + wxPoint2DDouble(0.0, 20.0), m_position, m_angle);
212 gcText.
Draw(pt, gcText.GetWidth(), gcText.GetHeight(), dc, m_angle, m_online ? wxColour(255, 60, 0) : m_offlineElementColour);
221 dc.SetPen(wxPen(m_online ? wxColour(0, 60, 255) : m_offlineElementColour, 2));
222 std::vector<wxPoint> ptList;
223 for (
double x = m_position.m_x - m_width / 2.0 + 2; x < (m_position.m_x + m_width / 2.0); x += (m_width - 4.0) / 6.0) {
224 wxPoint pt = RotateAround(wxPoint2DDouble(x, m_position.m_y + std::sin((x - (m_position.m_x - m_width / 2.0 + 2)) / m_width * 6 * M_PI) * 20.0), m_position, m_angle);
225 ptList.emplace_back(pt);
227 dc.DrawSpline(ptList.size(), ptList.data());
233 double rotAngle = m_rotationAngle;
234 if (!clockwise) rotAngle = -m_rotationAngle;
237 if (m_angle >= 360 || m_angle <= -360) m_angle = 0.0;
240 UpdateSwitchesPosition();
252 wxString tipText = m_data.name;
253 tipText += wxT(
"\n");
255 tipText += wxString::Format(_(
"\nP = %.5f p.u."), m_data.power.real());
256 tipText += wxString::Format(_(
"\nQ = %.5f p.u."), m_data.power.imag());
257 if (
auto itCurrrent = m_data.currHarmonics.find(1); itCurrrent != m_data.currHarmonics.end())
258 tipText += wxString::Format(_(
"\nI = %.5f A"), std::abs(itCurrrent->second));
260 wxString harmonicsInfo = _(
"\n\nHarmonics info:");
261 bool hasHarmonics =
false;
262 for (
auto& [order, current] : m_data.currHarmonics) {
265 harmonicsInfo += wxString::Format(_(
"\nIh(%d): %.5f%s%.2f%s A"), order, std::abs(current), wxString(L
'\u2220'), wxRadToDeg(std::arg(current)), wxString(L
'\u00B0'));
269 if (hasHarmonics) tipText += harmonicsInfo;
277 emtForm.SetTitle(_(
"Electromagnetic Transient"));
278 emtForm.CenterOnParent();
279 if (emtForm.ShowModal() == wxID_OK) {
285rapidxml::xml_node<>* EMTElement::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
287 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"EMTElement");
288 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
290 SaveCADProperties(doc, elementNode);
292 auto electricalProp = XMLParser::AppendNode(doc, elementNode,
"ElectricalProperties");
293 auto isOnline = XMLParser::AppendNode(doc, electricalProp,
"IsOnline");
294 XMLParser::SetNodeValue(doc, isOnline, m_online);
295 auto name = XMLParser::AppendNode(doc, electricalProp,
"Name");
296 XMLParser::SetNodeValue(doc, name, m_data.name);
297 auto atpFilePath = XMLParser::AppendNode(doc, electricalProp,
"ATPFilePath");
298 XMLParser::SetNodeValue(doc, atpFilePath, m_data.atpFile.GetFullPath());
299 auto atpNodeName = XMLParser::AppendNode(doc, electricalProp,
"ATPNodeName");
300 XMLParser::SetNodeValue(doc, atpNodeName, m_data.atpNodeName);
301 auto stepSize = XMLParser::AppendNode(doc, electricalProp,
"StepSize");
302 XMLParser::SetNodeValue(doc, stepSize, m_data.stepSize);
303 auto cyclesToSS = XMLParser::AppendNode(doc, electricalProp,
"CyclesToSS");
304 XMLParser::SetNodeValue(doc, cyclesToSS, m_data.cyclesToSS);
305 auto recordFrequency = XMLParser::AppendNode(doc, electricalProp,
"RecordFrequency");
306 XMLParser::SetNodeValue(doc, recordFrequency, m_data.recordFrequency);
307 auto useMedianFilter = XMLParser::AppendNode(doc, electricalProp,
"UseMedianFilter");
308 XMLParser::SetNodeValue(doc, useMedianFilter, m_data.useMedianFilter);
309 auto numMaxHarmonics = XMLParser::AppendNode(doc, electricalProp,
"NumMaxHarmonics");
310 XMLParser::SetNodeValue(doc, numMaxHarmonics, m_data.numMaxHarmonics);
315bool EMTElement::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
317 if (!OpenCADProperties(elementNode, parentList))
return false;
319 auto electricalProp = elementNode->first_node(
"ElectricalProperties");
320 if (!electricalProp)
return false;
322 SetOnline(XMLParser::GetNodeValueInt(electricalProp,
"IsOnline"));
323 m_data.name = electricalProp->first_node(
"Name")->value();
324 m_data.atpFile = wxFileName(electricalProp->first_node(
"ATPFilePath")->value());
325 m_data.atpNodeName = electricalProp->first_node(
"ATPNodeName")->value();
326 m_data.stepSize = XMLParser::GetNodeValueDouble(electricalProp,
"StepSize");
327 m_data.cyclesToSS = XMLParser::GetNodeValueInt(electricalProp,
"CyclesToSS");
328 m_data.recordFrequency = XMLParser::GetNodeValueInt(electricalProp,
"RecordFrequency");
329 m_data.useMedianFilter = XMLParser::GetNodeValueInt(electricalProp,
"UseMedianFilter");
330 m_data.numMaxHarmonics = XMLParser::GetNodeValueInt(electricalProp,
"NumMaxHarmonics");
336wxArrayString EMTElement::GetATPNodes(wxArrayString atpFile)
338 wxArrayString nodeList;
340 std::vector<wxString> mode0Cards{
"/OUTPUT",
"BLANK",
"TACS",
"INCLUDE",
"TRANSFORMER" };
341 std::vector<wxString> mode1Cards{
"/SOURCE" };
342 std::vector<wxString> mode2Cards{
"/BRANCH",
"/SWITCH", };
343 wxString ground =
"000000";
344 for (wxString line : atpFile) {
345 if (line.IsEmpty())
continue;
346 if (tolower(line[0]) ==
'c')
continue;
348 bool cardHeader =
false;
349 for (wxString card : mode0Cards) {
350 if (line.Find(card) != wxNOT_FOUND) {
356 for (wxString card : mode1Cards) {
357 if (line.Find(card) != wxNOT_FOUND) {
363 for (wxString card : mode2Cards) {
364 if (line.Find(card) != wxNOT_FOUND) {
370 if (cardHeader)
continue;
377 node = line(2, 6).Trim();
378 node.Replace(
" ",
"0");
379 if (node != ground && node[5] >=
'A') {
380 if (nodeList.Index(node(0, 5)) == wxNOT_FOUND)
381 nodeList.Add(node(0, 5));
386 if (node != ground && node[5] >=
'A') {
387 if (nodeList.Index(node(0, 5)) == wxNOT_FOUND)
388 nodeList.Add(node(0, 5));
391 if (node != ground && node[5] >=
'A') {
392 if (nodeList.Index(node(0, 5)) == wxNOT_FOUND)
393 nodeList.Add(node(0, 5));
401bool EMTElement::SetATPParameter(wxTextFile& atpFile,
const wxString& card,
const int& line,
const int& initPos,
const int& size,
const wxString& value)
403 bool foundCard =
false;
405 wxString lineStr =
"";
406 lineStr = atpFile.GetFirstLine();
407 while (!atpFile.Eof())
409 if (tolower(lineStr[0]) ==
'c') {
410 lineStr = atpFile.GetNextLine();
413 if (lineStr.Find(card) != wxNOT_FOUND) {
417 if (foundCard) currLine++;
418 if (foundCard && currLine == line) {
419 for (
int i = initPos; i < initPos + size; i++) {
420 lineStr[i] = value[i - initPos];
422 atpFile.RemoveLine(atpFile.GetCurrentLine());
423 atpFile.InsertLine(lineStr, atpFile.GetCurrentLine());
427 lineStr = atpFile.GetNextLine();
432bool EMTElement::AddConnectionToNode(wxTextFile& atpFile,
const wxString& node)
435 bool hasBusData =
false;
437 if (!m_parentList.empty()) {
438 if (m_parentList[0] !=
nullptr) {
439 Bus* bus =
static_cast<Bus*
>(m_parentList[0]);
440 busData = bus->GetElectricalData();
445 wxString switchMask =
" PSPNC%c%s%c MEASURING %d";
446 wxString sourceMask =
"14PSPNC%c %s%s%s -1. 100.";
447 wxString lineStr =
"";
448 int outCardPos = -1, switchCardPos = -1, sourceCardPos = -1;
449 lineStr = atpFile.GetFirstLine();
450 while (!atpFile.Eof())
452 if (lineStr.IsEmpty()) {
453 lineStr = atpFile.GetNextLine();
456 if (tolower(lineStr[0]) ==
'c') {
457 lineStr = atpFile.GetNextLine();
461 if (lineStr.Find(
"/SWITCH") != wxNOT_FOUND)
462 switchCardPos = atpFile.GetCurrentLine() + 1;
464 if (lineStr.Find(
"/SOURCE") != wxNOT_FOUND)
465 sourceCardPos = atpFile.GetCurrentLine() + 1;
467 if (lineStr.Find(
"/OUTPUT") != wxNOT_FOUND)
468 outCardPos = atpFile.GetCurrentLine();
470 lineStr = atpFile.GetNextLine();
473 if (outCardPos < 0)
return false;
475 if (switchCardPos < 0 && outCardPos > 0) {
476 atpFile.InsertLine(
"/SWITCH", outCardPos);
477 switchCardPos = outCardPos + 1;
480 if (sourceCardPos < 0 && outCardPos > 0) {
481 atpFile.InsertLine(
"/SOURCE", outCardPos);
482 sourceCardPos = outCardPos + 1;
485 for (
char i =
'A'; i <=
'C'; ++i) {
486 lineStr = wxString::Format(switchMask, i, node, i, i ==
'A' ? 1 : 0);
487 atpFile.InsertLine(lineStr, switchCardPos + (i -
'A'));
491 double voltage = std::abs(m_data.puVoltage) * m_data.baseVoltage * (std::sqrt(2) / std::sqrt(3));
492 wxString ampl = wxString::FromCDouble(voltage, 3);
493 wxString freq = wxString::FromCDouble(m_data.frequency, 5);
495 while (freq.Length() < 10) freq =
" " + freq;
496 while (ampl.Length() < 10) ampl =
" " + ampl;
498 int cardPos = sourceCardPos;
499 for (
char i =
'A'; i <=
'C'; ++i) {
500 wxString angle = wxString::FromCDouble(std::arg(m_data.puVoltage) * 180.0 / M_PI - 120.0 * (i -
'A'), 5);
503 while (angle.Length() < 10) angle =
" " + angle;
505 lineStr = wxString::Format(sourceMask, i, ampl, freq, angle);
506 atpFile.InsertLine(lineStr, cardPos);
511 for (
size_t j = 0; j < busData.harmonicOrder.size(); ++j) {
512 int order = busData.harmonicOrder[j];
513 if (order == 1)
continue;
515 std::complex<double> harmVoltage = busData.harmonicVoltage[j];
517 voltage = std::abs(busData.harmonicVoltage[j]) * m_data.baseVoltage * (std::sqrt(2) / std::sqrt(3));
518 wxString amplH = wxString::FromCDouble(voltage, 3);
519 while (amplH.Length() < 10) amplH =
" " + amplH;
521 wxString freqH = wxString::FromCDouble(m_data.frequency *
static_cast<double>(order), 5);
522 while (freqH.Length() < 10) freqH =
" " + freqH;
524 double angleValue = std::arg(harmVoltage) * 180.0 / M_PI;
525 if (order % 3 == 1) {
526 angleValue += -120.0 * (i -
'A');
528 else if (order % 3 == 2) {
529 angleValue += 120.0 * (i -
'A');
533 wxString angleH = wxString::FromCDouble(angleValue, 5);
534 while (angleH.Length() < 10) angleH =
" " + angleH;
536 lineStr = wxString::Format(sourceMask, i, amplH, freqH, angleH);
537 atpFile.InsertLine(lineStr, cardPos);
546std::vector<double> EMTElement::MedianFilter(
const std::vector<double>& data)
550 size_t n = data.size();
551 std::vector<double> result(data);
554 if (data.empty() || n < 1)
563 double* extension =
new double[n + 4];
568 memcpy(extension + 2, data.data(), n *
sizeof(
double));
569 for (
int i = 0; i < 2; ++i)
571 extension[i] = data[1 - i];
572 extension[n + 2 + i] = data[n - 1 - i];
575 result = DoMedianFilter(extension, result, n + 4);
582bool EMTElement::CalculateCurrent(wxString& errorMsg,
const bool& saveFFTData)
584 wxFileName fileName(m_data.atpFile);
585 if (!fileName.IsOk()) {
586 errorMsg = wxString::Format(_(
"Invalid ATP file path for the electromagnetic element \"%s\"."), m_data.name);
591 double fundFreq = m_data.frequency;
592 double timeSim = (1.0 / fundFreq) *
static_cast<double>(m_data.cyclesToSS);
595 wxString atpFolder = m_data.atpPath.GetPath();
603 wxTextFile origFile(m_data.atpFile.GetFullPath());
605 fileName.SetPath(m_data.atpWorkFolder);
606 wxString name = fileName.GetFullPath();
607 wxTextFile copyFile(fileName.GetFullPath());
608 if (origFile.Open()) {
610 for (
size_t i = 0; i < origFile.GetLineCount(); i++) {
611 copyFile.AddLine(origFile.GetLine(i));
615 wxString stepSizeStr = wxString::Format(
"%.2E", m_data.stepSize);
616 stepSizeStr.Replace(wxT(
","), wxT(
"."));
617 wxString timeStr = wxString::Format(
"%.6f", timeSim);
618 timeStr.Replace(wxT(
","), wxT(
"."));
619 SetATPParameter(copyFile, wxT(
"BEGIN NEW DATA CASE"), 1, 0, 8, stepSizeStr);
620 SetATPParameter(copyFile, wxT(
"BEGIN NEW DATA CASE"), 1, 8, 8, timeStr);
621 SetATPParameter(copyFile, wxT(
"BEGIN NEW DATA CASE"), 2, 0, 8, wxT(
"99999999"));
622 SetATPParameter(copyFile, wxT(
"BEGIN NEW DATA CASE"), 2, 8, 8, wxString::Format(
"%8d", m_data.recordFrequency));
625 AddConnectionToNode(copyFile, m_data.atpNodeName);
630 errorMsg = wxString::Format(_(
"Fail to open ATP file of the electromagnetic element \"%s\"."), m_data.name);
635 wxString cmd = m_data.atpPath.GetFullPath() + wxT(
" both ") + fileName.GetFullPath() + wxT(
" s -R");
636 wxExecute(cmd, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_NODISABLE,
nullptr, &env);
639 wxDir dir(atpFolder);
640 if (dir.IsOpened()) {
642 bool cont = dir.GetFirst(&file, wxT(
"*.tmp"), wxDIR_FILES);
644 wxRemoveFile(atpFolder + wxT(
"/") + file);
645 cont = dir.GetNext(&file);
649 fileName.SetFullName(fileName.GetName() + wxT(
".pl4"));
650 wxFileInputStream pl4File(fileName.GetFullPath());
652 if (pl4File.IsOk() && pl4File.GetSize() > 0) {
653 wxDataInputStream store(pl4File);
654 store.UseBasicPrecisions();
655 float* buffer =
new float[2];
656 std::vector<double> timeVec;
657 std::vector<double> valueVec;
658 double oldTime = 0.0;
660 while (!pl4File.Eof()) {
661 store.ReadFloat(buffer, 2);
662 if (read16Bytes > 11 && (buffer[0] >= oldTime)) {
664 timeVec.emplace_back(buffer[0]);
665 valueVec.emplace_back(buffer[1]);
671 if (m_data.useMedianFilter)
672 valueVec = MedianFilter(valueVec);
675 double dataStepSize = timeVec[1] - timeVec[0];
676 bool useRemainder =
false;
677 size_t n = ceil(1.0 / (dataStepSize * fundFreq));
680 if (n > valueVec.size()) {
685 double fs = 1.0 / dataStepSize;
686 double df = fs /
static_cast<double>(n);
689 double rmder = abs(remainder(fundFreq, df));
691 while (rmder > 1e-3) {
693 df = fs /
static_cast<double>(n);
695 if (abs(remainder(fundFreq, df)) < rmder) minRmderN = n;
697 rmder = abs(remainder(fundFreq, df));
701 df = fs /
static_cast<double>(n);
707 double dtWindow = timeVec[timeVec.size() - 1] - timeVec[timeVec.size() - n];
712 out = (fftw_complex*)fftw_malloc(
sizeof(fftw_complex) * (n / 2 + 1));
713 in = (
double*)fftw_malloc(
sizeof(
double) * n);
715 for (
size_t i = (valueVec.size() - n); i < valueVec.size(); ++i) {
716 size_t index = i - (valueVec.size() - n);
717 in[index] = valueVec[i];
720 p = fftw_plan_dft_r2c_1d(n, in, out, FFTW_ESTIMATE);
722 fftw_destroy_plan(p);
724 double ampCorrection = 2.0 /
static_cast<double>(n);
728 double fundMagnitude = 0.0;
729 for (
size_t i = 0; i < (n / 2 + 1); i++) {
730 double freq =
static_cast<double>(i) * df;
731 if (remainder(freq, fundFreq)) {
733 fundMagnitude = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]) * ampCorrection;
738 errorMsg = wxString::Format(_(
"Fail to identify the fundamental frequency of the electromagnetic element \"%s\"."), m_data.name);
743 m_data.currHarmonics.clear();
745 for (
size_t i = 0; i < (n / 2 + 1); i++) {
746 double freq =
static_cast<double>(i) * df;
747 int order =
static_cast<int>(round(freq / fundFreq));
749 if (order > m_data.numMaxHarmonics)
break;
751 double magnitude = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]) * ampCorrection;
752 if ((magnitude / fundMagnitude) > (m_data.harmonicsThreshold / 100.0)) {
755 m_data.currHarmonics[order] = std::complex<double>(out[i][0], out[i][1]) * ampCorrection / sqrt(2.0);
760 m_data.atpData.clear();
761 m_data.inFFTData.clear();
762 m_data.outFFTData.clear();
763 for (
size_t i = 0; i < valueVec.size(); i++) {
764 m_data.atpData.emplace_back(std::make_pair(timeVec[i], valueVec[i]));
766 for (
size_t i = 0; i < n; i++) {
767 m_data.inFFTData.emplace_back(std::make_pair(timeVec[i + timeVec.size() - n], in[i]));
769 for (
size_t i = 0; i < (n / 2 + 1); i++) {
770 std::complex<double> value(out[i][0] * ampCorrection, out[i][1] * ampCorrection);
771 double freq =
static_cast<double>(i) * df;
772 m_data.outFFTData.emplace_back(std::make_pair(freq, value));
781 wxString atpErrorMsg = _(
"No error found.");
783 fileName.SetFullName(fileName.GetName() + wxT(
".lis"));
784 wxTextFile lisFile(fileName.GetFullPath());
786 if (!lisFile.Exists()) {
787 errorMsg = wxString::Format(_(
"Fail to run ATP file of the electromagnetic element \"%s\".\nThe ATP program did not return any error messages."), m_data.name);
790 if (lisFile.Open()) {
791 wxString line = lisFile.GetFirstLine() +
"\n";
792 bool foundError =
false;
795 while (!lisFile.Eof())
797 if (line.Find(wxT(
"KILL ")) != wxNOT_FOUND) {
801 if (line.Find(wxT(
"----------")) != wxNOT_FOUND) {
804 if (foundError && lineCount == 1)
805 atpErrorMsg += line +
" ";
806 line = lisFile.GetNextLine();
812 errorMsg = wxString::Format(_(
"Fail to run ATP file of the electromagnetic element \"%s\".\nThe ATP program did not return any error messages."), m_data.name);
816 errorMsg = wxString::Format(_(
"Fail to run ATP file of the electromagnetic element \"%s\".\nThe ATP returned the following error message:\n\"%s\""), m_data.name, atpErrorMsg);
822void EMTElement::UpdateData(
const PropertiesData* properties,
bool updateVoltageBase)
824 if (properties !=
nullptr) {
825 m_data.frequency = properties->GetSimulationPropertiesData().stabilityFrequency;
826 m_data.atpPath = properties->GetGeneralPropertiesData().atpPath;
827 m_data.atpWorkFolder = properties->GetGeneralPropertiesData().atpWorkFolder;
829 if (!m_parentList.empty()) {
830 Bus* bus =
static_cast<Bus*
>(m_parentList[0]);
831 if (bus !=
nullptr) {
832 auto busData = bus->GetElectricalData();
833 std::complex<double> voltage = std::complex<double>(1.0, 0.0);
834 if (abs(busData.voltage) > 1e-3)
835 m_data.puVoltage = busData.voltage;
836 if (updateVoltageBase) {
837 m_data.baseVoltage = GetValueFromUnit(busData.nominalVoltage, busData.nominalVoltageUnit);
844std::vector<double> EMTElement::DoMedianFilter(
double* extension, std::vector<double>& result,
const int& n)
847 for (
int i = 2; i < n - 2; ++i)
851 for (
int j = 0; j < 5; ++j)
852 window[j] = extension[i - 2 + j];
854 for (
int j = 0; j < 3; ++j)
858 for (
int k = j + 1; k < 5; ++k)
859 if (window[k] < window[min])
862 const double temp = window[j];
863 window[j] = window[min];
867 result[i - 2] = window[2];
Node for power elements. All others power elements are connected through this.
Element to connect ATP-EMTP.
virtual void Rotate(bool clockwise=true)
Rotate the element.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
virtual void DrawDC(wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual wxString GetTipText() const
Get the tip text.
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,...
virtual bool ShowForm(wxWindow *parent, Element *element)
Show element data form.
virtual Element * GetCopy()
Get a the element copy.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
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 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.
Class to draw text on Graphics Context using wxWidgets.
virtual void Draw(wxPoint2DDouble position, wxGraphicsContext *gc, double angle=0.0, wxColour colour= *wxBLACK) const
Draw the text in wxGraphicsContext.
virtual void SetText(wxString text)
Set correctly a new text string.
virtual void DrawDCSwitches(wxGraphicsContext *gc) const
Draw switch.
virtual void UpdateSwitches()
Update the switch position.
virtual wxPoint2DDouble GetSwitchPoint(Element *parent, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
Get the correct switch position.
General and simulation data manager.