Giter Site home page Giter Site logo

fullnetworksystems.jl's Introduction

FullNetworkSystems

Stable Dev Build Status Coverage Code Style: Blue ColPrac: Contributor's Guide on Collaborative Practices for Community Packages

This package defines a set of types and data structures to represent an energy grid and the components within it. The types defined can be used to build Optimal Power Flow (OPF) and associated optimisation problems e.g. unit commitment and economic dispatch. The data structures are designed so that the relevant data can be easily accessed and used to formulate these optimisation problems.

Features of the System

The System subtypes (SystemDA and SystemRT) both contain fields which represent static components of an energy grid. Firstly, the Generator type has a unit_code field identifying the generator and fields for attributes that do not change over time e.g. ramps rates, minimum up- and down-time, and technology.

generator = Generator(
    unit_code=111,
    zone=1,
    startup_cost=0.0,
    shutdown_cost=1.0,
    no_load_cost=1.0,
    min_uptime=24.0,
    min_downtime=24.0,
    ramp_up=2.0,
    ramp_down=2.0,
    technology=:steam_turbine
)

The Bus and Branch types can be used to model bus injection and branch flow constraints (see this blog post introducing these OPF modelling concepts). These types store attributes of buses and branches that do not change over time. The to_bus and from_bus fields in the Branch type can be used to create a network map of which branches connect which buses.

bus_a = Bus(name="A", base_voltage=100.0)
bus_c = Bus(name="C", base_voltage=100.0)

branch = Branch(
    name="1",
    to_bus="A",
    from_bus="C",
    rate_a=10.0,
    rate_b=10.0,
    is_monitored=true,
    break_points=(100.0, 102.0),
    penalties=(5.0, 6.0),
    resistance=1.0,
    reactance=1.0
)

The fields gens_per_bus, loads_per_bus etc. provide topological information about the energy grid by mapping which buses components in the system are located at. Each mapping is in the form of a Dictionary where the keys are bus names and the values are vectors of component identifiers (e.g. Generator unit_codes).

Along with the static attributes and topology information the System types include fields for time series data. Firstly there are the time series associated with generators defined in a GeneratorTimeSeries. One particular time series to note is the offer_curve, which represents the offers of generation submitted by each generator for each hour. This time series is expected to be a KeyedArray where the dimensions are generator ids x datetimes and the fields are vectors of (price, volume) pairs.

generator_ids = [111, 222, 333]
datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23)
gen_time_series = GeneratorTimeSeries(;
    initial_generation = KeyedArray(fill(100.0, length(generator_ids)); generator_ids),
    offer_curve = KeyedArray(fill([(1.0, 100.0)], length(generator_ids), length(datetimes)); generator_ids, datetimes)
    ...
)

The GeneratorTimeSeries type includes fields for additional time series such as generation limits, ramp limits and ancillary service offers, which means these features can be included in an optimisation problem (see GeneratorTimeSeries for details of all the time series fields). GeneratorStatus types also contain time series data, specifically associated with the status of the generator (e.g. whether it is on or off, how long it has been on or off). This is useful for including factors such as ramp rates in an optimisation problem, as the status of a generator limits how much it can ramp up or down in a given hour.

The SystemDA type includes fields for additional supply (increment) and demand (decrement, price_sensitive_loads) bids for the purpose of modelling virtual participation in electricity markets (introduction to virtual bidding). The bid data is expected to be a KeyedArray where the dimensions are ids x datetimes and the fields are vectors of price-volume pairs, as in the offer curves.

bid_ids = ["123", "456", "789"] # unique identifers
datetimes = DateTime(2017, 12, 15):Hour(1):DateTime(2017, 12, 15, 23)
increments = KeyedArray(
    fill([(0.1, 10.0)], length(generator_ids), length(datetimes)); bid_ids, datetimes
)

Grid Matrices

This package also provides functions to calculate the Power Transfer Distribution Factor (PTDF) and Line Outage Distribution Factor (LODF) matrices for a System of branches and buses. These matrices are useful for modelling branch flow constraints under different contingency scenarios. The System types have fields to store these matrices.

Example Modelling Problem

The following is an example of how the data stored in a SystemRT object can be used to build a JuMP model. The objective of the model is to solve a simple energy balance problem, where a set of loads (demands) need to be met by generation (supply), for the minimum possible cost. Assume we have built all the components of the system described in the previous sections so that we can construct an instance of a system (see SystemRT for specific details of the components in a SystemRT).

using AxisKeys
using FullNetworkSystems
using HiGHS
using JuMP

# Here we model a simple system where demands (loads) need to be met by supply for minimum cost
# We assume all components are colocated, so do not model branch constraints or nodal injections
system = SystemRT(
    gens_per_bus,
    loads_per_bus,
    zones,
    buses,
    generators,
    branches,
    lodfs,
    ptdf,
    generator_time_series,
    generator_status,
    loads
)

model = Model()
units = keys(get_generators(system))
datetimes = get_datetimes(system)

@variable(model, generation[u in units, t in datetimes] >= 0)

load = get_loads(system)
@constraint(
    model,
    energy_balance[t in datetimes],
    sum(model[:generation][u, t] for u in units) == sum(load(l, t) for l in axiskeys(load, 1))
)

# For simplicity, use a fixed price per MW, rather than a variable price offer curve
offer_curves = get_offer_curve(system)
prices = map(curve -> only(first.(curve)), offer_curves)
cost = AffExpr()
for u in units, t in datetimes
    add_to_expression!(cost, prices(u, t), model[:generation][u, t])
end

@objective(model, Min, cost)

set_optimizer(model, HiGHS.Optimizer)
optimize!(model)

fullnetworksystems.jl's People

Contributors

bsnelling avatar nickrobinson251 avatar

