From fa20bafe8408a3847efd855df6b5da3fd40a9c2a Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Fri, 29 Mar 2024 15:49:13 -0300 Subject: [PATCH 01/20] Add tilt transformation * added `tilt` function to `_transformations.py` * added it to the `index.rst` * created an example in `tilt.py` * added it to `__init__.py` * added the class `TestTilt` in `test_transformations.py`; however, the test is failling. --- doc/api/index.rst | 1 + examples/transformations/tilt.py | 123 ++++++++++++++++++++++++ harmonica/__init__.py | 1 + harmonica/_transformations.py | 63 +++++++++++- harmonica/tests/test_transformations.py | 70 ++++++++++++++ 5 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 examples/transformations/tilt.py diff --git a/doc/api/index.rst b/doc/api/index.rst index 2b85813b0..d4341b1c8 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -33,6 +33,7 @@ Apply well known transformations regular gridded potential fields data. gaussian_highpass reduction_to_pole total_gradient_amplitude + tilt Frequency domain filters ------------------------ diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py new file mode 100644 index 000000000..a0f638779 --- /dev/null +++ b/examples/transformations/tilt.py @@ -0,0 +1,123 @@ +# Copyright (c) 2018 The Harmonica Developers. +# Distributed under the terms of the BSD 3-Clause License. +# SPDX-License-Identifier: BSD-3-Clause +# +# This code is part of the Fatiando a Terra project (https://www.fatiando.org) +# +""" +Tilt of a regular grid +========================================== +""" +import ensaio +import pygmt +import verde as vd +import xarray as xr +import xrft + +import harmonica as hm + +# Fetch magnetic grid over the Lightning Creek Sill Complex, Australia using +# Ensaio and load it with Xarray +fname = ensaio.fetch_lightning_creek_magnetic(version=1) +magnetic_grid = xr.load_dataarray(fname) + +# Pad the grid to increase accuracy of the FFT filter +pad_width = { + "easting": magnetic_grid.easting.size // 3, + "northing": magnetic_grid.northing.size // 3, +} +# drop the extra height coordinate +magnetic_grid_no_height = magnetic_grid.drop_vars("height") +magnetic_grid_padded = xrft.pad(magnetic_grid_no_height, pad_width) + +# Compute the tilt of the grid +tilt_grid = hm.tilt(magnetic_grid_padded) + +# Unpad the tilt grid +tilt_grid = xrft.unpad(tilt_grid, pad_width) + +# # Show the tilt +# print("\nTilt:\n", tilt_grid) + +# Define the inclination and declination of the region by the time of the data +# acquisition (1990). +inclination, declination = -52.98, 6.51 + +# Apply a reduction to the pole over the magnetic anomaly grid. We will assume +# that the sources share the same inclination and declination as the +# geomagnetic field. +rtp_grid_padded = hm.reduction_to_pole( + magnetic_grid_padded, inclination=inclination, declination=declination +) + +# Unpad the reduced to the pole grid +rtp_grid = xrft.unpad(rtp_grid_padded, pad_width) + +# Compute the tilt of the padded rtp grid +tilt_rtp_grid = hm.tilt(rtp_grid_padded) + +# Unpad the tilt grid +tilt_rtp_grid = xrft.unpad(tilt_rtp_grid, pad_width) + +# # Show the tilt from RTP +# print("\nTilt from RTP:\n", tilt_rtp_grid) + +# Plot original magnetic anomaly, its RTP, and the tilt of both +fig = pygmt.Figure() +with fig.subplot(nrows=2, ncols=2, figsize=("28c", "30c"), sharey="l"): + scale = 0.5 * vd.maxabs(magnetic_grid, rtp_grid) + with fig.set_panel(panel=0): + # Make colormap of data + pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) + # Plot magnetic anomaly grid + fig.grdimage( + grid=magnetic_grid, + projection="X?", + cmap=True, + # frame=["a", "+tMagnetic anomaly intensity (TMI)"], + ) + with fig.set_panel(panel=1): + # Make colormap of data + pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) + # Plot reduced to the pole magnetic anomaly grid + fig.grdimage( + grid=rtp_grid, + projection="X?", + cmap=True, + # frame=["a", "+tTMI reduced to the pole (RTP)"], + ) + # Add colorbar + fig.colorbar( + frame='af+l"Magnetic anomaly intensity [nT]"', + position="JMR+o1/1c+e", + ) + scale = 0.6 * vd.maxabs(tilt_grid, tilt_rtp_grid) + with fig.set_panel(panel=2): + # Make colormap for tilt (saturate it a little bit) + pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) + # Plot tilt + fig.grdimage( + grid=tilt_grid, + projection="X?", + cmap=True, + # frame=["a", "+tTilt from TMI"], + ) + # Add colorbar + with fig.set_panel(panel=3): + # Make colormap for tilt rtp (saturate it a little bit) + scale = 0.6 * vd.maxabs(tilt_rtp_grid) + pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) + # Plot tilt + fig.grdimage( + grid=tilt_rtp_grid, + projection="X?", + cmap=True, + # frame=["a", "+tTilt from RTP"], + ) + # Add colorbar + fig.colorbar( + frame='af+l"Tilt [rad]"', + position="JMR+o1/1c+e", + ) +fig.show() +a = 2 \ No newline at end of file diff --git a/harmonica/__init__.py b/harmonica/__init__.py index 73132f7de..bdcff0f6c 100644 --- a/harmonica/__init__.py +++ b/harmonica/__init__.py @@ -27,6 +27,7 @@ gaussian_highpass, gaussian_lowpass, reduction_to_pole, + tilt, total_gradient_amplitude, upward_continuation, ) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 3957e73ad..a8ab2aa50 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -343,7 +343,7 @@ def reduction_to_pole( def total_gradient_amplitude(grid): r""" - Calculate the total gradient amplitude a magnetic field grid + Calculates the total gradient amplitude of a potential field grid Compute the total gradient amplitude of a regular gridded potential field `M`. The horizontal derivatives are calculated though finite-differences @@ -392,6 +392,67 @@ def total_gradient_amplitude(grid): # return the total gradient amplitude return np.sqrt(gradient[0] ** 2 + gradient[1] ** 2 + gradient[2] ** 2) +def tilt(grid): + r""" + Calculates the tilt of a potential field grid as defined by Miller and Singh (1994) + + Compute the tilt of a regular gridded potential field + `M`. The horizontal derivatives are calculated though finite-differences + while the upward derivative is calculated using FFT. + + Parameters + ---------- + grid : :class:`xarray.DataArray` + A two dimensional :class:`xarray.DataArray` whose coordinates are + evenly spaced (regular grid). Its dimensions should be in the following + order: *northing*, *easting*. Its coordinates should be defined in the + same units. + + Returns + ------- + tilt_grid : :class:`xarray.DataArray` + A :class:`xarray.DataArray` after calculating the + tilt of the passed ``grid``. + + Notes + ----- + The tilt is calculated as: + + .. math:: + + tilt(f) = tan^{-1}\left( + \frac{ + \frac{\partial M}{\partial z}}{ + \sqrt{\frac{\partial M}{\partial x}^2 + + \frac{\partial M}{\partial y}^2}} + \right) + + where :math:`M` is the regularly gridded potential field. + + When used on magnetic total field anomaly data, works best if the data is + reduced to the pole. + + It's useful to plot the zero contour line of the tilt to represent possible + outlines of the source bodies. Use matplotlib's ``pyplot.contour`` or + ``pyplot.tricontour`` for this. + + References + ---------- + [Blakely1995]_ + """ + # Run sanity checks on the grid + grid_sanity_checks(grid) + # Calculate the gradients of the grid + gradient = ( + derivative_easting(grid, order=1), + derivative_northing(grid, order=1), + derivative_upward(grid, order=1), + ) + # Calculate and return the tilt + horiz_deriv = np.sqrt(gradient[0]**2 + gradient[1]**2) + tilt = np.arctan2(gradient[2], horiz_deriv) + return tilt + def _get_dataarray_coordinate(grid, dimension_index): """ diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index d93b365bb..5cb110c94 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -25,6 +25,7 @@ gaussian_highpass, gaussian_lowpass, reduction_to_pole, + tilt, total_gradient_amplitude, upward_continuation, ) @@ -591,6 +592,75 @@ def test_invalid_grid_with_nans(self, sample_potential): total_gradient_amplitude(sample_potential) +class TestTilt: + """ + Test tilt function + """ + + def test_against_synthetic( + self, sample_potential, sample_g_n, sample_g_e, sample_g_z + ): + """ + Test tilt function against the synthetic model + """ + # Pad the potential field grid to improve accuracy + pad_width = { + "easting": sample_potential.easting.size // 3, + "northing": sample_potential.northing.size // 3, + } + # need to drop upward coordinate (bug in xrft) + potential_padded = xrft.pad( + sample_potential.drop_vars("upward"), + pad_width=pad_width, + ) + # Calculate the tilt and unpad it + tilt_grid = tilt(potential_padded) + tilt_grid = xrft.unpad(tilt_grid, pad_width) + # Compare against g_tilt (trim the borders to ignore boundary effects) + trim = 6 + tilt_grid = tilt_grid[trim:-trim, trim:-trim] + g_e = sample_g_e[trim:-trim, trim:-trim] * 1e-5 # convert to SI units + g_n = sample_g_n[trim:-trim, trim:-trim] * 1e-5 # convert to SI units + g_z = sample_g_z[trim:-trim, trim:-trim] * 1e-5 # convert to SI units + g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) + g_tilt = np.arctan2(g_z, g_horiz_deriv) + rms = root_mean_square_error(tilt_grid, g_tilt) + assert rms / np.abs(tilt_grid).max() < 0.1 + + def test_invalid_grid_single_dimension(self): + """ + Check if tilt raises error on grid with single + dimension + """ + x = np.linspace(0, 10, 11) + y = x**2 + grid = xr.DataArray(y, coords={"x": x}, dims=("x",)) + with pytest.raises(ValueError, match="Invalid grid with 1 dimensions."): + tilt(grid) + + def test_invalid_grid_three_dimensions(self): + """ + Check if tilt raises error on grid with three + dimensions + """ + x = np.linspace(0, 10, 11) + y = np.linspace(-4, 4, 9) + z = np.linspace(20, 30, 5) + xx, yy, zz = np.meshgrid(x, y, z) + data = xx + yy + zz + grid = xr.DataArray(data, coords={"x": x, "y": y, "z": z}, dims=("y", "x", "z")) + with pytest.raises(ValueError, match="Invalid grid with 3 dimensions."): + tilt(grid) + + def test_invalid_grid_with_nans(self, sample_potential): + """ + Check if tilt raises error if grid contains nans + """ + sample_potential.values[0, 0] = np.nan + with pytest.raises(ValueError, match="Found nan"): + tilt(sample_potential) + + class Testfilter: """ Test filter result against the output from oasis montaj From cca42cf61adebe2bd562d2dd5a48943edfeca982 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Fri, 29 Mar 2024 16:15:17 -0300 Subject: [PATCH 02/20] corrected format --- examples/transformations/tilt.py | 7 +++---- harmonica/_transformations.py | 9 +++++---- harmonica/tests/test_transformations.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py index a0f638779..2f4ba2615 100644 --- a/examples/transformations/tilt.py +++ b/examples/transformations/tilt.py @@ -6,7 +6,7 @@ # """ Tilt of a regular grid -========================================== +====================== """ import ensaio import pygmt @@ -97,8 +97,8 @@ pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) # Plot tilt fig.grdimage( - grid=tilt_grid, - projection="X?", + grid=tilt_grid, + projection="X?", cmap=True, # frame=["a", "+tTilt from TMI"], ) @@ -120,4 +120,3 @@ position="JMR+o1/1c+e", ) fig.show() -a = 2 \ No newline at end of file diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index a8ab2aa50..dc4291a0e 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -392,10 +392,11 @@ def total_gradient_amplitude(grid): # return the total gradient amplitude return np.sqrt(gradient[0] ** 2 + gradient[1] ** 2 + gradient[2] ** 2) + def tilt(grid): r""" Calculates the tilt of a potential field grid as defined by Miller and Singh (1994) - + Compute the tilt of a regular gridded potential field `M`. The horizontal derivatives are calculated though finite-differences while the upward derivative is calculated using FFT. @@ -448,9 +449,9 @@ def tilt(grid): derivative_northing(grid, order=1), derivative_upward(grid, order=1), ) - # Calculate and return the tilt - horiz_deriv = np.sqrt(gradient[0]**2 + gradient[1]**2) - tilt = np.arctan2(gradient[2], horiz_deriv) + # Calculate and return the tilt + horiz_deriv = np.sqrt(gradient[0] ** 2 + gradient[1] ** 2) + tilt = np.arctan2(gradient[2], horiz_deriv) return tilt diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index 5cb110c94..5e6d77206 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -623,7 +623,7 @@ def test_against_synthetic( g_n = sample_g_n[trim:-trim, trim:-trim] * 1e-5 # convert to SI units g_z = sample_g_z[trim:-trim, trim:-trim] * 1e-5 # convert to SI units g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) - g_tilt = np.arctan2(g_z, g_horiz_deriv) + g_tilt = np.arctan2(g_z, g_horiz_deriv) rms = root_mean_square_error(tilt_grid, g_tilt) assert rms / np.abs(tilt_grid).max() < 0.1 From 11bcd703fd92d0de7f6449d185737f3430e1af45 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Fri, 29 Mar 2024 16:27:19 -0300 Subject: [PATCH 03/20] fixed formatting in `_transformations.py`. --- harmonica/_transformations.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index dc4291a0e..6de111f3c 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -395,7 +395,8 @@ def total_gradient_amplitude(grid): def tilt(grid): r""" - Calculates the tilt of a potential field grid as defined by Miller and Singh (1994) + Calculates the tilt of a potential field grid as defined + by Miller and Singh (1994) Compute the tilt of a regular gridded potential field `M`. The horizontal derivatives are calculated though finite-differences From f531eb50902325906f27def30bbf0479e36b1bae Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Thu, 4 Apr 2024 20:06:58 -0300 Subject: [PATCH 04/20] changed g_z signal in the test of tilt For some reason that I don't know the test of tilt passes when you change the sinal of g_z in `TestTilt:test_against_synthetic` in `test_transformations.py` --- harmonica/tests/test_transformations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index 5e6d77206..a14b951a8 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -623,7 +623,7 @@ def test_against_synthetic( g_n = sample_g_n[trim:-trim, trim:-trim] * 1e-5 # convert to SI units g_z = sample_g_z[trim:-trim, trim:-trim] * 1e-5 # convert to SI units g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) - g_tilt = np.arctan2(g_z, g_horiz_deriv) + g_tilt = np.arctan2(-g_z, g_horiz_deriv) rms = root_mean_square_error(tilt_grid, g_tilt) assert rms / np.abs(tilt_grid).max() < 0.1 From 1bc80af6593ee7002c97d7c29240760fbdaaa936 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 15 Apr 2024 17:42:28 -0300 Subject: [PATCH 05/20] Update harmonica/_transformations.py Co-authored-by: Leonardo Uieda --- harmonica/_transformations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 6de111f3c..4b939a415 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -435,8 +435,8 @@ def tilt(grid): reduced to the pole. It's useful to plot the zero contour line of the tilt to represent possible - outlines of the source bodies. Use matplotlib's ``pyplot.contour`` or - ``pyplot.tricontour`` for this. + outlines of the source bodies. Use :func:`matplotlib.pyplot.contour` or + :func:`matplotlib.pyplot.tricontour` for this. References ---------- From b4c687d112c8c16a2110dfeef664aeeb6bd11da5 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 15 Apr 2024 18:55:24 -0300 Subject: [PATCH 06/20] make doc still seems failing * changed from `tilt` to `tilt_angle` * added Miller and Sing 1994 reference --- doc/api/index.rst | 2 +- doc/references.rst | 1 + examples/transformations/tilt.py | 4 ++-- harmonica/__init__.py | 2 +- harmonica/_transformations.py | 25 +++++++++++++------------ harmonica/tests/test_transformations.py | 10 +++++----- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/doc/api/index.rst b/doc/api/index.rst index d4341b1c8..75e38ce9d 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -33,7 +33,7 @@ Apply well known transformations regular gridded potential fields data. gaussian_highpass reduction_to_pole total_gradient_amplitude - tilt + tilt_angle Frequency domain filters ------------------------ diff --git a/doc/references.rst b/doc/references.rst index 25dc76f17..b4a93c42c 100644 --- a/doc/references.rst +++ b/doc/references.rst @@ -11,6 +11,7 @@ References .. [Grombein2013] Grombein, T., Seitz, K., Heck, B. (2013), Optimized formulas for the gravitational field of a tesseroid, Journal of Geodesy. doi:`10.1007/s00190-013-0636-1 `__ .. [Hofmann-WellenhofMoritz2006] Hofmann-Wellenhof, B., & Moritz, H. (2006). Physical Geodesy (2nd, corr. ed. 2006 edition ed.). Wien ; New York: Springer. .. [LiGotze2001] Li, X. and H. J. Gotze, 2001, Tutorial: Ellipsoid, geoid, gravity, geodesy, and geophysics, Geophysics, 66(6), p. 1660-1668, doi:`10.1190/1.1487109 `__ +.. [MillerSingh1994] Miller, H. G., & Singh, V. (1994). Potential field tilt — A new concept for location of potential field sources. Journal of Applied Geophysics, 32(3), 213–217. doi:`10.1016/0926-9851(94)90002-7 `__ .. [Nagy2000] Nagy, D., Papp, G. & Benedek, J.(2000). The gravitational potential and its derivatives for the prism. Journal of Geodesy 74: 552. doi:`10.1007/s001900000116 `__ .. [Nagy2002] Nagy, D., Papp, G. & Benedek, J.(2002). Corrections to "The gravitational potential and its derivatives for the prism". Journal of Geodesy. doi:`10.1007/s00190-002-0264-7 `__ .. [Oliveira2021] Oliveira Jr, Vanderlei C. and Uieda, Leonardo and Barbosa, Valeria C. F.. Sketch of three coordinate systems: Geocentric Cartesian, Geocentric Geodetic, and Topocentric Cartesian. figshare. doi: `10.6084/m9.figshare.15044241.v1 `__ diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py index 2f4ba2615..c6c8d0042 100644 --- a/examples/transformations/tilt.py +++ b/examples/transformations/tilt.py @@ -31,7 +31,7 @@ magnetic_grid_padded = xrft.pad(magnetic_grid_no_height, pad_width) # Compute the tilt of the grid -tilt_grid = hm.tilt(magnetic_grid_padded) +tilt_grid = hm.tilt_angle(magnetic_grid_padded) # Unpad the tilt grid tilt_grid = xrft.unpad(tilt_grid, pad_width) @@ -54,7 +54,7 @@ rtp_grid = xrft.unpad(rtp_grid_padded, pad_width) # Compute the tilt of the padded rtp grid -tilt_rtp_grid = hm.tilt(rtp_grid_padded) +tilt_rtp_grid = hm.tilt_angle(rtp_grid_padded) # Unpad the tilt grid tilt_rtp_grid = xrft.unpad(tilt_rtp_grid, pad_width) diff --git a/harmonica/__init__.py b/harmonica/__init__.py index bdcff0f6c..355ed7be7 100644 --- a/harmonica/__init__.py +++ b/harmonica/__init__.py @@ -27,7 +27,7 @@ gaussian_highpass, gaussian_lowpass, reduction_to_pole, - tilt, + tilt_angle, total_gradient_amplitude, upward_continuation, ) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 6de111f3c..9b79c4bc3 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -393,14 +393,21 @@ def total_gradient_amplitude(grid): return np.sqrt(gradient[0] ** 2 + gradient[1] ** 2 + gradient[2] ** 2) -def tilt(grid): +def tilt_angle(grid): r""" - Calculates the tilt of a potential field grid as defined + Calculates the tilt angle of a potential field grid by Miller and Singh (1994) - Compute the tilt of a regular gridded potential field - `M`. The horizontal derivatives are calculated though finite-differences - while the upward derivative is calculated using FFT. + Compute the tilt angle of a potential field :math:`M` sampled on a regular grid. + + .. tip:: + + When used on magnetic total field anomaly data, works best if the data is + reduced to the pole. + + It's useful to plot the zero contour line of the tilt to represent possible + outlines of the source bodies. Use matplotlib's ``pyplot.contour`` or + ``pyplot.tricontour`` for this. Parameters ---------- @@ -431,16 +438,10 @@ def tilt(grid): where :math:`M` is the regularly gridded potential field. - When used on magnetic total field anomaly data, works best if the data is - reduced to the pole. - - It's useful to plot the zero contour line of the tilt to represent possible - outlines of the source bodies. Use matplotlib's ``pyplot.contour`` or - ``pyplot.tricontour`` for this. - References ---------- [Blakely1995]_ + [MillerSingh1994]_ """ # Run sanity checks on the grid grid_sanity_checks(grid) diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index a14b951a8..90299c64d 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -25,7 +25,7 @@ gaussian_highpass, gaussian_lowpass, reduction_to_pole, - tilt, + tilt_angle, total_gradient_amplitude, upward_continuation, ) @@ -614,7 +614,7 @@ def test_against_synthetic( pad_width=pad_width, ) # Calculate the tilt and unpad it - tilt_grid = tilt(potential_padded) + tilt_grid = tilt_angle(potential_padded) tilt_grid = xrft.unpad(tilt_grid, pad_width) # Compare against g_tilt (trim the borders to ignore boundary effects) trim = 6 @@ -636,7 +636,7 @@ def test_invalid_grid_single_dimension(self): y = x**2 grid = xr.DataArray(y, coords={"x": x}, dims=("x",)) with pytest.raises(ValueError, match="Invalid grid with 1 dimensions."): - tilt(grid) + tilt_angle(grid) def test_invalid_grid_three_dimensions(self): """ @@ -650,7 +650,7 @@ def test_invalid_grid_three_dimensions(self): data = xx + yy + zz grid = xr.DataArray(data, coords={"x": x, "y": y, "z": z}, dims=("y", "x", "z")) with pytest.raises(ValueError, match="Invalid grid with 3 dimensions."): - tilt(grid) + tilt_angle(grid) def test_invalid_grid_with_nans(self, sample_potential): """ @@ -658,7 +658,7 @@ def test_invalid_grid_with_nans(self, sample_potential): """ sample_potential.values[0, 0] = np.nan with pytest.raises(ValueError, match="Found nan"): - tilt(sample_potential) + tilt_angle(sample_potential) class Testfilter: From 1406c29ceb6fc3a3836ba0ffcb20053d919bde2e Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 11:47:16 -0300 Subject: [PATCH 07/20] Update harmonica/_transformations.py Co-authored-by: Santiago Soler --- harmonica/_transformations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 4b939a415..749bf48ac 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -395,8 +395,7 @@ def total_gradient_amplitude(grid): def tilt(grid): r""" - Calculates the tilt of a potential field grid as defined - by Miller and Singh (1994) + Calculates the tilt angle of a potential field grid Compute the tilt of a regular gridded potential field `M`. The horizontal derivatives are calculated though finite-differences From dc06c3f0a33bf161d8914972311450fa5066f194 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 11:47:36 -0300 Subject: [PATCH 08/20] Update harmonica/_transformations.py Co-authored-by: Santiago Soler --- harmonica/_transformations.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 749bf48ac..e4a51f0dd 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -421,12 +421,17 @@ def tilt(grid): .. math:: - tilt(f) = tan^{-1}\left( + \text{tilt}(f) = \tan^{-1} \left( \frac{ - \frac{\partial M}{\partial z}}{ - \sqrt{\frac{\partial M}{\partial x}^2 + - \frac{\partial M}{\partial y}^2}} - \right) + \frac{\partial M}{\partial z} + }{ + \sqrt{ + \left( \frac{\partial M}{\partial x} \right)^2 + + + \left( \frac{\partial M}{\partial y} \right)^2 + } + } + \right) where :math:`M` is the regularly gridded potential field. From 5fa4572eccfad3b17e5d19aa14adf441708901f9 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 11:49:30 -0300 Subject: [PATCH 09/20] Update harmonica/tests/test_transformations.py Co-authored-by: Santiago Soler --- harmonica/tests/test_transformations.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index a14b951a8..6b39eb2dc 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -619,9 +619,6 @@ def test_against_synthetic( # Compare against g_tilt (trim the borders to ignore boundary effects) trim = 6 tilt_grid = tilt_grid[trim:-trim, trim:-trim] - g_e = sample_g_e[trim:-trim, trim:-trim] * 1e-5 # convert to SI units - g_n = sample_g_n[trim:-trim, trim:-trim] * 1e-5 # convert to SI units - g_z = sample_g_z[trim:-trim, trim:-trim] * 1e-5 # convert to SI units g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) g_tilt = np.arctan2(-g_z, g_horiz_deriv) rms = root_mean_square_error(tilt_grid, g_tilt) From 3ec18fdc78249eeb8b33961c6cebae6cb9639133 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 12:01:13 -0300 Subject: [PATCH 10/20] Update harmonica/tests/test_transformations.py Co-authored-by: Santiago Soler --- harmonica/tests/test_transformations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index 6b39eb2dc..db14fb24a 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -620,7 +620,7 @@ def test_against_synthetic( trim = 6 tilt_grid = tilt_grid[trim:-trim, trim:-trim] g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) - g_tilt = np.arctan2(-g_z, g_horiz_deriv) + g_tilt = np.arctan2(-g_z, g_horiz_deriv) # use -g_z to use the _upward_ derivative rms = root_mean_square_error(tilt_grid, g_tilt) assert rms / np.abs(tilt_grid).max() < 0.1 From 6ace014ac504b05e6b5c81345f54a7869ce687b3 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 12:01:45 -0300 Subject: [PATCH 11/20] Update harmonica/_transformations.py Co-authored-by: Santiago Soler --- harmonica/_transformations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index e4a51f0dd..135debb86 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -398,7 +398,7 @@ def tilt(grid): Calculates the tilt angle of a potential field grid Compute the tilt of a regular gridded potential field - `M`. The horizontal derivatives are calculated though finite-differences + :math:`M`. The horizontal derivatives are calculated though finite-differences while the upward derivative is calculated using FFT. Parameters From 045eda08e17343c0691804a7d8e69611c9cd9c05 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 19:44:36 -0300 Subject: [PATCH 12/20] fixed formatting --- harmonica/_transformations.py | 13 +++++++------ harmonica/filters/_fft.py | 2 +- harmonica/tests/test_tesseroid.py | 8 +------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 9b79c4bc3..e2017990a 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -398,16 +398,17 @@ def tilt_angle(grid): Calculates the tilt angle of a potential field grid by Miller and Singh (1994) - Compute the tilt angle of a potential field :math:`M` sampled on a regular grid. + Compute the tilt angle of a potential field + :math:`M` sampled on a regular grid. .. tip:: - When used on magnetic total field anomaly data, works best if the data is - reduced to the pole. + When used on magnetic total field anomaly data, works best + if the data is reduced to the pole. - It's useful to plot the zero contour line of the tilt to represent possible - outlines of the source bodies. Use matplotlib's ``pyplot.contour`` or - ``pyplot.tricontour`` for this. + It's useful to plot the zero contour line of the tilt + to represent possible outlines of the source bodies. + Use matplotlib's ``pyplot.contour`` or ``pyplot.tricontour`` for this. Parameters ---------- diff --git a/harmonica/filters/_fft.py b/harmonica/filters/_fft.py index 870748753..b495c0c1f 100644 --- a/harmonica/filters/_fft.py +++ b/harmonica/filters/_fft.py @@ -79,5 +79,5 @@ def ifft(fourier_transform, true_phase=True, true_amplitude=True, **kwargs): fourier_transform, true_phase=true_phase, true_amplitude=true_amplitude, - **kwargs + **kwargs, ) diff --git a/harmonica/tests/test_tesseroid.py b/harmonica/tests/test_tesseroid.py index 2ead87aa3..734f91a26 100644 --- a/harmonica/tests/test_tesseroid.py +++ b/harmonica/tests/test_tesseroid.py @@ -633,13 +633,7 @@ def spherical_shell_analytical(top, bottom, density, radius): homogeneous spherical shell """ potential = ( - 4 - / 3 - * np.pi - * GRAVITATIONAL_CONST - * density - * (top**3 - bottom**3) - / radius + 4 / 3 * np.pi * GRAVITATIONAL_CONST * density * (top**3 - bottom**3) / radius ) analytical = { "potential": potential, From 7a76c13f0b9523c6939a840e06b93014b544fed2 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Mon, 1 Jul 2024 19:53:40 -0300 Subject: [PATCH 13/20] Update _transformations.py --- harmonica/_transformations.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 8acc908aa..824c061f5 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -398,8 +398,9 @@ def tilt_angle(grid): Calculates the tilt angle of a potential field grid Compute the tilt of a regular gridded potential field - :math:`M`. The horizontal derivatives are calculated though finite-differences - while the upward derivative is calculated using FFT. + :math:`M`. The horizontal derivatives are calculated + though finite-differences while the upward derivative + is calculated using FFT. Parameters ---------- From ee1d61f38655dfda2366e86f68a3b438964953b1 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Tue, 2 Jul 2024 14:02:17 -0300 Subject: [PATCH 14/20] Update test_transformations.py fixed TestTilt::test_against_synthetic --- harmonica/tests/test_transformations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index c53c33cd8..96fb008a9 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -619,6 +619,9 @@ def test_against_synthetic( # Compare against g_tilt (trim the borders to ignore boundary effects) trim = 6 tilt_grid = tilt_grid[trim:-trim, trim:-trim] + g_e = sample_g_e[trim:-trim, trim:-trim] + g_n = sample_g_n[trim:-trim, trim:-trim] + g_z = sample_g_z[trim:-trim, trim:-trim] g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) g_tilt = np.arctan2(-g_z, g_horiz_deriv) # use -g_z to use the _upward_ derivative rms = root_mean_square_error(tilt_grid, g_tilt) From febfb1917a7e6088035ce28297b9b5fe411a5c20 Mon Sep 17 00:00:00 2001 From: Leonardo Miquelutti Date: Tue, 2 Jul 2024 14:08:31 -0300 Subject: [PATCH 15/20] fix format/style --- harmonica/_transformations.py | 2 +- harmonica/tests/test_transformations.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 824c061f5..f64629d9f 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -398,7 +398,7 @@ def tilt_angle(grid): Calculates the tilt angle of a potential field grid Compute the tilt of a regular gridded potential field - :math:`M`. The horizontal derivatives are calculated + :math:`M`. The horizontal derivatives are calculated though finite-differences while the upward derivative is calculated using FFT. diff --git a/harmonica/tests/test_transformations.py b/harmonica/tests/test_transformations.py index 96fb008a9..d3194752f 100644 --- a/harmonica/tests/test_transformations.py +++ b/harmonica/tests/test_transformations.py @@ -623,7 +623,9 @@ def test_against_synthetic( g_n = sample_g_n[trim:-trim, trim:-trim] g_z = sample_g_z[trim:-trim, trim:-trim] g_horiz_deriv = np.sqrt(g_e**2 + g_n**2) - g_tilt = np.arctan2(-g_z, g_horiz_deriv) # use -g_z to use the _upward_ derivative + g_tilt = np.arctan2( + -g_z, g_horiz_deriv + ) # use -g_z to use the _upward_ derivative rms = root_mean_square_error(tilt_grid, g_tilt) assert rms / np.abs(tilt_grid).max() < 0.1 From c58a47391c9661b132eedcd573a84d8d5fca1b89 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 12 Aug 2024 11:57:00 -0300 Subject: [PATCH 16/20] Specify units of the returned tilt DataArray --- harmonica/_transformations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index f64629d9f..61316bbd4 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -413,8 +413,8 @@ def tilt_angle(grid): Returns ------- tilt_grid : :class:`xarray.DataArray` - A :class:`xarray.DataArray` after calculating the - tilt of the passed ``grid``. + A :class:`xarray.DataArray` with the calculated tilt + in radians. Notes ----- From 7fe94931bb9289591dde39ec8072c1981f1ac2cc Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 12 Aug 2024 11:57:12 -0300 Subject: [PATCH 17/20] Fix typo --- harmonica/_transformations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/_transformations.py b/harmonica/_transformations.py index 61316bbd4..ea7c253b3 100644 --- a/harmonica/_transformations.py +++ b/harmonica/_transformations.py @@ -399,7 +399,7 @@ def tilt_angle(grid): Compute the tilt of a regular gridded potential field :math:`M`. The horizontal derivatives are calculated - though finite-differences while the upward derivative + through finite-differences while the upward derivative is calculated using FFT. Parameters From 9cb90fc6b613f44af49abf9416310ada6dbec1ff Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 12 Aug 2024 11:57:27 -0300 Subject: [PATCH 18/20] Uncomment line in example --- examples/transformations/tilt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py index c6c8d0042..51b63c1a5 100644 --- a/examples/transformations/tilt.py +++ b/examples/transformations/tilt.py @@ -36,8 +36,8 @@ # Unpad the tilt grid tilt_grid = xrft.unpad(tilt_grid, pad_width) -# # Show the tilt -# print("\nTilt:\n", tilt_grid) +# Show the tilt +print("\nTilt:\n", tilt_grid) # Define the inclination and declination of the region by the time of the data # acquisition (1990). From 379bcc67763e4efdd134eeabeaafe0e6bab374bb Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 12 Aug 2024 11:57:36 -0300 Subject: [PATCH 19/20] Uncomment line in example --- examples/transformations/tilt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py index 51b63c1a5..e848f6b87 100644 --- a/examples/transformations/tilt.py +++ b/examples/transformations/tilt.py @@ -59,8 +59,8 @@ # Unpad the tilt grid tilt_rtp_grid = xrft.unpad(tilt_rtp_grid, pad_width) -# # Show the tilt from RTP -# print("\nTilt from RTP:\n", tilt_rtp_grid) +# Show the tilt from RTP +print("\nTilt from RTP:\n", tilt_rtp_grid) # Plot original magnetic anomaly, its RTP, and the tilt of both fig = pygmt.Figure() From 2a9dee25280e0da80c145f4283927e974c298d84 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 12 Aug 2024 14:40:41 -0300 Subject: [PATCH 20/20] Minor improvements to the plots --- examples/transformations/tilt.py | 36 ++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/examples/transformations/tilt.py b/examples/transformations/tilt.py index e848f6b87..93e6afd29 100644 --- a/examples/transformations/tilt.py +++ b/examples/transformations/tilt.py @@ -63,8 +63,21 @@ print("\nTilt from RTP:\n", tilt_rtp_grid) # Plot original magnetic anomaly, its RTP, and the tilt of both +region = ( + magnetic_grid.easting.values.min(), + magnetic_grid.easting.values.max(), + magnetic_grid.northing.values.min(), + magnetic_grid.northing.values.max(), +) fig = pygmt.Figure() -with fig.subplot(nrows=2, ncols=2, figsize=("28c", "30c"), sharey="l"): +with fig.subplot( + nrows=2, + ncols=2, + subsize=("20c", "20c"), + sharex="b", + sharey="l", + margins=["1c", "1c"], +): scale = 0.5 * vd.maxabs(magnetic_grid, rtp_grid) with fig.set_panel(panel=0): # Make colormap of data @@ -74,7 +87,7 @@ grid=magnetic_grid, projection="X?", cmap=True, - # frame=["a", "+tMagnetic anomaly intensity (TMI)"], + frame=["a", "+tTotal field anomaly grid"], ) with fig.set_panel(panel=1): # Make colormap of data @@ -84,13 +97,15 @@ grid=rtp_grid, projection="X?", cmap=True, - # frame=["a", "+tTMI reduced to the pole (RTP)"], + frame=["a", "+tReduced to the pole (RTP)"], ) # Add colorbar + label = "nT" fig.colorbar( - frame='af+l"Magnetic anomaly intensity [nT]"', - position="JMR+o1/1c+e", + frame=f"af+l{label}", + position="JMR+o1/-0.25c+e", ) + scale = 0.6 * vd.maxabs(tilt_grid, tilt_rtp_grid) with fig.set_panel(panel=2): # Make colormap for tilt (saturate it a little bit) @@ -100,23 +115,22 @@ grid=tilt_grid, projection="X?", cmap=True, - # frame=["a", "+tTilt from TMI"], + frame=["a", "+tTilt of total field anomaly grid"], ) - # Add colorbar with fig.set_panel(panel=3): # Make colormap for tilt rtp (saturate it a little bit) - scale = 0.6 * vd.maxabs(tilt_rtp_grid) pygmt.makecpt(cmap="polar+h", series=[-scale, scale], background=True) # Plot tilt fig.grdimage( grid=tilt_rtp_grid, projection="X?", cmap=True, - # frame=["a", "+tTilt from RTP"], + frame=["a", "+tTilt of RTP grid"], ) # Add colorbar + label = "rad" fig.colorbar( - frame='af+l"Tilt [rad]"', - position="JMR+o1/1c+e", + frame=f"af+l{label}", + position="JMR+o1/-0.25c+e", ) fig.show()