question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

3D gravity inversion of prism layers

See original GitHub issue

Description of the desired feature:

I’m working on a 3D gravity inversion for my Ph.D. to model the bathymetry beneath a float ice shelf. I’m using a vertical-prisms approach, and I’m mostly interested in a geometry inversion, where each prism’s density remains unchanged, but the prisms’ tops or bottoms are updated to minimize the observed gravity - forward gravity misfit. While this inversion is in the early stages, I’m already using several harmonica tools so I think it would be great to include a fully supported inversion module in harmonica.

Below is a description of my specific use case, a list of what I already have coded, and ideas for additional features to add.

Current state of inversion

Model setup

  • input an arbitrary number of layer topographies (i.e. ice surface, ice base, bathymetry, basement, Moho)

  • create a layer of vertical prisms between each set of grids

    • harmonica.prism_layer()
      • example: for water layer: surface = ice base, reference = bathymetry
    • assign a density to each prism (currently using constant density within each layer)
      • prism_layer(... properties={'density':water_density)
    • allow different cell sizes between layers
      • if lower grid cell size doesn’t match upper grid’s, use pygmt.grdtrack() to sample the lower grids values at the grid’s prism locations
  • calculate forward gravity of each layer

  • calculate gravity misfit

    • add forward gravities for all layers, and subtract from observed gravity

run geometry inversion

  • designate an active_layer ex: active_layer='bathymetry_prisms'
  • each iteration of the inversion will yield a surface_correction
    • set a max surface change per iteration
    • applied to the active_layer tops, as well as the bottoms of the layer above active_layer
    • recalculate the forward gravity of these 2 updated layers.
      • recalculte the misfit with these updated forward gravities before starting the 2nd iteration
  • currently this inversion works by finding the least-squares solution to the matrix equation Ax=b, where A is the Jacobian matrix of the vertical derivative of gravity acceleration, and b is the initial misfit between observed and forward gravity.
    • the least squares solution is found with scipy.sparse.linalg.lsqr()
    • the vertical derivative is approximated with the Hammer approximation of prisms, using annuluses as opposed to prisms.

This is just the method I already had implemented, suggest from my advisor, but I’m happy to alter this and would appreciate suggestions.

Features to add:

  • enforce constraint point (points of known bathymetry) by multiplying surface_correction by a constraints grid * 0’s at constraints, linearly increasing to 1’s at a specified distance from nearest constraints * scipy.spatial.cKDTree.query() to get grid of minimum distances to constraints * gmt grdmath LE and GE to set constraints to 0 and other points to 1 * gmt grdblend to merge the clipped grids * and rioxarray.interpolate_na() to linearly interpolate between 0 and 1

  • allow irregular grid spacing within each layer (i.e. grid spacing increase for a buffer zone, outside the main area of interest)

  • allow depth-dependent density within each prism. Only for the forward modelling

  • density inversion

    • similar to the above geometry inversion, but updated each prism’s density to minimize the misfit.
    • this is useful for the initial setup of the geometry inversion, allow the regional field to be accounted for in a layers density, leaving only the residual field left to invert on.

Discussion

  • Do we want to refer to this type of inversion as a geometry inversion, structural inversion, boundary inversion or something else? There doesn’t seem to be much of a consensus on what you call this.
  • I’m pretty new to inversion theory, so if anyone has recommendations on where to learn this material please let me know!
  • I think some of the packages I have linked above could be replaced with Fatianado packages to keep dependencies minimal.
  • All the inputs in my inversion are stored in a nested dictionary layers, with a dictionary for each layer, which includes a .nc filename, a resolution, constant density values, and a dataframe and dataset for each layer. This makes it easy to apply functions to an arbitrary number of input layers (example below). If there’s a better method for this let me know.
layers_list =[ 'ice', 'water', 'bathymetry', 'basement',]
spacing_list = [10e3, 10e3, 10e3, 10e3,]
rho_list = [ 920, 1030, 2600, 2800,]
fname_list=[
            '../inversion_layers/BedMachine_surface_filled_inv.nc',
            '../inversion_layers/BedMachine_icebase_filled_inv.nc',
            ../inversion_layers/BedMachine_bed_inv.nc',
            '../inversion_layers/ROSETTA_basement_BedMachine_bed_inv.nc',
            ]

# create nested dictionary of layer properties 
layers = {j:{'spacing':spacing_list[i], 
            'fname':fname_list[i], 
            'rho':rho_list[i]} for i, j in enumerate(layers_list)}

for i, j in enumerate(layers_list):
    # load each .nc as a xarray dataset
    layers[j]['grid']=xr.load_dataset(layers[j]['fname'])
    # create prism layer between each set of input grids
    layers[j]['prisms']=hm.prism_layer(
        coordinates=(list(layers[j]['grid'].x), list(layers[j]['grid'].y)),   
        surface=layers[j]['grid'].z, 
        reference=layers[reversed_layers_list[i-1]]['grid'].z,
        properties={'density':layers[j]['grid'].density})

# calculate forward gravity for each prism layer
for k, v in layers.items():
    df_grav[f'{k}_forward_grav'] = v['prisms'].prism_layer.gravity(
        coordinates = (df_grav.x, df_grav.y, df_grav.z),
        field = 'g_z', progressbar=True)
  • to remove edge effects, I’m using a buffer, currently 200km in each direction, as shown in the below figures by the black square. You can see the edge effects in the second to last panel, which is the lowest layer of prisms. I am doing all calculations on the full region, but only showing results within the buffer zone. Does this seem like a reasonable approach? It adds a lot of prisms, but this could be mitigated by using discretize.TreeMesh(). for the buffer region. I’m unsure if I should be calculating misfit for the whole region or just the inside. Also, I’m not sure if I should invert the prisms just within the inside, or for the whole region.

Are you willing to help implement and maintain this feature?

Yes I’m happy and excited to implement this, but I’m in the last year of my Ph.D. so the outcome of this inversion is more pressing than the implementation of the inversion into Harmonica. Hopefully they can happen simultaneously. @santisoler has suggested looking at both the old Fatiando module fatiando.inversion and some code related to Uieda and Barbosa 2016 for inspiration on how to implement this, which I will do.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
craigmillernzcommented, Aug 17, 2022

Matt you might find this useful for general inversion theory - from UBC. This used to be a standalone website, but it looks like you now have to download. Follow the instructions on the page. https://gif.eos.ubc.ca/IAG

0reactions
santisolercommented, Dec 9, 2022

Hi @mdtanker. I’m glad you are getting into the rabbit hole of inversion, it’s a fun one haha!

The good news here is that you are already using some simple kind of smallness regularization. The verde.base.least_squares function relies on the sklearn.linear_model.Ridge class that minimizes the objective function:

$$ \phi(\mathbf{m}) = \lVert \mathbf{d}_\text{obs} - \mathbf{G} \mathbf{m} \rVert^2 + \alpha \lVert \mathbf{m} \rVert^2 $$

The second term that involves the alpha (damping parameter) and the l2 norm of the parameters is a Tikhonov zeroth order regularization. SimPEG implements the smallness by defining this regularization term as the l2 norm of the difference between the model parameters and the reference model. In this reference model you can include you constrains:

$$ \phi(\mathbf{m}) = \lVert \mathbf{d}\text{obs} - \mathbf{G} \mathbf{m} \rVert^2 + \alpha \lVert \mathbf{m} - \mathbf{m}\text{ref} \rVert^2 $$

Here’s a nice chapter to get into these topics: https://pubs.geoscienceworld.org/books/book/2111/chapter-abstract/114879875/Inversion-for-Applied-Geophysics-A-Tutorial?redirectedFrom=fulltext

The problem is that you won’t be able to use the Ridge class for defining a reference model and you’ll probably have to write you own implementation of the regularization.

There are also some lecture notes from @leouieda and @birocoles. They have really nice stuff there, particularly if you need to implement this stuff: https://www.semanticscholar.org/paper/Tópicos-de-inversão-em-geofísica-OliveiraVanderlei-Leonardo/fd651157443f37ad603057a10d683322ff123263

Read more comments on GitHub >

github_iconTop Results From Across the Web

3D Gravity Inversion on Unstructured Grids - MDPI
Conventionally, gravity forward modeling is implemented by dividing the subsurface into a large number of rectangular prisms, and the gravity ...
Read more >
3D gravity inversion through an adaptive-learning ... - CiteSeerX
We have developed a gravity inversion method to estimate a 3D density-contrast distribution producing strongly inter- fering gravity anomalies.
Read more >
3D inversion of gravity data with unstructured mesh and least ...
Here an isoparametric finite-element (FE) methodology is used to design an unstructured mesh for use in 3D inverse modeling of gravity data. The...
Read more >
DecNet: Decomposition network for 3D gravity inversion
The position and residual density of these prisms are listed in Table 2. Figure 5 shows the 3D model and the corresponding gravity...
Read more >
3D Modeling and Inversion of Gravity Data in Exploration Scale
Conventional gravity inversion is based on discretizing the earth model into a set of rectangular prisms, and considering each prism has a constant...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found