Skip to content

v5.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 03 Aug 08:34

PowerDynamics v5.0.0

Diff since v4.4.1

Version 5.0.0 Changelog

Two themes: the ecosystem move that comes down from
NetworkDynamics v1.0/v1.1
(ModelingToolkit v11, the reworked initialization pipeline, SciML v3), and a per-unit
overhaul that turns base values from per-device constructor arguments into a property of
the bus and line.

The library saw a broad naming and unit cleanup in the process. Renamed parameters fail
loudly at construction, so the practical upgrade path is to build your models and follow
the errors — check the docstrings for anything that no longer takes the argument you
pass it
. The parts that change numbers rather than names are called out explicitly
below.

Breaking: ecosystem

  • All NetworkDynamics v1.0 breaking changes apply unchanged. PowerDynamics
    @reexports NetworkDynamics, so its API is part of the PowerDynamics API — most
    notably set_mtk_defaults! → non-mutating set_mtk_defaults, symbolic expressions as a
    guess now error in favour of initf/guessf, find_fixpoint takes an NWState, and
    MTK models are no longer simplified by mtkcompile. See the
    NetworkDynamics release notes
    for the details and the rewrites. NetworkDynamics ≥ 1.1.0 is required.
  • ModelingToolkitModelingToolkitBase + SciCompDSL
    (#261). MTK v11 split into
    ModelingToolkitBase (MIT) and ModelingToolkit (AGPL); PowerDynamics depends only on
    the MIT half, and @mtkmodel moved out into SciCompDSL. Along with it: Symbolics ≥7,
    SciMLBase ≥3 and the OrdinaryDiffEq* subpackages ≥2.
  • The Bus(...)/Line(...) deprecations are removed; use compile_bus/compile_line.

Breaking: per-unit system

Base values are now part of the model structure rather than something you pass to each
device. There is a new documentation chapter, "Per-Unit Systems", that explains the
whole scheme; the short version:

  • Global bases with accessors: a system power base (100 MVA), frequency base (2π·50
    rad/s) and fallback voltage base, set through the new exported set_Sbase!,
    set_ωbase!/set_fbase! and set_Vbase! (plus get_* counterparts). They are read at
    model construction, so set them before building anything.
  • New SystemBase component carries the global quantities (Sbase, ωbase, ωframe).
    Exactly one instance named systembase sits at the top level of every bus and line;
    MTKBus/MTKLine/compile_bus/compile_line place it for you.
  • BusBar and LineEnd carry Vbase, the one genuinely local base, along with derived
    Ibase/Zbase/Ybase and SI observables (u_kV, P_MW, Q_MVAr, i_kA). Sbase is a
    three-phase power and Vbase line-to-line, hence Ibase = Sbase/(√3·Vbase) — the exported
    helper Ibase(S, V) changed accordingly.
  • Devices inherit their bases and no longer take S_b/V_b/ω_b. Drop those arguments
    at the call site: set the power/frequency base globally and the per-bus voltage base with
    the new MTKBus(…; Vbase=…) / compile_bus(…; Vbase=…) keyword. Vbase propagates
    outward from there — a line end inherits from the bus it connects to (a transformer is
    just a line whose two ends resolve differently, ratio included), and a satellite/injector
    bus inherits from its hub. An explicitly set value always wins.
  • Sn/Vn are now optional, weakly defaulting to the bus base, so a machine running on
    the system base needs no rating at all.
  • LineEnd requires a side keyword (:src/:dst) — it selects which bus the end
    inherits from. MTKLine passes it automatically; hand-rolled line models must be updated.
  • New check_base_consistency(s::NWState), run automatically by initialize_from_pf[!]
    (check=:error by default, :warn/:none to downgrade), verifying that the global bases
    agree everywhere and that every line end and satellite bus matches the bus it hangs off.

Breaking: naming and units in the library

  • Setpoints are spelled …set (Pset, Qset, Vset, ωset, …); n/nom is reserved
    for genuine nameplate ratings (Sn, Vn). Models ported from elsewhere keep their
    source's names so they can still be checked against the original — the AVRs' vref and
    the governors' ω_ref/p_ref are unchanged.

  • EMT components name parameters for what they hold: reactance X and susceptance B
    instead of L and C, which stored reactance/susceptance values all along. The topology
    is in the model name, so there is no ambiguity.

  • Frequency vocabulary is split three ways. One symbol used to do three unrelated jobs:

    symbol kind unit meaning
    ωbase unit rad/s converts SI time ↔ pu; the frequency at which X = ωbase·L, B = ωbase·C were evaluated
    ωframe gauge pu speed of the global dq frame; pinned to 1
    ωset setpoint pu frequency a droop/damping law is commanded to hold

    Consequences throughout the library: an ω0 = 2π*50 keyword on the dynamic shunts,
    branches and ComposableInverter models is gone (they take ωbase from the bus instead),
    every place where the reference frame was an invisible literal 1 now writes ωframe,
    and the dq cross-coupling terms of the EMT components gained the ωframe factor they were
    missing. Since ωframe == 1 these are all algebraically identical to before — the OpenIPSL
    comparison tests pass unchanged.

  • Three models genuinely change behaviour and their parameter sets do not carry over:

    • Swing and IdealDroopInverter had no ωbase at all, so with ω in pu the
      angle advanced ~314× too slowly. Both now use Dt(θ) ~ ωbase*(ω - ωframe). Swing's
      M is the inertia M = 2H in seconds (default 6.0, i.e. H = 3 s) and D a damping
      power coefficient in pu/pu (default 2.0); IdealDroopInverter's Kp is a pu droop
      (default 0.05). Retune existing parameter sets against these units. A physically sized
      machine is lightly damped, so call sites that want a quickly settling transient now pass
      a D well above the default.
    • DroopOuter/DroopInverter: ω is now pu rather than rad/s, making Kp a
      dimensionless droop (default 0.05). Convert an existing value with
      Kp_new = Kp_old/ωbase — with that substitution the rewrite is an exact identity, and
      the linearization is unchanged.

    IdealDroopInverter and DroopInverter now also share the same droop defaults
    Kp = Kq = 0.05; they previously disagreed on Kq, so swapping one model for the other
    silently changed the voltage stiffness.

  • Smaller renames in the same spirit — VoltageDependentLoad's VnVset,
    PSSE_GENCLS's output port ωoutSPEED_out, and unified state/output names on the
    two PLLs. Consult the docstrings.

Initialization

  • Backward initialization through nested components. NetworkDynamics' new
    initf/guessf metadata lets each model carry its own init recipe; composed together
    they chain into a DAG that can fix every free variable from the powerflow alone, with no
    nonlinear solve at all. The new tutorial "Backward Initialization of Nested Models" builds
    a full generator bus that initializes to "No free variables!", and a new "Advanced
    Initialization"
    documentation chapter covers the mechanics.
  • @pfinitconstraint/@pfinitformula hygiene fixed: both macros mishandled escaping, so
    a constraint could not close over local runtime variables. They now capture locals
    correctly and show prints the macro form you wrote.
  • initialize_from_pf no longer ignores pfs0 — the supplied start state was
    overwritten with the powerflow network, so a hand-tuned guess had no effect. tol/nwtol
    are now forwarded to the residual check.

Library

  • Optional stator dynamics in SauerPaiMachine via SauerPaiMachine(; stator_dynamics=true).
  • compile_bus/compile_line accept a bare injector/branch, wrapping it in
    MTKBus/MTKLine automatically instead of erroring.
  • New mtkcompile keyword on compile_bus/compile_line, forwarded to
    VertexModel/EdgeModel.
  • Bug fixes: ClassicalMachine's Park transform applied the current base ratio upside down
    (wrong currents whenever Sn ≠ Sbase); a disconnected ComposableInverter kept driving
    its LCL filter and wound up to ≈70 pu internally; limited-integrator callbacks now resolve
    the integrator state through the observed-alias chain instead of assuming a name survives
    compilation (visible in PSSE_HYGOV); TurbineGovTypeI could not be constructed at all
    (it is constructible now, but remains untested — there is no reference trajectory for it).
  • Saturation functions (QUAD_SE, EXP_SE, PSSE_ESST4B's FEX_function) are AD-safe and
    use NaNMath, so a slightly out-of-range argument no longer throws a DomainError
    mid-initialization.

Documentation

  • New chapters: "Per-Unit Systems" and "Advanced Initialization" (see above).
  • New "Getting Started with Julia" section
    (#276): the setup page was
    rewritten and joined by "Environment Management" and an opinionated "How to Structure
    Research Projects" guide, aimed at users who arrive at PowerDynamics before they arrive at
    Julia.
  • New tutorial "Backward Initialization of Nested Models"; the EMT toymodel example was
    overhauled, and the initialization and modeling-concepts chapters were reworked for v5.

Merged pull requests:

Closed issues:

  • Vector state definition not working in @mtkmodel (#240)
  • Need dynamic branch and load (Series RL) model (#241)
  • Find fix point from dynamic equation (#245)
  • Failed to pre-compile PowerDynamics package (#263)
  • unspecific error message for power flow initialization (#266)