Stargazers

zuoj avatar  avatar Abraham Alvarez Bustos avatar john avatar ChangXin Sun avatar Elias Carvalho avatar Frames White avatar

Watchers

James Cloos avatar  avatar

Forkers

saschamcdonald

fullnetworksystems.jl's Issues

Should we have `get_increments` etc.?

In reviewing our accessor function (#23) and then looking at our internal code using this package, I'm wondering why we have one accessor get_bids, used like get_bids(system, :increment), rather than:

  • get_increments (or get_increment_bids)
  • get_decrements (or get_decrement_bids)
  • get_price_sensitive_demand (or get_price_sensitive_demand_bids)

having 3 separate functions seem more consistent with the rest of the API

Also, some downstream code effectively defines

get_virtual_bids(system) = vcat(get_bids(system, :increment), get_bids(system, :decrement))

perhaps we should define something like that here too?

Review `Branch` constructors

We have several ways of constructing Branch. It's unclear if some of the fields should really have default values (resistance, reactance for example should probably not have defaults) and what the best constructors should be in practice. We should review the current defined methods and what we think it's best for us.

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Define Technology types

Currently the technology field of Generator is a Symbol. It may be useful to define technology types so that users can dispatch on the technology of a generator in analysis.

Allow a PTDF to be missing from a System

The PTDF is a large matrix, which can take longer to save to disk than to recalculate. Therefore it is useful to be able to save the System to disk without the PTDF. We should allow the PTDF to be missing in a System.

Allow fixed loads to have bid prices

In ERCOT, fixed loads submit price offers and are interpreted as "all or nothing" blocks.
We don't have any kind of fixed load cost currently, so we need to add this.
This could be done in two ways: 1) add a new type of bid (other than inc, dec, psl), or 2) allow fixed loads to have bid prices associated with them

Add named tuple constructors for components

Some of the static components defined here have lots of fields, which makes constructors hard to follow. We could add named tuple constructors for these components so the user can do e.g.

br1 = Branch(;
        name="1",
        to_bus="A",
        from_bus="B",
        rate_a=10.0,
        rate_b=10.0,
        is_monitored=true,
        break_points=(100.0, 102.0),
        penalties=(5.0, 6.0),
        resistance=1.0,
        reactance=1.0
)

Define and export the table types `System` expects

Right now we use row-tables like branches::Dictionary{BranchName, Branch}, and only export the row type Branch, which makes building up a full table outside of this package quite awkward, since users must know to build a Dictionary{BranchName, Branch}. In particular they must know to have BranchName keys. But BranchName is just an alias for a Base type, used internally to make the code easier to read and maintain; it's not really intended as part of the public API, although it clearly is since the System constructor requires a Dictionary{BranchName, Branch}.

I think the public API of the package would be simpler for users if we exported the table types, e.g.:

const Branches = Dictionary{BranchName, Branch}
export Branches

Define a type for ancillary services

Ancillary service offers are currently represented by a KeyedArray{Float64, 2}, where the keys are service provider ids x datetimes and the values are prices ($/pu). A missing in the keyed array means a provider did not submit an offer for that service at that time.

This data structure is insufficient to store all the information needed for other market clearing formulations. Defining a type would be useful because:

  • more information about the offer can be stored
  • different ISOs name the services differently but a lot of the concepts overlap so encoding characteristics about the ancillary service itself should facilitate code reuse in the models package

AncillaryService type structure

  • offer curve::EnergyBids: as with other offers, ancillary service providers can submit bids as price-volume pairs with extra constraints e.g. multi-hour blocks (see #36 for EnergyBids details). Using the EnergyBids type should mean we no longer need to support missing offers because an offer of [(0.0, 0.0)] is equivalent
  • name::Symbol: the name of the service
  • generation_direction::Bool?: ancillary service offers can be offers for additional generation but also offers to reduce generation (e.g. regulation up and regulation down respectively). The field could be a Bool or singleton types Up <: GenerationDirection, Down <: GenerationDirection
  • online_offline::Bool: ancillary service requirements can specify requirements from both online resources and offline resources

Implement new generalised type for storing energy bids

Call this new type EnergyBids for the sake of the issue. EnergyBids would replace the KeyedArray{Vector{Tuple{Float64, Float64}}, 2} that currently represents a set of bids in a System. The keys of these KeyedArrays are bid ids x datetimes and the values are vectors of price-volume pairs ($, pu). Initially this was sufficient to represent the concept of energy bids. In order to support the formulations of other ISOs there are additional requirements for the EnergyBids type:

  • price sensitive bids can still be stored as KeyedArray{Vector{Tuple{Float64, Float64}}, 2} where the axis keys are bid ids x datetimes
  • multi hour bids - bids submitted with a true multi hour flag have to clear as a block (either they all clear or none do), this can be stored in the EnergyBids as a KeyedArray{Int} where the integers act as an id for a block of multi-hour bids
  • fixed bids - bids that are "fixed" must clear at the specified volume or not at all. The alternative is variable type bids which can clear at a volume up to the specified volume. This can be stored in EnergyBids as a KeyedArray{Bool}

So the type would look like:

struct EnergyBids
    """
    Multi-hour block id. In a multi-hour block either all bids clear or none do.
    """
    multi_hour::KeyedArray{Int64, 2}
    """
    Bool indicating whether the bid is a fixed type which can only clear at the given price
    and volume.
    """
    is_fixed_type::KeyedArray{Bool, 2}
    "Bid curve, `KeyedArray` where the axis keys are `generator names x datetimes`."
    bid_curve::KeyedArray{PriceSensitiveBid, 2}
end

Additional requirements

  • it may be useful to have a way to differentiate between supply and demand EnergyBids, either by adding a field to EnergyBids or using subtypes

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.