Skip to content

Add enable_ront: regulated transformer (rONT) as an LV voltage measure (#377) - #729

Open
nader-00 wants to merge 6 commits into
devfrom
feature/377-ront
Open

Add enable_ront: regulated transformer (rONT) as an LV voltage measure (#377)#729
nader-00 wants to merge 6 commits into
devfrom
feature/377-ront

Conversation

@nader-00

Copy link
Copy Markdown
Collaborator

Description

Adds an opt-in LV voltage-issue reinforcement measure: converting an LV
station's standard transformer to a regelbarer Ortsnetztransformator (rONT,
regulated distribution transformer) instead of reinforcing/disconnecting LV
lines, for grids where a bounded voltage-control range at the station is
sufficient to resolve all LV voltage issues.

Part of the broader effort in #377 (add/improve grid reinforcement methods).

Problem

Today, LV voltage issues are always solved by splitting/reinforcing LV
feeders (reinforce_lines_voltage_issues), even in cases where the actual
spread of LV voltage deviations across the grid is small enough that a
regulated transformer at the station — a real, commonly used piece of
equipment in German LV grids — would resolve the issue without touching a
single line.

Solution

  • New enable_ront: bool = False parameter on reinforce_grid(). When
    enabled, for every LV grid with voltage issues, before falling back to line
    reinforcement, checks whether a regulated transformer with a bounded
    control range (lv_grid_ront_feasible()) can resolve all voltage issues at
    that grid — if so, converts the station's transformer to its rONT
    equivalent (reinforce_lv_grid_ront_voltage_issues()) instead of touching
    lines.
  • Model: rONT is treated as electrically identical to the standard
    transformer it replaces (same s_nom/r_pu/x_pu) — the voltage-
    regulating behaviour is modelled as a widened, bounded allowed-voltage
    window around the transformer's own unregulated secondary voltage
    (v_unreg(t)), evaluated in _lv_allowed_voltage_limits(), rather than by
    simulating an actual tap-changer in the power flow. This is a deliberate
    simplification (Option A from the design discussion) — see Caveats below.
  • Feasibility check (lv_grid_ront_feasible()): the single source of
    truth used both by the reinforcement guard and by the RONT-aware final
    check, so "a RONT was installed" and "the final +/-10% band check passes"
    are structurally guaranteed to agree, not just empirically.
  • Final check: the existing global +/-10% voltage check
    (allowed_voltage_limits(split_voltage_band=False)) excludes (does not
    silently pass) buses belonging to a feasible-RONT grid, using the same
    lv_grid_ront_feasible() function — everything else keeps the untouched,
    hard +/-10% band as a backstop.
  • New equipment rows: one " RONT"-suffixed variant per standard LV
    transformer size in equipment-parameters_LV_transformers.csv, impedance-
    identical to its base type (is_ront()/ront_type_name()/
    standard_type_name() helpers in tools.py).
  • New config: [grid_expansion_ront].ront_voltage_range = 0.10 and
    [costs_transformers].ront_surcharge = 20 (both sourced, see Config
    values below).

Two additional, incidental fixes found during integration testing

  1. Duplicate-transformer bug: _reinforce_station_overloading()'s
    "second transformer of the same kind" clone branch copied the RONT-tagged
    type_info verbatim onto a new transformer meant to resolve an
    independent overload — meaning a RONT station could end up with two
    RONT-tagged transformers instead of one RONT (voltage) plus one correctly
    typed standard transformer (overload). Fixed via an is_ront() check +
    standard_type_name() reassignment before the clone is renamed. The
    invariant is "no station has more than one RONT-tagged transformer", not
    "no station has more than one transformer" — a RONT station legitimately
    can gain a second, standard transformer for an independent overload.
  2. Transformer cost bug (unrelated to the above, found during cost
    validation): grid_expansion_costs() only ever costed transformers with
    equipment_changes["change"] == "added", never "changed". RONT
    conversions are logged as "changed" (the transformer itself is not
    replaced, only its type_info), so RONT's ront_surcharge was silently
    never costed (always 0 kEUR). Fixed by extending the costable-transformer
    filter to include "changed" entries (excluding any later "removed"),
    mirroring how "changed" lines are already costed unconditionally.

Config values (sourced)

  • ront_voltage_range = 0.10: FNN-Hinweis "Regelbarer Ortsnetztransformator
    (rONT) — Einsatz in Netzplanung und Netzbetrieb" (2016), sections
    5.9.1/5.9.2 — the network-planning criterion uses +/-10% (9 taps, 2.5%
    step); market products and all five practical examples in annex B confirm
    +/-4 x 2.5%.
  • ront_surcharge = 20 kEUR: same source, section 5.8.1 — names the cost
    components (procurement, installation incl. sensors/control electronics,
    documentation) but no absolute figures; the value itself is a placeholder,
    operator- and product-specific, and should be adapted per use case.

Caveats (idealisation, called out explicitly)

  • Real rONTs are stepped (2.5% per tap) with a control bandwidth/dead-band of
    typically ~4% (1.6x the tap step, FNN section 5.5.2) before the regulator
    switches. This PR's ref(t) model is continuous and dead-band-free — an
    optimistic idealisation relative to any real device, in addition to the
    general limitation that no actual tap-changer is simulated in the power
    flow.
  • rONT cannot solve overload/thermal problems (FNN 2016, section 7/5.3) —
    this PR only ever uses it for voltage issues; an independent overload at
    the same station is still resolved by adding a correctly-typed standard
    transformer (see the duplicate-transformer fix above).
  • Empirical benefit magnitude, for context: FNN annex B reports rONT cost
    ratios of 1:2.5 to 1:6-7 versus conventional expansion in real projects
    (~28% savings). Our own validation sample (33 known voltage-driven LV
    grids across 7 real networks) shows a comparable order of magnitude
    (status-quo 2573.0 kEUR -> RONT 1297.36 kEUR total, ~50%), though our flat,
    size- and cross-section-independent line cost model likely understates the
    conventional alternative rather than overstating it.

Validation

  • Full unit test suite for the touched modules: 61 passed.
  • Regression: enable_ront=False confirmed bit-identical to dev (pickled
    transformers_df, topology_lines_df, equipment_changes,
    grid_expansion_costs, compared via pandas.testing.assert_frame_equal),
    re-checked after every code change.
  • Integration across all 33 known voltage-driven LV grids in 7 real networks
    (excluding one grid with a known, unrelated non-convergence issue): 33/33
    resolved via RONT, 0/33 residual violations against the final +/-10% check,
    7/7 networks without a ValueError, 0 stations with more than one
    RONT-tagged transformer, 33/33 grids with equal-or-fewer reinforcement
    measures than the status quo.

Related, but independent work

Type of change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue) — the two incidental
    fixes described above

