diff --git a/docs/source/versions.rst b/docs/source/versions.rst index 1cdab4e0..716082c4 100644 --- a/docs/source/versions.rst +++ b/docs/source/versions.rst @@ -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 ~~~~~~~~~~~~~~~~~~ diff --git a/src/volue/mesh/_base_session.py b/src/volue/mesh/_base_session.py index 77af193d..80b405ed 100644 --- a/src/volue/mesh/_base_session.py +++ b/src/volue/mesh/_base_session.py @@ -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. @@ -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. @@ -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. @@ -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: @@ -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, ) @@ -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: @@ -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, ) diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index d83ed8b2..1f0f6ac8 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -432,6 +432,7 @@ 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( @@ -439,7 +440,7 @@ def run_inflow_calculation( 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"): diff --git a/src/volue/mesh/aio/_connection.py b/src/volue/mesh/aio/_connection.py index d4373341..006eb76b 100644 --- a/src/volue/mesh/aio/_connection.py +++ b/src/volue/mesh/aio/_connection.py @@ -440,6 +440,7 @@ 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( @@ -447,7 +448,7 @@ async def run_inflow_calculation( 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"): diff --git a/src/volue/mesh/examples/run_inflow_calculation.py b/src/volue/mesh/examples/run_inflow_calculation.py index ef00e486..530ce1a5 100644 --- a/src/volue/mesh/examples/run_inflow_calculation.py +++ b/src/volue/mesh/examples/run_inflow_calculation.py @@ -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( @@ -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( diff --git a/src/volue/mesh/examples/run_simulation.py b/src/volue/mesh/examples/run_simulation.py index c871e713..0ddbbdf5 100644 --- a/src/volue/mesh/examples/run_simulation.py +++ b/src/volue/mesh/examples/run_simulation.py @@ -1,6 +1,6 @@ import asyncio import logging -from datetime import datetime +from datetime import datetime, timedelta import helpers @@ -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( @@ -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( diff --git a/src/volue/mesh/proto/hydsim/v1alpha/hydsim.proto b/src/volue/mesh/proto/hydsim/v1alpha/hydsim.proto index 00a9e031..3edb9f7e 100644 --- a/src/volue/mesh/proto/hydsim/v1alpha/hydsim.proto +++ b/src/volue/mesh/proto/hydsim/v1alpha/hydsim.proto @@ -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. @@ -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 @@ -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