PHP library for electrical power system simulation: Load Flow and Transient Stability Analysis

Maintainers

Package info

github.com/tamashiroBR/ndse-tools

pkg:composer/tamashiro/ndse

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-25 03:49 UTC

This package is auto-updated.

Last update: 2026-07-12 14:26:21 UTC


README

Latest Stable Version License PHP Version Require

NDSE Tools is a PHP library for electrical power system simulation. It provides a steady-state Load Flow solver and a time-domain Transient Stability Analysis engine, both built on top of native PHP sparse matrix and complex number math utilities.

This PHP package was developed as part of the doctoral thesis defended at the Federal University of Uberlândia, Faculty of Electrical Engineering. The full thesis document is available at the university repository: https://repositorio.ufu.br/handle/123456789/18396.

Live Demo

A fully functional client3 interface is available for testing at:

http://ndse.ddns.net

The client3 is a web-based front-end that integrates directly with the NDSE simulation engine, allowing users to configure power system data, run Load Flow and Transient Stability Analysis, and visualise results interactively — no local installation required.

Requirements

  • PHP >= 7.4

Installation

Install the package via Composer:

composer require tamashiro/ndse

Features

Feature Description
Load Flow Newton-Raphson steady-state power flow solver with reactive power limit enforcement
Transient Analysis Implicit trapezoidal time-domain integration with Newton-Raphson corrector
Generator Models Classical (Gen1) and 4th-order (Gen2) synchronous machine models
Exciter Models IEEE Type I (Exc1) and simplified IEEE ST1-style (Exc2) excitation systems
Math Utilities Dense and sparse matrix operations, complex arithmetic, and linear algebra solvers

Usage

Load Flow — IEEE 9-Bus System

The following example runs a Newton-Raphson Load Flow on the IEEE 9-bus test system (Anderson & Fouad, 1994), which is the smallest standard benchmark for power flow studies.

use NDSE\Tools\LoadFlow;

