Skip to main content

powerio_dist/dss/
prop.rs

1//! OpenDSS class and property name tables, in definition order.
2//!
3//! Names and order come from each class's DefineProperties in the OpenDSS
4//! source (epri-dev/OpenDSS-C): the order fixes both positional property
5//! assignment and abbreviation resolution. Lookup is case insensitive, exact
6//! match first, then the first name in definition order with the query as a
7//! prefix (THashList::FindAbbrev). Every class list ends with the inherited
8//! properties: PD elements add normamps..repair, PC elements add spectrum,
9//! and every circuit element adds basefreq, enabled, like.
10
11/// One OpenDSS object class: canonical name plus ordered property names.
12pub struct DssClass {
13    pub name: &'static str,
14    pub props: &'static [&'static str],
15}
16
17macro_rules! class {
18    ($ident:ident, $name:literal, [$($p:literal),* $(,)?]) => {
19        pub static $ident: DssClass = DssClass { name: $name, props: &[$($p),*] };
20    };
21}
22
23class!(
24    LINE,
25    "line",
26    [
27        "bus1",
28        "bus2",
29        "linecode",
30        "length",
31        "phases",
32        "r1",
33        "x1",
34        "r0",
35        "x0",
36        "c1",
37        "c0",
38        "rmatrix",
39        "xmatrix",
40        "cmatrix",
41        "switch",
42        "rg",
43        "xg",
44        "rho",
45        "geometry",
46        "units",
47        "spacing",
48        "wires",
49        "earthmodel",
50        "cncables",
51        "tscables",
52        "b1",
53        "b0",
54        "seasons",
55        "ratings",
56        "linetype",
57        // inherited
58        "normamps",
59        "emergamps",
60        "faultrate",
61        "pctperm",
62        "repair",
63        "basefreq",
64        "enabled",
65        "like",
66    ]
67);
68
69class!(
70    LINECODE,
71    "linecode",
72    [
73        "nphases",
74        "r1",
75        "x1",
76        "r0",
77        "x0",
78        "c1",
79        "c0",
80        "units",
81        "rmatrix",
82        "xmatrix",
83        "cmatrix",
84        "basefreq",
85        "normamps",
86        "emergamps",
87        "faultrate",
88        "pctperm",
89        "repair",
90        "kron",
91        "rg",
92        "xg",
93        "rho",
94        "neutral",
95        "b1",
96        "b0",
97        "seasons",
98        "ratings",
99        "linetype",
100        // inherited
101        "like",
102    ]
103);
104
105class!(
106    LOAD,
107    "load",
108    [
109        "phases",
110        "bus1",
111        "kv",
112        "kw",
113        "pf",
114        "model",
115        "yearly",
116        "daily",
117        "duty",
118        "growth",
119        "conn",
120        "kvar",
121        "rneut",
122        "xneut",
123        "status",
124        "class",
125        "vminpu",
126        "vmaxpu",
127        "vminnorm",
128        "vminemerg",
129        "xfkva",
130        "allocationfactor",
131        "kva",
132        "%mean",
133        "%stddev",
134        "cvrwatts",
135        "cvrvars",
136        "kwh",
137        "kwhdays",
138        "cfactor",
139        "cvrcurve",
140        "numcust",
141        "zipv",
142        "%seriesrl",
143        "relweight",
144        "vlowpu",
145        "puxharm",
146        "xrharm",
147        // inherited
148        "spectrum",
149        "basefreq",
150        "enabled",
151        "like",
152    ]
153);
154
155class!(
156    TRANSFORMER,
157    "transformer",
158    [
159        "phases",
160        "windings",
161        "wdg",
162        "bus",
163        "conn",
164        "kv",
165        "kva",
166        "tap",
167        "%r",
168        "rneut",
169        "xneut",
170        "buses",
171        "conns",
172        "kvs",
173        "kvas",
174        "taps",
175        "xhl",
176        "xht",
177        "xlt",
178        "xscarray",
179        "thermal",
180        "n",
181        "m",
182        "flrise",
183        "hsrise",
184        "%loadloss",
185        "%noloadloss",
186        "normhkva",
187        "emerghkva",
188        "sub",
189        "maxtap",
190        "mintap",
191        "numtaps",
192        "subname",
193        "%imag",
194        "ppm_antifloat",
195        "%rs",
196        "bank",
197        "xfmrcode",
198        "xrconst",
199        "x12",
200        "x13",
201        "x23",
202        "leadlag",
203        "wdgcurrents",
204        "core",
205        "rdcohms",
206        "seasons",
207        "ratings",
208        // inherited
209        "normamps",
210        "emergamps",
211        "faultrate",
212        "pctperm",
213        "repair",
214        "basefreq",
215        "enabled",
216        "like",
217    ]
218);
219
220class!(
221    VSOURCE,
222    "vsource",
223    [
224        "bus1",
225        "basekv",
226        "pu",
227        "angle",
228        "frequency",
229        "phases",
230        "mvasc3",
231        "mvasc1",
232        "x1r1",
233        "x0r0",
234        "isc3",
235        "isc1",
236        "r1",
237        "x1",
238        "r0",
239        "x0",
240        "scantype",
241        "sequence",
242        "bus2",
243        "z1",
244        "z0",
245        "z2",
246        "puz1",
247        "puz0",
248        "puz2",
249        "basemva",
250        "yearly",
251        "daily",
252        "duty",
253        "model",
254        "puzideal",
255        // inherited
256        "spectrum",
257        "basefreq",
258        "enabled",
259        "like",
260    ]
261);
262
263class!(
264    CAPACITOR,
265    "capacitor",
266    [
267        "bus1",
268        "bus2",
269        "phases",
270        "kvar",
271        "kv",
272        "conn",
273        "cmatrix",
274        "cuf",
275        "r",
276        "xl",
277        "harm",
278        "numsteps",
279        "states",
280        // inherited
281        "normamps",
282        "emergamps",
283        "faultrate",
284        "pctperm",
285        "repair",
286        "basefreq",
287        "enabled",
288        "like",
289    ]
290);
291
292class!(
293    REACTOR,
294    "reactor",
295    [
296        "bus1",
297        "bus2",
298        "phases",
299        "kvar",
300        "kv",
301        "conn",
302        "rmatrix",
303        "xmatrix",
304        "parallel",
305        "r",
306        "x",
307        "rp",
308        "z1",
309        "z2",
310        "z0",
311        "z",
312        "rcurve",
313        "lcurve",
314        "lmh",
315        // inherited
316        "normamps",
317        "emergamps",
318        "faultrate",
319        "pctperm",
320        "repair",
321        "basefreq",
322        "enabled",
323        "like",
324    ]
325);
326
327class!(
328    GENERATOR,
329    "generator",
330    [
331        "phases",
332        "bus1",
333        "kv",
334        "kw",
335        "pf",
336        "kvar",
337        "model",
338        "vminpu",
339        "vmaxpu",
340        "yearly",
341        "daily",
342        "duty",
343        "dispmode",
344        "dispvalue",
345        "conn",
346        "rneut",
347        "xneut",
348        "status",
349        "class",
350        "vpu",
351        "maxkvar",
352        "minkvar",
353        "pvfactor",
354        "forceon",
355        "kva",
356        "mva",
357        "xd",
358        "xdp",
359        "xdpp",
360        "h",
361        "d",
362        "usermodel",
363        "userdata",
364        "shaftmodel",
365        "shaftdata",
366        "dutystart",
367        "debugtrace",
368        "balanced",
369        "xrdp",
370        "usefuel",
371        "fuelkwh",
372        "%fuel",
373        "%reserve",
374        "refuel",
375        "dynamiceq",
376        "dynout",
377        // inherited
378        "spectrum",
379        "basefreq",
380        "enabled",
381        "like",
382    ]
383);
384
385class!(
386    SWTCONTROL,
387    "swtcontrol",
388    [
389        "switchedobj",
390        "switchedterm",
391        "action",
392        "lock",
393        "delay",
394        "normal",
395        "state",
396        "reset",
397        // inherited
398        "basefreq",
399        "enabled",
400        "like",
401    ]
402);
403
404class!(
405    REGCONTROL,
406    "regcontrol",
407    [
408        "transformer",
409        "winding",
410        "vreg",
411        "band",
412        "ptratio",
413        "ctprim",
414        "r",
415        "x",
416        "bus",
417        "delay",
418        "reversible",
419        "revvreg",
420        "revband",
421        "revr",
422        "revx",
423        "tapdelay",
424        "debugtrace",
425        "maxtapchange",
426        "inversetime",
427        "tapwinding",
428        "vlimit",
429        "ptphase",
430        "revthreshold",
431        "revdelay",
432        "revneutral",
433        "eventlog",
434        "remoteptratio",
435        "tapnum",
436        "reset",
437        "ldc_z",
438        "rev_z",
439        "cogen",
440        // inherited
441        "basefreq",
442        "enabled",
443        "like",
444    ]
445);
446
447/// The Phase A classes with property tables. Anything else parses into the
448/// raw layer untyped.
449static CLASSES: &[&DssClass] = &[
450    &LINE,
451    &LINECODE,
452    &LOAD,
453    &TRANSFORMER,
454    &VSOURCE,
455    &CAPACITOR,
456    &REACTOR,
457    &GENERATOR,
458    &SWTCONTROL,
459    &REGCONTROL,
460];
461
462/// Case insensitive exact class name lookup (`circuit` is handled by the
463/// command layer, not here).
464pub fn class_by_name(name: &str) -> Option<&'static DssClass> {
465    CLASSES
466        .iter()
467        .find(|c| c.name.eq_ignore_ascii_case(name))
468        .copied()
469}
470
471impl DssClass {
472    /// Property lookup: exact case insensitive match first, then the first
473    /// property in definition order that starts with the query.
474    pub fn prop_index(&self, query: &str) -> Option<usize> {
475        let q = query.to_ascii_lowercase();
476        self.props
477            .iter()
478            .position(|p| *p == q)
479            .or_else(|| self.props.iter().position(|p| p.starts_with(&q)))
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486
487    #[test]
488    fn exact_beats_prefix() {
489        // "r1" is exact even though "r0" or "rmatrix" share the prefix.
490        assert_eq!(LINE.prop_index("r1"), Some(5));
491        assert_eq!(LINE.prop_index("R1"), Some(5));
492    }
493
494    #[test]
495    fn first_prefix_match_in_definition_order() {
496        // "r" has no exact match; the first r* property in order is r1.
497        assert_eq!(LINE.prop_index("r"), Some(5));
498        // "rm" picks rmatrix, not rg or rho.
499        assert_eq!(LINE.prop_index("rm"), Some(11));
500        // "norm" picks normamps from the inherited tail.
501        assert_eq!(LINE.prop_index("norm"), Some(30));
502    }
503
504    #[test]
505    fn percent_properties() {
506        assert_eq!(TRANSFORMER.prop_index("%R"), Some(8));
507        assert_eq!(TRANSFORMER.prop_index("%Rs"), Some(36));
508        assert_eq!(TRANSFORMER.prop_index("%loadloss"), Some(25));
509    }
510
511    #[test]
512    fn load_positions_match_the_engine() {
513        // Load.cpp DefineProperties: RelWeight 35, Vlowpu 36, puXharm 37,
514        // XRharm 38 (1-based); a missing slot would shift every later
515        // positional assignment.
516        assert_eq!(LOAD.prop_index("relweight"), Some(34));
517        assert_eq!(LOAD.prop_index("vlowpu"), Some(35));
518        assert_eq!(LOAD.prop_index("puxharm"), Some(36));
519        assert_eq!(LOAD.prop_index("xrharm"), Some(37));
520        assert_eq!(LOAD.props.len(), 38 + 4); // 38 own + spectrum, basefreq, enabled, like
521    }
522
523    #[test]
524    fn class_lookup() {
525        assert!(class_by_name("Line").is_some());
526        assert!(class_by_name("LINECODE").is_some());
527        assert!(class_by_name("reactor").is_some());
528        assert!(class_by_name("xfmrcode").is_none());
529    }
530
531    #[test]
532    fn reactor_positions_match_the_engine() {
533        // Reactor.pas DefineProperties: 19 own properties (bus1..lmh) plus the
534        // PD-element inherited tail. A missing slot would shift positional
535        // assignment of every later property.
536        assert_eq!(REACTOR.prop_index("bus1"), Some(0));
537        assert_eq!(REACTOR.prop_index("kvar"), Some(3));
538        assert_eq!(REACTOR.prop_index("lmh"), Some(18));
539        // "normamps" opens the inherited PD tail right after lmh.
540        assert_eq!(REACTOR.prop_index("normamps"), Some(19));
541        assert_eq!(REACTOR.props.len(), 19 + 8);
542    }
543
544    #[test]
545    fn unknown_property() {
546        assert_eq!(LINE.prop_index("zzz"), None);
547    }
548}