Skip to content

Commit

Permalink
feat(binding_constraints): draft to implement matrix validation
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinBelthle committed Nov 20, 2023
1 parent 9c7bff4 commit cd36f1c
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@
ONES_SCENARIO_MATRIX = "ones_scenario_matrix"

# Binding constraint aliases
BINDING_CONSTRAINT_HOURLY = "empty_2nd_member_hourly"
BINDING_CONSTRAINT_DAILY = "empty_2nd_member_daily"
BINDING_CONSTRAINT_WEEKLY = "empty_2nd_member_daily"
BINDING_CONSTRAINT_HOURLY_V86 = "empty_2nd_member_hourly_v86"
BINDING_CONSTRAINT_DAILY_V86 = "empty_2nd_member_daily_v86"
BINDING_CONSTRAINT_WEEKLY_V86 = "empty_2nd_member_daily_v86"

BINDING_CONSTRAINT_HOURLY_V87 = "empty_2nd_member_hourly_v87"
BINDING_CONSTRAINT_DAILY_V87 = "empty_2nd_member_daily_v87"
BINDING_CONSTRAINT_WEEKLY_V87 = "empty_2nd_member_daily_v87"

# Short-term storage aliases
ST_STORAGE_PMAX_INJECTION = ONES_SCENARIO_MATRIX
Expand Down Expand Up @@ -89,10 +93,17 @@ def _init(self) -> None:
self.hashes[MISCGEN_TS] = self.matrix_service.create(FIXED_8_COLUMNS)

# Binding constraint matrices
series = matrix_constants.binding_constraint.series_before_v87
self.hashes[BINDING_CONSTRAINT_HOURLY] = self.matrix_service.create(series.default_binding_constraint_hourly)
self.hashes[BINDING_CONSTRAINT_DAILY] = self.matrix_service.create(series.default_binding_constraint_daily)
self.hashes[BINDING_CONSTRAINT_WEEKLY] = self.matrix_service.create(series.default_binding_constraint_weekly)
# fmt:off
series_before_v87 = matrix_constants.binding_constraint.series_before_v87
self.hashes[BINDING_CONSTRAINT_HOURLY_V86] = self.matrix_service.create(series_before_v87.default_binding_constraint_hourly)
self.hashes[BINDING_CONSTRAINT_DAILY_V86] = self.matrix_service.create(series_before_v87.default_binding_constraint_daily)
self.hashes[BINDING_CONSTRAINT_WEEKLY_V86] = self.matrix_service.create(series_before_v87.default_binding_constraint_weekly)

series_after_v87 = matrix_constants.binding_constraint.series_after_v87
self.hashes[BINDING_CONSTRAINT_HOURLY_V87] = self.matrix_service.create(series_after_v87.default_binding_constraint_hourly)
self.hashes[BINDING_CONSTRAINT_DAILY_V87] = self.matrix_service.create(series_after_v87.default_binding_constraint_daily)
self.hashes[BINDING_CONSTRAINT_WEEKLY_V87] = self.matrix_service.create(series_after_v87.default_binding_constraint_weekly)
# fmt:on

# Some short-term storage matrices use np.ones((8760, 1))
self.hashes[ONES_SCENARIO_MATRIX] = self.matrix_service.create(
Expand Down Expand Up @@ -151,17 +162,20 @@ def get_default_reserves(self) -> str:
def get_default_miscgen(self) -> str:
return MATRIX_PROTOCOL_PREFIX + self.hashes[MISCGEN_TS]

def get_binding_constraint_hourly(self) -> str:
"""2D-matrix of shape (8760, 3), filled-in with zeros."""
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_HOURLY]
def get_binding_constraint_hourly(self, version: int) -> str:
if version < 870:
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_HOURLY_V86]
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_HOURLY_V87]

def get_binding_constraint_daily(self) -> str:
"""2D-matrix of shape (365, 3), filled-in with zeros."""
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_DAILY]
def get_binding_constraint_daily(self, version: int) -> str:
if version < 870:
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_DAILY_V86]
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_DAILY_V87]

def get_binding_constraint_weekly(self) -> str:
"""2D-matrix of shape (52, 3), filled-in with zeros."""
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_WEEKLY]
def get_binding_constraint_weekly(self, version: int) -> str:
if version < 870:
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_WEEKLY_V86]
return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_WEEKLY_V87]

def get_st_storage_pmax_injection(self) -> str:
"""2D-matrix of shape (8760, 1), filled-in with ones."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
MatrixType = List[List[MatrixData]]


def check_matrix_values(time_step: BindingConstraintFrequency, values: MatrixType) -> None:
def check_matrix_values(time_step: BindingConstraintFrequency, values: MatrixType, version: int) -> None:
"""
Check the binding constraint's matrix values for the specified time step.
Args:
time_step: The frequency of the binding constraint: "hourly", "daily" or "weekly".
values: The binding constraint's 2nd member matrix.
version: Corresponds to the study version
Raises:
ValueError:
Expand All @@ -47,8 +48,13 @@ def check_matrix_values(time_step: BindingConstraintFrequency, values: MatrixTyp
}
# Check the matrix values and create the corresponding matrix link
array = np.array(values, dtype=np.float64)
if array.shape != shapes[time_step]:
raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}")
actual_shape = array.shape
expected_shape = shapes[time_step]
if version < 870:
if actual_shape != expected_shape:
raise ValueError(f"Invalid matrix shape {actual_shape}, expected {expected_shape}")
elif actual_shape[0] != expected_shape[0]:
raise ValueError(f"Invalid matrix length {actual_shape[0]}, expected {expected_shape[0]}")
if np.isnan(array).any():
raise ValueError("Matrix values cannot contain NaN")

Expand All @@ -67,6 +73,8 @@ class AbstractBindingConstraintCommand(ICommand, metaclass=ABCMeta):
filter_year_by_year: Optional[str] = None
filter_synthesis: Optional[str] = None
comments: Optional[str] = None
group: Optional[str] = None
version: int = 1

def to_dto(self) -> CommandDTO:
args = {
Expand All @@ -77,6 +85,7 @@ def to_dto(self) -> CommandDTO:
"comments": self.comments,
"filter_year_by_year": self.filter_year_by_year,
"filter_synthesis": self.filter_synthesis,
"group": self.group,
}
if self.values is not None:
args["values"] = strip_matrix_protocol(self.values)
Expand Down Expand Up @@ -113,20 +122,25 @@ def validate_series(
constants: GeneratorMatrixConstants
constants = values["command_context"].generator_matrix_constants
time_step = values["time_step"]
# todo: change this fake version to the actual study version. I do not see a nice way to do it :/
# Solutions i thought of :
# 1) Add the study version to the class attributes -> Solve the issue but weird and hard to do sometimes
# 2) Add the value pre=True in the validator and each time creating a class, specify the study version.
# It's also weird and hard to do ...
fake_version = 860
if v is None:
# Use an already-registered default matrix
methods = {
BindingConstraintFrequency.HOURLY: constants.get_binding_constraint_hourly,
BindingConstraintFrequency.DAILY: constants.get_binding_constraint_daily,
BindingConstraintFrequency.WEEKLY: constants.get_binding_constraint_weekly,
BindingConstraintFrequency.HOURLY: constants.get_binding_constraint_hourly(fake_version),
BindingConstraintFrequency.DAILY: constants.get_binding_constraint_daily(fake_version),
BindingConstraintFrequency.WEEKLY: constants.get_binding_constraint_weekly(fake_version),
}
method = methods[time_step]
return method()
return methods[time_step]
if isinstance(v, str):
# Check the matrix link
return validate_matrix(v, values)
if isinstance(v, list):
check_matrix_values(time_step, v)
check_matrix_values(time_step, v, fake_version)
return validate_matrix(v, values)
# Invalid datatype
# pragma: no cover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def validate_series(
# Check the matrix link
return validate_matrix(v, values)
if isinstance(v, list):
check_matrix_values(time_step, v)
# todo: change this fake version by the actual study version
fake_version = 860
check_matrix_values(time_step, v, fake_version)
return validate_matrix(v, values)
# Invalid datatype
# pragma: no cover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,42 @@ def test_get_st_storage(self, tmp_path):
matrix_dto5 = generator.matrix_service.get(matrix_id5)
assert np.array(matrix_dto5.data).all() == matrix_constants.st_storage.series.inflows.all()

def test_get_binding_constraint(self, tmp_path):
def test_get_binding_constraint_before_v87(self, tmp_path):
version = 860
generator = GeneratorMatrixConstants(matrix_service=SimpleMatrixService(bucket_dir=tmp_path))
series = matrix_constants.binding_constraint.series
series = matrix_constants.binding_constraint.series_before_v87

hourly = generator.get_binding_constraint_hourly()
hourly = generator.get_binding_constraint_hourly(version)
hourly_matrix_id = hourly.split(MATRIX_PROTOCOL_PREFIX)[1]
hourly_matrix_dto = generator.matrix_service.get(hourly_matrix_id)
assert np.array(hourly_matrix_dto.data).all() == series.default_binding_constraint_hourly.all()

daily = generator.get_binding_constraint_daily()
daily = generator.get_binding_constraint_daily(version)
daily_matrix_id = daily.split(MATRIX_PROTOCOL_PREFIX)[1]
daily_matrix_dto = generator.matrix_service.get(daily_matrix_id)
assert np.array(daily_matrix_dto.data).all() == series.default_binding_constraint_daily.all()

weekly = generator.get_binding_constraint_weekly()
weekly = generator.get_binding_constraint_weekly(version)
weekly_matrix_id = weekly.split(MATRIX_PROTOCOL_PREFIX)[1]
weekly_matrix_dto = generator.matrix_service.get(weekly_matrix_id)
assert np.array(weekly_matrix_dto.data).all() == series.default_binding_constraint_weekly.all()

def test_get_binding_constraint_after_v87(self, tmp_path):
version = 870
generator = GeneratorMatrixConstants(matrix_service=SimpleMatrixService(bucket_dir=tmp_path))
series = matrix_constants.binding_constraint.series_after_v87

hourly = generator.get_binding_constraint_hourly(version)
hourly_matrix_id = hourly.split(MATRIX_PROTOCOL_PREFIX)[1]
hourly_matrix_dto = generator.matrix_service.get(hourly_matrix_id)
assert np.array(hourly_matrix_dto.data).all() == series.default_binding_constraint_hourly.all()

daily = generator.get_binding_constraint_daily(version)
daily_matrix_id = daily.split(MATRIX_PROTOCOL_PREFIX)[1]
daily_matrix_dto = generator.matrix_service.get(daily_matrix_id)
assert np.array(daily_matrix_dto.data).all() == series.default_binding_constraint_daily.all()

weekly = generator.get_binding_constraint_weekly(version)
weekly_matrix_id = weekly.split(MATRIX_PROTOCOL_PREFIX)[1]
weekly_matrix_dto = generator.matrix_service.get(weekly_matrix_id)
assert np.array(weekly_matrix_dto.data).all() == series.default_binding_constraint_weekly.all()

0 comments on commit cd36f1c

Please sign in to comment.