diff --git a/src/constraints/ramping-and-unit-commitment.jl b/src/constraints/ramping-and-unit-commitment.jl index 34fa23d4..22310b42 100644 --- a/src/constraints/ramping-and-unit-commitment.jl +++ b/src/constraints/ramping-and-unit-commitment.jl @@ -7,9 +7,9 @@ Adds the ramping constraints for producer and conversion assets where ramping = """ function add_ramping_constraints!( model, + variables, graph, df_units_on_and_outflows, - df_units_on, df_highest_out, outgoing_flow_highest_out_resolution, accumulated_units, @@ -55,9 +55,10 @@ function add_ramping_constraints!( model[:limit_units_on] = [ @constraint( model, - row.units_on ≤ accumulated_units[accumulated_units_lookup[(row.asset, row.year)]], - base_name = "limit_units_on[$(row.asset),$(row.year),$(row.rep_period),$(row.timesteps_block)]" - ) for row in eachrow(df_units_on) + units_on ≤ accumulated_units[accumulated_units_lookup[(row.asset, row.year)]], + base_name = "limit_units_on[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" + ) for (units_on, row) in + zip(variables[:units_on].container, eachrow(variables[:units_on].indices)) ] # - Minimum output flow above the minimum operating point diff --git a/src/constraints/storage.jl b/src/constraints/storage.jl index 2e5667af..09115b78 100644 --- a/src/constraints/storage.jl +++ b/src/constraints/storage.jl @@ -8,37 +8,41 @@ Adds the storage asset constraints to the model. function add_storage_constraints!( model, + variables, graph, dataframes, accumulated_energy_capacity, incoming_flow_lowest_storage_resolution_intra_rp, outgoing_flow_lowest_storage_resolution_intra_rp, - df_storage_intra_rp_balance_grouped, - df_storage_inter_rp_balance_grouped, - storage_level_intra_rp, - storage_level_inter_rp, incoming_flow_storage_inter_rp_balance, outgoing_flow_storage_inter_rp_balance, ) ## INTRA-TEMPORAL CONSTRAINTS (within a representative period) + storage_level_intra_rp = variables[:storage_level_intra_rp] + df_storage_intra_rp_balance_grouped = + DataFrames.groupby(storage_level_intra_rp.indices, [:asset, :year, :rep_period]) + + storage_level_inter_rp = variables[:storage_level_inter_rp] + df_storage_inter_rp_balance_grouped = + DataFrames.groupby(storage_level_inter_rp.indices, [:asset, :year]) # - Balance constraint (using the lowest temporal resolution) - for ((a, rp, y), sub_df) in pairs(df_storage_intra_rp_balance_grouped) + for ((a, y, rp), sub_df) in pairs(df_storage_intra_rp_balance_grouped) # This assumes an ordering of the time blocks, that is guaranteed inside # construct_dataframes # The storage_inflows have been moved here model[Symbol("storage_intra_rp_balance_$(a)_$(y)_$(rp)")] = [ @constraint( model, - storage_level_intra_rp[row.index] == + storage_level_intra_rp.container[row.index] == ( if k > 1 - storage_level_intra_rp[row.index-1] # This assumes contiguous index + storage_level_intra_rp.container[row.index-1] # This assumes contiguous index else ( if ismissing(graph[a].initial_storage_level[row.year]) - storage_level_intra_rp[last(sub_df.index)] + storage_level_intra_rp.container[last(sub_df.index)] else graph[a].initial_storage_level[row.year] end @@ -51,12 +55,12 @@ function add_storage_constraints!( row.year, row.year, ("inflows", rp), - row.timesteps_block, + row.time_block_start:row.time_block_end, 0.0, ) * graph[a].storage_inflows[row.year] + incoming_flow_lowest_storage_resolution_intra_rp[row.index] - outgoing_flow_lowest_storage_resolution_intra_rp[row.index], - base_name = "storage_intra_rp_balance[$a,$y,$rp,$(row.timesteps_block)]" + base_name = "storage_intra_rp_balance[$a,$y,$rp,$(row.time_block_start:row.time_block_end)]" ) for (k, row) in enumerate(eachrow(sub_df)) ] end @@ -65,44 +69,44 @@ function add_storage_constraints!( model[:max_storage_level_intra_rp_limit] = [ @constraint( model, - storage_level_intra_rp[row.index] ≤ + storage_level_intra_rp.container[row.index] ≤ profile_aggregation( Statistics.mean, graph[row.asset].rep_periods_profiles, row.year, row.year, ("max-storage-level", row.rep_period), - row.timesteps_block, + row.time_block_start:row.time_block_end, 1.0, ) * accumulated_energy_capacity[row.year, row.asset], - base_name = "max_storage_level_intra_rp_limit[$(row.asset),$(row.year),$(row.rep_period),$(row.timesteps_block)]" - ) for row in eachrow(dataframes[:storage_level_intra_rp]) + base_name = "max_storage_level_intra_rp_limit[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" + ) for row in eachrow(storage_level_intra_rp.indices) ] # - Minimum storage level model[:min_storage_level_intra_rp_limit] = [ @constraint( model, - storage_level_intra_rp[row.index] ≥ + storage_level_intra_rp.container[row.index] ≥ profile_aggregation( Statistics.mean, graph[row.asset].rep_periods_profiles, row.year, row.year, ("min_storage_level", row.rep_period), - row.timesteps_block, + row.time_block_start:row.time_block_end, 0.0, ) * accumulated_energy_capacity[row.year, row.asset], - base_name = "min_storage_level_intra_rp_limit[$(row.asset),$(row.year),$(row.rep_period),$(row.timesteps_block)]" - ) for row in eachrow(dataframes[:storage_level_intra_rp]) + base_name = "min_storage_level_intra_rp_limit[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" + ) for row in eachrow(storage_level_intra_rp.indices) ] # - Cycling condition - for ((a, _, y), sub_df) in pairs(df_storage_intra_rp_balance_grouped) + for ((a, y, _), sub_df) in pairs(df_storage_intra_rp_balance_grouped) # Ordering is assumed if !ismissing(graph[a].initial_storage_level[y]) JuMP.set_lower_bound( - storage_level_intra_rp[last(sub_df.index)], + storage_level_intra_rp.container[last(sub_df.index)], graph[a].initial_storage_level[y], ) end @@ -118,14 +122,14 @@ function add_storage_constraints!( model[Symbol("storage_inter_rp_balance_$(a)_$(y)")] = [ @constraint( model, - storage_level_inter_rp[row.index] == + storage_level_inter_rp.container[row.index] == ( if k > 1 - storage_level_inter_rp[row.index-1] # This assumes contiguous index + storage_level_inter_rp.container[row.index-1] # This assumes contiguous index else ( if ismissing(graph[a].initial_storage_level[row.year]) - storage_level_inter_rp[last(sub_df.index)] + storage_level_inter_rp.container[last(sub_df.index)] else graph[a].initial_storage_level[row.year] end @@ -135,7 +139,7 @@ function add_storage_constraints!( row.inflows_profile_aggregation + incoming_flow_storage_inter_rp_balance[row.index] - outgoing_flow_storage_inter_rp_balance[row.index], - base_name = "storage_inter_rp_balance[$a,$(row.year),$(row.periods_block)]" + base_name = "storage_inter_rp_balance[$a,$(row.year),$(row.period_block_start):$(row.period_block_end)]" ) for (k, row) in enumerate(eachrow(sub_df)) ] end @@ -144,36 +148,36 @@ function add_storage_constraints!( model[:max_storage_level_inter_rp_limit] = [ @constraint( model, - storage_level_inter_rp[row.index] ≤ + storage_level_inter_rp.container[row.index] ≤ profile_aggregation( Statistics.mean, graph[row.asset].timeframe_profiles, row.year, row.year, "max_storage_level", - row.periods_block, + row.period_block_start:row.period_block_end, 1.0, ) * accumulated_energy_capacity[row.year, row.asset], - base_name = "max_storage_level_inter_rp_limit[$(row.asset),$(row.year),$(row.periods_block)]" - ) for row in eachrow(dataframes[:storage_level_inter_rp]) + base_name = "max_storage_level_inter_rp_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" + ) for row in eachrow(storage_level_inter_rp.indices) ] # - Minimum storage level model[:min_storage_level_inter_rp_limit] = [ @constraint( model, - storage_level_inter_rp[row.index] ≥ + storage_level_inter_rp.container[row.index] ≥ profile_aggregation( Statistics.mean, graph[row.asset].timeframe_profiles, row.year, row.year, "min_storage_level", - row.periods_block, + row.period_block_start:row.period_block_end, 0.0, ) * accumulated_energy_capacity[row.year, row.asset], - base_name = "min_storage_level_inter_rp_limit[$(row.asset),$(row.year),$(row.periods_block)]" - ) for row in eachrow(dataframes[:storage_level_inter_rp]) + base_name = "min_storage_level_inter_rp_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" + ) for row in eachrow(storage_level_inter_rp.indices) ] # - Cycling condition @@ -181,7 +185,7 @@ function add_storage_constraints!( # Ordering is assumed if !ismissing(graph[a].initial_storage_level[y]) JuMP.set_lower_bound( - storage_level_inter_rp[last(sub_df.index)], + storage_level_inter_rp.container[last(sub_df.index)], graph[a].initial_storage_level[y], ) end diff --git a/src/constraints/transport.jl b/src/constraints/transport.jl index e6c2e5a6..839caf32 100644 --- a/src/constraints/transport.jl +++ b/src/constraints/transport.jl @@ -36,7 +36,7 @@ function add_transport_constraints!( row.year, row.year, ("availability", row.rep_period), - row.timesteps_block, + row.time_block_start:row.time_block_end, 1.0, ) * graph[row.from, row.to].capacity * @@ -54,7 +54,7 @@ function add_transport_constraints!( row.year, row.year, ("availability", row.rep_period), - row.timesteps_block, + row.time_block_start:row.time_block_end, 1.0, ) * graph[row.from, row.to].capacity * @@ -69,7 +69,7 @@ function add_transport_constraints!( @constraint( model, flow[row.index] ≤ upper_bound_transport_flow[idx], - base_name = "max_transport_flow_limit[($(row.from),$(row.to)),$(row.year),$(row.rep_period),$(row.timesteps_block)]" + base_name = "max_transport_flow_limit[($(row.from),$(row.to)),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" ) for (idx, row) in enumerate(eachrow(transport_flows_indices)) ] @@ -78,7 +78,7 @@ function add_transport_constraints!( @constraint( model, flow[row.index] ≥ -lower_bound_transport_flow[idx], - base_name = "min_transport_flow_limit[($(row.from),$(row.to)),$(row.year),$(row.rep_period),$(row.timesteps_block)]" + base_name = "min_transport_flow_limit[($(row.from),$(row.to)),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" ) for (idx, row) in enumerate(eachrow(transport_flows_indices)) ] end diff --git a/src/create-model.jl b/src/create-model.jl index eefaf0e6..e9febf6c 100644 --- a/src/create-model.jl +++ b/src/create-model.jl @@ -62,13 +62,7 @@ function create_model( # Unpacking dataframes @timeit to "unpacking dataframes" begin - df_flows = dataframes[:flows] - df_units_on = dataframes[:units_on] df_units_on_and_outflows = dataframes[:units_on_and_outflows] - df_storage_intra_rp_balance_grouped = - DataFrames.groupby(dataframes[:storage_level_intra_rp], [:asset, :rep_period, :year]) - df_storage_inter_rp_balance_grouped = - DataFrames.groupby(dataframes[:storage_level_inter_rp], [:asset, :year]) end ## Model @@ -84,14 +78,6 @@ function create_model( ) @timeit to "add_storage_variables!" add_storage_variables!(model, graph, sets, variables) - # TODO: This should disapear after the changes on add_expressions_to_dataframe! and storing the solution - model[:flow] = df_flows.flow = variables[:flow].container - model[:units_on] = df_units_on.units_on = variables[:units_on].container - storage_level_intra_rp = - model[:storage_level_intra_rp] = variables[:storage_level_intra_rp].container - storage_level_inter_rp = - model[:storage_level_inter_rp] = variables[:storage_level_inter_rp].container - ## Add expressions to dataframes # TODO: What will improve this? Variables (#884)?, Constraints? ( @@ -171,15 +157,12 @@ function create_model( @timeit to "add_storage_constraints!" add_storage_constraints!( model, + variables, graph, dataframes, accumulated_energy_capacity, incoming_flow_lowest_storage_resolution_intra_rp, outgoing_flow_lowest_storage_resolution_intra_rp, - df_storage_intra_rp_balance_grouped, - df_storage_inter_rp_balance_grouped, - storage_level_intra_rp, - storage_level_inter_rp, incoming_flow_storage_inter_rp_balance, outgoing_flow_storage_inter_rp_balance, ) @@ -224,9 +207,9 @@ function create_model( if !isempty(dataframes[:units_on_and_outflows]) @timeit to "add_ramping_constraints!" add_ramping_constraints!( model, + variables, graph, df_units_on_and_outflows, - df_units_on, dataframes[:highest_out], outgoing_flow_highest_out_resolution, accumulated_units, diff --git a/src/io.jl b/src/io.jl index 28b8b073..95cda782 100644 --- a/src/io.jl +++ b/src/io.jl @@ -230,7 +230,11 @@ function create_internal_structures(connection) graph = MetaGraphsNext.MetaGraph(_graph, asset_data, flow_data, nothing, nothing, nothing) + # TODO: Move these function calls to the correct place tmp_create_partition_tables(connection) + tmp_create_union_tables(connection) + tmp_create_lowest_resolution_table(connection) + df = TulipaIO.get_table(connection, "asset_time_resolution") gdf = DataFrames.groupby(df, [:asset, :year, :rep_period]) for ((a, year, rp), _df) in pairs(gdf) @@ -460,70 +464,71 @@ function save_solution_to_file(output_folder, graph, dataframes, solution) or 30 for flows. =# - output_file = joinpath(output_folder, "flows.csv") - output_table = DataFrames.select( - dataframes[:flows], - :from, - :to, - :year, - :rep_period, - :timesteps_block => :timestep, - ) - output_table.value = solution.flow - output_table = DataFrames.flatten( - DataFrames.transform( - output_table, - [:timestep, :value] => - DataFrames.ByRow( - (timesteps_block, value) -> begin # transform each row using these two columns - n = length(timesteps_block) - (timesteps_block, Iterators.repeated(value, n)) # e.g., (3:5, [30, 30, 30]) - end, - ) => [:timestep, :value], - ), - [:timestep, :value], # flatten, e.g., [(3, 30), (4, 30), (5, 30)] - ) - output_table |> CSV.write(output_file) - - output_file = joinpath(output_folder, "storage-level-intra-rp.csv") - output_table = DataFrames.select( - dataframes[:storage_level_intra_rp], - :asset, - :rep_period, - :timesteps_block => :timestep, - ) - output_table.value = solution.storage_level_intra_rp - if !isempty(output_table.asset) - output_table = DataFrames.combine(DataFrames.groupby(output_table, :asset)) do subgroup - _check_initial_storage_level!(subgroup, graph) - _interpolate_storage_level!(subgroup, :timestep) - end - end - output_table |> CSV.write(output_file) - - output_file = joinpath(output_folder, "storage-level-inter-rp.csv") - output_table = - DataFrames.select(dataframes[:storage_level_inter_rp], :asset, :periods_block => :period) - output_table.value = solution.storage_level_inter_rp - if !isempty(output_table.asset) - output_table = DataFrames.combine(DataFrames.groupby(output_table, :asset)) do subgroup - _check_initial_storage_level!(subgroup, graph) - _interpolate_storage_level!(subgroup, :period) - end - end - output_table |> CSV.write(output_file) - - output_file = joinpath(output_folder, "max-energy-inter-rp.csv") - output_table = - DataFrames.select(dataframes[:max_energy_inter_rp], :asset, :periods_block => :period) - output_table.value = solution.max_energy_inter_rp - output_table |> CSV.write(output_file) - - output_file = joinpath(output_folder, "min-energy-inter-rp.csv") - output_table = - DataFrames.select(dataframes[:min_energy_inter_rp], :asset, :periods_block => :period) - output_table.value = solution.min_energy_inter_rp - output_table |> CSV.write(output_file) + # TODO: Fix all output + # output_file = joinpath(output_folder, "flows.csv") + # output_table = DataFrames.select( + # dataframes[:flows], + # :from, + # :to, + # :year, + # :rep_period, + # :timesteps_block => :timestep, + # ) + # output_table.value = solution.flow + # output_table = DataFrames.flatten( + # DataFrames.transform( + # output_table, + # [:timestep, :value] => + # DataFrames.ByRow( + # (timesteps_block, value) -> begin # transform each row using these two columns + # n = length(timesteps_block) + # (timesteps_block, Iterators.repeated(value, n)) # e.g., (3:5, [30, 30, 30]) + # end, + # ) => [:timestep, :value], + # ), + # [:timestep, :value], # flatten, e.g., [(3, 30), (4, 30), (5, 30)] + # ) + # output_table |> CSV.write(output_file) + + # output_file = joinpath(output_folder, "storage-level-intra-rp.csv") + # output_table = DataFrames.select( + # dataframes[:storage_level_intra_rp], + # :asset, + # :rep_period, + # :timesteps_block => :timestep, + # ) + # output_table.value = solution.storage_level_intra_rp + # if !isempty(output_table.asset) + # output_table = DataFrames.combine(DataFrames.groupby(output_table, :asset)) do subgroup + # _check_initial_storage_level!(subgroup, graph) + # _interpolate_storage_level!(subgroup, :timestep) + # end + # end + # output_table |> CSV.write(output_file) + + # output_file = joinpath(output_folder, "storage-level-inter-rp.csv") + # output_table = + # DataFrames.select(dataframes[:storage_level_inter_rp], :asset, :periods_block => :period) + # output_table.value = solution.storage_level_inter_rp + # if !isempty(output_table.asset) + # output_table = DataFrames.combine(DataFrames.groupby(output_table, :asset)) do subgroup + # _check_initial_storage_level!(subgroup, graph) + # _interpolate_storage_level!(subgroup, :period) + # end + # end + # output_table |> CSV.write(output_file) + # + # output_file = joinpath(output_folder, "max-energy-inter-rp.csv") + # output_table = + # DataFrames.select(dataframes[:max_energy_inter_rp], :asset, :periods_block => :period) + # output_table.value = solution.max_energy_inter_rp + # output_table |> CSV.write(output_file) + # + # output_file = joinpath(output_folder, "min-energy-inter-rp.csv") + # output_table = + # DataFrames.select(dataframes[:min_energy_inter_rp], :asset, :periods_block => :period) + # output_table.value = solution.min_energy_inter_rp + # output_table |> CSV.write(output_file) return end diff --git a/src/model-preparation.jl b/src/model-preparation.jl index b7df4e02..72f5e1a3 100644 --- a/src/model-preparation.jl +++ b/src/model-preparation.jl @@ -89,55 +89,6 @@ function construct_dataframes( ) dataframes[:highest_out].index = 1:size(dataframes[:highest_out], 1) - # DataFrame to store the flow variables - dataframes[:flows] = DuckDB.query( - connection, - "SELECT - from_asset as from, - to_asset as to, - year, - rep_period, - efficiency, - time_block_start, - time_block_end - FROM flow_time_resolution", - ) |> DataFrame - dataframes[:flows].timesteps_block = map( - r -> r[1]:r[2], - zip(dataframes[:flows].time_block_start, dataframes[:flows].time_block_end), - ) - dataframes[:flows].index = 1:size(dataframes[:flows], 1) - - # DataFrame to store the units_on variables - dataframes[:units_on] = - DuckDB.query( - connection, - "SELECT - atr.asset, - atr.year, - atr.rep_period, - atr.time_block_start, - atr.time_block_end - FROM asset_time_resolution AS atr - LEFT JOIN asset - ON asset.asset = atr.asset - WHERE - asset.type IN ('producer','conversion') - AND asset.unit_commitment = true", - ) |> DataFrame - - dataframes[:units_on].timesteps_block = map( - r -> r[1]:r[2], - zip(dataframes[:units_on].time_block_start, dataframes[:units_on].time_block_end), - ) - - dataframes[:units_on].index = 1:size(dataframes[:units_on], 1) - - # Dataframe to store the storage level variable between (inter) representative period (e.g., seasonal storage) - # Only for storage assets - dataframes[:storage_level_inter_rp] = - _construct_inter_rp_dataframes(A, graph, years, a -> a.type == "storage") - # 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] = _construct_inter_rp_dataframes( @@ -213,7 +164,7 @@ This strategy is based on the replies in this discourse thread: """ function add_expression_terms_intra_rp_constraints!( df_cons, - df_flows, + flow::TulipaVariable, workspace, representative_periods, graph; @@ -245,7 +196,7 @@ function add_expression_terms_intra_rp_constraints!( if conditions_to_add_min_outgoing_flow_duration df_cons[!, :min_outgoing_flow_duration] .= 1 end - grouped_flows = DataFrames.groupby(df_flows, [:year, :rep_period, case.asset_match]) + grouped_flows = DataFrames.groupby(flow.indices, [:year, :rep_period, case.asset_match]) for ((year, rep_period, asset), sub_df) in pairs(grouped_cons) if !haskey(grouped_flows, (year, rep_period, asset)) continue @@ -259,7 +210,7 @@ function add_expression_terms_intra_rp_constraints!( # Store the corresponding flow in the workspace for row in eachrow(grouped_flows[(year, rep_period, asset)]) asset = row[case.asset_match] - for t in row.timesteps_block + for t in row.time_block_start:row.time_block_end # Set the efficiency to 1 for inflows and outflows of hub and consumer assets, and outflows for producer assets # And when you want the highest resolution (which is asset type-agnostic) efficiency_coefficient = @@ -275,18 +226,27 @@ function add_expression_terms_intra_rp_constraints!( end JuMP.add_to_expression!( workspace[t], - row.flow, + flow.container[row.index], resolution * efficiency_coefficient, ) if conditions_to_add_min_outgoing_flow_duration - outgoing_flow_durations = - min(outgoing_flow_durations, length(row.timesteps_block)) + outgoing_flow_durations = min( + outgoing_flow_durations, + row.time_block_end - row.time_block_start + 1, + ) end end end # Sum the corresponding flows from the workspace for row in eachrow(sub_df) - row[case.col_name] = agg(@view workspace[row.timesteps_block]) + # TODO: This is a hack to handle constraint tables that still have timesteps_block + # In particular, storage_level_intra_rp + if haskey(row, :timesteps_block) + row[case.col_name] = agg(@view workspace[row.timesteps_block]) + else + row[case.col_name] = + agg(@view workspace[row.time_block_start:row.time_block_end]) + end if conditions_to_add_min_outgoing_flow_duration row[:min_outgoing_flow_duration] = outgoing_flow_durations end @@ -362,14 +322,18 @@ This strategy is based on the replies in this discourse thread: - https://discourse.julialang.org/t/help-improving-the-speed-of-a-dataframes-operation/107615/23 """ -function add_expression_units_on_terms_intra_rp_constraints!(df_cons, df_units_on, workspace) +function add_expression_units_on_terms_intra_rp_constraints!( + df_cons, + units_on::TulipaVariable, + workspace, +) # Aggregating function: since the constraint is in the highest resolution we can aggregate with unique. agg = v -> sum(unique(v)) grouped_cons = DataFrames.groupby(df_cons, [:rep_period, :asset]) df_cons[!, :units_on] .= JuMP.AffExpr(0.0) - grouped_units_on = DataFrames.groupby(df_units_on, [:rep_period, :asset]) + grouped_units_on = DataFrames.groupby(units_on.indices, [:rep_period, :asset]) for ((rep_period, asset), sub_df) in pairs(grouped_cons) haskey(grouped_units_on, (rep_period, asset)) || continue @@ -378,8 +342,8 @@ function add_expression_units_on_terms_intra_rp_constraints!(df_cons, df_units_o end # Store the corresponding variables in the workspace for row in eachrow(grouped_units_on[(rep_period, asset)]) - for t in row.timesteps_block - JuMP.add_to_expression!(workspace[t], row.units_on) + for t in row.time_block_start:row.time_block_end + JuMP.add_to_expression!(workspace[t], units_on.container[row.index]) end end # Apply the agg funtion to the corresponding variables from the workspace @@ -405,7 +369,7 @@ This function is only used internally in the model. """ function add_expression_terms_inter_rp_constraints!( df_inter, - df_flows, + flow::TulipaVariable, df_map, graph, representative_periods; @@ -423,7 +387,11 @@ function add_expression_terms_inter_rp_constraints!( # Incoming, outgoing flows, and profile aggregation for row_inter in eachrow(df_inter) - sub_df_map = filter(:period => in(row_inter.periods_block), df_map; view = true) + sub_df_map = filter( + :period => p -> row_inter.period_block_start <= p <= row_inter.period_block_end, + df_map; + view = true, + ) for row_map in eachrow(sub_df_map) # Skip inactive row_inter or undefined for that year @@ -436,33 +404,36 @@ function add_expression_terms_inter_rp_constraints!( [:from, :year, :rep_period] => (from, y, rp) -> (from, y, rp) == (row_inter.asset, row_map.year, row_map.rep_period), - df_flows; + flow.indices; view = true, ) - sub_df_flows.duration = length.(sub_df_flows.timesteps_block) + sub_df_flows.duration = sub_df_flows.time_block_end - sub_df_flows.time_block_start .+ 1 if is_storage_level row_inter.outgoing_flow += LinearAlgebra.dot( - sub_df_flows.flow, + flow.container[sub_df_flows.index], sub_df_flows.duration ./ sub_df_flows.efficiency, ) * row_map.weight else row_inter.outgoing_flow += - LinearAlgebra.dot(sub_df_flows.flow, sub_df_flows.duration) * row_map.weight + LinearAlgebra.dot(flow.container[sub_df_flows.index], sub_df_flows.duration) * + row_map.weight end if is_storage_level + # TODO: There is some repetition here or am I missing something? sub_df_flows = filter( [:to, :year, :rep_period] => (to, y, rp) -> (to, y, rp) == (row_inter.asset, row_map.year, row_map.rep_period), - df_flows; + flow.indices; view = true, ) - sub_df_flows.duration = length.(sub_df_flows.timesteps_block) + sub_df_flows.duration = + sub_df_flows.time_block_end - sub_df_flows.time_block_start .+ 1 row_inter.incoming_flow += LinearAlgebra.dot( - sub_df_flows.flow, + flow.container[sub_df_flows.index], sub_df_flows.duration .* sub_df_flows.efficiency, ) * row_map.weight @@ -500,16 +471,20 @@ function add_expressions_to_dataframe!( # Creating the incoming and outgoing flow expressions add_expression_terms_intra_rp_constraints!( dataframes[:lowest], - dataframes[:flows], + variables[:flow], expression_workspace, representative_periods, graph; use_highest_resolution = false, multiply_by_duration = true, ) + # TODO: storage_level_intra_rp is serving as a constraints indices + # This should be fixed when: + # - the constraint is separate from the variable + # - the incoming and outgoing flows are stored outside the DF add_expression_terms_intra_rp_constraints!( - dataframes[:storage_level_intra_rp], - dataframes[:flows], + variables[:storage_level_intra_rp].indices, + variables[:flow], expression_workspace, representative_periods, graph; @@ -518,7 +493,7 @@ function add_expressions_to_dataframe!( ) add_expression_terms_intra_rp_constraints!( dataframes[:highest_in_out], - dataframes[:flows], + variables[:flow], expression_workspace, representative_periods, graph; @@ -527,7 +502,7 @@ function add_expressions_to_dataframe!( ) add_expression_terms_intra_rp_constraints!( dataframes[:highest_in], - dataframes[:flows], + variables[:flow], expression_workspace, representative_periods, graph; @@ -536,7 +511,7 @@ function add_expressions_to_dataframe!( ) add_expression_terms_intra_rp_constraints!( dataframes[:highest_out], - dataframes[:flows], + variables[:flow], expression_workspace, representative_periods, graph; @@ -547,7 +522,7 @@ function add_expressions_to_dataframe!( if !isempty(dataframes[:units_on_and_outflows]) add_expression_terms_intra_rp_constraints!( dataframes[:units_on_and_outflows], - dataframes[:flows], + variables[:flow], expression_workspace, representative_periods, graph; @@ -557,23 +532,30 @@ function add_expressions_to_dataframe!( ) end add_expression_terms_inter_rp_constraints!( - dataframes[:storage_level_inter_rp], - dataframes[:flows], + variables[:storage_level_inter_rp].indices, + variables[:flow], timeframe.map_periods_to_rp, graph, representative_periods; is_storage_level = true, ) + # TODO: This hack allows changing the function to use period_block_start and period_block_end + # This can be removed after the max_energy_inter_rp and min_energy_inter_rp are moved to TulipaVariable + for name in (:max_energy_inter_rp, :min_energy_inter_rp) + df = dataframes[name] + df.period_block_start = first.(df.periods_block) + df.period_block_end = last.(df.periods_block) + end add_expression_terms_inter_rp_constraints!( dataframes[:max_energy_inter_rp], - dataframes[:flows], + variables[:flow], timeframe.map_periods_to_rp, graph, representative_periods, ) add_expression_terms_inter_rp_constraints!( dataframes[:min_energy_inter_rp], - dataframes[:flows], + variables[:flow], timeframe.map_periods_to_rp, graph, representative_periods, @@ -593,7 +575,7 @@ function add_expressions_to_dataframe!( if !isempty(dataframes[:units_on_and_outflows]) add_expression_units_on_terms_intra_rp_constraints!( dataframes[:units_on_and_outflows], - dataframes[:units_on], + variables[:units_on], expression_workspace, ) end @@ -604,10 +586,10 @@ function add_expressions_to_dataframe!( model[:outgoing_flow_lowest_resolution] = dataframes[:lowest].outgoing_flow incoming_flow_lowest_storage_resolution_intra_rp = model[:incoming_flow_lowest_storage_resolution_intra_rp] = - dataframes[:storage_level_intra_rp].incoming_flow + variables[:storage_level_intra_rp].indices.incoming_flow outgoing_flow_lowest_storage_resolution_intra_rp = model[:outgoing_flow_lowest_storage_resolution_intra_rp] = - dataframes[:storage_level_intra_rp].outgoing_flow + variables[:storage_level_intra_rp].indices.outgoing_flow incoming_flow_highest_in_out_resolution = model[:incoming_flow_highest_in_out_resolution] = dataframes[:highest_in_out].incoming_flow @@ -620,10 +602,10 @@ function add_expressions_to_dataframe!( model[:outgoing_flow_highest_out_resolution] = dataframes[:highest_out].outgoing_flow incoming_flow_storage_inter_rp_balance = model[:incoming_flow_storage_inter_rp_balance] = - dataframes[:storage_level_inter_rp].incoming_flow + variables[:storage_level_inter_rp].indices.incoming_flow outgoing_flow_storage_inter_rp_balance = model[:outgoing_flow_storage_inter_rp_balance] = - dataframes[:storage_level_inter_rp].outgoing_flow + variables[:storage_level_inter_rp].indices.outgoing_flow # Below, we drop zero coefficients, but probably we don't have any # (if the implementation is correct) JuMP.drop_zeros!.(incoming_flow_lowest_resolution) diff --git a/src/objective.jl b/src/objective.jl index 268e2624..4a7159b6 100644 --- a/src/objective.jl +++ b/src/objective.jl @@ -125,9 +125,14 @@ function add_objective!( sum( weight_for_operation_discounts[row.year] * representative_periods[row.year][row.rep_period].weight * - duration(row.timesteps_block, row.rep_period, representative_periods[row.year]) * + duration( + row.time_block_start:row.time_block_end, + row.rep_period, + representative_periods[row.year], + ) * graph[row.from, row.to].variable_cost[row.year] * - row.flow for row in eachrow(dataframes[:flows]) + flow for (flow, row) in + zip(variables[:flow].container, eachrow(variables[:flow].indices)) ) ) @@ -136,9 +141,14 @@ function add_objective!( sum( weight_for_operation_discounts[row.year] * representative_periods[row.year][row.rep_period].weight * - duration(row.timesteps_block, row.rep_period, representative_periods[row.year]) * + duration( + row.time_block_start:row.time_block_end, + row.rep_period, + representative_periods[row.year], + ) * graph[row.asset].units_on_cost[row.year] * - row.units_on for row in eachrow(dataframes[:units_on]) if + units_on for (units_on, row) in + zip(variables[:units_on].container, eachrow(variables[:units_on].indices)) if !ismissing(graph[row.asset].units_on_cost[row.year]) ) ) diff --git a/src/solve-model.jl b/src/solve-model.jl index 823aac48..26edfa70 100644 --- a/src/solve-model.jl +++ b/src/solve-model.jl @@ -42,36 +42,41 @@ function solve_model!( graph[a].investment_integer_storage_energy ? round(Int, value) : value end - for row in eachrow(energy_problem.dataframes[:storage_level_intra_rp]) - a, rp, timesteps_block, value = - row.asset, row.rep_period, row.timesteps_block, row.solution - graph[a].storage_level_intra_rp[(rp, timesteps_block)] = value - end - - for row in eachrow(energy_problem.dataframes[:storage_level_inter_rp]) - a, pb, value = row.asset, row.periods_block, row.solution - graph[a].storage_level_inter_rp[pb] = value - end - - for row in eachrow(energy_problem.dataframes[:max_energy_inter_rp]) - a, pb, value = row.asset, row.periods_block, row.solution - graph[a].max_energy_inter_rp[pb] = value - end - - for row in eachrow(energy_problem.dataframes[:min_energy_inter_rp]) - a, pb, value = row.asset, row.periods_block, row.solution - graph[a].min_energy_inter_rp[pb] = value - end + # TODO: fix this + # for row in eachrow(energy_problem.dataframes[:storage_level_intra_rp]) + # a, rp, timesteps_block, value = + # row.asset, row.rep_period, row.timesteps_block, row.solution + # graph[a].storage_level_intra_rp[(rp, timesteps_block)] = value + # end + # + # for row in eachrow(energy_problem.dataframes[:storage_level_inter_rp]) + # a, pb, value = row.asset, row.periods_block, row.solution + # graph[a].storage_level_inter_rp[pb] = value + # end + # + # for row in eachrow(energy_problem.dataframes[:max_energy_inter_rp]) + # a, pb, value = row.asset, row.periods_block, row.solution + # graph[a].max_energy_inter_rp[pb] = value + # end + # + # for row in eachrow(energy_problem.dataframes[:min_energy_inter_rp]) + # a, pb, value = row.asset, row.periods_block, row.solution + # graph[a].min_energy_inter_rp[pb] = value + # end for ((y, (u, v)), value) in energy_problem.solution.flows_investment graph[u, v].investment[y] = graph[u, v].investment_integer ? round(Int, value) : value end - for row in eachrow(energy_problem.dataframes[:flows]) - u, v, rp, timesteps_block, value = - row.from, row.to, row.rep_period, row.timesteps_block, row.solution - graph[u, v].flow[(rp, timesteps_block)] = value - end + # TODO: Fix this + # for row in eachrow(energy_problem.variables[:flow].indices) + # u, v, rp, timesteps_block, value = row.from, + # row.to, + # row.rep_period, + # row.time_block_start:row.time_block_end, + # row.solution + # graph[u, v].flow[(rp, timesteps_block)] = value + # end end energy_problem.timings["solving the model"] = elapsed_time_solve_model @@ -95,11 +100,12 @@ function solve_model!(dataframes, model, args...; kwargs...) return nothing end - dataframes[:flows].solution = solution.flow - dataframes[:storage_level_intra_rp].solution = solution.storage_level_intra_rp - dataframes[:storage_level_inter_rp].solution = solution.storage_level_inter_rp - dataframes[:max_energy_inter_rp].solution = solution.max_energy_inter_rp - dataframes[:min_energy_inter_rp].solution = solution.min_energy_inter_rp + # TODO: fix this later + # dataframes[:flow].solution = solution.flow + # dataframes[:storage_level_intra_rp].solution = solution.storage_level_intra_rp + # dataframes[:storage_level_inter_rp].solution = solution.storage_level_inter_rp + # dataframes[:max_energy_inter_rp].solution = solution.max_energy_inter_rp + # dataframes[:min_energy_inter_rp].solution = solution.min_energy_inter_rp return solution end @@ -190,11 +196,11 @@ function solve_model( Dict(k => JuMP.value(v) for (k, v) in variables[:assets_investment].lookup), Dict(k => JuMP.value(v) for (k, v) in variables[:assets_investment_energy].lookup), Dict(k => JuMP.value(v) for (k, v) in variables[:flows_investment].lookup), - JuMP.value.(model[:storage_level_intra_rp]), - JuMP.value.(model[:storage_level_inter_rp]), + JuMP.value.(variables[:storage_level_intra_rp].container), + JuMP.value.(variables[:storage_level_inter_rp].container), JuMP.value.(model[:max_energy_inter_rp]), JuMP.value.(model[:min_energy_inter_rp]), - JuMP.value.(model[:flow]), + JuMP.value.(variables[:flow].container), JuMP.objective_value(model), compute_dual_variables(model), ) diff --git a/src/structures.jl b/src/structures.jl index c4984307..5688f481 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -308,7 +308,7 @@ mutable struct EnergyProblem end elapsed_time_vars = @elapsed begin - variables = compute_variables_indices(connection, dataframes) + variables = compute_variables_indices(connection) end energy_problem = new( diff --git a/src/time-resolution.jl b/src/time-resolution.jl index 97704f89..7860a371 100644 --- a/src/time-resolution.jl +++ b/src/time-resolution.jl @@ -36,19 +36,19 @@ function compute_constraints_partitions(graph, representative_periods, years) strategy = :lowest, asset_filter = (a, y) -> graph[a].type in ["conversion", "producer"], ), - ( - name = :storage_level_intra_rp, - partitions = _all, - strategy = :lowest, - asset_filter = (a, y) -> graph[a].type == "storage" && !graph[a].is_seasonal, - ), - ( - name = :lowest_in_out, - partitions = _allflows, - strategy = :lowest, - asset_filter = (a, y) -> - graph[a].type == "storage" && !ismissing(graph[a].use_binary_storage_method), - ), + # ( + # name = :storage_level_intra_rp, + # partitions = _all, + # strategy = :lowest, + # asset_filter = (a, y) -> graph[a].type == "storage" && !graph[a].is_seasonal, + # ), + # ( + # name = :lowest_in_out, + # partitions = _allflows, + # strategy = :lowest, + # asset_filter = (a, y) -> + # graph[a].type == "storage" && !ismissing(graph[a].use_binary_storage_method), + # ), # ( # WIP: Testing removing this in favor of using table cons_indices_highest_in_out # name = :highest_in_out, # partitions = _allflows, @@ -67,13 +67,13 @@ function compute_constraints_partitions(graph, representative_periods, years) # strategy = :highest, # asset_filter = (a, y) -> graph[a].type in ["producer", "storage", "conversion"], # ), - ( - name = :units_on, - partitions = _assets, - strategy = :highest, - asset_filter = (a, y) -> - graph[a].type in ["producer", "conversion"] && graph[a].unit_commitment, - ), + # ( + # name = :units_on, + # partitions = _assets, + # strategy = :highest, + # asset_filter = (a, y) -> + # graph[a].type in ["producer", "conversion"] && graph[a].unit_commitment, + # ), ( name = :units_on_and_outflows, partitions = _assets_and_outflows, diff --git a/src/tmp.jl b/src/tmp.jl index f548093e..a5922a50 100644 --- a/src/tmp.jl +++ b/src/tmp.jl @@ -11,7 +11,9 @@ function _append_given_durations(appender, row, durations) DuckDB.append(appender, row.to_asset) end DuckDB.append(appender, row.year) - DuckDB.append(appender, row.rep_period) + if haskey(row, :rep_period) + DuckDB.append(appender, row.rep_period) + end if haskey(row, :efficiency) DuckDB.append(appender, row.efficiency) end @@ -188,6 +190,148 @@ function tmp_create_partition_tables(connection) _append_given_durations(appender, row, durations) end DuckDB.close(appender) + + DBInterface.execute( + connection, + "CREATE OR REPLACE TABLE asset_timeframe_time_resolution( + asset STRING, + year INT, + period_block_start INT, + period_block_end INT + )", + ) + + appender = DuckDB.Appender(connection, "asset_timeframe_time_resolution") + for row in DuckDB.query( + connection, + "SELECT asset, sub.year, specification, partition, num_periods AS num_periods + FROM assets_timeframe_partitions AS main + LEFT JOIN ( + SELECT year, MAX(period) AS num_periods + FROM timeframe_data + GROUP BY year + ) AS sub + ON main.year = sub.year + ", + ) + durations = if row.specification == "uniform" + step = parse(Int, row.partition) + durations = Iterators.repeated(step, div(row.num_periods, step)) + elseif row.specification == "explicit" + durations = parse.(Int, split(row.partition, ";")) + elseif row.specification == "math" + atoms = split(row.partition, "+") + durations = + ( + begin + r, d = parse.(Int, split(atom, "x")) + Iterators.repeated(d, r) + end for atom in atoms + ) |> Iterators.flatten + else + error("Row specification '$(row.specification)' is not valid") + end + _append_given_durations(appender, row, durations) + end + DuckDB.close(appender) +end + +function tmp_create_union_tables(connection) + # These are equivalent to the partitions in time-resolution.jl + # But computed in a more general context to be used by variables as well + + # Union of all incoming and outgoing flows + DuckDB.execute( + connection, + "CREATE OR REPLACE TEMP TABLE t_union_all_flows AS + SELECT from_asset as asset, year, rep_period, time_block_start, time_block_end + FROM flow_time_resolution + UNION + SELECT to_asset as asset, year, rep_period, time_block_start, time_block_end + FROM flow_time_resolution + ", + ) + + # Union of all assets, and incoming and outgoing flows + DuckDB.execute( + connection, + "CREATE OR REPLACE TEMP TABLE t_union_all AS + SELECT asset, year, rep_period, time_block_start, time_block_end + FROM asset_time_resolution + UNION + SELECT asset, year, rep_period, time_block_start, time_block_end + FROM t_union_all_flows + ", + ) +end + +function _append_lowest_helper(appender, group, s, e) + for x in group + DuckDB.append(appender, x) + end + DuckDB.append(appender, s) + DuckDB.append(appender, e) + DuckDB.end_row(appender) +end + +function tmp_create_lowest_resolution_table(connection) + # These are generic tables to be used to create some variables and constraints + + # The logic: + # - t_union has groups in order, then s:e ordered by s increasing and e decreasing + # - in a group (asset, year, rep_period) we can take the first range then + # - continue in the sequence selecting the next largest e + + for union_table in ("t_union_all_flows", "t_union_all") + table_name = replace(union_table, "t_union" => "t_lowest") + DuckDB.execute( + connection, + "CREATE OR REPLACE TABLE $table_name( + asset STRING, + year INT, + rep_period INT, + time_block_start INT, + time_block_end INT + )", + ) + appender = DuckDB.Appender(connection, table_name) + s = 0 + e_candidate = 0 + current_group = ("", 0, 0) + @timeit to "append $table_name rows" for row in DuckDB.query( + connection, + "SELECT * FROM $union_table + ORDER BY asset, year, rep_period, time_block_start, time_block_end DESC + ", + ) + if (row.asset, row.year, row.rep_period) != current_group + # New group, create the last entry + # Except for the initial case and when it was already added + if s != 0 && s <= e_candidate + _append_lowest_helper(appender, current_group, s, e_candidate) + end + # Start of a new group + current_group = (row.asset, row.year, row.rep_period) + e_candidate = row.time_block_end + s = 1 + end + if row.time_block_start > s + # Since it's ordered, we ran out of candidates, so this marks the beginning of a new section + # Then, let's append and update + _append_lowest_helper(appender, current_group, s, e_candidate) + s = e_candidate + 1 + e_candidate = row.time_block_end + else + # This row has a candidate + e_candidate = max(e_candidate, row.time_block_end) + end + end + # Add the last entry + if s > 0 && s <= e_candidate # Being safe + _append_lowest_helper(appender, current_group, s, e_candidate) + end + DuckDB.close(appender) + end end function tmp_create_constraints_indices(connection) diff --git a/src/variables/create.jl b/src/variables/create.jl index e578668f..4f4af6f7 100644 --- a/src/variables/create.jl +++ b/src/variables/create.jl @@ -2,13 +2,108 @@ export compute_variables_indices # TODO: Allow changing table names to make unit tests possible # The signature should be something like `...(connection; assets_data="t_assets_data", ...)` -function compute_variables_indices(connection, dataframes) - variables = Dict( - :flow => TulipaVariable(dataframes[:flows]), - :units_on => TulipaVariable(dataframes[:units_on]), - :storage_level_intra_rp => TulipaVariable(dataframes[:storage_level_intra_rp]), - :storage_level_inter_rp => TulipaVariable(dataframes[:storage_level_inter_rp]), - :is_charging => TulipaVariable(dataframes[:lowest_in_out]), +function compute_variables_indices(connection) + # TODO: Format SQL queries consistently (is there a way to add a linter/formatter?) + variables = Dict{Symbol,TulipaVariable}() + + variables[:flow] = TulipaVariable( + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + SELECT + nextval('id') as index, + from_asset as from, + to_asset as to, + year, + rep_period, + efficiency, + time_block_start, + time_block_end + FROM flow_time_resolution + ", + ) |> DataFrame, + ) + + variables[:units_on] = TulipaVariable( + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + SELECT + nextval('id') as index, + atr.asset, + atr.year, + atr.rep_period, + atr.time_block_start, + atr.time_block_end + FROM asset_time_resolution AS atr + LEFT JOIN asset + ON asset.asset = atr.asset + WHERE + asset.type IN ('producer','conversion') + AND asset.unit_commitment = true + ", + ) |> DataFrame, + ) + + variables[:is_charging] = TulipaVariable( + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + SELECT + nextval('id') as index, + t_low.asset, + t_low.year, + t_low.rep_period, + t_low.time_block_start, + t_low.time_block_end + FROM t_lowest_all_flows AS t_low + LEFT JOIN asset + ON t_low.asset = asset.asset + WHERE + asset.type = 'storage' + AND asset.use_binary_storage_method = true + ", + ) |> DataFrame, + ) + + variables[:storage_level_intra_rp] = TulipaVariable( + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + SELECT + nextval('id') as index, + t_low.asset, + t_low.year, + t_low.rep_period, + t_low.time_block_start, + t_low.time_block_end + FROM t_lowest_all AS t_low + LEFT JOIN asset + ON t_low.asset = asset.asset + WHERE + asset.type = 'storage' + AND asset.is_seasonal = false + ", + ) |> DataFrame, + ) + + variables[:storage_level_inter_rp] = TulipaVariable( + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + SELECT + nextval('id') as index, + asset.asset, + attr.year, + attr.period_block_start, + attr.period_block_end, + FROM asset_timeframe_time_resolution AS attr + LEFT JOIN asset + ON attr.asset = asset.asset + WHERE + asset.type = 'storage' + ", + ) |> DataFrame, ) variables[:flows_investment] = TulipaVariable( @@ -27,7 +122,6 @@ function compute_variables_indices(connection, dataframes) flow_milestone.investable = true", ) |> DataFrame, ) - dataframes[:flows_investment] = variables[:flows_investment].indices variables[:assets_investment] = TulipaVariable( DuckDB.query( @@ -43,7 +137,6 @@ function compute_variables_indices(connection, dataframes) asset_milestone.investable = true", ) |> DataFrame, ) - dataframes[:assets_investment] = variables[:assets_investment].indices variables[:assets_decommission_simple_method] = TulipaVariable( DuckDB.query( diff --git a/src/variables/flows.jl b/src/variables/flows.jl index 1e484900..a2d524f9 100644 --- a/src/variables/flows.jl +++ b/src/variables/flows.jl @@ -14,7 +14,7 @@ function add_flow_variables!(model, variables) variables[:flow].container = [ @variable( model, - base_name = "flow[($(row.from), $(row.to)), $(row.year), $(row.rep_period), $(row.timesteps_block)]" + base_name = "flow[($(row.from), $(row.to)), $(row.year), $(row.rep_period), $(row.time_block_start):$(row.time_block_end)]" ) for row in eachrow(flows_indices) ] diff --git a/src/variables/storage.jl b/src/variables/storage.jl index 1bb14386..d637c332 100644 --- a/src/variables/storage.jl +++ b/src/variables/storage.jl @@ -16,7 +16,7 @@ function add_storage_variables!(model, graph, sets, variables) @variable( model, lower_bound = 0.0, - base_name = "storage_level_intra_rp[$(row.asset),$(row.year),$(row.rep_period),$(row.timesteps_block)]" + base_name = "storage_level_intra_rp[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" ) for row in eachrow(storage_level_intra_rp_indices) ] @@ -24,7 +24,7 @@ function add_storage_variables!(model, graph, sets, variables) @variable( model, lower_bound = 0.0, - base_name = "storage_level_inter_rp[$(row.asset),$(row.year),$(row.periods_block)]" + base_name = "storage_level_inter_rp[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" ) for row in eachrow(storage_level_inter_rp_indices) ] diff --git a/src/variables/unit-commitment.jl b/src/variables/unit-commitment.jl index 4c64f0a8..21d01af6 100644 --- a/src/variables/unit-commitment.jl +++ b/src/variables/unit-commitment.jl @@ -10,11 +10,12 @@ Additionally, variables are constrained to be integers based on the `sets` struc function add_unit_commitment_variables!(model, sets, variables) units_on_indices = variables[:units_on].indices + # TODO: Add integer = ... variables[:units_on].container = [ @variable( model, lower_bound = 0.0, - base_name = "units_on[$(row.asset),$(row.year),$(row.rep_period),$(row.timesteps_block)]" + base_name = "units_on[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" ) for row in eachrow(units_on_indices) ] diff --git a/test/test-pipeline.jl b/test/test-pipeline.jl index cb50df47..fe7ceed0 100644 --- a/test/test-pipeline.jl +++ b/test/test-pipeline.jl @@ -32,7 +32,7 @@ end ) model_parameters = ModelParameters(connection) sets = create_sets(graph, years) - variables = compute_variables_indices(connection, dataframes) + variables = compute_variables_indices(connection) # Create model model = create_model(