Skip to main content

powerio_dist/bmopf/
mod.rs

1//! The BMOPF task force JSON schema
2//! (<https://github.com/distribution-system-opt/dsopt-schema>).
3//!
4//! Everything is explicit SI: volts, watts, vars, ohms, siemens, meters,
5//! radians, string bus ids and terminal names. Both schema versions set
6//! `additionalProperties: false` on electrical elements and permit free-form
7//! top-level `extras` and `meta.provenance` objects, so the strict emitter drops what the target
8//! version cannot carry and says so per field.
9//!
10//! # Two schema versions
11//!
12//! [`BmopfSchemaVersion`] names the version. Schema 0.1.0 is what the task force
13//! accepts: ten element classes and four transformer subtypes. Schema 0.2.0
14//! is the proposal that declares the classes 0.1.0 has no table for, and
15//! gives the transformer taps, winding neutral impedance, and no load
16//! admittance their own subtype slots.
17//!
18//! The reader accepts both, whatever a document states, because every class
19//! 0.2.0 declares at the top level also reads from the `extras` slot 0.1.0
20//! files it under. It resolves the version from `meta.$schema` and reports
21//! `READ.BMOPF.SCHEMA_ABSENT` when the document states none and
22//! `READ.BMOPF.SCHEMA_UNKNOWN` when the value names no version: the class
23//! layout of such a document is stated nowhere, and a consumer that assumes
24//! one reads a different network on the other.
25//!
26//! The writer targets one version, 0.2.0 by default. Writing 0.2.0 keeps supported proposal fields in place. Writing 0.1.0 relocates what that version has no slot for, listed
27//! below, and reports each move.
28//!
29//! # What a 0.1.0 write parks under `extras`
30//!
31//! Some data has a physical meaning schema 0.1.0 cannot express in place but
32//! can carry in the free form `extras` object, and that data is relocated
33//! rather than dropped, with a warning per element. A consumer that reads the
34//! document and ignores `extras` gets a network that differs physically from
35//! the source and no error saying so: a tapped transformer reads as nominal
36//! tap, an impedance grounded neutral loses its internal grounding branch, and the
37//! magnetizing branch disappears.
38//!
39//! ## Transformer fields
40//!
41//! Schema 0.1.0 has no slot for these nine fields in the `single_phase`,
42//! `center_tap`, `wye_delta`, and `delta_wye` subtype definitions, so a
43//! transformer carrying any of them emits them under
44//! `extras.transformer.<subtype>.<name>`, keyed by the same transformer name
45//! the subtype table uses:
46//!
47//! `tap`, `tap_min`, `tap_max`, `r_neutral_from`, `x_neutral_from`,
48//! `r_neutral_to`, `x_neutral_to`, `g_no_load`, `b_no_load`.
49//!
50//! Schema 0.2.0 declares all nine on those subtypes, so they stay in place
51//! and only the three tap names change, to `tap_ratio`, `tap_ratio_min`, and
52//! `tap_ratio_max`, the spelling its regulator subtypes use.
53//!
54//! Neither version defines untyped `transformer.<subtype>` passthrough
55//! objects, so those keep every field in place and nothing moves. The BMOPF
56//! reader folds a 0.1.0 overlay back onto the subtype objects; on a key
57//! collision the field already in the subtype object wins.
58//!
59//! ## Whole tables
60//!
61//! Schema 0.1.0 has no top level slot for these classes, so under that
62//! version each emits at `extras.<class>.<name>` with the keying its top
63//! level table used:
64//!
65//! - `ibr` and `control_profile`, emitted from the typed model
66//! - `dc_bus`, `dc_branch`, `dc_grounding`, `dc_load`, `dc_source`,
67//!   `time_series`, emitted from untyped objects of that class
68//! - `capacitor`, only for a capacitor too malformed to type; a typed
69//!   capacitor goes to the strict top level `capacitor` table
70//!
71//! Schema 0.2.0 declares every one of them but `capacitor`, whose top level
72//! table stays strict there too, so a malformed capacitor keeps its raw
73//! properties under `extras` under either version.
74//!
75//! A source document's own `extras` object is stashed on read and re-emitted
76//! verbatim, so consumer keys beside these survive a write and read back.
77//!
78//! # Regulator subtypes
79//!
80//! Schema 0.1.0 defines no regulator subtype; the task force's BMOPFTools
81//! toolchain extends it with `single_phase_autotransformer` and
82//! `open_delta_regulator`, and schema 0.2.0 declares both. This emitter
83//! writes them at top level under 0.2.0 and in `extras.transformer` under 0.1.0.
84//! An OpenDSS transformer a RegControl targets emits as
85//! `transformer.single_phase_autotransformer` when it reads as a series
86//! regulator (one phase, two windings of equal connection, voltage, and
87//! rating on distinct buses), and two identical line to line legs spelling
88//! one open delta connection (ABBC/BCAC/CABA) merge into one
89//! `transformer.open_delta_regulator` object named after the first leg. A
90//! BMOPF document that already carries either subtype re-emits it verbatim.
91//!
92//! # `terminal_conventions` goes stale on a rename
93//!
94//! The emitted `terminal_conventions` block is the source document's own
95//! block re-emitted verbatim, or, absent that, one authored from the terminal
96//! names in the model: `n` and `N` are neutral, and `4` in the complete
97//! `1,2,3,4` convention is neutral; other names are phase labels. Either way the block describes the terminal naming
98//! of the document that carries it and nothing else. A consumer that renames
99//! terminals must delete the block and recompute it from the renamed
100//! terminals; carried across a rename it sorts names no bus has any more, and
101//! nothing else in the document contradicts it.
102
103mod geo;
104mod profile;
105pub(crate) mod read;
106mod write;
107
108pub use profile::{
109    BMOPF_PROPOSAL_COMMIT, BMOPF_PROPOSAL_SHA256, BMOPF_PROPOSAL_URL, BmopfSchemaVersion,
110};
111pub(crate) use write::emit_bmopf_json_text_with_options;
112pub use write::{BMOPF_SCHEMA_ID, BMOPF_SCHEMA_VERSION, BmopfEmitOptions};
113
114mod validate;