Add a Component in Natural Units

PowerSystems.jl has three per-unitization options for getting, setting and displaying data.

Currently, only one of these options – "DEVICE_BASE" – is supported when using a constructor function define a component. You can see an example of the default capabilities using "DEVICE_BASE" here.

We hope to add capability to define components in "NATURAL_UNITS" with constructors in the future, but for now, below is a workaround for users who prefer to define data using "NATURAL_UNITS" (e.g., MW, MVA, MVAR, or MW/min):

Step 1: Set Units Base

Set your (previously-defined) System's units base to "NATURAL_UNITS":

set_units_base_system!(system, "NATURAL_UNITS")
[ Info: Unit System changed to UnitSystem.NATURAL_UNITS = 2

Now, the "setter" functions have been switched to define data using natural units (MW, MVA, etc.), taking care of the necessary data conversions behind the scenes.

Step 2: Define Empty Component

Define an empty component with 0.0 or nothing for all the power-related fields except base_power, which is always in MVA.

For example:

gas1 = ThermalStandard(;
    name = "gas1",
    available = true,
    status = true,
    bus = get_component(ACBus, system, "Cobb"), # Attach to a previously-defined bus named Cobb
    active_power = 0.0,
    reactive_power = 0.0,
    rating = 0.0,
    active_power_limits = (min = 0.0, max = 0.0),
    reactive_power_limits = nothing,
    ramp_limits = nothing,
    operation_cost = ThermalGenerationCost(nothing),
    base_power = 30.0, # MVA
    time_limits = (up = 8.0, down = 8.0), # Hours, unaffected by per-unitization
    must_run = false,
    prime_mover_type = PrimeMovers.CC,
    fuel = ThermalFuels.NATURAL_GAS,
);

ThermalStandard: gas1:
   name: gas1
   available: true
   status: true
   bus: ACBus: Cobb
   active_power: 0.0
   reactive_power: 0.0
   rating: 0.0
   active_power_limits: (min = 0.0, max = 0.0)
   reactive_power_limits: nothing
   ramp_limits: nothing
   operation_cost: ThermalGenerationCost composed of variable: CostCurve{LinearCurve}
   base_power: 30.0
   time_limits: (up = 8.0, down = 8.0)
   must_run: false
   prime_mover_type: PrimeMovers.CC = 4
   fuel: ThermalFuels.NATURAL_GAS = 18
   services: 0-element Vector{Service}
   time_at_status: 10000.0
   dynamic_injector: nothing
   ext: Dict{String, Any}()
   internal: InfrastructureSystems.InfrastructureSystemsInternal
   has_supplemental_attributes: false
   has_time_series: false

Step 3: Attach the Component

Attach the component to your System:

add_component!(system, gas1)

Step 4: Add Data with "setter" Functions

Use individual "setter" functions to set each the value of each numeric field in natural units:

set_rating!(gas1, 30.0) #MVA
set_active_power_limits!(gas1, (min = 6.0, max = 30.0)) # MW
set_reactive_power_limits!(gas1, (min = 6.0, max = 30.0)) # MVAR
set_ramp_limits!(gas1, (up = 6.0, down = 6.0)) #MW/min
(up = 0.2, down = 0.2)

Notice the return values are divided by the base_power of 30 MW, showing the setters have done the per-unit conversion into "DEVICE_BASE" behind the scenes.

Tip

Steps 2-4 can be called within a for loop to define many components at once (or step 3 can be replaced with add_components! to add all components at once).

See Also