From de2e0108d4d866dad830579084393cb0113d4c62 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2024 12:37:07 -0700 Subject: [PATCH 01/13] Change default value of depth in equivalent sources Change the default value of the `depth` argument in `EquivalentSources` to `"default"`. If this value is passed, then the depth of the sources is set as 4.5 times the median distance between first neighbouring sources. If a numerical value is passed, this is the one that will be used. Introduce a new `depth_` attribute where the estimated/passed numeric value is stored. If `points` is passed, the `depth_` attribute is set to None. Add a test checking that the depth value is properly set. --- harmonica/_equivalent_sources/cartesian.py | 51 +++++++++++++++----- harmonica/tests/test_eq_sources_cartesian.py | 14 ++++++ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index f86fcb41a..213d3ce98 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -64,11 +64,16 @@ class EquivalentSources(vdb.BaseGridder): The depth of the sources can be controlled by the ``depth`` argument. Each source is located beneath each data point or block-averaged location - at a depth equal to its elevation minus the value of the ``depth`` - argument. - In both cases a positive value of ``depth`` locates sources _beneath_ the - data points or the block-averaged locations, thus a negative ``depth`` will - put the sources _above_ them. + at a depth equal to its elevation minus the value of the ``depth_`` + attribute. + If ``"default"`` is passed to the ``depth`` argument, then the ``depth_`` + attribute is set to 4.5 times the distance between first neighbouring + sources. + If a numerical value is passed to the ``depth`` argument, then this is the + one used for the ``depth_`` attribute. + A positive value of ``depth_`` locates sources _beneath_ the data points or + the block-averaged locations, thus a negative ``depth_`` will put the + sources _above_ them. Custom source locations can be chosen by specifying the ``points`` argument, in which case the ``block_size`` and ``depth`` arguments will be @@ -100,13 +105,16 @@ class EquivalentSources(vdb.BaseGridder): If None, will place one point source below each observation point at a fixed relative depth below the observation point [Cooper2000]_. Defaults to None. - depth : float + depth : float or "default" Parameter used to control the depth at which the point sources will be located. - Each source is located beneath each data point (or block-averaged - location) at a depth equal to its elevation minus the ``depth`` value. + If a value is provided, each source is located beneath each data point + (or block-averaged location) at a depth equal to its elevation minus + the ``depth`` value. + If set to ``"default"``, the depth of the sources will be estimated as + 4.5 times the median distance between first neighbouring sources. This parameter is ignored if *points* is specified. - Defaults to 500. + Defaults to ``"default"``. block_size: float, tuple = (s_north, s_east) or None Size of the blocks used on block-averaged equivalent sources. If a single value is passed, the blocks will have a square shape. @@ -129,6 +137,10 @@ class EquivalentSources(vdb.BaseGridder): Coordinates of the equivalent point sources. coefs_ : array Estimated coefficients of every point source. + depth_ : float or None + Estimated depth of the sources calculated as 4.5 times the median + distance between first neighbouring sources. This attribute is set to + None if ``points`` is passed. region_ : tuple The boundaries (``[W, E, S, N]``) of the data used to fit the interpolator. Used as the default region for the @@ -154,11 +166,16 @@ def __init__( self, damping=None, points=None, - depth=500, + depth: float | str = "default", block_size=None, parallel=True, dtype="float64", ): + if isinstance(depth, str) and depth != "default": + raise ValueError( + f"Found invalid 'depth' value equal to '{depth}'." + "It should be 'default' or a numeric value." + ) self.damping = damping self.points = points self.depth = depth @@ -205,6 +222,7 @@ def fit(self, coordinates, data, weights=None): if self.points is None: self.points_ = self._build_points(coordinates) else: + self.depth_ = None # set depth_ to None so we don't leave it unset self.points_ = tuple( p.astype(self.dtype) for p in vdb.n_1d_arrays(self.points, 3) ) @@ -220,7 +238,12 @@ def _build_points(self, coordinates): and apply block-averaging if ``block_size`` is not None. The point sources will be placed beneath the (averaged) observation points at a depth calculated as the elevation of the data point minus - the ``depth``. + the ``depth_`` attribute. + + If ``depth`` is set to ``"default"``, the ``depth_`` attribute is set + as 4.5 times the median distance between first neighbouring sources. + If ``depth`` is set to a numerical value, this is used for the + ``depth_`` attribute. Parameters ---------- @@ -238,10 +261,14 @@ def _build_points(self, coordinates): """ if self.block_size is not None: coordinates = self._block_average_coordinates(coordinates) + if self.depth == "default": + self.depth_ = 4.5 * np.median(vd.median_distance(coordinates, k_nearest=1)) + else: + self.depth_ = self.depth return ( coordinates[0], coordinates[1], - coordinates[2] - self.depth, + coordinates[2] - self.depth_, ) def _block_average_coordinates(self, coordinates): diff --git a/harmonica/tests/test_eq_sources_cartesian.py b/harmonica/tests/test_eq_sources_cartesian.py index 14a98a9c6..70b03a3ef 100644 --- a/harmonica/tests/test_eq_sources_cartesian.py +++ b/harmonica/tests/test_eq_sources_cartesian.py @@ -449,3 +449,17 @@ def test_error_ignored_args(coordinates_small, data_small, region): msg = "The 'bla' arguments are being ignored." with pytest.warns(FutureWarning, match=msg): eqs.grid(coordinates=grid_coords, bla="bla") + + +def test_default_depth(coordinates, data): + """ + Test if the depth of sources is correctly set by the default strategy + """ + # Get distance to first neighbour in the grid + easting, northing = coordinates[:2] + d_easting = easting[1, 1] - easting[0, 0] + d_northing = northing[1, 1] - northing[0, 0] + first_neighbour_distance = min(d_easting, d_northing) + # Fit the equivalent sources with default `depth` + eqs = EquivalentSources().fit(coordinates, data) + npt.assert_allclose(eqs.depth_, first_neighbour_distance * 4.5) From 9e8162b91e2bf52d5953e260805cf53e2f225025 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2024 12:40:04 -0700 Subject: [PATCH 02/13] Merge test functions for equivalent sources Merge the tests functions that checked the accuracy of equivalent sources using different dtypes. --- harmonica/tests/test_eq_sources_cartesian.py | 57 ++++++-------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/harmonica/tests/test_eq_sources_cartesian.py b/harmonica/tests/test_eq_sources_cartesian.py index 70b03a3ef..913157207 100644 --- a/harmonica/tests/test_eq_sources_cartesian.py +++ b/harmonica/tests/test_eq_sources_cartesian.py @@ -104,50 +104,27 @@ def fixture_coordinates_9x9(region): @run_only_with_numba -def test_equivalent_sources_cartesian(region, points, masses, coordinates, data): +@pytest.mark.parametrize("dtype", ("default", "float32")) +def test_equivalent_sources_cartesian(region, points, masses, coordinates, data, dtype): """ Check that predictions are reasonable when interpolating from one grid to a denser grid. Use Cartesian coordinates. """ - # The interpolation should be perfect on the data points - eqs = EquivalentSources() - eqs.fit(coordinates, data) - npt.assert_allclose(data, eqs.predict(coordinates), rtol=1e-5) - - # Gridding onto a denser grid should be reasonably accurate when compared - # to synthetic values - upward = 0 - shape = (60, 60) - grid_coords = vd.grid_coordinates(region=region, shape=shape, extra_coords=upward) - true = point_gravity(grid_coords, points, masses, field="g_z") - npt.assert_allclose(true, eqs.predict(grid_coords), rtol=1e-3) - - # Test grid method - grid = eqs.grid(grid_coords) - npt.assert_allclose(true, grid.scalars, rtol=1e-3) - - # Test profile method - point1 = (region[0], region[2]) - point2 = (region[0], region[3]) - profile = eqs.profile(point1, point2, upward, shape[0]) - true = point_gravity( - (profile.easting, profile.northing, profile.upward), points, masses, field="g_z" - ) - npt.assert_allclose(true, profile.scalars, rtol=1e-3) + # Set absolute tolerances for tests based on dtype (float32 should be less + # accurate) + if dtype == "float32": + kwargs = dict(dtype=dtype) + atol = 1.5e-3 * vd.maxabs(data) + else: + kwargs = {} + atol = 1e-3 * vd.maxabs(data) + # Fit the equivalent sources + eqs = EquivalentSources(**kwargs) + eqs.fit(coordinates, data) -@run_only_with_numba -def test_equivalent_sources_cartesian_float32( - region, points, masses, coordinates, data -): - """ - Check that predictions are reasonable when interpolating from one grid to - a denser grid, using float32 as dtype. - """ # The interpolation should be perfect on the data points - eqs = EquivalentSources(dtype="float32") - eqs.fit(coordinates, data) - npt.assert_allclose(data, eqs.predict(coordinates), atol=1e-3 * vd.maxabs(data)) + npt.assert_allclose(data, eqs.predict(coordinates), atol=atol) # Gridding onto a denser grid should be reasonably accurate when compared # to synthetic values @@ -155,11 +132,11 @@ def test_equivalent_sources_cartesian_float32( shape = (60, 60) grid_coords = vd.grid_coordinates(region=region, shape=shape, extra_coords=upward) true = point_gravity(grid_coords, points, masses, field="g_z") - npt.assert_allclose(true, eqs.predict(grid_coords), atol=1e-3 * vd.maxabs(true)) + npt.assert_allclose(true, eqs.predict(grid_coords), atol=atol) # Test grid method grid = eqs.grid(grid_coords) - npt.assert_allclose(true, grid.scalars, atol=1e-3 * vd.maxabs(true)) + npt.assert_allclose(true, grid.scalars, atol=atol) # Test profile method point1 = (region[0], region[2]) @@ -168,7 +145,7 @@ def test_equivalent_sources_cartesian_float32( true = point_gravity( (profile.easting, profile.northing, profile.upward), points, masses, field="g_z" ) - npt.assert_allclose(true, profile.scalars, atol=1e-3 * vd.maxabs(true)) + npt.assert_allclose(true, profile.scalars, atol=atol) def test_equivalent_sources_small_data_cartesian(region, points, masses): From 470479f187e4b39fe56e6d1d643240ed8857fb1c Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 3 May 2024 15:29:08 -0700 Subject: [PATCH 03/13] Import annotations from __future__ --- harmonica/_equivalent_sources/cartesian.py | 1 + 1 file changed, 1 insertion(+) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index 213d3ce98..3b77b9eb7 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -7,6 +7,7 @@ """ Equivalent sources for generic harmonic functions in Cartesian coordinates """ +from __future__ import annotations import warnings import numpy as np From c5e2d386eb54455ecc994d89a880d439fb565ff7 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 3 May 2024 15:31:06 -0700 Subject: [PATCH 04/13] Run black --- harmonica/_equivalent_sources/cartesian.py | 1 + 1 file changed, 1 insertion(+) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index 3b77b9eb7..14d87976f 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -8,6 +8,7 @@ Equivalent sources for generic harmonic functions in Cartesian coordinates """ from __future__ import annotations + import warnings import numpy as np From 2308f27c674c48ec9fd2ab04849aa325642675ae Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 3 May 2024 15:38:54 -0700 Subject: [PATCH 05/13] Fix typo --- harmonica/_equivalent_sources/cartesian.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index 14d87976f..69892efca 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -69,7 +69,7 @@ class EquivalentSources(vdb.BaseGridder): at a depth equal to its elevation minus the value of the ``depth_`` attribute. If ``"default"`` is passed to the ``depth`` argument, then the ``depth_`` - attribute is set to 4.5 times the distance between first neighbouring + attribute is set to 4.5 times the distance between first neighboring sources. If a numerical value is passed to the ``depth`` argument, then this is the one used for the ``depth_`` attribute. @@ -114,7 +114,7 @@ class EquivalentSources(vdb.BaseGridder): (or block-averaged location) at a depth equal to its elevation minus the ``depth`` value. If set to ``"default"``, the depth of the sources will be estimated as - 4.5 times the median distance between first neighbouring sources. + 4.5 times the median distance between first neighboring sources. This parameter is ignored if *points* is specified. Defaults to ``"default"``. block_size: float, tuple = (s_north, s_east) or None @@ -141,7 +141,7 @@ class EquivalentSources(vdb.BaseGridder): Estimated coefficients of every point source. depth_ : float or None Estimated depth of the sources calculated as 4.5 times the median - distance between first neighbouring sources. This attribute is set to + distance between first neighboring sources. This attribute is set to None if ``points`` is passed. region_ : tuple The boundaries (``[W, E, S, N]``) of the data used to fit the @@ -243,7 +243,7 @@ def _build_points(self, coordinates): the ``depth_`` attribute. If ``depth`` is set to ``"default"``, the ``depth_`` attribute is set - as 4.5 times the median distance between first neighbouring sources. + as 4.5 times the median distance between first neighboring sources. If ``depth`` is set to a numerical value, this is used for the ``depth_`` attribute. From 19a7cc78faa0a5e2adc22ff19c6b0198c1ba9e87 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 3 May 2024 15:40:35 -0700 Subject: [PATCH 06/13] Use default depth in tutorial Add admonition explaining the default behaviour of the ``depth`` argument. --- doc/user_guide/equivalent_sources/index.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/user_guide/equivalent_sources/index.rst b/doc/user_guide/equivalent_sources/index.rst index 35a54f67f..079c9cba3 100644 --- a/doc/user_guide/equivalent_sources/index.rst +++ b/doc/user_guide/equivalent_sources/index.rst @@ -72,7 +72,7 @@ Now we can initialize the :class:`harmonica.EquivalentSources` class. import harmonica as hm - equivalent_sources = hm.EquivalentSources(depth=10e3, damping=10) + equivalent_sources = hm.EquivalentSources(damping=10) equivalent_sources By default, it places the sources one beneath each data point at a relative @@ -81,6 +81,16 @@ This *relative depth* can be set through the ``depth`` argument. Deepest sources generate smoother predictions (*underfitting*), while shallow ones tend to overfit the data. +.. hint:: + + By default, since Harmonica v0.7.0, the sources will be located at a depth + below the data points estimated as 4.5 times the distance between + first neighboring sources. Alternatively, we can set a value for this depth + below the data points through the ``depth`` argument. + + The estimated value for the depth of the sources can be explored through the + :attr:`harmonica.EquivalentSources.depth_` attribute. + The ``damping`` parameter is used to smooth the coefficients of the sources and stabilize the least square problem. A higher ``damping`` will create smoother predictions, while a lower one could overfit the data and create artifacts. From 61a32650383d08ce3a92173a4b6c0678d63b259c Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 14 May 2024 09:47:38 -0700 Subject: [PATCH 07/13] Replace median for mean Use the mean to compute the average distance of neighboring data points. --- harmonica/_equivalent_sources/cartesian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index 69892efca..e9aaf7470 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -264,7 +264,7 @@ def _build_points(self, coordinates): if self.block_size is not None: coordinates = self._block_average_coordinates(coordinates) if self.depth == "default": - self.depth_ = 4.5 * np.median(vd.median_distance(coordinates, k_nearest=1)) + self.depth_ = 4.5 * np.mean(vd.median_distance(coordinates, k_nearest=1)) else: self.depth_ = self.depth return ( From 06f156566052361a2fdee6314206e7f6fc0e3e33 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 11 Jun 2024 09:23:47 -0700 Subject: [PATCH 08/13] Increase atol for float32 Try to make tests on Python 3.8 pass --- harmonica/tests/test_eq_sources_cartesian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/tests/test_eq_sources_cartesian.py b/harmonica/tests/test_eq_sources_cartesian.py index 913157207..9b9060208 100644 --- a/harmonica/tests/test_eq_sources_cartesian.py +++ b/harmonica/tests/test_eq_sources_cartesian.py @@ -114,7 +114,7 @@ def test_equivalent_sources_cartesian(region, points, masses, coordinates, data, # accurate) if dtype == "float32": kwargs = dict(dtype=dtype) - atol = 1.5e-3 * vd.maxabs(data) + atol = 1.6e-3 * vd.maxabs(data) else: kwargs = {} atol = 1e-3 * vd.maxabs(data) From b6f915b96aaf0ab3c2c15b491bde3c72fa92b152 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 11 Jun 2024 11:03:08 -0700 Subject: [PATCH 09/13] Increase atol for float32 --- harmonica/tests/test_eq_sources_cartesian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/tests/test_eq_sources_cartesian.py b/harmonica/tests/test_eq_sources_cartesian.py index 9b9060208..9cf49834c 100644 --- a/harmonica/tests/test_eq_sources_cartesian.py +++ b/harmonica/tests/test_eq_sources_cartesian.py @@ -114,7 +114,7 @@ def test_equivalent_sources_cartesian(region, points, masses, coordinates, data, # accurate) if dtype == "float32": kwargs = dict(dtype=dtype) - atol = 1.6e-3 * vd.maxabs(data) + atol = 1.7e-3 * vd.maxabs(data) else: kwargs = {} atol = 1e-3 * vd.maxabs(data) From b6a2e0aad357842f2c6920098a6631e281d1bff8 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 11 Jun 2024 12:01:35 -0700 Subject: [PATCH 10/13] Add trailing space in error message --- harmonica/_equivalent_sources/cartesian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index e9aaf7470..b7b6eec93 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -175,7 +175,7 @@ def __init__( ): if isinstance(depth, str) and depth != "default": raise ValueError( - f"Found invalid 'depth' value equal to '{depth}'." + f"Found invalid 'depth' value equal to '{depth}' ." "It should be 'default' or a numeric value." ) self.damping = damping From e0901748aa1bdc7ece471fc6cd6b96383479bbef Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 11 Jun 2024 12:01:51 -0700 Subject: [PATCH 11/13] Add test for invalid depth parameters --- harmonica/tests/test_eq_sources_cartesian.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/harmonica/tests/test_eq_sources_cartesian.py b/harmonica/tests/test_eq_sources_cartesian.py index 9cf49834c..33f008d46 100644 --- a/harmonica/tests/test_eq_sources_cartesian.py +++ b/harmonica/tests/test_eq_sources_cartesian.py @@ -440,3 +440,13 @@ def test_default_depth(coordinates, data): # Fit the equivalent sources with default `depth` eqs = EquivalentSources().fit(coordinates, data) npt.assert_allclose(eqs.depth_, first_neighbour_distance * 4.5) + + +def test_invalid_depth(): + """ + Test if error is raised after passing invalid value for depth. + """ + invalid_depth = "this is not a valid one" + msg = f"Found invalid 'depth' value equal to '{invalid_depth}'" + with pytest.raises(ValueError, match=msg): + EquivalentSources(depth=invalid_depth) From acb734ee817e1247ae1fd969e26e29504bd6ce1d Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 11 Jun 2024 14:47:43 -0700 Subject: [PATCH 12/13] Replace median for mean in docs and example --- doc/user_guide/equivalent_sources/index.rst | 2 +- harmonica/_equivalent_sources/cartesian.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/user_guide/equivalent_sources/index.rst b/doc/user_guide/equivalent_sources/index.rst index 6a134f3d4..a9a6e79fb 100644 --- a/doc/user_guide/equivalent_sources/index.rst +++ b/doc/user_guide/equivalent_sources/index.rst @@ -84,7 +84,7 @@ ones tend to overfit the data. .. hint:: By default, since Harmonica v0.7.0, the sources will be located at a depth - below the data points estimated as 4.5 times the distance between + below the data points estimated as 4.5 times the mean distance between first neighboring sources. Alternatively, we can set a value for this depth below the data points through the ``depth`` argument. diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index b7b6eec93..936a4b947 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -69,7 +69,7 @@ class EquivalentSources(vdb.BaseGridder): at a depth equal to its elevation minus the value of the ``depth_`` attribute. If ``"default"`` is passed to the ``depth`` argument, then the ``depth_`` - attribute is set to 4.5 times the distance between first neighboring + attribute is set to 4.5 times the mean distance between first neighboring sources. If a numerical value is passed to the ``depth`` argument, then this is the one used for the ``depth_`` attribute. @@ -114,7 +114,7 @@ class EquivalentSources(vdb.BaseGridder): (or block-averaged location) at a depth equal to its elevation minus the ``depth`` value. If set to ``"default"``, the depth of the sources will be estimated as - 4.5 times the median distance between first neighboring sources. + 4.5 times the mean distance between first neighboring sources. This parameter is ignored if *points* is specified. Defaults to ``"default"``. block_size: float, tuple = (s_north, s_east) or None @@ -140,7 +140,7 @@ class EquivalentSources(vdb.BaseGridder): coefs_ : array Estimated coefficients of every point source. depth_ : float or None - Estimated depth of the sources calculated as 4.5 times the median + Estimated depth of the sources calculated as 4.5 times the mean distance between first neighboring sources. This attribute is set to None if ``points`` is passed. region_ : tuple @@ -243,7 +243,7 @@ def _build_points(self, coordinates): the ``depth_`` attribute. If ``depth`` is set to ``"default"``, the ``depth_`` attribute is set - as 4.5 times the median distance between first neighboring sources. + as 4.5 times the mean distance between first neighboring sources. If ``depth`` is set to a numerical value, this is used for the ``depth_`` attribute. From 17dd753357bedce57a00e9ae1e0e3ea4eaa92db5 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 12 Jun 2024 09:07:57 -0700 Subject: [PATCH 13/13] Fix typo in error message Co-authored-by: Leonardo Uieda --- harmonica/_equivalent_sources/cartesian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/harmonica/_equivalent_sources/cartesian.py b/harmonica/_equivalent_sources/cartesian.py index 936a4b947..2944f719e 100644 --- a/harmonica/_equivalent_sources/cartesian.py +++ b/harmonica/_equivalent_sources/cartesian.py @@ -175,7 +175,7 @@ def __init__( ): if isinstance(depth, str) and depth != "default": raise ValueError( - f"Found invalid 'depth' value equal to '{depth}' ." + f"Found invalid 'depth' value equal to '{depth}'. " "It should be 'default' or a numeric value." ) self.damping = damping