Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add progressbar to prism forward gravity calculations #315

Merged
merged 15 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"matplotlib": ("https://matplotlib.org/", None),
"pyproj": ("https://pyproj4.github.io/pyproj/stable/", 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
Expand Down
1 change: 1 addition & 0 deletions env/requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ coverage
pyvista
vtk>=9
netcdf4
numba_progress
34 changes: 32 additions & 2 deletions harmonica/forward/prism.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
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


Expand All @@ -20,6 +26,7 @@ def prism_gravity(
field,
parallel=True,
dtype="float64",
progressbar=False,
disable_checks=False,
):
"""
Expand Down Expand Up @@ -73,6 +80,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 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
Expand Down Expand Up @@ -130,9 +141,23 @@ 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:
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
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
Expand Down Expand Up @@ -186,7 +211,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=None):
"""
Compute gravitational field of prisms on computations points

Expand All @@ -210,6 +235,8 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out):
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):
for m in range(prisms.shape[0]):
Expand All @@ -233,6 +260,9 @@ def jit_prism_gravity(coordinates, prisms, density, kernel, out):
shift_upward - coordinates[2][l],
)
)
# Update progress bar if called
if update_progressbar:
progress_proxy.update(1)


@jit(nopython=True)
Expand Down
5 changes: 4 additions & 1 deletion harmonica/forward/prism_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,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

Expand Down Expand Up @@ -358,6 +360,7 @@ def gravity(self, coordinates, field, density_name="density", **kwargs):
prisms=boundaries,
density=density,
field=field,
progressbar=progressbar,
**kwargs,
)

Expand Down
54 changes: 54 additions & 0 deletions harmonica/tests/test_prism.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -381,3 +388,50 @@ def test_prisms_parallel_vs_serial():
coordinates, prisms, densities, field=field, parallel=False
)
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():
mdtanker marked this conversation as resolved.
Show resolved Hide resolved
"""
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("harmonica.forward.prism.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
)
43 changes: 43 additions & 0 deletions harmonica/tests/test_prism_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Test prisms layer
"""
import warnings
from unittest.mock import patch

import numpy as np
import numpy.testing as npt
Expand All @@ -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):
Expand Down Expand Up @@ -422,3 +428,40 @@ 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.skipif(ProgressBar is None, reason="requires numba_progress")
@pytest.mark.use_numba
def test_progress_bar(dummy_layer):
mdtanker marked this conversation as resolved.
Show resolved Hide resolved
"""
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("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
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)