TulipaEnergy / TulipaEnergyModel.jl

An energy system optimization model that is flexible, computationally efficient, and academically robust.
Apache License 2.0
17 stars 17 forks source link

Improve/refactor dataframe creation for inter-period constraints #659

Closed datejada closed 2 days ago

datejada commented 3 days ago

What and Why

The function construct_dataframes in the file create-model.jl has the following code to create the dataframes for each inter-temporal constraint. The code it is too similar, so the idea is to explore the option to create a more general function that creates the dataframes instead of repeating code.

    # Dataframe to store the storage level between (inter) representative period variable (e.g., seasonal storage)
    # Only for storage assets
    dataframes[:storage_level_inter_rp] = DataFrame(
        (
            (
                (asset = a, periods_block = periods_block) for
                periods_block in graph[a].timeframe_partitions
            ) for a in A if graph[a].type == :storage
        ) |> Iterators.flatten,
    )
    if size(dataframes[:storage_level_inter_rp], 1) == 0
        dataframes[:storage_level_inter_rp] =
            DataFrame(; asset = Symbol[], periods_block = PeriodsBlock[])
    end
    dataframes[:storage_level_inter_rp].index = 1:size(dataframes[:storage_level_inter_rp], 1)

    # Dataframe to store the constraints for assets with maximum energy between (inter) representative periods
    # Only for assets with max energy limit
    dataframes[:max_energy_inter_rp] = DataFrame(
        (
            (
                (asset = a, periods_block = periods_block) for
                periods_block in graph[a].timeframe_partitions
            ) for a in A if !ismissing(graph[a].max_energy_timeframe_partition)
        ) |> Iterators.flatten,
    )
    if size(dataframes[:max_energy_inter_rp], 1) == 0
        dataframes[:max_energy_inter_rp] =
            DataFrame(; asset = Symbol[], periods_block = PeriodsBlock[])
    end
    dataframes[:max_energy_inter_rp].index = 1:size(dataframes[:max_energy_inter_rp], 1)

    # Dataframe to store the constraints for assets with minimum energy between (inter) representative periods
    # Only for assets with min energy limit
    dataframes[:min_energy_inter_rp] = DataFrame(
        (
            (
                (asset = a, periods_block = periods_block) for
                periods_block in graph[a].timeframe_partitions
            ) for a in A if !ismissing(graph[a].min_energy_timeframe_partition)
        ) |> Iterators.flatten,
    )
    if size(dataframes[:min_energy_inter_rp], 1) == 0
        dataframes[:min_energy_inter_rp] =
            DataFrame(; asset = Symbol[], periods_block = PeriodsBlock[])
    end
    dataframes[:min_energy_inter_rp].index = 1:size(dataframes[:min_energy_inter_rp], 1)

Possible Drawbacks

The code might be more abstract

Related Issues

658

datejada commented 3 days ago

@abelsiqueira, any other thoughts on this issue?

abelsiqueira commented 3 days ago

Not much. I think it can be implemented as a function that you pass the df_key (like :min_energy_inter_rp), and an asset_filter (like a -> !ismissing(graph[a].min_energy_timeframe_partition)). No other thoughts at the moment.