Skip to content

Commit

Permalink
Fix resolution for simulation and inflow calc (#461)
Browse files Browse the repository at this point in the history
When implementing run_simulation and run_inflow_calculation I used our
predefined protobuf time series resolutions for simulation and inflow
calculation resolution. This is not correct and those might run on
resolutions we don't allow for time series.

Update proto to use
https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/duration.proto,
which is used by the Mesh server starting with v2.14.0.

See Volue/energy-mesh#5305 (internal).
See Volue/energy-mesh#5317 (internal).
  • Loading branch information
erny-powel authored May 6, 2024
1 parent 7ea01b0 commit 12d7c7e
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 11 deletions.
4 changes: 3 additions & 1 deletion docs/source/versions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Compatible with
New features
~~~~~~~~~~~~~~~~~~

- TBA
- It's now possible to specify the resolution of a hydro simulation or inflow
calculation using the optinal `resolution` argument to `run_simulation` and
`run_inflow_calculation`. See :doc:`hydsim` for more information.

Changes
~~~~~~~~~~~~~~~~~~
Expand Down
22 changes: 19 additions & 3 deletions src/volue/mesh/_base_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,7 @@ def run_simulation(
end_time: The (exclusive) end of the simulation interval.
resolution: The resolution of the simulation. The default resolution
of the simulation case is used if this is left as `None`.
Officially supported resolutions are 5, 10, 15, and 60 minutes,
but other resolutions may work. **Unimplemented.**
Officially supported resolutions are 5, 10, 15, and 60 minutes.
scenario: The scenario(s) to run. All scenarios are run if left as
`None`, no scenarios are run if set as -1, and a specific
numbered scenario is run if set as the number of that scenario.
Expand Down Expand Up @@ -878,6 +877,7 @@ def run_inflow_calculation(
start_time: datetime,
end_time: datetime,
*,
resolution: timedelta = None,
return_datasets: bool = False,
) -> Union[typing.Iterator[None], typing.AsyncIterator[None]]:
"""Run an inflow calculation using HydSim on the Mesh server.
Expand All @@ -889,6 +889,9 @@ def run_inflow_calculation(
water_course: The water course to calculate.
start_time: The (inclusive) start of the calculation interval.
end_time: The (exclusive) end of the calculation interval.
resolution: The resolution of the simulation. The default resolution
of the inflow calculation case is used if this is left as `None`.
Officially supported resolutions are 5, 10, 15, and 60 minutes.
return_datasets: Generate and return HydSim datasets that can be used
by Volue to diagnose issues with inflow calculations. For performance
reasons this should be false when not trying to diagnose an issue.
Expand Down Expand Up @@ -1484,7 +1487,7 @@ def _prepare_run_simulation_request(
case: str,
start_time: datetime,
end_time: datetime,
resolution: Timeseries.Resolution,
resolution: timedelta,
scenario: int,
return_datasets: bool,
) -> hydsim_pb2.RunHydroSimulationRequest:
Expand All @@ -1494,11 +1497,17 @@ def _prepare_run_simulation_request(
case_group, case_name = case.split("/", maxsplit=1)
simulation = f"Model/{model}/{case_group}.has_OptimisationCases/{case_name}.has_OptimisationParameters/Optimal.has_HydroSimulation/HydroSimulation"

proto_resolution = None
if resolution is not None:
proto_resolution = protobuf.duration_pb2.Duration()
proto_resolution.FromTimedelta(resolution)

return hydsim_pb2.RunHydroSimulationRequest(
session_id=_to_proto_guid(self.session_id),
simulation=_to_proto_object_mesh_id(simulation),
interval=_to_proto_utcinterval(start_time, end_time),
scenario=scenario,
resolution=proto_resolution,
return_datasets=return_datasets,
)

Expand All @@ -1507,6 +1516,7 @@ def _prepare_run_inflow_calculation_request(
targets: List[Object],
start_time: datetime,
end_time: datetime,
resolution: timedelta,
return_datasets: bool,
) -> hydsim_pb2.RunInflowCalculationRequest:
if start_time is None or end_time is None:
Expand All @@ -1515,10 +1525,16 @@ def _prepare_run_inflow_calculation_request(
if len(targets) != 1:
raise ValueError(f"expected one water course, found {len(targets)}")

proto_resolution = None
if resolution is not None:
proto_resolution = protobuf.duration_pb2.Duration()
proto_resolution.FromTimedelta(resolution)

return hydsim_pb2.RunInflowCalculationRequest(
session_id=_to_proto_guid(self.session_id),
watercourse=_to_proto_object_mesh_id(targets[0].id),
interval=_to_proto_utcinterval(start_time, end_time),
resolution=proto_resolution,
return_datasets=return_datasets,
)

Expand Down
3 changes: 2 additions & 1 deletion src/volue/mesh/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,14 +432,15 @@ def run_inflow_calculation(
start_time: datetime,
end_time: datetime,
*,
resolution: timedelta = None,
return_datasets: bool = False,
) -> typing.Iterator[None]:
targets = self.search_for_objects(
f"Model/{model}/Mesh.To_Areas/{area}",
f"To_HydroProduction/To_WaterCourses/@[.Name={water_course}]",
)
request = self._prepare_run_inflow_calculation_request(
targets, start_time, end_time, return_datasets
targets, start_time, end_time, resolution, return_datasets
)
for response in self.hydsim_service.RunInflowCalculation(request):
if response.HasField("log_message"):
Expand Down
3 changes: 2 additions & 1 deletion src/volue/mesh/aio/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,15 @@ async def run_inflow_calculation(
start_time: datetime,
end_time: datetime,
*,
resolution: timedelta = None,
return_datasets: bool = False,
) -> typing.AsyncIterator[None]:
targets = await self.search_for_objects(
f"Model/{model}/Mesh.To_Areas/{area}",
f"To_HydroProduction/To_WaterCourses/@[.Name={water_course}]",
)
request = self._prepare_run_inflow_calculation_request(
targets, start_time, end_time, return_datasets
targets, start_time, end_time, resolution, return_datasets
)
async for response in self.hydsim_service.RunInflowCalculation(request):
if response.HasField("log_message"):
Expand Down
2 changes: 2 additions & 0 deletions src/volue/mesh/examples/run_inflow_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def sync_run_inflow_calculation(address, port, root_pem_certificate):
start_time,
end_time,
return_datasets=True,
resolution=timedelta(minutes=5),
):
if isinstance(response, mesh.LogMessage):
print(
Expand Down Expand Up @@ -58,6 +59,7 @@ async def async_run_inflow_calculation(address, port, root_pem_certificate):
start_time,
end_time,
return_datasets=True,
resolution=timedelta(minutes=5),
):
if isinstance(response, mesh.LogMessage):
print(
Expand Down
16 changes: 13 additions & 3 deletions src/volue/mesh/examples/run_simulation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
import logging
from datetime import datetime
from datetime import datetime, timedelta

import helpers

Expand All @@ -20,7 +20,12 @@ def sync_run_simulation(address, port, root_pem_certificate):

try:
for response in session.run_simulation(
"Mesh", "Cases/Demo", start_time, end_time, return_datasets=True
"Mesh",
"Cases/Demo",
start_time,
end_time,
return_datasets=True,
resolution=timedelta(minutes=5),
):
if isinstance(response, mesh.LogMessage):
print(
Expand All @@ -47,7 +52,12 @@ async def async_run_simulation(address, port, root_pem_certificate):

try:
async for response in session.run_simulation(
"Mesh", "Cases/Demo", start_time, end_time, return_datasets=True
"Mesh",
"Cases/Demo",
start_time,
end_time,
return_datasets=True,
resolution=timedelta(minutes=5),
):
if isinstance(response, mesh.LogMessage):
print(
Expand Down
12 changes: 10 additions & 2 deletions src/volue/mesh/proto/hydsim/v1alpha/hydsim.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ syntax = "proto3";

package volue.mesh.grpc.hydsim.v1alpha;

import "google/protobuf/duration.proto";

import "volue/mesh/proto/type/resources.proto";

// Experimental HydSim API, very unstable.
Expand All @@ -25,7 +27,10 @@ message RunHydroSimulationRequest {
volue.mesh.grpc.type.Guid session_id = 1;
volue.mesh.grpc.type.MeshId simulation = 2;
volue.mesh.grpc.type.UtcInterval interval = 3;
volue.mesh.grpc.type.Resolution resolution = 4;

// The resolution to use for the simulation. Must be 5, 10, 15, or 60 minutes.
// If left unset the default resolution of the simulation is used.
google.protobuf.Duration resolution = 4;

// The scenario to use. The default of zero runs all scenarios, -1 runs no
// scenarios, and a number referring to an existing scenario runs that
Expand All @@ -44,7 +49,10 @@ message RunInflowCalculationRequest {
volue.mesh.grpc.type.Guid session_id = 1;
volue.mesh.grpc.type.MeshId watercourse = 2;
volue.mesh.grpc.type.UtcInterval interval = 3;
volue.mesh.grpc.type.Resolution resolution = 4;

// The resolution to use for the inflow calculation. Must be 5, 10, 15, or 60 minutes.
// If left unset the default resolution of the inflow calculation is used.
google.protobuf.Duration resolution = 4;

// Generate and return HydSim datasets that can be used by Volue to diagnose
// issues with inflow calculations. For performance reasons this should be
Expand Down

0 comments on commit 12d7c7e

Please sign in to comment.