Measurement Model
JuliaGrid provides the Measurement type to store measurement data, with the following fields: voltmeter, ammeter, wattmeter, varmeter, and pmu. These fields contain measurement data such as bus voltage magnitude, branch current magnitude, active power flow and injection, reactive power flow and injection, and bus voltage and branch current phasors.
The type Measurement can be created using a function:
Additionally, the user can create both the PowerSystem and Measurement types using the wrapper function:
ems.
JuliaGrid supports two modes for populating the Measurement type: using built-in functions or using HDF5 files.
To work with HDF5 files, JuliaGrid provides the function:
Once the Measurement type is established, voltmeters, ammeters, wattmeters, varmeters, and phasor measurement units (PMUs) can be added using the following functions:
Also, JuliaGrid provides macros @voltmeter, @ammeter, @wattmeter, @varmeter, and @pmu to define templates that aid in creating measurement devices. These templates help avoid entering the same parameters repeatedly.
It is important to note that measurement devices associated with branches can only be incorporated if the branch is in-service. This reflects JuliaGrid's approach to mimic a network topology processor, where logical data analysis configures the energized components of the power system.
Moreover, it is feasible to modify the parameters of measurement devices. When these functions are executed, all relevant fields within the Measurement type will be automatically updated. These functions include:
The functions for updating measurement devices serve a dual purpose. While their primary function is to modify the Measurement type, they can also accept compatible state estimation models. When feasible, these functions not only modify the Measurement type but also adapt the analysis model, often resulting in improved computational efficiency. Dedicated manuals for specific analyses describe this feature in detail.
Finally, users can randomly alter the measurement set by activating or deactivating devices through the following function:
Furthermore, each specific measurement set can be modified using the following functions:
Build Model
The measurement function creates an instance of the Measurement type. It requires a PowerSystem instance representing the system being observed and a string specifying the path to the relevant HDF5 measurement file. Alternatively, a Measurement object can be created without any initial data, allowing the user to construct the measurements from scratch.
HDF5 File
To use the HDF5 file as input to create the Measurement type, the data must first be saved using the saveMeasurement function. Suppose the measurement data is saved as monitoring.h5 in the C:\hdf5 directory, and the corresponding IEEE 14-bus system data is saved as case14.h5 in the same directory. In that case, the following code constructs the Measurement type:
system = powerSystem("C:/hdf5/case14.h5")
monitoring = measurement(system, "C:/hdf5/monitoring.h5")The same result can also be achieved using the wrapper function ems:
system, monitoring = ems("C:/hdf5/case14.h5", "C:/hdf5/monitoring.h5")Model from Scratch
To start building a model from scratch, first construct a power system, then add measurement devices to buses or branches. For example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 1.0, variance = 1e-3)
addWattmeter!(monitoring; from = "Branch 1", active = 0.2, variance = 1e-4, noise = true)This creates a voltmeter that measures the bus voltage magnitude at Bus 1, with the associated mean and variance values expressed as per-unit values:
julia> [monitoring.voltmeter.magnitude.mean monitoring.voltmeter.magnitude.variance]1×2 Matrix{Float64}: 1.0 0.001
Similarly, this creates a wattmeter that measures the active power flow at the from-bus end of Branch 1, with the corresponding mean and variance values expressed as per-unit values:
julia> [monitoring.wattmeter.active.mean monitoring.wattmeter.active.variance]1×2 Matrix{Float64}: 0.181728 0.0001
The measurement values (i.e., means) can be generated by adding white Gaussian noise with specified variance values to perturb the original values. Set noise = true within the functions used for adding devices to generate these values.
Save Model
Once the Measurement type has been created using one of the methods outlined in Build Model, the current data can be stored in an HDF5 file using the saveMeasurement function:
saveMeasurement(monitoring; path = "C:/hdf5/monitoring.h5")All electrical quantities saved in the HDF5 file are stored as per-unit values and radians.
Add Voltmeter
Users can add voltmeters to a loaded measurement type or to one created from scratch. For example, initialize the Measurement type and add voltmeters using the addVoltmeter! function:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 0.9, variance = 1e-4)
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 1.0, variance = 1e-3, noise = true)This example creates two voltmeters that measure the bus voltage magnitude at Bus 1. For the second voltmeter, the measurement value is generated internally by adding white Gaussian noise with variance to the magnitude value. The resulting data is:
julia> [monitoring.voltmeter.magnitude.mean monitoring.voltmeter.magnitude.variance]2×2 Matrix{Float64}: 0.9 0.0001 1.01603 0.001
See the addVoltmeter! documentation for the list of supported keywords.
Customizing Input Units for Keywords
By default, the magnitude and variance keywords are expected to be provided as per-unit values. However, users can specify these values in volts if they prefer. For instance, consider the following example:
@voltage(kV, rad, V)
system, monitoring = ems()
addBus!(system; label = "Bus 1", base = sqrt(3) * 135e3)
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 121.5, variance = 0.0135)
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 135, variance = 0.135, noise = true)In this example, the voltage magnitude is specified in kilovolts (kV), and the variance follows the same input-unit convention. Even though kilovolts are used as the input units, these keywords are stored as per-unit values:
julia> [monitoring.voltmeter.magnitude.mean monitoring.voltmeter.magnitude.variance]2×2 Matrix{Float64}: 0.9 0.0001 0.980895 0.001
When users choose to input data in volts, measurement values and variances are related to line-to-neutral voltages, while the base values are defined for line-to-line voltages. Therefore, a conversion using $\sqrt{3}$ is necessary. For more information, refer to the Per-Unit System section.
Print Data in the REPL
Users can print the voltmeter data in the REPL using any units that have been configured:
printVoltmeterData(monitoring)|-----------------------------------------|
| Voltmeter Data |
|-----------------------------------------|
| Label | Voltage Magnitude |
| | |
| | Measurement | Variance | Status |
| | [kV] | [kV] | |
|-------|-------------|----------|--------|
| 1 | 121.5000 | 1.35e-02 | 1 |
| 2 | 132.4209 | 1.35e-01 | 1 |
|-----------------------------------------|Additionally, users can display stored data for a specific voltmeter with:
print(monitoring; voltmeter = 1)📁 1
├── 📂 Voltage Magnitude Measurement
│ ├── Mean: 0.9
│ ├── Variance: 0.0001
│ └── Status: 1
└── 📂 Layout
├── Bus: Bus 1
└── Index: 1Finally, the unit system used for voltmeter-related keywords can be checked with:
@info(unit, voltmeter)📁 Voltmeter Keyword Units
└── 📂 Voltage Magnitude Measurement
├── magnitude: kV
└── variance: kVAdd Ammeter
Users can add ammeters to an existing measurement type or to one created from scratch using the addAmmeter! function, as demonstrated in the following example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addAmmeter!(monitoring; from = "Branch 1", magnitude = 0.8, variance = 0.1, noise = true)
addAmmeter!(monitoring; to = "Branch 1", magnitude = 0.9, variance = 1e-3, square = true)This example creates one ammeter that measures the branch current magnitude at the from-bus end of Branch 1, as indicated by the from keyword. It also creates an ammeter that measures the branch current magnitude at the to-bus end of the branch using the to keyword.
For the first ammeter, the measurement value is generated by adding white Gaussian noise with variance to the magnitude value. For the second ammeter, the measurement value is treated as known and is defined by magnitude. The resulting data is:
julia> [monitoring.ammeter.magnitude.mean monitoring.ammeter.magnitude.variance]2×2 Matrix{Float64}: 0.643471 0.1 0.9 0.001
The square keyword is used for the second ammeter to indicate that the measurement will be included in AC state estimation in squared form. This means the corresponding equation is introduced without a square root; in the AC state estimation model, the measurement mean is squared and the variance is propagated as $v_{I^2} \approx 4z_I^2v_I$. This approach enhances the robustness of state estimation when handling such measurements.
See the addAmmeter! documentation for the list of supported keywords.
Customizing Input Units for Keywords
By default, the magnitude and variance keywords are expected to be provided as per-unit values. However, users can express these values in amperes (A), as shown in the following example:
@current(A, rad)
system, monitoring = ems()
addBus!(system; label = "Bus 1", base = 135e3)
addBus!(system; label = "Bus 2", base = 135e3)
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addAmmeter!(monitoring; from = "Branch 1", magnitude = 342, variance = 43, noise = true)
addAmmeter!(monitoring; to = "Branch 1", magnitude = 385, variance = 0.43, square = true)In this example, the current magnitude is specified in amperes (A), and the variance follows the same input-unit convention. It is worth noting that, despite using amperes as the input units, these keywords will still be stored in the per-unit system:
julia> [monitoring.ammeter.magnitude.mean monitoring.ammeter.magnitude.variance]2×2 Matrix{Float64}: -0.0180951 0.100546 0.900233 0.00100546
Print Data in the REPL
Users can print the ammeter data in the REPL using any units that have been configured:
printAmmeterData(monitoring)|-----------------------------------------|
| Ammeter Data |
|-----------------------------------------|
| Label | Current Magnitude |
| | |
| | Measurement | Variance | Status |
| | [A] | [A] | |
|-------|-------------|----------|--------|
| 1 | -7.7387 | 4.30e+01 | 1 |
| 2 | 385.0000 | 4.30e-01 | 1 |
|-----------------------------------------|Additionally, users can display stored data for a specific ammeter with:
print(monitoring; ammeter = 1)📁 1
├── 📂 Current Magnitude Measurement
│ ├── Mean: -0.018095101629183485
│ ├── Variance: 0.10054554937937334
│ └── Status: 1
└── 📂 Layout
├── From-Bus: Branch 1
└── Index: 1Finally, the unit system used for ammeter-related keywords can be checked with:
@info(unit, ammeter)📁 Ammeter Keyword Units
└── 📂 Current Magnitude Measurement
├── magnitude: A
└── variance: AAdd Wattmeter
Users can add wattmeters to an existing measurement type or to one created from scratch using the addWattmeter! function, as demonstrated in the following example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addWattmeter!(monitoring; bus = "Bus 1", active = 0.6, variance = 1e-3)
addWattmeter!(monitoring; from = "Branch 1", active = 0.3, variance = 1e-2)
addWattmeter!(monitoring; to = "Branch 1", active = 0.1, variance = 1e-3, noise = true)This example adds one wattmeter to measure the active power injection at Bus 1, as indicated by the use of the bus keyword. Additionally, two wattmeters are introduced to measure the active power flow on both sides of Branch 1 using the from and to keywords.
For the first and second wattmeters, the measurement values are treated as known and are defined by active. For the third wattmeter, the measurement value is generated by adding white Gaussian noise with variance to the active value. The resulting measurement data is:
julia> [monitoring.wattmeter.active.mean monitoring.wattmeter.active.variance]3×2 Matrix{Float64}: 0.6 0.001 0.3 0.01 0.0813488 0.001
See the addWattmeter! documentation for the list of supported keywords.
Customizing Input Units for Keywords
By default, the active and variance keywords are expected to be provided in per-unit values. However, users can express these values in watts if they prefer, as demonstrated in the following example:
@power(MW, pu)
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addWattmeter!(monitoring; bus = "Bus 1", active = 60, variance = 1e-1)
addWattmeter!(monitoring; from = "Branch 1", active = 30, variance = 1)
addWattmeter!(monitoring; to = "Branch 1", active = 10, variance = 1e-1, noise = true)In this example, the active power is specified in megawatts (MW), and the variance follows the same input-unit convention. Even though megawatts are used as the input units, these keywords are stored in the per-unit system:
julia> [monitoring.wattmeter.active.mean monitoring.wattmeter.active.variance]3×2 Matrix{Float64}: 0.6 0.001 0.3 0.01 0.117057 0.001
Print Data in the REPL
Users can print the wattmeter data in the REPL using any units that have been configured:
printWattmeterData(monitoring)|-----------------------------------------|
| Wattmeter Data |
|-----------------------------------------|
| Label | Active Power |
| | |
| | Measurement | Variance | Status |
| | [MW] | [MW] | |
|-------|-------------|----------|--------|
| 1 | 60.0000 | 1.00e-01 | 1 |
| 2 | 30.0000 | 1.00e+00 | 1 |
| 3 | 11.7057 | 1.00e-01 | 1 |
|-----------------------------------------|Additionally, users can display stored data for a specific wattmeter with:
print(monitoring; wattmeter = 1)📁 1
├── 📂 Active Power Measurement
│ ├── Mean: 0.6
│ ├── Variance: 0.001
│ └── Status: 1
└── 📂 Layout
├── Bus: Bus 1
└── Index: 1Finally, the unit system used for wattmeter-related keywords can be checked with:
@info(unit, wattmeter)📁 Wattmeter Keyword Units
└── 📂 Active Power Measurement
├── active: MW
└── variance: MWAdd Varmeter
To add varmeters, apply the same approach described in the Add Wattmeter section, but use the addVarmeter! function, as demonstrated in the following example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addVarmeter!(monitoring; bus = "Bus 1", reactive = 0.2, variance = 1e-3)
addVarmeter!(monitoring; from = "Branch 1", reactive = 0.1, variance = 1e-2)
addVarmeter!(monitoring; to = "Branch 1", reactive = 0.05, variance = 1e-3, noise = true)In this context, one varmeter has been added to measure the reactive power injection at Bus 1, as indicated by the use of the bus keyword. Additionally, two varmeters have been introduced to measure the reactive power flow on both sides of Branch 1 using the from and to keywords. As a result, the following outcomes are observed:
julia> [monitoring.varmeter.reactive.mean monitoring.varmeter.reactive.variance]3×2 Matrix{Float64}: 0.2 0.001 0.1 0.01 0.0598749 0.001
See the addVarmeter! documentation for the list of supported keywords.
Customizing Input Units for Keywords
As with the previous device, users can select units other than per-unit values. In this case, they can use megavolt-amperes reactive (MVAr), as illustrated in the following example:
@power(pu, MVAr)
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addVarmeter!(monitoring; bus = "Bus 1", reactive = 20, variance = 1e-1)
addVarmeter!(monitoring; from = "Branch 1", reactive = 10, variance = 1)
addVarmeter!(monitoring; to = "Branch 1", reactive = 5, variance = 1e-1, noise = true)The reactive power is specified in MVAr, and the variance follows the same input-unit convention. JuliaGrid will still store the values in the per-unit system:
julia> [monitoring.varmeter.reactive.mean monitoring.varmeter.reactive.variance]3×2 Matrix{Float64}: 0.2 0.001 0.1 0.01 0.0460165 0.001
Print Data in the REPL
Users can print the varmeter data in the REPL using any units that have been configured:
printVarmeterData(monitoring)|-----------------------------------------|
| Varmeter Data |
|-----------------------------------------|
| Label | Reactive Power |
| | |
| | Measurement | Variance | Status |
| | [MVAr] | [MVAr] | |
|-------|-------------|----------|--------|
| 1 | 20.0000 | 1.00e-01 | 1 |
| 2 | 10.0000 | 1.00e+00 | 1 |
| 3 | 4.6017 | 1.00e-01 | 1 |
|-----------------------------------------|Additionally, users can display stored data for a specific varmeter with:
print(monitoring; varmeter = 1)📁 1
├── 📂 Reactive Power Measurement
│ ├── Mean: 0.2
│ ├── Variance: 0.001
│ └── Status: 1
└── 📂 Layout
├── Bus: Bus 1
└── Index: 1Finally, the unit system used for varmeter-related keywords can be checked with:
@info(unit, varmeter)📁 Varmeter Keyword Units
└── 📂 Reactive Power Measurement
├── reactive: MVAr
└── variance: MVArAdd PMU
Users can add PMUs to an existing measurement type or create one from scratch using the addPmu! function, as demonstrated in the following example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addPmu!(monitoring; bus = "Bus 1", magnitude = 1.1, angle = 0.1, varianceMagnitude = 0.1)
addPmu!(monitoring; from = "Branch 1", magnitude = 1.0, angle = -0.2, noise = true)
addPmu!(monitoring; to = "Branch 1", magnitude = 0.9, angle = 0.0, varianceAngle = 0.001)While a PMU is typically understood as a device that measures the bus voltage phasor and all branch current phasors incident to the bus, JuliaGrid represents PMUs as individual phasor measurements to provide greater flexibility. Each phasor is described by magnitude and angle values, along with the corresponding variances, in the polar coordinate system.
In this context, one PMU has been added to measure the bus voltage phasor at Bus 1, as indicated by the use of the bus keyword. Additionally, two PMUs have been introduced to measure the branch current phasors on both sides of Branch 1 using the from and to keywords.
For the first and third PMUs, the measurement values are treated as known and are defined by the magnitude and angle keywords. For the second PMU, the measurement value is generated by adding white Gaussian noise with varianceMagnitude and varianceAngle to the magnitude and angle values, respectively. When variance values are omitted, the defaults are used, both equal to 1e-8. The resulting data is:
julia> [monitoring.pmu.magnitude.mean monitoring.pmu.magnitude.variance]3×2 Matrix{Float64}: 1.1 0.1 0.999984 1.0e-8 0.9 1.0e-8julia> [monitoring.pmu.angle.mean monitoring.pmu.angle.variance]3×2 Matrix{Float64}: 0.1 1.0e-8 -0.199991 1.0e-8 0.0 0.001
See the addPmu! documentation for the list of supported keywords.
PMU State Estimation and Coordinate System
When users add PMUs and create a PmuStateEstimation type, they specify that the estimation model should rely only on PMUs. In this case, phasor measurements are always incorporated in the rectangular coordinate system. Here, the real and imaginary components of the phasor measurements become correlated, but these correlations are typically ignored [2]. To account for them, users can set the keyword correlated = true. For example:
addPmu!(monitoring; bus = "Bus 2", magnitude = 1, angle = 0)
addPmu!(monitoring; from = "Branch 1", magnitude = 0.9, angle = -0.3, correlated = true)For the first phasor measurement, correlation is neglected, whereas for the second, it is considered.
AC State Estimation and Coordinate Systems
In AC state estimation, when users create an AcStateEstimation type, PMUs are by default integrated into the rectangular coordinate system, where correlations are neglected. Users can also set correlated = true to account for the correlation between the real and imaginary components of the phasor measurements. Additionally, in the AC state estimation model, users can incorporate phasor measurements in the polar coordinate system by specifying polar = true.
For example, add PMUs:
addPmu!(monitoring; bus = "Bus 2", magnitude = 1, angle = 0, polar = true, square = true)
addPmu!(monitoring; from = "Branch 1", magnitude = 0.9, angle = -0.3, correlated = true)The first phasor measurement will be incorporated into the AC state estimation model using the polar coordinate system. Additionally, by setting square = true, the current magnitude measurement will be included in its squared form. The second PMU will be integrated into the rectangular coordinate system with correlation between the real and imaginary components enabled.
It is noteworthy that expressing current phasor measurements in polar coordinates can lead to ill-conditioned problems due to small current magnitudes, whereas using rectangular representation can resolve this issue.
Customizing Input Units for Keywords
By default, the magnitude and varianceMagnitude keywords are expected to be provided in per-unit, while the angle and varianceAngle keywords are expected to be provided in radians. However, users can express these values in different units, such as volts (V) and degrees (deg) if the PMU is set to a bus, or amperes (A) and degrees (deg) if the PMU is set to a branch. This flexibility is demonstrated in the following:
@voltage(kV, deg, V)
@current(A, deg)
system, monitoring = ems()
addBus!(system; label = "Bus 1", base = 135e3)
addBus!(system; label = "Bus 2", base = 135e3)
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addPmu!(monitoring; bus = "Bus 1", magnitude = 85.74, angle = 5.73, varianceAngle = 0.06)
addPmu!(monitoring; from = "Branch 1", magnitude = 427.67, angle = -11.46, noise = true)
addPmu!(monitoring; to = "Branch 1", magnitude = 384.91, angle = 0.0)In this example, kilovolts (kV) and degrees (deg) are specified for the PMU located at Bus 1, and amperes (A) and degrees (deg) are specified for the PMUs located at Branch 1. The magnitude and angle variances follow the same input-unit conventions. Regardless of the input units, the values are stored as per-unit values and radians:
julia> [monitoring.pmu.magnitude.mean monitoring.pmu.magnitude.variance]3×2 Matrix{Float64}: 1.10004 1.0e-8 1.0 1.0e-8 0.900023 1.0e-8julia> [monitoring.pmu.angle.mean monitoring.pmu.angle.variance]3×2 Matrix{Float64}: 0.100007 0.0010472 -0.200178 1.0e-8 0.0 1.0e-8
Print Data in the REPL
Users can print the PMU data in the REPL using any units that have been configured:
printPmuData(monitoring)|---------------------------------------------------------------------------|
| PMU Data |
|---------------------------------------------------------------------------|
| Label | Voltage Magnitude | Voltage Angle |
| | | |
| | Measurement | Variance | Status | Measurement | Variance | Status |
| | [kV] | [kV] | | [deg] | [deg] | |
|-------|-------------|----------|--------|-------------|----------|--------|
| 1 | 85.7400 | 7.79e-07 | 1 | 5.7300 | 6.00e-02 | 1 |
|---------------------------------------------------------------------------|
|---------------------------------------------------------------------------|
| PMU Data |
|---------------------------------------------------------------------------|
| Label | Current Magnitude | Current Angle |
| | | |
| | Measurement | Variance | Status | Measurement | Variance | Status |
| | [A] | [A] | | [deg] | [deg] | |
|-------|-------------|----------|--------|-------------|----------|--------|
| 2 | 427.6670 | 4.28e-06 | 1 | -11.4694 | 5.73e-07 | 1 |
| 3 | 384.9100 | 4.28e-06 | 1 | 0.0000 | 5.73e-07 | 1 |
|---------------------------------------------------------------------------|Additionally, users can display stored data for a specific PMU with:
print(monitoring; pmu = 1)📁 1
├── 📂 Voltage Magnitude Measurement
│ ├── Mean: 1.100044712895967
│ ├── Variance: 1.0e-8
│ └── Status: 1
├── 📂 Voltage Angle Measurement
│ ├── Mean: 0.10000736613927509
│ ├── Variance: 0.0010471975511965976
│ └── Status: 1
└── 📂 Layout
├── Bus: Bus 1
├── Polar: false
├── Correlated: false
└── Index: 1Finally, the unit system used for PMU-related keywords can be checked with:
@info(unit, pmu)📁 PMU Keyword Units
├── 📂 Voltage Phasor Measurement
│ ├── magnitude: kV
│ ├── varianceMagnitude: kV
│ ├── angle: deg
│ └── varianceAngle: deg
└── 📂 Current Phasor Measurement
├── magnitude: A
├── varianceMagnitude: A
├── angle: deg
└── varianceAngle: degAdd Templates
The functions addVoltmeter!, addAmmeter!, addWattmeter!, addVarmeter!, and addPmu! are used to add measurement devices. In cases where specific keywords are not explicitly defined, default values are automatically assigned to certain parameters.
Default Keyword Values
When using the addVoltmeter! function, the default variance is set to variance = 1e-4 per-unit, and the voltmeter's operational status is automatically assumed to be in-service, as indicated by the setting of status = 1.
Similarly, for the addAmmeter! function, the default variances are variance = 1e-4 per-unit, and the default statuses are status = 1. This means that if a user places an ammeter at either the from-bus or to-bus end of a branch, the default settings are identical. The following subsection explains how to customize these defaults for each location.
As with ammeters, the addWattmeter! and addVarmeter! functions use default variances of variance = 1e-4 per-unit and default statuses of status = 1, regardless of whether the wattmeter or varmeter is placed at the bus, the from-bus end, or the to-bus end. Users can customize these defaults for each measurement-device location.
For the addPmu! function, the default magnitude and angle variances are varianceMagnitude = 1e-8 and varianceAngle = 1e-8 as per-unit values. The default status is status = 1, regardless of whether the PMU is placed at the bus, the from-bus end, or the to-bus end. Users can customize these defaults for each measurement-device location. For AC state estimation, the coordinate system defaults to polar = false, while correlation in the rectangular system is disabled with correlated = false.
Across all measurement devices, the method for generating measurement means is established as noise = false.
Change Default Keyword Values
In JuliaGrid, users can customize default values and assign custom settings using the @voltmeter, @ammeter, @wattmeter, @varmeter, and @pmu macros. These macros create voltmeter, ammeter, wattmeter, varmeter, and PMU templates that are used each time functions for adding measurement devices are called. Here is an example of creating these templates with customized default values:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
@voltmeter(variance = 1e-4, noise = true)
addVoltmeter!(monitoring; label = "Voltmeter 1", bus = "Bus 1", magnitude = 1.0)
@ammeter(varianceFrom = 1e-3, varianceTo = 1e-4, statusTo = 0)
addAmmeter!(monitoring; label = "Ammeter 1", from = "Branch 1", magnitude = 1.1)
addAmmeter!(monitoring; label = "Ammeter 2", to = "Branch 1", magnitude = 0.9)
@wattmeter(varianceBus = 1e-3, statusFrom = 0, noise = true)
addWattmeter!(monitoring; label = "Wattmeter 1", bus = "Bus 1", active = 0.6)
addWattmeter!(monitoring; label = "Wattmeter 2", from = "Branch 1", active = 0.3)
addWattmeter!(monitoring; label = "Wattmeter 3", to = "Branch 1", active = 0.1)
@varmeter(varianceFrom = 1e-3, varianceTo = 1e-3, statusBus = 0)
addVarmeter!(monitoring; label = "Varmeter 1", bus = "Bus 1", reactive = 0.2)
addVarmeter!(monitoring; label = "Varmeter 2", from = "Branch 1", reactive = 0.1)
addVarmeter!(monitoring; label = "Varmeter 3", to = "Branch 1", reactive = 0.05)
@pmu(varianceMagnitudeBus = 1e-4, statusBus = 0, varianceAngleFrom = 1e-3)
addPmu!(monitoring; label = "PMU 1", bus = "Bus 1", magnitude = 1.1, angle = -0.1)
addPmu!(monitoring; label = "PMU 2", from = "Branch 1", magnitude = 1.0, angle = -0.2)
addPmu!(monitoring; label = "PMU 3", to = "Branch 1", magnitude = 0.9, angle = 0.0)For instance, when adding a wattmeter to the bus, the varianceBus = 1e-3 will be applied, or if it is added to the from-bus end of the branch, these wattmeters will be set out-of-service according to statusFrom = 0.
It is important to note that changing input units will also impact the templates accordingly.
Users can view the templates associated with voltmeter, ammeter, wattmeter, varmeter, or PMU keywords. For example, to check templates related to wattmeter keywords, use:
@info(template, wattmeter)📁 Wattmeter Template
├── 📂 Label
│ └── label: ?
├── 📂 Active Power Injection Measurement
│ ├── varianceBus: 0.001 [pu]
│ └── statusBus: 1
├── 📂 From-Bus Active Power Flow Measurement
│ ├── varianceFrom: 0.0001 [pu]
│ └── statusFrom: 0
├── 📂 To-Bus Active Power Flow Measurement
│ ├── varianceTo: 0.0001 [pu]
│ └── statusTo: 1
└── 📂 Setting
└── noise: trueMultiple Templates
In the case of calling the macros multiple times, the provided keywords and values will be combined into a single template for the corresponding measurement device.
Reset Templates
Reset the measurement device templates to their default settings with:
@default(voltmeter)
@default(ammeter)
@default(wattmeter)
@default(varmeter)
@default(pmu)Additionally, users can reset all templates using the macro:
@default(template)Labels
JuliaGrid requires a unique label for each voltmeter, ammeter, wattmeter, varmeter, or PMU. These labels are stored in ordered dictionaries, functioning as pairs of strings and integers. The string is the device label, while the integer tracks the internal numbering of measurement devices.
All previous examples except the last one use automatic labeling by omitting the label keyword. In such cases, JuliaGrid assigns unique labels to measurement devices using a sequential set of increasing integers. The last example demonstrates user-defined labeling.
String labels improve readability, but in larger models, the overhead from using strings can become substantial. To reduce memory usage, users can configure ordered dictionaries to accept and store integers as labels:
@config(label = Integer)Integer-Based Labeling
Instead of using strings for labels, Julia provides the @config macro to enable storing labels as integers:
@config(label = Integer)
system, monitoring = ems()
addBus!(system; label = 1)
addBus!(system; label = 2)
addBranch!(system; label = 1, from = 1, to = 2, reactance = 0.12)
addVoltmeter!(monitoring; label = 1, bus = 1, magnitude = 1.0)
addAmmeter!(monitoring; label = 1, from = 1, magnitude = 1.1)
addAmmeter!(monitoring; label = 2, to = 1, magnitude = 0.9)Note that the @config macro must be executed first. Otherwise, even if integers are passed to the functions, labels will be stored as strings. In this example, all labels, both in the power system and in the measurement system, are stored as integers.
Integer-String-Based Labeling
In addition to using only strings or only integers, JuliaGrid supports mixed labeling. Users can fine-tune labels for all power system components as well as for all measurement devices. For example:
@branch(label = Integer)
@ammeter(label = Integer)
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = 1, from = "Bus 1", to = "Bus 2", reactance = 0.12)
addVoltmeter!(monitoring; label = "Voltmeter 1", bus = "Bus 1", magnitude = 1.0)
addAmmeter!(monitoring; label = 1, from = 1, magnitude = 1.1)
addAmmeter!(monitoring; label = 2, to = 1, magnitude = 0.9)In this example, string labels are used for buses and voltmeters, while integers are used for branches and ammeters. The same configuration can be created using the @config macro along with the macros for specifying power system components and measurement devices:
@config(label = Integer)
@bus(label = String)
@voltmeter(label = String)Automated Labeling Using Templates
Labels can also be created using templates and the symbol ? to insert an incremental set of integers at any position. In addition, users can use the symbol ! to insert the location of the measurement device into the label. For example:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
@voltmeter(label = "Voltmeter ?")
addVoltmeter!(monitoring; bus = "Bus 1", magnitude = 1.0)
addVoltmeter!(monitoring; bus = "Bus 2", magnitude = 0.9)
@ammeter(label = "!")
addAmmeter!(monitoring; from = "Branch 1", magnitude = 1.1)
addAmmeter!(monitoring; to = "Branch 1", magnitude = 0.9)
@wattmeter(label = "Wattmeter ?: !")
addWattmeter!(monitoring; bus = "Bus 1", active = 0.6)
addWattmeter!(monitoring; from = "Branch 1", active = 0.3)To illustrate, the voltmeter labels are defined with incremental integers:
julia> monitoring.voltmeter.labelOrderedCollections.OrderedDict{String, Int64} with 2 entries: "Voltmeter 1" => 1 "Voltmeter 2" => 2
Moreover, for ammeter labels, location information is used:
julia> monitoring.ammeter.labelOrderedCollections.OrderedDict{String, Int64} with 2 entries: "From Branch 1" => 1 "To Branch 1" => 2
Lastly, for wattmeters, a combination of both approaches is used:
julia> monitoring.wattmeter.labelOrderedCollections.OrderedDict{String, Int64} with 2 entries: "Wattmeter 1: Bus 1" => 1 "Wattmeter 2: From Branch 1" => 2
Retrieving Labels
Stored labels can be retrieved as follows. Consider the following model:
system, monitoring = ems()
addBus!(system; label = "Bus 1")
addBus!(system; label = "Bus 2")
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.12)
addBranch!(system; label = "Branch 2", from = "Bus 2", to = "Bus 1", reactance = 0.14)
addWattmeter!(monitoring; label = "Wattmeter 2", bus = "Bus 2", active = 0.6)
addWattmeter!(monitoring; label = "Wattmeter 1", bus = "Bus 1", active = 0.2)
addWattmeter!(monitoring; label = "Wattmeter 4", from = "Branch 1", active = 0.3)
addWattmeter!(monitoring; label = "Wattmeter 3", to = "Branch 1", active = 0.1)
addWattmeter!(monitoring; label = "Wattmeter 5", from = "Branch 2", active = 0.1)To access the wattmeter labels, use:
julia> monitoring.wattmeter.labelOrderedCollections.OrderedDict{String, Int64} with 5 entries: "Wattmeter 2" => 1 "Wattmeter 1" => 2 "Wattmeter 4" => 3 "Wattmeter 3" => 4 "Wattmeter 5" => 5
To obtain only labels, use:
julia> label = collect(keys(monitoring.wattmeter.label))5-element Vector{String}: "Wattmeter 2" "Wattmeter 1" "Wattmeter 4" "Wattmeter 3" "Wattmeter 5"
To isolate the wattmeters positioned either at the buses or at the ends of branches (from-bus or to-bus), users can achieve this using the following code:
julia> label[monitoring.wattmeter.layout.bus]2-element Vector{String}: "Wattmeter 2" "Wattmeter 1"julia> label[monitoring.wattmeter.layout.from]2-element Vector{String}: "Wattmeter 4" "Wattmeter 5"julia> label[monitoring.wattmeter.layout.to]1-element Vector{String}: "Wattmeter 3"
Furthermore, when using the addWattmeter! function, the labels for the keywords bus, from, and to are stored internally as numerical values. To retrieve bus labels, use:
julia> label = collect(keys(system.bus.label));julia> label[monitoring.wattmeter.layout.index[monitoring.wattmeter.layout.bus]]2-element Vector{String}: "Bus 2" "Bus 1"
Similarly, to obtain labels for branches, use:
julia> label = collect(keys(system.branch.label));julia> label[monitoring.wattmeter.layout.index[monitoring.wattmeter.layout.from]]2-element Vector{String}: "Branch 1" "Branch 2"julia> label[monitoring.wattmeter.layout.index[monitoring.wattmeter.layout.to]]1-element Vector{String}: "Branch 1"
This procedure is applicable to all measurement devices, including voltmeters, ammeters, varmeters, and PMUs.
JuliaGrid can print labels alongside various types of data. For instance, users can use the following code to print labels in combination with specific data:
julia> print(monitoring.wattmeter.label, monitoring.wattmeter.active.mean)Wattmeter 2: 0.6 Wattmeter 1: 0.2 Wattmeter 4: 0.3 Wattmeter 3: 0.1 Wattmeter 5: 0.1
Managing Labels in HDF5 Imports
When saving the measurements to an HDF5 file, the label type (strings or integers) will match the type chosen during system setup. Similarly, when loading data from an HDF5 file, the label type is preserved exactly as it was saved, regardless of any settings provided by the @config macro or macros related to measurement devices.
Add Multiple Devices
Users can add measurement devices with data generated from one of the AC analyses, specifically, using results obtained from either AC power flow or AC optimal power flow. To do this, users simply need to provide an AC analysis object as an argument to one of the functions responsible for adding measurement devices:
system, monitoring = ems()
addBus!(system; label = "Bus 1", type = 3, active = 0.5, magnitude = 0.9, angle = 0.0)
addBus!(system; label = "Bus 2", type = 1, reactive = 0.05, magnitude = 1.1, angle = -0.1)
addBus!(system; label = "Bus 3", type = 1, active = 0.5, magnitude = 1.0, angle = -0.2)
@branch(resistance = 0.03, susceptance = 0.02)
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.5)
addBranch!(system; label = "Branch 2", from = "Bus 1", to = "Bus 3", reactance = 0.1)
addBranch!(system; label = "Branch 3", from = "Bus 2", to = "Bus 3", reactance = 0.2)
addGenerator!(system; label = "Generator 1", bus = "Bus 1", active = 0.2)
addGenerator!(system; label = "Generator 2", bus = "Bus 2", active = 1.2)
analysis = newtonRaphson(system)
powerFlow!(analysis; power = true, current = true)
@voltmeter(label = "!", noise = true)
addVoltmeter!(monitoring, analysis; variance = 1e-3)
@ammeter(label = "!")
addAmmeter!(monitoring, analysis; varianceFrom = 1e-3, statusTo = 0, noise = true)
@wattmeter(label = "!")
addWattmeter!(monitoring, analysis; varianceBus = 1e-3, statusFrom = 0)
@varmeter(label = "!")
addVarmeter!(monitoring, analysis; varianceFrom = 1e-3, statusBus = 0)
@pmu(label = "!", polar = true)
addPmu!(monitoring, analysis; varianceMagnitudeBus = 1e-3)This example adds voltmeters to all buses and ammeters to both ends of each branch. It sets noise = true once in the template and once directly in the function, so measurement values are generated by adding white Gaussian noise with specified variances to the values obtained from the AC power flow analysis.
For wattmeters, varmeters, and PMUs added to all buses and branches, the default setting noise = false produces measurement values that match those obtained from the AC power flow analysis. When PMUs are included in the AC state estimation model, the polar coordinate system is selected by setting polar = true.
It is important to note that JuliaGrid follows a specific order: it first adds bus measurements, then branch measurements. For branches, it adds a measurement located at the from-bus end and, immediately after, a measurement at the to-bus end. This process is repeated for all in-service branches.
Groups of measurements can also be added with the functions that add measurements individually. This approach may be more straightforward. For example, to add wattmeters similarly to the procedure outlined above, use:
Pᵢ = analysis.power.injection.active
for (label, idx) in system.bus.label
addWattmeter!(monitoring; bus = label, active = Pᵢ[idx], variance = 1e-3)
end
Pᵢⱼ = analysis.power.from.active
Pⱼᵢ = analysis.power.to.active
for (label, idx) in system.branch.label
addWattmeter!(monitoring; from = label, active = Pᵢⱼ[idx], status = 0)
addWattmeter!(monitoring; to = label, active = Pⱼᵢ[idx])
endUpdate Devices
After the addition of measurement devices to the Measurement type, users can modify all parameters as defined in the function that added these measurement devices.
Update Voltmeter
Users can modify all parameters as defined within the addVoltmeter! function. For illustration, continue with the example from the Add Device Groups section:
updateVoltmeter!(monitoring; label = "Bus 2", magnitude = 0.9, noise = false)This example updates the measurement value of the voltmeter located at Bus 2, and this measurement is now generated without the inclusion of white Gaussian noise.
Update Ammeter
Similarly, users can modify all parameters defined within the addAmmeter! function. Using the same example from the Add Device Groups section, use:
updateAmmeter!(monitoring; label = "From Branch 2", magnitude = 1.2, variance = 1e-4)
updateAmmeter!(monitoring; label = "To Branch 2", status = 0)This example adjusts the measurement and variance values of the ammeter located at Branch 2, specifically at the from-bus end. Next, it deactivates the ammeter at the same branch on the to-bus end.
Update Wattmeter
Following the same logic, users can modify all parameters defined within the addWattmeter! function:
updateWattmeter!(monitoring; label = "Bus 1", active = 1.2, variance = 1e-4)
updateWattmeter!(monitoring; label = "To Branch 1", variance = 1e-6)This example modifies the measurement and variance values for the wattmeter located at Bus 1. The wattmeter at Branch 1 on the to-bus end retains its measurement value, while only the measurement variance is adjusted.
Update Varmeter
Following the same logic, users can modify all parameters defined within the addVarmeter! function:
updateVarmeter!(monitoring; label = "Bus 1", reactive = 1.2)
updateVarmeter!(monitoring; label = "Bus 2", status = 0)This example adjusts the measurement value of the varmeter located at Bus 1, while using a previously defined variance. It also deactivates the varmeter at Bus 2 and designates it as out-of-service.
Update PMU
Finally, users can modify all PMU parameters defined within the addPmu! function:
updatePmu!(monitoring; label = "Bus 1", magnitude = 1.05, noise = true)
updatePmu!(monitoring; label = "From Branch 1", varianceAngle = 1e-6, polar = false)This example adjusts the magnitude measurement value of the PMU located at Bus 1. The measurement is generated by adding white Gaussian noise using the specified variance value to perturb the magnitude value, while keeping the bus voltage angle value unchanged. For the PMU placed at Branch 1 on the from-bus end, the existing measurement values are retained and only the angle variance is adjusted. The measurement is also included in the rectangular coordinate system for the AC state estimation.
Measurement Set
Once measurement devices are integrated into the Measurement type, users can create randomized measurement sets. More precisely, users can activate or deactivate devices according to specific settings. To illustrate this feature, first create a measurement set with the following example:
system, monitoring = ems()
addBus!(system; label = "Bus 1", type = 3, active = 0.5, magnitude = 0.9, angle = 0.0)
addBus!(system; label = "Bus 2", type = 1, reactive = 0.05, magnitude = 1.1, angle = -0.1)
addBus!(system; label = "Bus 3", type = 1, active = 0.5, magnitude = 1.0, angle = -0.2)
@branch(resistance = 0.03, susceptance = 0.02)
addBranch!(system; label = "Branch 1", from = "Bus 1", to = "Bus 2", reactance = 0.5)
addBranch!(system; label = "Branch 2", from = "Bus 1", to = "Bus 3", reactance = 0.1)
addBranch!(system; label = "Branch 3", from = "Bus 2", to = "Bus 3", reactance = 0.2)
addGenerator!(system; label = "Generator 1", bus = "Bus 1", active = 0.2)
addGenerator!(system; label = "Generator 2", bus = "Bus 2", active = 1.2)
analysis = newtonRaphson(system)
powerFlow!(analysis; power = true, current = true)
addVoltmeter!(monitoring, analysis)
addAmmeter!(monitoring, analysis)
addPmu!(monitoring, analysis)Activating Devices
As a starting point, create a measurement set where all devices are set in-service based on default settings. This example generates a measurement set comprising 3 voltmeters, 6 ammeters, and 9 PMUs.
Users can modify the status of in-service devices with the status! function. For example, to keep only 12 of the 18 devices in-service, use:
status!(monitoring; inservice = 12)Upon executing this function, 12 devices will be randomly selected as in-service, while the remaining 6 will be set out-of-service.
Furthermore, users can refine the status changes for specific measurements. For example, to activate only 2 ammeters while deactivating the remaining ammeters:
statusAmmeter!(monitoring; inservice = 2)This action will result in 2 ammeters being in-service and 4 being out-of-service.
Users can further refine these actions by specifying devices at particular locations within the power system. For instance, to enable 3 PMUs at buses to measure bus voltage phasors while deactivating all PMUs at branches that measure current phasors, use:
statusPmu!(monitoring; inserviceBus = 3, inserviceFrom = 0, inserviceTo = 0)The outcome will be that 3 PMUs are set in-service at buses for voltage phasor measurements, while all PMUs at branches measuring current phasors will be set out-of-service.
Deactivating Devices
Likewise, users can specify the number of devices to be set out-of-service rather than defining the number of in-service devices. For instance, to deactivate just 2 devices from the total measurement set, use:
status!(monitoring; outservice = 2)This randomly deactivates 2 devices, while the rest remain in-service. Similar to the previous approach, users can apply this to specific devices or use fine-tuning as needed.
Activating Devices Using Redundancy
Furthermore, users can use redundancy, which represents the ratio between measurement devices and state variables. For example, to set the number of measurement devices to 1.2 times greater than the number of state variables, use:
status!(monitoring; redundancy = 1.2)Considering that the number of state variables is 5 (excluding the voltage angle related to the slack bus), using a redundancy value of 1.2 will result in 6 devices being set in-service, while the remainder will be deactivated. As before, users can target specific devices or adjust settings as needed.