diff --git a/docs/_toc.yml b/docs/_toc.yml index 92c31edbc..18bb7317b 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -11,6 +11,7 @@ chapters: - file: inversion/regularization - file: inversion/mesh_design - file: inversion/joint_inversion + - file: inversion/depth_of_investigation - file: case_study/introduction sections: - file: case_study/background diff --git a/docs/case_study/airborne_tem.py b/docs/case_study/airborne_tem.py index e75c7a629..157b97f00 100644 --- a/docs/case_study/airborne_tem.py +++ b/docs/case_study/airborne_tem.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/background.py b/docs/case_study/background.py index 873e3fa9b..044f7157a 100644 --- a/docs/case_study/background.py +++ b/docs/case_study/background.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/dc_resistivity.py b/docs/case_study/dc_resistivity.py index ff712ca3e..f1ecf5ef8 100644 --- a/docs/case_study/dc_resistivity.py +++ b/docs/case_study/dc_resistivity.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/gravity.py b/docs/case_study/gravity.py index 8a7f9766e..da717e2f3 100644 --- a/docs/case_study/gravity.py +++ b/docs/case_study/gravity.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/introduction.py b/docs/case_study/introduction.py index d7555c0bc..b643aad1a 100644 --- a/docs/case_study/introduction.py +++ b/docs/case_study/introduction.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/joint_inversion.py b/docs/case_study/joint_inversion.py index ad2e9cebc..24317b63c 100644 --- a/docs/case_study/joint_inversion.py +++ b/docs/case_study/joint_inversion.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/magnetic.py b/docs/case_study/magnetic.py index a00e2018a..c5cc68593 100644 --- a/docs/case_study/magnetic.py +++ b/docs/case_study/magnetic.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/case_study/tipper.py b/docs/case_study/tipper.py index cd0ec0a64..e4dcfa7c1 100644 --- a/docs/case_study/tipper.py +++ b/docs/case_study/tipper.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/inversion/data_misfit.py b/docs/inversion/data_misfit.py index 3475cd5ea..97665429a 100644 --- a/docs/inversion/data_misfit.py +++ b/docs/inversion/data_misfit.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/inversion/depth_of_investigation.ipynb b/docs/inversion/depth_of_investigation.ipynb new file mode 100644 index 000000000..7f32087fc --- /dev/null +++ b/docs/inversion/depth_of_investigation.ipynb @@ -0,0 +1,127 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c6c8ad14-6e0f-405b-8356-3b9b6ab049f8", + "metadata": {}, + "source": [ + "# Depth of Investigation\n", + "\n", + "In geophysics, \"depth of investigation\" refers to the maximum depth below the surface from which a geophysical survey can reliably measure. It depends on factors like the survey design and physical properties of the subsurface material. Several strategies have been proposed to assess uncertainties in models recovered from inversion. {cite:t}`nabighian_1989` used a skin depth approach for electromagnetic surveys, assuming a background halfspace resistivity. {cite:t}`li_1999` implemented a cut-off value based on two inverted models obtained with slightly different assumptions. {cite:t}`christiansen_2012` proposed a mask based on the sum-square of sensitivities to estimate a volume of low confidence. In the following, we discuss the algorithm and implementation of the sensitivity cutoff strategy." + ] + }, + { + "cell_type": "markdown", + "id": "c13ffcec-8d95-4ee9-876f-9aa92e2c534a", + "metadata": {}, + "source": [ + "## Sensitivities\n", + "\n", + "The sensitivity matrix is calculated as part of the optimization problem solved by SimPEG while inverting geophysical data. The sensitivity matrix represents the degree to which each predicted datum changes with respect to a perturbation in each model cell. It is given in matrix form by\n", + "\n", + "$$\n", + "\\mathbf{J} = \\frac{\\mathbf\\partial{F}(\\mathbf{m})}{\\partial{\\mathbf{m}}}\n", + "$$\n", + "\n", + "where $\\mathbf{m}$ is the model vector, and $F(\\mathbf{m})$ represents the forward modelling operation as a function of the model. The sensitivity matrix $\\mathbf{J}$ is a dense array with dimensions $N\\times M$, where $N$ is the number of data and $M$ are the number of mesh cells.\n", + "\n", + "The depth of investigation mask is a property of the cells of the mesh only so the rows of the sensitivity matrix (data) are sum-square normalized as follows.\n", + "\n", + "$$\n", + "\\mathbf{J}_{norm} = \\left[\\sum_{n=1}^{N}\\left(\\frac{\\mathbf{J}_{n:}}{w_n}\\right)^{2}\\right]^{(1/2)}\n", + "$$\n", + "\n", + "where $w_n$ are the data uncertainties associated with the $n^{th}$ datum.\n", + "\n", + "The resulting vector $J_{norm}$ can then be thought of as the degree to which the aggregate data changes due to a small perturbation in each model cell. The depth of investigation mask is then computed by thresholding those sensitivities" + ] + }, + { + "cell_type": "markdown", + "id": "d8bb85c5-5e63-4617-ab6b-a7b6536c1d2c", + "metadata": {}, + "source": [ + "## Thresholding\n", + "\n", + "The depth of investigation can be estimated by assigning a threshold on the sum-squared sensitivity vector. This can be done as a percentile, percentage, or log-percentage. In the percentile method, the mask is formed by eliminating all cells in which the sensitivity falls below the lowest $n$% of the number of data where $n$ is the chosen cutoff. In the percent method the data are transformed into a percentage of the largest value\n", + "\n", + "$$\n", + "d_{scaled} = \\frac{100 \\cdot d}{max(d)}\n", + "$$\n", + "\n", + "and the mask is formed by eliminating all cells in which the sensitivity falls below the lowest $n$% of the data values where $n$ is the chosen cutoff. Finally, the log-percent mask transforms the data into log-space before carrying out the percentage thresholding described above." + ] + }, + { + "cell_type": "markdown", + "id": "83b4ae8f-5509-40b1-98c2-39acefba438c", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "The depth of investigation methods based on sensitivity cutoffs described above are exposed to Geoscience ANALYST Pro Geophysics users through a ui.json interface. In order to save the sensitivities during a SimPEG inversion, the 'Save sensitivities' option must be selected from the 'Optional parameters' tab of the SimPEG inversion ui.json window.\n", + "\n", + "![save_sensitivities](./images/save_sensitivities.png)\n", + "\n", + "This will result in a new model generated and saved into the computational mesh at each iteration.\n", + "\n", + "![sensitivity_models](./images/sensitivity_models.png)\n", + "\n", + "The ui.json interface allows the user to select a mesh from the **simpeg-drivers** result and any of the generated sensitivity models, a cutoff threshold, method, and optional name for the output.\n", + "\n", + "![interface](./images/uijson.png)\n", + "\n", + "The cutoff methods are selectable in the dropdown\n", + "\n", + "![cutoff_options](./images/cutoff_options.png)\n", + "\n", + "Whatever the options, the application will create a sensitivity cutoff mask saved on the mesh\n", + "\n", + "![mask](./images/sensitivity_mask.png)\n", + "\n", + "which can then be applied to any of the iterations to show only the cells that exceeded the sensitivity threshold.\n", + "\n", + "![apply_mask](./images/apply_mask.png)\n", + "\n", + "![masked_model](./images/masked_model.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1079da0d-4ea2-4f00-b957-92199dd39cdb", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "920f0637-4028-40c7-b2c6-5976f5e90afd", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/inversion/depth_of_investigation.py b/docs/inversion/depth_of_investigation.py new file mode 100644 index 000000000..8a9cc3b89 --- /dev/null +++ b/docs/inversion/depth_of_investigation.py @@ -0,0 +1,75 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.16.7 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# # Depth of Investigation +# +# In geophysics, "depth of investigation" refers to the maximum depth below the surface from which a geophysical survey can reliably measure. It depends on factors like the survey design and physical properties of the subsurface material. Several strategies have been proposed to assess uncertainties in models recovered from inversion. {cite:t}`nabighian_1989` used a skin depth approach for electromagnetic surveys, assuming a background halfspace resistivity. {cite:t}`li_1999` implemented a cut-off value based on two inverted models obtained with slightly different assumptions. {cite:t}`christiansen_2012` proposed a mask based on the sum-square of sensitivities to estimate a volume of low confidence. In the following, we discuss the algorithm and implementation of the sensitivity cutoff strategy. + +# ## Sensitivities +# +# The sensitivity matrix is calculated as part of the optimization problem solved by SimPEG while inverting geophysical data. The sensitivity matrix represents the degree to which each predicted datum changes with respect to a perturbation in each model cell. It is given in matrix form by +# +# $$ +# \mathbf{J} = \frac{\mathbf\partial{F}(\mathbf{m})}{\partial{\mathbf{m}}} +# $$ +# +# where $\mathbf{m}$ is the model vector, and $F(\mathbf{m})$ represents the forward modelling operation as a function of the model. The sensitivity matrix $\mathbf{J}$ is a dense array with dimensions $N\times M$, where $N$ is the number of data and $M$ are the number of mesh cells. +# +# The depth of investigation mask is a property of the cells of the mesh only so the rows of the sensitivity matrix (data) are sum-square normalized as follows. +# +# $$ +# \mathbf{J}_{norm} = \left[\sum_{n=1}^{N}\left(\frac{\mathbf{J}_{n:}}{w_n}\right)^{2}\right]^{(1/2)} +# $$ +# +# where $w_n$ are the data uncertainties associated with the $n^{th}$ datum. +# +# The resulting vector $J_{norm}$ can then be thought of as the degree to which the aggregate data changes due to a small perturbation in each model cell. The depth of investigation mask is then computed by thresholding those sensitivities + +# ## Thresholding +# +# The depth of investigation can be estimated by assigning a threshold on the sum-squared sensitivity vector. This can be done as a percentile, percentage, or log-percentage. In the percentile method, the mask is formed by eliminating all cells in which the sensitivity falls below the lowest $n$% of the number of data where $n$ is the chosen cutoff. In the percent method the data are transformed into a percentage of the largest value +# +# $$ +# d_{scaled} = \frac{100 \cdot d}{max(d)} +# $$ +# +# and the mask is formed by eliminating all cells in which the sensitivity falls below the lowest $n$% of the data values where $n$ is the chosen cutoff. Finally, the log-percent mask transforms the data into log-space before carrying out the percentage thresholding described above. + +# ## Usage +# +# The depth of investigation methods based on sensitivity cutoffs described above are exposed to Geoscience ANALYST Pro Geophysics users through a ui.json interface. In order to save the sensitivities during a SimPEG inversion, the 'Save sensitivities' option must be selected from the 'Optional parameters' tab of the SimPEG inversion ui.json window. +# +# ![save_sensitivities](./images/save_sensitivities.png) +# +# This will result in a new model generated and saved into the computational mesh at each iteration. +# +# ![sensitivity_models](./images/sensitivity_models.png) +# +# The ui.json interface allows the user to select a mesh from the **simpeg-drivers** result and any of the generated sensitivity models, a cutoff threshold, method, and optional name for the output. +# +# ![interface](./images/uijson.png) +# +# The cutoff methods are selectable in the dropdown +# +# ![cutoff_options](./images/cutoff_options.png) +# +# Whatever the options, the application will create a sensitivity cutoff mask saved on the mesh +# +# ![mask](./images/sensitivity_mask.png) +# +# which can then be applied to any of the iterations to show only the cells that exceeded the sensitivity threshold. +# +# ![apply_mask](./images/apply_mask.png) +# +# ![masked_model](./images/masked_model.png) diff --git a/docs/inversion/fundamentals.ipynb b/docs/inversion/fundamentals.ipynb index b9cd8b5f2..0df0b67c9 100644 --- a/docs/inversion/fundamentals.ipynb +++ b/docs/inversion/fundamentals.ipynb @@ -291,6 +291,18 @@ "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" } }, "nbformat": 4, diff --git a/docs/inversion/fundamentals.py b/docs/inversion/fundamentals.py index 608b74281..801126dd6 100644 --- a/docs/inversion/fundamentals.py +++ b/docs/inversion/fundamentals.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/inversion/images/apply_mask.png b/docs/inversion/images/apply_mask.png new file mode 100644 index 000000000..29af72f68 Binary files /dev/null and b/docs/inversion/images/apply_mask.png differ diff --git a/docs/inversion/images/cutoff_options.png b/docs/inversion/images/cutoff_options.png new file mode 100644 index 000000000..d9e0ba51d Binary files /dev/null and b/docs/inversion/images/cutoff_options.png differ diff --git a/docs/inversion/images/masked_model.png b/docs/inversion/images/masked_model.png new file mode 100644 index 000000000..606726cc3 Binary files /dev/null and b/docs/inversion/images/masked_model.png differ diff --git a/docs/inversion/images/save_sensitivities.png b/docs/inversion/images/save_sensitivities.png new file mode 100644 index 000000000..df92780c3 Binary files /dev/null and b/docs/inversion/images/save_sensitivities.png differ diff --git a/docs/inversion/images/sensitivity_mask.png b/docs/inversion/images/sensitivity_mask.png new file mode 100644 index 000000000..5b2155cbb Binary files /dev/null and b/docs/inversion/images/sensitivity_mask.png differ diff --git a/docs/inversion/images/sensitivity_models.png b/docs/inversion/images/sensitivity_models.png new file mode 100644 index 000000000..dac74157a Binary files /dev/null and b/docs/inversion/images/sensitivity_models.png differ diff --git a/docs/inversion/images/uijson.png b/docs/inversion/images/uijson.png new file mode 100644 index 000000000..5de440c47 Binary files /dev/null and b/docs/inversion/images/uijson.png differ diff --git a/docs/inversion/index.ipynb b/docs/inversion/index.ipynb index e3f6ce27c..4673b1b9e 100644 --- a/docs/inversion/index.ipynb +++ b/docs/inversion/index.ipynb @@ -20,8 +20,18 @@ "\n", "- [Joint/Coupling Strategies](joint_inversion.ipynb): Inverting multiple geophysical surveys.\n", "\n", + "- [Depth of Investigation](depth_of_investigation.ipynb): Using sensitivities to set depth extents\n", + "\n", "![inversion_ui](./images/inversion_ui.png)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a67ee4b4-0500-4414-917b-9bce3ce1e801", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -29,6 +39,18 @@ "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" } }, "nbformat": 4, diff --git a/docs/inversion/index.py b/docs/inversion/index.py index 149a8ae25..a1acbb68d 100644 --- a/docs/inversion/index.py +++ b/docs/inversion/index.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python @@ -14,11 +14,7 @@ # # Fundamentals # -# This module documents the use of [SimPEG](simpeg.xyz) for geophysical data inversion with user-interface (UIjson) made -# available through the [Mira Geoscience-geoapps](https://mirageoscience-geoapps.readthedocs-hosted.com/) project. -# While the code itself has its own documentation, there is a need to demonstrate the effect of parameters controlling -# the inversion. -# This document is meant to be a reference guide with practical examples to help practitioners with their inversion work. +# This module documents the use of [SimPEG](simpeg.xyz) for geophysical data inversion with user-interface (UIjson) made available through the [Mira Geoscience-geoapps](https://mirageoscience-geoapps.readthedocs-hosted.com/) project. While the code itself has its own documentation, there is a need to demonstrate the effect of parameters controlling the inversion. This document is meant to be a reference guide with practical examples to help practitioners with their inversion work. # # # - [Background](fundamentals.ipynb): An overview of the inversion framework. @@ -31,4 +27,6 @@ # # - [Joint/Coupling Strategies](joint_inversion.ipynb): Inverting multiple geophysical surveys. # +# - [Depth of Investigation](depth_of_investigation.ipynb): Using sensitivities to set depth extents +# # ![inversion_ui](./images/inversion_ui.png) diff --git a/docs/inversion/joint_inversion.ipynb b/docs/inversion/joint_inversion.ipynb index b914998fb..529c9b1a0 100644 --- a/docs/inversion/joint_inversion.ipynb +++ b/docs/inversion/joint_inversion.ipynb @@ -136,6 +136,18 @@ "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" } }, "nbformat": 4, diff --git a/docs/inversion/joint_inversion.py b/docs/inversion/joint_inversion.py index 87ef0a3be..bf37d31dc 100644 --- a/docs/inversion/joint_inversion.py +++ b/docs/inversion/joint_inversion.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/inversion/mesh_design.py b/docs/inversion/mesh_design.py index 38dcae533..8508fe050 100644 --- a/docs/inversion/mesh_design.py +++ b/docs/inversion/mesh_design.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/inversion/regularization.py b/docs/inversion/regularization.py index a1c575ea4..48fe3f6ff 100644 --- a/docs/inversion/regularization.py +++ b/docs/inversion/regularization.py @@ -5,7 +5,7 @@ # extension: .py # format_name: light # format_version: '1.5' -# jupytext_version: 1.16.2 +# jupytext_version: 1.16.7 # kernelspec: # display_name: Python 3 (ipykernel) # language: python diff --git a/docs/references.bib b/docs/references.bib index de256cfbe..11dfaadd4 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -1,6 +1,14 @@ --- --- +@book{nabighian_1989, + title={Time-domain electromagnetic prospecting methods}, + volume={2}, + publisher={Society of Exploration Geophysicists}, + author={Nabighian, M. N., and Macnae, J. C.}, + year={1989}, +} + @book{nabigian_1991, title = {Time Domain Electromagnetic Prospecting Methods}, @@ -101,3 +109,25 @@ @article{fournier_2019 pages = {268-282}, doi = {10.1093/gji/ggz156} } + + +@article{li_1999, + title = {Estimating depth of investigation in DC resistivity and IP surveys}, + volume = {2}, + journal = {Geophysics}, + issue = {64}, + author = {Li Y., and D. W. Oldenburg}, + year = {1999}, + pages = {403-416}, +} + + +@article{christiansen_2012, + title={A global measure for depth of investigation.}, + volume={4}, + issue={77}, + journal={Geophysics}, + author={Christiansen A.V. and Auken E.}, + year={2012}, + pages={WB171–WB177} +}