Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/GridOPTICS/GridPACK/llms.txt

Use this file to discover all available pages before exploring further.

GridPACK’s state estimation module provides a parallel weighted least squares (WLS) solver for computing the most likely operating state of a power network from a sparse, noisy set of measurements. The SEAppModule class in the gridpack::state_estimation namespace accepts measurements of bus voltage magnitude, voltage angle, real and reactive power injection, real and reactive branch power flow, and branch current magnitude. After solving the Newton-Raphson normal equations, the module runs an automatic bad data detection loop based on the chi-square distribution of weighted residuals. Results can be saved to DataCollection objects and used to initialize a dynamic simulation, making state estimation a natural first stage in a hybrid estimation-simulation workflow.

Setting up and running state estimation

1

Open the configuration file

Parse the XML input deck and obtain a configuration pointer. The State_estimation block must be accessible from the cursor when readNetwork() is called.
gridpack::utility::Configuration *config =
    gridpack::utility::Configuration::configuration();
config->open("input.xml", world);
2

Read and partition the network

Create an empty SENetwork and pass it to readNetwork(). The method reads the PSS/E RAW file named in the networkConfiguration field and partitions the network across MPI ranks.
boost::shared_ptr<gridpack::state_estimation::SENetwork>
    se_network(new gridpack::state_estimation::SENetwork(world));

gridpack::state_estimation::SEAppModule se_app;
se_app.readNetwork(se_network, config);
If the network already exists (cloned from a power flow or previous calculation), use setNetwork() instead to avoid re-reading and re-partitioning:
se_app.setNetwork(se_network, config);
3

Load measurements

Read measurements from the XML file named in the measurementList field:
se_app.readMeasurements();
Alternatively, supply measurements programmatically using setMeasurements(). This is useful when measurements come from another module or a non-XML data source:
std::vector<gridpack::state_estimation::Measurement> meas;
// ... populate meas with bus and branch measurements ...
se_app.setMeasurements(meas);
4

Initialize exchange buffers

Set up internal indices and the MPI exchange buffers:
se_app.initialize();
5

Solve the WLS problem

Run the Newton-Raphson WLS solver with automatic bad data detection:
se_app.solve();
The method returns after convergence or after exhausting the iteration limit. Query hasConverged() to check the outcome.
if (!se_app.hasConverged()) {
    // Handle non-convergence
}
6

Write and save results

Write bus voltages, branch power flows, and a measurement residual comparison to standard output:
se_app.write();
Persist results into DataCollection objects for downstream modules:
se_app.saveData();
Stored variables include BUS_SE_VMAG, BUS_SE_VANG, GENERATOR_SE_PGEN[i], and GENERATOR_SE_QGEN[i].

Measurement types

The measurement file (or setMeasurements() vector) supports the following types:
TypeDescriptionElement
VMVoltage magnitude (pu)Bus
VAVoltage angle (radians)Bus
PIReal power injection (pu)Bus
QIReactive power injection (pu)Bus
PIJReal power flow, from-bus direction (pu)Branch
QIJReactive power flow, from-bus direction (pu)Branch
IIJCurrent magnitude, from-bus direction (pu)Branch
IJICurrent magnitude, to-bus direction (pu)Branch
Each measurement entry also carries a Deviation field (standard deviation in the same units) that sets the WLS weight as 1 / σ².

Measurement file format

The measurement file is a standalone XML document referenced by the measurementList field:
<Measurements>
  <!-- Bus voltage magnitude at bus 1 -->
  <Measurement>
    <Type>VM</Type>
    <Bus>1</Bus>
    <Value>1.0600</Value>
    <Deviation>0.0050</Deviation>
  </Measurement>

  <!-- Real power flow from bus 1 to bus 2, circuit BL -->
  <Measurement>
    <Type>PIJ</Type>
    <FromBus>1</FromBus>
    <ToBus>2</ToBus>
    <CKT>BL</CKT>
    <Value>1.5688</Value>
    <Deviation>0.0100</Deviation>
  </Measurement>

  <!-- Real power injection at bus 1 -->
  <Measurement>
    <Type>PI</Type>
    <Bus>1</Bus>
    <Value>2.3240</Value>
    <Deviation>0.0100</Deviation>
  </Measurement>

  <!-- Current magnitude from bus 1 to bus 2 -->
  <Measurement>
    <Type>IIJ</Type>
    <FromBus>1</FromBus>
    <ToBus>2</ToBus>
    <CKT>BL</CKT>
    <Value>1.5920</Value>
    <Deviation>0.0100</Deviation>
  </Measurement>
</Measurements>
Multiple measurements are allowed on a single bus or branch. The state estimation module does not verify observability; if there are insufficient measurements for the system to be mathematically solvable, the solver will fail to converge or crash.

XML configuration reference

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <State_estimation>
    <!-- Network file (PSS/E RAW; version-specific suffixes are also supported) -->
    <networkConfiguration> IEEE14.raw </networkConfiguration>

    <!-- Measurement file -->
    <measurementList> IEEE14_meas.xml </measurementList>

    <!-- Newton-Raphson convergence parameters -->
    <maxIteration>20</maxIteration>
    <tolerance>1.0e-6</tolerance>
    <dampingFactor>1.0</dampingFactor>   <!-- 1.0 = no damping; <1.0 for ill-conditioned cases -->

    <!-- Bad data detection parameters -->
    <maxBadDataIterations>5</maxBadDataIterations>
    <badDataThreshold>3.0</badDataThreshold>   <!-- normalized residual threshold -->

    <!-- Diagnostic output verbosity: "basic", "standard", or "detailed" -->
    <diagnosticOutputLevel>standard</diagnosticOutputLevel>

    <!-- Optional voltage constraints modeled as virtual measurements -->
    <useVoltageConstraints>false</useVoltageConstraints>
    <minVoltage>0.9</minVoltage>
    <maxVoltage>1.1</maxVoltage>

    <LinearSolver>
      <PETScOptions>
        -ksp_type richardson
        -pc_type lu
        -pc_factor_mat_solver_type superlu_dist
        -ksp_max_it 1
      </PETScOptions>
    </LinearSolver>
  </State_estimation>
</Configuration>

Weighted least squares algorithm

The WLS estimator minimizes the objective function J(x) = (z − h(x))ᵀ W (z − h(x)), where z is the measurement vector, h(x) is the vector of measurement functions evaluated at the state x, and W = diag(1/σᵢ²) is the diagonal weight matrix. The normal equations are solved iteratively using Newton-Raphson:
Hᵀ W H Δx = Hᵀ W (z − h(x))
where H = ∂h/∂x is the Jacobian. GridPACK assembles H in parallel using the mapped bus-vector framework and solves the normal equations using PETSc. Sparse matrix storage is enabled internally when beneficial for large systems, controlled by the p_use_sparse_matrices flag in the implementation. The dampingFactor parameter scales each Newton-Raphson update step before it is applied to the state vector. A value of 1.0 (default) gives full Newton-Raphson steps. Values below 1.0 can improve convergence stability for poorly conditioned measurement sets.

Bad data detection

After each Newton-Raphson convergence, the solver computes the chi-square statistic for the weighted measurement residuals. If the statistic exceeds the threshold for the system’s degrees of freedom, the measurement with the largest normalized residual is identified and removed from the estimator. The solver then re-converges with the reduced measurement set. This process repeats up to maxBadDataIterations (default 5) times. A measurement is flagged as bad data when its normalized residual exceeds badDataThreshold (default 3.0 standard deviations). The chi-square threshold is computed from the degrees of freedom (number of measurements minus number of state variables) at the configured confidence level.
The write() output includes a side-by-side comparison of each measurement’s estimated value and its original measured value, making it straightforward to identify which measurements were flagged or have large residuals.

Diagnostic output levels

The diagnosticOutputLevel parameter controls solver verbosity:
LevelOutput
basicConvergence status only
standardPer-iteration mismatch, bad data summary, measurement residuals
detailedFull Jacobian performance statistics, per-measurement normalized residuals, chi-square value and threshold, degrees of freedom

Python API

The Python binding exposes setMeasurements() for supplying measurements at runtime, which is useful in co-simulation and scripting workflows:
import gridpack

se = gridpack.state_estimation.SEAppModule()
se.setNetwork(se_network, config)

measurements = [
    gridpack.state_estimation.Measurement(type="VM", bus=1, value=1.06, deviation=0.005),
    gridpack.state_estimation.Measurement(type="PI", bus=1, value=2.324, deviation=0.01),
    # ... additional measurements ...
]
se.setMeasurements(measurements)
se.initialize()
se.solve()
se.write()
This API was added in GridPACK v3.6 and is documented in the Sphinx Python interface reference.

Voltage constraint measurements

When useVoltageConstraints is true, the solver adds virtual voltage magnitude measurements at every bus with very tight standard deviations (deviation=0.001) that constrain voltages to the [minVoltage, maxVoltage] interval. PV bus voltages are handled separately via handlePVBusVoltages(), which ensures that generator terminal voltages are treated as equality constraints rather than soft measurements.

Chaining state estimation with dynamic simulation

Because saveData() writes results into the network’s DataCollection objects, a state estimation solution can serve directly as the initial operating point for dynamic simulation initialization, bypassing the need for a separate power flow run:
// Step 1: run state estimation
se_app.readNetwork(se_network, config);
se_app.readMeasurements();
se_app.initialize();
se_app.solve();
se_app.saveData();

// Step 2: clone the network and hand off to dynamic simulation
boost::shared_ptr<gridpack::dynamic_simulation::DSFullNetwork> ds_network;
// Clone se_network into ds_network (GridPACK network clone API)
// ...

gridpack::dynamic_simulation::DSFullApp ds_app;
ds_app.setNetwork(ds_network, ds_config);
ds_app.readGenerators();
ds_app.initialize();  // uses BUS_SE_VMAG / BUS_SE_VANG for initialization
The stored variables BUS_SE_VMAG and BUS_SE_VANG are used by the dynamic simulation factory to set the initial bus voltages instead of the default power flow values.

Test cases

GridPACK ships updated IEEE 14-bus and IEEE 118-bus test inputs for state estimation in the src/applications/modules/state_estimation/test/ directory. These include measurement files for all supported types and input XML files demonstrating bad data injection and detection.

Power flow

Run a conventional power flow as an alternative base-case initialization method.

Dynamic simulation

Chain state estimation results into a transient stability simulation.

Build docs developers (and LLMs) love