|
| 1 | +from io import StringIO |
1 | 2 | from types import SimpleNamespace |
2 | 3 |
|
3 | 4 | import numpy as np |
4 | 5 | import pytest |
| 6 | +from HGD import params as hgd_params |
5 | 7 |
|
6 | 8 | try: |
7 | 9 | from HGD.motion import d2q4_cpp |
@@ -289,3 +291,72 @@ def test_tau_relaxes_when_particle_exceeds_local_pore_size(): |
289 | 291 | assert not np.isnan(s_out[i0, j0, k]) |
290 | 292 | assert u_out[i0, j0, k] == pytest.approx(1.0 * relax_factor, abs=1e-10) |
291 | 293 | assert v_out[i0, j0, k] == pytest.approx(-0.5 * relax_factor, abs=1e-10) |
| 294 | + |
| 295 | + |
| 296 | +def test_inertia_true_can_be_loaded_from_json5(): |
| 297 | + _, p = hgd_params.load_file(StringIO("{ inertia: true, nx: 3, ny: 4, nm: 2 }")) |
| 298 | + assert p.inertia is True |
| 299 | + |
| 300 | + |
| 301 | +@pytest.mark.parametrize( |
| 302 | + "vx, vy, expected_ij", |
| 303 | + [ |
| 304 | + (1.0, 0.0, (5, 4)), |
| 305 | + (-1.0, 0.0, (3, 4)), |
| 306 | + (0.0, 1.0, (4, 5)), |
| 307 | + (0.0, -1.0, (4, 3)), |
| 308 | + ], |
| 309 | +) |
| 310 | +def test_directional_streaming_without_gravity(vx, vy, expected_ij): |
| 311 | + p = _make_params(nx=9, ny=9, nm=10) |
| 312 | + p.g = 0.0 |
| 313 | + start = (4, 4, 0) |
| 314 | + u, v, s = _single_particle_state(start=start, vx=vx, vy=vy, nx=p.nx, ny=p.ny, nm=p.nm) |
| 315 | + |
| 316 | + u_out, v_out, s_out = d2q4_cpp.stream(u, v, s, p) |
| 317 | + i, j, k = _particle_pos(s_out) |
| 318 | + assert (i, j) == expected_ij |
| 319 | + assert u_out[i, j, k] == pytest.approx(vx, abs=1e-12) |
| 320 | + assert v_out[i, j, k] == pytest.approx(vy, abs=1e-12) |
| 321 | + |
| 322 | + |
| 323 | +def test_particle_does_not_penetrate_masked_wall(): |
| 324 | + p = _make_params(nx=9, ny=9, nm=10) |
| 325 | + start = (4, 4, 0) |
| 326 | + p.boundary_mask[start[0] + 1, start[1]] = True |
| 327 | + |
| 328 | + u, v, s = _single_particle_state(start=start, vx=1.0, vy=0.0, nx=p.nx, ny=p.ny, nm=p.nm) |
| 329 | + u_out, v_out, s_out = d2q4_cpp.stream(u, v, s, p) |
| 330 | + i, j, k = _particle_pos(s_out) |
| 331 | + |
| 332 | + assert (i, j, k) == start |
| 333 | + assert u_out[i, j, k] == pytest.approx(1.0, abs=1e-12) |
| 334 | + assert v_out[i, j, k] == pytest.approx(0.0, abs=1e-12) |
| 335 | + |
| 336 | + |
| 337 | +def test_tau_relaxation_matches_exponential_decay_over_time(): |
| 338 | + p = _make_params(nx=7, ny=7, nm=10) |
| 339 | + p.tau = 2.0 |
| 340 | + p.nu_cs = 0.5 |
| 341 | + |
| 342 | + s = np.full((p.nx, p.ny, p.nm), np.nan, dtype=np.float64) |
| 343 | + u = np.zeros((p.nx, p.ny, p.nm), dtype=np.float64) |
| 344 | + v = np.zeros((p.nx, p.ny, p.nm), dtype=np.float64) |
| 345 | + |
| 346 | + i0, j0 = 3, 3 |
| 347 | + for k in range(6): |
| 348 | + s[i0, j0, k] = 1.0 |
| 349 | + u[i0, j0, k] = 1.0 |
| 350 | + v[i0, j0, k] = -0.5 |
| 351 | + |
| 352 | + p.boundary_mask[2, 3] = True |
| 353 | + p.boundary_mask[4, 3] = True |
| 354 | + p.boundary_mask[3, 2] = True |
| 355 | + p.boundary_mask[3, 4] = True |
| 356 | + |
| 357 | + for step in range(1, 4): |
| 358 | + u, v, s = d2q4_cpp.stream(u, v, s, p) |
| 359 | + expected_factor = np.exp(-step * p.dt / p.tau) |
| 360 | + for k in range(6): |
| 361 | + assert u[i0, j0, k] == pytest.approx(expected_factor, abs=1e-10) |
| 362 | + assert v[i0, j0, k] == pytest.approx(-0.5 * expected_factor, abs=1e-10) |
0 commit comments