49 if (
nodes->size() == 0) {
58 this->
nodes->push_back(tNode);
65 this->
nodes->erase(std::remove(this->
nodes->begin(), this->nodes->end(), node), this->nodes->end());
71 if (tElement ==
nullptr) {
79 if (tElement ==
nullptr) {
81 if (node !=
nullptr) {
93 if (tElement ==
nullptr) {
101 if (node->getName() == name) {
110 if (node->getId() == id) {
119 if (el->getName() == name) {
124 if (voltageSource->getName() == name) {
125 return voltageSource;
133 if (el->getId() == id) {
142 if (voltageSource->getId() == id) {
143 return voltageSource;
160 current += voltageSource->getCurrent();
170 currents +=
toString(voltageSource->getCurrent(), 4) +
" ";
172 if (!currents.empty()) {
180 std::vector<Element*>* vsources =
new std::vector<Element*>(0);
184 vsources->push_back(el);
199void Circuit::removeColumn(Eigen::MatrixXd& matrix,
int colToRemove) {
200 const int numRows = (int)matrix.rows();
201 const int numCols = (int)matrix.cols() - 1;
203 if (colToRemove < numCols) {
204 matrix.block(0, colToRemove, numRows, numCols - colToRemove) = matrix.rightCols(numCols - colToRemove);
207 matrix.conservativeResize(numRows, numCols);
210bool Circuit::solveEquationsNRmethod(
double* eqn,
double* vals, std::vector<int>* removable_ids) {
213 int numofeqs = numofcolumn - (int)removable_ids->size();
216 Eigen::MatrixXd A = Eigen::Map < Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> >(eqn, numofeqs, numofcolumn);
221 for (std::vector<int>::reverse_iterator it = removable_ids->rbegin(); it != removable_ids->rend(); ++it) {
222 id = (*it >= 0 ? *it : -(*it));
231 Node* tNode =
nullptr;
232 for (
int i = 0; i < numofcolumn; i++) {
234 if (tNode !=
nullptr) {
240 WRITE_ERROR(
TL(
"Index of renumbered node exceeded the reduced number of equations."));
249 if (tElem !=
nullptr) {
251 WRITE_ERROR(
TL(
"Index of renumbered element exceeded the reduced number of equations."));
258 WRITE_ERROR(
TL(
"Structural error in reduced circuit matrix."));
262 Eigen::Map<Eigen::VectorXd> b(vals, numofeqs);
263 Eigen::VectorXd x = A.colPivHouseholderQr().solve(b);
266 Eigen::MatrixXd J = A;
269 int max_iter_of_NR = 10;
277 std::vector<double> alpha_notSolution;
279 double alpha_res = 1e-2;
281 double currentSumActual = 0.0;
283 Eigen::VectorXd x_best = x;
284 bool x_best_exist =
true;
286 if (x.maxCoeff() > 10e6 || x.minCoeff() < -10e6) {
287 WRITE_ERROR(
TL(
"Initial solution x used during solving DC circuit is out of bounds.\n"));
299 for (
int i = 0; i < numofeqs - (int)
voltageSources->size(); i++) {
305 for (
auto& node : *
nodes) {
306 if (node->isGround() || node->isRemovable() || node->getNumMatrixRow() == -2) {
309 if (node->getNumMatrixRow() != i) {
310 WRITE_ERROR(
TL(
"wrongly assigned row of matrix A during solving the circuit"));
314 for (
auto it_element = node->getElements()->begin(); it_element != node->getElements()->end(); it_element++) {
317 if ((*it_element)->isEnabled()) {
319 int PosNode_NumACol = (*it_element)->getPosNode()->getNumMatrixCol();
320 int NegNode_NumACol = (*it_element)->getNegNode()->getNumMatrixCol();
322 if (PosNode_NumACol == -1) {
324 diff_voltage = -x[NegNode_NumACol];
325 }
else if (NegNode_NumACol == -1) {
327 diff_voltage = x[PosNode_NumACol];
330 diff_voltage = (x[PosNode_NumACol] - x[NegNode_NumACol]);
333 if ((*it_element)->getPosNode() == node) {
335 vals[i] -= alpha * (*it_element)->getPowerWanted() / diff_voltage;
336 (*it_element)->setCurrent(-alpha * (*it_element)->getPowerWanted() / diff_voltage);
337 if (PosNode_NumACol != -1) {
339 J(i, PosNode_NumACol) -= alpha * (*it_element)->getPowerWanted() / diff_voltage / diff_voltage;
341 if (NegNode_NumACol != -1) {
343 J(i, NegNode_NumACol) += alpha * (*it_element)->getPowerWanted() / diff_voltage / diff_voltage;
347 vals[i] += alpha * (*it_element)->getPowerWanted() / diff_voltage;
352 WRITE_WARNING(
TL(
"The negative node of current source is not the ground."))
353 if (PosNode_NumACol != -1) {
355 J(i, PosNode_NumACol) += alpha * (*it_element)->getPowerWanted() / diff_voltage / diff_voltage;
357 if (NegNode_NumACol != -1) {
359 J(i, NegNode_NumACol) -= alpha * (*it_element)->getPowerWanted() / diff_voltage / diff_voltage;
373 currentSumActual = 0;
375 currentSumActual -= vals[i];
378 if ((A * x - b).norm() < 1e-6) {
382 alpha_notSolution.push_back(alpha);
392 alpha_notSolution.push_back(alpha);
403 }
else if (iterNR == max_iter_of_NR) {
405 alpha_notSolution.push_back(alpha);
413 dx = -J.colPivHouseholderQr().solve(A * x - b);
418 if (alpha_notSolution.empty()) {
423 if ((alpha_notSolution.back() -
alphaBest) < alpha_res) {
424 max_iter_of_NR = 2 * max_iter_of_NR;
428 alpha_res = alpha_res / 10;
430 if (alpha_res < 5e-5) {
433 alpha = alpha_notSolution.back();
434 alpha_notSolution.pop_back();
442 for (
int i = 0; i < numofeqs; i++) {
449 for (
auto& node : *
nodes) {
450 if (node->isGround() || node->isRemovable() || node->getNumMatrixRow() == -2) {
453 if (node->getNumMatrixRow() != i) {
454 WRITE_ERROR(
TL(
"wrongly assigned row of matrix A during solving the circuit"));
456 for (
auto it_element = node->getElements()->begin(); it_element != node->getElements()->end(); it_element++) {
458 if ((*it_element)->isEnabled()) {
460 int PosNode_NumACol = (*it_element)->getPosNode()->getNumMatrixCol();
461 int NegNode_NumACol = (*it_element)->getNegNode()->getNumMatrixCol();
462 if (PosNode_NumACol == -1) {
463 diff_voltage = -x_best[NegNode_NumACol];
464 }
else if (NegNode_NumACol == -1) {
465 diff_voltage = x_best[PosNode_NumACol];
467 diff_voltage = (x_best[PosNode_NumACol] - x_best[NegNode_NumACol]);
470 if ((*it_element)->getPosNode() == node) {
471 (*it_element)->setCurrent(-
alphaBest * (*it_element)->getPowerWanted() / diff_voltage);
477 WRITE_WARNING(
TL(
"The negative node of current source is not the ground."))
493 int numofeqs = numofcolumn - (int)removable_ids->size();
498 Node* tNode =
nullptr;
499 for (
int i = 0; i < numofcolumn; i++) {
501 if (tNode !=
nullptr)
506 WRITE_ERROR(
TL(
"Results deployment during circuit evaluation was unsuccessful."));
514 if (tElem !=
nullptr) {
516 WRITE_ERROR(
TL(
"Results deployment during circuit evaluation was unsuccessful."));
524 WRITE_ERROR(
TL(
"Results deployment during circuit evaluation was unsuccessful."));
529 Node* nextNONremovableNode1 =
nullptr;
530 Node* nextNONremovableNode2 =
nullptr;
533 if (!node->isRemovable()) {
536 if (node->getElements()->size() != 2) {
540 el1 = node->getElements()->front();
541 el2 = node->getElements()->back();
560 y = ((1 - x) * nextNONremovableNode1->
getVoltage()) + (x * nextNONremovableNode2->
getVoltage());
562 node->setRemovability(
false);
567 double currentSum = 0;
568 for (
Element*
const el : *voltageSource->getPosNode()->getElements()) {
570 if (el != voltageSource) {
572 currentSum += (voltageSource->getPosNode()->getVoltage() - el->getTheOtherNode(voltageSource->getPosNode())->
getVoltage()) / el->getResistance();
574 WRITE_WARNING(
TL(
"Cannot assign unambigous electric current value to two voltage sources connected in parallel at the same node."));
578 voltageSource->setCurrent(currentSum);
583 nodes =
new std::vector<Node*>(0);
584 elements =
new std::vector<Element*>(0);
592 nodes =
new std::vector<Node*>(0);
593 elements =
new std::vector<Element*>(0);
601bool Circuit::_solveNRmethod() {
602 double* eqn =
nullptr;
603 double* vals =
nullptr;
604 std::vector<int> removable_ids;
607 createEquationsNRmethod(eqn, vals, &removable_ids);
608 if (!solveEquationsNRmethod(eqn, vals, &removable_ids)) {
619bool Circuit::solve() {
623 return this->_solveNRmethod();
626bool Circuit::createEquationsNRmethod(
double*& eqs,
double*& vals, std::vector<int>* removable_ids) {
634 int m = n - (int)(removable_ids->size() +
voltageSources->size());
637 eqs =
new double[m * n];
638 vals =
new double[m];
640 for (
int i = 0; i < m; i++) {
642 for (
int j = 0; j < n; j++) {
649 for (std::vector<Node*>::iterator it =
nodes->begin(); it !=
nodes->end(); it++) {
650 if ((*it)->isGround() || (*it)->isRemovable()) {
652 (*it)->setNumMatrixRow(-1);
657 bool noVoltageSource = createEquationNRmethod((*it), (eqs + n * i), vals[i], removable_ids);
659 if (noVoltageSource) {
660 (*it)->setNumMatrixRow(i);
663 (*it)->setNumMatrixRow(-2);
665 for (
int j = 0; j < n; j++) {
672 std::sort(removable_ids->begin(), removable_ids->end(), std::less<int>());
677 createEquation((*it), (eqs + n * i), vals[i]);
684bool Circuit::createEquation(
Element* vsource,
double* eqn,
double& val) {
699bool Circuit::createEquationNRmethod(
Node* node,
double* eqn,
double& val, std::vector<int>* removable_ids) {
701 for (std::vector<Element*>::iterator it = node->
getElements()->begin(); it != node->
getElements()->end(); it++) {
703 switch ((*it)->getType()) {
705 if ((*it)->isEnabled()) {
706 x = (*it)->getResistance();
708 Node* nextNONremovableNode = (*it)->getTheOtherNode(node);
709 Element* nextSerialResistor = *it;
711 nextSerialResistor = nextNONremovableNode->
getAnOtherElement(nextSerialResistor);
713 nextNONremovableNode = nextSerialResistor->
getTheOtherNode(nextNONremovableNode);
717 eqn[node->
getId()] += x;
719 if (!nextNONremovableNode->
isGround()) {
720 eqn[nextNONremovableNode->
getId()] -= x;
725 if ((*it)->isEnabled()) {
727 if ((*it)->getPosNode() == node) {
730 x = (*it)->getPowerWanted() /
voltageSources->front()->getVoltage();
738 if ((*it)->getPosNode() == node) {
743 eqn[(*it)->getId()] += x;
745 removable_ids->push_back((*it)->getId());
762 for (std::vector<Node*>::iterator it =
nodes->begin(); it !=
nodes->end(); it++) {
764 if ((*it)->getElements()->size() == 2 && !(*it)->isGround()) {
766 (*it)->setRemovability(
true);
767 for (std::vector<Element*>::iterator it2 = (*it)->getElements()->begin(); it2 != (*it)->getElements()->end(); it2++) {
769 (*it)->setRemovability(
false);
773 if ((*it)->isRemovable()) {
775 removable_ids->push_back((*it)->getId());
778 (*it)->setRemovability(
false);
782 std::sort(removable_ids->begin(), removable_ids->end(), std::less<int>());
794 WRITE_WARNING(
TL(
"Trying to add resistor element into the overhead wire circuit with resistance < 1e-6. "))
796 WRITE_ERROR(
TL(
"Trying to add resistor element into the overhead wire circuit with resistance < 0. "))
805 std::cout <<
"The element: '" + name +
"' already exists.";
809 e =
new Element(name, et, value);
834 this->
elements->erase(std::remove(this->
elements->begin(), this->elements->end(), element), this->elements->end());
841 if (voltageSource->getNegNode() == unusedNode) {
842 voltageSource->setNegNode(newNode);
846 if (voltageSource->getPosNode() == unusedNode) {
847 voltageSource->setPosNode(newNode);
853 if (element->getNegNode() == unusedNode) {
854 element->setNegNode(newNode);
858 if (element->getPosNode() == unusedNode) {
859 element->setPosNode(newNode);
870 if (unusedNode->
getId() != modLastId) {
872 if (node_last !=
nullptr) {
876 if (elem_last !=
nullptr) {
879 WRITE_ERROR(
TL(
"The element or node with the last Id was not found in the circuit!"));
889 for (std::vector<Element*>::iterator it =
elements->begin(); it !=
elements->end(); it++) {
891 (*it)->setEnabled(
true);
896 (*it)->setEnabled(
true);
903 for (std::vector<Node*>::iterator it =
nodes->begin(); it !=
nodes->end(); it++) {
904 if ((*it)->getNumOfElements() < 2) {
906 if ((*it)->getNumOfElements() < 1) {
913 if ((*it)->getPosNode() ==
nullptr || (*it)->getNegNode() ==
nullptr) {
915 WRITE_ERRORF(
TL(
"Circuit Voltage Source '%' is connected to less than two nodes, please adjust the definition of the section (with substation '%')."), (*it)->getName(), substationId);
920 for (std::vector<Element*>::iterator it =
elements->begin(); it !=
elements->end(); it++) {
921 if ((*it)->getPosNode() ==
nullptr || (*it)->getNegNode() ==
nullptr) {
923 WRITE_ERRORF(
TL(
"Circuit Element '%' is connected to less than two nodes, please adjust the definition of the section (with substation '%')."), (*it)->getName(), substationId);
930 bool* nodesVisited =
new bool[num];
931 for (
int i = 0; i < num; i++) {
932 nodesVisited[i] =
false;
936 if (!
getNode(-1)->isGround()) {
938 WRITE_ERRORF(
TL(
"Circuit Node with id '-1' is not the grounded, please adjust the definition of the section (with substation '%')."), substationId);
940 std::vector<Node*>* queue =
new std::vector<Node*>(0);
941 Node* node =
nullptr;
942 Node* neigboringNode =
nullptr;
946 queue->push_back(node);
948 while (!queue->empty()) {
949 node = queue->back();
951 if (!nodesVisited[node->
getId()]) {
952 nodesVisited[node->
getId()] =
true;
954 neigboringNode = (*it)->getTheOtherNode(node);
956 queue->push_back(neigboringNode);
959 nodesVisited[(*it)->getId()] = 1;
962 WRITE_ERRORF(
TL(
"A Circuit Resistor Element connects the ground, please adjust the definition of the section (with substation '%')."), substationId);
968 for (
int i = 0; i < num; i++) {
969 if (nodesVisited[i] == 0) {
971 WRITE_WARNINGF(
TL(
"Circuit Node or Voltage Source with internal id '%' has been not visited during checking of the circuit. The circuit is disconnected, please adjust the definition of the section (with substation '%')."),
toString(i), substationId);
static std::mutex circuit_lock
#define WRITE_WARNINGF(...)
#define WRITE_ERRORF(...)
#define WRITE_WARNING(msg)
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
std::vector< Node * > * nodes
std::vector< Element * > * getCurrentSources()
Node * addNode(std::string name)
double getVoltage(std::string name)
int getNumVoltageSources()
Element * addElement(std::string name, double value, Node *pNode, Node *nNode, Element::ElementType et)
void eraseNode(Node *node)
double getCurrent(std::string name)
Element * getElement(std::string name)
double circuitCurrentLimit
The electric current limit of the voltage sources.
double getTotalCurrentOfCircuitSources()
The sum of voltage source currents in the circuit.
void detectRemovableNodes(std::vector< int > *removable_ids)
double getCurrentLimit()
@ brief Get the electric current limit of this circuit.
std::vector< Element * > * elements
void replaceAndDeleteNode(Node *unusedNode, Node *newNode)
Element * getVoltageSource(int id)
double getTotalPowerOfCircuitSources()
The sum of voltage source powers in the circuit.
void deployResults(double *vals, std::vector< int > *removable_ids)
double getResistance(std::string name)
double alphaBest
Best alpha scaling value.
bool checkCircuit(std::string substationId="")
std::vector< Element * > * voltageSources
@ ALPHA_VOLTAGE_LIMITS
The scaling alpha is applied (is not one] due to voltage limits.
@ ALPHA_NOT_APPLIED
The scaling alpha is not applied (is one)
@ ALPHA_CURRENT_LIMITS
The scaling alpha is applied (is not one) due to current limits.
@ ALPHA_NOT_CONVERGING
The Newton-Rhapson method has reached maximum iterations and no solution of circuit has been found wi...
Node * getNode(std::string name)
static bool myCurrentLimits
whether per-substation overhead-wire current limits should be enforced
std::string & getCurrentsOfCircuitSource(std::string ¤ts)
List of currents of voltage sources as a string.
void eraseElement(Element *element)
void setNegNode(Node *node)
void setPosNode(Node *node)
Node * getTheOtherNode(Node *node)
@ CURRENT_SOURCE_traction_wire
@ VOLTAGE_SOURCE_traction_wire
void setVoltage(double volt)
void setGround(bool isground)
void setNumMatrixCol(int num)
Element * getAnOtherElement(Element *element)
void addElement(Element *element)
void eraseElement(Element *element)
std::vector< Element * > * getElements()