From 4a19002cbd7af83f28bb94fb2a53910bf7c02189 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Tue, 24 May 2022 13:23:06 +1200 Subject: [PATCH 01/10] added progress bar for forward gravity calculations --- harmonica/forward/prism.py | 29 +++++++++++++++++++++++++++-- harmonica/forward/prism_layer.py | 6 +++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index baac487dc..5fe1fd7fc 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -20,6 +20,7 @@ def prism_gravity( field, parallel=True, dtype="float64", + progressbar=False, disable_checks=False, ): """ @@ -73,6 +74,10 @@ def prism_gravity( dtype : data-type (optional) Data type assigned to the resulting gravitational field. Default to ``np.float64``. + progressbar : bool (optional) + If True a progress bar for the number of gravity coordinates to solve + in the forward calculation will be displayed using the numba-progress + package. Default to ``False``. disable_checks : bool (optional) Flag that controls whether to perform a sanity check on the model. Should be set to ``True`` only when it is certain that the input model @@ -130,9 +135,26 @@ def prism_gravity( + "mismatch the number of prisms ({})".format(prisms.shape[0]) ) _check_prisms(prisms) + # Show progress bar for 'jit_prism_gravity' function + if progressbar: + try: + from numba_progress import ProgressBar + + progress_proxy = ProgressBar(total=coordinates[0].size) + except ImportError: + raise ImportError( + "Missing optional dependency 'numba_progress' required if progressbar=True" + ) + else: + progress_proxy = None # Compute gravitational field - dispatcher(parallel)(coordinates, prisms, density, kernels[field], result) + dispatcher(parallel)( + coordinates, prisms, density, kernels[field], result, progress_proxy + ) result *= GRAVITATIONAL_CONST + # Close previously created progress bars + if progressbar: + progress_proxy.close() # Convert to more convenient units if field == "g_z": result *= 1e5 # SI to mGal @@ -186,7 +208,7 @@ def _check_prisms(prisms): raise ValueError(err_msg) -def jit_prism_gravity(coordinates, prisms, density, kernel, out): +def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy): """ Compute gravitational field of prisms on computations points @@ -212,6 +234,9 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out): """ # Iterate over computation points and prisms for l in prange(coordinates[0].size): + # Update progress bar if called + if progress_proxy is not None: + progress_proxy.update(1) for m in range(prisms.shape[0]): # Iterate over the prism boundaries to compute the result of the # integration (see Nagy et al., 2000) diff --git a/harmonica/forward/prism_layer.py b/harmonica/forward/prism_layer.py index a23a21cb1..a6109e7a7 100644 --- a/harmonica/forward/prism_layer.py +++ b/harmonica/forward/prism_layer.py @@ -22,6 +22,7 @@ def prism_layer( surface, reference, properties=None, + progressbar=False, ): """ Create a layer of prisms of equal size @@ -305,7 +306,9 @@ def update_top_bottom(self, surface, reference): self._obj.coords["top"] = (self.dims, top) self._obj.coords["bottom"] = (self.dims, bottom) - def gravity(self, coordinates, field, density_name="density", **kwargs): + def gravity( + self, coordinates, field, progressbar=False, density_name="density", **kwargs + ): """ Computes the gravity generated by the layer of prisms @@ -358,6 +361,7 @@ def gravity(self, coordinates, field, density_name="density", **kwargs): prisms=boundaries, density=density, field=field, + progressbar=progressbar, **kwargs, ) From ed938834191ccd2e921b507b2f7d62b3159f76fd Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Mon, 30 May 2022 08:31:03 +1200 Subject: [PATCH 02/10] Update harmonica/forward/prism.py Co-authored-by: Santiago Soler --- harmonica/forward/prism.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index 5fe1fd7fc..2ed1319db 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -75,9 +75,9 @@ def prism_gravity( Data type assigned to the resulting gravitational field. Default to ``np.float64``. progressbar : bool (optional) - If True a progress bar for the number of gravity coordinates to solve - in the forward calculation will be displayed using the numba-progress - package. Default to ``False``. + If True, a progress bar of the computation will be printed to standard error (stderr). + Requires :mod:`numba_progress` to be installed. + Default to ``False``. disable_checks : bool (optional) Flag that controls whether to perform a sanity check on the model. Should be set to ``True`` only when it is certain that the input model From 0481a3f4704c821ba6aadf5f676f052c467c9046 Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Mon, 30 May 2022 08:39:40 +1200 Subject: [PATCH 03/10] Update harmonica/forward/prism_layer.py removed progressbar=False from prism_layer.py Co-authored-by: Santiago Soler --- harmonica/forward/prism_layer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/harmonica/forward/prism_layer.py b/harmonica/forward/prism_layer.py index a6109e7a7..1f3359b0c 100644 --- a/harmonica/forward/prism_layer.py +++ b/harmonica/forward/prism_layer.py @@ -22,7 +22,6 @@ def prism_layer( surface, reference, properties=None, - progressbar=False, ): """ Create a layer of prisms of equal size From 14b754ff0d5e51c8c99275c60c031bcfe84bada5 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Mon, 30 May 2022 10:58:05 +1200 Subject: [PATCH 04/10] added suggestions from santi --- harmonica/forward/prism.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index 2ed1319db..b7900852a 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -75,8 +75,8 @@ def prism_gravity( Data type assigned to the resulting gravitational field. Default to ``np.float64``. progressbar : bool (optional) - If True, a progress bar of the computation will be printed to standard error (stderr). - Requires :mod:`numba_progress` to be installed. + If True, a progress bar of the computation will be printed to standard + error (stderr). Requires :mod:`numba_progress` to be installed. Default to ``False``. disable_checks : bool (optional) Flag that controls whether to perform a sanity check on the model. @@ -135,16 +135,19 @@ def prism_gravity( + "mismatch the number of prisms ({})".format(prisms.shape[0]) ) _check_prisms(prisms) + # Attempt to import numba_progress + try: + from numba_progress import ProgressBar + except ImportError: + ProgressBar = None # Show progress bar for 'jit_prism_gravity' function if progressbar: - try: - from numba_progress import ProgressBar - - progress_proxy = ProgressBar(total=coordinates[0].size) - except ImportError: + if ProgressBar is None: raise ImportError( "Missing optional dependency 'numba_progress' required if progressbar=True" ) + progress_proxy = ProgressBar(total=coordinates[0].size) + else: progress_proxy = None # Compute gravitational field @@ -208,7 +211,7 @@ def _check_prisms(prisms): raise ValueError(err_msg) -def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy): +def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy=None): """ Compute gravitational field of prisms on computations points @@ -232,11 +235,10 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy) Array where the resulting field values will be stored. Must have the same size as the arrays contained on ``coordinates``. """ + # Check if we need to update the progressbar on each iteration + update_progressbar = progress_proxy is not None # Iterate over computation points and prisms for l in prange(coordinates[0].size): - # Update progress bar if called - if progress_proxy is not None: - progress_proxy.update(1) for m in range(prisms.shape[0]): # Iterate over the prism boundaries to compute the result of the # integration (see Nagy et al., 2000) @@ -258,7 +260,10 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy) shift_upward - coordinates[2][l], ) ) - + # Update progress bar if called + # if progress_proxy is not None: + if update_progressbar: + progress_proxy.update(1) @jit(nopython=True) def kernel_potential(easting, northing, upward): From 5fcfe5f0859c73868fae3fa2e10ea1ec9c493c35 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Mon, 30 May 2022 11:11:09 +1200 Subject: [PATCH 05/10] added numba_progress URL and removed comment --- doc/conf.py | 1 + harmonica/forward/prism.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 47a82c92a..50204714e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -51,6 +51,7 @@ "boule": ("https://www.fatiando.org/boule/latest/", None), "matplotlib": ("https://matplotlib.org/", None), "pyvista": ("https://docs.pyvista.org", None), + "numba_progress": ("https://pypi.org/project/numba-progress/", None), } # Autosummary pages will be generated by sphinx-autogen instead of sphinx-build diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index b7900852a..cb7905d6f 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -261,7 +261,6 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy= ) ) # Update progress bar if called - # if progress_proxy is not None: if update_progressbar: progress_proxy.update(1) From 5db2ddd9536608b668dd190af8e0e25dc561b3d9 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Thu, 9 Jun 2022 13:27:58 +1200 Subject: [PATCH 06/10] added tests for progressbar --- harmonica/forward/prism.py | 3 +- harmonica/tests/test_prism.py | 53 +++++++++++++++++++++++++++++ harmonica/tests/test_prism_layer.py | 40 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index cb7905d6f..b116c1d20 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -147,7 +147,7 @@ def prism_gravity( "Missing optional dependency 'numba_progress' required if progressbar=True" ) progress_proxy = ProgressBar(total=coordinates[0].size) - + else: progress_proxy = None # Compute gravitational field @@ -264,6 +264,7 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out, progress_proxy= if update_progressbar: progress_proxy.update(1) + @jit(nopython=True) def kernel_potential(easting, northing, upward): """ diff --git a/harmonica/tests/test_prism.py b/harmonica/tests/test_prism.py index 325072f38..9c4b4f6ce 100644 --- a/harmonica/tests/test_prism.py +++ b/harmonica/tests/test_prism.py @@ -7,11 +7,18 @@ """ Test forward modelling for prisms. """ +from unittest.mock import patch + import numpy as np import numpy.testing as npt import pytest import verde as vd +try: + from numba_progress import ProgressBar +except ImportError: + ProgressBar = None + from ..forward.prism import _check_prisms, prism_gravity, safe_atan2, safe_log from ..gravity_corrections import bouguer_correction from .utils import run_only_with_numba @@ -381,3 +388,49 @@ def test_prisms_parallel_vs_serial(): coordinates, prisms, densities, field=field, parallel=False ) npt.assert_allclose(result_parallel, result_serial) + + +@pytest.mark.use_numba +def test_progress_bar(): + """ + Check if forward gravity results with and without progress bar match + """ + prisms = [ + [-100, 0, -100, 0, -10, 0], + [0, 100, -100, 0, -10, 0], + [-100, 0, 0, 100, -10, 0], + [0, 100, 0, 100, -10, 0], + ] + densities = [2000, 3000, 4000, 5000] + coordinates = vd.grid_coordinates( + region=(-100, 100, -100, 100), spacing=20, extra_coords=10 + ) + for field in ("potential", "g_z"): + result_progress_true = prism_gravity( + coordinates, prisms, densities, field=field, progressbar=True + ) + result_progress_false = prism_gravity( + coordinates, prisms, densities, field=field, progressbar=False + ) + npt.assert_allclose(result_progress_true, result_progress_false) + + +@patch("numba_progress.ProgressBar", None) +def test_numba_progress_missing_error(): + """ + Check if error is raised when progresbar=True and numba_progress package + is not installed. + """ + prisms = [ + [-100, 0, -100, 0, -10, 0], + [0, 100, -100, 0, -10, 0], + [-100, 0, 0, 100, -10, 0], + [0, 100, 0, 100, -10, 0], + ] + densities = [2000, 3000, 4000, 5000] + coordinates = [0, 0, 0] + # Check if error is raised + with pytest.raises(ImportError): + prism_gravity( + coordinates, prisms, densities, field="potential", progressbar=True + ) diff --git a/harmonica/tests/test_prism_layer.py b/harmonica/tests/test_prism_layer.py index ddcbe57c4..5927724fb 100644 --- a/harmonica/tests/test_prism_layer.py +++ b/harmonica/tests/test_prism_layer.py @@ -8,6 +8,7 @@ Test prisms layer """ import warnings +from unittest.mock import patch import numpy as np import numpy.testing as npt @@ -22,6 +23,11 @@ except ImportError: pyvista = None +try: + from numba_progress import ProgressBar +except ImportError: + ProgressBar = None + @pytest.fixture(params=("numpy", "xarray")) def dummy_layer(request): @@ -422,3 +428,37 @@ def test_to_pyvista(dummy_layer, properties): assert pv_grid.array_names == ["density"] assert pv_grid.get_array("density").ndim == 1 npt.assert_allclose(pv_grid.get_array("density"), layer.density.values.ravel()) + + +@pytest.mark.use_numba +def test_progress_bar(dummy_layer): + """ + Check if forward gravity results with and without progress bar match + """ + coordinates = vd.grid_coordinates((1, 3, 7, 10), spacing=1, extra_coords=30.0) + (easting, northing), surface, reference, density = dummy_layer + layer = prism_layer( + (easting, northing), surface, reference, properties={"density": density} + ) + result_progress_true = layer.prism_layer.gravity(coordinates, field='g_z', progressbar=True + ) + + result_progress_false = layer.prism_layer.gravity(coordinates, field='g_z', progressbar=False + ) + npt.assert_allclose(result_progress_true, result_progress_false) + + +@patch("numba_progress.ProgressBar", None) +def test_numba_progress_missing_error(dummy_layer): + """ + Check if error is raised when progressbar=True and numba_progress package + is not installed. + """ + coordinates = vd.grid_coordinates((1, 3, 7, 10), spacing=1, extra_coords=30.0) + (easting, northing), surface, reference, density = dummy_layer + layer = prism_layer( + (easting, northing), surface, reference, properties={"density": density} + ) + # Check if error is raised + with pytest.raises(ImportError): + layer.prism_layer.gravity(coordinates, field="g_z", progressbar=True) From c256a5f6a44332d8e87be3cfcd13653044d729d9 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Fri, 10 Jun 2022 18:02:02 +1200 Subject: [PATCH 07/10] added mark.skipif decorators --- env/requirements-tests.txt | 1 + harmonica/forward/prism.py | 6 ++---- harmonica/tests/test_prism.py | 3 ++- harmonica/tests/test_prism_layer.py | 11 +++++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/env/requirements-tests.txt b/env/requirements-tests.txt index 2a8058e81..ed9de3da8 100644 --- a/env/requirements-tests.txt +++ b/env/requirements-tests.txt @@ -6,3 +6,4 @@ coverage pyvista vtk>=9 netcdf4 +numba_progress \ No newline at end of file diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index b116c1d20..51f0a50be 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -9,10 +9,9 @@ """ import numpy as np from numba import jit, prange - + from ..constants import GRAVITATIONAL_CONST - def prism_gravity( coordinates, prisms, @@ -139,7 +138,7 @@ def prism_gravity( try: from numba_progress import ProgressBar except ImportError: - ProgressBar = None + ProgressBar = None # Show progress bar for 'jit_prism_gravity' function if progressbar: if ProgressBar is None: @@ -147,7 +146,6 @@ def prism_gravity( "Missing optional dependency 'numba_progress' required if progressbar=True" ) progress_proxy = ProgressBar(total=coordinates[0].size) - else: progress_proxy = None # Compute gravitational field diff --git a/harmonica/tests/test_prism.py b/harmonica/tests/test_prism.py index 9c4b4f6ce..15758995b 100644 --- a/harmonica/tests/test_prism.py +++ b/harmonica/tests/test_prism.py @@ -390,6 +390,7 @@ def test_prisms_parallel_vs_serial(): npt.assert_allclose(result_parallel, result_serial) +@pytest.mark.skipif(ProgressBar is None, reason="requires numba_progress") @pytest.mark.use_numba def test_progress_bar(): """ @@ -418,7 +419,7 @@ def test_progress_bar(): @patch("numba_progress.ProgressBar", None) def test_numba_progress_missing_error(): """ - Check if error is raised when progresbar=True and numba_progress package + Check if error is raised when progresbar=True and numba_progress package is not installed. """ prisms = [ diff --git a/harmonica/tests/test_prism_layer.py b/harmonica/tests/test_prism_layer.py index 5927724fb..03ba456a7 100644 --- a/harmonica/tests/test_prism_layer.py +++ b/harmonica/tests/test_prism_layer.py @@ -430,6 +430,7 @@ def test_to_pyvista(dummy_layer, properties): npt.assert_allclose(pv_grid.get_array("density"), layer.density.values.ravel()) +@pytest.mark.skipif(ProgressBar is None, reason="requires numba_progress") @pytest.mark.use_numba def test_progress_bar(dummy_layer): """ @@ -440,10 +441,12 @@ def test_progress_bar(dummy_layer): layer = prism_layer( (easting, northing), surface, reference, properties={"density": density} ) - result_progress_true = layer.prism_layer.gravity(coordinates, field='g_z', progressbar=True + result_progress_true = layer.prism_layer.gravity( + coordinates, field="g_z", progressbar=True ) - - result_progress_false = layer.prism_layer.gravity(coordinates, field='g_z', progressbar=False + + result_progress_false = layer.prism_layer.gravity( + coordinates, field="g_z", progressbar=False ) npt.assert_allclose(result_progress_true, result_progress_false) @@ -451,7 +454,7 @@ def test_progress_bar(dummy_layer): @patch("numba_progress.ProgressBar", None) def test_numba_progress_missing_error(dummy_layer): """ - Check if error is raised when progressbar=True and numba_progress package + Check if error is raised when progressbar=True and numba_progress package is not installed. """ coordinates = vd.grid_coordinates((1, 3, 7, 10), spacing=1, extra_coords=30.0) From 0226c2c8f64aa011464a02c2f36de56533faea71 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Mon, 13 Jun 2022 12:04:06 +1200 Subject: [PATCH 08/10] fixed test issues --- harmonica/forward/prism.py | 14 ++++++++------ harmonica/tests/test_prism.py | 2 +- harmonica/tests/test_prism_layer.py | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/harmonica/forward/prism.py b/harmonica/forward/prism.py index 51f0a50be..4d22c29c9 100644 --- a/harmonica/forward/prism.py +++ b/harmonica/forward/prism.py @@ -9,9 +9,16 @@ """ import numpy as np from numba import jit, prange - + +# Attempt to import numba_progress +try: + from numba_progress import ProgressBar +except ImportError: + ProgressBar = None + from ..constants import GRAVITATIONAL_CONST + def prism_gravity( coordinates, prisms, @@ -134,11 +141,6 @@ def prism_gravity( + "mismatch the number of prisms ({})".format(prisms.shape[0]) ) _check_prisms(prisms) - # Attempt to import numba_progress - try: - from numba_progress import ProgressBar - except ImportError: - ProgressBar = None # Show progress bar for 'jit_prism_gravity' function if progressbar: if ProgressBar is None: diff --git a/harmonica/tests/test_prism.py b/harmonica/tests/test_prism.py index 15758995b..d3c5ad58d 100644 --- a/harmonica/tests/test_prism.py +++ b/harmonica/tests/test_prism.py @@ -416,7 +416,7 @@ def test_progress_bar(): npt.assert_allclose(result_progress_true, result_progress_false) -@patch("numba_progress.ProgressBar", None) +@patch("harmonica.forward.prism.ProgressBar", None) def test_numba_progress_missing_error(): """ Check if error is raised when progresbar=True and numba_progress package diff --git a/harmonica/tests/test_prism_layer.py b/harmonica/tests/test_prism_layer.py index 03ba456a7..634e5630d 100644 --- a/harmonica/tests/test_prism_layer.py +++ b/harmonica/tests/test_prism_layer.py @@ -451,7 +451,7 @@ def test_progress_bar(dummy_layer): npt.assert_allclose(result_progress_true, result_progress_false) -@patch("numba_progress.ProgressBar", None) +@patch("harmonica.forward.prism.ProgressBar", None) def test_numba_progress_missing_error(dummy_layer): """ Check if error is raised when progressbar=True and numba_progress package From 0a0fdf41e68c0b4780c2aa7fc8a3720f517a4ef9 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 14 Jun 2022 11:59:53 -0300 Subject: [PATCH 09/10] Add numba_progress to environment.yml --- environment.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/environment.yml b/environment.yml index a28daf01a..fbaab3449 100644 --- a/environment.yml +++ b/environment.yml @@ -56,3 +56,6 @@ dependencies: # Install flake8-unused-arguments through pip # (not available through conda yet) - flake8-unused-arguments==0.0.9 + # Install numba_progress through pip + # (not available through conda yet) + - numba_progress From 2da5b89c1bbd0e052a4f93a343781e617149f305 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 14 Jun 2022 12:00:22 -0300 Subject: [PATCH 10/10] Include numba_progress in optional dependency in docs --- doc/install.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/install.rst b/doc/install.rst index 1cb5f442d..ece3201a2 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -40,6 +40,9 @@ Optional: * `pyvista `__ and `vtk `__ (>= 9): for 3D visualizations. See :func:`harmonica.prism_to_pyvista`. +* `numba_progress `__ for + printing a progress bar on some forward modelling computations. + See :func:`harmonica.prism_gravity`. The examples in the :ref:`gallery` also use: