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
105// DSS C-API 87d85c2622c8281b92255335bc7c09b11191b21d,
106// General/LoadShape.pas: TLoadShapePropLegacy plus inherited Like.
107// This table also enables bounded BatchEdit through the ordinary assignment path.
108class!(
109    LOADSHAPE,
110    "loadshape",
111    [
112        "npts",
113        "interval",
114        "mult",
115        "hour",
116        "mean",
117        "stddev",
118        "csvfile",
119        "sngfile",
120        "dblfile",
121        "action",
122        "qmult",
123        "useactual",
124        "pmax",
125        "qmax",
126        "sinterval",
127        "minterval",
128        "pbase",
129        "qbase",
130        "pmult",
131        "pqcsvfile",
132        "memorymapping",
133        "interpolation",
134        "like",
135    ]
136);
137
138class!(
139    LOAD,
140    "load",
141    [
142        "phases",
143        "bus1",
144        "kv",
145        "kw",
146        "pf",
147        "model",
148        "yearly",
149        "daily",
150        "duty",
151        "growth",
152        "conn",
153        "kvar",
154        "rneut",
155        "xneut",
156        "status",
157        "class",
158        "vminpu",
159        "vmaxpu",
160        "vminnorm",
161        "vminemerg",
162        "xfkva",
163        "allocationfactor",
164        "kva",
165        "%mean",
166        "%stddev",
167        "cvrwatts",
168        "cvrvars",
169        "kwh",
170        "kwhdays",
171        "cfactor",
172        "cvrcurve",
173        "numcust",
174        "zipv",
175        "%seriesrl",
176        "relweight",
177        "vlowpu",
178        "puxharm",
179        "xrharm",
180        // inherited
181        "spectrum",
182        "basefreq",
183        "enabled",
184        "like",
185    ]
186);
187
188class!(
189    TRANSFORMER,
190    "transformer",
191    [
192        "phases",
193        "windings",
194        "wdg",
195        "bus",
196        "conn",
197        "kv",
198        "kva",
199        "tap",
200        "%r",
201        "rneut",
202        "xneut",
203        "buses",
204        "conns",
205        "kvs",
206        "kvas",
207        "taps",
208        "xhl",
209        "xht",
210        "xlt",
211        "xscarray",
212        "thermal",
213        "n",
214        "m",
215        "flrise",
216        "hsrise",
217        "%loadloss",
218        "%noloadloss",
219        "normhkva",
220        "emerghkva",
221        "sub",
222        "maxtap",
223        "mintap",
224        "numtaps",
225        "subname",
226        "%imag",
227        "ppm_antifloat",
228        "%rs",
229        "bank",
230        "xfmrcode",
231        "xrconst",
232        "x12",
233        "x13",
234        "x23",
235        "leadlag",
236        "wdgcurrents",
237        "core",
238        "rdcohms",
239        "seasons",
240        "ratings",
241        // inherited
242        "normamps",
243        "emergamps",
244        "faultrate",
245        "pctperm",
246        "repair",
247        "basefreq",
248        "enabled",
249        "like",
250    ]
251);
252
253// Pinned DSS C-API XfmrCode.pas; codes have no bus or circuit-element fields.
254class!(
255    XFMR_CODE,
256    "xfmrcode",
257    [
258        "phases",
259        "windings",
260        "wdg",
261        "conn",
262        "kv",
263        "kva",
264        "tap",
265        "%r",
266        "rneut",
267        "xneut",
268        "conns",
269        "kvs",
270        "kvas",
271        "taps",
272        "xhl",
273        "xht",
274        "xlt",
275        "xscarray",
276        "thermal",
277        "n",
278        "m",
279        "flrise",
280        "hsrise",
281        "%loadloss",
282        "%noloadloss",
283        "normhkva",
284        "emerghkva",
285        "maxtap",
286        "mintap",
287        "numtaps",
288        "%imag",
289        "ppm_antifloat",
290        "%rs",
291        "x12",
292        "x13",
293        "x23",
294        "rdcohms",
295        "seasons",
296        "ratings",
297        "like",
298    ]
299);
300
301class!(
302    VSOURCE,
303    "vsource",
304    [
305        "bus1",
306        "basekv",
307        "pu",
308        "angle",
309        "frequency",
310        "phases",
311        "mvasc3",
312        "mvasc1",
313        "x1r1",
314        "x0r0",
315        "isc3",
316        "isc1",
317        "r1",
318        "x1",
319        "r0",
320        "x0",
321        "scantype",
322        "sequence",
323        "bus2",
324        "z1",
325        "z0",
326        "z2",
327        "puz1",
328        "puz0",
329        "puz2",
330        "basemva",
331        "yearly",
332        "daily",
333        "duty",
334        "model",
335        "puzideal",
336        // inherited
337        "spectrum",
338        "basefreq",
339        "enabled",
340        "like",
341    ]
342);
343
344class!(
345    CAPACITOR,
346    "capacitor",
347    [
348        "bus1",
349        "bus2",
350        "phases",
351        "kvar",
352        "kv",
353        "conn",
354        "cmatrix",
355        "cuf",
356        "r",
357        "xl",
358        "harm",
359        "numsteps",
360        "states",
361        // inherited
362        "normamps",
363        "emergamps",
364        "faultrate",
365        "pctperm",
366        "repair",
367        "basefreq",
368        "enabled",
369        "like",
370    ]
371);
372
373class!(
374    REACTOR,
375    "reactor",
376    [
377        "bus1",
378        "bus2",
379        "phases",
380        "kvar",
381        "kv",
382        "conn",
383        "rmatrix",
384        "xmatrix",
385        "parallel",
386        "r",
387        "x",
388        "rp",
389        "z1",
390        "z2",
391        "z0",
392        "z",
393        "rcurve",
394        "lcurve",
395        "lmh",
396        // inherited
397        "normamps",
398        "emergamps",
399        "faultrate",
400        "pctperm",
401        "repair",
402        "basefreq",
403        "enabled",
404        "like",
405    ]
406);
407
408class!(
409    GENERATOR,
410    "generator",
411    [
412        "phases",
413        "bus1",
414        "kv",
415        "kw",
416        "pf",
417        "kvar",
418        "model",
419        "vminpu",
420        "vmaxpu",
421        "yearly",
422        "daily",
423        "duty",
424        "dispmode",
425        "dispvalue",
426        "conn",
427        "rneut",
428        "xneut",
429        "status",
430        "class",
431        "vpu",
432        "maxkvar",
433        "minkvar",
434        "pvfactor",
435        "forceon",
436        "kva",
437        "mva",
438        "xd",
439        "xdp",
440        "xdpp",
441        "h",
442        "d",
443        "usermodel",
444        "userdata",
445        "shaftmodel",
446        "shaftdata",
447        "dutystart",
448        "debugtrace",
449        "balanced",
450        "xrdp",
451        "usefuel",
452        "fuelkwh",
453        "%fuel",
454        "%reserve",
455        "refuel",
456        "dynamiceq",
457        "dynout",
458        // inherited
459        "spectrum",
460        "basefreq",
461        "enabled",
462        "like",
463    ]
464);
465
466class!(
467    PVSYSTEM,
468    "pvsystem",
469    [
470        "phases",
471        "bus1",
472        "kv",
473        "irradiance",
474        "pmpp",
475        "%pmpp",
476        "temperature",
477        "pf",
478        "conn",
479        "kvar",
480        "kva",
481        "%cutin",
482        "%cutout",
483        "effcurve",
484        "ptcurve",
485        "daily",
486        "yearly",
487        "duty",
488        "tdaily",
489        "tyearly",
490        "tduty",
491        "class",
492        "user_model",
493        "user_data",
494        "debugtrace",
495        "varfollowinverter",
496        "duty_start",
497        "wattpriority",
498        "pfpriority",
499        "pctpmpp",
500        "pctcutin",
501        "pctcutout",
502        "kvarmax",
503        "kvarmaxabs",
504        "kvarlimit",
505        "kvarlimitneg",
506        "%pminnovars",
507        "%pminkvarmax",
508        "vminpu",
509        "vmaxpu",
510        // inherited
511        "spectrum",
512        "basefreq",
513        "enabled",
514        "like",
515    ]
516);
517
518class!(
519    XYCURVE,
520    "xycurve",
521    [
522        "npts", "points", "yarray", "xarray", "csvfile", "sngfile", "dblfile", "x", "y",
523        // inherited
524        "like",
525    ]
526);
527
528class!(
529    INVCONTROL,
530    "invcontrol",
531    [
532        "derlist",
533        "mode",
534        "combimode",
535        "vvc_curve1",
536        "hysteresis_offset",
537        "voltage_curvex_ref",
538        "avgwindowlen",
539        "volt_watt_curve",
540        "dbvmin",
541        "dbvmax",
542        "armingtime",
543        "deltaq_factor",
544        "voltagechange_tolerance",
545        "varchange_tolerance",
546        "voltwatt_curve",
547        "voltwattyaxis",
548        "rateofchange_mode",
549        "lpftau",
550        "riseFallLimit",
551        "deltaP_factor",
552        "eventlog",
553        "refreactivepower",
554        "activepchange_tolerance",
555        "monvoltagecalc",
556        "monbus",
557        "monbusesvbase",
558        // inherited
559        "basefreq",
560        "enabled",
561        "like",
562    ]
563);
564
565class!(
566    SWTCONTROL,
567    "swtcontrol",
568    [
569        "switchedobj",
570        "switchedterm",
571        "action",
572        "lock",
573        "delay",
574        "normal",
575        "state",
576        "reset",
577        // inherited
578        "basefreq",
579        "enabled",
580        "like",
581    ]
582);
583
584class!(
585    REGCONTROL,
586    "regcontrol",
587    [
588        "transformer",
589        "winding",
590        "vreg",
591        "band",
592        "ptratio",
593        "ctprim",
594        "r",
595        "x",
596        "bus",
597        "delay",
598        "reversible",
599        "revvreg",
600        "revband",
601        "revr",
602        "revx",
603        "tapdelay",
604        "debugtrace",
605        "maxtapchange",
606        "inversetime",
607        "tapwinding",
608        "vlimit",
609        "ptphase",
610        "revthreshold",
611        "revdelay",
612        "revneutral",
613        "eventlog",
614        "remoteptratio",
615        "tapnum",
616        "reset",
617        "ldc_z",
618        "rev_z",
619        "cogen",
620        // inherited
621        "basefreq",
622        "enabled",
623        "like",
624    ]
625);
626
627// Pinned DSS C-API ConductorData.pas (WireData adds no own fields).
628class!(
629    WIRE_DATA,
630    "wiredata",
631    [
632        "rdc",
633        "rac",
634        "runits",
635        "gmrac",
636        "gmrunits",
637        "radius",
638        "radunits",
639        "normamps",
640        "emergamps",
641        "diam",
642        "seasons",
643        "ratings",
644        "capradius",
645        "like",
646    ]
647);
648
649// CNData inherits CableData and then ConductorData in the pinned engine.
650class!(
651    CN_DATA,
652    "cndata",
653    [
654        "k",
655        "diastrand",
656        "gmrstrand",
657        "rstrand",
658        "epsr",
659        "inslayer",
660        "diains",
661        "diacable",
662        "rdc",
663        "rac",
664        "runits",
665        "gmrac",
666        "gmrunits",
667        "radius",
668        "radunits",
669        "normamps",
670        "emergamps",
671        "diam",
672        "seasons",
673        "ratings",
674        "capradius",
675        "like"
676    ]
677);
678
679class!(
680    LINE_GEOMETRY,
681    "linegeometry",
682    [
683        "nconds",
684        "nphases",
685        "cond",
686        "wire",
687        "x",
688        "h",
689        "units",
690        "normamps",
691        "emergamps",
692        "reduce",
693        "spacing",
694        "wires",
695        "cncable",
696        "tscable",
697        "cncables",
698        "tscables",
699        "seasons",
700        "ratings",
701        "linetype",
702        "like",
703    ]
704);
705
706class!(
707    LINE_SPACING,
708    "linespacing",
709    ["nconds", "nphases", "x", "h", "units", "like"]
710);
711
712/// Classes with ordered property tables; other classes remain untyped.
713static CLASSES: &[&DssClass] = &[
714    &LINE,
715    &LINECODE,
716    &WIRE_DATA,
717    &CN_DATA,
718    &LINE_GEOMETRY,
719    &LINE_SPACING,
720    &LOAD,
721    &LOADSHAPE,
722    &TRANSFORMER,
723    &XFMR_CODE,
724    &VSOURCE,
725    &CAPACITOR,
726    &REACTOR,
727    &GENERATOR,
728    &PVSYSTEM,
729    &XYCURVE,
730    &INVCONTROL,
731    &SWTCONTROL,
732    &REGCONTROL,
733];
734
735/// Case insensitive exact class name lookup (`circuit` is handled by the
736/// command layer, not here).
737pub fn class_by_name(name: &str) -> Option<&'static DssClass> {
738    CLASSES
739        .iter()
740        .find(|c| c.name.eq_ignore_ascii_case(name))
741        .copied()
742}
743
744impl DssClass {
745    /// Property lookup: exact case insensitive match first, then the first
746    /// property in definition order that starts with the query.
747    pub fn prop_index(&self, query: &str) -> Option<usize> {
748        let q = query.to_ascii_lowercase();
749        self.props
750            .iter()
751            .position(|p| *p == q)
752            .or_else(|| self.props.iter().position(|p| p.starts_with(&q)))
753    }
754}
755
756#[cfg(test)]
757mod tests {
758    use super::*;
759
760    #[test]
761    fn exact_beats_prefix() {
762        // "r1" is exact even though "r0" or "rmatrix" share the prefix.
763        assert_eq!(LINE.prop_index("r1"), Some(5));
764        assert_eq!(LINE.prop_index("R1"), Some(5));
765    }
766
767    #[test]
768    fn first_prefix_match_in_definition_order() {
769        // "r" has no exact match; the first r* property in order is r1.
770        assert_eq!(LINE.prop_index("r"), Some(5));
771        // "rm" picks rmatrix, not rg or rho.
772        assert_eq!(LINE.prop_index("rm"), Some(11));
773        // "norm" picks normamps from the inherited tail.
774        assert_eq!(LINE.prop_index("norm"), Some(30));
775    }
776
777    #[test]
778    fn percent_properties() {
779        assert_eq!(TRANSFORMER.prop_index("%R"), Some(8));
780        assert_eq!(TRANSFORMER.prop_index("%Rs"), Some(36));
781        assert_eq!(TRANSFORMER.prop_index("%loadloss"), Some(25));
782    }
783
784    #[test]
785    fn load_positions_match_the_engine() {
786        // Load.cpp DefineProperties: RelWeight 35, Vlowpu 36, puXharm 37,
787        // XRharm 38 (1-based); a missing slot would shift every later
788        // positional assignment.
789        assert_eq!(LOAD.prop_index("relweight"), Some(34));
790        assert_eq!(LOAD.prop_index("vlowpu"), Some(35));
791        assert_eq!(LOAD.prop_index("puxharm"), Some(36));
792        assert_eq!(LOAD.prop_index("xrharm"), Some(37));
793        assert_eq!(LOAD.props.len(), 38 + 4); // 38 own + spectrum, basefreq, enabled, like
794    }
795
796    #[test]
797    fn class_lookup() {
798        assert!(class_by_name("Line").is_some());
799        assert!(class_by_name("LINECODE").is_some());
800        assert!(class_by_name("reactor").is_some());
801        assert_eq!(class_by_name("xfmrcode").unwrap().props[3], "conn");
802    }
803
804    #[test]
805    fn reactor_positions_match_the_engine() {
806        // Reactor.pas DefineProperties: 19 own properties (bus1..lmh) plus the
807        // PD-element inherited tail. A missing slot would shift positional
808        // assignment of every later property.
809        assert_eq!(REACTOR.prop_index("bus1"), Some(0));
810        assert_eq!(REACTOR.prop_index("kvar"), Some(3));
811        assert_eq!(REACTOR.prop_index("lmh"), Some(18));
812        // "normamps" opens the inherited PD tail right after lmh.
813        assert_eq!(REACTOR.prop_index("normamps"), Some(19));
814        assert_eq!(REACTOR.props.len(), 19 + 8);
815    }
816
817    #[test]
818    fn unknown_property() {
819        assert_eq!(LINE.prop_index("zzz"), None);
820    }
821}