Gereralized Assets and Flows

Tulipa uses Assets and Flows as generalized components to build energy systems.

Explore the files

Take a look at the files in the my-awesome-energy-system/tutorial-2 folder.

Do you notice any changes compared to the files you worked with in tutorial 1?

Run the workflow

In my_workflow.jl you can simply change the name of your input directory and run your code.
From the Basics Tutorial, it should look something like this:

Tip

Remember to activate the environment in the current directory using the following code in your Julia REPL:

using Pkg: Pkg
Pkg.activate(".")
# Load the packages
import TulipaIO as TIO
import TulipaEnergyModel as TEM
using DuckDB
using DataFrames
using Plots

# Define the directories - notice we now select tutorial 2 for both the input and output directory
input_dir = joinpath(@__DIR__, "my-awesome-energy-system/tutorial-2")

# Create the connection and read the input files
connection = DBInterface.connect(DuckDB.DB)
TIO.read_csv_folder(connection, input_dir)

# Add the defaults
TEM.populate_with_defaults!(connection)

# Run the model
energy_problem =
    TEM.run_scenario(connection);
EnergyProblem:
  - Model created!
    - Number of variables: 96360
    - Number of constraints for variable bounds: 87600
    - Number of structural constraints: 122640
  - Model solved!
    - Termination status: OPTIMAL
    - Objective value: 1.4854146333461672e8
    - Objective breakdown:
      - assets_fixed_cost_aggregated_vintage_method: 0.0
      - assets_fixed_cost_compact_vintage_method: 0.0
      - assets_investment_cost: 0.0
      - flows_fixed_cost: 0.0
      - flows_investment_cost: 0.0
      - flows_operational_cost: 1.4854146333461672e8
      - storage_assets_energy_fixed_cost: 0.0
      - storage_assets_energy_investment_cost: 0.0
      - units_on_operational_cost: 0.0
      - vintage_flows_operational_cost: 0.0
Tip

Remember that you can always define and create the output directory if it doesn't exist to export the results to csv files. Then you can use the output_folder keyword argument in the run_scenario function to save the results in that folder.

Explore the results

Explore the flow that goes from the hub to the e_demand:

flows = TIO.get_table(connection, "var_flow")

from_asset = "hub"
to_asset = "e_demand"
year = 2030
rep_period = 1

filtered_flow = filter(
    row ->
        row.from_asset == from_asset &&
            row.to_asset == to_asset &&
            row.milestone_year == year &&
            row.rep_period == rep_period,
    flows,
)

plot(
    filtered_flow.time_block_start,
    filtered_flow.solution;
    label=string(from_asset, " -> ", to_asset),
    xlabel="Hour",
    ylabel="[MWh]",
)
Example block output

Explore the congestion using the duals in the results and displaying the first 5 rows of the table:

transport = TIO.get_table(connection, "cons_transport_flow_limit_aggregated_vintage_method")

names(transport)

first(
    filter(
    row ->
        row.dual_max_transport_flow_limit_aggregated_vintage_method != 0.0,
    transport,
    ),
    5,
)
5×10 DataFrame
Rowidfrom_assetto_assetmilestone_yearrep_periodtime_block_starttime_block_endvar_flow_iddual_max_transport_flow_limit_aggregated_vintage_methoddual_min_transport_flow_limit_aggregated_vintage_method
Int64StringStringInt32Int32Int32Int32Int64Float64Float64
125hube_demand20301252587625-45.00.0
226hube_demand20301262687626-45.00.0
327hube_demand20301272787627-45.00.0
429hube_demand20301292987629-45.00.0
530hube_demand20301303087630-45.00.0
Test Your Knowledge

Can you explain the values you get from the column dual_max_transport_flow_limit_aggregated_vintage_method? Hint: consider what is currently defining the capacity to transport the flow between the assets you see in the table.

Challenge: Add a Battery

Try adding a battery (a short-term storage asset) that can only charge from the solar PV and discharges to the e_demand consumer.