Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the flexible temporal resolution implementation #1119

Open
3 tasks
datejada opened this issue Oct 15, 2024 · 9 comments
Open
3 tasks

Fix the flexible temporal resolution implementation #1119

datejada opened this issue Oct 15, 2024 · 9 comments
Assignees

Comments

@datejada
Copy link
Member

datejada commented Oct 15, 2024

Use as a reference the table in the link below to determine the constraints resolutions in SpineOpt:

https://tulipaenergy.github.io/TulipaEnergyModel.jl/dev/30-concepts/#flex-time-res

Consider the following points:

  • List all constraints in SpineOpt and map the required resolution of the constraint
  • Define the defaults for each constraint and the possible options for the user.

Constraints:

@DillonJ
Copy link
Collaborator

DillonJ commented Oct 15, 2024

I think a first step is cataloging any constraints that need to make an assumption or decision about how temporal discontinuitues are to be resolved. Generally, this will be any constraint that involves flow variables from different nodes or timesteps. If we assume for now that discontinuities across timesteps are handled ok, then it's just where flows from different nodes interact.

@gnawin
Copy link
Collaborator

gnawin commented Nov 1, 2024

Below is a list of all constraints in SpineOpt and their current type, power or energy. The type is determined by whether the constraint has a duration. Some constraints are marked n.a. because they do not involve flex resolution.

Also note that nodes do not allow different resolutions, so only the connections and units can have flows of different resolution.

@g-moralesespana @datejada

Name Current constraint type Comment
constraint_candidate_connection_flow_lb energy
constraint_candidate_connection_flow_ub power
constraint_compression_ratio energy
constraint_connection_flow_capacity energy
constraint_connection_flow_gas_capacity energy gas
constraint_connection_flow_lodf power
constraint_connection_flow_intact_flow energy
constraint_connection_intact_flow_capacity energy
constraint_connection_intact_flow_ptdf power
constraint_connection_lifetime n.a. only capacity
constraint_connection_unitary_gas_flow n.a. only binary
constraint_connections_invested_available n.a. only capacity
constraint_connections_invested_transition n.a. only capacity
constraint_cyclic_node_state power only storage state
constraint_fix_node_pressure_point power gas
constraint_investment_group_minimum_capacity_invested_available n.a. only capacity
constraint_investment_group_minimum_entities_invested_available n.a. only capacity
constraint_investment_group_equal_investments n.a. only capacity
constraint_max_node_pressure power pressure
constraint_max_node_voltage_angle power voltage angle
constraint_min_capacity_margin n.a. only capacity
constraint_min_down_time n.a. only binary
constraint_min_node_pressure power pressure
constraint_min_node_voltage_angle power voltage angle
constraint_min_scheduled_outage_duration energy
constraint_min_up_time n.a. only binary
constraint_minimum_operating_point energy
constraint_mp_any_invested_cuts n.a. only capacity
constraint_mp_min_res_gen_to_demand_ratio_cuts energy
constraint_nodal_balance power
constraint_node_injection energy
constraint_node_state_capacity power storage
constraint_node_voltage_angle energy voltage angle
constraint_non_spinning_reserves_lower_bound energy
constraint_operating_point_bounds n.a. only binary
constraint_operating_point_rank n.a. only binary
constraint_ramp_down energy
constraint_ramp_up energy
constraint_ratio_out_in_connection_flow energy
constraint_ratio_out_in_connection_intact_flow energy
constraint_ratio_unit_flow energy
constraint_storage_lifetime n.a. only capacity
constraint_storage_line_pack energy storage gas
constraint_storages_invested_available n.a. only capacity
constraint_storages_invested_transition n.a. only capacity
constraint_total_cumulated_unit_flow energy
constraint_unit_flow_capacity energy
constraint_unit_flow_op_bounds power
constraint_unit_flow_op_rank power
constraint_unit_flow_op_sum power
constraint_unit_lifetime n.a. only capacity
constraint_unit_state_transition n.a. only binary
constraint_units_available n.a. only binary
constraint_units_invested_available n.a. only binary
constraint_units_invested_transition n.a. only binary
constraint_units_out_of_service_contiguity n.a. only binary
constraint_units_out_of_service_transition n.a. only binary
constraint_user_constraint energy

@datejada
Copy link
Member Author

datejada commented Nov 5, 2024

A draft summary of the meeting on the topic (05/11/2024), thanks to @tarskul:

For flexible time steps we need to:

  • choose between lowest resolution or highest resolution.
  • choose between energy equation and power equation. (OR choose between directional and bidirectional?)

Some examples:

  • capacity constraints: highest resolution (otherwise the production can be double when the unit )
  • conversion constraint: lowest resolution (otherwise all inputs are the same)
  • nodal balance: highest resolution (otherwise ideal storage)

SpineOpt avoids some of this because it:

  • does not have constraints for where two different flows contribute to one capacity constraint.
  • sets the temporal resolution at a node. That means that all flows have the same resolution coming in and out the node. Only units or connections (i.e. conversions) can have different resolutions in their flows.
  • is written in power constraints. That is generally easier than energy equations.

