Author Topic: Experiment using Matrix Equation in C program  (Read 1775 times)

0 Members and 1 Guest are viewing this topic.

Offline JOJOElectro29Topic starter

  • Contributor
  • Posts: 18
  • Country: sg
Experiment using Matrix Equation in C program
« on: May 24, 2025, 12:58:01 pm »
Hi I have an Experiment where I have to make a C program to calculate nodal voltages using 5x5 Matrix but I can't seem to get the code to work. I used PSIM to simulate the nodal voltages values and attached to desired output. Would appreciate any help Thank You.
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #1 on: May 24, 2025, 05:58:27 pm »

Some comments:

1. You should print out the matrix equation you are solving and compare it with what the PDF gives. For that matter, you should print out the matrix for a simpler circuit so you can hand check the results.

2. The way I understand the methodology you have to pick a value for "R" and I'm not seeing that in the program.

3. Voltage sources will affect the Y matrix as well as the constants vector and I'm not seeing that.
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #2 on: May 24, 2025, 07:49:33 pm »
Here is what I get:

Code: [Select]
Equations:
      0 = 0.001*U0 - 0.001*U5 + 2*U0/R - U1/R - U3/R + 8.3/R
      0 = 0.003*U1 - 0.001*U2 - 0.001*U4 - 0.001*U5 - U0/R + U1/R - 5.0/R
      0 = -0.001*U1 + 0.003*U2 - 0.001*U3 - 0.001*U4
      0 = -0.001*U2 + 0.001*U3 - U0/R + U3/R - 3.3/R
      0 = -0.001*U1 - 0.001*U2 + 0.003*U4 - 0.001*U5
      0 = -0.001*U0 - 0.001*U1 - 0.001*U4 + 0.003*U5
      0 = U0
      0 = R - 0.001

Solution:
     U0 = 0.0
     U1 = 4.99999617143341
     U2 = 4.11428383673643
     U3 = 3.30000081428302
     U4 = 4.04285452449287
     U5 = 3.01428356530876
      R = 0.00100000000000000

I've attached the code and you can also run it here:

https://python-fiddle.com/saved/acbbb6bc-20b2-4c35-b8ed-2e4a82e5e21c

Btw - thanks for bringing up this method. I've never seen it before and it's a pretty slick idea.
« Last Edit: May 24, 2025, 07:58:46 pm by ledtester »
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #3 on: May 26, 2025, 05:38:13 am »
So I've figured out how to modify the method which does not rely on transforming voltage sources into current sources. This enables you to get exact answers (as if there are perfect voltage sources anyway).

The key is to combine voltage sources into "supernodes".

In the example above, we combine nodes 0, 1 and 3 into a supernode. We will replace the equations:

Y[0] = 0, Y[1] = 0 and Y[3] = 0

with these:

Y[0]+Y[1]+Y[3] = 0, U1-U0 = 5, U3-U0 = 3.3

The expression Y[0]+Y[1]+Y[3] represents the current into the supernode {0,1,3}.

You can run the modified code here:

https://python-fiddle.com/saved/063bbc81-972a-4e52-8f35-64b3f5090820

The output:

Code: [Select]
Supernodes: [[0, 1, 3]]
Equations:
      0 = 0.001*U0 + 0.003*U1 - 0.002*U2 + 0.001*U3 - 0.001*U4 - 0.002*U5
      0 = 0
      0 = -0.001*U1 + 0.003*U2 - 0.001*U3 - 0.001*U4
      0 = 0
      0 = -0.001*U1 - 0.001*U2 + 0.003*U4 - 0.001*U5
      0 = -0.001*U0 - 0.001*U1 - 0.001*U4 + 0.003*U5
      0 = -U0 + U1 - 5.0
      0 = -U0 + U3 - 3.3
      0 = U0

Solution:
     U0 = 0.0
     U1 = 5.00000000000000
     U2 = 4.11428571428571
     U3 = 3.30000000000000
     U4 = 4.04285714285714
     U5 = 3.01428571428571
 
