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}