So the conflict can only arise in the units/connections. SpineOpt only uses the lowest resolution whenever two flows do come together in an equation. Is this the correct way?

  • High resolution version: there is no flexibility as the high resolution simply equals the low resolution flow.
  • Low resolution version: High resolution has the flexibility to only get the average low resolution flow. That flexibility was the reason why the low resolution was chosen in SpineOpt. However, that causes the aforementioned issue (incorrect output).

TNO proposes to make the nodes flexible such that the high resolution version can be used in the unit (correct output) and the flexibility can still be used. See attached PDF file FlexRes.pdf

@datejada
Copy link
Member Author

datejada commented Nov 12, 2024

First change: The constraints for connections between two nodes with different resolutions should be on the highest resolution. This option is the default value for the users.

  • Create a simple test example (or add an extra test) to see the constraints that are now (and show how they should be.
  • Check the SpineOpt connection constraints
  • Write the changes in the constraints

@DillonJ
Copy link
Collaborator

DillonJ commented Nov 12, 2024

@datejada with the option also to implement the lowest resolution

@DillonJ
Copy link
Collaborator

DillonJ commented Nov 12, 2024

Instances of handling different flow resolutions:

constraint_ratio_unit_flow:


 for (t, path) in t_lowest_resolution_path(
            m,
            unit_flow_indices(m; unit=u, node=[n1, n2]),
            unit_flow_indices(m; unit=u, node=n1, direction=d1),
            unit_flow_indices(m; unit=u, node=n2, direction=d2),
            units_on_indices(m; unit=u),
        )


constraint_ratio_out_in_connection_flow:

 for (t, path_out) in t_lowest_resolution_path(
            m, connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
        )


constraint_user_constraint:

for (t, path) in t_lowest_resolution_path(
            m, Iterators.flatten(user_constraint_all_indices(m; user_constraint=uc))
        )


constraint_minimum_operating_point:

 for (t, path) in t_lowest_resolution_path(
            m, unit_flow_indices(m; unit=u, node=ng, direction=d), units_on_indices(m; unit=u)
        )

constraint_connection_flow_capacity:

 for (t, path) in t_lowest_resolution_path(
            m,
            connection_flow_indices(m; connection=conn, node=ng, direction=d),
            connections_invested_available_indices(m; connection=conn),
        )


constraint_candidate_connection_flow_lb:
for (t, path) in t_lowest_resolution_path(
            m,
            Iterators.flatten(
                (
                    connection_flow_indices(m; connection=conn, node=n, direction=d),
                    connections_invested_available_indices(m; connection=conn),
                )
            )
        )

@gnawin
Copy link
Collaborator

gnawin commented Nov 21, 2024

We started with checking the constraint fix_ratio_out_in_connection_flow. In the test set-up, node_a is hourly, node_b is two_hourly. SpineOpt generates 1 constraint:

fix_ratio_out_in_connection_flow(connection = connection_ab, node1 = node_b, node2 = node_a, stochastic_path = Object[parent, child], t = 2000-01-01T00:00~(2 hours)~>2000-01-01T02:00) : 

connection_flow[connection_ab, node_a, from_node, parent, 2000-01-01T00:00~(1 hour)~>2000-01-01T01:00] 
+ 
connection_flow[connection_ab, node_a, from_node, child, 2000-01-01T01:00~(1 hour)~>2000-01-01T02:00] 
==
2 connection_flow[connection_ab, node_b, to_node, parent, 2000-01-01T00:00~(2 hours)~>2000-01-01T02:00]

However, we would like to see the following 2 constraints:

fix_ratio_out_in_connection_flow(connection = connection_ab, node1 = node_b, node2 = node_a, stochastic_path = Object[parent, child], t = 2000-01-01T00:00~(1 hour)~>2000-01-01T01:00) : 

connection_flow[connection_ab, node_a, from_node, parent, 2000-01-01T00:00~(1 hour)~>2000-01-01T01:00] 
+ 
==
connection_flow[connection_ab, node_b, to_node, parent, 2000-01-01T00:00~(2 hours)~>2000-01-01T02:00]
fix_ratio_out_in_connection_flow(connection = connection_ab, node1 = node_b, node2 = node_a, stochastic_path = Object[parent, child], t = 2000-01-01T01:00~(1 hour)~>2000-01-01T02:00) : 

connection_flow[connection_ab, node_a, from_node, child, 2000-01-01T01:00~(1 hour)~>2000-01-01T02:00] 
==
connection_flow[connection_ab, node_b, to_node, parent, 2000-01-01T00:00~(2 hours)~>2000-01-01T02:00]

In function constraint_ratio_out_in_connection_flow_indices, we have

for (t, path_out) in t_lowest_resolution_path(
            m, connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
        )

We tried

for (t, path_out) in t_highest_resolution_path(
            m, connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
        )

and

for (t, path_out) in t_highest_resolution(
            m, connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
        )

Neither are working. So we need to dive deeper with some help.

@g-moralesespana @datejada

@DillonJ
Copy link
Collaborator

DillonJ commented Nov 21, 2024

@gnawin can you be more specific about what isn't working?

@DillonJ
Copy link
Collaborator

DillonJ commented Nov 21, 2024

@gnawin this is a nice start. I see that the function t_highest_resolution_path() doesn't exist. You will have to write it. Look at t_lowest_resolution_path() for inspiration and perhaps if you get stuck, we could try to help with the specific issues you are facing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants