Model

This is the most abstract part of the project. It is also the part where efficiency is most important. This includes both processing performance and memory usage. The model should never depend on the GUI, CLI, or file IO. There are multiple reasons for this:

  • This module might become its own library

  • It is the common denominator between the CLI and GUI and those should be separate. It is planned that these can be compiled separately.

  • The project is easier to maintain this way

Note

While we plan to make Ohmcha support nonlinear circuits and custom user circuits of any kind, only linear circuits are being implemented in the early stages. Everything that follows relates strictly to linear circuits.

Modelling a component

Everything that has at least a current and a voltage associated with it, is represented as a component. A special case of a component is a branch. Component is an abstract class. It contains a struct MetaInfo which includes information about the component that is not needed for solving the circuit, such as the component name.

For implementation details, see Component.

Modelling a branch

A Branch is a subclass of Component. While it is not an abstract class in C++ terminology, it is fairly abstract in that every two-terminal element and subcircuit can be represented as a branch. For example, a resistor and emf connected in series are to be represented as a single branch. Or, a single current source. Even parallel and combined connections can be represented this way, as long as that connection effectively has 2 terminals. It is advisable to combine connections into a single branch wherever possible, for increased performance.

In order to explain the model, an example is useful.

Example

Let’s look at an example that is used throughout the documentation:

developer/../examples/example_1/example_1.svg

Consider branch 1 in isolation – the branch number and the index of its current are identical in this representation.

The branch can be mathematically represented as:

\[V_1 - V_3 = E_1 + R_1 \cdot I_1\]

We can rearrange this a bit:

\[V_1 - V_3 - R_1 \cdot I_1 = E_1\]

or, in matrix form:

\[[ \begin{array}{ccc} 1 & -1 & -R_1 \end{array} ] \cdot [ \begin{array}{ccc} V_1 & V_3 & I_1 \end{array} ]^T = E_1\]

In fact, an arbitrary linear branch can be represented in this way:

\[\mathbf A \cdot [ \begin{array}{ccc} V_1 & V_2 & I \end{array} ]^T = B\]

where \(V_1\) is the voltage of the first node, and \(V_2\) of the second node this branch is connected to, and \(I\) is the current through. this branch. The order of these variables is determined using the following convention: current flows through a branch from node 1 to node 2. We will refer to this last equation as the constitutive relation of the branch.

For completeness, we will display the results for the other branches:

\[ \begin{align}\begin{aligned}\mathbf A_2 = [ \begin{array}{ccc} 1 & -1 & -R_2 \end{array} ],\ B_2 = 0\\\mathbf A_3 = [ \begin{array}{ccc} 1 & -1 & -R_3 \end{array} ],\ B_3 = 0\\\mathbf A_4 = [ \begin{array}{ccc} 0 & 0 & 1 \end{array} ],\ B_4 = I_4\\\mathbf A_5 = [ \begin{array}{ccc} 1 & -1 & 0 \end{array} ],\ B_5 = E_5\end{aligned}\end{align} \]

To sum up, the only things that an algorithm needs to know about this branch are: node 1, node 2, matrix \(\mathbf A\) and \(B\).

Note that by combining multiple components into a single branch, the branch itself does not retain information of each component that is part of it. For example, if a branch represents a series connection of a resistor and emf, you can use the algorithms to calculate the current through both of them, since it is a property of the branch. However, you can’t calculate the voltage drop on the resistor by merely knowing the mathematical model of the branch.

This is not a problem. Firstly, the algorithms don’t care. The algorithms’ only job is to find an explicit representation of the system. Secondly, since each component keeps a reference to its branch, by iterating through all components, one can find all variables, in a way that is less costly than the algorithms. Additionally, the user often won’t care about all the variables in the system. This is another performance boost.

Classes

Component

class Ohmcha::Component

Subclassed by Ohmcha::Branch, Ohmcha::CurrentSource, Ohmcha::Emf, Ohmcha::Node, Ohmcha::Resistor

Public Functions

Component *copy() const = 0
void setName(const std::string &name)
void setPosition(Pos *position)

Set the position of this component in the schematic. If position is null, the position is considered arbitrary.

void setAngle(float angle)
void setTextPos(const Pos &pos)
void setTextAngle(float angle)
void setTextOrientationIndependent(bool flag)
void setTextAnchor(int anchor)
std::string getName() const

Return

Name given by the user.

Pos *getPosition() const

See setPosition

float getAngle() const
Pos getTextPos() const
float getTextAngle() const
bool isTextOrientationIndependent() const
int getTextAnchor() const
int getTerminalCount() const = 0

Public Static Functions

Component *newByName(std::string name)

Dynamically allocate a Component based on the specified class name.

Return

Pointer to the dynamically allocated object.

Protected Attributes

MetaInfo *metaInfo = nullptr
struct MetaInfo

Additional information that is not relevant for algorithms.

Public Members

std::string name
Pos *pos = {}
Pos textPos
int textAnchor = 3
float angle = {}
float textAngle = {}
bool textOrientationIndependent = false
struct Pos

Public Members

float x = 0
float y = 0

Branch

class Ohmcha::Branch : public Ohmcha::Component

An electrical branch.

Public Functions

Branch()

A default branch, equivalent to a short circuit.

Branch(const Branch &branch1, const Branch &branch2)

Create a branch by merging two branches that contain a common node.

Exceptions
  • TODO..if: they don’t have a common node

Branch(RowVector3f A, float B)
Branch(Node &node1, Node &node2, RowVector3f A, float B)
Branch(Component *component, Node *node1 = nullptr, Node *node2 = nullptr)
Component *copy() const override
void addComponent(Component *component, bool inverted = false)
void setA(const RowVector3f &matrix)
void setB(float x)
void setNode1(Node &n)
void setNode2(Node &n)
void setNode1(Node *n)
void setNode2(Node *n)
RowVector3f getA() const
float getB() const
Node *getNode1() const
Node *getNode2() const
int getTerminalCount() const override
bool hasNode(Node *node) const
Node *getOtherNode(Node *node) const

Public Members

std::vector<Component*> attached
std::vector<bool> inversions

Protected Attributes

Node *node1 = {}

Current flows through this object from node 1 to node 2. Consequently, the reference voltage is positive if node 1 is at a higher potential than node 2.

Node *node2 = {}
RowVector3f A

A branch has three quantities: V1 (potential at node1), V2 (potential at node2), I (current from node1 to node2). to represent a linear branch, we require an equation A * transpose([ V1 V2 I ]) = B.

float B

Resistor

class Ohmcha::Resistor : public Ohmcha::Component

Public Functions

Resistor()
Resistor(float resistance)
Component *copy() const override
void setResistance(float r)
int getTerminalCount() const override
float getResistance() const
Pos getTextPos() const override

Private Members

float resistance = 1

Custom Component

Custom user components are all represented as instances of CustomComponent. The attributes of this class are the number of terminals, the graphical representation (e.g. an image), and the mathematical model.

Todo

Custom user components are not implemented yet.