Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
994084e
Fold PowerSystemsUnits into PSY with unit-aware accessors
luke-kiernan Apr 17, 2026
053031b
Regenerate structs against new IS template
luke-kiernan Apr 17, 2026
0a4165a
Require explicit units on getters/setters; drop DU/SU/NU duplicates
luke-kiernan Apr 17, 2026
2ac8529
Regenerate structs: 2-arg-only getters + display_units_arg trait
luke-kiernan Apr 17, 2026
4703e42
Make base_power accessors unit-aware, move raw anchor to _get_base_power
luke-kiernan Apr 21, 2026
6a6e7ef
Regenerate structs: base_power emits _get_base_power
luke-kiernan Apr 21, 2026
85eca70
Parameterize OperationalCost subtypes on unit-system type
luke-kiernan Apr 24, 2026
de54885
Pin InfrastructureSystems to lk/units-domain-agnostic-is4 via [sources]
luke-kiernan Apr 24, 2026
a245312
Generate `_unitful` companion getters; bare `get_X` returns Float64
luke-kiernan May 6, 2026
58dfa7f
Merge psy6: switch JSON3→JSON, rewrite unit tests off PSB
luke-kiernan May 8, 2026
7143e72
non-attached component and printing
luke-kiernan May 18, 2026
012aa03
Port unit-aware base_power setters; rename org to Sienna-Platform
luke-kiernan May 21, 2026
304c1f5
Fix DimensionError in total_load_rating
luke-kiernan May 26, 2026
a182688
Add test coverage for total_load_rating
luke-kiernan May 26, 2026
6bf10cc
Handle Nothing in ImportExportCost kw constructor
luke-kiernan May 27, 2026
54aa3a6
Require units on series_susceptance/admittance accessors
luke-kiernan May 27, 2026
ff930c3
Test unit-aware get_base_power_{12,23,13} on Transformer3W
luke-kiernan May 27, 2026
93c99a5
Route 3W transformer getters through convert_units; add regression test
luke-kiernan May 27, 2026
6ae7c0d
review issues
jd-lara May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e"
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[sources]
InfrastructureSystems = {url = "https://github.com/Sienna-Platform/InfrastructureSystems.jl.git", rev = "lk/units-domain-agnostic-is4"}

[compat]
DataStructures = "^0.19"
Expand All @@ -24,9 +29,8 @@ JSON = "^1.5"
LinearAlgebra = "1"
Logging = "1"
PrettyTables = "3.1"
StructTypes = "^1.9"
TimeSeries = "0.25"
Unicode = "1"
Unitful = "^1.12"
julia = "^1.10"

[sources]
InfrastructureSystems = {url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl", rev = "IS4"}
40 changes: 39 additions & 1 deletion src/PowerSystems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,6 @@ export set_name!
export get_component_uuids
export get_description
export set_description!
export get_base_power
export get_frequency
export get_frequency_droop
export set_units_base_system!
Expand Down Expand Up @@ -591,6 +590,20 @@ export StructDefinition
export generate_struct_file
export generate_struct_files
export UnitSystem # internal.jl
# Unit types for explicit units in getters/setters
export MW, Mvar, MVA, kV, OHMS, SIEMENS
export DU, SU, NU, DeviceBaseUnit, SystemBaseUnit, NaturalUnit
export AbstractRelativeUnit, RelativeQuantity
export UnitCategory,
PowerCategory, ImpedanceCategory, AdmittanceCategory,
VoltageCategory, CurrentCategory
export POWER, IMPEDANCE, ADMITTANCE, VOLTAGE, CURRENT
export natural_unit, base_value, system_base_value, convert_units, DEFAULT_UNITS
export ustrip
# Hand-written unit-bearing companions for `exclude_getter` descriptor entries
# (their bare-number counterparts get exported via generated/includes.jl).
export get_base_power_unitful
export get_base_power_12_unitful, get_base_power_23_unitful, get_base_power_13_unitful

# ComponentSelector
export ComponentSelector
Expand Down Expand Up @@ -623,6 +636,22 @@ import DataStructures: OrderedDict, SortedDict
import JSON
import Base.to_index
import PrettyTables
import Unitful
using Unitful: @u_str, @unit, Quantity, Units, uconvert
import StructTypes

# Relative-unit primitives live in IS; PSY re-exports them for downstream
# packages so that `PSY.DU`, `PSY.RelativeQuantity`, etc. keep working.
import InfrastructureSystems:
AbstractRelativeUnit,
DeviceBaseUnit,
SystemBaseUnit,
NaturalUnit,
RelativeQuantity,
DU,
SU,
NU,
ustrip

# Import InfrastructureSystems both as full module name (needed for internal macros like @forward)
# and with alias for convenient usage throughout the codebase
Expand Down Expand Up @@ -819,6 +848,11 @@ include("utils/logging.jl")
include("utils/IO/base_checks.jl")
include("utils/generate_struct_files.jl")

# Units machinery (formerly PowerSystemsUnits.jl)
include("units/types.jl")
include("units/conversions.jl")
include("units/serialization.jl")

include("definitions.jl")
include("models/static_models.jl")
include("models/dynamic_models.jl")
Expand Down Expand Up @@ -922,4 +956,8 @@ precompile(
(TupleTimeSeries{StartUpStages}, ThermalStandard, Dates.DateTime),
)

function __init__()
Unitful.register(PowerSystems)
end

end # module
61 changes: 42 additions & 19 deletions src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,24 @@ Return a user-modifiable dictionary to store extra information.
get_ext(sys::System) = IS.get_ext(sys.internal)

"""
Return the system's base power.
Unitless system base power (MVA) — internal anchor for unit conversion.
"""
get_base_power(sys::System) = sys.units_settings.base_value
_get_base_power(sys::System) = sys.units_settings.base_value

"""
Return the system's base power as a bare number in the requested units (e.g.
`NU`, `MW`, `SU`). For the unit-bearing value see [`get_base_power_unitful`](@ref).
"""
get_base_power(sys::System, u) = IS._strip_units(get_base_power_unitful(sys, u))

"""
Return the system's base power as a unit-bearing quantity. See
[`get_base_power`](@ref) for a bare number.
"""
get_base_power_unitful(sys::System, ::NaturalUnit) = _get_base_power(sys) * MVA
get_base_power_unitful(sys::System, u::Unitful.Units) =
Unitful.uconvert(u, _get_base_power(sys) * MVA)
get_base_power_unitful(sys::System, ::SystemBaseUnit) = 1.0 * SU

"""
Return the system's frequency.
Expand Down Expand Up @@ -521,15 +536,18 @@ function get_units_base(system::System)
end

"""
A "context manager" that sets the [`System`](@ref)'s [units base](@ref per_unit) to the
given value, executes the function, then sets the units base back.
A "context manager" that temporarily sets the [`System`](@ref)'s [units base](@ref per_unit)
setting to the given value, executes the function, then restores the previous setting.

Note that the unit-aware getters and setters take their units explicitly (e.g.
`get_active_power(gen, NU)`); this setting only affects code that reads the system's
configured units base via [`get_units_base`](@ref).

# Examples
```julia
active_power_mw = with_units_base(sys, UnitSystem.NATURAL_UNITS) do
get_active_power(gen)
with_units_base(sys, UnitSystem.NATURAL_UNITS) do
get_units_base(sys) # "NATURAL_UNITS" within the block; restored afterward
end
# now active_power_mw is in natural units no matter what units base the system is in
```
"""
function with_units_base(f::Function, sys::System, units::Union{UnitSystem, String})
Expand All @@ -556,15 +574,18 @@ function _set_units_base!(c::Component, settings::UnitSystem)
end

"""
A "context manager" that sets the [`Component`](@ref)'s [units base](@ref per_unit) to the
given value, executes the function, then sets the units base back.
A "context manager" that temporarily sets the [`Component`](@ref)'s [units base](@ref per_unit)
setting to the given value, executes the function, then restores the previous setting.

Note that the unit-aware getters and setters take their units explicitly (e.g.
`get_active_power(component, NU)`); this setting only affects code that reads the
component's configured units base.

# Examples
```julia
active_power_mw = with_units_base(component, UnitSystem.NATURAL_UNITS) do
get_active_power(component)
with_units_base(component, UnitSystem.NATURAL_UNITS) do
get_units_setting(component) # carries NATURAL_UNITS within the block; restored afterward
end
# now active_power_mw is in natural units no matter what units base the system is in
```
"""
function with_units_base(f::Function, c::Component, units::Union{UnitSystem, String})
Expand Down Expand Up @@ -2341,7 +2362,7 @@ Returns `true` if all values are valid, `false` otherwise.
"""
function check_ac_transmission_rate_values(sys::System)
is_valid = true
base_power = get_base_power(sys)
base_power = _get_base_power(sys)
for line in
Iterators.flatten((get_components(Line, sys), get_components(MonitoredLine, sys)))
if !check_rating_values(line, base_power)
Expand Down Expand Up @@ -2942,7 +2963,7 @@ end

function handle_component_addition!(sys::System, dyn_injector::DynamicInjection; kwargs...)
static_injector = kwargs[:static_injector]
static_base_power = get_base_power(static_injector)
static_base_power = _get_base_power(static_injector)
set_base_power!(dyn_injector, static_base_power)
set_dynamic_injector!(static_injector, dyn_injector)
return
Expand Down Expand Up @@ -3261,15 +3282,17 @@ function convert_component!(
new_type::Type{StandardLoad};
kwargs...,
)
# Raw device-base values: struct fields are stored in device base (Float64);
# we copy the underlying field directly to avoid SU-conversion round-tripping.
new_load = new_type(;
name = get_name(old_load),
available = get_available(old_load),
bus = get_bus(old_load),
base_power = get_base_power(old_load),
constant_active_power = get_active_power(old_load),
constant_reactive_power = get_reactive_power(old_load),
max_constant_active_power = get_max_active_power(old_load),
max_constant_reactive_power = get_max_active_power(old_load),
base_power = _get_base_power(old_load),
constant_active_power = old_load.active_power,
constant_reactive_power = old_load.reactive_power,
max_constant_active_power = old_load.max_active_power,
max_constant_reactive_power = old_load.max_active_power,
conformity = get_conformity(old_load),
dynamic_injector = get_dynamic_injector(old_load),
internal = _copy_internal_for_conversion(old_load),
Expand Down
6 changes: 6 additions & 0 deletions src/definitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ const POWER_SYSTEM_STRUCT_DESCRIPTOR_FILE =

const DEFAULT_SYSTEM_FREQUENCY = 60.0

const DEFAULT_BASE_MVA = 100.0
# Accumulator type for MW-sum helpers in system_checks.jl. Bare `Float64`
# because unit-aware getters now return bare numbers; the `MW` arg merely
# selects the unit basis (see `_sum_or_zero`).
const MW_ACCUMULATOR_TYPE = Float64

const INFINITE_TIME = 1e4
const START_COST = 1e8
const INFINITE_COST = 1e8
Expand Down
Loading
Loading