Base Case - Building

The data for this case is available in the folder data/case_1

In this tutorial, we will build a simple case containing some basic elements that can help us understand some of the functionalities of the IARA package.

We'll start by importing the necessary packages.

using Dates
using DataFrames
using IARA

We also need to define a directory to store the case.

const PATH_BASE_CASE = joinpath(@__DIR__, "data", "case_1")
"/home/runner/work/IARA.jl/IARA.jl/docs/build/tutorial/data/case_1"

Fundamental elements

Parameters indexed in time

The parameters indexed in time in IARA model are defined by the number of periods and subperiods.

A period represents a macro time period, such as a week, month, season or year. In each period, the model will solve an optimization problem that represents the decisions that need to be made based on available information.

A subperiod represents a sub time period of the period, such as an hour, a day, or simply a collection of hours.

A scenario in this context are defined as openings. Each period has a number of openings, and as the process evolves, the next period will have a number of openings that grow exponentially (e.g., if a period has 3 openings, the next period will have $3^2 = 9$ possibilities). These openings represent possible future paths or branches in the decision process. For example, a node could represent a dry or wet season, a high or low demand, or a high or low price scenario.

A Policy Graph is a representation of the decision making process in the model. There are two types of policy graphs: linear and cyclic.

In a linear policy graph, the periods are connected in a linear sequence, where the decisions made in one period affect the decisions in the next period.

In a cyclic policy graph, the periods are connected in a cyclic sequence, where the decisions made in the last period affect the decisions in the first period.

For more details, see the SDDP.jl documentation.

For this initial case, we will define a cyclic policy graph, with two periods (nodes) that will represent the Winter and Summer seasons and an yearly discount rate of 10%.

Additionally, each period will be consists of a single subperiod, with a duration of 24 hours.

To illustrate the concept of this cyclic policy graph, we can think of a two-period diagram as follows:

In this case, we will define 4 scenarios.

Now we can initialize our case, with the defined temporal parameters. The cycle duration in hours is the duration of a subproblem, given by the sum of the subperiod duration vector, times the number of periods.

number_of_periods = 2
number_of_subperiods = 1
number_of_scenarios = 4
subperiod_duration_in_hours = [24.0]
cycle_discount_rate = 0.1
cycle_duration_in_hours = sum(subperiod_duration_in_hours) * number_of_periods

Using IARA.create_study! we can create a new study. This function will return a database reference that will store all the information about the case.

db = IARA.create_study!(PATH_BASE_CASE;
    number_of_periods = number_of_periods,
    number_of_scenarios = number_of_scenarios,
    number_of_subperiods = number_of_subperiods,
    subperiod_duration_in_hours = subperiod_duration_in_hours,
    policy_graph_type = IARA.Configurations_PolicyGraphType.CYCLIC_WITH_FIXED_ROOT,
    number_of_nodes = number_of_periods,
    cycle_discount_rate = cycle_discount_rate,
    cycle_duration_in_hours = cycle_duration_in_hours,
    demand_deficit_cost = 3000.0,
);

Spatial Units

Bus

A bus is a connection point in a power system where multiple electrical components (such as generators, loads, transformers, or transmission lines) are set. Each bus need to be linked to a financial zone, which will be introduced in the next step.

For this case, we will have a single bus, named "Island", to which all the elements will be connected. We can add a bus to the database using the functionalities IARA.add_bus!.

IARA.add_bus!(db; label = "Island")

Financial elements

Zones

A zone is a group of buses or a geographical area within a power system, often representing a specific region or subsystem. For this case, we will also define a single zone.

We can add a zone to the database using the method IARA.add_zone!.

IARA.add_zone!(db; label = "Island Zone")

Now we can link the bus to the zone, using the function IARA.update_bus_relation!.

IARA.update_bus_relation!(
    db,
    "Island";
    collection = "Zone",
    relation_type = "id",
    related_label = "Island Zone",
)

Asset Owners

In the bidding format, we need to define the asset owners that will participate in the market. These owners will be responsible for submitting bids for their assets, which will be represented by Bidding Groups. Each Bidding Group will be associated with a set of assets that belong to the same owner.

For now, we will define two asset owners: Thermal Owner and Price Taker. The first will assume the role of price maker, while the second will act as a price taker.

We can add an asset owner to the database using the functionalities IARA.add_asset_owner!.

IARA.add_asset_owner!(
    db;
    label = "Thermal Owner",
    price_type = IARA.AssetOwner_PriceType.PRICE_MAKER,
)
IARA.add_asset_owner!(
    db;
    label = "Price Taker",
    price_type = IARA.AssetOwner_PriceType.PRICE_TAKER,
)

Bidding Groups

Now we can define the Bidding Groups related to the asset owners that we have created earlier. Each Asset Owner will have a single Bidding Group, which will be associated with all their assets.

In each Bidding Group, we have to define the risk factor (mark-up) that will be applied to the cost of the assets. For this example, we will define a risk factor of 50% for both Bidding Groups.

We can add a Bidding Group to the database using the method IARA.add_bidding_group!.

In the independent_bid_max_segments we need to set the maximum number of segments that the Bidding Group can have. For this case, we will set it to the number of units that each owner has.

