From c8588a453e3ab4266be52b199e0e75f02c903e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dieter=20Werthm=C3=BCller?= Date: Tue, 16 Jul 2024 15:13:35 +0200 Subject: [PATCH] Tests utils --- resmda/reservoir_simulator.py | 19 ++++++-- tests/test_data_assimilation.py | 5 ++ tests/test_reservoir_simulator.py | 5 ++ tests/test_utils.py | 80 +++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 tests/test_data_assimilation.py create mode 100644 tests/test_reservoir_simulator.py diff --git a/resmda/reservoir_simulator.py b/resmda/reservoir_simulator.py index 73c22f0..70f9dc0 100644 --- a/resmda/reservoir_simulator.py +++ b/resmda/reservoir_simulator.py @@ -29,9 +29,20 @@ def __dir__(): class Simulator: """A small 2D Reservoir Simulator. - 2D connection-based, single phase, single component system using the Darcy - assumption, backward Euler for time discretization, and finite volume for - space discretization. + 2D connection-based, single-phase, single-component system using the Darcy + assumption, employing backward Euler for time discretization and finite + volume for space discretization. It simulates a single-phase fluid (likely + water) with compressibility in a reservoir with constant porosity and a + heterogeneous permeability field. The simulator utilizes SciPy's + ``sparse.linalg.solve`` for pressure solutions and calculates + transmissibilities for inter-block connections. Well modeling is handled + through the Peaceman well model for well indices, with constant pressure + boundary conditions for injection and production wells. The simulation + operates on a 2D grid with user-defined dimensions (nx, ny), uses flexible + time steps, and starts from a specified initial pressure condition. + Physical processes accounted for include fluid density changes with + pressure (modeling a slightly compressible fluid) while assuming constant + fluid viscosity." Created by following the course **AESM304A - Flow and Simulation of Subsurface processes** at @@ -60,7 +71,9 @@ class Simulator: wells : {ndarray, None}, default: None Nd array of shape ``(nwells, 3)``, indicating well locations (with cell indices) and pressure. If None, the default is used, which is + np.array([[0, 0, 180], [self.nx-1, self.ny-1, 120]]) + corresponding to a well in the first and in the last cell, with a pressure of 180 and 120, respectively. dx, dz : floats, default: 50.0, 10.0 diff --git a/tests/test_data_assimilation.py b/tests/test_data_assimilation.py new file mode 100644 index 0000000..4080568 --- /dev/null +++ b/tests/test_data_assimilation.py @@ -0,0 +1,5 @@ +from resmda import data_assimilation + + +def test_all_dir(): + assert set(data_assimilation.__all__) == set(dir(data_assimilation)) diff --git a/tests/test_reservoir_simulator.py b/tests/test_reservoir_simulator.py new file mode 100644 index 0000000..323645b --- /dev/null +++ b/tests/test_reservoir_simulator.py @@ -0,0 +1,5 @@ +from resmda import reservoir_simulator + + +def test_all_dir(): + assert set(reservoir_simulator.__all__) == set(dir(reservoir_simulator)) diff --git a/tests/test_utils.py b/tests/test_utils.py index dc5c3d0..84206fc 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,10 +1,90 @@ +import scooby import numpy as np +from numpy.testing import assert_allclose from resmda import utils +def test_gaussian_covariance(): + # Small length, no variance => eye + assert_allclose(utils.gaussian_covariance(4, 4, [0.1, 0.1], 0, 0), + np.eye(4*4)) + + # Small length, some variance => still eye + assert_allclose( + utils.gaussian_covariance( + nx=4, ny=4, length=[0.1, 0.1], theta=0, variance=1 + ), + np.eye(4*4) + ) + + # Simply check some values with variance + x = utils.gaussian_covariance(4, 4, [2, 1], 30, 2) + assert_allclose(np.diag(x), 1.) + assert_allclose(np.diag(x, -1)[3::4], 0) + for i in range(3): + assert_allclose(np.diag(x, -1)[i::4], 0.00024027168) + assert_allclose(np.diag(x, -4), 0.58600724) + for i in [2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]: + assert_allclose(np.diag(x, -i), 0) + + # Simply check some values without variance + x = utils.gaussian_covariance(4, 4, [1, 2], 0, 0) + assert_allclose(np.diag(x), 1.) + assert_allclose(np.diag(x, -1)[0], 0.20833333) + assert_allclose(np.diag(x, -3)[1], 0.13466999) + assert_allclose(np.diag(x, -4)[0], 0.6848958) + assert_allclose(np.diag(x, -5)[0], 0.13466999) + assert_allclose(np.diag(x, -7)[1], 0.030032475) + assert_allclose(np.diag(x, -8)[0], 0.20833333) + assert_allclose(np.diag(x, -9)[0], 0.030032475) + assert_allclose(np.diag(x, -11)[1], 0.0004445008) + assert_allclose(np.diag(x, -12)[0], 0.016493056) + assert_allclose(np.diag(x, -13)[0], 0.0004445008) + for i in [2, 6, 10, 14, 15]: + assert_allclose(np.diag(x, -i), 0) + + +def test_localization_matrix(): + # [[ 0, 0, 0, 0], + # [ 4, 5, 0, 0], + # [ 8, 9, 10, 0], + # [12, 13, 14, 15]] + tril = np.tril(np.arange(16).reshape(4, 4)) + full = tril + np.tril(tril, -1).T + triu = np.triu(full) + + data_positions = np.array([[1, 0]], dtype=int) + solution = np.array([[[4]], [[5]], [[9]], [[13]]]) + outtril = utils.localization_matrix(tril, data_positions, (4, 1)) + assert_allclose(solution, outtril) + outtriu = utils.localization_matrix(triu, data_positions, (4, 1), 'upper') + assert_allclose(solution, outtriu) + outfull = utils.localization_matrix(full, data_positions, (4, 1), 'full') + assert_allclose(solution, outfull) + + def test_random(): assert isinstance(utils.rng(), np.random.Generator) assert isinstance(utils.rng(11), np.random.Generator) rng = np.random.default_rng() assert rng == utils.rng(rng) + + +def test_Report(capsys): + out, _ = capsys.readouterr() # Empty capsys + + # Reporting is done by the external package scooby. + # We just ensure the shown packages do not change (core and optional). + out1 = utils.Report() + out2 = scooby.Report( + core=['numpy', 'scipy', 'numba', 'resmda'], + optional=['matplotlib', 'IPython'], + ncol=3) + + # Ensure they're the same; exclude time to avoid errors. + assert out1.__repr__()[115:] == out2.__repr__()[115:] + + +def test_all_dir(): + assert set(utils.__all__) == set(dir(utils))