calliope-project / calliope

A multi-scale energy systems modelling framework

Home Page:https://www.callio.pe

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`False` as a default to 'switch off' a constraint can lead to unexpected results

brynpickering opened this issue · comments

Problem description

As discovered in #292, if you have mixed data in an xarray DataArray, where one dtype is boolean, it is not possible to save it to file. These mixed dtypes come from parameters that can be either a finite numeric value or False. If False, associated constraints are effectively switched off. However, if the value is 0, this is considered a numeric value and the constraint is applied with that value (e.g. energy_cap_equals goes from being switched off to effectively stopping the technology from having any capacity).

To save a mixed bool-numeric dtype to file, the most straightforward approach is to convert it to a pure numeric dtype. However, when loading that data into a calliope model and running it, what were once False values are loaded back in as 0. The result of running this model will be different to running the original.

Getting here is something of an edge case:

model = Calliope.Model(...)
model.run()
# There are unlikely to be any mixed dtypes here, so no problem will be caused
model.to_netcdf(...) 

# This will give the same result as the initial run, 
# but inputs are returned from the Pyomo object, 
# with default values filled in (`NaN`s now become e.g. `False`). 
new_model = model.backend.rerun()  

# To succeed here, the new mixed dtype DataArrays need to be converted to numeric
new_model.to_netcdf(...)  

# This result will now be different to the initial run, 
# since the underlying input data has had its mixed dtypes 
# converted to pure numeric for saving to NetCDF.
new_model.run(force_rerun=True) 

This bug is silent but deadly. It could be introduced without following the abovementioned steps (e.g. just defining False for a parameter explicitly in the input YAML). It's clear that this shouldn't happen. We've noted the poor defaults in the past (#260) but need to really address it in some way in v0.6.8.

I would aim to have all equals constraints default to None or NaN and actively look for that when deciding whether to set a constraint in the Pyomo backend. This requires changing the Pyomo parameter domains (#313) to Any as None/NaN don't fit into e.g. NonNegativeReals. There is no major issue here, except that a user can then update the Pyomo backend parameters with any value they wish, rather than the set of values that are actually 'allowed' (e.g. could put a negative energy_cap_max in). The model would then be infeasible, which is better than silently solving the model incorrectly.

Calliope version

v0.6.8-dev