IARA.add_bidding_group!(
    db;
    label = "Thermal Owner",
    assetowner_id = "Thermal Owner",
    risk_factor = [0.5],
    segment_fraction = [1.0],
    independent_bid_max_segments = 2, # number of units
)
IARA.add_bidding_group!(
    db;
    label = "Price Taker",
    assetowner_id = "Price Taker",
    risk_factor = [0.5],
    segment_fraction = [1.0],
    independent_bid_max_segments = 5, # number of units
)

Physical Elements

Demand

This case will have three demand units. We can add them to the database using the function IARA.add_demand_unit!. We can enable or disable the demand unit. This can be changed by setting the existing attribute to 1 in the parameters time series DataFrame.

The demand over each period, subperiod and scenario will be defined by a time series file. We will define it at the end of this tutorial.

For this case, the demand will exist from the beginning.

IARA.add_demand_unit!(db;
    label = "Demand1",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
    ),
    bus_id = "Island",
)

IARA.add_demand_unit!(db;
    label = "Demand2",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
    ),
    bus_id = "Island",
)

IARA.add_demand_unit!(db;
    label = "Demand3",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
    ),
    bus_id = "Island",
)

Generation Units

Our case will have a mix of renewable and thermal units. In the table below, we can see some characteristics of each unit.

TechnologyNameOwnerMaximum Generation (MW)Cost ($/MWh)
RenewableSolar1Price Taker80
ThermalThermal1Thermal Owner2010
ThermalThermal2Price Taker2030
ThermalThermal3Thermal Owner20100
ThermalThermal4Price Taker20300
ThermalThermal5Price Taker501000
ThermalThermal6Price Taker503000

Renewable Units

We will start by adding a solar unit to the database, using IARA.add_renewable_unit!.

IARA.add_renewable_unit!(
    db;
    label = "Solar1",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [80.0],
        om_cost = [0.0],
        curtailment_cost = [100.0],
    ),
    biddinggroup_id = "Price Taker",
    bus_id = "Island",
)

The generation of the solar unit requires a time series file. Like the demand, we will define it at the end of this tutorial.

Thermal Units

Now we can add the thermal units to the database, using IARA.add_thermal_unit!.

IARA.add_thermal_unit!(
    db;
    label = "Thermal1",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [20.0],
        om_cost = [10.0],
    ),
    biddinggroup_id = "Thermal Owner",
    bus_id = "Island",
)

IARA.add_thermal_unit!(
    db;
    label = "Thermal2",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [20.0],
        om_cost = [30.0],
    ),
    biddinggroup_id = "Price Taker",
    bus_id = "Island",
)

IARA.add_thermal_unit!(
    db;
    label = "Thermal3",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [20.0],
        om_cost = [100.0],
    ),
    biddinggroup_id = "Thermal Owner",
    bus_id = "Island",
)

IARA.add_thermal_unit!(
    db;
    label = "Thermal4",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [20.0],
        om_cost = [300.0],
    ),
    biddinggroup_id = "Price Taker",
    bus_id = "Island",
)

IARA.add_thermal_unit!(
    db;
    label = "Thermal5",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [50.0],
        om_cost = [1000.0],
    ),
    biddinggroup_id = "Price Taker",
    bus_id = "Island",
)

IARA.add_thermal_unit!(
    db;
    label = "Thermal6",
    parameters = DataFrame(;
        date_time = [DateTime(0)],
        existing = [1],
        max_generation = [50.0],
        om_cost = [3000.0],
    ),
    biddinggroup_id = "Price Taker",
    bus_id = "Island",
)

Time Series

Recap

In the beginning of this tutorial, we defined that there were 4 scenarios, 2 periods and 1 subperiod per period.

Loading time series files

Using a text editor, we have created the following CSV files containing time series information about the demand and solar generation:

  • demands.csv
  • solar_generation.csv

You can find them in the data/case_1 folder.

Let's take a quick look at the demand file using IARA.time_series_dataframe.

IARA.time_series_dataframe(joinpath(PATH_BASE_CASE, "demands.csv"))
8×6 DataFrame
RowperiodscenariosubperiodDemand1Demand2Demand3
Int64Int64Int64Float32Float32Float32
11111.559810.720.36
21211.080.720.36
31311.080.720.36
41411.559810.720.36
52111.80.720.36
62211.320190.720.36
72311.320190.720.36
82411.80.720.36

and the solar generation file

IARA.time_series_dataframe(joinpath(PATH_BASE_CASE, "solar_generation.csv"))
8×4 DataFrame
RowperiodscenariosubperiodSolar1
Int64Int64Int64Float32
11110.250008
21210.124992
31310.250008
41410.124992
52110.375
62210.250008
72310.375
82410.250008

Now, we have to link them to the database.

IARA.link_time_series_to_file(
    db,
    "RenewableUnit";
    generation = "solar_generation",
)

IARA.link_time_series_to_file(
    db,
    "DemandUnit";
    demand = "demands",
)

Closing the database

Now that we have added all the elements and linked the time series files, we can close the database to run the case with the function IARA.close_study!.

IARA.close_study!(db)

This page was generated using Literate.jl.