Skip to content

Commit ac0a147

Browse files
authored
Merge pull request #1232 from NREL-Sienna/gks/with_units_base
Add `with_units_base` feature
2 parents 774695e + 6e8e2f2 commit ac0a147

File tree

5 files changed

+70
-12
lines changed

5 files changed

+70
-12
lines changed

docs/src/explanation/per_unit.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ These three unit bases allow easy conversion between unit systems.
1919
This allows `PowerSystems.jl` users to input data in the formats they have available,
2020
as well as view data in the unit system that is most intuitive to them.
2121

22-
You can get and set the unit system setting of a `System` with [`get_units_base`](@ref)
23-
and [`set_units_base_system!`](@ref).
22+
You can get and set the unit system setting of a `System` with [`get_units_base`](@ref) and
23+
[`set_units_base_system!`](@ref). To support a less stateful style of programming,
24+
`PowerSystems.jl` provides the `Logging.with_logger`-inspired "context manager"-type
25+
function [`with_units_base`](@ref), which sets the unit system to a particular value,
26+
performs some action, then automatically sets the unit system back to its previous value.
2427

2528
Conversion between unit systems does not change
2629
the stored parameter values. Instead, unit system conversions are made when accessing

docs/src/tutorials/creating_system.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ get_rating(retrieved_component)
357357
See that now the data is now 1.0 (5.0 MVA per-unitized by the generator (i.e., the device's)
358358
`base_power` of 5.0 MVA), which is the format we used to originally define the device.
359359

360+
As a shortcut to temporarily set the `System`'s unit system to a particular value, perform
361+
some action, and then automatically set it back to what it was before, we can use
362+
`with_units_base` and a [`do` block](https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments):
363+
364+
```@repl basics
365+
with_units_base(sys, "NATURAL_UNITS") do
366+
# Everything inside this block will run as if the unit system were NATURAL_UNITS
367+
get_rating(retrieved_component)
368+
end
369+
get_units_base(sys) # Unit system goes back to previous value when the block ends
370+
```
371+
360372
Recall that if you ever need to check a `System`'s settings, including the unit system being
361373
used by all the getter functions, you can always just print the `System`:
362374

src/PowerSystems.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ export set_description!
416416
export get_base_power
417417
export get_frequency
418418
export set_units_base_system!
419+
export with_units_base
419420
export to_json
420421
export from_json
421422
export serialize

src/base.jl

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -513,27 +513,61 @@ function set_units_setting!(
513513
return
514514
end
515515

516+
function _set_units_base!(system::System, settings::UnitSystem)
517+
to_change = (system.units_settings.unit_system != settings)
518+
to_change && (system.units_settings.unit_system = settings)
519+
return (to_change, settings)
520+
end
521+
522+
_set_units_base!(system::System, settings::String) =
523+
_set_units_base!(system::System, UNIT_SYSTEM_MAPPING[uppercase(settings)])
524+
516525
"""
517526
Sets the units base for the getter functions on the devices. It modifies the behavior of all getter functions
527+
528+
# Examples
529+
```julia
530+
set_units_base_system!(sys, "NATURAL_UNITS")
531+
```
532+
```julia
533+
set_units_base_system!(sys, UnitSystem.SYSTEM_BASE)
534+
```
518535
"""
519-
function set_units_base_system!(system::System, settings::String)
520-
set_units_base_system!(system::System, UNIT_SYSTEM_MAPPING[uppercase(settings)])
536+
function set_units_base_system!(system::System, units::Union{UnitSystem, String})
537+
changed, new_units = _set_units_base!(system::System, units)
538+
changed && @info "Unit System changed to $new_units"
521539
return
522540
end
523541

524-
function set_units_base_system!(system::System, settings::UnitSystem)
525-
if system.units_settings.unit_system != settings
526-
system.units_settings.unit_system = settings
527-
@info "Unit System changed to $settings"
528-
end
529-
return
530-
end
542+
_get_units_base(system::System) = system.units_settings.unit_system
531543

532544
"""
533545
Get the system's [unit base](@ref per_unit))
534546
"""
535547
function get_units_base(system::System)
536-
return string(system.units_settings.unit_system)
548+
return string(_get_units_base(system))
549+
end
550+
551+
"""
552+
A "context manager" that sets the [`System`](@ref)'s [units base](@ref per_unit) to the
553+
given value, executes the function, then sets the units base back.
554+
555+
# Examples
556+
```julia
557+
active_power_mw = with_units_base(sys, UnitSystem.NATURAL_UNITS) do
558+
get_active_power(gen)
559+
end
560+
# now active_power_mw is in natural units no matter what units base the system is in
561+
```
562+
"""
563+
function with_units_base(f::Function, sys::System, units::Union{UnitSystem, String})
564+
old_units = _get_units_base(sys)
565+
_set_units_base!(sys, units)
566+
try
567+
f()
568+
finally
569+
_set_units_base!(sys, old_units)
570+
end
537571
end
538572

539573
function get_units_setting(component::T) where {T <: Component}

test/test_system.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ end
215215
@test get_units_base(sys) == "DEVICE_BASE"
216216
set_units_base_system!(sys, "SYSTEM_BASE")
217217
@test get_units_base(sys) == "SYSTEM_BASE"
218+
219+
gen = get_component(ThermalStandard, sys, "322_CT_6")
220+
active_power_mw = with_units_base(sys, UnitSystem.NATURAL_UNITS) do
221+
get_active_power(gen)
222+
end
223+
@test get_units_base(sys) == "SYSTEM_BASE"
224+
set_units_base_system!(sys, UnitSystem.NATURAL_UNITS)
225+
@test active_power_mw == get_active_power(gen)
218226
end
219227

220228
@testset "Test add_time_series multiple components" begin

0 commit comments

Comments
 (0)