powerio.dist

Parse and convert multiconductor distribution networks.

The typed model uses wire coordinates. Supported formats are OpenDSS .dss, PowerModelsDistribution ENGINEERING JSON (pmd-json), and BMOPF JSON (bmopf-json). Same format writes can return retained source bytes. Cross format writes report unsupported fields in ~powerio.Conversion.

import powerio.dist as dist

net = dist.parse_file("feeder.dss")
for w in net.warnings:
    print("parse:", w)
conv = net.to_format("pmd-json")
  1"""Parse and convert multiconductor distribution networks.
  2
  3The typed model uses wire coordinates. Supported formats are OpenDSS ``.dss``,
  4PowerModelsDistribution ENGINEERING JSON (``pmd-json``), and BMOPF JSON
  5(``bmopf-json``). Same format writes can return retained source bytes. Cross
  6format writes report unsupported fields in :class:`~powerio.Conversion`.
  7
  8    import powerio.dist as dist
  9
 10    net = dist.parse_file("feeder.dss")
 11    for w in net.warnings:
 12        print("parse:", w)
 13    conv = net.to_format("pmd-json")
 14"""
 15
 16from __future__ import annotations
 17
 18import json as _json
 19from typing import Any, Optional
 20
 21from . import Conversion, _powerio
 22
 23__all__ = [
 24    "MulticonductorNetwork",
 25    "convert_file",
 26    "convert_str",
 27    "parse_file",
 28    "parse_str",
 29]
 30
 31
 32class MulticonductorNetwork:
 33    """A parsed multiconductor distribution network in wire coordinates.
 34
 35    Buses carry named terminals, lines carry conductor impedance matrices, and
 36    transformers carry per winding connections. This type is distinct from the
 37    positive sequence :class:`powerio.BalancedNetwork`; balanced matrix builders do not
 38    accept it.
 39    """
 40
 41    def __init__(self, inner) -> None:
 42        self._inner = inner
 43
 44    @property
 45    def name(self) -> Optional[str]:
 46        """Distribution network name when the source format carries one."""
 47        return self._inner.name()
 48
 49    @property
 50    def source_format(self) -> Optional[str]:
 51        """Format parsed from: ``dss``, ``pmd-json``, or ``bmopf-json``."""
 52        return self._inner.source_format()
 53
 54    @property
 55    def warnings(self) -> "list[str]":
 56        """Return source fields not represented and assumptions made while parsing."""
 57        return self._inner.warnings()
 58
 59    @property
 60    def n_buses(self) -> int:
 61        return self._inner.n_buses()
 62
 63    @property
 64    def n_lines(self) -> int:
 65        return self._inner.n_lines()
 66
 67    @property
 68    def n_transformers(self) -> int:
 69        return self._inner.n_transformers()
 70
 71    @property
 72    def n_loads(self) -> int:
 73        return self._inner.n_loads()
 74
 75    @property
 76    def n_generators(self) -> int:
 77        return self._inner.n_generators()
 78
 79    @property
 80    def n_sources(self) -> int:
 81        return self._inner.n_sources()
 82
 83    def to_format(self, to: str) -> Conversion:
 84        """Serialize to ``to`` (``dss``, ``pmd-json``, ``bmopf-json``).
 85
 86        Writing back to the source format echoes the retained source text byte
 87        for byte; a cross format write regenerates from the typed model and
 88        reports every fidelity loss in the warnings.
 89        """
 90        text, warnings = self._inner.to_format(to)
 91        return Conversion(text, warnings)
 92
 93    def to_canonical_format(self, to: str) -> Conversion:
 94        """Serialize to ``to`` from the typed model, bypassing source echo."""
 95        text, warnings = self._inner.to_canonical_format(to)
 96        return Conversion(text, warnings)
 97
 98    def write_file(self, path: Any, to: str) -> list[str]:
 99        """Serialize to ``to`` and write it to ``path`` byte exact.
100
101        Any sidecar the writer produces goes beside ``path``: a dss write of a
102        network with bus coordinates emits a ``Buscoords`` directive, and the
103        CSV it names is written too. Returns the fidelity warnings. See
104        :meth:`powerio.BalancedNetwork.write_file` for why this beats writing
105        :meth:`to_format` text through ``open(path, "w")`` on Windows.
106        """
107        return self._inner.write_file(str(path), to)
108
109    def graph(self) -> Any:
110        """Collapsed bus and terminal graph as Python data."""
111        return _json.loads(self._inner.graph_json())
112
113    def geo_layer(self) -> Any:
114        """This case's coordinates as a canonical GeoJSON FeatureCollection.
115
116        Raises when the case carries none.
117        """
118        return _json.loads(self._inner.geo_layer_json())
119
120    def apply_geo_layer(
121        self, text: str, name_hint: Optional[str] = None
122    ) -> tuple["MulticonductorNetwork", Any]:
123        """Apply a geographic sidecar and return ``(placed, report)``.
124
125        ``text`` is any form :func:`powerio.parse_geo` accepts. This network
126        is unchanged; the placed copy drops the retained source text, so a
127        same-format write re-serializes.
128        """
129        inner, report = self._inner.apply_geo_layer(text, name_hint)
130        return MulticonductorNetwork(inner), report
131
132    def __repr__(self) -> str:
133        return self._inner.__repr__()
134
135
136
137
138def parse_file(path: Any, from_: Optional[str] = None) -> MulticonductorNetwork:
139    """Parse a distribution network file.
140
141    The format comes from ``from_`` when given, else from the file itself:
142    ``.dss`` is OpenDSS, and ``.json`` holding the ENGINEERING ``data_model``
143    key is PMD JSON, otherwise BMOPF JSON.
144    """
145    return MulticonductorNetwork(_powerio.dist_parse_file(str(path), from_))
146
147
148def parse_str(text: str, format: str) -> MulticonductorNetwork:
149    """Parse an in-memory distribution network of the named ``format``."""
150    return MulticonductorNetwork(_powerio.dist_parse_str(text, format))
151
152
153def convert_file(path: Any, to: str, from_: Optional[str] = None) -> Conversion:
154    """Convert a distribution network file to ``to`` in one call.
155
156    The warnings carry both the parse warnings and the writer's fidelity
157    losses (there is no :class:`MulticonductorNetwork` to query them from).
158    """
159    text, warnings = _powerio.dist_convert_file(str(path), to, from_)
160    return Conversion(text, warnings)
161
162
163def convert_str(text: str, to: str, format: str) -> Conversion:
164    """Convert an in-memory distribution network of the named ``format`` to ``to``.
165
166    The signature matches :func:`powerio.convert_str`: input, target, source,
167    except ``format`` is required (there is no extension to infer from and no
168    default). The warnings carry both the parse warnings and the writer's
169    fidelity losses (there is no :class:`MulticonductorNetwork` to query them from).
170    """
171    text, warnings = _powerio.dist_convert_str(text, to, format)
172    return Conversion(text, warnings)
class MulticonductorNetwork:
 33class MulticonductorNetwork:
 34    """A parsed multiconductor distribution network in wire coordinates.
 35
 36    Buses carry named terminals, lines carry conductor impedance matrices, and
 37    transformers carry per winding connections. This type is distinct from the
 38    positive sequence :class:`powerio.BalancedNetwork`; balanced matrix builders do not
 39    accept it.
 40    """
 41
 42    def __init__(self, inner) -> None:
 43        self._inner = inner
 44
 45    @property
 46    def name(self) -> Optional[str]:
 47        """Distribution network name when the source format carries one."""
 48        return self._inner.name()
 49
 50    @property
 51    def source_format(self) -> Optional[str]:
 52        """Format parsed from: ``dss``, ``pmd-json``, or ``bmopf-json``."""
 53        return self._inner.source_format()
 54
 55    @property
 56    def warnings(self) -> "list[str]":
 57        """Return source fields not represented and assumptions made while parsing."""
 58        return self._inner.warnings()
 59
 60    @property
 61    def n_buses(self) -> int:
 62        return self._inner.n_buses()
 63
 64    @property
 65    def n_lines(self) -> int:
 66        return self._inner.n_lines()
 67
 68    @property
 69    def n_transformers(self) -> int:
 70        return self._inner.n_transformers()
 71
 72    @property
 73    def n_loads(self) -> int:
 74        return self._inner.n_loads()
 75
 76    @property
 77    def n_generators(self) -> int:
 78        return self._inner.n_generators()
 79
 80    @property
 81    def n_sources(self) -> int:
 82        return self._inner.n_sources()
 83
 84    def to_format(self, to: str) -> Conversion:
 85        """Serialize to ``to`` (``dss``, ``pmd-json``, ``bmopf-json``).
 86
 87        Writing back to the source format echoes the retained source text byte
 88        for byte; a cross format write regenerates from the typed model and
 89        reports every fidelity loss in the warnings.
 90        """
 91        text, warnings = self._inner.to_format(to)
 92        return Conversion(text, warnings)
 93
 94    def to_canonical_format(self, to: str) -> Conversion:
 95        """Serialize to ``to`` from the typed model, bypassing source echo."""
 96        text, warnings = self._inner.to_canonical_format(to)
 97        return Conversion(text, warnings)
 98
 99    def write_file(self, path: Any, to: str) -> list[str]:
100        """Serialize to ``to`` and write it to ``path`` byte exact.
101
102        Any sidecar the writer produces goes beside ``path``: a dss write of a
103        network with bus coordinates emits a ``Buscoords`` directive, and the
104        CSV it names is written too. Returns the fidelity warnings. See
105        :meth:`powerio.BalancedNetwork.write_file` for why this beats writing
106        :meth:`to_format` text through ``open(path, "w")`` on Windows.
107        """
108        return self._inner.write_file(str(path), to)
109
110    def graph(self) -> Any:
111        """Collapsed bus and terminal graph as Python data."""
112        return _json.loads(self._inner.graph_json())
113
114    def geo_layer(self) -> Any:
115        """This case's coordinates as a canonical GeoJSON FeatureCollection.
116
117        Raises when the case carries none.
118        """
119        return _json.loads(self._inner.geo_layer_json())
120
121    def apply_geo_layer(
122        self, text: str, name_hint: Optional[str] = None
123    ) -> tuple["MulticonductorNetwork", Any]:
124        """Apply a geographic sidecar and return ``(placed, report)``.
125
126        ``text`` is any form :func:`powerio.parse_geo` accepts. This network
127        is unchanged; the placed copy drops the retained source text, so a
128        same-format write re-serializes.
129        """
130        inner, report = self._inner.apply_geo_layer(text, name_hint)
131        return MulticonductorNetwork(inner), report
132
133    def __repr__(self) -> str:
134        return self._inner.__repr__()