The following users thanked this post: JOJOElectro29

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Experiment using Matrix Equation in C program
« Reply #4 on: May 26, 2025, 06:35:41 am »
So I've figured out how to modify the method which does not rely on transforming voltage sources into current sources. This enables you to get exact answers (as if there are perfect voltage sources anyway).
If you install Maxima, you can trivially verify your solution:
Code: [Select]
solve([ U1/R + (U1-U2)/1000 + (U1-U4)/1000 + (U1-U5)/1000 = 5/R,
        (U2-U1)/1000 + (U2-U3)/1000 + (U2-U4)/1000 = 0,
        U3/R + (U3-U2)/1000 = 3.3/R,
        (U4-U2)/1000 + (U4-U5)/1000 + (U4-U1)/1000 = 0,
        U5/1000 + (U5-U4)/1000 + (U5-U1)/1000 = 0,
        U1 = 5 ], [ U1, U2, U3, U4, U5, R ]);
float(%);
There are two solutions, one with R=0.0, and one with R=-2010.  Assuming we reject negative resistances, then the mathematically accurate solution to the system of equations is U1=5, U2≃4.114, U3=3.3, U4≃4.043, and U5≃3.014.  The values in ledtester's post above match that mathematically accurate result to at least 15 digits of precision.

Maxima actually computes the exact solution as a fraction (if you replace =3.3/R with =33/(10*R)), and the float(%); reprints the solution converting fractions to floating-point numbers.  If you want that at a specific number of decimal digits N, use fpprec:N; and bfloat(%); instead.
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #5 on: May 26, 2025, 07:24:32 am »
The Maxima solutions are very interesting and it has led me to a simpler algorithm...

1) create the equations as before
2) for every equation with /R terms, multiply the equation by R and set R = 0; if an equations doesn't have any /R terms leave it alone
3) solve the resulting (now linear) system in terms of the voltage variables

Multiplying an equation by R and then setting R to zero is just removing the non-/R terms.

For instance, in the above example we would transform the equations as follows:

U1/R + (U1-U2)/1000 + (U1-U4)/1000 + (U1-U5)/1000 = 5/R --> U1 = 5

(U2-U1)/1000 + (U2-U3)/1000 + (U2-U4)/1000 = 0 -> no /R terms so no change

U3/R + (U3-U2)/1000 = 3.3/R --> U3 = 3.3

(U4-U2)/1000 + (U4-U5)/1000 + (U4-U1)/1000 = 0 --> no change

U5/1000 + (U5-U4)/1000 + (U5-U1)/1000 = 0 --> no change
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Experiment using Matrix Equation in C program
« Reply #6 on: May 26, 2025, 08:08:56 am »
Sure, should work just fine.

In case anyone wonders, we are dealing with the simple system of linear equations, say
$$\left \lbrace \, \begin{aligned}
a_{11} U_1 + a_{12} U_2 + a_{13} U_3 &= b_1 \\
a_{21} U_1 + a_{22} U_2 + a_{23} U_3 &= b_2 \\
a_{31} U_1 + a_{32} U_2 + a_{33} U_3 &= b_3 \\
\end{aligned} \right .$$
which in linear algebra is usually written as
$$\mathbf{A} \mathbf{U} = \mathbf{b}$$
where
$$\mathbf{A} = \left [ \begin{matrix} a_{11} & a_{12} & a_{13} \\
a_{21} & a_{22} & a_{23} \\
a_{31} & a_{32} & a_{33} \\
\end{matrix} \right ], \quad \mathbf{U} = \left[ \begin{matrix} U_1 \\
U_2 \\
U_3 \\
\end{matrix} \right ], \quad \mathbf{b} = \left[ \begin{matrix} b_1 \\
b_2 \\
b_3 \\
\end{matrix} \right ]$$
Note that \$\mathbf{U}\$ and \$\mathbf{b}\$ are column vectors, and that \$\mathbf{A}\$ has as many columns as \$\mathbf{U}\$ has elements, and as many rows as \$\mathbf{b}\$ has elements.

