Skip to main content

powerio_dist/pmd/
mod.rs

1//! The PowerModelsDistribution ENGINEERING model as JSON ("PMD JSON").
2//!
3//! The byte conventions follow PMD's own `print_file`/`parse_file` pair:
4//! matrices as arrays of arrays read back via `hcat` (inner arrays are
5//! columns), `Inf`/`NaN` as `null` restored by field suffix (`_ub`/`max`
6//! to +Inf, `_lb`/`min` to -Inf, anything else NaN), enums as uppercase
7//! strings, kV and kW scales with angles in degrees, meters for lengths,
8//! per unit transformer impedances, and integer terminals with grounding
9//! as `grounded` plus `rg`/`xg` on the bus.
10//!
11//! A multinetwork document (PMD's `make_multinetwork` output, with
12//! `"multinetwork": true` and one network per time step under `nw`) is a
13//! time series rather than one network: [`parse_multinetwork`] reads it as a
14//! [`TimeSeries`] of networks, and [`crate::parse`] refuses it with a message
15//! that says so.
16
17pub mod math;
18pub(crate) mod read;
19mod write;
20
21pub use math::PmdMathEmitOptions;
22pub(crate) use write::emit_pmd_json_text;
23
24use powerio_core::{PioModule, Source, TimeSeries};
25
26use crate::MulticonductorNetwork;
27
28/// Whether the source's primary document is a PMD multinetwork ENGINEERING
29/// model (`"multinetwork": true`). The check reads the one flag without
30/// building the document tree. A source that cannot be read, or is not a
31/// JSON object, is not one.
32#[must_use]
33pub fn is_multinetwork(source: &Source) -> bool {
34    crate::convert::primary(source).is_ok_and(|buffer| {
35        crate::convert::source_text(&buffer).is_ok_and(read::text_is_multinetwork)
36    })
37}
38
39/// Parse a PMD multinetwork ENGINEERING document into one
40/// [`MulticonductorNetwork`] per time step. Time points follow the document's
41/// `mn_lookup` times, are labeled by their `nw` keys, and carry each step's
42/// `time_elapsed` as their duration. The source is retained on the module,
43/// so a same format emission echoes the input bytes.
44///
45/// # Errors
46/// The document is not JSON, not the ENGINEERING model, not a multinetwork
47/// document, or holds no time step. The failure carries the reader's
48/// findings and retains the source.
49pub fn parse_multinetwork(
50    source: Source,
51) -> Result<PioModule<TimeSeries<MulticonductorNetwork>>, powerio_core::Error> {
52    let mut warnings = crate::collect::Diagnostics::new();
53    let parsed = crate::convert::primary(&source).and_then(|buffer| {
54        read::parse_pmd_multinetwork_collecting(
55            crate::convert::source_text(&buffer)?,
56            &mut warnings,
57        )
58    });
59    match parsed {
60        Ok(series) => {
61            let source = match powerio_core::FormatId::new("pmd-json") {
62                Ok(format) => source.with_format(format),
63                Err(_) => source,
64            };
65            PioModule::parsed(series, source, warnings.into_records())
66        }
67        Err(error) => {
68            let core = powerio_core::Error::new(error.code(), error.to_string());
69            Err(core
70                .with_diagnostics(warnings.into_records())
71                .with_cause(error)
72                .with_source(source))
73        }
74    }
75}