Skip to content

Commit 9c7c03f

Browse files
authored
Match bmipy (#12)
* Add shape property This makes it easier to implement the BMI. * Fix typo * Use array parameter in grid shape, spacing, and origin This better matches the BMI form in bmipy. * Set grid type to uniform_rectilinear This matches the docs. * Use heat shape property for grid rank and size This is a convenience. Also cast the return from *get_grid_size* to `int` to pass the bmi-tester. * Use dest parameter in get_value methods This better matches the form from bmipy. * Update tests for updated BMI functions * Add six as a requirement It's found by conda but not by pip. * Remove unused import * Make pretty * Test on py38 and py39 * Split off testing requirements to new file And added bmi-tester as a testing requirement. * Run bmi-tester in the CI I chose to keep everything in conda to try to avoid mixing package sources.
1 parent 46e5221 commit 9c7c03f

10 files changed

Lines changed: 94 additions & 62 deletions

File tree

.travis.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ os:
44
- osx
55
env:
66
matrix:
7-
- CONDA_ENV=3.7
8-
- CONDA_ENV=3.6
7+
- CONDA_ENV=3.8
8+
- CONDA_ENV=3.9
99
sudo: false
1010
jobs:
1111
include:
@@ -49,10 +49,13 @@ before_install:
4949
- export PATH="$HOME/anaconda/bin:$PATH"
5050
- hash -r
5151
- conda config --set always_yes yes --set changeps1 no
52-
- conda create -n test_env python=$CONDA_ENV
52+
- conda create -n test_env python=$CONDA_ENV --file=requirements.txt -c conda-forge
5353
- source activate test_env
5454
install:
55-
- pip install . -r requirements.txt
55+
- pip install .
56+
before_script:
57+
- conda install --file=requirements-testing.txt -c conda-forge
5658
script:
5759
- pytest --cov=heat --cov-report=xml:$(pwd)/coverage.xml -vvv
60+
- bmi-test heat:BmiHeat --config-file=./examples/heat.yaml --root-dir=./examples -vvv
5861
after_success: coveralls

examples/heat.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Heat model configuration
2+
shape:
3+
- 6
4+
- 8
5+
spacing:
6+
- 1.0
7+
- 1.0
8+
origin:
9+
- 0.0
10+
- 0.0
11+
alpha: 1.0

heat/bmi_heat.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"""Basic Model Interface implementation for the 2D heat model."""
33

44
import numpy as np
5-
65
from bmipy import Bmi
76

87
from .heat import Heat
@@ -49,7 +48,7 @@ def initialize(self, filename=None):
4948
self._var_units = {"plate_surface__temperature": "K"}
5049
self._var_loc = {"plate_surface__temperature": "node"}
5150
self._grids = {0: ["plate_surface__temperature"]}
52-
self._grid_type = {0: "uniform_rectilinear_grid"}
51+
self._grid_type = {0: "uniform_rectilinear"}
5352

5453
def update(self):
5554
"""Advance model by one time step."""
@@ -167,7 +166,7 @@ def get_grid_rank(self, grid_id):
167166
int
168167
Rank of grid.
169168
"""
170-
return len(self.get_grid_shape(grid_id))
169+
return len(self._model.shape)
171170

172171
def get_grid_size(self, grid_id):
173172
"""Size of grid.
@@ -182,7 +181,7 @@ def get_grid_size(self, grid_id):
182181
int
183182
Size of grid.
184183
"""
185-
return np.prod(self.get_grid_shape(grid_id))
184+
return int(np.prod(self._model.shape))
186185

187186
def get_value_ptr(self, var_name):
188187
"""Reference to values.
@@ -199,28 +198,33 @@ def get_value_ptr(self, var_name):
199198
"""
200199
return self._values[var_name]
201200

202-
def get_value(self, var_name):
201+
def get_value(self, var_name, dest):
203202
"""Copy of values.
204203
205204
Parameters
206205
----------
207206
var_name : str
208207
Name of variable as CSDMS Standard Name.
208+
dest : ndarray
209+
A numpy array into which to place the values.
209210
210211
Returns
211212
-------
212213
array_like
213214
Copy of values.
214215
"""
215-
return self.get_value_ptr(var_name).copy()
216+
dest[:] = self.get_value_ptr(var_name).flatten()
217+
return dest
216218

217-
def get_value_at_indices(self, var_name, indices):
219+
def get_value_at_indices(self, var_name, dest, indices):
218220
"""Get values at particular indices.
219221
220222
Parameters
221223
----------
222224
var_name : str
223225
Name of variable as CSDMS Standard Name.
226+
dest : ndarray
227+
A numpy array into which to place the values.
224228
indices : array_like
225229
Array of indices.
226230
@@ -229,7 +233,8 @@ def get_value_at_indices(self, var_name, indices):
229233
array_like
230234
Values at indices.
231235
"""
232-
return self.get_value_ptr(var_name).take(indices)
236+
dest[:] = self.get_value_ptr(var_name).take(indices)
237+
return dest
233238

234239
def set_value(self, var_name, src):
235240
"""Set model values.
@@ -279,18 +284,21 @@ def get_output_var_names(self):
279284
"""Get names of output variables."""
280285
return self._output_var_names
281286

282-
def get_grid_shape(self, grid_id):
287+
def get_grid_shape(self, grid_id, shape):
283288
"""Number of rows and columns of uniform rectilinear grid."""
284289
var_name = self._grids[grid_id][0]
285-
return self.get_value_ptr(var_name).shape
290+
shape[:] = self.get_value_ptr(var_name).shape
291+
return shape
286292

287-
def get_grid_spacing(self, grid_id):
293+
def get_grid_spacing(self, grid_id, spacing):
288294
"""Spacing of rows and columns of uniform rectilinear grid."""
289-
return self._model.spacing
295+
spacing[:] = self._model.spacing
296+
return spacing
290297

291-
def get_grid_origin(self, grid_id):
298+
def get_grid_origin(self, grid_id, origin):
292299
"""Origin of uniform rectilinear grid."""
293-
return self._model.origin
300+
origin[:] = self._model.origin
301+
return origin
294302

295303
def get_grid_type(self, grid_id):
296304
"""Type of grid."""

heat/heat.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def __init__(
8686
):
8787
"""Create a new heat model.
8888
89-
Paramters
89+
Parameters
9090
---------
9191
shape : array_like, optional
9292
The shape of the solution grid as (*rows*, *columns*).
@@ -139,8 +139,13 @@ def time_step(self, time_step):
139139
self._time_step = time_step
140140

141141
@property
142-
def spacing(self):
142+
def shape(self):
143143
"""Shape of the model grid."""
144+
return self._shape
145+
146+
@property
147+
def spacing(self):
148+
"""Spacing between nodes of the model grid."""
144149
return self._spacing
145150

146151
@property

requirements-testing.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
coveralls
2+
flake8
3+
pytest
4+
pytest-cov
5+
six
6+
bmi-tester

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
coveralls
2-
flake8
3-
pytest
4-
pytest-cov
1+
numpy
2+
scipy
3+
pyyaml
4+
bmipy

setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,3 @@
2727
packages=find_packages(),
2828
cmdclass=versioneer.get_cmdclass(),
2929
)
30-
31-

tests/test_get_value.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python
22
from numpy.testing import assert_array_almost_equal, assert_array_less
3+
import numpy as np
34

45
from heat import BmiHeat
56

@@ -17,8 +18,11 @@ def test_get_value_copy():
1718
model = BmiHeat()
1819
model.initialize()
1920

20-
z0 = model.get_value("plate_surface__temperature")
21-
z1 = model.get_value("plate_surface__temperature")
21+
dest0 = np.empty(model.get_grid_size(0), dtype=float)
22+
dest1 = np.empty(model.get_grid_size(0), dtype=float)
23+
24+
z0 = model.get_value("plate_surface__temperature", dest0)
25+
z1 = model.get_value("plate_surface__temperature", dest1)
2226

2327
assert z0 is not z1
2428
assert_array_almost_equal(z0, z1)
@@ -28,11 +32,13 @@ def test_get_value_pointer():
2832
model = BmiHeat()
2933
model.initialize()
3034

35+
dest1 = np.empty(model.get_grid_size(0), dtype=float)
36+
3137
z0 = model.get_value_ptr("plate_surface__temperature")
32-
z1 = model.get_value("plate_surface__temperature")
38+
z1 = model.get_value("plate_surface__temperature", dest1)
3339

3440
assert z0 is not z1
35-
assert_array_almost_equal(z0, z1)
41+
assert_array_almost_equal(z0.flatten(), z1)
3642

3743
for _ in range(10):
3844
model.update()
@@ -44,8 +50,10 @@ def test_get_value_at_indices():
4450
model = BmiHeat()
4551
model.initialize()
4652

53+
dest = np.empty(3, dtype=float)
54+
4755
z0 = model.get_value_ptr("plate_surface__temperature")
48-
z1 = model.get_value_at_indices("plate_surface__temperature", [0, 2, 4])
56+
z1 = model.get_value_at_indices("plate_surface__temperature", dest, [0, 2, 4])
4957

5058
assert_array_almost_equal(z0.take((0, 2, 4)), z1)
5159

tests/test_grid_info.py

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python
2-
import pytest
2+
import numpy as np
3+
from numpy.testing import assert_array_equal
34

45
from heat import BmiHeat
56

@@ -47,49 +48,34 @@ def test_grid_var_rank():
4748
assert model.get_grid_rank(grid_id) == 2
4849

4950

50-
def test_grid_var_rank_fail():
51-
model = BmiHeat()
52-
model.initialize()
53-
with pytest.raises(KeyError):
54-
model.get_grid_rank(invalid_grid_id)
55-
56-
5751
def test_grid_var_size():
5852
model = BmiHeat()
5953
model.initialize()
6054
assert model.get_grid_size(grid_id) == 200
6155

6256

63-
def test_grid_var_size_fail():
64-
model = BmiHeat()
65-
model.initialize()
66-
with pytest.raises(KeyError):
67-
model.get_grid_size(invalid_grid_id)
68-
69-
7057
def test_grid_var_shape():
7158
model = BmiHeat()
7259
model.initialize()
73-
assert model.get_grid_shape(grid_id) == (10, 20)
74-
75-
76-
def test_grid_var_shape_fail():
77-
model = BmiHeat()
78-
model.initialize()
79-
with pytest.raises(KeyError):
80-
model.get_grid_shape(invalid_grid_id)
60+
ndim = model.get_grid_rank(0)
61+
shape = np.empty(ndim, dtype=np.int32)
62+
assert_array_equal(model.get_grid_shape(grid_id, shape), (10, 20))
8163

8264

8365
def test_grid_var_spacing():
8466
model = BmiHeat()
8567
model.initialize()
86-
assert model.get_grid_spacing(grid_id) == (1.0, 1.0)
68+
ndim = model.get_grid_rank(0)
69+
spacing = np.empty(ndim, dtype=np.int32)
70+
assert_array_equal(model.get_grid_spacing(grid_id, spacing), (1.0, 1.0))
8771

8872

8973
def test_grid_var_origin():
9074
model = BmiHeat()
9175
model.initialize()
92-
assert model.get_grid_origin(grid_id) == (0.0, 0.0)
76+
ndim = model.get_grid_rank(0)
77+
origin = np.empty(ndim, dtype=np.int32)
78+
assert_array_equal(model.get_grid_origin(grid_id, origin), (0.0, 0.0))
9379

9480

9581
def test_grid_var_type():
@@ -101,4 +87,4 @@ def test_grid_var_type():
10187
def test_grid_type():
10288
model = BmiHeat()
10389
model.initialize()
104-
assert model.get_grid_type(grid_id) == "uniform_rectilinear_grid"
90+
assert model.get_grid_type(grid_id) == "uniform_rectilinear"

tests/test_irf.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
from io import StringIO
33

4-
from numpy.testing import assert_almost_equal, assert_array_less
4+
from numpy.testing import assert_almost_equal, assert_array_less, assert_array_equal
55
import numpy as np
66
import yaml
77

@@ -36,16 +36,20 @@ def test_initialize_defaults():
3636
model.initialize()
3737

3838
assert_almost_equal(model.get_current_time(), 0.0)
39-
assert_array_less(model.get_value("plate_surface__temperature"), 1.0)
40-
assert_array_less(0.0, model.get_value("plate_surface__temperature"))
39+
z0 = model.get_value_ptr("plate_surface__temperature")
40+
assert_array_less(z0, 1.0)
41+
assert_array_less(0.0, z0)
4142

4243

4344
def test_initialize_from_file_like():
4445
config = StringIO(yaml.dump({"shape": [7, 5]}))
4546
model = BmiHeat()
4647
model.initialize(config)
4748

48-
assert model.get_grid_shape(0) == (7, 5)
49+
ndim = model.get_grid_rank(0)
50+
shape = np.empty(ndim, dtype=np.int32)
51+
52+
assert_array_equal(model.get_grid_shape(0, shape), (7, 5))
4953

5054

5155
def test_initialize_from_file():
@@ -62,7 +66,10 @@ def test_initialize_from_file():
6266

6367
os.remove(name)
6468

65-
assert model.get_grid_shape(0) == (7, 5)
69+
ndim = model.get_grid_rank(0)
70+
shape = np.empty(ndim, dtype=np.int32)
71+
72+
assert_array_equal(model.get_grid_shape(0, shape), (7, 5))
6673

6774

6875
def test_update():

0 commit comments

Comments
 (0)