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:

  1. 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.

  2. 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.

  3. 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:

SpotMarket(market_regions, unit_info[, ...])

Class for constructing and dispatching the spot market on an interval basis.

Exceptions:

ModelBuildError

Raise for building model components in wrong order.

MissingTable

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,

    see AEMO doc,

    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.

set_fcas_requirements_constraints(...)

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.

set_joint_ramping_constraints_raise_reg(...)

Create constraints that ensure the provision of energy and fcas raise are within unit ramping capabilities.

set_joint_ramping_constraints_lower_reg(...)

Create constraints that ensure the provision of energy and fcas are within unit ramping capabilities.

set_joint_capacity_constraints(...)

Creates constraints to ensure there is adequate capacity for contingency, regulation and energy dispatch.

set_energy_and_regulation_capacity_constraints(...)

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.

set_generic_constraints(...)

Creates a set of generic constraints, adding the constraint type, rhs.

link_units_to_generic_constraints(...)

Set the lhs coefficients of generic constraints on unit basis.

link_regions_to_generic_constraints(...)

Set the lhs coefficients of generic constraints on region basis.

link_interconnectors_to_generic_constraints(...)

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.

set_tie_break_constraints(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.

get_unit_dispatch()

Retrieves the energy dispatch for each unit.

get_energy_prices()

Retrieves the energy price in each market region.

get_fcas_prices()

Retrives the price associated with each set of FCAS requirement constraints.

get_interconnector_flows()

Retrieves the flows for each interconnector.

get_region_dispatch_summary()

Calculates a dispatch summary at the regional level.

get_fcas_availability()

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.

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.

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.

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.