From 9e428a009e2de94c039a2e311e83017041aa1f51 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Sun, 5 Jul 2026 17:26:43 +0200 Subject: [PATCH 1/9] add `estimate_required_memory` for cartesian equivalent sources --- .../_equivalent_sources/cartesian.py | 43 +++++++++++++++++++ test/test_eq_sources_cartesian.py | 19 ++++++++ 2 files changed, 62 insertions(+) diff --git a/src/harmonica/_equivalent_sources/cartesian.py b/src/harmonica/_equivalent_sources/cartesian.py index 1e9b982d5..1340f7baf 100644 --- a/src/harmonica/_equivalent_sources/cartesian.py +++ b/src/harmonica/_equivalent_sources/cartesian.py @@ -190,6 +190,49 @@ def __init__( # Define Green's function for Cartesian coordinates self.greens_function = greens_func_cartesian + def estimate_required_memory(self, coordinates): + """ + Estimate the memory required for storing the Jacobian matrix. + + Parameters + ---------- + coordinates : tuple of arrays + Arrays with the coordinates of each data point. Should be in the + following order: (``easting``, ``northing``, ``upward``, ...). + Only ``easting``, ``northing``, and ``upward`` will be used, all + subsequent coordinates will be ignored. + + Returns + ------- + memory_required : int + Amount of memory required to store the Jacobian matrix in + bytes. + + Examples + -------- + >>> import bordado as bd + >>> coordinates = bd.random_coordinates( + ... region=(-1e3, 3e3, 2e3, 5e3), + ... size=100, + ... non_dimensional_coords=100, + ... random_seed=42, + ... ) + >>> eqs = EquivalentSources() + >>> n_bytes = eqs.estimate_required_memory(coordinates) + >>> int(n_bytes) + 80000 + """ + # Build the sources and assign the points_ attribute + coordinates = vdb.n_1d_arrays(coordinates, 3) + points = self._build_points(coordinates) + # Get the number of sources and data + n_data = coordinates[0].size + n_points = points[0].size + # Compute the size of the Jacobian matrix + jacobian_size = n_data * n_points + # Estimate size of the Jacobian matrix in bytes + return jacobian_size * np.dtype(self.dtype).itemsize + def fit(self, coordinates, data, weights=None): """ Fit the coefficients of the equivalent sources. diff --git a/test/test_eq_sources_cartesian.py b/test/test_eq_sources_cartesian.py index d7267d44e..692ef9298 100644 --- a/test/test_eq_sources_cartesian.py +++ b/test/test_eq_sources_cartesian.py @@ -494,3 +494,22 @@ def test_zero_depth(): msg = "Depth value cannot be zero. It should be a non-zero numeric value." with pytest.raises(ValueError, match=msg): EquivalentSources(depth=zero_depth) + + +@pytest.mark.parametrize("spacing", [100, 500, 1e3]) +@pytest.mark.parametrize(("dtype", "itemsize"), [("float32", 4), ("float64", 8)]) +def test_memory_estimation(spacing, dtype, itemsize): + """ + Test the estimate_required_memory class method. + """ + region = (-1e3, 5e3, 2e3, 8e3) + coordinates = bd.grid_coordinates( + region=region, spacing=spacing, non_dimensional_coords=0 + ) + # Compute expected required memory + n_data = coordinates[0].size + expected_required_memory = n_data * n_data * itemsize + # Estimate required memory + eqs = EquivalentSources(dtype=dtype) + required_memory = eqs.estimate_required_memory(coordinates) + assert required_memory == expected_required_memory From c6945de3d3f41df6352eef800443bbca6fdf293e Mon Sep 17 00:00:00 2001 From: mdtanker Date: Sun, 5 Jul 2026 17:27:03 +0200 Subject: [PATCH 2/9] add `estimate_required_memory` for spherical equivalent sources --- .../_equivalent_sources/spherical.py | 52 ++++++++++++++++++- test/test_eq_sources_spherical.py | 18 +++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/harmonica/_equivalent_sources/spherical.py b/src/harmonica/_equivalent_sources/spherical.py index 52368daff..41218c1b4 100644 --- a/src/harmonica/_equivalent_sources/spherical.py +++ b/src/harmonica/_equivalent_sources/spherical.py @@ -106,7 +106,7 @@ class EquivalentSourcesSph(vdb.BaseGridder): # as xr.Dataset. dims = ("spherical_latitude", "longitude") - # Overwrite the defalt name for the upward coordinate. + # Overwrite the default name for the upward coordinate. extra_coords_name = "radius" def __init__( @@ -123,6 +123,56 @@ def __init__( # Define Green's function for spherical coordinates self.greens_function = greens_func_spherical + def estimate_required_memory(self, coordinates): + """ + Estimate the memory required for storing the Jacobian matrix. + + Parameters + ---------- + coordinates : tuple of arrays + Arrays with the coordinates of each data point. Should be in the + following order: (``easting``, ``northing``, ``upward``, ...). + Only ``easting``, ``northing``, and ``upward`` will be used, all + subsequent coordinates will be ignored. + + Returns + ------- + memory_required : int + Amount of memory required to store the Jacobian matrix in + bytes. + + Examples + -------- + >>> import bordado as bd + >>> coordinates = bd.random_coordinates( + ... region=(-70, -60, -40, -30), + ... size=100, + ... non_dimensional_coords=100, + ... random_seed=42, + ... ) + >>> eqs = EquivalentSourcesSph() + >>> n_bytes = eqs.estimate_required_memory(coordinates) + >>> int(n_bytes) + 80000 + """ + # Build the sources and assign the points_ attribute + coordinates = vdb.n_1d_arrays(coordinates, 3) + if self.points is None: + points = ( + coordinates[0], + coordinates[1], + coordinates[2] - self.relative_depth, + ) + else: + points = vdb.n_1d_arrays(self.points, 3) + # Get the number of sources and data + n_data = coordinates[0].size + n_points = points[0].size + # Compute the size of the Jacobian matrix + jacobian_size = n_data * n_points + # Estimate size of a single element of the Jacobian matrix in bytes + return jacobian_size * np.dtype(coordinates[0].dtype).itemsize + def fit(self, coordinates, data, weights=None): """ Fit the coefficients of the equivalent sources. diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index b12509a1c..1b6caa1af 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -300,3 +300,21 @@ 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") + + +@pytest.mark.parametrize("spacing", [100, 500, 1e3]) +def test_memory_estimation(spacing): + """ + Test the estimate_required_memory class method. + """ + region = (-1e3, 5e3, 2e3, 8e3) + coordinates = bd.grid_coordinates( + region=region, spacing=spacing, non_dimensional_coords=0 + ) + # Compute expected required memory + n_data = coordinates[0].size + expected_required_memory = n_data * n_data * 8 + # Estimate required memory + eqs = EquivalentSourcesSph() + required_memory = eqs.estimate_required_memory(coordinates) + assert required_memory == expected_required_memory From f5a213e474a8bf197428726178063f157f09badb Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Thu, 9 Jul 2026 08:55:50 +0200 Subject: [PATCH 3/9] Update src/harmonica/_equivalent_sources/spherical.py Co-authored-by: Santiago Soler --- src/harmonica/_equivalent_sources/spherical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harmonica/_equivalent_sources/spherical.py b/src/harmonica/_equivalent_sources/spherical.py index 41218c1b4..606311b1c 100644 --- a/src/harmonica/_equivalent_sources/spherical.py +++ b/src/harmonica/_equivalent_sources/spherical.py @@ -131,8 +131,8 @@ def estimate_required_memory(self, coordinates): ---------- coordinates : tuple of arrays Arrays with the coordinates of each data point. Should be in the - following order: (``easting``, ``northing``, ``upward``, ...). - Only ``easting``, ``northing``, and ``upward`` will be used, all + following order: (``longitude``, ``latitude``, ``radius``, ...). + Only ``longitude``, ``latitude``, and ``radius`` will be used, all subsequent coordinates will be ignored. Returns From aa83620dd977fc7104dfb57afa789a29102a1405 Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:55:20 +0200 Subject: [PATCH 4/9] Update test_eq_sources_spherical.py --- test/test_eq_sources_spherical.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index abdd062c6..b04c0c46b 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -318,3 +318,25 @@ def test_memory_estimation(spacing): eqs = EquivalentSourcesSph() required_memory = eqs.estimate_required_memory(coordinates) assert required_memory == expected_required_memory + + +def test_memory_estimation_supplied_points(): + """ + Test the estimate_required_memory class method with user-supplied points. + """ + region = (-1e3, 5e3, 2e3, 8e3) + coordinates = bd.grid_coordinates( + region=region, shape=(6, 6, non_dimensional_coords=0 + ) + # instead of 1 point beneath each datapoint, give custom points + points = bd.grid_coordinates( + region=region, shape=(3, 3), non_dimensional_coords=0 + ) + # Compute expected required memory + n_data = coordinates[0].size + n_coords = points[0].size + expected_required_memory = n_coords * n_data * 8 + # Estimate required memory + eqs = EquivalentSourcesSph(points=points) + required_memory = eqs.estimate_required_memory(coordinates) + assert required_memory == expected_required_memory From b8ba9a50b649b43efbf75bbb7ce5b9af60537408 Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:56:18 +0200 Subject: [PATCH 5/9] typo --- test/test_eq_sources_spherical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index b04c0c46b..142c9f693 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -326,7 +326,7 @@ def test_memory_estimation_supplied_points(): """ region = (-1e3, 5e3, 2e3, 8e3) coordinates = bd.grid_coordinates( - region=region, shape=(6, 6, non_dimensional_coords=0 + region=region, shape=(6, 6), non_dimensional_coords=0 ) # instead of 1 point beneath each datapoint, give custom points points = bd.grid_coordinates( From 777a73c29dddb83fe1926ad625a1b55e2e1fbf6a Mon Sep 17 00:00:00 2001 From: mdtanker Date: Mon, 13 Jul 2026 17:30:57 +0200 Subject: [PATCH 6/9] formatting --- test/test_eq_sources_spherical.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index 142c9f693..d18db0256 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -329,9 +329,7 @@ def test_memory_estimation_supplied_points(): region=region, shape=(6, 6), non_dimensional_coords=0 ) # instead of 1 point beneath each datapoint, give custom points - points = bd.grid_coordinates( - region=region, shape=(3, 3), non_dimensional_coords=0 - ) + points = bd.grid_coordinates(region=region, shape=(3, 3), non_dimensional_coords=0) # Compute expected required memory n_data = coordinates[0].size n_coords = points[0].size From df202c2d68c408823914ad57c1a9b4fb31dd12f5 Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:29:46 +0200 Subject: [PATCH 7/9] Update test/test_eq_sources_spherical.py Co-authored-by: Santiago Soler --- test/test_eq_sources_spherical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index d18db0256..c8c3f3c1c 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -307,9 +307,9 @@ def test_memory_estimation(spacing): """ Test the estimate_required_memory class method. """ - region = (-1e3, 5e3, 2e3, 8e3) + region = (-20, 20, -20, 20) coordinates = bd.grid_coordinates( - region=region, spacing=spacing, non_dimensional_coords=0 + region=region, spacing=spacing, non_dimensional_coords=100.0 ) # Compute expected required memory n_data = coordinates[0].size From 56b8409f7d9721012106210789035d8479265463 Mon Sep 17 00:00:00 2001 From: Matt Tankersley <81199856+mdtanker@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:30:02 +0200 Subject: [PATCH 8/9] Update test/test_eq_sources_spherical.py Co-authored-by: Santiago Soler --- test/test_eq_sources_spherical.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index c8c3f3c1c..9909a8d9a 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -324,12 +324,12 @@ def test_memory_estimation_supplied_points(): """ Test the estimate_required_memory class method with user-supplied points. """ - region = (-1e3, 5e3, 2e3, 8e3) + region = (-20, 20, -20, 20) coordinates = bd.grid_coordinates( - region=region, shape=(6, 6), non_dimensional_coords=0 + region=region, shape=(6, 6), non_dimensional_coords=100.0 ) # instead of 1 point beneath each datapoint, give custom points - points = bd.grid_coordinates(region=region, shape=(3, 3), non_dimensional_coords=0) + points = bd.grid_coordinates(region=region, shape=(3, 3), non_dimensional_coords=50.0) # Compute expected required memory n_data = coordinates[0].size n_coords = points[0].size From 298be0f02e5eb8310d85ab606a9bfd0b43e8c000 Mon Sep 17 00:00:00 2001 From: mdtanker Date: Wed, 15 Jul 2026 10:34:07 +0200 Subject: [PATCH 9/9] formatting --- test/test_eq_sources_spherical.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_eq_sources_spherical.py b/test/test_eq_sources_spherical.py index 9909a8d9a..67c4ceb14 100644 --- a/test/test_eq_sources_spherical.py +++ b/test/test_eq_sources_spherical.py @@ -329,7 +329,9 @@ def test_memory_estimation_supplied_points(): region=region, shape=(6, 6), non_dimensional_coords=100.0 ) # instead of 1 point beneath each datapoint, give custom points - points = bd.grid_coordinates(region=region, shape=(3, 3), non_dimensional_coords=50.0) + points = bd.grid_coordinates( + region=region, shape=(3, 3), non_dimensional_coords=50.0 + ) # Compute expected required memory n_data = coordinates[0].size n_coords = points[0].size