Add costs for imported/exported power
This how-to guide explains how to add an ImportExportCost to a Source component to model imports and exports with neighboring areas or external grids.
This guide assumes a System is already defined.
Overview
A Source component represents an infinite bus with constant voltage output, commonly used to represent:
- Very large machines on a single bus in dynamics simulations
- Import/export connections in operational simulations
The ImportExportCost operating cost allows you to specify:
- Import offer curves (buy prices for importing power)
- Export offer curves (sell prices for exporting power)
- Weekly energy limits for imports and exports
- Ancillary service offers
Step 1: Define Import and Export Curves
You can define import and export curves in several ways, depending on your data format.
Option A: Simple Single-Price Curves
For a simple constant price over a power range:
julia> # Import curve: buy power at $25/MWh up to 200 MW import_curve = make_import_curve(; power = 200.0, price = 25.0)CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 25.0 for x in [0.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0julia> # Export curve: sell power at $30/MWh up to 200 MW export_curve = make_export_curve(; power = 200.0, price = 30.0)CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 30.0 for x in [0.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0
Option B: Piecewise Linear Curves
For more complex pricing with multiple segments:
julia> # Import curve with increasing prices as more power is imported import_curve = make_import_curve(; power = [0.0, 100.0, 105.0, 120.0, 200.0], price = [5.0, 10.0, 20.0, 40.0], )CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 5.0 for x in [0.0, 100.0) 10.0 for x in [100.0, 105.0) 20.0 for x in [105.0, 120.0) 40.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0julia> # Export curve with decreasing prices as more power is exported export_curve = make_export_curve(; power = [0.0, 100.0, 105.0, 120.0, 200.0], price = [40.0, 20.0, 10.0, 5.0], )CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 40.0 for x in [0.0, 100.0) 20.0 for x in [100.0, 105.0) 10.0 for x in [105.0, 120.0) 5.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0
- Import curves must have non-decreasing (convex) slopes
- Export curves must have non-increasing (concave) slopes
- Power values must have one more entry than price values
Step 2: Create the ImportExportCost
Use the curves to create an ImportExportCost:
julia> ie_cost = ImportExportCost(; import_offer_curves = import_curve, export_offer_curves = export_curve, energy_import_weekly_limit = 10000.0, # MWh per week (optional) energy_export_weekly_limit = 10000.0, # MWh per week (optional) )ImportExportCost: import_offer_curves: CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 5.0 for x in [0.0, 100.0) 10.0 for x in [100.0, 105.0) 20.0 for x in [105.0, 120.0) 40.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0 export_offer_curves: CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 40.0 for x in [0.0, 100.0) 20.0 for x in [100.0, 105.0) 10.0 for x in [105.0, 120.0) 5.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0 energy_import_weekly_limit: 10000.0 energy_export_weekly_limit: 10000.0 ancillary_service_offers: Service[]
Step 3: Add the Cost to the Source Component
Define a Source component with the import/export cost, or alternatively use set_operation_cost! to add the cost to an existing source:
julia> source = Source(; name = "external_grid", available = true, bus = get_component(ACBus, sys, "nodeC"), active_power = 0.0, reactive_power = 0.0, active_power_limits = (min = -200.0, max = 200.0), # Negative for export reactive_power_limits = (min = -100.0, max = 100.0), R_th = 0.01, X_th = 0.02, internal_voltage = 1.0, internal_angle = 0.0, base_power = 100.0, operation_cost = ie_cost, )Source: external_grid: name: external_grid available: true bus: ACBus: nodeC active_power: 0.0 reactive_power: 0.0 active_power_limits: (min = -20000.0, max = 20000.0) reactive_power_limits: (min = -10000.0, max = 10000.0) R_th: 0.01 X_th: 0.02 internal_voltage: 1.0 internal_angle: 0.0 base_power: 100.0 operation_cost: ImportExportCost composed of import_offer_curves: CostCurve{PiecewiseIncrementalCurve}, export_offer_curves: CostCurve{PiecewiseIncrementalCurve} dynamic_injector: nothing services: 0-element Vector{Service} ext: Dict{String, Any}() internal: InfrastructureSystems.InfrastructureSystemsInternal has_supplemental_attributes: false has_time_series: false
The active_power_limits should span negative (for export) to positive (for import) values. Negative power indicates exporting power to the external grid.
Step 4: Add the Source to the System
Add the source component to your system:
julia> add_component!(sys, source)Verify the source was added correctly:
julia> get_component(Source, sys, "external_grid")Source: external_grid: name: external_grid available: true bus: ACBus: nodeC active_power: 0.0 reactive_power: 0.0 active_power_limits: (min = -200.0, max = 200.0) reactive_power_limits: (min = -100.0, max = 100.0) R_th: 0.01 X_th: 0.02 internal_voltage: 1.0 internal_angle: 0.0 base_power: 100.0 operation_cost: ImportExportCost composed of import_offer_curves: CostCurve{PiecewiseIncrementalCurve}, export_offer_curves: CostCurve{PiecewiseIncrementalCurve} dynamic_injector: nothing services: 0-element Vector{Service} ext: Dict{String, Any}() InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: falsejulia> get_operation_cost(get_component(Source, sys, "external_grid"))ImportExportCost: import_offer_curves: CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 5.0 for x in [0.0, 100.0) 10.0 for x in [100.0, 105.0) 20.0 for x in [105.0, 120.0) 40.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0 export_offer_curves: CostCurve: value_curve: PiecewiseIncrementalCurve where value at zero is 0.0, initial value is 0.0, derivative function f is: f(x) = 40.0 for x in [0.0, 100.0) 20.0 for x in [100.0, 105.0) 10.0 for x in [105.0, 120.0) 5.0 for x in [120.0, 200.0) power_units: UnitSystem.NATURAL_UNITS = 2 vom_cost: LinearCurve (a type of InputOutputCurve) where function is: f(x) = 0.0 x + 0.0 energy_import_weekly_limit: 10000.0 energy_export_weekly_limit: 10000.0 ancillary_service_offers: Service[]
See Also
Source- Documentation for the Source componentImportExportCost- Documentation for ImportExportCost- Adding an Operating Cost - General guide for operating costs
make_import_curve- Function to create import curvesmake_export_curve- Function to create export curves