diff --git a/RemoteBMI.jl/example/.gitignore b/RemoteBMI.jl/example/.gitignore new file mode 100644 index 0000000..58b0ddd --- /dev/null +++ b/RemoteBMI.jl/example/.gitignore @@ -0,0 +1,2 @@ +data +sbm_config.toml diff --git a/RemoteBMI.jl/example/README.md b/RemoteBMI.jl/example/README.md index f279f53..d1cd916 100644 --- a/RemoteBMI.jl/example/README.md +++ b/RemoteBMI.jl/example/README.md @@ -25,11 +25,10 @@ Interact with it using the Python client. ```python from remotebmi.client.client import RemoteBmiClient from remotebmi.reserve import reserve_values +import os client = RemoteBmiClient('http://localhost:50555') -# TODO use placeholder for path -# client.initialize('/heat.toml') -client.initialize('/home/stefanv/git/eWaterCycle/remotebmi/python/heat.toml') +client.initialize(os.getcwd() + '/heat.toml') client.get_component_name() 'The 2D Heat Equation' client.update() @@ -50,3 +49,108 @@ r = client.get_value('plate_surface__temperature', dest) r client.finalize() ``` + +# Run Wflow.jl + +```shell +julia --project=. +] +add Wflow +# Install RemoteBMI +dev .. + + +# Download a parameter set +# From https://deltares.github.io/Wflow.jl/stable/user_guide/sample_data/#wflow_sbm_data +# with *nc moved to data/input +toml_url = "https://raw.githubusercontent.com/Deltares/Wflow.jl/master/test/sbm_config.toml" +staticmaps = "https://github.com/visr/wflow-artifacts/releases/download/v0.2.9/staticmaps-moselle.nc" +forcing = "https://github.com/visr/wflow-artifacts/releases/download/v0.2.6/forcing-moselle.nc" +instates = "https://github.com/visr/wflow-artifacts/releases/download/v0.2.6/instates-moselle.nc" +# create a "data" directory in the current directory +datadir = joinpath(@__DIR__, "data") +mkpath(datadir) +inputDir = joinpath(datadir, "input") +mkpath(inputDir) +toml_path = joinpath(@__DIR__, "sbm_config.toml") +# download resources to current and data dirs +download(staticmaps, joinpath(inputDir, "staticmaps-moselle.nc")) +download(forcing, joinpath(datadir, "forcing-moselle.nc")) +download(instates, joinpath(inputDir, "instates-moselle.nc")) +download(toml_url, toml_path) + +# Run server +using Wflow +import RemoteBMI +port = parse(Int, get(ENV, "BMI_PORT", "50051")) +RemoteBMI.run(Wflow.Model, "0.0.0.0", port) +``` + +Interact with it using the Python client. + +```python +from remotebmi.client.client import RemoteBmiClient +from remotebmi.reserve import reserve_values, reserve_grid_nodes, reserve_grid_edge_nodes, reserve_grid_nodes_per_face, reserve_grid_face_ +import numpy as np +import os + +client = RemoteBmiClient('http://localhost:50051') +# Change to location of sbm_config.toml +os.chdir('../RemoteBMI.jl/example') +%time client.initialize(os.getcwd() + '/sbm_config.toml') +CPU times: user 3.08 ms, sys: 8 μs, total: 3.09 ms +Wall time: 18.7 s +%time client.update() +CPU times: user 1.45 ms, sys: 78 μs, total: 1.53 ms +Wall time: 7.78 s +%time client.update() +CPU times: user 596 μs, sys: 980 μs, total: 1.58 ms +Wall time: 227 ms +client.get_component_name() +'sbm' +client.get_current_time() +172800.0 +client.get_time_units() +'s' +client.get_output_var_names() +['vertical.nlayers', + 'vertical.n_unsatlayers', + ... + 'lateral.river.q', + ... + 'lateral.river.reservoir.evaporation', + 'lateral.river.reservoir.actevap'] +client.get_var_location('lateral.river.q') +'node' +client.get_var_type('lateral.river.q') +numpy.float64 +client.get_var_grid('lateral.river.q') +3 +client.get_grid_type(3) +'unstructured' +client.get_grid_rank(3) +2 +client.get_grid_size(3) +5809 +client.get_grid_shape(3, np.empty(2)) +# Method does not exist, which is ok for this grid type +x = client.get_grid_x(3, reserve_grid_nodes(client, 3)) +len(x) +5809 +y = client.get_grid_y(3, reserve_grid_nodes(client, 3)) +len(y) +5809 +value = client.get_value('lateral.river.q', reserve_values(client, 'lateral.river.q')) +len(value) +5809 +client.get_grid_node_count(3) +5809 +client.get_grid_edge_count(3) +5808 +client.get_grid_face_count(3) +# Method does not exist, which is ok for this var location +edge_nodes = client.get_grid_edge_nodes(3, reserve_grid_edge_nodes(client, 3)) +len(edge_nodes) +11616 +client.finalize() +``` diff --git a/RemoteBMI.jl/src/RemoteBMI.jl b/RemoteBMI.jl/src/RemoteBMI.jl index 63dd8e4..a0cc5d7 100644 --- a/RemoteBMI.jl/src/RemoteBMI.jl +++ b/RemoteBMI.jl/src/RemoteBMI.jl @@ -124,17 +124,17 @@ function reserve_grid_coords(m, grid::Int64, dim_index::Int8)::Vector{Float64} end function get_grid_x(req::HTTP.Request, grid::Int64;)::Vector{Float64} - x = reserve_grid_coords(m, grid, 1) + x = reserve_grid_coords(m, grid, Int8(1)) return BMI.get_grid_x(m, grid, x) end function get_grid_y(req::HTTP.Request, grid::Int64;)::Vector{Float64} - y = reserve_grid_coords(m, grid, 2) + y = reserve_grid_coords(m, grid, Int8(2)) return BMI.get_grid_y(m, grid, y) end function get_grid_z(req::HTTP.Request, grid::Int64;)::Vector{Float64} - z = reserve_grid_coords(m, grid, 3) + z = reserve_grid_coords(m, grid, Int8(3)) return BMI.get_grid_z(m, grid, z) end diff --git a/python/remotebmi/client/client.py b/python/remotebmi/client/client.py index 1c1a434..3355bd2 100644 --- a/python/remotebmi/client/client.py +++ b/python/remotebmi/client/client.py @@ -5,11 +5,19 @@ class RemoteBmiClient(Bmi): - def __init__(self, base_url, max_keepalive_connections=0): + def __init__(self, base_url, timeout=60 * 60 * 24, max_keepalive_connections=0): + """RemoteBmiClient constructor + + Args: + base_url: Where the remote BMI server is running. + timeout: How long a response can take. + Defaults to 1 day. Set to None to disable timeout. + max_keepalive_connections: How many connections to keep alive. + """ # In some Python environments the reusing connection causes `illegal status line: bytesarray(b'14')` error # So we need to disable keepalive connections to be more reliable, but less efficient limits = Limits(max_keepalive_connections=max_keepalive_connections) - self.client = Client(base_url=base_url, limits=limits) + self.client = Client(base_url=base_url, timeout=timeout, limits=limits) def __del__(self): self.client.close() diff --git a/python/remotebmi/reserve.py b/python/remotebmi/reserve.py index 002c0dd..6d5bf9a 100644 --- a/python/remotebmi/reserve.py +++ b/python/remotebmi/reserve.py @@ -23,7 +23,7 @@ def reserve_grid_padding(model: Bmi, grid_id: int) -> np.ndarray: return np.empty(model.get_grid_rank(grid_id), dtype=np.float64) -def reserve_grid_nodes(model: Bmi, grid_id: int, dim_index: int) -> np.ndarray: +def reserve_grid_nodes(model: Bmi, grid_id: int, dim_index: int = 0) -> np.ndarray: """Reserve dest for :func:`bmipy.Bmi.get_grid_x`, :func:`bmipy.Bmi.get_grid_y` and :func:`bmipy.Bmi.get_grid_z` The dim_index goes x,y,z and model.get_grid_shape goes z,y,x or y,x so index is inverted