Skip to main content

surge_network/network/
asset.rs

1// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0
2//! Physical asset and wire information types from CIM IEC 61968-11 Asset package.
3//!
4//! These types store conductor properties, cable construction details, tower
5//! geometry, transformer nameplate data, and general asset metadata parsed from
6//! CGMES WireInfo / CableInfo / WireSpacingInfo / TransformerTankInfo classes.
7//!
8//! Future use: Carson's equation / Ametani impedance calculation from wire geometry.
9
10use std::collections::HashMap;
11
12use chrono::{DateTime, Utc};
13use serde::{Deserialize, Serialize};
14
15/// Physical conductor properties from CIM WireInfo / OverheadWireInfo.
16#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17pub struct WireProperties {
18    /// Human-readable name (from IdentifiedObject.name).
19    pub name: String,
20    /// AC resistance at 75 deg C (Ohm/km).
21    pub r_ac75_ohm_per_km: Option<f64>,
22    /// DC resistance at 20 deg C (Ohm/km).
23    pub r_dc20_ohm_per_km: Option<f64>,
24    /// Geometric Mean Radius (m).
25    pub gmr_m: Option<f64>,
26    /// Conductor radius (m).
27    pub radius_m: Option<f64>,
28    /// Size designation (e.g., "795 kcmil", "Drake").
29    pub size_description: Option<String>,
30    /// WireMaterialKind: copper, aluminum, steel, acsr, etc.
31    pub material: Option<String>,
32    /// Number of strands.
33    pub strand_count: Option<u32>,
34    /// Number of steel core strands.
35    pub core_strand_count: Option<u32>,
36    /// Ampacity (A).
37    pub rated_current_a: Option<f64>,
38}
39
40/// Cable-specific properties from CIM CableInfo / ConcentricNeutralCableInfo / TapeShieldCableInfo.
41#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct CableProperties {
43    /// Base wire properties inherited from WireInfo.
44    pub wire: WireProperties,
45    /// Rated temperature (deg C).
46    pub nominal_temperature_c: Option<f64>,
47    /// Insulation material (CableConstructionKind).
48    pub insulation_material: Option<String>,
49    /// Insulation thickness (mm).
50    pub insulation_thickness_mm: Option<f64>,
51    /// Outer jacket thickness (mm).
52    pub outer_jacket_thickness_mm: Option<f64>,
53    /// Shield material (CableShieldMaterialKind).
54    pub shield_material: Option<String>,
55    /// Diameter over insulation (mm).
56    pub diameter_over_insulation_mm: Option<f64>,
57    /// Diameter over jacket (mm).
58    pub diameter_over_jacket_mm: Option<f64>,
59    /// Diameter over screen (mm).
60    pub diameter_over_screen_mm: Option<f64>,
61    /// Whether strand fill is used.
62    pub is_strand_fill: Option<bool>,
63    // -- Concentric neutral cable fields --
64    /// Number of neutral strands (concentric neutral cable).
65    pub neutral_strand_count: Option<u32>,
66    /// GMR of neutral strand in meters (concentric neutral cable).
67    pub neutral_strand_gmr_m: Option<f64>,
68    /// Radius of neutral strand in meters (concentric neutral cable).
69    pub neutral_strand_radius_m: Option<f64>,
70    /// DC resistance of neutral at 20 deg C in Ohm/km (concentric neutral cable).
71    pub neutral_strand_rdc20_ohm_per_km: Option<f64>,
72    // -- Tape shield cable fields --
73    /// Tape thickness in mm (tape shield cable).
74    pub tape_thickness_mm: Option<f64>,
75    /// Tape lap percent overlap (tape shield cable).
76    pub tape_lap_percent: Option<f64>,
77}
78
79/// A single conductor position within a tower/spacing configuration.
80#[derive(Debug, Clone, Default, Serialize, Deserialize)]
81pub struct WirePosition {
82    /// Horizontal position from tower center (m).
83    pub x_m: f64,
84    /// Height above ground (m).
85    pub y_m: f64,
86    /// Phase assignment (A, B, C, N, s1, s2).
87    pub phase: Option<String>,
88    /// Ordering within the spacing.
89    pub sequence_number: u32,
90}
91
92/// Tower/spacing geometry for overhead or underground lines from CIM WireSpacingInfo.
93#[derive(Debug, Clone, Default, Serialize, Deserialize)]
94pub struct WireSpacing {
95    /// Human-readable name.
96    pub name: String,
97    /// Whether this is an underground cable spacing (vs overhead).
98    pub is_cable: bool,
99    /// Number of conductors per phase (bundle count).
100    pub phase_wire_count: u32,
101    /// Bundle spacing in meters.
102    pub phase_wire_spacing_m: Option<f64>,
103    /// Conductor positions (phase and neutral).
104    pub positions: Vec<WirePosition>,
105}
106
107/// Transformer nameplate data from CIM TransformerTankInfo / PowerTransformerInfo.
108#[derive(Debug, Clone, Default, Serialize, Deserialize)]
109pub struct TransformerInfoData {
110    /// Human-readable name.
111    pub name: String,
112    /// Winding information (one per TransformerEndInfo).
113    pub windings: Vec<TransformerWindingInfo>,
114    /// No-load (iron) loss in watts.
115    pub no_load_loss_w: Option<f64>,
116    /// Exciting current as percentage of rated current.
117    pub exciting_current_pct: Option<f64>,
118    /// Short-circuit (copper) loss in watts.
119    pub short_circuit_loss_w: Option<f64>,
120    /// Leakage impedance as percentage.
121    pub leakage_impedance_pct: Option<f64>,
122}
123
124/// Per-winding data from CIM TransformerEndInfo.
125#[derive(Debug, Clone, Default, Serialize, Deserialize)]
126pub struct TransformerWindingInfo {
127    /// Winding number (1, 2, 3).
128    pub end_number: u32,
129    /// Nameplate MVA.
130    pub rated_s_mva: Option<f64>,
131    /// Rated voltage (kV).
132    pub rated_u_kv: Option<f64>,
133    /// Winding resistance (Ohm).
134    pub r_ohm: Option<f64>,
135    /// Connection kind (Y, D, Yn, etc.).
136    pub connection_kind: Option<String>,
137    /// Insulation voltage (kV).
138    pub insulation_u_kv: Option<f64>,
139    /// Short-term emergency rating (MVA).
140    pub short_term_s_mva: Option<f64>,
141}
142
143/// General asset metadata from CIM Asset / ProductAssetModel.
144#[derive(Debug, Clone, Default, Serialize, Deserialize)]
145pub struct AssetMetadata {
146    /// Equipment mRID this asset record is associated with.
147    pub equipment_mrid: String,
148    /// Serial number.
149    pub serial_number: Option<String>,
150    /// Manufacturer name (from ProductAssetModel).
151    pub manufacturer: Option<String>,
152    /// Model number.
153    pub model_number: Option<String>,
154    /// Date of manufacture.
155    pub manufactured_date: Option<DateTime<Utc>>,
156    /// Installation date.
157    pub installation_date: Option<DateTime<Utc>>,
158    /// Retirement date.
159    pub retired_date: Option<DateTime<Utc>>,
160}
161
162/// Complete asset information container for the network.
163///
164/// Populated by the CGMES parser when Asset/WireInfo profile data is present.
165/// Keyed by CIM mRID for cross-reference with equipment objects.
166#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct AssetCatalog {
168    /// Wire/conductor definitions keyed by CIM mRID.
169    pub wire_infos: HashMap<String, WireProperties>,
170    /// Cable definitions keyed by CIM mRID.
171    pub cable_infos: HashMap<String, CableProperties>,
172    /// Wire spacing/tower geometry keyed by CIM mRID.
173    pub wire_spacings: HashMap<String, WireSpacing>,
174    /// Transformer info keyed by CIM mRID.
175    pub transformer_infos: HashMap<String, TransformerInfoData>,
176    /// Asset metadata keyed by equipment mRID.
177    pub asset_metadata: HashMap<String, AssetMetadata>,
178}
179
180impl AssetCatalog {
181    /// Returns true if no asset data has been populated.
182    pub fn is_empty(&self) -> bool {
183        self.wire_infos.is_empty()
184            && self.cable_infos.is_empty()
185            && self.wire_spacings.is_empty()
186            && self.transformer_infos.is_empty()
187            && self.asset_metadata.is_empty()
188    }
189}