A parsed multiconductor distribution network in wire coordinates.

Buses carry named terminals, lines carry conductor impedance matrices, and transformers carry per winding connections. This type is distinct from the positive sequence powerio.BalancedNetwork; balanced matrix builders do not accept it.

MulticonductorNetwork(inner)
42    def __init__(self, inner) -> None:
43        self._inner = inner
name: Optional[str]
45    @property
46    def name(self) -> Optional[str]:
47        """Distribution network name when the source format carries one."""
48        return self._inner.name()

Distribution network name when the source format carries one.

source_format: Optional[str]
50    @property
51    def source_format(self) -> Optional[str]:
52        """Format parsed from: ``dss``, ``pmd-json``, or ``bmopf-json``."""
53        return self._inner.source_format()

Format parsed from: dss, pmd-json, or bmopf-json.

warnings: list[str]
55    @property
56    def warnings(self) -> "list[str]":
57        """Return source fields not represented and assumptions made while parsing."""
58        return self._inner.warnings()

Return source fields not represented and assumptions made while parsing.

n_buses: int
60    @property
61    def n_buses(self) -> int:
62        return self._inner.n_buses()
n_lines: int
64    @property
65    def n_lines(self) -> int:
66        return self._inner.n_lines()
n_transformers: int
68    @property
69    def n_transformers(self) -> int:
70        return self._inner.n_transformers()
n_loads: int
72    @property
73    def n_loads(self) -> int:
74        return self._inner.n_loads()
n_generators: int
76    @property
77    def n_generators(self) -> int:
78        return self._inner.n_generators()
n_sources: int
80    @property
81    def n_sources(self) -> int:
82        return self._inner.n_sources()
def to_format(self, to: str) -> powerio.Conversion:
84    def to_format(self, to: str) -> Conversion:
85        """Serialize to ``to`` (``dss``, ``pmd-json``, ``bmopf-json``).
86
87        Writing back to the source format echoes the retained source text byte
88        for byte; a cross format write regenerates from the typed model and
89        reports every fidelity loss in the warnings.
90        """
91        text, warnings = self._inner.to_format(to)
92        return Conversion(text, warnings)

Serialize to to (dss, pmd-json, bmopf-json).

Writing back to the source format echoes the retained source text byte for byte; a cross format write regenerates from the typed model and reports every fidelity loss in the warnings.

def to_canonical_format(self, to: str) -> powerio.Conversion:
94    def to_canonical_format(self, to: str) -> Conversion:
95        """Serialize to ``to`` from the typed model, bypassing source echo."""
96        text, warnings = self._inner.to_canonical_format(to)
97        return Conversion(text, warnings)

Serialize to to from the typed model, bypassing source echo.

def write_file(self, path: Any, to: str) -> list[str]:
 99    def write_file(self, path: Any, to: str) -> list[str]:
100        """Serialize to ``to`` and write it to ``path`` byte exact.
101
102        Any sidecar the writer produces goes beside ``path``: a dss write of a
103        network with bus coordinates emits a ``Buscoords`` directive, and the
104        CSV it names is written too. Returns the fidelity warnings. See
105        :meth:`powerio.BalancedNetwork.write_file` for why this beats writing
106        :meth:`to_format` text through ``open(path, "w")`` on Windows.
107        """
108        return self._inner.write_file(str(path), to)

Serialize to to and write it to path byte exact.

Any sidecar the writer produces goes beside path: a dss write of a network with bus coordinates emits a Buscoords directive, and the CSV it names is written too. Returns the fidelity warnings. See powerio.BalancedNetwork.write_file() for why this beats writing to_format() text through open(path, "w") on Windows.

def graph(self) -> Any:
110    def graph(self) -> Any:
111        """Collapsed bus and terminal graph as Python data."""
112        return _json.loads(self._inner.graph_json())

Collapsed bus and terminal graph as Python data.

def geo_layer(self) -> Any:
114    def geo_layer(self) -> Any:
115        """This case's coordinates as a canonical GeoJSON FeatureCollection.
116
117        Raises when the case carries none.
118        """
119        return _json.loads(self._inner.geo_layer_json())

This case's coordinates as a canonical GeoJSON FeatureCollection.

Raises when the case carries none.

def apply_geo_layer( self, text: str, name_hint: Optional[str] = None) -> tuple[MulticonductorNetwork, typing.Any]:
121    def apply_geo_layer(
122        self, text: str, name_hint: Optional[str] = None
123    ) -> tuple["MulticonductorNetwork", Any]:
124        """Apply a geographic sidecar and return ``(placed, report)``.
125
126        ``text`` is any form :func:`powerio.parse_geo` accepts. This network
127        is unchanged; the placed copy drops the retained source text, so a
128        same-format write re-serializes.
129        """
130        inner, report = self._inner.apply_geo_layer(text, name_hint)
131        return MulticonductorNetwork(inner), report

Apply a geographic sidecar and return (placed, report).

text is any form powerio.parse_geo() accepts. This network is unchanged; the placed copy drops the retained source text, so a same-format write re-serializes.

def convert_file(path: Any, to: str, from_: Optional[str] = None) -> powerio.Conversion:
154def convert_file(path: Any, to: str, from_: Optional[str] = None) -> Conversion:
155    """Convert a distribution network file to ``to`` in one call.
156
157    The warnings carry both the parse warnings and the writer's fidelity
158    losses (there is no :class:`MulticonductorNetwork` to query them from).
159    """
160    text, warnings = _powerio.dist_convert_file(str(path), to, from_)
161    return Conversion(text, warnings)

Convert a distribution network file to to in one call.

The warnings carry both the parse warnings and the writer's fidelity losses (there is no MulticonductorNetwork to query them from).

def convert_str(text: str, to: str, format: str) -> powerio.Conversion:
164def convert_str(text: str, to: str, format: str) -> Conversion:
165    """Convert an in-memory distribution network of the named ``format`` to ``to``.
166
167    The signature matches :func:`powerio.convert_str`: input, target, source,
168    except ``format`` is required (there is no extension to infer from and no
169    default). The warnings carry both the parse warnings and the writer's
170    fidelity losses (there is no :class:`MulticonductorNetwork` to query them from).
171    """
172    text, warnings = _powerio.dist_convert_str(text, to, format)
173    return Conversion(text, warnings)

Convert an in-memory distribution network of the named format to to.

The signature matches powerio.convert_str(): input, target, source, except format is required (there is no extension to infer from and no default). The warnings carry both the parse warnings and the writer's fidelity losses (there is no MulticonductorNetwork to query them from).

def parse_file( path: Any, from_: Optional[str] = None) -> MulticonductorNetwork:
139def parse_file(path: Any, from_: Optional[str] = None) -> MulticonductorNetwork:
140    """Parse a distribution network file.
141
142    The format comes from ``from_`` when given, else from the file itself:
143    ``.dss`` is OpenDSS, and ``.json`` holding the ENGINEERING ``data_model``
144    key is PMD JSON, otherwise BMOPF JSON.
145    """
146    return MulticonductorNetwork(_powerio.dist_parse_file(str(path), from_))

Parse a distribution network file.

The format comes from from_ when given, else from the file itself: .dss is OpenDSS, and .json holding the ENGINEERING data_model key is PMD JSON, otherwise BMOPF JSON.

def parse_str(text: str, format: str) -> MulticonductorNetwork:
149def parse_str(text: str, format: str) -> MulticonductorNetwork:
150    """Parse an in-memory distribution network of the named ``format``."""
151    return MulticonductorNetwork(_powerio.dist_parse_str(text, format))

Parse an in-memory distribution network of the named format.