|
20 | 20 | ) |
21 | 21 |
|
22 | 22 |
|
| 23 | +# fmt: off |
| 24 | +SOIL_GRID = np.array([ |
| 25 | + -5. , -4.8 , -4.6 , -4.4 , -4.2 , -4. , -3.8 , -3.6 , |
| 26 | + -3.4 , -3.2 , -3. , -2.8 , -2.6 , -2.45 , -2.3 , -2.2 , |
| 27 | + -2.1 , -2. , -1.9 , -1.8 , -1.7 , -1.6 , -1.5 , -1.4 , |
| 28 | + -1.3 , -1.2 , -1.1 , -1. , -0.9 , -0.8 , -0.7 , -0.6 , |
| 29 | + -0.55 , -0.5 , -0.45 , -0.4 , -0.35 , -0.325, -0.3 , -0.275, |
| 30 | + -0.25 , -0.23 , -0.21 , -0.19 , -0.17 , -0.15 , -0.13 , -0.11 , |
| 31 | + -0.09 , -0.07 , -0.05 , -0.03 , -0.02 , -0.01 , -0., |
| 32 | +]) |
| 33 | + |
| 34 | +INVALID_METHODS = ( |
| 35 | + "get_grid_spacing", "get_grid_origin", "get_var_location", "get_grid_node_count", |
| 36 | + "get_grid_edge_count", "get_grid_face_count", "get_grid_edge_nodes", |
| 37 | + "get_grid_face_edges", "get_grid_face_nodes", "get_grid_nodes_per_face" |
| 38 | +) |
| 39 | +# fmt: on |
| 40 | + |
| 41 | + |
23 | 42 | def docker_available(): |
24 | 43 | try: |
25 | 44 | docker.APIClient() |
@@ -98,49 +117,160 @@ def prepare_data_config(tmpdir_factory, prep_input_data) -> Path: |
98 | 117 | return config_dir |
99 | 118 |
|
100 | 119 |
|
101 | | -@pytest.mark.skipif(not docker_available(), reason="Docker not available") |
102 | | -def test_initialize(prepare_data_config): |
| 120 | +@pytest.fixture(scope="class") |
| 121 | +def uninitialized_model(): |
103 | 122 | model = StemmusScopeBmi() |
| 123 | + yield model |
| 124 | + try: |
| 125 | + model.finalize() |
| 126 | + except: # noqa |
| 127 | + pass |
104 | 128 |
|
105 | | - assert model.get_component_name() == "STEMMUS_SCOPE" |
106 | | - with pytest.raises(ValueError, match="STEMMUS_SCOPE process is not running"): |
107 | | - model.update() |
108 | 129 |
|
| 130 | +@pytest.fixture(scope="class") |
| 131 | +def initialized_model(uninitialized_model, prepare_data_config): |
| 132 | + model: StemmusScopeBmi = uninitialized_model |
109 | 133 | model.initialize(str(prepare_data_config)) |
| 134 | + yield model |
| 135 | + model.finalize() |
110 | 136 |
|
111 | | - assert isinstance(model.get_input_item_count(), int) |
112 | | - assert isinstance(model.get_output_item_count(), int) |
113 | | - assert "soil_temperature" in model.get_input_var_names() |
114 | | - assert "respiration" in model.get_output_var_names() |
115 | 137 |
|
116 | | - assert model.get_var_grid("respiration") == 0 |
117 | | - assert model.get_var_grid("soil_temperature") == 1 |
| 138 | +@pytest.fixture(scope="class") |
| 139 | +def updated_model(uninitialized_model, prepare_data_config): |
| 140 | + model: StemmusScopeBmi = uninitialized_model |
| 141 | + model.initialize(str(prepare_data_config)) |
| 142 | + model.update() |
| 143 | + yield model |
| 144 | + model.finalize() |
118 | 145 |
|
119 | | - assert model.get_var_type("soil_temperature") == "float64" |
120 | 146 |
|
121 | | - # model.get_grid_size needs to have .update() run. |
122 | | - model.update() |
| 147 | +@pytest.mark.skipif(not docker_available(), reason="Docker not available") |
| 148 | +class TestUninitialized: |
| 149 | + def test_component_name(self, uninitialized_model): |
| 150 | + assert uninitialized_model.get_component_name() == "STEMMUS_SCOPE" |
123 | 151 |
|
124 | | - dest = np.zeros(model.get_grid_size(0)) |
125 | | - np.testing.assert_almost_equal(model.get_grid_x(0, x=dest), np.array([-107.80752563])) |
126 | | - np.testing.assert_almost_equal(model.get_grid_y(0, y=dest), np.array([37.93380356])) |
| 152 | + def test_invalid_update(self, uninitialized_model): |
| 153 | + with pytest.raises(ValueError, match="STEMMUS_SCOPE process is not running"): |
| 154 | + uninitialized_model.update() |
127 | 155 |
|
128 | | - with pytest.raises(ValueError, match="has no dimension `z`"): |
129 | | - model.get_grid_z(0, z=dest) |
| 156 | + def test_get_ptr(self, uninitialized_model): |
| 157 | + with pytest.raises(NotImplementedError): |
| 158 | + uninitialized_model.get_value_ptr("soil_temperature") |
130 | 159 |
|
131 | | - model.update() |
| 160 | + @pytest.mark.parametrize("method_name", INVALID_METHODS) |
| 161 | + def test_not_implemented(self, uninitialized_model, method_name): |
| 162 | + method = getattr(uninitialized_model, method_name) |
| 163 | + nargs = method.__code__.co_argcount - 1 # remove "self" |
| 164 | + with pytest.raises(NotImplementedError): |
| 165 | + method(*(nargs * [0])) |
132 | 166 |
|
133 | | - dest = np.zeros(1) |
134 | | - model.get_value("respiration", dest) |
135 | | - assert dest[0] != 0. |
| 167 | + def test_initialize(self, uninitialized_model, prepare_data_config): |
| 168 | + uninitialized_model.initialize(str(prepare_data_config)) |
136 | 169 |
|
137 | | - dest = np.zeros(1) |
138 | | - model.set_value_at_indices( |
139 | | - "soil_temperature", |
140 | | - inds=np.array([0]), |
141 | | - src=np.array([0.]), |
142 | | - ) |
143 | | - model.get_value_at_indices("soil_temperature", dest, inds=np.array([0])) |
144 | | - assert dest[0] == 0. |
145 | 170 |
|
146 | | - model.finalize() |
| 171 | +@pytest.mark.skipif(not docker_available(), reason="Docker not available") |
| 172 | +class TestInitializedModel: |
| 173 | + def test_input_item(self, initialized_model): |
| 174 | + assert isinstance(initialized_model.get_input_item_count(), int) |
| 175 | + |
| 176 | + def test_output_item(self, initialized_model): |
| 177 | + assert isinstance(initialized_model.get_output_item_count(), int) |
| 178 | + |
| 179 | + def test_input_var(self, initialized_model): |
| 180 | + assert "soil_temperature" in initialized_model.get_input_var_names() |
| 181 | + |
| 182 | + def test_output_var(self, initialized_model): |
| 183 | + assert "respiration" in initialized_model.get_output_var_names() |
| 184 | + |
| 185 | + def test_var_grid(self, initialized_model): |
| 186 | + assert initialized_model.get_var_grid("respiration") == 0 |
| 187 | + assert initialized_model.get_var_grid("soil_temperature") == 1 |
| 188 | + |
| 189 | + def test_var_type(self, initialized_model): |
| 190 | + assert initialized_model.get_var_type("soil_temperature") == "float64" |
| 191 | + |
| 192 | + def test_grid_type(self, initialized_model): |
| 193 | + assert initialized_model.get_grid_type(0) == "rectilinear" |
| 194 | + assert initialized_model.get_grid_type(1) == "rectilinear" |
| 195 | + |
| 196 | + def test_var_units(self, initialized_model): |
| 197 | + assert initialized_model.get_var_units("soil_temperature") == "degC" |
| 198 | + |
| 199 | + def test_grid_rank(self, initialized_model): |
| 200 | + grid_resp = initialized_model.get_var_grid("respiration") |
| 201 | + grid_t = initialized_model.get_var_grid("soil_temperature") |
| 202 | + assert initialized_model.get_grid_rank(grid_resp) == 2 |
| 203 | + assert initialized_model.get_grid_rank(grid_t) == 3 |
| 204 | + |
| 205 | + def test_get_time_units(self, initialized_model): |
| 206 | + assert ( |
| 207 | + initialized_model.get_time_units() |
| 208 | + == "seconds since 1970-01-01 00:00:00.0 +0000" |
| 209 | + ) |
| 210 | + |
| 211 | + def test_get_start_time(self, initialized_model): |
| 212 | + assert initialized_model.get_start_time() == 820454400.0 # 1996-01-01 00:00:00 |
| 213 | + |
| 214 | + def test_get_end_time(self, initialized_model): |
| 215 | + assert initialized_model.get_end_time() == 820461600.0 # 1996-01-01 02:00:00 |
| 216 | + |
| 217 | + def test_model_update(self, initialized_model): |
| 218 | + initialized_model.update() |
| 219 | + |
| 220 | + |
| 221 | +class TestUpdatedModel: |
| 222 | + # Many of these should be available after init |
| 223 | + def test_get_current_time(self, updated_model): |
| 224 | + assert updated_model.get_current_time() == ( |
| 225 | + updated_model.get_start_time() + updated_model.get_time_step() |
| 226 | + ) |
| 227 | + |
| 228 | + def test_get_time_step(self, updated_model): |
| 229 | + assert updated_model.get_time_step() == 1800 |
| 230 | + |
| 231 | + def test_grid_coords(self, updated_model): |
| 232 | + dest = np.zeros(updated_model.get_grid_size(0)) |
| 233 | + np.testing.assert_almost_equal( |
| 234 | + updated_model.get_grid_x(0, x=dest), np.array([-107.80752563]) |
| 235 | + ) |
| 236 | + np.testing.assert_almost_equal( |
| 237 | + updated_model.get_grid_y(0, y=dest), np.array([37.93380356]) |
| 238 | + ) |
| 239 | + |
| 240 | + def test_invalid_dimension(self, updated_model): |
| 241 | + dest = np.zeros(updated_model.get_grid_size(0)) |
| 242 | + with pytest.raises(ValueError, match="has no dimension `z`"): |
| 243 | + updated_model.get_grid_z(0, z=dest) |
| 244 | + |
| 245 | + def test_grid_z(self, updated_model): |
| 246 | + grid = updated_model.get_var_grid("soil_temperature") |
| 247 | + dest = np.zeros(updated_model.get_grid_size(grid)) |
| 248 | + updated_model.get_grid_z(grid, z=dest) |
| 249 | + np.testing.assert_array_equal(dest, SOIL_GRID) |
| 250 | + |
| 251 | + def test_grid_shape(self, updated_model): |
| 252 | + grid = updated_model.get_var_grid("soil_temperature") |
| 253 | + shape = np.zeros(updated_model.get_grid_rank(grid), dtype=int) |
| 254 | + updated_model.get_grid_shape(grid, shape) |
| 255 | + np.testing.assert_array_equal(shape, np.array([55, 1, 1], dtype=int)) |
| 256 | + |
| 257 | + def test_get_value(self, updated_model): |
| 258 | + dest = np.zeros(1) |
| 259 | + updated_model.get_value("respiration", dest) |
| 260 | + assert dest[0] != 0.0 |
| 261 | + |
| 262 | + def test_set_value_inds(self, updated_model): |
| 263 | + dest = np.zeros(1) |
| 264 | + updated_model.set_value_at_indices( |
| 265 | + "soil_temperature", |
| 266 | + inds=np.array([0]), |
| 267 | + src=np.array([0.0]), |
| 268 | + ) |
| 269 | + updated_model.get_value_at_indices("soil_temperature", dest, inds=np.array([0])) |
| 270 | + assert dest[0] == 0.0 |
| 271 | + |
| 272 | + def test_itemsize(self, updated_model): |
| 273 | + assert updated_model.get_var_itemsize("soil_temperature") == 8 # ==64 bits |
| 274 | + |
| 275 | + def test_get_var_nbytes(self, updated_model): |
| 276 | + assert updated_model.get_var_nbytes("soil_temperature") == 8 * 55 |
0 commit comments