When \$\mathbf{A}\$ is square, i.e. \$\mathbf{U}\$ and \$\mathbf{b}\$ have the same number of elements, then if a solution exists it is
$$\mathbf{U} = \mathbf{A}^{-1} \mathbf{b}$$
where \$\mathbf{A}^{-1}\$ is the matrix inverse of \$\mathbf{A}\$.

When \$\mathbf{A}\$ is not square, the best solution in the least squares sense is
$$\mathbf{U} = \mathbf{A}^{*} \mathbf{b}$$
where \$\mathbf{A}^{*}\$ is the pseudoinverse or Moore–Penrose inverse of matrix \$\mathbf{A}\$.

The two inverse above link to corresponding Wikipedia articles, which list some of the common methods of computing the solutions.  With numpy, you can use numpy.linalg.inv() and numpy.linalg.pinv(), respectively.  In ones own C code, without any supporting matrix or linear algebra library, follow your course notes, or any of the many numerical computation approaches you find online and in references; but usually matrix inverse exercises are done using Cramer's rule, and pseudoinverse via singular value decomposition.
 
The following users thanked this post: Andy Watson

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #7 on: May 26, 2025, 08:10:12 am »
I've updated my python code:

https://python-fiddle.com/saved/a7996e17-5715-4cae-aab4-f84166f367e7

Setting up the equations now boils down to this:

Code: [Select]
    U = [ sympy.symbols("U"+str(k)) for k in range(0,Nnodes) ]
    Y = [0]*Nnodes
    YR = [0]*Nnodes
    for type, i, j, value in Circuit:
        value = float(value)
        if type == 0:  # resistor from node i to node j
            g = 1/value
            Y[i] += U[i]*g - U[j]*g
            Y[j] += U[j]*g - U[i]*g
        else:  # voltage source from i to j
            YR[i] += U[i] - U[j] + value
            YR[j] += U[j] - U[i] - value
    eqns = []
    for i in range(Nnodes):
        if YR[i] != 0:
            eqns.append(YR[i])
        else:
            eqns.append(Y[i])

Basically, if a node is part of a voltage source we only use the constraints given by the voltage sources that node is part of. Otherwise the equation for that node is the KCL equation.
 
The following users thanked this post: JOJOElectro29

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 5745
  • Country: Earth
Re: Experiment using Matrix Equation in C program
« Reply #8 on: May 26, 2025, 08:33:14 am »
You can solve system of equations using Octave with symbolic package:
Code: [Select]
pkg load symbolic;

% define symbolic variables
syms U0 U1 U2 U3 U4 U5 R;

% define system of equations
eqns = [
    0 == 0.001*U0 - 0.001*U5 + 2*U0/R - U1/R - U3/R + 8.3/R,
    0 == 0.003*U1 - 0.001*U2 - 0.001*U4 - 0.001*U5 - U0/R + U1/R - 5.0/R,
    0 == -0.001*U1 + 0.003*U2 - 0.001*U3 - 0.001*U4,
    0 == -0.001*U2 + 0.001*U3 - U0/R + U3/R - 3.3/R,
    0 == -0.001*U1 - 0.001*U2 + 0.003*U4 - 0.001*U5,
    0 == -0.001*U0 - 0.001*U1 - 0.001*U4 + 0.003*U5,
    0 == U0,
    0 == R - 0.001,
];

% list of variables to solve for
vars = [U0, U1, U2, U3, U4, U5, R];

% solve the system
sol = solve(eqns, vars);

% print results
fprintf('U0 = %g\n', double(sol.U0));
fprintf('U1 = %g\n', double(sol.U1));
fprintf('U2 = %g\n', double(sol.U2));
fprintf('U3 = %g\n', double(sol.U3));
fprintf('U4 = %g\n', double(sol.U4));
fprintf('U5 = %g\n', double(sol.U5));
fprintf('R  = %g\n', double(sol.R));

Result:
Code: [Select]
U0 = 0
U1 = 5
U2 = 4.11428
U3 = 3.3
U4 = 4.04285
U5 = 3.01428
R  = 0.001

PS: it will spam you with some warnings "passing floating-point values to sym is dangerous", you can fix it by using sym constants instead of floating point values, or just by disable the warnings:
Code: [Select]
warning off all;

