Skip to content

Commit

Permalink
Support star and zigzag transformers with non brought out neutrals (#310
Browse files Browse the repository at this point in the history
)

Fixes #308
  • Loading branch information
alihamdan authored Jan 6, 2025
1 parent 71daae0 commit a7e8a61
Show file tree
Hide file tree
Showing 10 changed files with 654 additions and 27 deletions.
2 changes: 2 additions & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ og:description: See what's new in the latest release of Roseau Load Flow !

## Unreleased

- {gh-pr}`310` {gh-issue}`308` Support star and zig-zag windings with non-brought out neutral. In
earlier versions, vector groups like "Yd11" were considered identical to "YNd11".
- {gh-pr}`307` {gh-issue}`296` Make `line.res_violated` and `bus.res_violated` return a boolean array
indicating if the corresponding phase is violated. This is consistent with the dataframe results
`en.res_lines` and `en.res_buses_voltages`. For old behavior, use `line_or_bus.res_violated.any()`.
Expand Down
58 changes: 55 additions & 3 deletions roseau/load_flow/io/dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import copy
import logging
import warnings
from collections import defaultdict
from typing import TYPE_CHECKING, TypeVar

import numpy as np
Expand All @@ -35,7 +36,7 @@

logger = logging.getLogger(__name__)

NETWORK_JSON_VERSION = 3
NETWORK_JSON_VERSION = 4
"""The current version of the network JSON file format."""

_T = TypeVar("_T", bound=AbstractBranch)
Expand Down Expand Up @@ -94,7 +95,7 @@ def network_from_dict( # noqa: C901
data = copy.deepcopy(data) # Make a copy to avoid modifying the original

version = data.get("version", 0)
if version <= 2:
if version <= 3:
warnings.warn(
f"Got an outdated network file (version {version}), trying to update to the current format "
f"(version {NETWORK_JSON_VERSION}). Please save the network again.",
Expand All @@ -108,6 +109,8 @@ def network_from_dict( # noqa: C901
data = v1_to_v2_converter(data)
if version <= 2:
data = v2_to_v3_converter(data)
if version <= 3:
data = v3_to_v4_converter(data)
else:
# If we arrive here, we dealt with all legacy versions, it must be the current one
assert version == NETWORK_JSON_VERSION, f"Unsupported network file version {version}."
Expand Down Expand Up @@ -662,7 +665,7 @@ def v2_to_v3_converter(data: JsonDict) -> JsonDict: # noqa: C901
"""
assert data.get("version", 0) == 2, data["version"]

# The name of min_voltage, max_voltage and max_voltage_level has changed so they are not usable anymore.
# The name of min_voltage and max_voltage have changed so they are not usable anymore.
old_buses = data.get("buses", [])
buses = []
bus_warning_emitted: bool = False
Expand Down Expand Up @@ -749,3 +752,52 @@ def v2_to_v3_converter(data: JsonDict) -> JsonDict: # noqa: C901
results["short_circuits"] = data["short_circuits"] # Unchanged

return results


def v3_to_v4_converter(data: JsonDict) -> JsonDict:
"""Convert a v3 network dict to a v4 network dict.
Args:
data:
The v3 network data.
Returns:
The v4 network data.
"""
assert data.get("version", 0) == 3, data["version"]

tr_phases_per_params = defaultdict(list)
for tr in data["transformers"]:
tr_phases_per_params[tr["params_id"]].append((tr["phases1"], tr["phases2"]))

old_transformer_params = data["transformers_params"]
transformer_params = []
for tp_data in old_transformer_params:
w1, w2, clock = TransformerParameters.extract_windings(tp_data["vg"])
# Handle brought out neutrals that were not declared as such
if w1 in ("Y", "Z") and any(tr_phases[0] == "abcn" for tr_phases in tr_phases_per_params[tp_data["id"]]):
w1 += "N"
if w2 in ("y", "z") and any(tr_phases[1] == "abcn" for tr_phases in tr_phases_per_params[tp_data["id"]]):
w2 += "n"
# Normalize the vector group (dyN11 -> Dyn11)
tp_data["vg"] = f"{w1}{w2}{clock}"
transformer_params.append(tp_data)

results = {
"version": 4,
"is_multiphase": data["is_multiphase"], # Unchanged
"grounds": data["grounds"], # Unchanged
"potential_refs": data["potential_refs"], # Unchanged
"buses": data["buses"], # <---- Unchanged
"lines": data["lines"], # <---- Unchanged
"switches": data["switches"], # Unchanged
"transformers": data["transformers"], # <---- Unchanged
"loads": data["loads"], # Unchanged
"sources": data["sources"], # Unchanged
"lines_params": data["lines_params"], # <---- Unchanged
"transformers_params": transformer_params, # <---- Changed
}
if "short_circuits" in data:
results["short_circuits"] = data["short_circuits"] # Unchanged

return results
99 changes: 99 additions & 0 deletions roseau/load_flow/io/tests/data/generators/v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""This script generates a version 2 JSON file.
Run with roseau-load-flow==0.11.0, NOT WITH THE CURRENT VERSION::
uv run --no-project --script roseau/load_flow/io/tests/data/generators/v3.py
"""

# Script metadata generated by `uv`
# /// script
# requires-python = ">=3.10,<3.14"
# dependencies = [
# "roseau-load-flow==0.11.0",
# ]
# ///
import cmath
from pathlib import Path

import numpy as np
from shapely import LineString, Point

import roseau.load_flow as rlf
from roseau.load_flow.io.dict import NETWORK_JSON_VERSION

if NETWORK_JSON_VERSION != 3:
raise ValueError(
f"This script is meant to generate version 3 JSON files, current version: {NETWORK_JSON_VERSION}"
f"\nExecute with `uv run --no-project --script roseau/load_flow/io/tests/data/generators/v3.py`"
)

OUTPUT = Path(__file__).parent.parent / f"network_json_v{NETWORK_JSON_VERSION}.json"

bus1 = rlf.Bus(
id=1, phases="abcn", geometry=Point(0.0, 0.0), nominal_voltage=66e3, min_voltage_level=0.98, max_voltage_level=1.02
)
bus2 = rlf.Bus(
id=2, phases="abcn", geometry=Point(0.0, 1.0), nominal_voltage=66e3, min_voltage_level=0.98, max_voltage_level=1.02
)
bus3 = rlf.Bus(
id=3, phases="abc", geometry=Point(0.0, 1.0), nominal_voltage=20e3, min_voltage_level=0.95, max_voltage_level=1.10
)
bus4 = rlf.Bus(
id=4, phases="abc", geometry=Point(0.0, 2.0), nominal_voltage=20e3, min_voltage_level=0.95, max_voltage_level=1.10
)

ground = rlf.Ground(id="gnd")
ground.connect(bus1, phase="n")
rlf.PotentialRef(id="pref", element=ground)
rlf.PotentialRef(id="pref2", element=bus3)

vs = rlf.VoltageSource(id=1, bus=bus1, voltages=66e3, phases="abc")
power = cmath.rect(40e3, np.acos(0.95))
rlf.PowerLoad(id=1, bus=bus2, phases="abcn", powers=[power, power, power])

lp_66kv = rlf.LineParameters(
id=1,
z_line=0.35 * np.eye(4, dtype=complex),
y_shunt=1e-6j * np.eye(4, dtype=complex),
line_type=rlf.LineType.OVERHEAD,
materials=rlf.Material.CU,
insulators=None,
sections=rlf.Q_(240, "mm^2"),
ampacities=250,
)
lp_20kv = rlf.LineParameters(
id=2,
z_line=0.35 * np.eye(3, dtype=complex),
line_type=rlf.LineType.UNDERGROUND,
materials=rlf.Material.AL,
insulators=rlf.Insulator.PVC,
sections=rlf.Q_(150, "mm^2"),
ampacities=160,
)
rlf.Line(
id=1, bus1=bus1, bus2=bus2, parameters=lp_66kv, length=10.0, geometry=LineString([(0, 0), (1, 0)]), ground=ground
)
rlf.Line(id=2, bus1=bus3, bus2=bus4, parameters=lp_20kv, length=1.0, geometry=LineString([(0, 1), (0, 2)]))

tp = rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="1", vg="Yd11", uhv=20000.0, ulv=400.0, sn=160000.0, p0=460.0, i0=0.023, psc=2350.0, vsc=0.04
)
rlf.Transformer(
id=1,
bus1=bus2,
bus2=bus3,
phases1="abcn",
phases2="abc",
parameters=tp,
geometry=Point(0.0, 1.0),
tap=1.025,
max_loading=0.9,
)
rlf.PowerLoad(id=3, bus=bus3, phases="ab", powers=[1000 + 200j])
rlf.CurrentLoad(id=4, bus=bus4, phases="bc", currents=[2 + 1j])
rlf.ImpedanceLoad(id=5, bus=bus4, phases="ca", impedances=[100 + 200j])

en = rlf.ElectricalNetwork.from_element(bus1)
en.solve_load_flow()

en.to_json(OUTPUT, include_results=True)
12 changes: 6 additions & 6 deletions roseau/load_flow/io/tests/data/network_json_v0.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
),
"160kVA_Yd11": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yd11",
vg="yd11",
vg="ynd11",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand All @@ -246,7 +246,7 @@
),
"160kVA_Yd5": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yd5",
vg="yd5",
vg="ynd5",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand All @@ -257,7 +257,7 @@
),
"160kVA_Yyn0": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yyn0",
vg="yyn0",
vg="ynyn0",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand All @@ -268,7 +268,7 @@
),
"160kVA_Yyn6": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yyn6",
vg="yyn6",
vg="ynyn6",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand All @@ -279,7 +279,7 @@
),
"160kVA_Yzn11": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yzn11",
vg="yzn11",
vg="ynzn11",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand All @@ -290,7 +290,7 @@
),
"160kVA_Yzn5": rlf.TransformerParameters.from_open_and_short_circuit_tests(
id="160kVA_Yzn5",
vg="yzn5",
vg="ynzn5",
sn=160000.0,
uhv=20000.0,
ulv=400.0,
Expand Down
Loading

0 comments on commit a7e8a61

Please sign in to comment.