/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file Circuit.h14/// @author Jakub Sevcik (RICE)15/// @author Jan Prikryl (RICE)16/// @date 2019-12-1517///18/// @note based on console-based C++ DC circuits simulator,19/// https://github.com/rka97/Circuits-Solver by20/// Ahmad Khaled, Ahmad Essam, Omnia Zakaria, Mary Nader21/// and available under MIT license, see https://github.com/rka97/Circuits-Solver/blob/master/LICENSE22///23// Representation of electric circuit of overhead wires24/****************************************************************************/25#pragma once26#include <config.h>2728#include <vector>29#ifdef HAVE_EIGEN30#ifdef _MSC_VER31#pragma warning(push)32#pragma warning(disable: 4127 4464 5031)33#endif34// avoid warnings in clang35#ifdef __clang__36#pragma clang system_header37#endif38#include <Eigen/Dense>39#include <Eigen/Geometry>40#include <Eigen/Sparse>41#ifdef _MSC_VER42#pragma warning(pop)43#endif44#endif4546#include "Element.h"4748// ===========================================================================49// class declarations50// ===========================================================================51class Node;525354// ===========================================================================55// class definitions56// ===========================================================================57/**58* All interactions will be through this class, the user will know nothing about the other classes,59* and will interact only through the names of the elements/nodes.60*/61class Circuit {6263private:6465std::vector<Node*>* nodes;66std::vector<Element*>* elements;67std::vector<Element*>* voltageSources;6869int lastId;70bool iscleaned;7172/// @brief The electric current limit of the voltage sources.73double circuitCurrentLimit;7475/**76* @brief Best alpha scaling value.77*78* This parameter is used to scale down the power demands of current sources (vehicles79* that draw power from the circuit) so that a solution of the system can be found.80* Note: the system is nonlinear (quadratic), hence in some cases (typically too high81* power demands) a solution cannot be found. In that moment we decrease all power82* requirements by `alpha` and try to solve again, until we find alpha that ensures83* stable solution. This is then reported as alphaBest.84*/85double alphaBest;86public:87/**88* @brief Flag of alpha scaling parameter89*90* returns ALPHA_NOT_APPLIED => alpha should be 191* returns ALPHA_CURRENT_LIMITS => alpha is lower than one due to electric current limits of the substation92* returns ALPHA_VOLTAGE_LIMITS => alpha is not one due to inability of network to transfer requested power due to overhead wire resistance93* returns ALPHA_NOT_CONVERGING => number of allowed iterations exceeded94*/95enum alphaFlag {96/// @brief The scaling alpha is not applied (is one)97ALPHA_NOT_APPLIED = 0,98/// @brief The scaling alpha is applied (is not one) due to current limits99ALPHA_CURRENT_LIMITS,100/// @brief The scaling alpha is applied (is not one] due to voltage limits101ALPHA_VOLTAGE_LIMITS,102/// @brief The Newton-Rhapson method has reached maximum iterations and no solution of circuit has been found with actual value of alpha103ALPHA_NOT_CONVERGING104};105private:106alphaFlag alphaReason;107108public:109Node* getNode(std::string name);110Element* getElement(std::string name);111Node* getNode(int id);112Element* getVoltageSource(int id);113std::vector<Element*>* getCurrentSources();114115/// @brief The sum of voltage source powers in the circuit116double getTotalPowerOfCircuitSources();117/// @brief The sum of voltage source currents in the circuit118double getTotalCurrentOfCircuitSources();119/// @brief List of currents of voltage sources as a string120std::string& getCurrentsOfCircuitSource(std::string& currents);121122void lock();123void unlock();124125/// @brief return alphaBest variable, the best alpha scaling value126double getAlphaBest() {127return alphaBest;128}129130/// @brief return the reason why `alpha` scaling value has been used131alphaFlag getAlphaReason() {132return alphaReason;133}134135private:136137Element* getElement(int id);138/*139* detects removable nodes = sets node variable "isremovable" to true if node is removable and adds id of such node to "removable_ids" vector140* node is denoted as removable if it is connected just to 2 elements and both of them are resistor141* the reason is that in such case there are two serial resistor and we can only sum their resistance value142*143* "removable_ids" vector is sort from the least to the greatest144*/145void detectRemovableNodes(std::vector<int>* removable_ids);146147void deployResults(double* vals, std::vector<int>* removable_ids);148149#ifdef HAVE_EIGEN150/*151* creates all of the equations that represent the circuit152* in the form Ax = B(1/x) where A and B are matrices153* @param eqn : A154* @param vals : B155*/156bool createEquationsNRmethod(double*& eqs, double*& vals, std::vector<int>* removable_ids);157158/*159* creates the nodal equation of the node 'node' GV = I160* in the form Ax = B(1/x) where A is a matrix with one row161* @param node : the node to be analyzed162* @param eqn : A163* @param val : B164*/165bool createEquationNRmethod(Node* node, double* eqn, double& val, std::vector<int>* removable_ids);166167/**168* @brief Create the equation of the voltage source.169* Create the equation V2 - V1 = E of the voltage source in the form Ax = B,170* where A is a matrix with one row, B a value171* @param[in] vsource The voltage source172* @param[in] eqn : A173* @param[in] val : B174* @return ???175*/176bool createEquation(Element* vsource, double* eqn, double& val);177178/*179* removes the "colToRemove"-th column from matrix "matrix"180*/181void removeColumn(Eigen::MatrixXd& matrix, const int colToRemove);182183/*184* solves the system of nonlinear equations Ax = B(1/x)185* @param eqn : A186* @param vals : B187*/188bool solveEquationsNRmethod(double* eqn, double* vals, std::vector<int>*);189190bool _solveNRmethod();191192#endif193public:194195// a Constructor, same functionality as "init" functions196Circuit();197// RICE_CHECK: Is this a traction substation current limit, global for all substations?198/// @brief Constructor with user-specified current limit parameter.199Circuit(double currentLimit);200201// adds an element with name "name", type "type" and value "value" to positive node "pNode" and negative node "nNode""202Element* addElement(std::string name, double value, Node* pNode, Node* nNode, Element::ElementType et);203204void eraseElement(Element* element);205206// adds a node with name "name"207Node* addNode(std::string name);208209// erases a node with name "name"210void eraseNode(Node* node);211212// gets current through element "name"213double getCurrent(std::string name);214215// gets voltage across element or node "name"216double getVoltage(std::string name);217218// gets the resistance of an element.219double getResistance(std::string name);220221// gets the number of voltage sources in the circuit.222int getNumVoltageSources();223224// checks if the circuit's connections are correct.225bool checkCircuit(std::string substationId = "");226227#ifdef HAVE_EIGEN228// solves the circuit and deploys the results229bool solve();230#endif231232// cleans up after superposition.233void cleanUpSP();234235//replaces unusedNode with newNode everywhere in the circuit, modifies the ids of other nodes and elements, decreases the id by one and deletes unusedNode236void replaceAndDeleteNode(Node* unusedNode, Node* newNode);237238// returns lastId239int getLastId() {240return lastId;241};242243// decreases lastId by one244void decreaseLastId() {245lastId--;246};247248/// RICE_CHECK: Is this identical to the current limit of a traction substation?249/// @brief Set the electric current limit of this circuit.250void setCurrentLimit(double myCurrentLimit) {251circuitCurrentLimit = myCurrentLimit;252};253254/// @ brief Get the electric current limit of this circuit.255double getCurrentLimit() {256return circuitCurrentLimit;257};258};259260261