diff --git a/docs/_toc.yml b/docs/_toc.yml index 92c31edb..18bb7317 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 e75c7a62..157b97f0 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 873e3fa9..044f7157 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 ff712ca3..f1ecf5ef 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 8a7f9766..da717e2f 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 d7555c0b..b643aad1 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 ad2e9ceb..24317b63 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 a00e2018..c5cc6859 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 cd0ec0a6..e4dcfa7c 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 3475cd5e..97665429 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 00000000..7f32087f --- /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 00000000..8a9cc3b8 --- /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 b9cd8b5f..0df0b67c 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 608b7428..801126dd 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 00000000..29af72f6 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 00000000..d9e0ba51 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 00000000..606726cc 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 00000000..df92780c 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 00000000..5b2155cb 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 00000000..dac74157 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 00000000..5de440c4 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 e3f6ce27..4673b1b9 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 149a8ae2..a1acbb68 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 b914998f..529c9b1a 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 87ef0a3b..bf37d31d 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 38dcae53..8508fe05 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 a1c575ea..48fe3f6f 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 de256cfb..11dfaadd 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} +}