Checklist:

  • New and adjusted code is formatted using the pre-commit hooks
  • New and adjusted code includes type hinting now
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • The Read the Docs documentation is compiling correctly
  • If new packages are needed, I added them to setup.py etc.
  • I have added new features to the corresponding whatsnew file

nader-00 added 2 commits July 23, 2026 16:32
Adds an enable_ront flag to reinforce_grid() that resolves LV-internal
voltage issues by relabelling the existing station transformer as a RONT
(regelbarer Ortsnetztransformator) instead of disconnecting or reinforcing
lines, for grids where lv_grid_ront_feasible() confirms that a RONT with a
bounded control range (ront_voltage_range config, default +/-6%, placeholder
pending a citable source) can resolve all voltage issues at every time step
- respecting both that control range and the hard +/-10% system voltage
limit. lv_grid_ront_feasible() is the single source of truth used by both
the enable_ront guard and the RONT-aware final absolute check, so a grid for
which RONT is set is guaranteed to also pass that check.

The RONT is electrically identical to the standard transformer it replaces
(s_nom/r_pu/x_pu unchanged, only type_info changes) - its regulating effect
is modelled as an idealised, bounded reference-voltage shift in the voltage
limit checks, not as an actual tap-changing transformer in the power flow.

Also fixes two pre-existing gaps surfaced while validating this feature:

- _reinforce_station_overloading()'s "second transformer of the same kind"
  clone branch could clone a RONT-tagged transformer's type_info onto the
  new transformer, mislabelling a plain overload-driven unit as a second
  RONT. Fixed via a new standard_type_name() helper (inverse of
  ront_type_name()).
- grid_expansion_costs() only costed transformers with change=="added",
  silently never costing "changed" transformers (RONT conversions use
  change=="changed", analogous to how line type upgrades are logged).
  Extended to cost "changed" transformers too, excluding ones later removed.

Validated against 33 known voltage-driven LV grids across 7 real networks:
33/33 resolved via RONT, 0 residual violations, 0 stations with more than
one RONT-tagged transformer, real Grenzfall 32182/1118050007 confirmed
solved. enable_ront=False remains bit-identical to dev.
ront_voltage_range: 0.06 (unsourced placeholder) -> 0.10, per FNN-Hinweis
"Regelbarer Ortsnetztransformator (rONT) -- Einsatz in Netzplanung und
Netzbetrieb" (2016), sections 5.9.1/5.9.2 -- the network planning criterion
uses +/-10% (9 taps, 2.5% step); market products and all five practical
examples in annex B confirm +/-4 x 2.5%. Comment now also notes that real
rONTs are stepped with a ~4% control deadband (section 5.5.2), whereas our
ref(t) model is continuous and deadband-free -- an optimistic idealisation,
to be called out explicitly in the PR description.

ront_surcharge: kept at 20 kEUR, comment replaced to point at the actual
FNN 2016 cost-component discussion (section 5.8.1) instead of the earlier,
less specific literature pointers -- FNN names cost components but no
absolute figures, so the value itself remains a placeholder to be adapted
per operator/product.

Re-validated with the new default: full test suite green, enable_ront=False
still bit-identical to dev, integration re-run against the 33 known cases
unchanged (33/33 resolved, 0 residual violations, 7/7 networks without
ValueError, 0 stations with more than one RONT-tagged transformer).
- remove lv_grid_voltage_spread() (dead since lv_grid_ront_feasible()
  became the shared guard/check criterion) and its tests
- remove the 7 unused "<size> RONT" catalogue rows; rONT type_info is a
  marker (impedance comes from the existing transformer row), documented
  in ront_type_name()
- move ront_voltage_range to [grid_expansion_allowed_voltage_deviations]
  next to lv_max_v_rise/lv_max_v_drop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant