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

Choosing constraints: parameter vs. relationship #13

Closed
spine-o-bot opened this issue Feb 3, 2021 · 13 comments
Closed

Choosing constraints: parameter vs. relationship #13

spine-o-bot opened this issue Feb 3, 2021 · 13 comments

Comments

@spine-o-bot
Copy link

In GitLab by @jkiviluo on Sep 14, 2018, 11:23

There's been a bit pro-longed discussion over Powerpoint slides, e-mail and telco between @poncelet, @DillonJ and me that has included this problem. There's also been some references to it in other issues like https://gitlab.vtt.fi/spine/model/issues/59#note_2700. We are also preparing another issue on the archetype (so don't discuss that here).

There are at least two major ways to express when a constraint would be used for a particular object in Spine Model.

  1. The specification of parameter values directly trigger Model behavior (e.g., a ramp-rate constraint is generated in Spine model for those units for which the parameter “ramp_rate” would be specified).
  2. There is a separate switch to determine when a certain constraint is to be generated. This switch could be a separate parameter (e.g., use_ramp_constraint), but I (Juha) would prefer to use relationships as a switch.

Let's go over both approaches. I'm trying to keep it simple here (i.e. it can get more complicated). Also, this is just a partial example (the full details don't add much information).

Approach where the specification of the ’common’ parameters are also used as a trigger to generate constraints

     unit_A.max_upward_primary_reserve = 0.05  
     node_Leuven.constant_upward_primary_reserve = 100

Here the existence of a <> Null/NaN parameter value for the upward primary reserve would mean that the unit will participate in the upward primary reserve. Similarly, the upward primary reserve constraint in the node gets established by the existence of the (constant) value for the parameter.

Advantages of this approach:

  • Intuitive to use
  • Similar to what most modelers are currently used to
  • The numeric data directly implies what will happen in the model
  • Fast to input

Disadvantages of this approach:

  • Need to be able to distinguish between parameter value existence and 0 values (in JSON this should be possible, but probably not directly with the value column, which is a float)
  • If one wants to make a change to the model (e.g., do not use ramp constraints), the data would need to be changed. This could be done using the alternative/scenario construct, thereby leaving the base database intact. For the example you might have the above values for the 'base' scenario and then have a 'no reserves' scenario with alternative parameter values for both of them where
     unit_A.max_upward_primary_reserve = 0  
     node_Leuven.constant_upward_primary_reserve = 0

Thus, you would need to have a scenario 'no reserves' that is built by combining the 'base' scenario with the 'no reserves' alternative (resulting in the displacement of original values with the zeros). Alternatively, the archetype could contain a field where a list of parameters which will be ignored could be stored

  • Sometimes, a parameter can induce/appear in multiple equations. For instance, a ramp rate could restrict the ramping between sequential time steps, but could also directly restrict the provision of reserves in a separate equation. Getting control over one or the other separately (e.g., only keep the ramp constraint related to reserve provision) would not be possible, or separate parameters would be needed for that, e.g., ramp_rate_between_time_steps and ramp_rate_reserves_within_time_steps.
  • What happens when there is both time series and constant values for e.g. upward_primary_reserve? How to decide which one is used? A separate parameter flag? What if the flag points to a constant and only time series values are provided? Or should they be in separate alternatives (i.e. it is not allowed to have both constant and time series on single record (row))?
  • Users need to be aware of what behavior different parameters trigger (especially when one parameter can cause multiple behaviors, e.g. min. load could be handled with a linear approximation or with MIP)

Approach where relationships are used as a trigger to generate constraints

    unit_A.max_upward_primary_reserve = 0.05  
    node_Leuven.constant_upward_primary_reserve = 100
    node_Leuven__require_upward_primary_reserve
    unit_A__provide_upward_primary_reserve

require_upward_primary_reserve is what I call a method (relates to the archetype - feature - method chain, but let's not go there here). Here the existence of the node_Leuven__require_upward_primary_reserve causes the upward reserve constraint to be activated for node_Leuven.

Advantages of this approach:

  • Separation between numeric data and function. This allows easy re-use of databases containing all possible data (e.g. you might want to use the same data in different ways for the investment model and the UCED model that you run iteratively).
  • upward_primary_reserve can be switched off without using alternatives and scenarios. Just delete the node__upward_primary_reserves relationship and the numeric data remains intact.
  • To use time series instead of constant could be achieved by having separate methods for them (or possibly with a sub-method, but that might be overly complex) and there is no need for additional code to distinguish situations where both of them have been provided.
  • Use of methods provides more flexibility to select which constraints are activated and when (i.e. ramp rate constraint between consecutive time steps and ramp constraints for reserve provision).
  • Using relationships might increase transparency and error checking. It's easier to show what is happening with the relationship approach (e.g. a graph view on the methods in use) and you don't need to worry whether some parameters still have the necessary values, because ten weeks ago you did a model run where you might have taken something out, but you don't quite remember what... Of course Spine Toolbox should make those kinds of mistakes less likely, but will probably not eliminate them.
  • Using relationships between units/nodes and methods, and additional relationships between methods and parameters, allows checking that all necessary parameter values have been entered. If your method requires a specific parameter, it's possible to build a check on that and give feedback to the user. However, if you're using parameters to define function, this could be done using an archetype which has a field containing a list of obligatory parameters (with the difference that the parameter would be linked to the archetype, not the method - this goes again to the question what is an archetype and is not handled here).
  • If you really have alternative values for parameters, then it's fine to use the alternative/scenario construct. That's what it's for.

Disadvantages of this approach:

  • Requires additional objects (the ‘method’ objects), and relationships (unit__method or node__method)
  • Modelers might have a preconception that numeric data implies that a constraint will be generated (current practice in models such as TIMES, OSeMOSYS) - (e.g., if a user sees ramp_rate = 10 in the database, they might assume that ramping constraints will be generated). It might just need some time for the new philosophy to sink in.
  • List of methods can become long and therefore hard to use (but this would be handled by the archetype - feature - method chain that limits what the user sees only to relevant choices - that is not to be discussed in this issue).
  • Need to connect each unit to methods corresponding to all generic constraints? Or should the unit__method relationship be automatically generated for generic constraints when data is there? Or would the unit__generic_constraint_method be always generated and then parameter data will dictate what happens?
  • Increased complexity of the input Data Store
  • Might make it more complex for users to add functionality (need to create method (and feature objects), establish relationships between the method objects and the parameters, etc). Archetype - feature - method chain will make this easier, but not to be discussed here...
  • Splitting up into features and methods could be difficult in some cases (in principle feature is the phenomena/problem, while method is the concrete way to implement it)
@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 14, 2018, 11:33

That description was supposed to be free of opinion (probably quite not so) and Kris helped me to modify it to that direction (while also giving other input). So, I just wanted express my main reason why relationships would be great:

Using relationships needs additional objects (Data Store gets bit more complex), but I believe that life is easier in the end. I think it's important to have a clear separation between data and function. With this you can make re-usable databases that try to contain all possible data. Then you just apply rules how you want to use the database in a particular task (e.g. you might want to use the same data in different ways for the investment model and the UCED model that you run iteratively). I think it's more straightforward to keep the parameter data same and just create separate model instances where you select bit different methods for some of the features.

Now, that's just my current view. Good arguments in either direction are very welcome.

@spine-o-bot
Copy link
Author

In GitLab by @manuelma on Sep 16, 2018, 12:02

Nice post @juha_k. I just have a couple of questions:

unit_A.max_upward_primary_reserve = 0

From this I understand that if we want to 'deactivate' a parameter, we set its value to zero. Does that work? What if zero is a meaningful value?

  • What happens when there is both time series and constant values for e.g. upward_primary_reserve? How to decide which one is used?

This is an interesting question, but I don't see how it relates to the main issue? --At the moment, JuMP_all_out prioritizes the time series (json column) over the constant (value column).

relationships between methods and parameters

establish relationships between the method objects and the parameters

Just to be sure, do you mean define parameters for method objects? Because we can't define relationships between objects and parameters.

@spine-o-bot
Copy link
Author

In GitLab by @DillonJ on Sep 17, 2018, 12:15

My main problem with this approach is that relationships indicate some sort of relationship between objects

Parameters are data that tell us things about objects.

However, this relationship: node_Leuven__require_upward_primary_reserve is not a relationship between objects in any meaningful definition of an object. It's really a parameter masquerading as a relationship...

This also means that require_upward_primary_reserve is an object class.

So we have to create this new object class (that will appear in the tree) what are the object instances then and what do they represent? I suspect you're going to need something in "quotes".... my worst nightmare :)

As @manuelma alludes to, I think there are hidden implementation issues here. It just seems too awkward and non-intuitive.

I don't think using relationships in this way achieves separation of data and function... relationships are just as much data as parameters.

In the end of the day, you create parameters to indicate function, or create relationships (meaning you also need to create mostly empty new object classes and object instances)...

I really think it's not using objects and relationships in a nice way. It feels like a twisting of the data structure...

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 12:15

From this I understand that if we want to 'deactivate' a parameter, we set its value to zero. Does that work? What if zero is a meaningful value?

I don't know how it would work with the value field. For JSON we could use things like NaN. That might mean that we would not have a separate value field, just the JSON. But maybe there is another way to solve this.

This is an interesting question, but I don't see how it relates to the main issue? --At the moment, JuMP_all_out prioritizes the time series (json column) over the constant (value column).

It's a difference between the parameter and relationships approaches. With the relationship approach it is straightforward to establish which one is used (you select 'times series' method or a 'constant' method). With the parametric approach there needs to be a convention (that the user must know) or some other way to establish precedence.

Just to be sure, do you mean define parameters for method objects? Because we can't define relationships between objects and parameters.

Good point. We would need to either have object_class parameters that would list all the parameters (automatically) so that we can create that relationship or then allow a relationship between an object and parameter. We don't need to do this right away - it's only later when we want to enable parameter validation with archetypes.

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 12:17

@DillonJ require_upward_primary_reserve would not be an object class. It would be an object of the method class. Please re-consider with this in mind.

@spine-o-bot
Copy link
Author

In GitLab by @DillonJ on Sep 17, 2018, 12:22

@juha_k We already have a working mechanism for dealing with time varying data I.e. you can specify a constant value, a time patterned value, link to a time_series object or embed a time series in the json and as @manuelma mentions, JumpAllOut already supports this.

This is a huge case for a parameter based approach because we have no mechanism for time varying relationships, which in my view, would be very messy.

An advantage of the parameter based approach is also that they can be defined, if we wish, on the relationship between the entity and a temporal_stage object and take different values for each temporal stage, thus giving us significant capability in terms of having different formulations in different temporal_stages.

@spine-o-bot
Copy link
Author

In GitLab by @DillonJ on Sep 17, 2018, 12:34

Another issue is that you would need to refer to require_upward_primary_reserve in "quotes" and if it's wrong, you won't get an error. If you used parameters with enumeration as proposed in require_upward_primary_reserve you could write something like:

   if(require_upward_primary_reserve(unit=u) == upward_primary_reserve_required  
     #do stuff

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 12:50

This is a huge case for a parameter based approach because we have no mechanism for time varying relationships, which in my view, would be very messy.

Number data would always be in parameters, including time varying data. There is no need for time-varying relationships (I don't even know what you mean by it). The relationships are only used to say when the number data is used and when it is not used. (And if there is a need to say that something starts from T=5, then a number must be given in a parameter field).

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 12:52

An advantage of the parameter based approach is also that they can be defined, if we wish, on the relationship between the entity and a temporal_stage object and take different values for each temporal stage, thus giving us significant capability in terms of having different formulations in different temporal_stages.

We can also have unit__method__temporal_stage relationship when needed. The same capability is there too.

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 12:56

Another issue is that you would need to refer to require_upward_primary_reserve in "quotes" and if it's wrong, you won't get an error.

I don't get this. In Spine Model, one would have sets. No quotes needed. If you write something wrong in Spine Model, like unt instead of unit you are supposed to get an error so that you know to correct your mistake. Isn't that a good thing?

@spine-o-bot
Copy link
Author

In GitLab by @DillonJ on Sep 17, 2018, 12:58

I don't get this. In Spine Model, one would have sets. No quotes needed. If you write something wrong in Spine Model, like unt instead of unit you are supposed to get an error so that you know to correct your mistake. Isn't that a good thing?

Yes, it's a good thing, that was my point. With "stuffinquotes" you don't have this safeguard.

@spine-o-bot
Copy link
Author

In GitLab by @jkiviluo on Sep 17, 2018, 13:37

So, just to make clear. require_upward_reserve would be without quotes in Spine Model (it's an object) and would thus have error checking. I suppose it's still open how to get subsets in Spine Model, but's that's separate thing. Discussed in https://gitlab.vtt.fi/spine/data/issues/8.

@jkiviluo
Copy link
Member

We're nowadays using parameters that are methods and in 0.8.2 we will get method parameter type (with some additional functionality).

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

No branches or pull requests

2 participants