def test_eql_boost_large_region(): """ Check if EQLHarmonicBoost works on a large region The iterative process ignores the effect of sources on far observation points. If the region is very large, this error should be diminished. """ # Define a squared region region = (-1000e3, 1000e3, -1000e3, 1000e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(40, 40), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # The interpolation should be sufficiently accurate on the data points eql = EQLHarmonicBoost(window_size=100e3) eql.fit(coordinates, data) assert mean_squared_error( data, eql.predict(coordinates)) < 1e-5 * vd.maxabs(data) # Gridding onto a denser grid should be reasonably accurate when compared # to synthetic values grid = vd.grid_coordinates(region=region, shape=(60, 60), extra_coords=0) true = hm.point_mass_gravity(grid, points, masses, field="g_z") assert mean_squared_error(true, eql.predict(grid)) < 1e-3 * vd.maxabs(data)
def test_eql_boost_single_window(): """ Check if EQLHarmonicBoost works with a single window that covers the whole region """ # Define a squared region region = (-3e3, -1e3, 5e3, 7e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(40, 40), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # The interpolation should be perfect on the data points eql = EQLHarmonicBoost(window_size=region[1] - region[0]) eql.fit(coordinates, data) npt.assert_allclose(data, eql.predict(coordinates), rtol=1e-5) # Gridding onto a denser grid should be reasonably accurate when compared # to synthetic values grid = vd.grid_coordinates(region=region, shape=(60, 60), extra_coords=0) true = hm.point_mass_gravity(grid, points, masses, field="g_z") npt.assert_allclose(true, eql.predict(grid), rtol=1e-3)
def test_eql_boost_random_state(): """ Check if EQLHarmonicBoost produces same result by setting random_state """ region = (-3e3, -1e3, 5e3, 7e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(20, 20), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # Initialize two EQLHarmonicBoost with the same random_state eql_a = EQLHarmonicBoost(window_size=500, random_state=0) eql_a.fit(coordinates, data) eql_b = EQLHarmonicBoost(window_size=500, random_state=0) eql_b.fit(coordinates, data) # Check if fitted coefficients are the same npt.assert_allclose(eql_a.coefs_, eql_b.coefs_)
def test_eql_boost_warm_start(): """ Check if EQLHarmonicBoost can be fitted with warm_start """ region = (-3e3, -1e3, 5e3, 7e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(20, 20), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # Check if refitting with warm_start=True changes its coefficients eql = EQLHarmonicBoost(window_size=500, warm_start=True) eql.fit(coordinates, data) coefs = eql.coefs_.copy() eql.fit(coordinates, data) assert not np.allclose(coefs, eql.coefs_) # Check if refitting with warm_start=False doesn't change its coefficients # (need to set random_state, otherwise coefficients might be different due # to another random shuffling of the windows). eql = EQLHarmonicBoost(window_size=500, warm_start=False, random_state=0) eql.fit(coordinates, data) coefs = eql.coefs_.copy() eql.fit(coordinates, data) npt.assert_allclose(coefs, eql.coefs_)
def test_eql_boost_warm_start_new_coords(): """ Check if sources are not changed when warm_start is True If the gridder is already fitted (and has warm_start=True), the location of the sources must not be modified, even if the new fitting process is carried out with another set of observation points. """ region = (-3e3, -1e3, 5e3, 7e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(20, 20), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # Capture the location of sources created by EQLHarmonicBoost on the first fit eql = EQLHarmonicBoost(window_size=500, warm_start=True) eql.fit(coordinates, data) sources = tuple(c.copy() for c in eql.points_) # Fit the gridder with a new set of observation data coordinates_new = (coordinates[0] + 100, coordinates[1] - 100, coordinates[2]) data_new = hm.point_mass_gravity(coordinates_new, points, masses, field="g_z") eql.fit(coordinates_new, data_new) # Check if sources remain on the same location for i in range(3): npt.assert_allclose(sources[i], eql.points_[i])
def test_eql_boost_custom_points(): """ Check EQLHarmonicBoost with custom points Check if the iterative gridder works well with a custom set of sources. By default, the gridder puts one source beneath each data point, therefore the indices of data and sources windows are identical. When passing a custom set of sources, these indices may differ. """ region = (-3e3, -1e3, 5e3, 7e3) # Build synthetic point masses points = vd.grid_coordinates(region=region, shape=(6, 6), extra_coords=-1e3) masses = vd.datasets.CheckerBoard(amplitude=1e13, region=region).predict(points) # Define a set of observation points coordinates = vd.grid_coordinates(region=region, shape=(20, 20), extra_coords=0) # Get synthetic data data = hm.point_mass_gravity(coordinates, points, masses, field="g_z") # Define a set of block-averaged equivalent sources sources = block_averaged_sources(coordinates, 100, depth_type="relative_depth", depth=500) # Fit EQLHarmonicBoost with the block-averaged sources eql = EQLHarmonicBoost(window_size=500, points=sources) eql.fit(coordinates, data) # Check if sources are located on the same points for i in range(3): npt.assert_allclose(sources[i], eql.points_[i]) # The interpolation should be sufficiently accurate on the data points assert mean_squared_error( data, eql.predict(coordinates)) < 1e-3 * vd.maxabs(data)
northing = [7e3, 13e3] # The vertical coordinate is positive upward so negative numbers represent depth upward = [-0.5e3, -1e3] points = [easting, northing, upward] # We're using "negative" masses to represent a "mass deficit" since we assume # measurements are gravity disturbances, not actual gravity values. masses = [3e11, -10e11] # Define computation points on a grid at 500m above the ground coordinates = vd.grid_coordinates( region=[0, 20e3, 0, 20e3], shape=(100, 100), extra_coords=500 ) # Compute the downward component of the gravitational acceleration gravity = hm.point_mass_gravity( coordinates, points, masses, field="g_z", coordinate_system="cartesian" ) print(gravity) # Plot the results on a map fig, ax = plt.subplots(figsize=(8, 6)) ax.set_aspect("equal") # Get the maximum absolute value so we can center the colorbar on zero maxabs = vd.maxabs(gravity) img = ax.contourf( *coordinates[:2], gravity, 60, vmin=-maxabs, vmax=maxabs, cmap="seismic" ) plt.colorbar(img, ax=ax, pad=0.04, shrink=0.73, label="mGal") # Plot the point mass locations ax.plot(easting, northing, "oy") ax.set_title("Gravitational acceleration (downward)")