Skip to content

Commit

Permalink
fix(clusters): correct renewable cluster duplication for variants
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent-laporte-pro committed Mar 8, 2024
1 parent 80a69ca commit 4e4d157
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 31 deletions.
73 changes: 42 additions & 31 deletions antarest/study/business/areas/renewable_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Config:
def schema_extra(schema: t.MutableMapping[str, t.Any]) -> None:
schema["example"] = RenewableClusterInput(
group="Gas",
name="2 avail and must 1",
name="Gas Cluster XY",
enabled=False,
unitCount=100,
nominalCapacity=1000.0,
Expand Down Expand Up @@ -87,9 +87,9 @@ class Config:
@staticmethod
def schema_extra(schema: t.MutableMapping[str, t.Any]) -> None:
schema["example"] = RenewableClusterOutput(
id="2 avail and must 1",
id="Gas cluster YZ",
group="Gas",
name="2 avail and must 1",
name="Gas Cluster YZ",
enabled=False,
unitCount=100,
nominalCapacity=1000.0,
Expand Down Expand Up @@ -159,23 +159,25 @@ def create_cluster(
The newly created cluster.
"""
file_study = self._get_file_study(study)
study_version = study.version
cluster = cluster_data.to_config(study_version)

command = CreateRenewablesCluster(
area_id=area_id,
cluster_name=cluster.id,
parameters=cluster.dict(by_alias=True, exclude={"id"}),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)
cluster = cluster_data.to_config(study.version)
command = self._make_create_cluster_cmd(area_id, cluster)
execute_or_add_commands(
study,
file_study,
[command],
self.storage_service,
)
output = self.get_cluster(study, area_id, cluster.id)
return output

return self.get_cluster(study, area_id, cluster.id)
def _make_create_cluster_cmd(self, area_id: str, cluster: RenewableConfigType) -> CreateRenewablesCluster:
command = CreateRenewablesCluster(
area_id=area_id,
cluster_name=cluster.id,
parameters=cluster.dict(by_alias=True, exclude={"id"}),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)
return command

def get_cluster(self, study: Study, area_id: str, cluster_id: str) -> RenewableClusterOutput:
"""
Expand Down Expand Up @@ -276,43 +278,52 @@ def delete_clusters(self, study: Study, area_id: str, cluster_ids: t.Sequence[st

execute_or_add_commands(study, file_study, commands, self.storage_service)

def duplicate_cluster(self, study: Study, area_id: str, source_id: str, new_name: str) -> RenewableClusterOutput:
def duplicate_cluster(
self,
study: Study,
area_id: str,
source_id: str,
new_cluster_name: str,
) -> RenewableClusterOutput:
"""
Creates a duplicate cluster within the study area with a new name.
Args:
study: The study in which the cluster will be duplicated.
area_id: The identifier of the area where the cluster will be duplicated.
source_id: The identifier of the cluster to be duplicated.
new_name: The new name for the duplicated cluster.
new_cluster_name: The new name for the duplicated cluster.
Returns:
The duplicated cluster configuration.
Raises:
ClusterAlreadyExists: If a cluster with the new name already exists in the area.
"""
new_id = transform_name_to_id(new_name, lower=False)
if any(new_id.lower() == cluster.id.lower() for cluster in self.get_clusters(study, area_id)):
new_id = transform_name_to_id(new_cluster_name, lower=False)
lower_new_id = new_id.lower()
if any(lower_new_id == cluster.id.lower() for cluster in self.get_clusters(study, area_id)):
raise ClusterAlreadyExists("Renewable", new_id)

# Cluster duplication
current_cluster = self.get_cluster(study, area_id, source_id)
current_cluster.name = new_name
current_cluster.name = new_cluster_name
creation_form = RenewableClusterCreation(**current_cluster.dict(by_alias=False, exclude={"id"}))
new_cluster = self.create_cluster(study, area_id, creation_form)
new_config = creation_form.to_config(study.version)
create_cluster_cmd = self._make_create_cluster_cmd(area_id, new_config)

# Matrix edition
current_path = f"input/renewables/series/{area_id}/{source_id.lower()}/series"
new_path = f"input/renewables/series/{area_id}/{new_id.lower()}/series"
current_matrix = self.storage_service.raw_study_service.get(study, current_path)
command = [
ReplaceMatrix(
target=new_path,
matrix=current_matrix["data"],
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)
]
execute_or_add_commands(study, self._get_file_study(study), command, self.storage_service)
lower_source_id = source_id.lower()
source_path = f"input/renewables/series/{area_id}/{lower_source_id}/series"
new_path = f"input/renewables/series/{area_id}/{lower_new_id}/series"

# Prepare and execute commands
storage_service = self.storage_service.get_storage(study)
command_context = self.storage_service.variant_study_service.command_factory.command_context
current_matrix = storage_service.get(study, source_path)["data"]
replace_matrix_cmd = ReplaceMatrix(target=new_path, matrix=current_matrix, command_context=command_context)
commands = [create_cluster_cmd, replace_matrix_cmd]

execute_or_add_commands(study, self._get_file_study(study), commands, self.storage_service)

return new_cluster
return RenewableClusterOutput(**new_config.dict(by_alias=False))
142 changes: 142 additions & 0 deletions tests/integration/study_data_blueprint/test_renewable.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"""
import json
import re
import typing as t

import numpy as np
import pytest
Expand Down Expand Up @@ -488,3 +489,144 @@ def test_lifecycle(
description = obj["description"]
assert other_cluster_name.upper() in description
assert obj["exception"] == "ClusterAlreadyExists"

@pytest.fixture(name="base_study_id")
def base_study_id_fixture(self, request: t.Any, client: TestClient, user_access_token: str) -> str:
"""Prepare a managed study for the variant study tests."""
params = request.param
res = client.post(
"/v1/studies",
headers={"Authorization": f"Bearer {user_access_token}"},
params=params,
)
assert res.status_code in {200, 201}, res.json()
study_id: str = res.json()
return study_id

@pytest.fixture(name="variant_id")
def variant_id_fixture(self, request: t.Any, client: TestClient, user_access_token: str, base_study_id: str) -> str:
"""Prepare a variant study for the variant study tests."""
name = request.param
res = client.post(
f"/v1/studies/{base_study_id}/variants",
headers={"Authorization": f"Bearer {user_access_token}"},
params={"name": name},
)
assert res.status_code in {200, 201}, res.json()
study_id: str = res.json()
return study_id

# noinspection PyTestParametrized
@pytest.mark.parametrize("base_study_id", [{"name": "Base Study", "version": 860}], indirect=True)
@pytest.mark.parametrize("variant_id", ["Variant Study"], indirect=True)
def test_variant_lifecycle(self, client: TestClient, user_access_token: str, variant_id: str) -> None:
"""
In this test, we want to check that renewable clusters can be managed
in the context of a "variant" study.
"""
# Create an area
area_name = "France"
res = client.post(
f"/v1/studies/{variant_id}/areas",
headers={"Authorization": f"Bearer {user_access_token}"},
json={"name": area_name, "type": "AREA"},
)
assert res.status_code in {200, 201}, res.json()
area_cfg = res.json()
area_id = area_cfg["id"]

# Create a renewable cluster
cluster_name = "Th1"
res = client.post(
f"/v1/studies/{variant_id}/areas/{area_id}/clusters/renewable",
headers={"Authorization": f"Bearer {user_access_token}"},
json={
"name": cluster_name,
"group": "Wind Offshore",
"unitCount": 13,
"nominalCapacity": 42500,
},
)
assert res.status_code in {200, 201}, res.json()
cluster_id: str = res.json()["id"]

# Update the renewable cluster
res = client.patch(
f"/v1/studies/{variant_id}/areas/{area_id}/clusters/renewable/{cluster_id}",
headers={"Authorization": f"Bearer {user_access_token}"},
json={"unitCount": 15},
)
assert res.status_code == 200, res.json()
cluster_cfg = res.json()
assert cluster_cfg["unitCount"] == 15

# Update the series matrix
matrix = np.random.randint(0, 2, size=(8760, 1)).tolist()
matrix_path = f"input/renewables/series/{area_id}/{cluster_id.lower()}/series"
args = {"target": matrix_path, "matrix": matrix}
res = client.post(
f"/v1/studies/{variant_id}/commands",
json=[{"action": "replace_matrix", "args": args}],
headers={"Authorization": f"Bearer {user_access_token}"},
)
assert res.status_code in {200, 201}, res.json()

# Duplicate the renewable cluster
new_name = "Th2"
res = client.post(
f"/v1/studies/{variant_id}/areas/{area_id}/renewables/{cluster_id}",
headers={"Authorization": f"Bearer {user_access_token}"},
params={"newName": new_name},
)
assert res.status_code in {200, 201}, res.json()
cluster_cfg = res.json()
assert cluster_cfg["name"] == new_name
new_id = cluster_cfg["id"]

# Check that the duplicate has the right properties
res = client.get(
f"/v1/studies/{variant_id}/areas/{area_id}/clusters/renewable/{new_id}",
headers={"Authorization": f"Bearer {user_access_token}"},
)
assert res.status_code == 200, res.json()
cluster_cfg = res.json()
assert cluster_cfg["group"] == "Wind Offshore"
assert cluster_cfg["unitCount"] == 15
assert cluster_cfg["nominalCapacity"] == 42500

# Check that the duplicate has the right matrix
new_cluster_matrix_path = f"input/renewables/series/{area_id}/{new_id.lower()}/series"
res = client.get(
f"/v1/studies/{variant_id}/raw",
params={"path": new_cluster_matrix_path},
headers={"Authorization": f"Bearer {user_access_token}"},
)
assert res.status_code == 200
assert res.json()["data"] == matrix

# Delete the renewable cluster
res = client.delete(
f"/v1/studies/{variant_id}/areas/{area_id}/clusters/renewable",
headers={"Authorization": f"Bearer {user_access_token}"},
json=[cluster_id],
)
assert res.status_code == 204, res.json()

# Check the list of variant commands
res = client.get(
f"/v1/studies/{variant_id}/commands",
headers={"Authorization": f"Bearer {user_access_token}"},
)
assert res.status_code == 200, res.json()
commands = res.json()
assert len(commands) == 7
actions = [command["action"] for command in commands]
assert actions == [
"create_area",
"create_renewables_cluster",
"update_config",
"replace_matrix",
"create_renewables_cluster",
"replace_matrix",
"remove_renewables_cluster",
]

0 comments on commit 4e4d157

Please sign in to comment.