markets module
A model of the NEM spot market dispatch process.
Overview
The market, both in real life and in this model, is implemented as a linear program. Linear programs consist of three elements:
Decision variables: the quantities being optimised for. In an electricity market these will be things like the outputs of generators, the consumption of dispatchable loads and interconnector flows.
An objective function: the linear function being optimised. In this model of the spot market the cost of production is being minimised, and is defined as the sum of each bids dispatch level multiplied by the bid price.
A set of linear constraints: used to implement market features such as network constraints and interconnectors.
The class nempy.SpotMarket
is used to construct these elements and then solve the linear program to calculate
dispatch and pricing. The examples below give an overview of how method calls build the linear program.
Initialising the market instance, doesn’t create any part of the linear program, just saves general information for later use.
market = markets.SpotMarket(unit_info=unit_info, market_regions=['NSW'])
Providing volume bids creates a set of n decision variables, where n is the number of bids with a volume greater than zero.
market.set_unit_volume_bids(volume_bids)
Providing price bids creates the objective function, i.e. units will be dispatch to minimise cost, as determined by the bid prices.
market.set_unit_price_bids(price_bids)
Providing unit capacities creates a constraint for each unit that caps its total dispatch at a set capacity
market.set_unit_bid_capacity_constraints(unit_limits)
Providing regional energy demand creates a constraint for each region that forces supply from units and interconnectors to equal demand
market.set_demand_constraints(demand)
Specific examples for using this class are provided on the `examples1`_ page, detailed documentation of the class
nempy.markets.SpotMarket
is provided in the Reference material below.
Reference
Classes:
|
Class for constructing and dispatching the spot market on an interval basis. |
Exceptions:
Raise for building model components in wrong order. |
|
Raise for trying to access missing table. |
- class nempy.markets.SpotMarket(market_regions, unit_info, dispatch_interval=5)
Class for constructing and dispatching the spot market on an interval basis.
Examples
Define the unit information data needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
The units are given a default dispatch_type and loss_factor. Note this data is stored in a private method and not intended for public use.
>>> market._unit_info unit region dispatch_type loss_factor 0 A NSW generator 1.0 1 B NSW generator 1.0
- Parameters:
market_regions (list[str]) – The market regions, used to validate inputs.
unit_info (pd.DataFrame) –
Information on a unit basis, not all columns are required.
Columns:
Description:
unit
unique identifier of a dispatch unit, (as str)
region
location of unit, required (as str)
loss_factor
marginal, average or combined loss factors,
optional, (as np.int64)
dispatch_type
”load” or “generator”, optional, (as str)
dispatch_interval (int) – The length of the dispatch interval in minutes, used for interpreting ramp rates.
- solver_name
The solver to use must be one of solver options of the mip-python package that is used to interface to solvers. Currently the only support solvers are CBC and Gurobi, so allowed solver names are ‘CBC’ and ‘GUROBI’. Default value is CBC, CBC works out of the box after installing Nempy, but Gurobi must be installed separately.
- Type:
str
- Raises:
RepeatedRowError – If there is more than one row for any ‘unit’.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If the column ‘units’ or ‘regions’ is missing.
UnexpectedColumn – There is a column that is not ‘units’, ‘regions’, ‘dispatch_type’ or ‘loss_factor’.
ColumnValues – If there are inf, null or negative values in the ‘loss_factor’ column.
Methods:
set_unit_volume_bids
(volume_bids)Creates the decision variables corresponding to unit bids.
set_unit_price_bids
(price_bids)Creates the objective function costs corresponding to energy bids.
set_unit_bid_capacity_constraints
(unit_limits)Creates constraints that limit unit output based on their bid in max capacity.
set_unconstrained_intermitent_generation_forecast_constraint
(...)Creates constraints that limit unit output based on their forecast output.
set_unit_ramp_up_constraints
(ramp_details)Creates constraints on unit output based on ramp up rate.
set_unit_ramp_down_constraints
(ramp_details)Creates constraints on unit output based on ramp down rate.
set_fast_start_constraints
(fast_start_profiles)Create the constraints on fast start units dispatch,
see AEMO doc
set_demand_constraints
(demand)Creates constraints that force supply to equal to demand.
Creates constraints that force FCAS supply to equal requirements.
set_fcas_max_availability
(fcas_max_availability)Creates constraints to ensure fcas dispatch is limited to the availability specified in the FCAS trapezium.
Create constraints that ensure the provision of energy and fcas raise are within unit ramping capabilities.
Create constraints that ensure the provision of energy and fcas are within unit ramping capabilities.
Creates constraints to ensure there is adequate capacity for contingency, regulation and energy dispatch.
Creates constraints to ensure there is adequate capacity for regulation and energy dispatch targets.
set_interconnectors
(...)Create lossless links between specified regions.
set_interconnector_losses
(loss_functions, ...)Creates linearised loss functions for interconnectors.
Creates a set of generic constraints, adding the constraint type, rhs.
Set the lhs coefficients of generic constraints on unit basis.
Set the lhs coefficients of generic constraints on region basis.
Set the lhs coefficients of generic constraints on an interconnector basis.
make_constraints_elastic
(constraints_key, ...)Make a set of constraints elastic, so they can be violated at a predefined cost.
Creates a cost that attempts to balance the energy dispatch of equally priced bids within a region.
dispatch
([energy_market_ceiling_price, ...])Combines the elements of the linear program and solves to find optimal dispatch.
Retrieves the energy dispatch for each unit.
Retrieves the energy price in each market region.
Retrives the price associated with each set of FCAS requirement constraints.
Retrieves the flows for each interconnector.
Calculates a dispatch summary at the regional level.
Get the availability of fcas service on a unit level, after constraints.
- set_unit_volume_bids(volume_bids)
Creates the decision variables corresponding to unit bids.
Variables are created by reserving a variable id (as int) for each bid. Bids with a volume of 0 MW do not have a variable created. The lower bound of the variables are set to zero and the upper bound to the bid volume, the variable type is set to continuous. If a no services is specified for the bids they are given the default service value of energy is used.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 0.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
The market should now have the variables.
>>> print(market._decision_variables['bids']) unit capacity_band service variable_id lower_bound upper_bound type 0 A 1 energy 0 0.0 20.0 continuous 1 A 2 energy 1 0.0 20.0 continuous 2 A 3 energy 2 0.0 5.0 continuous 3 B 1 energy 3 0.0 50.0 continuous 4 B 2 energy 4 0.0 30.0 continuous
A mapping of these variables to constraints acting on that unit and service should also exist.
>>> print(market._variable_to_constraint_map['unit_level']['bids']) variable_id unit service coefficient 0 0 A energy 1.0 1 1 A energy 1.0 2 2 A energy 1.0 3 3 B energy 1.0 4 4 B energy 1.0
A mapping of these variables to constraints acting on the units region and service should also exist.
>>> print(market._variable_to_constraint_map['regional']['bids']) variable_id region service coefficient 0 0 NSW energy 1.0 1 1 NSW energy 1.0 2 2 NSW energy 1.0 3 3 NSW energy 1.0 4 4 NSW energy 1.0
- Parameters:
volume_bids (pd.DataFrame) –
Bids by unit, in MW, can contain up to 10 bid bands, these should be labeled ‘1’ to ‘10’.
Columns:
Description:
unit
unique identifier of a dispatch unit (as str)
service
the service being provided, optional,
default ‘energy’, (as str)
1
bid volume in the 1st band, in MW,
(as np.float64)
2
bid volume in the 2nd band, in MW, optional,
(as np.float64)
:
10
bid volume in the nth band, in MW, optional,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit and service combination.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If the column ‘units’ is missing or there are no bid bands.
UnexpectedColumn – There is a column that is not ‘unit’, ‘service’ or ‘1’ to ‘10’.
ColumnValues – If there are inf, null or negative values in the bid band columns.
- set_unit_price_bids(price_bids)
Creates the objective function costs corresponding to energy bids.
If no loss factors have been provided as part of the unit information when the model was initialised then the costs in the objective function are as bid. If loss factors are provided then the bid costs are referred to the regional reference node by dividing by the loss factor.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids. Bids for each unit need to be monotonically increasing.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [50.0, 100.0], ... '2': [100.0, 130.0], ... '3': [100.0, 150.0]})
Create the objective function components corresponding to the energy bids.
>>> market.set_unit_price_bids(price_bids)
The variable assocaited with each bid should now have a cost.
>>> print(market._objective_function_components['bids']) variable_id unit service capacity_band cost 0 0 A energy 1 50.0 1 1 A energy 2 100.0 2 2 A energy 3 100.0 3 3 B energy 1 100.0 4 4 B energy 2 130.0 5 5 B energy 3 150.0
- Parameters:
price_bids (pd.DataFrame) –
Bids by unit, in $/MW, can contain up to 10 bid bands.
Columns:
Description:
unit
unique identifier of a dispatch unit (as str)
service
the service being provided, optional, default ‘energy’, (as str)
1
bid price in the 1st band, in $/MW,
(as np.float64)
2
bid price in the 2nd band, in $/MW, optional,
(as np.float64)
:
10
bid price in the nth band, in $/MW, optional,
(as np.float64)
- Return type:
None
- Raises:
ModelBuildError – If the volume bids have not been set yet.
RepeatedRowError – If there is more than one row for any unit and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘units’ is missing or there are no bid bands.
UnexpectedColumn – There is a column that is not ‘units’, ‘region’ or ‘1’ to ‘10’.
ColumnValues – If there are inf, -inf or null values in the bid band columns.
BidsNotMonotonicIncreasing – If the bids band price for all units are not monotonic increasing.
- set_unit_bid_capacity_constraints(unit_limits)
Creates constraints that limit unit output based on their bid in max capacity. If a unit bids in zero volume then a constraint is not created.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of unit capacities.
>>> unit_limits = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'capacity': [60.0, 100.0]})
Create unit capacity based constraints.
>>> market.set_unit_bid_capacity_constraints(unit_limits)
The market should now have a set of constraints.
>>> print(market._constraints_rhs_and_type['unit_bid_capacity']) unit service constraint_id type rhs 0 A energy 0 <= 60.0 1 B energy 1 <= 100.0
… and a mapping of those constraints to the variable types on the lhs.
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['unit_bid_capacity']) constraint_id unit service coefficient 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
unit_limits (pd.DataFrame) –
Capacity by unit.
Columns:
Description:
unit
unique identifier of a dispatch unit (as str)
capacity
The maximum output of the unit if unconstrained
by ramp rate, in MW (as np.float64)
- Return type:
None
- Raises:
ModelBuildError – If the volume bids have not been set yet.
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the required types.
MissingColumnError – If the column ‘units’ or ‘capacity’ is missing.
UnexpectedColumn – There is a column that is not ‘units’ or ‘capacity’.
ColumnValues – If there are inf, null or negative values in the bid band columns.
- set_unconstrained_intermitent_generation_forecast_constraint(unit_limits)
Creates constraints that limit unit output based on their forecast output.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of unit forecast capacities.
>>> unit_limits = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'capacity': [60.0, 100.0]})
Create unit capacity based constraints.
>>> market.set_unconstrained_intermitent_generation_forecast_constraint(unit_limits)
The market should now have a set of constraints.
>>> print(market._constraints_rhs_and_type['uigf_capacity']) unit service constraint_id type rhs 0 A energy 0 <= 60.0 1 B energy 1 <= 100.0
… and a mapping of those constraints to the variable types on the lhs.
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['uigf_capacity']) constraint_id unit service coefficient 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
unit_limits (pd.DataFrame) –
Capacity by unit.
Columns:
Description:
unit
unique identifier of a dispatch unit (as str)
capacity
The maximum output of the unit if unconstrained
by ramp rate, in MW (as np.float64)
- Return type:
None
- Raises:
ModelBuildError – If the volume bids have not been set yet.
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If the column ‘units’ or ‘capacity’ is missing.
UnexpectedColumn – There is a column that is not ‘units’ or ‘capacity’.
ColumnValues – If there are inf, null or negative values in the bid band columns.
- set_unit_ramp_up_constraints(ramp_details)
Creates constraints on unit output based on ramp up rate.
Constrains the unit output to be <= initial_output + ramp_up_rate * (dispatch_interval / 60)
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info, ... dispatch_interval=30)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of unit ramp up rates.
>>> ramp_details = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'initial_output': [20.0, 50.0], ... 'ramp_up_rate': [30.0, 100.0]})
Create unit capacity based constraints.
>>> market.set_unit_ramp_up_constraints(ramp_details)
The market should now have a set of constraints.
>>> print(market._constraints_rhs_and_type['ramp_up']) unit service constraint_id type rhs 0 A energy 0 <= 35.0 1 B energy 1 <= 100.0
… and a mapping of those constraints to variable type for the lhs.
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['ramp_up']) constraint_id unit service coefficient 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
ramp_details (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
initial_output
the output of the unit at the start of
the dispatch interval, in MW,
(as np.float64)
ramp_up_rate
the maximum rate at which the unit can
increase output, in MW/h, (as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If the column ‘units’, ‘initial_output’ or ‘ramp_up_rate’ is missing.
UnexpectedColumn – There is a column that is not ‘units’, ‘initial_output’ or ‘ramp_up_rate’.
ColumnValues – If there are inf, null or negative values in the bid band columns.
- set_unit_ramp_down_constraints(ramp_details)
Creates constraints on unit output based on ramp down rate.
Will constrain the unit output to be >= initial_output - ramp_down_rate * (dispatch_interval / 60).
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info, ... dispatch_interval=30)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of unit ramp down rates, also need to provide the initial output of the units at the start of dispatch interval.
>>> ramp_details = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'initial_output': [20.0, 50.0], ... 'ramp_down_rate': [20.0, 10.0]})
Create unit capacity based constraints.
>>> market.set_unit_ramp_down_constraints(ramp_details)
The market should now have a set of constraints.
>>> print(market._constraints_rhs_and_type['ramp_down']) unit service constraint_id type rhs 0 A energy 0 >= 10.0 1 B energy 1 >= 45.0
… and a mapping of those constraints to variable type for the lhs.
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['ramp_down']) constraint_id unit service coefficient 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
ramp_details (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
initial_output
the output of the unit at the start of
the dispatch interval, in MW,
(as np.float64)
ramp_down_rate
the maximum rate at which the unit can,
decrease output, in MW/h, (as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If the column ‘units’, ‘initial_output’ or ‘ramp_down_rate’ is missing.
UnexpectedColumn – There is a column that is not ‘units’, ‘initial_output’ or ‘ramp_down_rate’.
ColumnValues – If there are inf, null or negative values in the bid band columns.
- set_fast_start_constraints(fast_start_profiles)
Create the constraints on fast start units dispatch,
see AEMO doc
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B', 'C', 'D', 'E'], ... 'region': ['NSW', 'NSW', 'NSW', 'NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info, ... dispatch_interval=30)
Define some example fast start conditions.
>>> fast_start_conditions = pd.DataFrame({ ... 'unit': ['A', 'B', 'C', 'D', 'E'], ... 'end_mode': [0, 1, 2, 3, 4], ... 'time_in_end_mode': [4.0, 5.0, 5.0, 12.0, 10.0], ... 'mode_two_length': [7.0, 4.0, 10.0, 8.0, 6.0], ... 'mode_four_length': [10.0, 10.0, 20.0, 8.0, 20.0], ... 'min_loading': [30.0, 40.0, 35.0, 50.0, 60.0]})
Add fast start constraints.
>>> market.set_fast_start_constraints(fast_start_conditions)
The market should now have a set of constraints.
>>> print(market._constraints_rhs_and_type['fast_start']) unit service constraint_id type rhs 0 A energy 0 <= 0.0 1 B energy 1 <= 0.0 0 C energy 2 >= 17.5 0 C energy 3 <= 17.5 0 D energy 4 >= 50.0 0 E energy 5 >= 30.0
… and a mapping of those constraints to variable type for the lhs.
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['fast_start']) constraint_id unit service coefficient 0 0 A energy 1.0 1 1 B energy 1.0 0 3 C energy 1.0 0 2 C energy 1.0 0 4 D energy 1.0 0 5 E energy 1.0
- Parameters:
fast_start_profiles (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
end_mode
the fast start dispatch mode the unit
will end the dispatch interval in,
in minutes, (as np.int64),
time_in_end_mode
the time the unit will have spent in the
end mode at the end of this dispatch
interval, in minutes (as np.int64)
mode_two_length
the length of dispatch mode 2 for the
unit, in minutes, (as np.int64)
mode_four_length
the length of dispatch mode 4 for the
unit, in minutes, (as np.int64)
min_loading
the minimum stable operating level of
unit, in MW, (as np.float64)
- Raises:
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If any columns are missing.
UnexpectedColumn – If any additional columns are present.
ColumnValues – If there are inf, null or negative values in any of the numeric columns.
- set_demand_constraints(demand)
Creates constraints that force supply to equal to demand.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a demand level in each region.
>>> demand = pd.DataFrame({ ... 'region': ['NSW'], ... 'demand': [100.0]})
Create constraints.
>>> market.set_demand_constraints(demand)
The market should now have a set of constraints.
>>> print(market._market_constraints_rhs_and_type['demand']) region constraint_id type rhs 0 NSW 0 = 100.0
… and a mapping of those constraints to variable type for the lhs.
>>> regional_mapping = market._constraint_to_variable_map['regional']
>>> print(regional_mapping['demand']) constraint_id region service coefficient 0 0 NSW energy 1.0
- Parameters:
demand (pd.DataFrame) –
Demand by region.
Columns:
Description:
region
unique identifier of a region, (as str)
demand
the non dispatchable demand, in MW,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘region’ or ‘demand’ is missing.
UnexpectedColumn – There is a column that is not ‘region’ or ‘demand’.
ColumnValues – If there are inf, null or negative values in the volume column.
- set_fcas_requirements_constraints(fcas_requirements)
Creates constraints that force FCAS supply to equal requirements.
Examples
Define the unit information data set needed to initialise the market, in this example all units are in the same region.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['QLD', 'NSW', 'VIC', 'SA'], ... unit_info=unit_info)
Define a regulation raise FCAS requirement that apply to all mainland states.
>>> fcas_requirements = pd.DataFrame({ ... 'set': ['raise_reg_main', 'raise_reg_main', ... 'raise_reg_main', 'raise_reg_main'], ... 'service': ['raise_reg', 'raise_reg', ... 'raise_reg', 'raise_reg'], ... 'region': ['QLD', 'NSW', 'VIC', 'SA'], ... 'volume': [100.0, 100.0, 100.0, 100.0]})
Create constraints.
>>> market.set_fcas_requirements_constraints(fcas_requirements)
The market should now have a set of constraints.
>>> print(market._market_constraints_rhs_and_type['fcas']) set constraint_id type rhs 0 raise_reg_main 0 = 100.0
… and a mapping of those constraints to variable type for the lhs.
>>> regional_mapping = market._constraint_to_variable_map['regional']
>>> print(regional_mapping['fcas']) constraint_id service region coefficient 0 0 raise_reg QLD 1.0 1 0 raise_reg NSW 1.0 2 0 raise_reg VIC 1.0 3 0 raise_reg SA 1.0
- Parameters:
fcas_requirements (pd.DataFrame) –
requirement by set and the regions and service the requirement applies to.
Columns:
Description:
set
unique identifier of the requirement set,
(as str)
service
the service or services the requirement set
applies to (as str)
region
the regions that can contribute to meeting a
requirement, (as str)
volume
the amount of service required, in MW,
(as np.float64)
type
the direction of the constrain ‘=’, ‘>=’ or
’<=’, optional, a value of ‘=’ is assumed if
the column is missing (as str)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any set, region and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘set’, ‘service’, ‘region’, or ‘volume’ is missing.
UnexpectedColumn – There is a column that is not ‘set’, ‘service’, ‘region’, ‘volume’ or ‘type’.
ColumnValues – If there are inf, null or negative values in the volume column.
- set_fcas_max_availability(fcas_max_availability)
Creates constraints to ensure fcas dispatch is limited to the availability specified in the FCAS trapezium.
The constraints are described in the
FCAS MODEL IN NEMDE documentation section 2
.Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define the FCAS max_availability.
>>> fcas_max_availability = pd.DataFrame({ ... 'unit': ['A'], ... 'service': ['raise_6s'], ... 'max_availability': [60.0]})
Set the joint availability constraints.
>>> market.set_fcas_max_availability(fcas_max_availability)
TNow the market should have the constraints and their mapping to decision varibales.
>>> print(market._constraints_rhs_and_type['fcas_max_availability']) unit service constraint_id type rhs 0 A raise_6s 0 <= 60.0
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['fcas_max_availability']) constraint_id unit service coefficient 0 0 A raise_6s 1.0
- Parameters:
fcas_max_availability (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
service
the fcas service being offered,
(as str)
max_availability
the maximum volume of the contingency
service, in MW, (as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the columns ‘unit’, ‘service’ or ‘max_availability’ is missing.
UnexpectedColumn – If there are columns other than ‘unit’, ‘service’ or ‘max_availability’.
ColumnValues – If there are inf, null or negative values in the columns of type np.float64.
- set_joint_ramping_constraints_raise_reg(ramp_details)
Create constraints that ensure the provision of energy and fcas raise are within unit ramping capabilities.
The constraints are described in the
FCAS MODEL IN NEMDE documentation section 6.1
.On a unit basis for generators they take the form of:
Energy dispatch + Regulation raise target <= initial output + ramp up rate * (dispatch_interval / 60)
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info, ... dispatch_interval=60)
Define unit initial outputs and ramping capabilities.
>>> ramp_details = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'initial_output': [100.0, 80.0], ... 'ramp_up_rate': [20.0, 10.0]})
Create the joint ramping constraints.
>>> market.set_joint_ramping_constraints_raise_reg(ramp_details)
Now the market should have the constraints and their mapping to decision varibales.
>>> print(market._constraints_rhs_and_type['joint_ramping_raise_reg']) unit constraint_id type rhs 0 A 0 <= 120.0 1 B 1 <= 90.0
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['joint_ramping_raise_reg']) constraint_id unit service coefficient 0 0 A raise_reg 1.0 1 1 B raise_reg 1.0 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
ramp_details (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
initial_output
the output of the unit at the start of
the dispatch interval, in MW,
(as np.float64)
ramp_up_rate
the maximum rate at which the unit can
increase output, in MW/h,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit in unit_limits.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the columns ‘unit’, ‘initial_output’ or ‘ramp_up_rate’ are missing from unit_limits.
UnexpectedColumn – If there are columns other than ‘unit’, ‘initial_output’ or ‘ramp_up_rate’ in unit_limits.
ColumnValues – If there are inf, null or negative values in the columns of type np.float64.
- set_joint_ramping_constraints_lower_reg(ramp_details)
Create constraints that ensure the provision of energy and fcas are within unit ramping capabilities.
The constraints are described in the
FCAS MODEL IN NEMDE documentation section 6.1
.On a unit basis for generators they take the form of:
Energy dispatch - Regulation lower target >= initial output - ramp down rate * (dispatch interval / 60)
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info, ... dispatch_interval=60)
Define unit initial outputs and ramping capabilities.
>>> ramp_details = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'initial_output': [100.0, 80.0], ... 'ramp_down_rate': [15.0, 25.0]})
Create the joint ramping constraints.
>>> market.set_joint_ramping_constraints_lower_reg(ramp_details)
Now the market should have the constraints and their mapping to decision varibales.
>>> print(market._constraints_rhs_and_type['joint_ramping_lower_reg']) unit constraint_id type rhs 0 A 0 >= 85.0 1 B 1 >= 55.0
>>> print(market._constraint_to_variable_map['unit_level']['joint_ramping_lower_reg']) constraint_id unit service coefficient 0 0 A lower_reg -1.0 1 1 B lower_reg -1.0 0 0 A energy 1.0 1 1 B energy 1.0
- Parameters:
ramp_details (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
initial_output
the output of the unit at the start of,
the dispatch interval, in MW,
(as np.float64)
ramp_down_rate
the maximum rate at which the unit can,
decrease output, in MW/h,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit in unit_limits.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the columns ‘unit’, ‘initial_output’ or ‘ramp_down_rate’ are missing from unit_limits.
UnexpectedColumn – If there are columns other than ‘unit’, ‘initial_output’ or ‘ramp_down_rate’ in unit_limits.
ColumnValues – If there are inf, null or negative values in the columns of type np.float64.
- set_joint_capacity_constraints(contingency_trapeziums)
Creates constraints to ensure there is adequate capacity for contingency, regulation and energy dispatch.
Create two constraints for each contingency services, one ensures operation on upper slope of the fcas contingency trapezium is consistent with regulation raise and energy dispatch, the second ensures operation on upper slope of the fcas contingency trapezium is consistent with regulation lower and energy dispatch.
The constraints are described in the
FCAS MODEL IN NEMDE documentation section 6.2
.Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A'], ... 'region': ['NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define the FCAS contingency trapeziums.
>>> contingency_trapeziums = pd.DataFrame({ ... 'unit': ['A'], ... 'service': ['raise_6s'], ... 'max_availability': [60.0], ... 'enablement_min': [20.0], ... 'low_break_point': [40.0], ... 'high_break_point': [60.0], ... 'enablement_max': [80.0]})
Set the joint capacity constraints.
>>> market.set_joint_capacity_constraints(contingency_trapeziums)
TNow the market should have the constraints and their mapping to decision varibales.
>>> print(market._constraints_rhs_and_type['joint_capacity']) unit service constraint_id type rhs 0 A raise_6s 0 <= 80.0 0 A raise_6s 1 >= 20.0
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['joint_capacity']) constraint_id unit service coefficient 0 0 A energy 1.000000 0 0 A raise_6s 0.333333 0 0 A raise_reg 1.000000 0 1 A energy 1.000000 0 1 A raise_6s -0.333333 0 1 A lower_reg -1.000000
- Parameters:
contingency_trapeziums (pd.DataFrame) –
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
service
the contingency service being offered,
(as str)
max_availability
the maximum volume of the contingency
service, in MW, (as np.float64)
enablement_min
the energy dispatch level at which the
unit can begin to provide the,
contingency service, in MW,
(as np.float64)
low_break_point
the energy dispatch level at which
the unit can provide the full
contingency service offered, in MW,
(as np.float64)
high_break_point
the energy dispatch level at which
the unit can no longer provide the
full contingency service offered,
in MW, (as np.float64)
enablement_max
the energy dispatch level at which
the unit can no longer provide
the contingency service, in MW,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit and service combination in contingency_trapeziums.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the columns ‘unit’, ‘service’, ‘max_availability’, ‘enablement_min’, ‘low_break_point’, ‘high_break_point’ or ‘enablement_max’ from contingency_trapeziums.
UnexpectedColumn – If there are columns other than ‘unit’, ‘service’, ‘max_availability’, ‘enablement_min’, ‘low_break_point’, ‘high_break_point’ or ‘enablement_max’ in contingency_trapeziums.
ColumnValues – If there are inf, null or negative values in the columns of type np.float64.
- set_energy_and_regulation_capacity_constraints(regulation_trapeziums)
Creates constraints to ensure there is adequate capacity for regulation and energy dispatch targets.
Create two constraints for each regulation services, one ensures operation on upper slope of the fcas regulation trapezium is consistent with energy dispatch, the second ensures operation on lower slope of the fcas regulation trapezium is consistent with energy dispatch.
The constraints are described in the
FCAS MODEL IN NEMDE documentation section 6.3
.Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A'], ... 'region': ['NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define the FCAS regulation trapeziums.
>>> regulation_trapeziums = pd.DataFrame({ ... 'unit': ['A'], ... 'service': ['raise_reg'], ... 'max_availability': [60.0], ... 'enablement_min': [20.0], ... 'low_break_point': [40.0], ... 'high_break_point': [60.0], ... 'enablement_max': [80.0]})
Set the joint capacity constraints.
>>> market.set_energy_and_regulation_capacity_constraints(regulation_trapeziums)
TNow the market should have the constraints and their mapping to decision varibales.
>>> print(market._constraints_rhs_and_type['energy_and_regulation_capacity']) unit service constraint_id type rhs 0 A raise_reg 0 <= 80.0 0 A raise_reg 1 >= 20.0
>>> unit_mapping = market._constraint_to_variable_map['unit_level']
>>> print(unit_mapping['energy_and_regulation_capacity']) constraint_id unit service coefficient 0 0 A energy 1.000000 0 0 A raise_reg 0.333333 0 1 A energy 1.000000 0 1 A raise_reg -0.333333
- Parameters:
regulation_trapeziums (pd.DataFrame) –
The FCAS trapeziums for the regulation services being offered.
Columns:
Description:
unit
unique identifier of a dispatch unit,
(as str)
service
the regulation service being offered,
(as str)
max_availability
the maximum volume of the contingency
service, in MW, (as np.float64)
enablement_min
the energy dispatch level at which
the unit can begin to provide
the regulation service, in MW,
(as np.float64)
low_break_point
the energy dispatch level at which
the unit can provide the full
regulation service offered, in MW,
(as np.float64)
high_break_point
the energy dispatch level at which the
unit can no longer provide the
full regulation service offered, in MW,
(as np.float64)
enablement_max
the energy dispatch level at which the
unit can no longer provide any
regulation service, in MW,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit and service combination in regulation_trapeziums.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the columns ‘unit’, ‘service’, ‘max_availability’, ‘enablement_min’, ‘low_break_point’, ‘high_break_point’ or ‘enablement_max’ from regulation_trapeziums.
UnexpectedColumn – If there are columns other than ‘unit’, ‘service’, ‘max_availability’, ‘enablement_min’, ‘low_break_point’, ‘high_break_point’ or ‘enablement_max’ in regulation_trapeziums.
ColumnValues – If there are inf, null or negative values in the columns of type np.float64.
- set_interconnectors(interconnector_directions_and_limits)
Create lossless links between specified regions.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A'], ... 'region': ['NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW', 'VIC'], ... unit_info=unit_info)
Define a an interconnector between NSW and VIC so generator can A can be used to meet demand in VIC.
>>> interconnector = pd.DataFrame({ ... 'interconnector': ['inter_one'], ... 'to_region': ['VIC'], ... 'from_region': ['NSW'], ... 'max': [100.0], ... 'min': [-100.0]})
Create the interconnector.
>>> market.set_interconnectors(interconnector)
The market should now have a decision variable defined for each interconnector.
>>> print(market._decision_variables['interconnectors']) interconnector link variable_id lower_bound upper_bound type generic_constraint_factor 0 inter_one inter_one 0 -100.0 100.0 continuous 1
… and a mapping of those variables to to regional energy constraints.
>>> regional = market._variable_to_constraint_map['regional']
>>> print(regional['interconnectors']) variable_id interconnector link region service coefficient 0 0 inter_one inter_one VIC energy 1.0 1 0 inter_one inter_one NSW energy -1.0
- Parameters:
interconnector_directions_and_limits (pd.DataFrame) –
Columns:
Description:
interconnector
unique identifier of a
interconnector, (as str)
to_region
the region that receives power
when flow is in the positive
direction, (as str)
from_region
the region that power is drawn
from when flow is in the
positive direction, (as str)
max
the maximum power flow in the
positive direction, in MW,
(as np.float64)
min
the maximum power flow in the
negative direction, in MW,
(as np.float64)
from_region_loss_factor
the loss factor at the from
region end of the interconnector,
refers the the from region end
to the regional reference node,
optional, assumed to equal 1.0,
if the column is not provided,
(as np.float)
to_region_loss_factor
the loss factor at the to region
end of the interconnector,
refers the to region end to the
regional reference node,
optional, assumed equal to 1.0
if the column is not provided,
(as np.float)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any interconnector.
ColumnDataTypeError – If columns are not of the require type.
MissingColumnError – If any columns are missing.
UnexpectedColumn – If there are any additional columns in the input DataFrame.
ColumnValues – If there are inf, null values in the max and min columns.
- set_interconnector_losses(loss_functions, interpolation_break_points)
Creates linearised loss functions for interconnectors.
Creates a loss variable for each interconnector, this variable models losses by adding demand to each region. The losses are proportioned to each region according to the from_region_loss_share. In a region with one interconnector, where the region is the nominal from region, the impact on the demand constraint would be:
generation - interconnector flow - interconnector losses * from_region_loss_share = demand
If the region was the nominal to region, then:
generation + interconnector flow - interconnector losses * (1 - from_region_loss_share) = demand
The loss variable is constrained to be a linear interpolation of the loss function between the two break points either side of to the actual line flow. This is achieved using a type 2 Special ordered set, where each variable is bound between 0 and 1, only 2 variables can be greater than 0 and all variables must sum to 1. The actual loss function is evaluated at each break point, the variables of the special order set are constrained such that their values weight the distance of the actual flow from the break points on either side e.g. If we had 3 break points at -100 MW, 0 MW and 100 MW, three weight variables w1, w2, and w3, and a loss function f, then the constraints would be of the form.
Constrain the weight variables to sum to one:
w1 + w2 + w3 = 1
Constrain the weight variables to give the relative weighting of adjacent breakpoint:
w1 * -100.0 + w2 * 0.0 + w3 * 100.0 = interconnector flow
Constrain the interconnector losses to be the weighted sum of the losses at the adjacent break point:
w1 * f(-100.0) + w2 * f(0.0) + w3 * f(100.0) = interconnector losses
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A'], ... 'region': ['NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW', 'VIC'], ... unit_info=unit_info)
Create the interconnector, this need to be done before a interconnector losses can be set.
>>> interconnectors = pd.DataFrame({ ... 'interconnector': ['little_link'], ... 'to_region': ['VIC'], ... 'from_region': ['NSW'], ... 'max': [100.0], ... 'min': [-120.0]})
>>> market.set_interconnectors(interconnectors)
Define the interconnector loss function. In this case losses are always 5 % of line flow.
>>> def constant_losses(flow): ... return abs(flow) * 0.05
Define the function on a per interconnector basis. Also details how the losses should be proportioned to the connected regions.
>>> loss_functions = pd.DataFrame({ ... 'interconnector': ['little_link'], ... 'from_region_loss_share': [0.5], # losses are shared equally. ... 'loss_function': [constant_losses]})
Define The points to linearly interpolate the loss function between. In this example the loss function is linear so only three points are needed, but if a non linear loss function was used then more points would result in a better approximation.
>>> interpolation_break_points = pd.DataFrame({ ... 'interconnector': ['little_link', 'little_link', 'little_link'], ... 'loss_segment': [1, 2, 3], ... 'break_point': [-120.0, 0.0, 100]})
>>> market.set_interconnector_losses(loss_functions, interpolation_break_points)
The market should now have a decision variable defined for each interconnector’s losses.
>>> print(market._decision_variables['interconnector_losses']) interconnector link variable_id lower_bound upper_bound type 0 little_link little_link 1 -120.0 120.0 continuous
… and a mapping of those variables to regional energy constraints.
>>> print(market._variable_to_constraint_map['regional']['interconnector_losses']) variable_id region service coefficient 0 1 VIC energy -0.5 1 1 NSW energy -0.5
The market will also have a special ordered set of weight variables for interpolating the loss function between the break points.
>>> print(market._decision_variables['interpolation_weights'].loc[:, ... ['interconnector', 'loss_segment', 'break_point', 'variable_id']]) interconnector loss_segment break_point variable_id 0 little_link 1 -120.0 2 1 little_link 2 0.0 3 2 little_link 3 100.0 4
>>> print(market._decision_variables['interpolation_weights'].loc[:, ... ['variable_id', 'lower_bound', 'upper_bound', 'type']]) variable_id lower_bound upper_bound type 0 2 0.0 1.0 continuous 1 3 0.0 1.0 continuous 2 4 0.0 1.0 continuous
and a set of constraints that implement the interpolation, see above explanation.
>>> print(market._constraints_rhs_and_type['interpolation_weights']) interconnector link constraint_id type rhs 0 little_link little_link 0 = 1.0
>>> print(market._constraints_dynamic_rhs_and_type['link_loss_to_flow']) interconnector link constraint_id type rhs_variable_id 0 little_link little_link 2 = 0 0 little_link little_link 1 = 1
>>> print(market._lhs_coefficients['interconnector_losses']) variable_id constraint_id coefficient 0 2 0 1.0 1 3 0 1.0 2 4 0 1.0 0 2 2 -120.0 1 3 2 0.0 2 4 2 100.0 0 2 1 6.0 1 3 1 0.0 2 4 1 5.0
- Parameters:
loss_functions (pd.DataFrame) –
Columns:
Description:
interconnector
unique identifier of a
interconnector, (as str)
from_region_loss_share
The fraction of loss occuring in
the from region, 0.0 to 1.0,
(as np.float64)
loss_function
A function that takes a flow,
in MW as a float and returns the
losses in MW, (as callable)
interpolation_break_points (pd.DataFrame) –
Columns:
Description:
interconnector
unique identifier of a interconnector,
(as str)
loss_segment
unique identifier of a loss segment on
an interconnector basis, (as np.float64)
break_point
points between which the loss function
will be linearly interpolated, in MW,
(as np.float64)
- Return type:
None
- Raises:
ModelBuildError – If all the interconnectors in the input data have not already been added to the model.
RepeatedRowError – If there is more than one row for any interconnector in loss_functions. Or if there is a repeated break point for an interconnector in interpolation_break_points.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If any columns are missing.
UnexpectedColumn – If there are any additional columns in the input DataFrames.
ColumnValues – If there are inf or null values in the numeric columns of either input DataFrames. Or if from_region_loss_share are outside the range of 0.0 to 1.0
- set_generic_constraints(generic_constraint_parameters)
Creates a set of generic constraints, adding the constraint type, rhs.
This sets a set of arbitrary constraints, but only the type and rhs values. The lhs terms can be added to these constraints using the methods link_units_to_generic_constraints, link_interconnectors_to_generic_constraints and link_regions_to_generic_constraints.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A'], ... 'region': ['NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of generic constraints and add them to the market.
>>> generic_constraint_parameters = pd.DataFrame({ ... 'set': ['A', 'B'], ... 'type': ['>=', '<='], ... 'rhs': [10.0, -100.0]})
>>> market.set_generic_constraints(generic_constraint_parameters)
Now the market should have a set of generic constraints.
>>> print(market._constraints_rhs_and_type['generic']) set constraint_id type rhs 0 A 0 >= 10.0 1 B 1 <= -100.0
- Parameters:
generic_constraint_parameters (pd.DataFrame) –
Columns:
Description:
set
the unique identifier of the constraint set,
(as str)
type
the direction of the constraint >=, <= or
=, (as str)
rhs
the right hand side value of the constraint,
(as np.float64)
- Return type:
None
- Raises:
RepeatedRowError – If there is more than one row for any unit.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘set’, ‘type’ or ‘rhs’ is missing.
UnexpectedColumn – There is a column that is not ‘set’, ‘type’ or ‘rhs’ .
ColumnValues – If there are inf or null values in the rhs column.
- link_units_to_generic_constraints(unit_coefficients)
Set the lhs coefficients of generic constraints on unit basis.
Notes
These sets also maps to the sets in the fcas market constraints. One potential use of this is prevent specific units from helping to meet fcas constraints by giving them a negative one (-1.0) coefficient using this method for particular fcas markey constraints.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'X', 'Y'], ... 'region': ['NSW', 'NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW', 'VIC'], ... unit_info=unit_info)
Define unit lhs coefficients for generic constraints.
>>> unit_coefficients = pd.DataFrame({ ... 'set': ['A', 'A', 'B'], ... 'unit': ['X', 'Y', 'X'], ... 'service': ['energy', 'energy', 'raise_reg'], ... 'coefficient': [1.0, 1.0, -1.0]})
>>> market.link_units_to_generic_constraints(unit_coefficients)
Note all this does is save this information to the market object, linking to specific variable ids and constraint id occurs when the dispatch method is called.
>>> print(market._generic_constraint_lhs['unit']) set unit service coefficient 0 A X energy 1.0 1 A Y energy 1.0 2 B X raise_reg -1.0
- Parameters:
unit_coefficients (pd.DataFrame) –
Columns:
Description:
set
the unique identifier of the constraint set
to map the lhs coefficients to, (as str)
unit
the unit whose variables will be mapped to
the lhs, (as str)
service
the service whose variables will be mapped to the lhs, (as str)
coefficient
the lhs coefficient (as np.float64)
- Raises:
RepeatedRowError – If there is more than one row for any set, unit and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘set’, ‘unit’, ‘serice’ or ‘coefficient’ is missing.
UnexpectedColumn – There is a column that is not ‘set’, ‘unit’, ‘serice’ or ‘coefficient’.
ColumnValues – If there are inf or null values in the rhs coefficient.
- link_regions_to_generic_constraints(region_coefficients)
Set the lhs coefficients of generic constraints on region basis.
This effectively acts as short cut for mapping unit variables to a generic constraint. If a particular service in a particular region is included here then all units in this region will have their variables of this service included on the lhs of this constraint set. If a particular unit needs to be excluded from an otherwise region wide constraint it can be given a coefficient with opposite sign to the region wide sign in the generic unit constraints, the coefficients from the two lhs set will be summed and cancel each other out.
Notes
These sets also map to the sets in the fcas market constraints.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['X', 'X']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['X', 'Y'], ... unit_info=unit_info)
Define region lhs coefficients for generic constraints.
>>> region_coefficients = pd.DataFrame({ ... 'set': ['A', 'A', 'B'], ... 'region': ['X', 'Y', 'X'], ... 'service': ['energy', 'energy', 'raise_reg'], ... 'coefficient': [1.0, 1.0, -1.0]})
>>> market.link_regions_to_generic_constraints(region_coefficients)
Note all this does is save this information to the market object, linking to specific variable ids and constraint id occurs when the dispatch method is called.
>>> print(market._generic_constraint_lhs['region']) set region service coefficient 0 A X energy 1.0 1 A Y energy 1.0 2 B X raise_reg -1.0
- Parameters:
region_coefficients (pd.DataFrame) –
Columns:
Description:
set
the unique identifier of the constraint set
to map the lhs coefficients to, (as str)
region
the region whose variables will be mapped
to the lhs, (as str)
service
the service whose variables will be mapped
to the lhs, (as str)
coefficient
the lhs coefficient (as np.float64)
- Raises:
RepeatedRowError – If there is more than one row for any set, region and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘set’, ‘region’, ‘service’ or ‘coefficient’ is missing.
UnexpectedColumn – There is a column that is not ‘set’, ‘region’, ‘service’ or ‘coefficient’.
ColumnValues – If there are inf or null values in the rhs coefficient.
- link_interconnectors_to_generic_constraints(interconnector_coefficients)
Set the lhs coefficients of generic constraints on an interconnector basis.
Notes
These sets also map to the set in the fcas market constraints.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['C', 'D'], ... 'region': ['X', 'X']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['X', 'Y'], ... unit_info=unit_info)
Define region lhs coefficients for generic constraints. All interconnector variables are for the energy service so no ‘service’ can be specified.
>>> interconnector_coefficients = pd.DataFrame({ ... 'set': ['A', 'A', 'B'], ... 'interconnector': ['X', 'Y', 'X'], ... 'coefficient': [1.0, 1.0, -1.0]})
>>> market.link_interconnectors_to_generic_constraints(interconnector_coefficients)
Note all this does is save this information to the market object, linking to specific variable ids and constraint id occurs when the dispatch method is called.
>>> print(market._generic_constraint_lhs['interconnectors']) set interconnector coefficient 0 A X 1.0 1 A Y 1.0 2 B X -1.0
- Parameters:
unit_coefficients (pd.DataFrame) –
Columns:
Description:
set
the unique identifier of the constraint set
to map the lhs coefficients to, (as str)
interconnetor
the interconnetor whose variables will be
mapped to the lhs, (as str)
coefficient
the lhs coefficient (as np.float64)
- Raises:
RepeatedRowError – If there is more than one row for any set, interconnetor and service combination.
ColumnDataTypeError – If columns are not of the required type.
MissingColumnError – If the column ‘set’, ‘interconnetor’ or ‘coefficient’ is missing.
UnexpectedColumn – There is a column that is not ‘set’, ‘interconnetor’ or ‘coefficient’.
ColumnValues – If there are inf or null values in the rhs coefficient.
- make_constraints_elastic(constraints_key, violation_cost)
Make a set of constraints elastic, so they can be violated at a predefined cost.
If an int or float is provided as the violation_cost, then this directly sets the cost. If a pd.DataFrame is provided then it must contain the columns ‘set’ and ‘cost’, ‘set’ is used to match the cost to the constraints, sets in the constraints that do not appear in the pd.DataFrame will not be made elastic.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['C', 'D'], ... 'region': ['X', 'X']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['X', 'Y'], ... unit_info=unit_info)
Define a set of generic constraints and add them to the market.
>>> generic_constraint_parameters = pd.DataFrame({ ... 'set': ['A', 'B'], ... 'type': ['>=', '<='], ... 'rhs': [10.0, -100.0]})
>>> market.set_generic_constraints(generic_constraint_parameters)
Now the market should have a set of generic constraints.
>>> print(market._constraints_rhs_and_type['generic']) set constraint_id type rhs 0 A 0 >= 10.0 1 B 1 <= -100.0
Now these constraints can be made elastic.
>>> market.make_constraints_elastic('generic', violation_cost=1000.0)
Now the market will contain extra decision variables to capture the cost of violating the constraint.
>>> print(market._decision_variables['generic_deficit']) variable_id lower_bound upper_bound type 0 0 0.0 inf continuous 1 1 0.0 inf continuous
>>> print(market._objective_function_components['generic_deficit']) variable_id cost 0 0 1000.0 1 1 1000.0
These will be mapped to the constraints
>>> print(market._lhs_coefficients['generic_deficit']) variable_id constraint_id coefficient 0 0 0 1.0 1 1 1 -1.0
If a pd.DataFrame is provided then we can set cost on a constraint basis.
>>> violation_cost = pd.DataFrame({ ... 'set': ['A', 'B'], ... 'cost': [1000.0, 2000.0]})
>>> market.make_constraints_elastic('generic', violation_cost=violation_cost)
>>> print(market._objective_function_components['generic_deficit']) variable_id cost 0 2 1000.0 1 3 2000.0
Note will the variable id get incremented with every use of the method only the latest set of variables are used.
- Parameters:
constraints_key (str) – The key used to reference the constraint set in the dict self.market_constraints_rhs_and_type or self.constraints_rhs_and_type. See the documentation for creating the constraint set to get this key.
violation_cost (str or float or int or pd.DataFrame) –
- Return type:
None
- Raises:
ValueError – If violation_cost is not str, numeric or pd.DataFrame.
ModelBuildError – If the constraint_key provided does not match any existing constraints.
MissingColumnError – If violation_cost is a pd.DataFrame and does not contain the columns set and cost. Or if the constraints to be made elastic do not have the set idenetifier.
RepeatedRowError – If violation_cost is a pd.DataFrame and has more than one row per set.
ColumnDataTypeError – If violation_cost is a pd.DataFrame and the column set is not str and the column cost is not numeric.
- set_tie_break_constraints(cost)
Creates a cost that attempts to balance the energy dispatch of equally priced bids within a region.
For each pair of bids from different generators in a region which are of the same price a constraint of the following form is created.
B1 * 1/C1 - B2 * 1/C2 + D1 - D2 = 0
Where B1 and B2 are the decision variables of each bid, C1 and C2 are the bid volumes, D1 and D2 are additional variables that have provided cost in the objective function. If a small cost (say 1e-6) is provided then this constraint balances the pro rata output of the bids.
For AEMO documentation of this constraint see AEMO doc <../../docs/pdfs/Schedule of Constraint Violation Penalty factors.pdf> section 3 item 47.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['X', 'X']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['X'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [50.0, 100.0], ... '2': [100.0, 130.0], ... '3': [110.0, 150.0]})
>>> market.set_unit_price_bids(price_bids)
Creat tie break constraints.
>>> market.set_tie_break_constraints(1e-3)
This should add set of constraints rhs, type and lhs coefficients
>>> market._decision_variables['bids'] unit capacity_band service variable_id lower_bound upper_bound type 0 A 1 energy 0 0.0 20.0 continuous 1 A 2 energy 1 0.0 20.0 continuous 2 A 3 energy 2 0.0 5.0 continuous 3 B 1 energy 3 0.0 50.0 continuous 4 B 2 energy 4 0.0 30.0 continuous 5 B 3 energy 5 0.0 10.0 continuous
>>> market._constraints_rhs_and_type['tie_break'] constraint_id type rhs 0 0 = 0.0
>>> market._lhs_coefficients['tie_break'] constraint_id variable_id coefficient 0 0 1 0.05 0 0 3 -0.02
And a set of deficiet variables that allow the constraints to violated at the specified cost.
>>> market._lhs_coefficients['tie_break_deficit'] variable_id constraint_id coefficient 0 6 0 -1.0 0 7 0 1.0
>>> market._objective_function_components['tie_break_deficit'] variable_id cost 0 6 0.001 0 7 0.001
- dispatch(energy_market_ceiling_price=None, energy_market_floor_price=None, fcas_market_ceiling_price=None, allow_over_constrained_dispatch_re_run=False)
Combines the elements of the linear program and solves to find optimal dispatch.
If allow_over_constrained_dispatch_re_run is set to True then constraints will be relaxed when market ceiling or floor prices are violated.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [50.0, 100.0], ... '2': [100.0, 130.0], ... '3': [100.0, 150.0]})
Create the objective function components corresponding to the the energy bids.
>>> market.set_unit_price_bids(price_bids)
Define a demand level in each region.
>>> demand = pd.DataFrame({ ... 'region': ['NSW'], ... 'demand': [100.0]})
Create unit capacity based constraints.
>>> market.set_demand_constraints(demand)
Call the dispatch method.
>>> market.dispatch()
Now the market dispatch can be retrieved.
>>> print(market.get_unit_dispatch()) unit service dispatch 0 A energy 45.0 1 B energy 55.0
And the market prices can be retrieved.
>>> print(market.get_energy_prices()) region price 0 NSW 130.0
- Return type:
None
- Raises:
ModelBuildError – If a model build process is incomplete, i.e. there are energy bids but not energy demand set.
- get_unit_dispatch()
Retrieves the energy dispatch for each unit.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [50.0, 100.0], ... '2': [100.0, 130.0], ... '3': [100.0, 150.0]})
Create the objective function components corresponding to the the energy bids.
>>> market.set_unit_price_bids(price_bids)
Define a demand level in each region.
>>> demand = pd.DataFrame({ ... 'region': ['NSW'], ... 'demand': [100.0]})
Create unit capacity based constraints.
>>> market.set_demand_constraints(demand)
Call the dispatch method.
>>> market.dispatch()
Now the market dispatch can be retrieved.
>>> print(market.get_unit_dispatch()) unit service dispatch 0 A energy 45.0 1 B energy 55.0
- Return type:
pd.DataFrame
- Raises:
ModelBuildError – If a model build process is incomplete, i.e. there are energy bids but not energy demand set.
- get_energy_prices()
Retrieves the energy price in each market region.
Energy prices are the shadow prices of the demand constraint in each market region.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW'], ... unit_info=unit_info)
Define a set of bids, in this example we have two units called A and B, with three bid bands.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [20.0, 50.0], ... '2': [20.0, 30.0], ... '3': [5.0, 10.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'B'], ... '1': [50.0, 100.0], ... '2': [100.0, 130.0], ... '3': [100.0, 150.0]})
Create the objective function components corresponding to the the energy bids.
>>> market.set_unit_price_bids(price_bids)
Define a demand level in each region.
>>> demand = pd.DataFrame({ ... 'region': ['NSW'], ... 'demand': [100.0]})
Create unit capacity based constraints.
>>> market.set_demand_constraints(demand)
Call the dispatch method.
>>> market.dispatch()
Now the market prices can be retrieved.
>>> print(market.get_energy_prices()) region price 0 NSW 130.0
- Return type:
pd.DateFrame
- Raises:
ModelBuildError – If a model build process is incomplete, i.e. there are energy bids but not energy demand set.
- get_fcas_prices()
Retrives the price associated with each set of FCAS requirement constraints.
- Return type:
pd.DateFrame
- get_interconnector_flows()
Retrieves the flows for each interconnector.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW', 'VIC'], ... unit_info=unit_info)
Define a set of bids, in this example we have just one unit that can provide 100 MW in NSW.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A'], ... '1': [100.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A'], ... '1': [80.0]})
Create the objective function components corresponding to the the energy bids.
>>> market.set_unit_price_bids(price_bids)
Define a demand level in each region, no power is required in NSW and 90.0 MW is required in VIC.
>>> demand = pd.DataFrame({ ... 'region': ['NSW', 'VIC'], ... 'demand': [0.0, 90.0]})
Create unit capacity based constraints.
>>> market.set_demand_constraints(demand)
Define a an interconnector between NSW and VIC so generator can A can be used to meet demand in VIC.
>>> interconnector = pd.DataFrame({ ... 'interconnector': ['inter_one'], ... 'to_region': ['VIC'], ... 'from_region': ['NSW'], ... 'max': [100.0], ... 'min': [-100.0]})
Create the interconnector.
>>> market.set_interconnectors(interconnector)
Call the dispatch method.
>>> market.dispatch()
Now the market dispatch can be retrieved.
>>> print(market.get_unit_dispatch()) unit service dispatch 0 A energy 90.0
And the interconnector flows can be retrieved.
>>> print(market.get_interconnector_flows()) interconnector link flow 0 inter_one inter_one 90.0
- Return type:
pd.DataFrame
- get_region_dispatch_summary()
Calculates a dispatch summary at the regional level.
Examples
Define the unit information data set needed to initialise the market.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
Initialise the market instance.
>>> market = SpotMarket(market_regions=['NSW', 'VIC'], ... unit_info=unit_info)
Define a set of bids, in this example we have just one unit that can provide 100 MW in NSW.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A'], ... '1': [100.0]})
Create energy unit bid decision variables.
>>> market.set_unit_volume_bids(volume_bids)
Define a set of prices for the bids.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A'], ... '1': [80.0]})
Create the objective function components corresponding to the the energy bids.
>>> market.set_unit_price_bids(price_bids)
Define a demand level in each region, no power is required in NSW and 90.0 MW is required in VIC.
>>> demand = pd.DataFrame({ ... 'region': ['NSW', 'VIC'], ... 'demand': [0.0, 90.0]})
Create unit capacity based constraints.
>>> market.set_demand_constraints(demand)
Define a an interconnector between NSW and VIC so generator can A can be used to meet demand in VIC.
>>> interconnector = pd.DataFrame({ ... 'interconnector': ['inter_one'], ... 'to_region': ['VIC'], ... 'from_region': ['NSW'], ... 'max': [100.0], ... 'min': [-100.0]})
Create the interconnector.
>>> market.set_interconnectors(interconnector)
Define the interconnector loss function. In this case losses are always 5 % of line flow.
>>> def constant_losses(flow=None): ... return abs(flow) * 0.05
Define the function on a per interconnector basis. Also details how the losses should be proportioned to the connected regions.
>>> loss_functions = pd.DataFrame({ ... 'interconnector': ['inter_one'], ... 'from_region_loss_share': [0.5], # losses are shared equally. ... 'loss_function': [constant_losses]})
Define the points to linearly interpolate the loss function between. In this example the loss function is linear so only three points are needed, but if a non linear loss function was used then more points would result in a better approximation.
>>> interpolation_break_points = pd.DataFrame({ ... 'interconnector': ['inter_one', 'inter_one', 'inter_one'], ... 'loss_segment': [1, 2, 3], ... 'break_point': [-120.0, 0.0, 100]})
>>> market.set_interconnector_losses(loss_functions, interpolation_break_points)
Call the dispatch method.
>>> market.dispatch()
Now the region dispatch summary can be retreived.
>>> print(market.get_region_dispatch_summary()) region dispatch inflow transmission_losses interconnector_losses 0 NSW 94.615385 -92.307692 0.0 2.307692 1 VIC 0.000000 92.307692 0.0 2.307692
- Returns:
Columns:
Description:
region
unique identifier of a market
region, required (as str)
dispatch
the net dispatch of units inside
a region i.e. generators dispatch
minus load dispatch, in MW. (as np.float64)
inflow
the net inflow from interconnectors,
not including losses, in MW
(as np.float64)
interconnector_losses
interconnector losses attributed
to region, in MW, (as np.float64)
- Return type:
pd.DataFrame
- get_fcas_availability()
Get the availability of fcas service on a unit level, after constraints.
Examples
Volume of each bid.
>>> volume_bids = pd.DataFrame({ ... 'unit': ['A', 'A', 'B', 'B', 'B'], ... 'service': ['energy', 'raise_6s', 'energy', ... 'raise_6s', 'raise_reg'], ... '1': [100.0, 10.0, 110.0, 15.0, 15.0]})
Price of each bid.
>>> price_bids = pd.DataFrame({ ... 'unit': ['A', 'A', 'B', 'B', 'B'], ... 'service': ['energy', 'raise_6s', 'energy', ... 'raise_6s', 'raise_reg'], ... '1': [50.0, 35.0, 60.0, 20.0, 30.0]})
Participant defined operational constraints on FCAS enablement.
>>> fcas_trapeziums = pd.DataFrame({ ... 'unit': ['B', 'B', 'A'], ... 'service': ['raise_reg', 'raise_6s', 'raise_6s'], ... 'max_availability': [15.0, 15.0, 10.0], ... 'enablement_min': [50.0, 50.0, 70.0], ... 'low_break_point': [65.0, 65.0, 80.0], ... 'high_break_point': [95.0, 95.0, 100.0], ... 'enablement_max': [110.0, 110.0, 110.0]})
Unit locations.
>>> unit_info = pd.DataFrame({ ... 'unit': ['A', 'B'], ... 'region': ['NSW', 'NSW']})
The demand in the regions being dispatched.
>>> demand = pd.DataFrame({ ... 'region': ['NSW'], ... 'demand': [195.0]})
FCAS requirement in the regions being dispatched.
>>> fcas_requirements = pd.DataFrame({ ... 'set': ['nsw_regulation_requirement', ... 'nsw_raise_6s_requirement'], ... 'region': ['NSW', 'NSW'], ... 'service': ['raise_reg', 'raise_6s'], ... 'volume': [10.0, 10.0]})
Create the market model with unit service bids.
>>> market = SpotMarket(unit_info=unit_info, ... market_regions=['NSW']) >>> market.set_unit_volume_bids(volume_bids) >>> market.set_unit_price_bids(price_bids)
Create constraints that enforce the top of the FCAS trapezium.
>>> fcas_availability = fcas_trapeziums.loc[:, ['unit', 'service', 'max_availability']] >>> market.set_fcas_max_availability(fcas_availability)
Create constraints the enforce the lower and upper slope of the FCAS regulation service trapeziums.
>>> regulation_trapeziums = fcas_trapeziums[fcas_trapeziums['service'] == 'raise_reg'] >>> market.set_energy_and_regulation_capacity_constraints(regulation_trapeziums)
Create constraints that enforce the lower and upper slope of the FCAS contingency trapezium. These constrains also scale slopes of the trapezium to ensure the co-dispatch of contingency and regulation services is technically feasible.
>>> contingency_trapeziums = fcas_trapeziums[fcas_trapeziums['service'] == 'raise_6s'] >>> market.set_joint_capacity_constraints(contingency_trapeziums)
Set the demand for energy.
>>> market.set_demand_constraints(demand)
Set the required volume of FCAS services.
>>> market.set_fcas_requirements_constraints(fcas_requirements)
Calculate dispatch and pricing
>>> market.dispatch()
Return the total dispatch of each unit in MW.
>>> print(market.get_unit_dispatch()) unit service dispatch 0 A energy 100.0 1 A raise_6s 5.0 2 B energy 95.0 3 B raise_6s 5.0 4 B raise_reg 10.0
Return the constrained availability of each units fcas service.
>>> print(market.get_fcas_availability()) unit service availability 0 A raise_6s 10.0 1 B raise_6s 5.0 2 B raise_reg 10.0
- exception nempy.markets.ModelBuildError
Raise for building model components in wrong order.
- exception nempy.markets.MissingTable
Raise for trying to access missing table.