Unitful getters and setters#1659
Conversation
- Vendor PSU sources into src/units/ (types, conversions, serialization).
RelativeQuantity, AbstractRelativeUnit, DU/SU/NU, convert_units, and
serialize_quantity now live in PSY.
- Replace stateful unit-system with explicit-units API on Component:
get_value / set_value use Val-dispatch through _convert_from_device_base
for type-stable conversions (DU/SU/MW/Mvar/Ω/S/Float64 targets).
- DEFAULT_UNITS = SU; Float64 fast path for hot loops returns raw
system-base numbers without the wrapper.
- Add MW_ACCUMULATOR_TYPE for _sum_or_zero helpers in system_checks;
total_capacity_rating / total_load_rating return MW-typed Unitful.Quantity.
- Fix convert_component! (PowerLoad → StandardLoad) to copy raw device-base
fields instead of round-tripping through SU getters.
- Add supplemental 2-arg get_max_active_power overloads for StaticInjection
and Union{StandardLoad, InterruptibleStandardLoad}.
- Drop PowerSystemsUnits dep; add Unitful and StructTypes deps.
- Port PSU test suite as test/test_units.jl (77 tests).
- Update tests for unit-bearing return types: test_accessors uses
_unwrap_units helper; test_printing unwraps units for occursin check;
with_units_base testsets replaced by explicit-units API tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated files now emit: get_<field>(value::T) = get_value(value, Val(:<field>), Val(:<cu>)) get_<field>(value::T, units) = get_value(value, Val(:<field>), Val(:<cu>), units) and matching set_<field>!(value::T, val) lines, replacing the previous stateful-unit-system accessors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Template-generated 1-arg getters are gone; all unit-bearing getters now take an explicit units argument. Bare `set_foo!(c, 0.5)` errors rather than silently treating the value as system base. - DU/SU/NU and RelativeQuantity now come from IS; this package imports and re-exports them, and trims src/units/types.jl to the power-specific Unitful @Units (Mvar, MVA) and the UnitArg alias. - Supplemental, HybridSystem, DynamicBranch, and generation.jl getters migrated to 2-arg. print.jl `_show_accessor_value` uses the new IS.display_units_arg trait for display-units selection. - Tests updated accordingly; relative-unit primitive tests moved to IS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regenerated against the updated IS template: unit-bearing structs now emit
only the 2-arg getter plus a `display_units_arg(::typeof(f), ::Type{T}) = SU`
line per field, keyed on both function and struct to avoid spooky
cross-type aliasing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Public get_base_power(c, units) dispatches on NU/MW/SU/DU/Float64. The unitless accessor is now _get_base_power (used as the internal conversion anchor). Same pattern for System and for ThreeWindingTransformer winding base powers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Descriptor now marks base_power fields exclude_getter=true so the generator emits an internal _get_ accessor; the public unit-aware getter is hand-written. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow the IS CostCurve/FuelCurve parameterization: add U<:AbstractUnitSystem
as a type parameter on ImportExportCost, MarketBidCost, ReserveDemandCurve,
ImportExportTimeSeriesCost, MarketBidTimeSeriesCost, and
ReserveDemandTimeSeriesCurve. Struct fields pin the contained CostCurve to
that shared U; constructors infer and validate.
Replace UnitSystem enum usage in cost-function code with AbstractUnitSystem
type instances (NaturalUnit(), SystemBaseUnit(), DeviceBaseUnit()). The
system-level UnitSystem enum in base.jl / SystemUnitsSettings is left
untouched.
AnyCostCurve{T} alias is used only at isa checks, where existential form is
structurally required.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
So the IS branch this PSY work depends on is discoverable from the repo rather than requiring local Pkg.develop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7ff28de to
de54885
Compare
|
Change Does mean for get-then-set you need: |
Bare `get_active_power(g, NU)` now returns `100.0`;
`get_active_power_unitful(g, NU)` returns `100.0 MW`. Same pattern for
hand-written `get_base_power` and `get_base_power_{12,23,13}`.
Drop the redundant `Float64` getter dispatch path. Add NU dispatch to
`_convert_from_device_base` for `:mva`/`:ohm`/`:siemens`. Add
`IS._strip_units(::Unitful.Quantity)` so IS stays Unitful-free.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PSB on `psy6` calls 1-arg `get_base_power(::ThermalStandard)` (removed on this branch), so the four new unit-aware testsets build their fixtures manually via `_sys_with_thermal` instead of `PSB.build_system`. Migrate `units/serialization.jl` and `test_units.jl` from JSON3 to JSON to match the dropped dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I plan to address #1128 (and am looking for some input on that issue), but otherwise this PR is pretty much finished. The one other detail that comes to mind: once I go make a compatible branch of PSB, I could also reset those 3 unit tests to using PSB systems. (I currently have them using tiny systems we build here so that the tests pass.) |
`set_base_power!` (and `set_base_power_{12,23,13}!`) now accept Float64,
Unitful.Quantity (power), and RelativeQuantity{_,SystemBaseUnit}; passing
a DeviceBaseUnit value errors since device base is 1.0 pu of itself.
Marks the 35 base_power fields as `exclude_setter` in the descriptor,
regenerates structs, hand-writes the setters in components.jl, and drops
redundant overrides on HybridSystem/DynamicGenerator/DynamicInverter
that would otherwise produce dispatch ambiguities. Adds tests covering
all paths.
Also updates the InfrastructureSystems [sources] URL from NREL-Sienna
to Sienna-Platform to match the GitHub org rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fa/sa branches multiplied by `MW` while sl and the empty-case zeros are bare `Float64`. Drop the `* MW` to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers the empty-system path and a mixed PowerLoad + FixedAdmittance system. The admittance branches were previously untested, which is how the DimensionError fixed in 304c1f5 reached main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
As I'm working my way through all the downstream packages, I'm finding a few more things here and there. I'll post the linked PRs here: |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code reviewFound 2 issues:
PowerSystems.jl/src/models/supplemental_accessors.jl Lines 311 to 334 in 93c99a5
Lines 543 to 548 in 93c99a5 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Resolve InterconnectingConverter generated-struct conflict (keep reactive_power_limits). Pin InfrastructureSystems [sources] to IS4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add HybridSystem to the descriptor-driven struct generator (power_system_structs.json) and regenerate it into src/models/generated/HybridSystem.jl, replacing the hand-written src/models/HybridSystem.jl. This brings HybridSystem in line with the post-#1659 Unitful accessor convention: unit-bearing getters now strip units and gain _unitful companions + display_units_arg traits, and setters route through set_value. It also fixes two latent bugs that the generated code no longer reproduces: set_interconnection_efficiency! wrote the wrong field, and three subcomponent setters referenced an undefined `value`. The non-generatable custom methods move to the supplemental files: guarded subcomponent setters + _raise_if_attached_to_system into supplemental_setters.jl; get_subcomponents, _get_components, and set_units_setting! into supplemental_accessors.jl. The (::Nothing) constructor still yields concrete subcomponent nulls while the keyword constructor defaults them to nothing, via the null_value/default split. Tests: test_hybrid_system, test_subsystems, test_serialization pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Add unitful getters and setters. Interface:
Relies on IS #574. This will break stuff downstream left and right, yes, but here's why I think it's worth it:
SYSTEM_BASE.edit: at the moment, turns out 1-argument getters/setters assume system base. But I'd prefer to have them error: better that than get silently incorrect results. Also, I'd consider adding a 3-arg setter, with the units separate:
set_active_power!(gen, 1, {DU/SU/NU}edit: You can still mess up by adding or comparing device-base quantities from components with different base powers. Same goes for comparing
SUquantities between different systems.