From d34ba229703b77aac066ed04e81f2af2f77e5358 Mon Sep 17 00:00:00 2001 From: Luke Kiernan Date: Thu, 10 Sep 2026 10:51:46 -0600 Subject: [PATCH 1/2] Follow the DeviceBaseUnit -> ComponentBaseUnit rename in the docs InfrastructureSystems#634 and PowerSystems#1791 renamed the per-unit marker: `DU`/`DeviceBaseUnit` are now `CU`/`ComponentBaseUnit`. PFFP names no marker in code -- it writes the OpenAPI wire format, whose `power_units` spelling (`COMPONENT_BASE`/`NATURAL_UNITS`) comes from SiennaSchemas and did not change, and `IS.UnitSystem.DEVICE_BASE` was not renamed -- so this is comments and docstrings only, describing the PowerSystems importer these documents are written for. The `device_base.jl` filename and the `_DEVICEBASE_*` constant names are deliberately left alone: renaming them would collide with the open work in #42 and #55 across the same files for no behavioural gain. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LP6eB1zx4eyE3hd7tpvSue --- src/openapi/build.jl | 2 +- src/openapi/cost.jl | 2 +- src/openapi/device_base.jl | 10 +++++----- test/test_openapi_branch.jl | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/openapi/build.jl b/src/openapi/build.jl index 398daee..c1b8ad7 100644 --- a/src/openapi/build.jl +++ b/src/openapi/build.jl @@ -74,7 +74,7 @@ a caller never mistakes a partial document for a complete one. values as computed; `"COMPONENT_BASE"` additionally runs [`apply_device_base_conversion!`](@ref) over the built document, converting every power-family field into per-unit on the component's own device base (or the system base, for the few types with none of their own) — -the document PowerSystems' `DeviceBaseUnit` importer expects. Keyword arguments — the +the document PowerSystems' `ComponentBaseUnit` importer expects. Keyword arguments — the `*_name_formatter`s the PSS/E metadata reimport path needs — are threaded through to every reader unconsumed. """ diff --git a/src/openapi/cost.jl b/src/openapi/cost.jl index fb53870..bd2fa96 100644 --- a/src/openapi/cost.jl +++ b/src/openapi/cost.jl @@ -19,7 +19,7 @@ function _zero_vom_cost() end """A `CostCurve` with a zero linear value curve, matching PSCB's `zero(CostCurve)` -(`NaturalUnit`, not `DeviceBaseUnit`) — the fallback for every generator/load type that +(`NaturalUnit`, not `ComponentBaseUnit`) — the fallback for every generator/load type that has no cost data to read from a PowerModels dict.""" function _zero_cost_curve() return PC.CostCurve(; diff --git a/src/openapi/device_base.jl b/src/openapi/device_base.jl index 4df1c96..5951b2c 100644 --- a/src/openapi/device_base.jl +++ b/src/openapi/device_base.jl @@ -8,7 +8,7 @@ # divides each power-family field by the component's own device base (or, for a type with # no device base of its own, the system base) — the exact inverse of what PowerSystems' own # `NaturalUnit` importer does (`src/openapi/import_handwritten.jl`/`src/models/generated/*.jl` there), -# so that PowerSystems' `DeviceBaseUnit` importer reading this document's numbers directly +# so that PowerSystems' `ComponentBaseUnit` importer reading this document's numbers directly # reproduces the same System a `NATURAL_UNITS` document produces through `NaturalUnit`. # # ── Field classification, mechanical path ─────────────────────────────────────── @@ -26,7 +26,7 @@ # device base exactly like its MW siblings in every PowerSystems converter checked). # # Two cases the mechanical rule cannot see, found by diffing PowerSystems' -# `to_openapi(..., ::DeviceBaseUnit)` against `::NaturalUnit)` field-by-field in +# `to_openapi(..., ::ComponentBaseUnit)` against `::NaturalUnit)` field-by-field in # `export_handwritten.jl`: # - `Area`/`LoadZone.peak_active_power`/`peak_reactive_power` (and # `TransmissionInterface.active_power_flow_limits`, not reachable from this package's @@ -58,7 +58,7 @@ # FIXED value for these discriminators, independent of the run's `power_units` — # confirmed by grepping every `set_value!(_, :*_units, ...)` call in this directory — # so the field's own representation never depends on the run's convention either, -# and PowerSystems' own converters confirm it is identical between `DeviceBaseUnit`/ +# and PowerSystems' own converters confirm it is identical between `ComponentBaseUnit`/ # `NaturalUnit` in every case checked. These are `:skip`. # 2. A **natural-unit choice** among sibling units of the SAME quantity # (`EnergyReservoirStorage.energy_units`: "MWH" vs "MWMIN", both genuine energy units) @@ -121,11 +121,11 @@ const _DEVICEBASE_INSTANCE_DISPATCHED = Dict{Tuple{String, Symbol}, Symbol}( # (Voltage/pu for VOLTAGE-family objectives, MW/MVAr for ACTIVE_POWER_FLOW/ # REACTIVE_POWER_FLOW/CONTROL_OF_DC_LINE-family objectives) -- but PowerSystems' own # to_openapi calls the SAME unscaled `_minmax_po(get_controlled_quantity_limits(circuit))` - # in BOTH the DeviceBaseUnit and NaturalUnit methods (export_handwritten.jl:166-167 and + # in BOTH the ComponentBaseUnit and NaturalUnit methods (export_handwritten.jl:166-167 and # :195-196) -- i.e. PSY never scales this field by base_power regardless of document # convention OR control_objective. A first cut of this registry made it `:dynamic` # (converting the power-flow-family branches) purely from the schema's declared - # quantity, without checking PSY's actual DU/NU pair -- wrong, and invisible on the + # quantity, without checking PSY's actual CU/NU pair -- wrong, and invisible on the # 14-bus fixture because every circuit there is control_objective = "FIXED" (a # VOLTAGE-family, already-`:skip` branch either way). Static `:skip`, matching # `control_limits`. diff --git a/test/test_openapi_branch.jl b/test/test_openapi_branch.jl index feda5e2..f75fe60 100644 --- a/test/test_openapi_branch.jl +++ b/test/test_openapi_branch.jl @@ -314,7 +314,7 @@ end # COMPONENT_BASE registry classify it `:dynamic` (converting the power-flow-family # branches by the circuit's own base_power). That was wrong: PowerSystems' own # `to_openapi` calls the SAME unscaled `_minmax_po(get_controlled_quantity_limits(...))` - # in BOTH `DeviceBaseUnit` and `NaturalUnit` (export_handwritten.jl:166-167, :195-196) — + # in BOTH `ComponentBaseUnit` and `NaturalUnit` (export_handwritten.jl:166-167, :195-196) — # this field never scales with the document convention, regardless of # `control_objective`. Invisible on the 14-bus fixture because every circuit there is # `control_objective = "FIXED"` (already `:skip` either way) — this test uses From 23467ba496e686b70115a560d34f5d49efbcf4c1 Mon Sep 17 00:00:00 2001 From: Luke Kiernan Date: Thu, 10 Sep 2026 10:52:36 -0600 Subject: [PATCH 2/2] Catch the remaining device-base references Two more prose sites the first pass missed: the qualified `IS.DU` in make_thermal_cost's docstring and a `PSY.DU` aside in the branch tests. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LP6eB1zx4eyE3hd7tpvSue --- src/openapi/cost.jl | 2 +- test/test_openapi_branch.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openapi/cost.jl b/src/openapi/cost.jl index bd2fa96..74457b1 100644 --- a/src/openapi/cost.jl +++ b/src/openapi/cost.jl @@ -95,7 +95,7 @@ Thermal generation cost from a MATPOWER-shaped `pm_gen`'s `"model"`/`"cost"` fie Model `1` is PIECEWISE_LINEAR, `2` is POLYNOMIAL (MATPOWER manual Table B-4). A generator carrying neither key gets a zero natural-unit cost curve, matching PSCB's own fallback (and its warning). The resulting variable cost is `COMPONENT_BASE` per-unit — PSCB's -`CostCurve(_, IS.DU)` — never natural units, unlike the zero-cost fallback. +`CostCurve(_, IS.CU)` — never natural units, unlike the zero-cost fallback. """ function make_thermal_cost(gen_name::AbstractString, pm_gen::Dict, sys_mbase::Float64) if !haskey(pm_gen, "model") diff --git a/test/test_openapi_branch.jl b/test/test_openapi_branch.jl index f75fe60..8e7b95e 100644 --- a/test/test_openapi_branch.jl +++ b/test/test_openapi_branch.jl @@ -117,7 +117,7 @@ end @test PFP.get_value(circuit, :base_voltage_primary) == d["base_voltage_from"] @test PFP.get_value(circuit, :base_voltage_secondary) == d["base_voltage_to"] # rating is per-unit-on-system-base in the raw pm dict (PowerModels' generic branch - # correction) but the circuit's rating field is device-base pu (PSY.DU); this fixture + # correction) but the circuit's rating field is component-base pu (PSY.CU); this fixture # cannot distinguish the two bases (base_power == sys_mbase) — the synthetic cases in # test_openapi_transformer_discriminators.jl do. @test PFP.get_value(circuit, :rating) ≈ d["rate_a"] * d["base_power"]