$data = [
    // [sbase(MVA), max_iter, tolerance, qlim]
    'optLF' => [100, 10, 1e-3, 1],

    // Bus columns: [id, type, Pg(MW), Qg(MVAr), Pd(MW), Qd(MVAr), Gs, Vm(pu), Va(deg), Qmax, Qmin]
    // Bus types: 1=PQ, 2=PV, 3=Slack
    'bus' => [
        [1, 3,   0,  0,   0,  0, 0, 1.040, 0.300, -300],  // Slack bus
        [2, 2, 163,  0,   0,  0, 0, 1.025, 0.300, -300],  // Generator bus
        [3, 2,  85,  0,   0,  0, 0, 1.025, 0.300, -300],  // Generator bus
        [4, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [5, 1,   0,  0,  90, 30, 0, 1.000, 0,        0],  // Load: 90 MW, 30 MVAr
        [6, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [7, 1,   0,  0, 100, 35, 0, 1.000, 0,        0],  // Load: 100 MW, 35 MVAr
        [8, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [9, 1,   0,  0, 125, 50, 0, 1.000, 0,        0],  // Load: 125 MW, 50 MVAr
    ],

    // Branch columns: [from, to, R(pu), X(pu), B(pu), tap, shift(deg), status]
    // tap=0 means transmission line; tap>0 means transformer
    'branch' => [
        [1, 4, 0.0000, 0.0576, 0.0000, 1, 0, 1],  // Transformer 1-4
        [4, 5, 0.0170, 0.0920, 0.1580, 0, 0, 1],
        [5, 6, 0.0390, 0.1700, 0.3580, 0, 0, 1],
        [3, 6, 0.0000, 0.0586, 0.0000, 1, 0, 1],  // Transformer 3-6
        [6, 7, 0.0119, 0.1008, 0.2090, 0, 0, 1],
        [7, 8, 0.0085, 0.0720, 0.1490, 0, 0, 1],
        [8, 2, 0.0000, 0.0625, 0.0000, 1, 0, 1],  // Transformer 8-2
        [8, 9, 0.0320, 0.1610, 0.3060, 0, 0, 1],
        [9, 4, 0.0100, 0.0850, 0.1760, 0, 0, 1],
    ],
];

$lf = new LoadFlow($data);
$lf->makeYbus();          // Build the admittance matrix (Y-bus)
$result = $lf->run();     // Run Newton-Raphson iterations

$output = json_decode($result, true);
// $output['iteration']  → number of iterations to converge
// $output['bus']        → bus results: [id, V(pu), angle(°), P(MW), Q(MVAr), ...]
// $output['branch']     → branch flows: [from, to, Pkm(MW), Qkm(MVAr), Pmk(MW), Qmk(MVAr), ...]
// $output['loss']       → [totalActiveLoss(MW), totalReactiveLoss(MVAr)]

For larger systems, the data can be loaded directly from a JSON file:

use NDSE\Tools\LoadFlow;

// Load the bundled IEEE 57-bus or 118-bus example data
$data = json_decode(file_get_contents(__DIR__ . '/examples/loadflow_57bus.json'), true);

$lf = new LoadFlow($data);
$lf->makeYbus();
$result = $lf->run();

Transient Stability Analysis — IEEE 9-Bus System

The following example runs a time-domain Transient Stability Analysis on the IEEE 9-bus system with a three-phase fault event. The fault is applied at bus 7 (branch 2-7) at t = 0.2 s and cleared at t = 0.3 s. The simulation uses the implicit trapezoidal method with a Newton-Raphson corrector.

use NDSE\Tools\TransientAnalysis;

$data = [
    // [sbase(MVA), max_iter, tolerance, qlim]
    'optLF' => [100, 10, 1e-3, 1],

    // [fbase(Hz), t0(s), method, dt(s)]
    'optTA' => [60, 0, 1, 1e-2],

    'bus' => [
        [1, 3,   0,  0,   0,  0, 0, 1.040, 0.300, -300],
        [2, 2, 163,  0,   0,  0, 0, 1.025, 0.300, -300],
        [3, 2,  85,  0,   0,  0, 0, 1.025, 0.300, -300],
        [4, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [5, 1,   0,  0,  90, 30, 0, 1.000, 0,        0],
        [6, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [7, 1,   0,  0, 100, 35, 0, 1.000, 0,        0],
        [8, 1,   0,  0,   0,  0, 0, 1.000, 0,        0],
        [9, 1,   0,  0, 125, 50, 0, 1.000, 0,        0],
    ],

    'branch' => [
        [1, 4, 0.0000, 0.0576, 0.0000, 1, 0, 1],
        [4, 5, 0.0170, 0.0920, 0.1580, 0, 0, 1],
        [5, 6, 0.0390, 0.1700, 0.3580, 0, 0, 1],
        [3, 6, 0.0000, 0.0586, 0.0000, 1, 0, 1],
        [6, 7, 0.0119, 0.1008, 0.2090, 0, 0, 1],
        [7, 8, 0.0085, 0.0720, 0.1490, 0, 0, 1],
        [8, 2, 0.0000, 0.0625, 0.0000, 1, 0, 1],
        [8, 9, 0.0320, 0.1610, 0.3060, 0, 0, 1],
        [9, 4, 0.0100, 0.0850, 0.1760, 0, 0, 1],
    ],

    // Generator models
    // Gen1 (classical 2nd-order): [bus, model=1, H(s), D, Xd, Xd', Xq, Xq', Td0', Tq0']
    // Gen2 (4th-order salient):   [bus, model=2, H(s), D, Xd, Xd', Xq, Xq', Td0', Tq0']
    'gen' => [
        [1, 1, 1.20, 0.02, 0.14, 0.14, 0.00, 0.00, 0.00, 0.00],  // Classical at bus 1
        [2, 1, 2.40, 0.01, 0.14, 0.14, 0.00, 0.00, 0.00, 0.00],  // Classical at bus 2
        [3, 2, 5.74, 0.02, 1.93, 1.77, 0.25, 0.25, 5.20, 0.81],  // 4th-order at bus 3
    ],

    // IEEE Type I Exciter: [bus, model=1, Ka, Ta, Vmin, Vmax, Ke, Te, Kf, Tf, E1, Se1]
    'exc' => [
        [3, 1, 50, 0.05, -0.17, 0.95, 0.04, 1.0, 0.014, 1.55, -1.7, 1.7],
    ],

    // Governor models (none in this example)
    'gov' => [],

    // Fault events: [type, fromBus, toBus, time(s), impedance]
    // type=1: three-phase fault; impedance=1e-10 → bolted fault; impedance=0 → cleared
    'event' => [
        [1, 2, 7, 0.2, 1e-10],  // Fault applied  at t = 0.2 s
        [1, 2, 7, 0.3, 0    ],  // Fault cleared  at t = 0.3 s
    ],
];

$ta = new TransientAnalysis($data);
$result = $ta->run();

$output = json_decode($result, true);
// $output['time']   → array of time instants (s)
// $output['delta']  → rotor angles per generator per time step (rad)
// $output['omega']  → angular speeds per generator per time step (rad/s)

Math Utilities

The math classes can also be used independently for general power system calculations.

Angle Conversions

use NDSE\Math\Angle;

$deg = new Angle(30, 'deg');
echo $deg->rad();   // 0.5236 (radians)

$rad = new Angle(M_PI / 4, 'rad');
echo $rad->deg();   // 45.0 (degrees)

Complex Number Arithmetic

use NDSE\Math\Angle;
use NDSE\Math\Complex;

// Rectangular form: Z = a + jb
$z1 = new Complex(3, 4);    // Z1 = 3 + j4
$z2 = new Complex(1, -2);   // Z2 = 1 - j2

echo $z1->abs();             // 5.0      (modulus |Z1|)
echo $z1->ang();             // 0.9273   (angle in radians; use rad2deg() to convert)

$sum  = $z1->add($z2);       // 4 + j2
$diff = $z1->sub($z2);       // 2 + j6
$prod = $z1->multiply($z2);  // 11 - j2
$quot = $z1->div($z2);       // -1 + j2
$conj = $z1->conj();         // 3 - j4  (conjugate)
$inv  = $z1->inv();          // 0.12 - j0.16  (inverse 1/Z1)
$neg  = $z1->neg();          // -3 - j4  (negation)

// Polar form: Z = |Z| ∠θ  (pass an Angle object as second argument)
$z_polar = new Complex(5, new Angle(53.13, 'deg'));  // 5∠53.13° ≈ 3 + j4
echo $z_polar->re;   // ≈ 3.0
echo $z_polar->img;  // ≈ 4.0

// Power engineering: transmission line impedance and admittance
$Z = new Complex(0.02, 0.08);   // Z = R + jX (pu)
$Y = $Z->inv();                  // Y = G + jB (pu)

// Apparent power: S = V × I*
$V = new Complex(1.0, 0.0);     // Voltage 1.0∠0° pu
$I = new Complex(0.8, -0.6);    // Current (lagging load)
$S = $V->multiply($I->conj());  // S = P + jQ
echo $S->re;   // 0.8 (active power, pu)
echo $S->img;  // 0.6 (reactive power, pu)

Dense Matrix Operations

use NDSE\Math\Matrix;

$A = new Matrix([
    [2, 1, 0],
    [1, 3, 1],
    [0, 1, 2],
]);

$B = new Matrix([[1,0,1],[0,2,0],[1,0,1]]);

$C  = $A->add($B);         // A + B
$D  = $A->multiply($B);    // A × B  (matrix product)
$E  = $A->multiply(2);     // 2 × A  (scalar product)
$At = $A->transpose();     // Aᵀ

// Extract column 1 (0-indexed)
$col = $A->subMatrix([], [1]);

// Filter rows where column 0 > 1
$rows = $A->subMatrix([0, '>', 1], []);

// Get a specific element (row 1, col 2)
$val = $A->get(1, 2);   // 1

// Create a zero matrix
$Z = Matrix::zeros(3, 3);

Sparse Matrix Operations

use NDSE\Math\Sparse;
use NDSE\Math\Complex;

// Build a 4×4 sparse complex Y-bus matrix
$Y = new Sparse(4, 4);

// Set diagonal (self-admittance)
$Y->set(new Complex(10, -30), 0, 0);
$Y->set(new Complex(10, -30), 1, 1);
$Y->set(new Complex(10, -30), 2, 2);
$Y->set(new Complex(10, -30), 3, 3);

// Set off-diagonal (mutual admittance)
$Y->set(new Complex(-5, 15), 0, 1);
$Y->set(new Complex(-5, 15), 1, 0);
$Y->set(new Complex(-5, 15), 1, 2);
$Y->set(new Complex(-5, 15), 2, 1);

// Retrieve a specific element
$y00 = $Y->get(0, 0);   // Complex(10, -30)

// Convert to dense Matrix for display or further operations
$Yfull = $Y->full();

LU Decomposition Solver

The LinAlg class provides the sparse LU decomposition solver used internally by the Newton-Raphson Load Flow to solve the Jacobian system J·Δx = r at each iteration. The sparse matrix must be built via Matrix->sparse() (CSC format).

use NDSE\Math\Matrix;
use NDSE\Math\Complex;
use NDSE\Math\LinAlg;

// --- Real-valued system: Ax = b ---
// 4x + 3y      = 10
// 6x + 3y      = 12
// 2x + 3y + 2z = 14
// Expected: x=1, y=2, z=3

$A = new Matrix([
    [4, 3, 0],
    [6, 3, 0],
    [2, 3, 2],
]);

$LU = LinAlg::LUdecomp($A->sparse());
$x  = LinAlg::LUsolver($LU, [10, 12, 14]);
// $x = [1.0, 2.0, 3.0]

// --- Complex system: Y·V = I (Y-bus network equations) ---
// Y = | 10-j30   -5+j15 |   I = | 1+j0 |
//     | -5+j15   10-j30 |        | 0+j0 |

$Ydense = new Matrix([
    [new Complex(10, -30), new Complex(-5,  15)],
    [new Complex(-5,  15), new Complex(10, -30)],
]);

$LUc  = LinAlg::LUdecomp($Ydense->sparse());
$Ivec = [new Complex(1, 0), new Complex(0, 0)];
$Vvec = LinAlg::LUsolver($LUc, $Ivec);
// $Vvec[0] → Complex bus voltage at node 0
// $Vvec[1] → Complex bus voltage at node 1

Example Files

The examples/ directory contains ready-to-run PHP scripts and JSON data files for all standard IEEE test systems used in the doctoral thesis:

Script System Type
example_math.php Math utilities (Angle, Complex, Matrix, Sparse, LinAlg)
example_loadflow_9bus.php IEEE 9-Bus Load Flow (inline data)
example_loadflow_57bus.php IEEE 57-Bus Load Flow (from JSON file)
example_loadflow_118bus.php IEEE 118-Bus Load Flow (from JSON file)
example_stability_9bus.php IEEE 9-Bus Transient Stability Analysis

Run any example from the command line after installing dependencies:

composer install
php examples/example_math.php
php examples/example_loadflow_9bus.php
php examples/example_loadflow_57bus.php
php examples/example_loadflow_118bus.php
php examples/example_stability_9bus.php

Package Structure

src/
├── File.php                  # JSON data file reader
├── Math/
│   ├── AbstractMatrix.php
│   ├── Angle.php
│   ├── Complex.php           # Complex number arithmetic
│   ├── LinAlg.php            # Gaussian elimination and LU decomposition
│   ├── Matrix.php            # Dense matrix operations
│   └── Sparse.php            # Sparse matrix (compressed column)
├── Models/
│   ├── AbstractModel.php     # Base class for dynamic equipment models
│   ├── Exc/
│   │   ├── Exc1.php          # IEEE Type I exciter
│   │   └── Exc2.php          # Simplified IEEE ST1 exciter
│   └── Gen/
│       ├── Gen1.php          # Classical synchronous generator
│       └── Gen2.php          # 4th-order synchronous generator
└── Tools/
    ├── AbstractTools.php     # Base class for solver tools
    ├── LoadFlow.php          # Newton-Raphson load flow solver
    └── TransientAnalysis.php # Trapezoidal transient stability solver

examples/
├── loadflow_9bus.json             # IEEE 9-Bus load flow data
├── loadflow_57bus.json            # IEEE 57-Bus load flow data
├── loadflow_118bus.json           # IEEE 118-Bus load flow data
├── stability_9bus.json            # IEEE 9-Bus transient stability data
├── example_loadflow_9bus.php      # Load flow example (inline data)
├── example_loadflow_57bus.php     # Load flow example (JSON file)
├── example_loadflow_118bus.php    # Load flow example (JSON file)
├── example_stability_9bus.php     # Transient stability example
└── example_math.php

License

This project is licensed under the GNU General Public License v2.0 or later. See the LICENSE file for details.