Skip to main content

surge_network/network/
measurement.rs

1// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0
2//! CIM-aligned measurement types for CGMES Measurement profile integration.
3//!
4//! These types store CIM mRID linkage and can be resolved to internal 0-based
5//! bus/branch indices at runtime by downstream crates (e.g., surge-se).
6
7use serde::{Deserialize, Serialize};
8
9/// CIM measurement type classification.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
11pub enum CimMeasurementType {
12    /// Active power (MW) — injection or flow.
13    #[default]
14    ActivePower,
15    /// Reactive power (MVAr) — injection or flow.
16    ReactivePower,
17    /// Voltage magnitude (kV or pu).
18    VoltageMagnitude,
19    /// Voltage angle (degrees or radians).
20    VoltageAngle,
21    /// Current magnitude (A or pu).
22    CurrentMagnitude,
23    /// Frequency (Hz).
24    Frequency,
25    /// Tap position (discrete integer).
26    TapPosition,
27    /// Switch/breaker status (open/closed).
28    SwitchStatus,
29    /// Energy accumulator (MWh).
30    EnergyAccumulator,
31    /// PMU voltage phasor real part.
32    PmuVoltageReal,
33    /// PMU voltage phasor imaginary part.
34    PmuVoltageImaginary,
35    /// PMU current phasor real part.
36    PmuCurrentReal,
37    /// PMU current phasor imaginary part.
38    PmuCurrentImaginary,
39}
40
41/// Measurement value source type.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
43pub enum MeasurementSource {
44    #[default]
45    Scada,
46    Pmu,
47    Manual,
48    Calculated,
49    Other,
50}
51
52/// Quality of a measurement value.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
54pub enum MeasurementQuality {
55    #[default]
56    Good,
57    Suspect,
58    Bad,
59    Missing,
60}
61
62/// A CIM-aligned measurement definition from the CGMES Measurement profile.
63///
64/// Links to network elements via bus number and optional branch circuit ID,
65/// allowing resolution to internal 0-based indices at runtime.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct CimMeasurement {
68    /// CIM mRID of this measurement.
69    pub mrid: String,
70    /// Human-readable name.
71    pub name: String,
72    /// What physical quantity this measures.
73    #[serde(alias = "meas_type")]
74    pub measurement_type: CimMeasurementType,
75    /// Bus number where this measurement is located.
76    pub bus: u32,
77    /// For flow/current measurements: the branch circuit identifier.
78    pub branch_circuit: Option<String>,
79    /// For flow measurements: which end of the branch (true = from-end).
80    pub from_end: bool,
81    /// Measured value (in engineering units: MW, MVAr, kV, A, Hz, etc.).
82    pub value: f64,
83    /// Standard deviation of measurement noise (same units as value).
84    pub sigma: f64,
85    /// Whether this measurement is enabled/active.
86    pub enabled: bool,
87    /// Data source.
88    pub source: MeasurementSource,
89    /// Data quality.
90    pub quality: MeasurementQuality,
91    /// CIM Terminal mRID (for traceability).
92    pub terminal_mrid: Option<String>,
93}
94
95impl Default for CimMeasurement {
96    fn default() -> Self {
97        Self {
98            mrid: String::new(),
99            name: String::new(),
100            measurement_type: CimMeasurementType::default(),
101            bus: 0,
102            branch_circuit: None,
103            from_end: true,
104            value: 0.0,
105            sigma: 0.02,
106            enabled: true,
107            source: MeasurementSource::default(),
108            quality: MeasurementQuality::default(),
109            terminal_mrid: None,
110        }
111    }
112}