as alternative, you can also write equations in this way:
Code: [Select]
eqns = [
    0 == sym('0.001*U0 - 0.001*U5 + 2*U0/R - U1/R - U3/R + 8.3/R'),
    0 == sym('0.003*U1 - 0.001*U2 - 0.001*U4 - 0.001*U5 - U0/R + U1/R - 5.0/R'),
    0 == sym('-0.001*U1 + 0.003*U2 - 0.001*U3 - 0.001*U4'),
    0 == sym('-0.001*U2 + 0.001*U3 - U0/R + U3/R - 3.3/R'),
    0 == sym('-0.001*U1 - 0.001*U2 + 0.003*U4 - 0.001*U5'),
    0 == sym('-0.001*U0 - 0.001*U1 - 0.001*U4 + 0.003*U5'),
    0 == sym('U0'),
    0 == sym('R - 0.001'),
];
« Last Edit: May 26, 2025, 08:59:44 am by radiolistener »
 

Offline JOJOElectro29Topic starter

  • Contributor
  • Posts: 18
  • Country: sg
Re: Experiment using Matrix Equation in C program
« Reply #9 on: May 26, 2025, 09:36:07 am »
Here is my C program.

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define MAX_NODES 10
#define MAX_ELEMENTS 100

typedef struct {
    int type;   // 0 = resistor, 1 = voltage source
    int i;
    int j;
    double value;
} Component;

Component elements[MAX_ELEMENTS];
int element_count = 0;
int node_count = 0;

void read_circuit(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror("Can't open file");
        exit(1);
    }

    char line[128];
    while (fgets(line, sizeof(line), file)) {
        Component e;
        char *token = strtok(line, ",");
        e.type = atoi(token);

        token = strtok(NULL, ",");
        e.i = atoi(token);

        token = strtok(NULL, ",");
        e.j = atoi(token);

        token = strtok(NULL, ",");
        e.value = atof(token);

        if (e.i > node_count) node_count = e.i;
        if (e.j > node_count) node_count = e.j;

        elements[element_count++] = e;
    }

    fclose(file);
    node_count++; // include max node index
}

void solve_circuit() {
    int Nnodes = node_count;

    // Y: KCL current equations
    // YR: Voltage constraints
    double Y[Nnodes][Nnodes];   // KCL contributions
    double YR[Nnodes][Nnodes];  // Voltage constraints
    double I[Nnodes];           // Constant terms

    memset(Y, 0, sizeof(Y));
    memset(YR, 0, sizeof(YR));
    memset(I, 0, sizeof(I));

    // Build equations
    for (int k = 0; k < element_count; k++) {
        int type = elements[k].type;
        int i = elements[k].i;
        int j = elements[k].j;
        double val = elements[k].value;

        if (type == 0) { // Resistor
            double g = 1.0 / val;

            Y[i][i] += g;
            Y[i][j] -= g;
            Y[j][j] += g;
            Y[j][i] -= g;

        } else if (type == 1) { // Voltage Source
            // Add constraint: U[i] - U[j] + V = 0
            YR[i][i] += 1;
            YR[i][j] -= 1;
            I[i] += -val;

            YR[j][i] -= 1;
            YR[j][j] += 1;
            I[j] += val;
        }
    }

    // Total system size = Nnodes equations
    double A[Nnodes][Nnodes];
    double B[Nnodes];

    for (int i = 0; i < Nnodes; i++) {
        for (int j = 0; j < Nnodes; j++) {
            if (YR[i][i] != 0 || YR[i][j] != 0) {
                A[i][j] = YR[i][j];
            } else {
                A[i][j] = Y[i][j];
            }
        }
        B[i] = I[i];
    }

    // Ground node: set U0 = 0
    for (int j = 0; j < Nnodes; j++) {
        A[0][j] = 0;
    }
    A[0][0] = 1;
    B[0] = 0;

    // Solve using Gaussian Elimination with partial pivoting
    for (int k = 0; k < Nnodes; k++) {
        int max_row = k;
        for (int r = k + 1; r < Nnodes; r++) {
            if (fabs(A[r][k]) > fabs(A[max_row][k])) {
                max_row = r;
            }
        }

        // Swap rows
        if (max_row != k) {
            for (int c = 0; c < Nnodes; c++) {
                double tmp = A[k][c];
                A[k][c] = A[max_row][c];
                A[max_row][c] = tmp;
            }
            double tmp = B[k];
            B[k] = B[max_row];
            B[max_row] = tmp;
        }

        // Normalize pivot row
        double pivot = A[k][k];
        if (fabs(pivot) < 1e-9) {
            printf("Singular matrix\n");
            exit(1);
        }

        for (int c = k; c < Nnodes; c++) {
            A[k][c] /= pivot;
        }
        B[k] /= pivot;

        // Eliminate other rows
        for (int r = 0; r < Nnodes; r++) {
            if (r != k && fabs(A[r][k]) > 1e-9) {
                double factor = A[r][k];
                for (int c = k; c < Nnodes; c++) {
                    A[r][c] -= factor * A[k][c];
                }
                B[r] -= factor * B[k];
            }
        }
    }

    // Recover voltages
    double U[Nnodes];
    for (int i = 0; i < Nnodes; i++) {
        U[i] = B[i];
    }

    // Print results
    printf("\nNode voltages (reference node U0 = 0):\n");
    for (int i = 0; i < Nnodes; i++) {
        printf("U%d = %.2f V\n", i, U[i]);
    }
}

int main() {
    read_circuit("exp5_circuit.txt");
    solve_circuit();
    return 0;
}
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4244
  • Country: us
Re: Experiment using Matrix Equation in C program
« Reply #10 on: May 28, 2025, 03:38:31 pm »
Ok - I’ve found a flaw in the approach I discussed in replies #5 and #7.

One thing that bothered me was that the approach replaced a voltage source with two equations which were the same and, indeed, this runs into trouble in the following circuit:

Code: [Select]
            --5V --- 1k --- GND --- 1k --- 3.3V--
node id:    2     1          0          3       4                     

The corresponding circuit definition:

Code: [Select]
Trouble = [
    [0, 0, 1, 1000],
    [1, 1, 2, 5],
    [0, 0, 3, 1000],
    [1, 3, 4, 3.3]
]

Here are the generated system of equations generated by that approach compared to the original algorithm which converts voltage sources into current sources with a resistance of R:


Code: [Select]
Reply #5 approach                        Original algorithm
 -----------------                        ------------------
 0 = 0.002*U0 - 0.001*U1 - 0.001*U3       0 = 0.002*U0 - 0.001*U1 - 0.001*U3
 0 = U1 - U2 + 5.0                        0 = -0.001*U0 + 0.001*U1 + U1/R - U2/R + 5.0/R
 0 = -U1 + U2 - 5.0                       0 = -U1/R + U2/R - 5.0/R
 0 = U3 - U4 + 3.3                        0 = -0.001*U0 + 0.001*U3 + U3/R - U4/R + 3.3/R
 0 = -U3 + U4 - 3.3                       0 = -U3/R + U4/R - 3.3/R
 0 = U0                                   0 = U0
                                          0 = R - 0.001

In the Reply #5 approach there are effectively only 3 independent equations which leaves the system underdetermined.

Even though the equation:

0 = -0.001*U0 + 0.001*U1 + U1/R - U2/R + 5.0/R

transforms into

0 = U1 - U2 + 5.0

in the limit as R goes to 0 apparently it is not valid to make that substitution.

The equations from my "supernode" algorithm (reply #3) are:

Code: [Select]
      0 = 0.002*U0 - 0.001*U1 - 0.001*U3
      0 = -0.001*U0 + 0.001*U1
      0 = -0.001*U0 + 0.001*U3
      0 = -U1 + U2 - 5.0
      0 = -U3 + U4 - 3.3
      0 = U0


and this does give the correct answer for this case and I have a lot more confidence that it is correct in all cases.
« Last Edit: May 28, 2025, 04:00:33 pm by ledtester »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->