1. Levelling to a grid#
[1]:
%load_ext autoreload
%autoreload 2
import boule
import numpy as np
import pandas as pd
import plotly.io as pio
import polartoolkit as ptk
import verde as vd
import airbornegeo
pio.renderers.default = "notebook"
/home/airbornegeo/airbornegeo/.pixi/envs/default/lib/python3.14/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
1.1. Load survey data#
This is a subset of the BAS AGAP survey over Antarctica’s Gamburtsev Subglacial Mountains. The file is download and subset in the notebook AGAP_magnetic_survey, and the BAS processing steps are repeated in the notebook processing_AGAP_magnetic_survey.
[2]:
data_df = pd.read_csv("data/AGAP_gravity_survey_processed.csv")
data_df = data_df[
[
"easting",
"northing",
"height",
"line",
"unixtime",
"distance_along_line",
"grav_disturbance_filt",
]
]
data_df.head()
[2]:
| easting | northing | height | line | unixtime | distance_along_line | grav_disturbance_filt | |
|---|---|---|---|---|---|---|---|
| 0 | 1.000024e+06 | 226237.330771 | 4156.1 | 1 | 1.229507e+09 | 0.000000 | 49.38 |
| 1 | 1.000083e+06 | 226246.631269 | 4156.0 | 1 | 1.229507e+09 | 59.842447 | 49.45 |
| 2 | 1.000142e+06 | 226255.809132 | 4156.1 | 1 | 1.229507e+09 | 119.693401 | 49.52 |
| 3 | 1.000201e+06 | 226264.969079 | 4156.4 | 1 | 1.229507e+09 | 179.545645 | 49.58 |
| 4 | 1.000260e+06 | 226274.156809 | 4156.6 | 1 | 1.229507e+09 | 239.285174 | 49.65 |
[3]:
# plot the data
airbornegeo.plotly_points(
data_df[::20],
color_col="grav_disturbance_filt",
robust=True,
size=2,
)
1.2. Get a grid of gravity disturbance at 10km#
[4]:
# Download satellite gravity data from EIGEN-6C4 model
region = vd.get_region((data_df.easting, data_df.northing))
eigen = ptk.fetch.gravity(
version="eigen",
spacing=5e3,
region=region,
)
eigen_df = vd.grid_to_table(eigen)
eigen_df = eigen_df.rename(columns={"x": "easting", "y": "northing"})
# reproject from EPSG 3031 to lat lon
eigen_df["lon"], eigen_df["lat"] = airbornegeo.reproject(
eigen_df.easting,
eigen_df.northing,
input_crs="EPSG:3031",
output_crs="EPSG:4326",
)
# calculated normal gravity at all EIGEN observation locations
eigen_df["normal_gravity"] = boule.WGS84.normal_gravity(
latitude=eigen_df.lat,
height=eigen_df.ellipsoidal_height,
)
# calculate gravity disturbance
eigen_df["disturbance"] = eigen_df.gravity - eigen_df.normal_gravity
# convert to a dataset
eigen_ds = eigen_df.set_index(["northing", "easting"]).to_xarray()
eigen_ds
[4]:
<xarray.Dataset> Size: 479kB
Dimensions: (northing: 94, easting: 127)
Coordinates:
* northing (northing) float64 752B 1.2e+05 1.25e+05 ... 5.85e+05
* easting (easting) float64 1kB 1e+06 1.005e+06 ... 1.63e+06
Data variables:
ellipsoidal_height (northing, easting) float32 48kB 1e+04 1e+04 ... 1e+04
gravity (northing, easting) float32 48kB 9.8e+05 ... 9.797e+05
lon (northing, easting) float64 96kB 83.16 83.19 ... 70.26
lat (northing, easting) float64 96kB -80.75 -80.7 ... -74.16
normal_gravity (northing, easting) float64 96kB 9.8e+05 ... 9.798e+05
disturbance (northing, easting) float64 96kB 38.05 38.12 ... -20.55[5]:
eigen_ds.disturbance.plot()
[5]:
<matplotlib.collections.QuadMesh at 0x7fc9e078cad0>
1.3. Upward continue survey data to same height as gravity grid#
In order to accurately compare the survey gravity data to the grid, the gravity data should be upward continued so it’s at the same altitude.
[6]:
# plot the data
airbornegeo.plotly_points(
data_df[::20],
color_col="height",
robust=True,
size=2,
)
[7]:
blocked_survey = airbornegeo.block_reduce(
data_df,
np.median,
spacing=1000,
reduce_by="distance_along_line",
groupby_column="line",
)
blocked_survey
Segments: 100%|██████████| 100/100 [00:04<00:00, 24.14it/s]
[7]:
| distance_along_line | easting | northing | height | unixtime | grav_disturbance_filt | line | |
|---|---|---|---|---|---|---|---|
| 0 | 477.521406 | 1.000496e+06 | 226310.158049 | 4159.1 | 1.229507e+09 | 49.89 | 1 |
| 1 | 1477.462561 | 1.001484e+06 | 226460.426302 | 4160.3 | 1.229507e+09 | 50.73 | 1 |
| 2 | 2504.019094 | 1.002497e+06 | 226630.494762 | 4156.0 | 1.229507e+09 | 51.09 | 1 |
| 3 | 3518.522095 | 1.003499e+06 | 226786.791629 | 4159.6 | 1.229507e+09 | 51.07 | 1 |
| 4 | 4528.427996 | 1.004498e+06 | 226936.380167 | 4165.7 | 1.229507e+09 | 51.43 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 21373 | 68170.289011 | 1.586103e+06 | 507079.737473 | 2049.2 | 1.230382e+09 | -1.66 | 100 |
| 21374 | 69150.087140 | 1.586259e+06 | 506112.838506 | 2051.8 | 1.230382e+09 | -2.58 | 100 |
| 21375 | 70159.126161 | 1.586438e+06 | 505120.357453 | 2053.1 | 1.230382e+09 | -4.03 | 100 |
| 21376 | 71178.859535 | 1.586603e+06 | 504114.078250 | 2058.5 | 1.230382e+09 | -5.99 | 100 |
| 21377 | 72178.780154 | 1.586775e+06 | 503129.075505 | 2072.0 | 1.230382e+09 | -8.16 | 100 |
21378 rows × 7 columns
[10]:
# fit a set of equivalent sources to each line individually
eqs = airbornegeo.eq_sources_1d(
blocked_survey,
data_column="grav_disturbance_filt",
depth="default",
damping=None,
block_size=1000, # for speed, block reduce sources
groupby_column="line",
)
eqs
Groups: 100%|██████████| 100/100 [00:04<00:00, 24.49it/s]
[10]:
{1: EquivalentSources(block_size=1000),
2: EquivalentSources(block_size=1000),
3: EquivalentSources(block_size=1000),
4: EquivalentSources(block_size=1000),
5: EquivalentSources(block_size=1000),
6: EquivalentSources(block_size=1000),
7: EquivalentSources(block_size=1000),
8: EquivalentSources(block_size=1000),
9: EquivalentSources(block_size=1000),
10: EquivalentSources(block_size=1000),
11: EquivalentSources(block_size=1000),
12: EquivalentSources(block_size=1000),
13: EquivalentSources(block_size=1000),
14: EquivalentSources(block_size=1000),
15: EquivalentSources(block_size=1000),
16: EquivalentSources(block_size=1000),
17: EquivalentSources(block_size=1000),
18: EquivalentSources(block_size=1000),
19: EquivalentSources(block_size=1000),
20: EquivalentSources(block_size=1000),
21: EquivalentSources(block_size=1000),
22: EquivalentSources(block_size=1000),
23: EquivalentSources(block_size=1000),
24: EquivalentSources(block_size=1000),
25: EquivalentSources(block_size=1000),
26: EquivalentSources(block_size=1000),
27: EquivalentSources(block_size=1000),
28: EquivalentSources(block_size=1000),
29: EquivalentSources(block_size=1000),
30: EquivalentSources(block_size=1000),
31: EquivalentSources(block_size=1000),
32: EquivalentSources(block_size=1000),
33: EquivalentSources(block_size=1000),
34: EquivalentSources(block_size=1000),
35: EquivalentSources(block_size=1000),
36: EquivalentSources(block_size=1000),
37: EquivalentSources(block_size=1000),
38: EquivalentSources(block_size=1000),
39: EquivalentSources(block_size=1000),
40: EquivalentSources(block_size=1000),
41: EquivalentSources(block_size=1000),
42: EquivalentSources(block_size=1000),
43: EquivalentSources(block_size=1000),
44: EquivalentSources(block_size=1000),
45: EquivalentSources(block_size=1000),
46: EquivalentSources(block_size=1000),
47: EquivalentSources(block_size=1000),
48: EquivalentSources(block_size=1000),
49: EquivalentSources(block_size=1000),
50: EquivalentSources(block_size=1000),
51: EquivalentSources(block_size=1000),
52: EquivalentSources(block_size=1000),
53: EquivalentSources(block_size=1000),
54: EquivalentSources(block_size=1000),
55: EquivalentSources(block_size=1000),
56: EquivalentSources(block_size=1000),
57: EquivalentSources(block_size=1000),
58: EquivalentSources(block_size=1000),
59: EquivalentSources(block_size=1000),
60: EquivalentSources(block_size=1000),
61: EquivalentSources(block_size=1000),
62: EquivalentSources(block_size=1000),
63: EquivalentSources(block_size=1000),
64: EquivalentSources(block_size=1000),
65: EquivalentSources(block_size=1000),
66: EquivalentSources(block_size=1000),
67: EquivalentSources(block_size=1000),
68: EquivalentSources(block_size=1000),
69: EquivalentSources(block_size=1000),
70: EquivalentSources(block_size=1000),
71: EquivalentSources(block_size=1000),
72: EquivalentSources(block_size=1000),
73: EquivalentSources(block_size=1000),
74: EquivalentSources(block_size=1000),
75: EquivalentSources(block_size=1000),
76: EquivalentSources(block_size=1000),
77: EquivalentSources(block_size=1000),
78: EquivalentSources(block_size=1000),
79: EquivalentSources(block_size=1000),
80: EquivalentSources(block_size=1000),
81: EquivalentSources(block_size=1000),
82: EquivalentSources(block_size=1000),
83: EquivalentSources(block_size=1000),
84: EquivalentSources(block_size=1000),
85: EquivalentSources(block_size=1000),
86: EquivalentSources(block_size=1000),
87: EquivalentSources(block_size=1000),
88: EquivalentSources(block_size=1000),
89: EquivalentSources(block_size=1000),
90: EquivalentSources(block_size=1000),
91: EquivalentSources(block_size=1000),
92: EquivalentSources(block_size=1000),
93: EquivalentSources(block_size=1000),
94: EquivalentSources(block_size=1000),
95: EquivalentSources(block_size=1000),
96: EquivalentSources(block_size=1000),
97: EquivalentSources(block_size=1000),
98: EquivalentSources(block_size=1000),
99: EquivalentSources(block_size=1000),
100: EquivalentSources(block_size=1000)}
[11]:
# upward continue each line to 10 km
blocked_survey["upward_continued_10km"] = airbornegeo.upward_continue_by_line(
blocked_survey,
eqs,
height=10e3,
)
blocked_survey.head()
Groups: 0%| | 0/100 [00:00<?, ?it/s]Groups: 100%|██████████| 100/100 [00:00<00:00, 337.91it/s]
[11]:
| distance_along_line | easting | northing | height | unixtime | grav_disturbance_filt | line | upward_continued_10km | |
|---|---|---|---|---|---|---|---|---|
| 0 | 477.521406 | 1.000496e+06 | 226310.158049 | 4159.1 | 1.229507e+09 | 49.89 | 1 | 40.232278 |
| 1 | 1477.462561 | 1.001484e+06 | 226460.426302 | 4160.3 | 1.229507e+09 | 50.73 | 1 | 40.972657 |
| 2 | 2504.019094 | 1.002497e+06 | 226630.494762 | 4156.0 | 1.229507e+09 | 51.09 | 1 | 41.713109 |
| 3 | 3518.522095 | 1.003499e+06 | 226786.791629 | 4159.6 | 1.229507e+09 | 51.07 | 1 | 42.431563 |
| 4 | 4528.427996 | 1.004498e+06 | 226936.380167 | 4165.7 | 1.229507e+09 | 51.43 | 1 | 43.141623 |
[12]:
# plot the upward continued data
airbornegeo.plotly_points(
blocked_survey,
color_col="upward_continued_10km",
robust=True,
size=2,
)
1.3.1. Sample the satellite gravity grid values into the dataframe#
[13]:
# sample grid along lines
blocked_survey["sampled_grid_values"] = airbornegeo.sample_grid(
blocked_survey,
eigen_ds.disturbance,
coord_names=("easting", "northing"),
)
blocked_survey.head()
grdtrack [WARNING]: Some input points were outside the grid domain(s).
[13]:
| distance_along_line | easting | northing | height | unixtime | grav_disturbance_filt | line | upward_continued_10km | sampled_grid_values | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 477.521406 | 1.000496e+06 | 226310.158049 | 4159.1 | 1.229507e+09 | 49.89 | 1 | 40.232278 | 48.675663 |
| 1 | 1477.462561 | 1.001484e+06 | 226460.426302 | 4160.3 | 1.229507e+09 | 50.73 | 1 | 40.972657 | 48.836891 |
| 2 | 2504.019094 | 1.002497e+06 | 226630.494762 | 4156.0 | 1.229507e+09 | 51.09 | 1 | 41.713109 | 49.005095 |
| 3 | 3518.522095 | 1.003499e+06 | 226786.791629 | 4159.6 | 1.229507e+09 | 51.07 | 1 | 42.431563 | 49.172386 |
| 4 | 4528.427996 | 1.004498e+06 | 226936.380167 | 4165.7 | 1.229507e+09 | 51.43 | 1 | 43.141623 | 49.342525 |
[14]:
# plot the sampled grid values
airbornegeo.plotly_points(
blocked_survey,
color_col="sampled_grid_values",
robust=True,
size=2,
)
[15]:
# plot the difference
blocked_survey["data_to_grid_diff"] = (
blocked_survey.upward_continued_10km - blocked_survey.sampled_grid_values
)
airbornegeo.plotly_points(
blocked_survey,
color_col="data_to_grid_diff",
robust=True,
size=2,
)
1.4. Level the lines to the grid#
[16]:
blocked_survey["levelled_trend_0"] = airbornegeo.level_to_grid(
blocked_survey,
degree=0, # DC shift
data_column="upward_continued_10km",
grid_column="sampled_grid_values",
groupby_column="line",
)
blocked_survey["levelled_trend_1"] = airbornegeo.level_to_grid(
blocked_survey,
degree=1, # DC shift + tilt
data_column="upward_continued_10km",
grid_column="sampled_grid_values",
groupby_column="line",
)
blocked_survey.head()
[16]:
| distance_along_line | easting | northing | height | unixtime | grav_disturbance_filt | line | upward_continued_10km | sampled_grid_values | data_to_grid_diff | levelled_trend_0 | levelled_trend_1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 477.521406 | 1.000496e+06 | 226310.158049 | 4159.1 | 1.229507e+09 | 49.89 | 1 | 40.232278 | 48.675663 | -8.443385 | 42.596650 | 39.485331 |
| 1 | 1477.462561 | 1.001484e+06 | 226460.426302 | 4160.3 | 1.229507e+09 | 50.73 | 1 | 40.972657 | 48.836891 | -7.864234 | 43.337029 | 40.252951 |
| 2 | 2504.019094 | 1.002497e+06 | 226630.494762 | 4156.0 | 1.229507e+09 | 51.09 | 1 | 41.713109 | 49.005095 | -7.291987 | 44.077481 | 41.021368 |
| 3 | 3518.522095 | 1.003499e+06 | 226786.791629 | 4159.6 | 1.229507e+09 | 51.07 | 1 | 42.431563 | 49.172386 | -6.740823 | 44.795935 | 41.767460 |
| 4 | 4528.427996 | 1.004498e+06 | 226936.380167 | 4165.7 | 1.229507e+09 | 51.43 | 1 | 43.141623 | 49.342525 | -6.200902 | 45.505995 | 42.505032 |
[17]:
# plot the levelling correction
blocked_survey["levelling_correction_trend_0"] = (
blocked_survey.upward_continued_10km - blocked_survey.levelled_trend_0
)
airbornegeo.plotly_points(
blocked_survey,
color_col="levelling_correction_trend_0",
robust=True,
size=2,
)
[18]:
# plot the levelling correction
blocked_survey["levelling_correction_trend_1"] = (
blocked_survey.upward_continued_10km - blocked_survey.levelled_trend_1
)
airbornegeo.plotly_points(
blocked_survey,
color_col="levelling_correction_trend_1",
robust=True,
size=2,
)
[19]:
# look at just 1 of the leveled lines
airbornegeo.plotly_profiles(
blocked_survey[blocked_survey.line == 2],
x="distance_along_line",
y=["sampled_grid_values", "upward_continued_10km", "levelled_trend_0"],
)
[20]:
# look at just 1 of the leveled lines
airbornegeo.plotly_profiles(
blocked_survey[blocked_survey.line == 2],
x="distance_along_line",
y=["sampled_grid_values", "upward_continued_10km", "levelled_trend_1"],
)
1.5. Level the entire survey to the grid#
By not supplying the groupby_column argument, instead of levelling each line in 1D, we can level the entire survey in 2D.
[21]:
blocked_survey["levelled_trend_1"] = airbornegeo.level_to_grid(
blocked_survey,
degree=1,
data_column="upward_continued_10km",
grid_column="sampled_grid_values",
)
blocked_survey["levelled_trend_2"] = airbornegeo.level_to_grid(
blocked_survey,
degree=2,
data_column="upward_continued_10km",
grid_column="sampled_grid_values",
)
blocked_survey.head()
[21]:
| distance_along_line | easting | northing | height | unixtime | grav_disturbance_filt | line | upward_continued_10km | sampled_grid_values | data_to_grid_diff | levelled_trend_0 | levelled_trend_1 | levelling_correction_trend_0 | levelling_correction_trend_1 | levelled_trend_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 477.521406 | 1.000496e+06 | 226310.158049 | 4159.1 | 1.229507e+09 | 49.89 | 1 | 40.232278 | 48.675663 | -8.443385 | 42.596650 | 48.066543 | -2.364372 | 0.746947 | 67.674905 |
| 1 | 1477.462561 | 1.001484e+06 | 226460.426302 | 4160.3 | 1.229507e+09 | 50.73 | 1 | 40.972657 | 48.836891 | -7.864234 | 43.337029 | 48.774343 | -2.364372 | 0.719706 | 68.193819 |
| 2 | 2504.019094 | 1.002497e+06 | 226630.494762 | 4156.0 | 1.229507e+09 | 51.09 | 1 | 41.713109 | 49.005095 | -7.291987 | 44.077481 | 49.481169 | -2.364372 | 0.691741 | 68.709263 |
| 3 | 3518.522095 | 1.003499e+06 | 226786.791629 | 4159.6 | 1.229507e+09 | 51.07 | 1 | 42.431563 | 49.172386 | -6.740823 | 44.795935 | 50.166525 | -2.364372 | 0.664103 | 69.204949 |
| 4 | 4528.427996 | 1.004498e+06 | 226936.380167 | 4165.7 | 1.229507e+09 | 51.43 | 1 | 43.141623 | 49.342525 | -6.200902 | 45.505995 | 50.843707 | -2.364372 | 0.636591 | 69.693441 |
[22]:
# plot the levelling correction
blocked_survey["levelling_correction_trend_1"] = (
blocked_survey.upward_continued_10km - blocked_survey.levelled_trend_1
)
airbornegeo.plotly_points(
blocked_survey,
color_col="levelling_correction_trend_1",
robust=True,
size=2,
)
[23]:
# plot the levelling correction
blocked_survey["levelling_correction_trend_2"] = (
blocked_survey.upward_continued_10km - blocked_survey.levelled_trend_2
)
airbornegeo.plotly_points(
blocked_survey,
color_col="levelling_correction_trend_2",
robust=True,
size=2,
)