├── notebooks
├── utilities
│ ├── __init__.py
│ └── gex_parser.py
└── live-skytem-inversion.ipynb
├── environment.yml
├── LICENSE
├── .gitignore
├── data
└── 20170606_337m2_Cal_DualWaveform_60Hz_414_412_418.gex
└── README.md
/notebooks/utilities/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: simpeg-segns2024
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - python=3.11.*
6 | - pip
7 | - numpy==1.23.*
8 | - simpeg==0.20.*
9 | - discretize==0.10.*
10 | - matplotlib==3.7.*
11 | - pooch==1.8.*
12 | - pandas==2.2.*
13 | - ipython
14 | - jupyter
15 | - jupyterlab
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 SimPEG
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/notebooks/utilities/gex_parser.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import re
3 |
4 | def parse_gex_file(fname):
5 | # build up a dictionary of details
6 | loop_points = []
7 | hm_wave = []
8 | lm_wave = []
9 | gate_times = []
10 | current_section = None
11 | general_info = {}
12 | channel_info = []
13 | channel_ids = []
14 | with open(fname, 'r') as f:
15 | for line in f:
16 | if line[-1] == '\n':
17 | line = line[:-1]
18 | if "[General]" in line:
19 | current_section = "General"
20 | info = general_info
21 | elif "[Channel" in line:
22 | current_section = "Channel"
23 | id_search = re.search("\d+", line).span()
24 | channel_id = int(line[id_search[0]:id_search[1]])
25 | if channel_id not in channel_ids:
26 | channel_ids.append(channel_id)
27 | channel_info.append({})
28 | info = channel_info[channel_id-1]
29 | line = line.split("=")
30 | if len(line)==2:
31 | description, data = line
32 | try:
33 | data = np.asarray(data.split(), dtype=float)
34 | if len(data) == 1:
35 | data = data[0]
36 | if int(data) == data:
37 | data = int(data)
38 | except:
39 | pass
40 |
41 | if (match:= re.search("TxLoopPoint", description)) is not None:
42 | # The Tx points will always be listed in order
43 | loop_points.append(data)
44 | elif (match:= re.search("Waveform\w+Point\d+", description)) is not None:
45 | if "LM" in description:
46 | lm_wave.append(data)
47 | if "HM" in description:
48 | hm_wave.append(data)
49 | elif (match:= re.search("GateTime\d+", description)) is not None:
50 | gate_times.append(data)
51 | else:
52 | try:
53 | data = np.asarray(data.split(), dtype=float)
54 | if len(data) == 1:
55 | data = data[0]
56 | except:
57 | pass
58 | info[description] = data
59 |
60 | general_info['TxLoopPoints'] = np.asarray(loop_points)
61 | hm_wave = np.asarray(hm_wave)
62 | waveforms = {}
63 | if len(hm_wave) > 0:
64 | wave = {'time':hm_wave[:, 0], 'form':hm_wave[:, 1]}
65 | waveforms['HM'] = wave
66 | lm_wave = np.asarray(lm_wave)
67 | if len(lm_wave) > 0:
68 | wave = {'time':lm_wave[:, 0], 'form':lm_wave[:, 1]}
69 | waveforms['LM'] = wave
70 | general_info['Waveforms'] = waveforms
71 | gate_times = np.asarray(gate_times)
72 | if len(gate_times) > 0:
73 | times = {}
74 | times['start'] = gate_times[:, 1]
75 | times['end'] = gate_times[:, 2]
76 | times['center'] = gate_times[:, 0]
77 | general_info['GateTimes'] = times
78 | info = {}
79 | info["General"] = general_info
80 | for channel in channel_ids:
81 | info[f"Channel{channel}"] = channel_info[channel-1]
82 | return info
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | .idea/
161 |
--------------------------------------------------------------------------------
/data/20170606_337m2_Cal_DualWaveform_60Hz_414_412_418.gex:
--------------------------------------------------------------------------------
1 | /Ge2 file for TX22. Monterey, Wyoming and Colorado. 304 60Hz
2 |
3 | [General]
4 |
5 | Description=Test geometry file
6 |
7 | GPSDifferentialPosition1= 11.68 2.79 -0.16
8 | GPSPosition1= 11.68 2.79 -0.16
9 | GPSPosition2= 10.51 3.95 -0.16
10 | AltimeterPosition1= 12.94 1.79 -0.12
11 | AltimeterPosition2= 12.94 -1.79 -0.12
12 | InclinometerPosition1= 12.79 1.64 -0.12
13 | InclinometerPosition2= 12.79 1.64 -0.12
14 | RxCoilPosition1= -13.25 0.00 -2.00
15 |
16 | LoopType=72
17 |
18 | FrontGateDelay=2.5E-06
19 | TxLoopArea=337.04
20 |
21 | TxLoopPoint1= -12.64 -2.13
22 | TxLoopPoint2= -6.15 -8.59
23 | TxLoopPoint3= 5.74 -8.59
24 | TxLoopPoint4= 11.13 -3.19
25 | TxLoopPoint5= 11.13 3.19
26 | TxLoopPoint6= 5.74 8.59
27 | TxLoopPoint7= -6.15 8.59
28 | TxLoopPoint8= -12.64 2.13
29 |
30 |
31 | NumberOfTurnsLM=1
32 | NumberOfTurnsHM=4
33 |
34 | WaveformLMPoint01= -3.1810E-03 0.0000E+00
35 | WaveformLMPoint02= -3.1356E-03 -7.3066E-02
36 | WaveformLMPoint03= -3.0120E-03 -2.0314E-01
37 | WaveformLMPoint04= -2.8327E-03 -3.9102E-01
38 | WaveformLMPoint05= -2.6918E-03 -5.5482E-01
39 | WaveformLMPoint06= -2.5540E-03 -7.3949E-01
40 | WaveformLMPoint07= -2.3810E-03 -1.0000E+00
41 | WaveformLMPoint08= -2.3809E-03 -9.8141E-01
42 | WaveformLMPoint09= -2.3807E-03 -9.1650E-01
43 | WaveformLMPoint10= -2.3805E-03 -8.2811E-01
44 | WaveformLMPoint11= -2.3800E-03 -5.4882E-01
45 | WaveformLMPoint12= -2.3798E-03 -4.5553E-01
46 | WaveformLMPoint13= -2.3796E-03 -3.7384E-01
47 | WaveformLMPoint14= -2.3794E-03 -3.0718E-01
48 | WaveformLMPoint15= -2.3792E-03 -2.4789E-01
49 | WaveformLMPoint16= -2.3788E-03 -1.5292E-01
50 | WaveformLMPoint17= -2.3785E-03 -1.1280E-01
51 | WaveformLMPoint18= -2.3782E-03 -7.9576E-02
52 | WaveformLMPoint19= -2.3775E-03 -3.4258E-02
53 | WaveformLMPoint20= -2.3767E-03 -1.0385E-02
54 | WaveformLMPoint21= -2.3753E-03 0.0000E+00
55 | WaveformLMPoint22= -8.0000E-04 0.0000E+00
56 | WaveformLMPoint23= -7.5465E-04 7.3066E-02
57 | WaveformLMPoint24= -6.3103E-04 2.0314E-01
58 | WaveformLMPoint25= -4.5168E-04 3.9102E-01
59 | WaveformLMPoint26= -3.1084E-04 5.5482E-01
60 | WaveformLMPoint27= -1.7304E-04 7.3949E-01
61 | WaveformLMPoint28= 0.0000E+00 1.0000E+00
62 | WaveformLMPoint29= 1.4000E-07 9.8141E-01
63 | WaveformLMPoint30= 3.2000E-07 9.1650E-01
64 | WaveformLMPoint31= 5.0000E-07 8.2811E-01
65 | WaveformLMPoint32= 1.0200E-06 5.4882E-01
66 | WaveformLMPoint33= 1.2200E-06 4.5553E-01
67 | WaveformLMPoint34= 1.4200E-06 3.7384E-01
68 | WaveformLMPoint35= 1.6100E-06 3.0718E-01
69 | WaveformLMPoint36= 1.8100E-06 2.4789E-01
70 | WaveformLMPoint37= 2.2400E-06 1.5292E-01
71 | WaveformLMPoint38= 2.5000E-06 1.1280E-01
72 | WaveformLMPoint39= 2.7900E-06 7.9576E-02
73 | WaveformLMPoint40= 3.4600E-06 3.4258E-02
74 | WaveformLMPoint41= 4.3300E-06 1.0385E-02
75 | WaveformLMPoint42= 5.6900E-06 0.0000E+00
76 |
77 | WaveformHMPoint01= -3.22220E-002 -0.00000E+000
78 | WaveformHMPoint02= -3.17433E-002 -1.51752E-001
79 | WaveformHMPoint03= -3.11906E-002 -3.04307E-001
80 | WaveformHMPoint04= -3.04936E-002 -4.61679E-001
81 | WaveformHMPoint05= -2.96885E-002 -5.96569E-001
82 | WaveformHMPoint06= -2.87993E-002 -7.10584E-001
83 | WaveformHMPoint07= -2.77539E-002 -8.00511E-001
84 | WaveformHMPoint08= -2.65282E-002 -8.79197E-001
85 | WaveformHMPoint09= -2.52425E-002 -9.35401E-001
86 | WaveformHMPoint10= -2.40889E-002 -9.67518E-001
87 | WaveformHMPoint11= -2.22220E-002 -1.00000E+000
88 | WaveformHMPoint12= -2.22210E-002 -9.93980E-001
89 | WaveformHMPoint13= -2.21770E-002 -3.63222E-002
90 | WaveformHMPoint14= -2.21758E-002 -1.66204E-002
91 | WaveformHMPoint15= -2.21750E-002 -8.13869E-003
92 | WaveformHMPoint16= -2.21743E-002 -4.25547E-003
93 | WaveformHMPoint17= -2.21727E-002 -2.51825E-003
94 | WaveformHMPoint18= -2.21714E-002 -1.29197E-003
95 | WaveformHMPoint19= -2.21700E-002 -0.00000E+000
96 | WaveformHMPoint20= -1.00000E-002 0.00000E+000
97 | WaveformHMPoint21= -9.52132E-003 1.51752E-001
98 | WaveformHMPoint22= -8.96857E-003 3.04307E-001
99 | WaveformHMPoint23= -8.27161E-003 4.61679E-001
100 | WaveformHMPoint24= -7.46652E-003 5.96569E-001
101 | WaveformHMPoint25= -6.57730E-003 7.10584E-001
102 | WaveformHMPoint26= -5.53188E-003 8.00511E-001
103 | WaveformHMPoint27= -4.30620E-003 8.79197E-001
104 | WaveformHMPoint28= -3.02045E-003 9.35401E-001
105 | WaveformHMPoint29= -1.86688E-003 9.67518E-001
106 | WaveformHMPoint30= 0.00000E+000 1.00000E+000
107 | WaveformHMPoint31= 9.82349E-007 9.93980E-001
108 | WaveformHMPoint32= 4.49657E-005 3.63222E-002
109 | WaveformHMPoint33= 4.61814E-005 1.66204E-002
110 | WaveformHMPoint34= 4.70129E-005 8.13869E-003
111 | WaveformHMPoint35= 4.76798E-005 4.25547E-003
112 | WaveformHMPoint36= 4.92797E-005 2.51825E-003
113 | WaveformHMPoint37= 5.05503E-005 1.29197E-003
114 | WaveformHMPoint38= 5.19580E-005 0.00000E+000
115 |
116 | RxCoilLPFilter1= 0.99 210E+3
117 | GateTime01=7.150E-07 4.300E-07 1.000E-06
118 | GateTime02=2.215E-06 1.430E-06 3.000E-06
119 | GateTime03=4.215E-06 3.430E-06 5.000E-06
120 | GateTime04=6.215E-06 5.430E-06 7.000E-06
121 | GateTime05=8.215E-06 7.430E-06 9.000E-06
122 | GateTime06=1.022E-05 9.430E-06 1.100E-05
123 | GateTime07=1.221E-05 1.143E-05 1.300E-05
124 | GateTime08=1.472E-05 1.343E-05 1.600E-05
125 | GateTime09=1.821E-05 1.643E-05 2.000E-05
126 | GateTime10=2.271E-05 2.043E-05 2.500E-05
127 | GateTime11=2.821E-05 2.543E-05 3.100E-05
128 | GateTime12=3.522E-05 3.143E-05 3.900E-05
129 | GateTime13=4.421E-05 3.943E-05 4.900E-05
130 | GateTime14=5.571E-05 4.943E-05 6.200E-05
131 | GateTime15=7.021E-05 6.243E-05 7.800E-05
132 | GateTime16=8.821E-05 7.843E-05 9.800E-05
133 | GateTime17=1.107E-04 9.843E-05 1.230E-04
134 | GateTime18=1.387E-04 1.234E-04 1.540E-04
135 | GateTime19=1.742E-04 1.544E-04 1.940E-04
136 | GateTime20=2.197E-04 1.944E-04 2.450E-04
137 | GateTime21=2.767E-04 2.454E-04 3.080E-04
138 | GateTime22=3.487E-04 3.084E-04 3.890E-04
139 | GateTime23=4.397E-04 3.894E-04 4.900E-04
140 | GateTime24=5.537E-04 4.904E-04 6.170E-04
141 | GateTime25=6.977E-04 6.174E-04 7.780E-04
142 | GateTime26=8.792E-04 7.784E-04 9.800E-04
143 | GateTime27=1.108E-03 9.804E-04 1.235E-03
144 | GateTime28=1.396E-03 1.235E-03 1.557E-03
145 | GateTime29=1.760E-03 1.557E-03 1.963E-03
146 | GateTime30=2.219E-03 1.963E-03 2.474E-03
147 | GateTime31=2.797E-03 2.474E-03 3.120E-03
148 | GateTime32=3.516E-03 3.120E-03 3.912E-03
149 | GateTime33=4.396E-03 3.912E-03 4.880E-03
150 | GateTime34=5.473E-03 4.880E-03 6.065E-03
151 | GateTime35=6.791E-03 6.065E-03 7.517E-03
152 | GateTime36=8.405E-03 7.517E-03 9.293E-03
153 | GateTime37=1.038E-02 9.293E-03 1.147E-02
154 |
155 | [Channel1]
156 | RxCoilNumber=1
157 | GateTimeShift=-1.8E-06
158 | GateFactor=0.96
159 | SystemResponseConvolution=0
160 | RemoveInitialGates=5
161 | PrimaryFieldDampingFactor=1e-6
162 | UniformDataSTD=0.03
163 | MeaTimeDelay=0.000E+00
164 | NoGates=26
165 | RepFreq=210.00
166 | FrontGateTime=-7.000E-07
167 | TiBLowPassFilter=1 3.00E+5
168 | TransmitterMoment=LM
169 | TxApproximateCurrent=8.79
170 | ReceiverPolarizationXYZ=Z
171 |
172 |
173 | [Channel2]
174 | RxCoilNumber=1
175 | GateTimeShift=-1.4E-06
176 | GateFactor=1
177 | SystemResponseConvolution=0
178 | RemoveInitialGates=8
179 | PrimaryFieldDampingFactor=0.01
180 | UniformDataSTD=0.03
181 | MeaTimeDelay=6.000E-05
182 | NoGates=36
183 | RepFreq=22.50
184 | FrontGateTime=7.000E-05
185 | TiBLowPassFilter=1 3.00E+5
186 | TransmitterMoment=HM
187 | TxApproximateCurrent=115.20
188 | ReceiverPolarizationXYZ=Z
189 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Geophysical inversions with SimPEG: an example with airborne electromagnetic data
2 | ## A SimPEG tutorial for the SEG Near Surface Webinar series.
3 |
4 | **Instructors:**
5 | [Joseph Capriotti][jcapriot]1
6 | [Lindsey Heagy][lindsey]2
7 | and
8 | [Seogi Kang][seogi]3
9 |
10 |
11 | > 1
12 | > Center for Gravity, Electrical, and Magnetic Studies,
13 | > Department of Geophysics, Colorado School of Mines.
14 | >
15 | > 2
16 | > Geophysical Inversion Facility. Earth, Ocean and Atmospheric
17 | > Sciences. University of British Columbia.
18 | >
19 | > 3
20 | > Environmental Geophysics Research Group, Department of Geophysics, Stanford University
21 |
22 |
23 | | **Information** | |
24 | |---|---|
25 | | When | Feb 14, 2024 11 am CST|
26 | | Materials | [Intro Slides](https://docs.google.com/presentation/d/1a2mtYsiRe_ylpGesgdkZJxeG_9tDjO8TPXVV6gzD0Ss/edit?usp=sharing) |
27 |
28 |
29 | ## About
30 |
31 | We were invited by the SEG Near-Surface Geophysics Student Subcommittee to give a tutorial
32 | as part of their Open-Source Software webinar series.
33 |
34 | > Discover the powerful open-source [SimPEG][simpeg] framework.
35 | > You will learn how to simulate and invert airborne electromagnetic (AEM) data using a real-world dataset.
36 |
37 | During this tutorial we will go through a notebook describing how to invert AEM
38 | data collected in the Salinas Valley of California using the SkyTEM system to monitor salt water intrusion. For details
39 | about the dataset please checkout [Gottschalk et. al. 2017][case-study]. We will focus on
40 | how to simulate and invert the data by representing the parameters of the SkyTEM system
41 | using SimPEG components.
42 |
43 | This notebook is what we will use and modify during the live tutorial:
44 |
45 | - [live-skytem-inversion.ipynb](notebooks/live-skytem-inversion.ipynb)
46 |
47 |
48 | Alternatively there is also a filled version of this notebook available if you do not want to follow
49 | along with the coding.
50 |
51 | - [skytem-inversion.ipynb](notebooks/skytem-inversion.ipynb)
52 |
53 | ## Setup
54 |
55 | During this workshop we'll use [Juptyer notebooks][jupyter] to simulate and invert
56 | SkyTEM data.
57 |
58 | To be able to follow the tutorial and run the notebook, you'll need to have
59 | access to a Python environment. This could be done through a **local Python
60 | installation** like [Anaconda][anaconda] or [Miniforge][miniforge], or through
61 | **online services** like [Google Colab][colab].
62 |
63 | We recommend installing locally so you can save your progress. In google colab, your
64 | progress is not saved between sessions, but if you are struggling getting the correct
65 | environment setup locally it should work as a backup option.
66 |
67 | Here will provide instructions to:
68 |
69 | - [Install Python locally](#install-python-locally)
70 | - or [Configure Google Colab](#configure-google-colab)
71 |
72 | ## Install Python locally
73 |
74 | To be able to run the Jupyter notebooks for this tutorial in our own machines,
75 | we'll have to follow these steps:
76 |
77 | 1. Install a Python distribution (like [Anaconda][anaconda] or
78 | [miniforge][miniforge]).
79 | 1. Create a [_conda environment_][conda-environ] with all the Python packages
80 | needed (for example, SimPEG).
81 | 1. Activate this conda environment and run [JupyterLab][jupyterlab] to start
82 | coding.
83 |
84 | ### Step 1: Install a Python distribution
85 |
86 | We recommend installing a Python distribution like [miniforge][miniforge] or
87 | [Anaconda][anaconda].
88 |
89 | Both of them will install Python and a package manager that allows us to
90 | install new Python libraries (like SimPEG for example), and also create
91 | _environments_.
92 |
93 | Anaconda uses the `conda` package manager, while Miniconda uses the new
94 | `mamba`, which works faster than `conda`.
95 |
96 | If you have either of both installed, you can skip this step. Otherwise, please
97 | follow their installation instructions:
98 |
99 | - Install miniforge: https://github.com/conda-forge/miniforge#install
100 | - Install Anaconda: https://docs.anaconda.com/anaconda/install
101 |
102 | ### Step 2: Create the `simpeg-segns2024` conda environment
103 |
104 | > [!IMPORTANT]
105 | > In the following steps we'll make use of the `mamba` package manager. In case
106 | > you installed Anaconda, use `conda` instead. You can simply replace `mamba`
107 | > for `conda` on every command we ask to use it and it'll work fine.
108 |
109 | 1. Download the [`environment.yml`][environment_yml] file from
110 | (right-click and select "Save page as" or similar).
111 | 1. Make sure that the file is called `environment.yml`.
112 | Windows sometimes adds a `.txt` to the end, which you should remove.
113 | 1. Open a terminal (_Anaconda Prompt_ or _Miniforge Prompt_ if you are running
114 | Windows). The following steps should be done in the terminal.
115 | 1. Navigate to the folder that has the downloaded environment file
116 | (if you don't know how to do this, take a moment to read [the Software
117 | Carpentry lesson on the Unix shell][shell-novice]).
118 | 1. Create the conda environment by running
119 | ```
120 | mamba env create --file environment.yml
121 | ```
122 | (this will download and install all of the packages used in
123 | the tutorial). If you installed Anaconda, then replace `mamba` for `conda`
124 | in the previous line.
125 |
126 | ### Step 3: Get the repository's utility code and data.
127 |
128 | If you are not comfortable using git, you will need to at a bare minimum you will
129 | need to download:
130 |
131 | - The data configuration file: [configuration](data/20170606_337m2_Cal_DualWaveform_60Hz_414_412_418.gex)
132 | - A parser script to read the configuration file: [gex_parser.py](notebooks/utilities/gex_parser.py)
133 | - The environment [`environment.yml`][environment_yml] as detailed above in Step 2.
134 |
135 | If you are comfortable using git, you have likely already cloned this repository and
136 | received all of the files you will need.
137 |
138 | >[!TIP]
139 | > You don't need to explicitly download the data file from github, we will be able to use
140 | > `pandas.read_csv` to retrieve the file for us.
141 |
142 |
143 | ### Step 4: Activate the `simpeg-segns2024` environment and start JupyterLab
144 |
145 | > [!TIP]
146 | > You'll need a browser that is able to run JupyterLab (basically anyone except
147 | > Internet Explorer). If you are in Windows, make sure you change your
148 | > default browser to a different one.
149 |
150 | Now we can activate the newly created `simpeg-segns2024` environment.
151 |
152 | 1. Open a terminal (_Anaconda Prompt_ or _Miniforge Prompt_ if you are running
153 | Windows).
154 | 1. Activate the `simpeg-segns2024` environment by running:
155 | ```
156 | conda activate simpeg-segns2024
157 | ```
158 | 1. With the `simpeg-segns2024` environment activated, we can start JupyterLab
159 | by running
160 | ```terminal
161 | jupyter-lab
162 | ```
163 | in the terminal. A new tab in our web browser should
164 | open showing JupyterLab's interface.
165 | 1. Navigate to the [live-skytem-inversion.ipynb](notebooks/live-skytem-inversion.ipynb) notebook.
166 |
167 | ## Configure Google Colab
168 |
169 | To be able to run the Jupyter notebooks for this tutorial in Google Colab,
170 | we'll need to follow these steps:
171 |
172 | 1. Login to our Google Colab account.
173 | 1. Copy the live notebook from GitHub.
174 | 1. Install some Python libraries that we'll need to use, such as
175 | [SimPEG][simpeg].
176 |
177 | ### Step 1: Login to our Google Colab account
178 |
179 | If you don't have a Google account, create one and log in. If you do, you just
180 | need to log in.
181 |
182 | ### Step 2: Open the Live notebook in Colab
183 |
184 | 1. Access to Google Colab by going to: https://colab.research.google.com
185 | 1. Find the top menu and choose `File` > `Open Notebook`.
186 | 1. Select the GitHub option
187 | 1. Paste the URL of this repository into the space: https://github.com/simpeg/segns-2024-tutorial
188 | 2. Select the [live-skytem-inversion.ipynb](notebooks/live-skytem-inversion.ipynb) notebook.
189 |
190 | ### Step 3: Install some Python libraries
191 |
192 | To be able to follow this workshop we need to install some Python libraries
193 | that aren't preinstalled in the default Google Colab environment.
194 |
195 | 1. Click on the first cell of the notebook (Create
196 | a new _Code_ cell and move it to the first position with the arrows icons
197 | that appear on its top-right).
198 | 1. Type the following line in the selected cell:
199 | ```
200 | !pip install simpeg==0.20.0 discretize==0.10.0
201 | ```
202 | Note the `!` sign at the beginning of the line, **don't remove it**.
203 | 1. Run that cell by clicking the _Play_ button on its left or by pressing
204 | `Shift+Enter` in your keyboard. `pip` should install all the packaged listed
205 | in that line. If installation goes smoothly, you should see a line that
206 | reads `Successfully installed ...` and lists all the new packages that had
207 | been installed.
208 |
209 | ### Step 4: Copy the repository's utility code and data to Colab.
210 |
211 | To be able to follow along with the tutorial, you will need to copy at least
212 | two files directly to Google Colab: the configuration file and the parser script.
213 | You can download them directly from github to Google Colab by typing in another cell (and then executing it):
214 |
215 | ```
216 | !wget https://raw.githubusercontent.com/simpeg/segns-2024-tutorial/main/data/20170606_337m2_Cal_DualWaveform_60Hz_414_412_418.gex
217 | !wget https://raw.githubusercontent.com/simpeg/segns-2024-tutorial/main/notebooks/utilities/gex_parser.py
218 | ```
219 |
220 | This will download the SkyTEM configuration file and a parser to easily read it into python.
221 |
222 |
223 | > [!IMPORTANT]
224 | > Every time you open a notebook in Colab or create a new one, you'll have to
225 | > reinstall these packages and download the data again (Google Colab doesn't save
226 | > installed states across notebooks).
227 | >
228 | > If it's a new notebook, just follow the previous instructions from the top.
229 | >
230 | > If it's an existing notebook, make sure that it has the `!pip install ...`
231 | > and the `!wget ...` lines at the top (add it otherwise), and run it.
232 |
233 | ## Acknowledgement
234 | AEM data used in this tutorial were acquired with funding from MCWRA (Monterey County Water Resources Agency) with the [SkyTEM](https://skytem.com/) system; acquisition oversight, planning, and processing by [Aqua Geo Framework](https://www.aquageoframeworks.com/).
235 |
236 | ## License
237 |
238 | This work is licensed under the [MIT
239 | License](https://opensource.org/license/mit/).
240 |
241 | [jcapriot]: https://www.github.com/jcapriot
242 | [lindsey]: https://lindseyjh.ca/
243 | [seogi]: https://sgkang.github.io/
244 | [simpeg]: https://www.simpeg.xyz
245 | [case-study]: https://library.seg.org/doi/10.1190/geo2019-0272.1
246 | [jupyter]: https://jupyter.org/
247 | [colab]: https://colab.research.google.com
248 | [anaconda]: https://www.anaconda.com/download
249 | [miniforge]: https://github.com/conda-forge/miniforge
250 | [conda-environ]: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html
251 | [jupyterlab]: https://jupyterlab.readthedocs.io
252 | [environment_yml]: https://raw.githubusercontent.com/simpeg/agrogeo24/main/environment.yml
253 | [shell-novice]: http://swcarpentry.github.io/shell-novice
254 |
--------------------------------------------------------------------------------
/notebooks/live-skytem-inversion.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "4d97d6c7-795f-4d02-b2fe-8bde20eb7a60",
6 | "metadata": {
7 | "editable": true,
8 | "slideshow": {
9 | "slide_type": "slide"
10 | },
11 | "tags": []
12 | },
13 | "source": [
14 | "# Geophysical inversions with `SimPEG`\n",
15 | "## An example with airborne electromagnetic data\n",
16 | "Presenters:\n",
17 | "Joseph Capriotti, Lindsey Heagy, Seogi Kang"
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": null,
23 | "id": "ccc4efb8-9556-40a3-bd95-ff79295ed020",
24 | "metadata": {
25 | "editable": true,
26 | "scrolled": true,
27 | "slideshow": {
28 | "slide_type": "skip"
29 | },
30 | "tags": []
31 | },
32 | "outputs": [],
33 | "source": [
34 | "import pandas as pd\n",
35 | "import numpy as np\n",
36 | "from utilities.gex_parser import parse_gex_file\n",
37 | "# If on google colab you will likely need to do instead:\n",
38 | "# from gex_parser import parse_gex_file\n",
39 | "import matplotlib.pyplot as plt\n",
40 | "\n",
41 | "# Start with just importing the time domain module of simpeg, and a utility to plot a layered model.\n",
42 | "from SimPEG.electromagnetics import time_domain as tdem\n",
43 | "from SimPEG.utils import plot_1d_layer_model\n",
44 | "\n",
45 | "import discretize"
46 | ]
47 | },
48 | {
49 | "cell_type": "markdown",
50 | "id": "99341123-e0b6-4dd8-9a0d-c03fe8260533",
51 | "metadata": {
52 | "editable": true,
53 | "slideshow": {
54 | "slide_type": "slide"
55 | },
56 | "tags": []
57 | },
58 | "source": [
59 | "## Reading in the Data\n",
60 | "\n",
61 | "First a bit about the skytem system. There are in general two sets of data:\n",
62 | "* The low-moment data which is more sensitive to the near surface\n",
63 | "* The high-moment data, which is (relatively) more sensitive to deeper structures.\n",
64 | "\n",
65 | "The system has a hexagonal loop transmitter, with a $\\frac{\\partial \\vec{B}}{\\partial t}$ sensor."
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "id": "46afbbed-2656-4575-83e5-fdbc2a4a9e87",
71 | "metadata": {
72 | "editable": true,
73 | "slideshow": {
74 | "slide_type": "slide"
75 | },
76 | "tags": []
77 | },
78 | "source": [
79 | "There are two files that represent our data:\n",
80 | "* A configuration file. Usually a `.gex` extension.\n",
81 | "* The processed data file.\n",
82 | " * Simple CSV file $\\rightarrow$ use `pandas`\n",
83 | " \n",
84 | "We've provided a simple parser to read the information in the configuration file into python."
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "id": "7653a6cc-1175-4f7a-989b-0d6968d12dc1",
91 | "metadata": {},
92 | "outputs": [],
93 | "source": [
94 | "# gex_file = "
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": null,
100 | "id": "5f71f8f2-2eb6-413e-ac85-1ddf4b217806",
101 | "metadata": {
102 | "editable": true,
103 | "scrolled": true,
104 | "slideshow": {
105 | "slide_type": "fragment"
106 | },
107 | "tags": []
108 | },
109 | "outputs": [],
110 | "source": [
111 | "gex_file"
112 | ]
113 | },
114 | {
115 | "cell_type": "markdown",
116 | "id": "cedba3b2-876a-4a82-8493-f96395dee827",
117 | "metadata": {},
118 | "source": [
119 | "Can simply use `pandas.read_csv` to read in the data file"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": null,
125 | "id": "2e184a7d-f2f1-444d-a194-014e7ed73906",
126 | "metadata": {
127 | "editable": true,
128 | "scrolled": true,
129 | "slideshow": {
130 | "slide_type": "fragment"
131 | },
132 | "tags": []
133 | },
134 | "outputs": [],
135 | "source": [
136 | "data_file = pd.read_csv('../data/MCWD3_dat.xyz')\n",
137 | "# you can alternatively use pandas to directly fetch this file from the internet (if on colab)\n",
138 | "# data_file = pd.read_csv(\"https://github.com/simpeg/segns-2024-tutorial/raw/main/data/MCWD3_dat.xyz\")\n",
139 | "data_file"
140 | ]
141 | },
142 | {
143 | "cell_type": "markdown",
144 | "id": "798f63dc-158e-4076-ac73-fcd8fa3454fc",
145 | "metadata": {},
146 | "source": [
147 | "inside this data file, missing data are marked with the value 9999, let's replace them with NaNs instead (which the plotting utilities will then ignore)."
148 | ]
149 | },
150 | {
151 | "cell_type": "code",
152 | "execution_count": null,
153 | "id": "8c250733-360b-4512-acc2-f73b74f3c306",
154 | "metadata": {},
155 | "outputs": [],
156 | "source": [
157 | "# Plot the data file"
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "id": "1a074a99-eed6-4bf0-99c6-b69e34f6055d",
163 | "metadata": {},
164 | "source": [
165 | "Let's plot the location of all the stations in this survey"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": null,
171 | "id": "05dc7e16-3146-4ddd-8c75-903ca69a4ac3",
172 | "metadata": {
173 | "editable": true,
174 | "slideshow": {
175 | "slide_type": "subslide"
176 | },
177 | "tags": []
178 | },
179 | "outputs": [],
180 | "source": [
181 | "plt.scatter(data_file.UTMX, data_file.UTMY, s=0.5)\n",
182 | "plt.xlabel('Easting (m)')\n",
183 | "plt.ylabel('Northing (m)');"
184 | ]
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "id": "1a408e7d-8328-44ee-b3e4-3a008e764868",
189 | "metadata": {},
190 | "source": [
191 | "A few bits of processing have already been done to this data set, in particular, they have been normalized for source current strength, normalized by the transmitter area, and have also been spatially averaged along flight lines to form \"stations\".\n",
192 | "\n",
193 | "Now we need a bit of information from the `.gex` file to tell us the number of high moment and low moment time gates for this survey"
194 | ]
195 | },
196 | {
197 | "cell_type": "code",
198 | "execution_count": null,
199 | "id": "6e955391-8fe4-4a6e-ae5d-f59bf20d608b",
200 | "metadata": {
201 | "editable": true,
202 | "slideshow": {
203 | "slide_type": "subslide"
204 | },
205 | "tags": []
206 | },
207 | "outputs": [],
208 | "source": [
209 | "# n_lm_gates = \n",
210 | "# n_hm_gates ="
211 | ]
212 | },
213 | {
214 | "cell_type": "markdown",
215 | "id": "009b6aff-35c2-4659-9b84-659d4f725e00",
216 | "metadata": {},
217 | "source": [
218 | "The data file will have `n_lm_gates` of db/dt data, `n_lm_gates` of relative error estimates, then `n_hm_gates` of the high moment db/dt data, followed by `n_hm_gates` relative error estimates."
219 | ]
220 | },
221 | {
222 | "cell_type": "markdown",
223 | "id": "9cfbcd72-2e93-4142-9dac-67a9b959709b",
224 | "metadata": {},
225 | "source": [
226 | "### Zooming in to a single station to work with"
227 | ]
228 | },
229 | {
230 | "cell_type": "markdown",
231 | "id": "1639f8b7-f867-41de-8e13-95bf65d9f85d",
232 | "metadata": {},
233 | "source": [
234 | "Now let's look at a single station, along some randomly chosen line.\n",
235 | "\n",
236 | "I can use panda's to easily group all of the data by a common line number\n",
237 | "and then get that group"
238 | ]
239 | },
240 | {
241 | "cell_type": "code",
242 | "execution_count": null,
243 | "id": "63468d8e-fa81-49c0-8a98-ec73263eb890",
244 | "metadata": {
245 | "editable": true,
246 | "slideshow": {
247 | "slide_type": "fragment"
248 | },
249 | "tags": []
250 | },
251 | "outputs": [],
252 | "source": [
253 | "# line_no\n",
254 | "# line_grouping\n",
255 | "# line"
256 | ]
257 | },
258 | {
259 | "cell_type": "markdown",
260 | "id": "a1fc4012-c3d9-4888-a2f9-e1fe3a9fb9c2",
261 | "metadata": {},
262 | "source": [
263 | "Let's look at all the data along that line."
264 | ]
265 | },
266 | {
267 | "cell_type": "code",
268 | "execution_count": null,
269 | "id": "ab16db74-7a91-404a-ae7c-1f545237e70f",
270 | "metadata": {
271 | "editable": true,
272 | "slideshow": {
273 | "slide_type": "subslide"
274 | },
275 | "tags": []
276 | },
277 | "outputs": [],
278 | "source": [
279 | "lm_data = line.iloc[:, 9:9+n_lm_gates]\n",
280 | "hm_data = line.iloc[:, 9+2*n_lm_gates:9+2*n_lm_gates + n_hm_gates]"
281 | ]
282 | },
283 | {
284 | "cell_type": "code",
285 | "execution_count": null,
286 | "id": "c765091a-fb27-4df0-9c50-2375dcd7e3dc",
287 | "metadata": {},
288 | "outputs": [],
289 | "source": [
290 | "# Plot the line data"
291 | ]
292 | },
293 | {
294 | "cell_type": "code",
295 | "execution_count": null,
296 | "id": "4f99c2a1-14f9-4283-bf7e-59748831e9fa",
297 | "metadata": {
298 | "editable": true,
299 | "slideshow": {
300 | "slide_type": "fragment"
301 | },
302 | "tags": []
303 | },
304 | "outputs": [],
305 | "source": [
306 | "# select a single sounding along that line, and grab all of the data associated with that station\n",
307 | "# Record 5590\n",
308 | "# station = \n",
309 | "\n",
310 | "station_lm_data = station.iloc[0, 9:9+n_lm_gates].to_numpy()\n",
311 | "station_lm_std = station.iloc[0, 9+n_lm_gates:9+2*n_lm_gates].to_numpy()\n",
312 | "station_hm_data = station.iloc[0, 9+2*n_lm_gates:9+2*n_lm_gates + n_hm_gates].to_numpy()\n",
313 | "station_hm_std = station.iloc[0, 9+2*n_lm_gates + n_hm_gates:].to_numpy()"
314 | ]
315 | },
316 | {
317 | "cell_type": "markdown",
318 | "id": "fd833038-50e0-4c76-881c-ebd375b3c854",
319 | "metadata": {},
320 | "source": [
321 | "Now we have numpy arrays of the recorded data for both the low and high moment data sets, along with their relative standard deviations."
322 | ]
323 | },
324 | {
325 | "cell_type": "code",
326 | "execution_count": null,
327 | "id": "b3c1aaa4-4f59-4636-b9ae-7f1dd8dcb0ea",
328 | "metadata": {
329 | "editable": true,
330 | "slideshow": {
331 | "slide_type": "subslide"
332 | },
333 | "tags": []
334 | },
335 | "outputs": [],
336 | "source": [
337 | "plt.scatter(data_file.UTMX, data_file.UTMY, s=0.5)\n",
338 | "# add the line and station highlights\n",
339 | "\n",
340 | "plt.xlabel('Easting (m)')\n",
341 | "plt.ylabel('Northing (m)');"
342 | ]
343 | },
344 | {
345 | "cell_type": "code",
346 | "execution_count": null,
347 | "id": "0a0578df-ea98-47ae-80c7-8d8ff9db94a4",
348 | "metadata": {},
349 | "outputs": [],
350 | "source": [
351 | "plt.semilogy(line.UTMY, lm_data)\n",
352 | "plt.semilogy(line.UTMY, hm_data)\n",
353 | "# show where the station is along the line using axvline\n",
354 | "plt.xlabel('Northing');"
355 | ]
356 | },
357 | {
358 | "cell_type": "markdown",
359 | "id": "f340ea12-4eee-4cc1-8351-75f806f374e6",
360 | "metadata": {},
361 | "source": [
362 | "In order to model the data, we still need a few things pieces of information.\n",
363 | "* The location in time of the measurements\n",
364 | "* The transmitter waveforms\n",
365 | "* The transmitter shape\n",
366 | "* The receiver location"
367 | ]
368 | },
369 | {
370 | "cell_type": "code",
371 | "execution_count": null,
372 | "id": "792242bf-90bf-4751-9d15-d5cddf53e986",
373 | "metadata": {
374 | "editable": true,
375 | "slideshow": {
376 | "slide_type": "subslide"
377 | },
378 | "tags": []
379 | },
380 | "outputs": [],
381 | "source": [
382 | "# In general the location of the gates is\n",
383 | "# gate_time = gate_centers + shift + delay\n",
384 | "# The Gex file also tells us which gates the respective moments use\n",
385 | "gate_centers = gex_file['General']['GateTimes']['center']\n",
386 | "\n",
387 | "channel_info = gex_file['Channel1']\n",
388 | "shift = channel_info['GateTimeShift']\n",
389 | "delay = channel_info['MeaTimeDelay']\n",
390 | "first_gate = channel_info['RemoveInitialGates']\n",
391 | "last_gate = channel_info['NoGates']\n",
392 | "# lm_times =\n",
393 | "\n",
394 | "channel_info = gex_file['Channel2']\n",
395 | "shift = channel_info['GateTimeShift']\n",
396 | "delay = channel_info['MeaTimeDelay']\n",
397 | "first_gate = channel_info['RemoveInitialGates']\n",
398 | "last_gate = channel_info['NoGates']\n",
399 | "# hm_times ="
400 | ]
401 | },
402 | {
403 | "cell_type": "markdown",
404 | "id": "21c0cff3-a2df-49c7-b7fe-60ae2f69090c",
405 | "metadata": {},
406 | "source": [
407 | "Some of the station's data are NaN's (which represent no data), Let's remove those points from our consideration"
408 | ]
409 | },
410 | {
411 | "cell_type": "code",
412 | "execution_count": null,
413 | "id": "dec09ae6-f51b-4da4-b52a-c0ff06198608",
414 | "metadata": {
415 | "editable": true,
416 | "slideshow": {
417 | "slide_type": "subslide"
418 | },
419 | "tags": []
420 | },
421 | "outputs": [],
422 | "source": [
423 | "lm_good_data = ~np.isnan(station_lm_data)\n",
424 | "hm_good_data = ~np.isnan(station_hm_data)\n",
425 | "\n",
426 | "lm_times = lm_times[lm_good_data]\n",
427 | "hm_times = hm_times[hm_good_data]\n",
428 | "\n",
429 | "station_lm_data = station_lm_data[lm_good_data]\n",
430 | "station_lm_std = station_lm_std[lm_good_data]\n",
431 | "station_hm_data = station_hm_data[hm_good_data]\n",
432 | "station_hm_std = station_hm_std[hm_good_data]\n",
433 | "\n",
434 | "# and of course reset our counts too\n",
435 | "n_lm_gates = len(lm_times)\n",
436 | "n_hm_gates = len(hm_times)"
437 | ]
438 | },
439 | {
440 | "cell_type": "markdown",
441 | "id": "79ff21e1-08d3-452a-9600-466c546fac01",
442 | "metadata": {},
443 | "source": [
444 | "and finally plot some decay curves.\n",
445 | "\n",
446 | "This is the data we will try to match."
447 | ]
448 | },
449 | {
450 | "cell_type": "code",
451 | "execution_count": null,
452 | "id": "1083dca9-07dc-4d55-9c84-3b352020b982",
453 | "metadata": {
454 | "editable": true,
455 | "slideshow": {
456 | "slide_type": "subslide"
457 | },
458 | "tags": []
459 | },
460 | "outputs": [],
461 | "source": [
462 | "# loglog plots of time and data"
463 | ]
464 | },
465 | {
466 | "cell_type": "markdown",
467 | "id": "7a453795-c69a-49df-8492-0d2286bdf10c",
468 | "metadata": {},
469 | "source": [
470 | "### Setting up the survey"
471 | ]
472 | },
473 | {
474 | "cell_type": "markdown",
475 | "id": "ed986b97-33ca-4bac-af55-e11148b17201",
476 | "metadata": {},
477 | "source": [
478 | "Now we want represent all of the configuration with `SimPEG` objects.\n",
479 | "\n",
480 | "SimPEG's general structure to hold this information is:\n",
481 | "* Create `Receiver`s (that observe data)\n",
482 | "* Create `Source`s (and attach receivers to those sources)\n",
483 | "* Gather all the sources into a `Survey`\n",
484 | "\n",
485 | "In the time-domain EM module:\n",
486 | "\n",
487 | "* `Receivers`\n",
488 | " * Observe at a specific location in space.\n",
489 | " * Observe a specific component (Here it will be the vertical component of $\\frac{d\\vec{B}}{dt}$)\n",
490 | " * At specific times (the time-gate centers).\n",
491 | " \n",
492 | "* `Sources`\n",
493 | " * Have specific types (Line Current, Magnetic Dipole, etc.)\n",
494 | " * Have a location (or set of locations for Line current)\n",
495 | " * Have waveforms\n",
496 | " * Are \"listened to\" by receivers.\n",
497 | "\n",
498 | "So let's start by grabbing the information we will need from the gex file.\n",
499 | "\n",
500 | "We already have the time gate locations. So, let's grab all of the information related to the transmitter.\n",
501 | "\n",
502 | "Specifically here, the waveforms of the low and high moment sources."
503 | ]
504 | },
505 | {
506 | "cell_type": "code",
507 | "execution_count": null,
508 | "id": "e9e56a8b-46b8-4662-843a-4aa7cb420419",
509 | "metadata": {
510 | "editable": true,
511 | "slideshow": {
512 | "slide_type": "subslide"
513 | },
514 | "tags": []
515 | },
516 | "outputs": [],
517 | "source": [
518 | "waves = gex_file['General']['Waveforms']\n",
519 | "# lm_wave_time\n",
520 | "# lm_wave_form\n",
521 | "# hm_wave_time\n",
522 | "# hm_wave_form\n",
523 | "# plot them in two subplots"
524 | ]
525 | },
526 | {
527 | "cell_type": "markdown",
528 | "id": "dee437f1-9b0b-43f0-bf11-30089f030320",
529 | "metadata": {},
530 | "source": [
531 | "The shape of the transmitter , and the offset of the receiver coil."
532 | ]
533 | },
534 | {
535 | "cell_type": "code",
536 | "execution_count": null,
537 | "id": "6436252e-b840-4543-a891-052e010b974a",
538 | "metadata": {
539 | "editable": true,
540 | "slideshow": {
541 | "slide_type": "subslide"
542 | },
543 | "tags": []
544 | },
545 | "outputs": [],
546 | "source": [
547 | "# We pad the locations of the Tx points with 0, to expand it from a 2D (x,y) pair to a 3D (x, y, z) pair.\n",
548 | "# We also add another row because we will need to close the transmitter loop.\n",
549 | "# get the tx shape, pad it and close the loop\n",
550 | "# tx_shape = \n",
551 | "\n",
552 | "# get the receiver offset\n",
553 | "# rx_offset = \n",
554 | "\n",
555 | "# let's plot them"
556 | ]
557 | },
558 | {
559 | "cell_type": "code",
560 | "execution_count": null,
561 | "id": "968e79a6-2eed-43df-b2cd-be8f97ec39b1",
562 | "metadata": {},
563 | "outputs": [],
564 | "source": [
565 | "# tx_shape cw or ccw?"
566 | ]
567 | },
568 | {
569 | "cell_type": "markdown",
570 | "id": "59ed04b8-f452-4224-9a96-4c4fe291d5ce",
571 | "metadata": {},
572 | "source": [
573 | "Let's give the transmitter and receiver explicit locations by adding the UTM coordinates of the station to the transmitter shape and the receiver offset."
574 | ]
575 | },
576 | {
577 | "cell_type": "code",
578 | "execution_count": null,
579 | "id": "0ac109d2-2a5f-47bc-b6ea-a7f2ab833310",
580 | "metadata": {
581 | "editable": true,
582 | "slideshow": {
583 | "slide_type": "slide"
584 | },
585 | "tags": []
586 | },
587 | "outputs": [],
588 | "source": [
589 | "# give them an absolute position (mostly just need to set the height here)\n",
590 | "# tx_loc = tx_shape + ...\n",
591 | "# rx_loc = rx_offset + ...\n",
592 | "# also let's grab the area\n",
593 | "# tx_area = "
594 | ]
595 | },
596 | {
597 | "cell_type": "markdown",
598 | "id": "3837d38d-4ca6-4282-9ba1-d8218b9fec44",
599 | "metadata": {},
600 | "source": [
601 | "We are often asked how to deal with both the low and high moment data in SimPEG. The answer is actually fairly straightforward as long as you understand the structure of a SimPEG survey, we model them as two seperate sources (who just so happen to be in the same location)."
602 | ]
603 | },
604 | {
605 | "cell_type": "code",
606 | "execution_count": null,
607 | "id": "3dcdb315-63f0-4a95-b1d5-7c1eb4c36b5a",
608 | "metadata": {
609 | "editable": true,
610 | "slideshow": {
611 | "slide_type": "slide"
612 | },
613 | "tags": []
614 | },
615 | "outputs": [],
616 | "source": [
617 | "# Low moment:\n",
618 | "# rx_lm PointMagneticFluxTimeDerivative\n",
619 | "\n",
620 | "# wave_lm PiecewiseLinearWaveform\n",
621 | "\n",
622 | "# src_lm LineCurrent"
623 | ]
624 | },
625 | {
626 | "cell_type": "code",
627 | "execution_count": null,
628 | "id": "8905e18d-4d69-4057-83e2-433cf1ca07c7",
629 | "metadata": {
630 | "editable": true,
631 | "slideshow": {
632 | "slide_type": "slide"
633 | },
634 | "tags": []
635 | },
636 | "outputs": [],
637 | "source": [
638 | "# high moment\n",
639 | "# rx_hm\n",
640 | "\n",
641 | "# wave_hm\n",
642 | "\n",
643 | "# src_hm"
644 | ]
645 | },
646 | {
647 | "cell_type": "markdown",
648 | "id": "5516c9c4-a009-4d27-921d-315c269b4dde",
649 | "metadata": {},
650 | "source": [
651 | "Now we can set up a survey that has two sources!"
652 | ]
653 | },
654 | {
655 | "cell_type": "code",
656 | "execution_count": null,
657 | "id": "bcf2685a-1f57-4334-99e0-cec6c12171b3",
658 | "metadata": {
659 | "editable": true,
660 | "slideshow": {
661 | "slide_type": "slide"
662 | },
663 | "tags": []
664 | },
665 | "outputs": [],
666 | "source": [
667 | "# srv"
668 | ]
669 | },
670 | {
671 | "cell_type": "markdown",
672 | "id": "c335963d-4268-4d75-965c-f7e71b3a2a19",
673 | "metadata": {},
674 | "source": [
675 | "Now we're ready to set up the forward simulation, of which we will use (for this tutorial), a Layered simulation.\n",
676 | "\n",
677 | "It has two physical properties:\n",
678 | "* The conductivity of each layer (`sigma`)\n",
679 | "* The thickness of each layer (`thicknesses`)"
680 | ]
681 | },
682 | {
683 | "cell_type": "code",
684 | "execution_count": null,
685 | "id": "25f0d5c5-d9f1-4069-8c4f-5c6ef8f93756",
686 | "metadata": {
687 | "editable": true,
688 | "slideshow": {
689 | "slide_type": "subslide"
690 | },
691 | "tags": []
692 | },
693 | "outputs": [],
694 | "source": [
695 | "#first a simple test\n",
696 | "thicknesses = []\n",
697 | "conductivities = [1E-2]"
698 | ]
699 | },
700 | {
701 | "cell_type": "code",
702 | "execution_count": null,
703 | "id": "902f26ee-e701-4000-bb69-ee190aa74cf6",
704 | "metadata": {
705 | "editable": true,
706 | "slideshow": {
707 | "slide_type": "fragment"
708 | },
709 | "tags": []
710 | },
711 | "outputs": [],
712 | "source": [
713 | "sim = tdem.Simulation1DLayered(srv, sigma=conductivities, thicknesses=thicknesses)"
714 | ]
715 | },
716 | {
717 | "cell_type": "markdown",
718 | "id": "a725c677-75f4-45a4-9f4c-7fed337fe50a",
719 | "metadata": {},
720 | "source": [
721 | "We can set these properties on the simulation and create data. The only requirement is that the conductivity array must be one longer than the thickness array.\n",
722 | "\n",
723 | "**note**: For this simulation the conductivities and thicknesses are defined from the surface downwards."
724 | ]
725 | },
726 | {
727 | "cell_type": "code",
728 | "execution_count": null,
729 | "id": "2c348cf4-5cc2-4e4b-88c7-f366e67761b6",
730 | "metadata": {
731 | "editable": true,
732 | "slideshow": {
733 | "slide_type": "fragment"
734 | },
735 | "tags": []
736 | },
737 | "outputs": [],
738 | "source": [
739 | "# sim.thicknesses =\n",
740 | "# sim.sigma = \n",
741 | "pre = sim.dpred(None) # no \"model\" (will get to this more later).\n",
742 | "\n",
743 | "pre_lm = pre[:n_lm_gates]\n",
744 | "pre_hm = pre[n_lm_gates:]\n",
745 | "\n",
746 | "plt.loglog(lm_times, -pre_lm) # negative here to account for simpeg convention of z+ up\n",
747 | "plt.loglog(hm_times, -pre_hm)\n",
748 | "plt.loglog(lm_times, station_lm_data*tx_area, color='C0', marker='.', linestyle='') # un-normalize by area\n",
749 | "plt.loglog(hm_times, station_hm_data*tx_area, color='C1', marker='.', linestyle='')"
750 | ]
751 | },
752 | {
753 | "cell_type": "markdown",
754 | "id": "82e06706-f60b-4382-b7bb-7f67d760b704",
755 | "metadata": {
756 | "editable": true,
757 | "slideshow": {
758 | "slide_type": "slide"
759 | },
760 | "tags": []
761 | },
762 | "source": [
763 | "# Setup an inversion\n",
764 | "\n",
765 | "An inversion has many pieces that we must all setup for simpeg.\n",
766 | "\n",
767 | "There are:\n",
768 | "* `Map`: Objects that tell simpeg what to invert for.\n",
769 | "* `Data`: a container for the observed data, its standard deviation, and the survey.\n",
770 | "* `ObjectiveFunctions`, which describe the function we minimize to perform the inversion.\n",
771 | " * `DataMisfit`: How we measure the fitness of a model to the observed data.\n",
772 | " * `Regularization`: A measure of the simplicity of a model.\n",
773 | "* `Minimization` Routines: Which method we use to iteratively minize the objective function.\n",
774 | "* `InverseProblem`: Defines the optimization problem and the minimization routine.\n",
775 | "* `Directives`: Operations that adjust parameters in the objective function as the inversion proceeds.\n",
776 | "* `Inversion`: Groups together all of the above to actually run an inversion.\n",
777 | "\n",
778 | "We will start by describing parametric inversions for simple 1D models, where the number of layers we invert for is much less than the number of data."
779 | ]
780 | },
781 | {
782 | "cell_type": "markdown",
783 | "id": "4f23f060-2af8-43f6-99fd-80420aceebb0",
784 | "metadata": {
785 | "editable": true,
786 | "slideshow": {
787 | "slide_type": "subslide"
788 | },
789 | "tags": []
790 | },
791 | "source": [
792 | "## Maps\n",
793 | "This is how we tell simpeg what to invert for.\n",
794 | "\n",
795 | "Maps define how we go from our inversion model to the physical properties."
796 | ]
797 | },
798 | {
799 | "cell_type": "code",
800 | "execution_count": null,
801 | "id": "c44e8423-f20d-41a8-a709-1e2d0ef28176",
802 | "metadata": {
803 | "editable": true,
804 | "slideshow": {
805 | "slide_type": "fragment"
806 | },
807 | "tags": []
808 | },
809 | "outputs": [],
810 | "source": [
811 | "from SimPEG import maps"
812 | ]
813 | },
814 | {
815 | "cell_type": "markdown",
816 | "id": "46dcdc32-a1e8-4b8a-9262-40ec4957c7ed",
817 | "metadata": {
818 | "editable": true,
819 | "slideshow": {
820 | "slide_type": "fragment"
821 | },
822 | "tags": []
823 | },
824 | "source": [
825 | "for example, this is a simple map that will transform its input according to:\n",
826 | "$$out = e^{in}$$"
827 | ]
828 | },
829 | {
830 | "cell_type": "code",
831 | "execution_count": null,
832 | "id": "7b29e91c-e542-4be5-bcdb-1b89de1caf82",
833 | "metadata": {
834 | "editable": true,
835 | "slideshow": {
836 | "slide_type": "fragment"
837 | },
838 | "tags": []
839 | },
840 | "outputs": [],
841 | "source": [
842 | "# exp_map ="
843 | ]
844 | },
845 | {
846 | "cell_type": "markdown",
847 | "id": "caca0e71-b1a9-4129-8d1b-7ea05f728504",
848 | "metadata": {
849 | "editable": true,
850 | "slideshow": {
851 | "slide_type": "subslide"
852 | },
853 | "tags": []
854 | },
855 | "source": [
856 | "This map is particularly useful for solving for conductivity for two reasons:\n",
857 | "1) Generally conductivity varies on a logarithmic scale\n",
858 | "2) We do not need to handle positivity constraints while minimizing\n",
859 | "\n",
860 | "We use maps in `SimPEG` to tell simulations what we will be inverting for."
861 | ]
862 | },
863 | {
864 | "cell_type": "markdown",
865 | "id": "d8091876-c007-44e5-8f57-d48a0313ff72",
866 | "metadata": {
867 | "editable": true,
868 | "slideshow": {
869 | "slide_type": "subslide"
870 | },
871 | "tags": []
872 | },
873 | "source": [
874 | "## Data\n",
875 | "We need to create a container for the data, things to remember about here:\n",
876 | "\n",
877 | "* The processed data were normalized by the transmitter area.\n",
878 | "* There is a sign difference in the convention for SimPEG and the processed data\n",
879 | "* The standard deviations are actually relative errors."
880 | ]
881 | },
882 | {
883 | "cell_type": "code",
884 | "execution_count": null,
885 | "id": "2538e797-3ba5-4e27-bfeb-d384beaf4e42",
886 | "metadata": {
887 | "editable": true,
888 | "slideshow": {
889 | "slide_type": "subslide"
890 | },
891 | "tags": []
892 | },
893 | "outputs": [],
894 | "source": [
895 | "from SimPEG import data"
896 | ]
897 | },
898 | {
899 | "cell_type": "code",
900 | "execution_count": null,
901 | "id": "372d912c-0a37-4d19-a213-69c3d9dc0e5b",
902 | "metadata": {
903 | "editable": true,
904 | "slideshow": {
905 | "slide_type": "subslide"
906 | },
907 | "tags": []
908 | },
909 | "outputs": [],
910 | "source": [
911 | "# dobs\n",
912 | "# rel_err\n",
913 | "\n",
914 | "# data_container"
915 | ]
916 | },
917 | {
918 | "cell_type": "markdown",
919 | "id": "942faa76-ad60-4ce9-a27c-498eef207d06",
920 | "metadata": {
921 | "editable": true,
922 | "slideshow": {
923 | "slide_type": "subslide"
924 | },
925 | "tags": []
926 | },
927 | "source": [
928 | "Since the data container knows about the survey, we can directly index it\n",
929 | "with sources and receivers to retrieve the data associated with that pair.\n",
930 | "\n",
931 | "This is particularly useful to ensure that you have passed your observed data in the order that `SimPEG` expects it."
932 | ]
933 | },
934 | {
935 | "cell_type": "code",
936 | "execution_count": null,
937 | "id": "4c9537b4-d19d-4c47-ba89-86a5caf505dd",
938 | "metadata": {
939 | "editable": true,
940 | "slideshow": {
941 | "slide_type": "fragment"
942 | },
943 | "tags": []
944 | },
945 | "outputs": [],
946 | "source": [
947 | "data_container[src_hm, rx_hm]"
948 | ]
949 | },
950 | {
951 | "cell_type": "markdown",
952 | "id": "7835cca0-8417-4e72-897d-5b4fc7ced99d",
953 | "metadata": {
954 | "editable": true,
955 | "slideshow": {
956 | "slide_type": "subslide"
957 | },
958 | "tags": []
959 | },
960 | "source": [
961 | "## Parametric Halfspace inversion.\n",
962 | "\n",
963 | "With a few key component concepts out of the way, Let's see how we can setup the layered simulation to solve for the best fitting halfspace.\n",
964 | "\n",
965 | "Let's setup a new simulation that does this, we can re-use our previous survey object."
966 | ]
967 | },
968 | {
969 | "cell_type": "code",
970 | "execution_count": null,
971 | "id": "f2e846e1-d411-493c-b8c8-dddd365bdd50",
972 | "metadata": {
973 | "editable": true,
974 | "slideshow": {
975 | "slide_type": "subslide"
976 | },
977 | "tags": []
978 | },
979 | "outputs": [],
980 | "source": [
981 | "sim_inv1 = tdem.Simulation1DLayered(srv, sigmaMap=exp_map)"
982 | ]
983 | },
984 | {
985 | "cell_type": "markdown",
986 | "id": "aba24a2a-2580-4ac0-a159-29cf5bfdaa6a",
987 | "metadata": {},
988 | "source": [
989 | "When we assign a model (or pass a model to a function), the simulation uses the physical property maps to translate the model to the physical properties"
990 | ]
991 | },
992 | {
993 | "cell_type": "code",
994 | "execution_count": null,
995 | "id": "36c7ac2d-9a94-4ae3-879a-59c4db42297a",
996 | "metadata": {
997 | "editable": true,
998 | "slideshow": {
999 | "slide_type": "fragment"
1000 | },
1001 | "tags": []
1002 | },
1003 | "outputs": [],
1004 | "source": [
1005 | "# try setting the model and looking at sigma"
1006 | ]
1007 | },
1008 | {
1009 | "cell_type": "markdown",
1010 | "id": "f71d3fdb-2042-40c2-bc04-d24fbbc65d1a",
1011 | "metadata": {
1012 | "editable": true,
1013 | "slideshow": {
1014 | "slide_type": "subslide"
1015 | },
1016 | "tags": []
1017 | },
1018 | "source": [
1019 | "### Objective functions\n",
1020 | "\n",
1021 | "SimPEG treats inversions as minimization functions, as such we need an objective function to minimize. For parametric inversions a data misfit is usually sufficient.\n",
1022 | "\n",
1023 | "$$\n",
1024 | "\\phi = \\phi_d + \\beta \\phi_m\n",
1025 | "$$\n",
1026 | "\n",
1027 | "We will start by defining just a simple data misfit function\n",
1028 | "* It needs to know how to compute data\n",
1029 | "* The data set you want to compare against.\n",
1030 | "\n",
1031 | "It needs to be able to evaluate:\n",
1032 | "$$\n",
1033 | "\\phi_d = |W_d ( d_{obs} - F(m))|^2\n",
1034 | "$$"
1035 | ]
1036 | },
1037 | {
1038 | "cell_type": "code",
1039 | "execution_count": null,
1040 | "id": "e164c4a7-0d2e-46b3-a6ee-0b290626fc92",
1041 | "metadata": {
1042 | "editable": true,
1043 | "slideshow": {
1044 | "slide_type": "fragment"
1045 | },
1046 | "tags": []
1047 | },
1048 | "outputs": [],
1049 | "source": [
1050 | "from SimPEG.data_misfit import L2DataMisfit"
1051 | ]
1052 | },
1053 | {
1054 | "cell_type": "code",
1055 | "execution_count": null,
1056 | "id": "88e14b5a-3bbb-408a-a8e2-3939a7f95e96",
1057 | "metadata": {
1058 | "editable": true,
1059 | "slideshow": {
1060 | "slide_type": "fragment"
1061 | },
1062 | "tags": []
1063 | },
1064 | "outputs": [],
1065 | "source": [
1066 | "# phi_d_1"
1067 | ]
1068 | },
1069 | {
1070 | "cell_type": "markdown",
1071 | "id": "907c97d3-c17f-4552-83e0-d678e5bf7e76",
1072 | "metadata": {
1073 | "editable": true,
1074 | "slideshow": {
1075 | "slide_type": "fragment"
1076 | },
1077 | "tags": []
1078 | },
1079 | "source": [
1080 | "We can evaluate the data misfit of a particular model by calling this object. (It also knows how to do derivative and (approximate) Hessian operations needed for minimization)."
1081 | ]
1082 | },
1083 | {
1084 | "cell_type": "code",
1085 | "execution_count": null,
1086 | "id": "fafa2c5d-eee4-480e-b470-50b8d63e678c",
1087 | "metadata": {
1088 | "editable": true,
1089 | "slideshow": {
1090 | "slide_type": "fragment"
1091 | },
1092 | "tags": []
1093 | },
1094 | "outputs": [],
1095 | "source": [
1096 | "# we can evaluate this function."
1097 | ]
1098 | },
1099 | {
1100 | "cell_type": "markdown",
1101 | "id": "75c52a31-0d5c-4f63-8cf7-392a288add8f",
1102 | "metadata": {
1103 | "editable": true,
1104 | "slideshow": {
1105 | "slide_type": "subslide"
1106 | },
1107 | "tags": []
1108 | },
1109 | "source": [
1110 | "If we wanted to know the best fitting halfspace for this data we could simply minimze this function. Let's grab a Gauss Newton minimizer."
1111 | ]
1112 | },
1113 | {
1114 | "cell_type": "code",
1115 | "execution_count": null,
1116 | "id": "26527625-3792-45dc-b970-1b7250398440",
1117 | "metadata": {
1118 | "editable": true,
1119 | "slideshow": {
1120 | "slide_type": "fragment"
1121 | },
1122 | "tags": []
1123 | },
1124 | "outputs": [],
1125 | "source": [
1126 | "from SimPEG import optimization"
1127 | ]
1128 | },
1129 | {
1130 | "cell_type": "code",
1131 | "execution_count": null,
1132 | "id": "6f06b099-2ec9-45c4-b7eb-724707a96520",
1133 | "metadata": {
1134 | "editable": true,
1135 | "slideshow": {
1136 | "slide_type": "fragment"
1137 | },
1138 | "tags": []
1139 | },
1140 | "outputs": [],
1141 | "source": [
1142 | "minimizer = opt = optimization.InexactGaussNewton(\n",
1143 | " maxIter=10, maxIterLS=20, maxIterCG=10, tolCG=1e-3\n",
1144 | ")\n",
1145 | "# Here, Inexact means we are going to use CG to solve for the step direction."
1146 | ]
1147 | },
1148 | {
1149 | "cell_type": "markdown",
1150 | "id": "6f676bb1-b89b-4b99-80a0-5523be73dc59",
1151 | "metadata": {
1152 | "editable": true,
1153 | "slideshow": {
1154 | "slide_type": "subslide"
1155 | },
1156 | "tags": []
1157 | },
1158 | "source": [
1159 | "Define our inverse problem to be minimized:\n",
1160 | "One odd quirk of a SimPEG minimization is that we must pass a regularization object to our inverse problem. However we can set `beta=0` to ignore it."
1161 | ]
1162 | },
1163 | {
1164 | "cell_type": "code",
1165 | "execution_count": null,
1166 | "id": "6fb39c04-f952-4ade-9393-479d8ef8dae5",
1167 | "metadata": {
1168 | "editable": true,
1169 | "slideshow": {
1170 | "slide_type": "fragment"
1171 | },
1172 | "tags": []
1173 | },
1174 | "outputs": [],
1175 | "source": [
1176 | "from SimPEG import regularization, inverse_problem\n",
1177 | "\n",
1178 | "empty_reg = regularization.Smallness(discretize.TensorMesh([1]))\n",
1179 | "# note we needed to pass a mesh that had a single cell\n",
1180 | "# in it because our model has 1 value."
1181 | ]
1182 | },
1183 | {
1184 | "cell_type": "code",
1185 | "execution_count": null,
1186 | "id": "558acc60-b1a0-4287-89ae-17021f7de229",
1187 | "metadata": {
1188 | "editable": true,
1189 | "slideshow": {
1190 | "slide_type": "fragment"
1191 | },
1192 | "tags": []
1193 | },
1194 | "outputs": [],
1195 | "source": [
1196 | "#inv_prob_1\n",
1197 | "\n",
1198 | "# sets up, phi_d + 0 * phi_m, meaning the minimizer is only going to act on the data misfit term."
1199 | ]
1200 | },
1201 | {
1202 | "cell_type": "code",
1203 | "execution_count": null,
1204 | "id": "915f8919-eefc-46d3-bfdc-3ab929c59c0a",
1205 | "metadata": {
1206 | "editable": true,
1207 | "slideshow": {
1208 | "slide_type": "subslide"
1209 | },
1210 | "tags": []
1211 | },
1212 | "outputs": [],
1213 | "source": [
1214 | "from SimPEG import inversion"
1215 | ]
1216 | },
1217 | {
1218 | "cell_type": "code",
1219 | "execution_count": null,
1220 | "id": "d0ad33b0-891c-44c6-becc-af4c35b1d2d8",
1221 | "metadata": {
1222 | "editable": true,
1223 | "slideshow": {
1224 | "slide_type": "subslide"
1225 | },
1226 | "tags": []
1227 | },
1228 | "outputs": [],
1229 | "source": [
1230 | "# inv1"
1231 | ]
1232 | },
1233 | {
1234 | "cell_type": "code",
1235 | "execution_count": null,
1236 | "id": "3711219b-14b4-484b-b368-20c5478e0880",
1237 | "metadata": {
1238 | "editable": true,
1239 | "slideshow": {
1240 | "slide_type": "subslide"
1241 | },
1242 | "tags": []
1243 | },
1244 | "outputs": [],
1245 | "source": [
1246 | "# Run inversion\n",
1247 | "recovered_model = inv1.run(m_0)"
1248 | ]
1249 | },
1250 | {
1251 | "cell_type": "code",
1252 | "execution_count": null,
1253 | "id": "8da3ad9d-0d74-418b-b606-d4c5a5c68508",
1254 | "metadata": {
1255 | "editable": true,
1256 | "slideshow": {
1257 | "slide_type": "fragment"
1258 | },
1259 | "tags": []
1260 | },
1261 | "outputs": [],
1262 | "source": [
1263 | "# What was our recovered best fitting halfspace?"
1264 | ]
1265 | },
1266 | {
1267 | "cell_type": "markdown",
1268 | "id": "0cfd94bc-3f9c-48c1-bafc-d9dd02f7e3d2",
1269 | "metadata": {},
1270 | "source": [
1271 | "There's another useful way of computing data from a simulation, using the function\n",
1272 | "`make_synthetic_data` which returns a `Data` object instead of just the `numpy` array."
1273 | ]
1274 | },
1275 | {
1276 | "cell_type": "code",
1277 | "execution_count": null,
1278 | "id": "1c3cefdf-a8f4-4320-8c88-797d0edc6503",
1279 | "metadata": {
1280 | "editable": true,
1281 | "slideshow": {
1282 | "slide_type": "subslide"
1283 | },
1284 | "tags": []
1285 | },
1286 | "outputs": [],
1287 | "source": [
1288 | "# This function returns a data object\n",
1289 | "# (so we can easily index it with receivers for plotting)"
1290 | ]
1291 | },
1292 | {
1293 | "cell_type": "code",
1294 | "execution_count": null,
1295 | "id": "ae669815-ce21-4e61-9f7a-c406a193a609",
1296 | "metadata": {
1297 | "editable": true,
1298 | "slideshow": {
1299 | "slide_type": "subslide"
1300 | },
1301 | "tags": []
1302 | },
1303 | "outputs": [],
1304 | "source": [
1305 | "def plot_data(data_obj):\n",
1306 | " plt.loglog(lm_times, -data_obj[src_lm, rx_lm])\n",
1307 | " plt.loglog(hm_times, -data_obj[src_hm, rx_hm])\n",
1308 | " \n",
1309 | " plt.loglog(\n",
1310 | " lm_times, -data_container[src_lm, rx_lm], color='C0', marker='x', linestyle=\"\"\n",
1311 | " )\n",
1312 | " plt.loglog(\n",
1313 | " hm_times, -data_container[src_hm, rx_hm], color='C1', marker='x', linestyle=\"\"\n",
1314 | " )\n",
1315 | "plot_data(data_pre)"
1316 | ]
1317 | },
1318 | {
1319 | "cell_type": "markdown",
1320 | "id": "37d511d2-5cba-42da-9183-7568b02a6ad4",
1321 | "metadata": {
1322 | "editable": true,
1323 | "slideshow": {
1324 | "slide_type": "subslide"
1325 | },
1326 | "tags": []
1327 | },
1328 | "source": [
1329 | "clearly a 1D model fits this reasonably well (in general), can we do better?"
1330 | ]
1331 | },
1332 | {
1333 | "cell_type": "markdown",
1334 | "id": "7d0f1c3d-f650-414f-8469-dd5ffe4e34c7",
1335 | "metadata": {},
1336 | "source": [
1337 | "### Multiple layers?"
1338 | ]
1339 | },
1340 | {
1341 | "cell_type": "markdown",
1342 | "id": "0eea3f98-2055-46ed-a8ae-a703b7ac5eb0",
1343 | "metadata": {
1344 | "editable": true,
1345 | "slideshow": {
1346 | "slide_type": "subslide"
1347 | },
1348 | "tags": []
1349 | },
1350 | "source": [
1351 | "How do we solve for a multi (but few) layered model? We need to tell simpeg to invert for both conductivity and thickness.\n",
1352 | "\n",
1353 | "Since we also need to ensure thicknesses are positive, lets use another `ExpMap` for them, but now our `model` consists of both values representing conductivity and thicknesses, so we need to tell SimPEG which parts correspond to each of them. There is a simple helper class to construct these called a `Wires`. It is very simple it says the first `X` values correspond to part 1, the next `Y` correspong to part 2."
1354 | ]
1355 | },
1356 | {
1357 | "cell_type": "code",
1358 | "execution_count": null,
1359 | "id": "438eeb5c-003a-4f67-b467-cb1971da9f3f",
1360 | "metadata": {
1361 | "editable": true,
1362 | "slideshow": {
1363 | "slide_type": "fragment"
1364 | },
1365 | "tags": []
1366 | },
1367 | "outputs": [],
1368 | "source": [
1369 | "n_layers = 2\n",
1370 | "# wire_map"
1371 | ]
1372 | },
1373 | {
1374 | "cell_type": "markdown",
1375 | "id": "2f5881f3-c149-4b28-9a47-d45bd1247de2",
1376 | "metadata": {
1377 | "editable": true,
1378 | "slideshow": {
1379 | "slide_type": "fragment"
1380 | },
1381 | "tags": []
1382 | },
1383 | "source": [
1384 | "It contains to projection maps which split the model into its conductivities and thickness portions, we still need to use the `exp_map` to say we want to work in the log domain."
1385 | ]
1386 | },
1387 | {
1388 | "cell_type": "code",
1389 | "execution_count": null,
1390 | "id": "053863d4-7a9e-4501-9828-b221cae506ac",
1391 | "metadata": {
1392 | "editable": true,
1393 | "slideshow": {
1394 | "slide_type": "fragment"
1395 | },
1396 | "tags": []
1397 | },
1398 | "outputs": [],
1399 | "source": [
1400 | "# setup with exponent map\n",
1401 | "# sigma_map\n",
1402 | "# thick_map"
1403 | ]
1404 | },
1405 | {
1406 | "cell_type": "code",
1407 | "execution_count": null,
1408 | "id": "6334a912-1b2f-4ce3-8723-c642401fb780",
1409 | "metadata": {
1410 | "editable": true,
1411 | "slideshow": {
1412 | "slide_type": "subslide"
1413 | },
1414 | "tags": []
1415 | },
1416 | "outputs": [],
1417 | "source": [
1418 | "# let's use the best fitting half-space as our initial model\n",
1419 | "# setup initial model\n",
1420 | "# m_sigma_0_2\n",
1421 | "# m_h_0_2\n",
1422 | "m_0_2 = np.r_[m_sigma_0_2, m_h_0_2]\n",
1423 | "\n",
1424 | "sim_inv2 = tdem.Simulation1DLayered(srv, sigmaMap=sigma_map, thicknessesMap=thick_map)"
1425 | ]
1426 | },
1427 | {
1428 | "cell_type": "code",
1429 | "execution_count": null,
1430 | "id": "39bb1129-7bd6-461d-b64c-a28daa07eb3c",
1431 | "metadata": {},
1432 | "outputs": [],
1433 | "source": [
1434 | "# The simulation knows how to go from the model to the physical properties\n",
1435 | "sim_inv2.model = m_0_2\n",
1436 | "# sim_inv2. ..."
1437 | ]
1438 | },
1439 | {
1440 | "cell_type": "markdown",
1441 | "id": "37ac4ac7-e316-427c-8111-768425fd8af8",
1442 | "metadata": {},
1443 | "source": [
1444 | "Now we can set up the same pieces as the half-space inversion."
1445 | ]
1446 | },
1447 | {
1448 | "cell_type": "code",
1449 | "execution_count": null,
1450 | "id": "3a6ef5f3-cb03-4967-932b-99ed94297346",
1451 | "metadata": {
1452 | "editable": true,
1453 | "slideshow": {
1454 | "slide_type": "fragment"
1455 | },
1456 | "tags": []
1457 | },
1458 | "outputs": [],
1459 | "source": [
1460 | "phi_d_2 = L2DataMisfit(data=data_container, simulation=sim_inv2)\n",
1461 | "\n",
1462 | "# create all of the same components of the inversion problem as before\n",
1463 | "minimizer2 = opt = optimization.InexactGaussNewton(\n",
1464 | " maxIter=10, maxIterLS=20, maxIterCG=10, tolCG=1e-3\n",
1465 | ")\n",
1466 | "\n",
1467 | "# again create a reg that has the same input size as the model vector, and then turn it off by setting `beta=0`\n",
1468 | "empty_reg = regularization.Smallness(discretize.TensorMesh([len(m_0_2)]))\n",
1469 | "inv_prob_2 = inverse_problem.BaseInvProblem(\n",
1470 | " phi_d_2, reg=empty_reg, opt=minimizer, beta=0.0\n",
1471 | ")"
1472 | ]
1473 | },
1474 | {
1475 | "cell_type": "markdown",
1476 | "id": "277f0ae4-c671-41eb-af48-4066fa347b57",
1477 | "metadata": {},
1478 | "source": [
1479 | "And run it!"
1480 | ]
1481 | },
1482 | {
1483 | "cell_type": "code",
1484 | "execution_count": null,
1485 | "id": "a851bd4e-5d54-442f-97c4-355a6be4fd26",
1486 | "metadata": {
1487 | "editable": true,
1488 | "slideshow": {
1489 | "slide_type": "subslide"
1490 | },
1491 | "tags": []
1492 | },
1493 | "outputs": [],
1494 | "source": [
1495 | "inv2 = inversion.BaseInversion(inv_prob_2, [])\n",
1496 | "\n",
1497 | "# Run inversion\n",
1498 | "recovered_model_2 = inv2.run(m_0_2)"
1499 | ]
1500 | },
1501 | {
1502 | "cell_type": "code",
1503 | "execution_count": null,
1504 | "id": "bfda91e2-e622-46e6-8266-a46b27f52aaf",
1505 | "metadata": {
1506 | "editable": true,
1507 | "slideshow": {
1508 | "slide_type": "fragment"
1509 | },
1510 | "tags": []
1511 | },
1512 | "outputs": [],
1513 | "source": [
1514 | "data_pre_2 = sim_inv2.make_synthetic_data(recovered_model_2)\n",
1515 | "sim_inv2.sigma, sim_inv2.thicknesses"
1516 | ]
1517 | },
1518 | {
1519 | "cell_type": "code",
1520 | "execution_count": null,
1521 | "id": "c6fc988c-88da-4abe-86c4-f807a0cbb6f9",
1522 | "metadata": {},
1523 | "outputs": [],
1524 | "source": [
1525 | "ax = plot_1d_layer_model(sim_inv2.thicknesses, sim_inv2.sigma)\n",
1526 | "ax.axvline(sim_inv1.sigma, color='C1')\n",
1527 | "ax.set_ylim([50, 0])\n",
1528 | "ax.set_xlim([1E-2, 1E0]);"
1529 | ]
1530 | },
1531 | {
1532 | "cell_type": "code",
1533 | "execution_count": null,
1534 | "id": "3bb7f174-91d5-4a41-83ac-2a511dd950b2",
1535 | "metadata": {
1536 | "editable": true,
1537 | "slideshow": {
1538 | "slide_type": "subslide"
1539 | },
1540 | "tags": []
1541 | },
1542 | "outputs": [],
1543 | "source": [
1544 | "plot_data(data_pre_2)"
1545 | ]
1546 | },
1547 | {
1548 | "cell_type": "markdown",
1549 | "id": "040bbfc8-e8c7-413d-9427-e7678400c940",
1550 | "metadata": {
1551 | "editable": true,
1552 | "slideshow": {
1553 | "slide_type": "subslide"
1554 | },
1555 | "tags": []
1556 | },
1557 | "source": [
1558 | "We could keep going this route to add more and more layers, but at some point we start to enter the realm of underdetermined problems. E.g. when the number of model parameters exceed the number of observed data. For this we need to think about the regularization objective function."
1559 | ]
1560 | },
1561 | {
1562 | "cell_type": "markdown",
1563 | "id": "658f8521-eade-44a9-b75e-8f3a26edc24a",
1564 | "metadata": {
1565 | "editable": true,
1566 | "slideshow": {
1567 | "slide_type": "slide"
1568 | },
1569 | "tags": []
1570 | },
1571 | "source": [
1572 | "## Regularized inversion\n",
1573 | "\n",
1574 | "Let's keep building off of the pieces we had before. Let's discretize our model into many different layers. But this time we will only invert for the conductivity of each layer. For this we need to set up a mesh. This should be a 1D mesh, and it's single dimension would represent depth, aka we define z+ down for this example (Most simpeg is z+ up though)."
1575 | ]
1576 | },
1577 | {
1578 | "cell_type": "markdown",
1579 | "id": "0ec59d6a-beae-4e6a-bc35-c0a56603af24",
1580 | "metadata": {},
1581 | "source": [
1582 | "We're going to define it with a single cell at 1m thickness, then increase the sizes geometrically by a factor of 1.05, for a total of 64 cells."
1583 | ]
1584 | },
1585 | {
1586 | "cell_type": "code",
1587 | "execution_count": null,
1588 | "id": "f37eb6d2-781a-4a9d-849b-faa57553f7cd",
1589 | "metadata": {
1590 | "editable": true,
1591 | "slideshow": {
1592 | "slide_type": "subslide"
1593 | },
1594 | "tags": []
1595 | },
1596 | "outputs": [],
1597 | "source": [
1598 | "# We can setup a mesh discretization using some shorthand:\n",
1599 | "# h = [dh, (dh, n_1), (dh, n_2, expansion_factor)\n",
1600 | "# h\n",
1601 | "# mesh"
1602 | ]
1603 | },
1604 | {
1605 | "cell_type": "markdown",
1606 | "id": "4230c6f9-30d2-4ae6-a4d5-f6087010d5fd",
1607 | "metadata": {},
1608 | "source": [
1609 | "This time we only need to setup a simulation that inverts for conductivity, but we give it the thickness of each layer.\n",
1610 | "Which would be the `h[0]` property on the mesh, but truncated to 63 cells. (The last layer is interpreted as infinity depth."
1611 | ]
1612 | },
1613 | {
1614 | "cell_type": "code",
1615 | "execution_count": null,
1616 | "id": "c4b5f8d7-8a10-4ac6-b838-4c48d6dd6fdf",
1617 | "metadata": {
1618 | "editable": true,
1619 | "slideshow": {
1620 | "slide_type": "fragment"
1621 | },
1622 | "tags": []
1623 | },
1624 | "outputs": [],
1625 | "source": [
1626 | "# sim_reg"
1627 | ]
1628 | },
1629 | {
1630 | "cell_type": "markdown",
1631 | "id": "bd5f744f-69f4-4e29-8b09-416313a08928",
1632 | "metadata": {},
1633 | "source": [
1634 | "Define yet another data misfit measure"
1635 | ]
1636 | },
1637 | {
1638 | "cell_type": "code",
1639 | "execution_count": null,
1640 | "id": "f485549e-9d66-403a-99af-63f295beef3a",
1641 | "metadata": {},
1642 | "outputs": [],
1643 | "source": [
1644 | "phi_d_reg = L2DataMisfit(data=data_container, simulation=sim_reg)"
1645 | ]
1646 | },
1647 | {
1648 | "cell_type": "markdown",
1649 | "id": "7216c7dd-4dbf-48d8-9b0a-5750e54bf672",
1650 | "metadata": {},
1651 | "source": [
1652 | "Now we need to also create a meaningful regularization function.\n",
1653 | "\n",
1654 | "We're going to use something like:\n",
1655 | "$$\n",
1656 | "\\phi_m = \\alpha_v \\int_V m^2 dV + \\alpha_z \\int_V (\\frac{\\partial m}{\\partial z})^2 dV\n",
1657 | "$$"
1658 | ]
1659 | },
1660 | {
1661 | "cell_type": "code",
1662 | "execution_count": null,
1663 | "id": "a7a69975-25be-4253-8bdc-481fed21164a",
1664 | "metadata": {
1665 | "editable": true,
1666 | "slideshow": {
1667 | "slide_type": "subslide"
1668 | },
1669 | "tags": []
1670 | },
1671 | "outputs": [],
1672 | "source": [
1673 | "# This class creates a function that measures the smallness\n",
1674 | "# and the smoothness of the model.\n",
1675 | "# reg WeightedLeastSquares"
1676 | ]
1677 | },
1678 | {
1679 | "cell_type": "markdown",
1680 | "id": "4c92e22f-b881-482f-95d4-2c47c67615d3",
1681 | "metadata": {},
1682 | "source": [
1683 | "This class has some good default choices for $\\alpha_s$ and $\\alpha_x$\n",
1684 | "\n",
1685 | "$$\\alpha_s=1$$\n",
1686 | "$$\\alpha_z = min(mesh.h)^2$$"
1687 | ]
1688 | },
1689 | {
1690 | "cell_type": "code",
1691 | "execution_count": null,
1692 | "id": "69ffcbdb-247d-4e07-9645-e31dae279fdb",
1693 | "metadata": {
1694 | "editable": true,
1695 | "slideshow": {
1696 | "slide_type": "fragment"
1697 | },
1698 | "tags": []
1699 | },
1700 | "outputs": [],
1701 | "source": [
1702 | "# m_0_reg"
1703 | ]
1704 | },
1705 | {
1706 | "cell_type": "code",
1707 | "execution_count": null,
1708 | "id": "9f7001f2-8c39-4115-920f-5c53ed4f18ed",
1709 | "metadata": {
1710 | "editable": true,
1711 | "slideshow": {
1712 | "slide_type": "fragment"
1713 | },
1714 | "tags": []
1715 | },
1716 | "outputs": [],
1717 | "source": [
1718 | "\n",
1719 | "# create all of the same components of the inversion problem as before\n",
1720 | "minimizer_reg = opt = optimization.InexactGaussNewton(\n",
1721 | " maxIter=10, maxIterLS=20, maxIterCG=10, tolCG=1e-3\n",
1722 | ")\n",
1723 | "\n",
1724 | "# inv_prob_3"
1725 | ]
1726 | },
1727 | {
1728 | "cell_type": "code",
1729 | "execution_count": null,
1730 | "id": "44c979e5-d738-4d15-9ac5-a441201520d1",
1731 | "metadata": {
1732 | "editable": true,
1733 | "slideshow": {
1734 | "slide_type": "subslide"
1735 | },
1736 | "tags": []
1737 | },
1738 | "outputs": [],
1739 | "source": [
1740 | "inv3 = inversion.BaseInversion(inv_prob_3, [])\n",
1741 | "\n",
1742 | "# Run inversion\n",
1743 | "recovered_model_3 = inv3.run(m_0_reg)"
1744 | ]
1745 | },
1746 | {
1747 | "cell_type": "code",
1748 | "execution_count": null,
1749 | "id": "c92eeb9e-0563-4a3f-83ed-65078905f260",
1750 | "metadata": {
1751 | "editable": true,
1752 | "slideshow": {
1753 | "slide_type": "fragment"
1754 | },
1755 | "tags": []
1756 | },
1757 | "outputs": [],
1758 | "source": [
1759 | "data_pre_3 = sim_reg.make_synthetic_data(recovered_model_3)\n",
1760 | "ax = plot_1d_layer_model(sim_inv2.thicknesses, sim_inv2.sigma)\n",
1761 | "ax.axvline(sim_inv1.sigma, color='C1')\n",
1762 | "plot_1d_layer_model(sim_reg.thicknesses, sim_reg.sigma, ax=ax, color='C2')\n",
1763 | "ax.set_ylim([100, 0])\n",
1764 | "ax.set_xlim([1E-2, 1E0])"
1765 | ]
1766 | },
1767 | {
1768 | "cell_type": "code",
1769 | "execution_count": null,
1770 | "id": "fe0528e3-ab60-4206-bbd3-4f332fcbadeb",
1771 | "metadata": {},
1772 | "outputs": [],
1773 | "source": [
1774 | "plot_data(data_pre_3)"
1775 | ]
1776 | },
1777 | {
1778 | "cell_type": "markdown",
1779 | "id": "a4d8a0c0-e6d1-4af1-865e-68b2ddfd2302",
1780 | "metadata": {},
1781 | "source": [
1782 | "If we knew our noise level, We usually look for a model such that tit's $\\phi_d(m) <= nD/2$. This comes from the expected value of a chi^2 disrtibutionl."
1783 | ]
1784 | },
1785 | {
1786 | "cell_type": "markdown",
1787 | "id": "0fe1cc40-1b99-4434-9288-5b6f9d0a132b",
1788 | "metadata": {
1789 | "editable": true,
1790 | "slideshow": {
1791 | "slide_type": "subslide"
1792 | },
1793 | "tags": []
1794 | },
1795 | "source": [
1796 | "This process of manually adjusting the regularization parameter is a bit tiresome. In SimPEG we can use `Directive`s to automate this process for us.\n",
1797 | "\n",
1798 | "Say we want to decrease the regularization parameter by a factor of 5 every two iterations:"
1799 | ]
1800 | },
1801 | {
1802 | "cell_type": "code",
1803 | "execution_count": null,
1804 | "id": "213d1744-3984-4a83-9f6d-b1e6f687e7cd",
1805 | "metadata": {
1806 | "editable": true,
1807 | "slideshow": {
1808 | "slide_type": "fragment"
1809 | },
1810 | "tags": []
1811 | },
1812 | "outputs": [],
1813 | "source": [
1814 | "from SimPEG import directives"
1815 | ]
1816 | },
1817 | {
1818 | "cell_type": "code",
1819 | "execution_count": null,
1820 | "id": "234c56be-b981-407f-a700-2f6fe6cb8acf",
1821 | "metadata": {
1822 | "editable": true,
1823 | "slideshow": {
1824 | "slide_type": "fragment"
1825 | },
1826 | "tags": []
1827 | },
1828 | "outputs": [],
1829 | "source": [
1830 | "# beta_cooler BetaSchedule"
1831 | ]
1832 | },
1833 | {
1834 | "cell_type": "markdown",
1835 | "id": "7a4564fb-ff68-4836-8184-04a7a165a643",
1836 | "metadata": {
1837 | "editable": true,
1838 | "slideshow": {
1839 | "slide_type": "fragment"
1840 | },
1841 | "tags": []
1842 | },
1843 | "source": [
1844 | "we also would likely want to keep track of the model and the function values at each iteration"
1845 | ]
1846 | },
1847 | {
1848 | "cell_type": "code",
1849 | "execution_count": null,
1850 | "id": "a35c5d5e-1212-45e5-be2b-a77d43b5b21e",
1851 | "metadata": {
1852 | "editable": true,
1853 | "slideshow": {
1854 | "slide_type": "fragment"
1855 | },
1856 | "tags": []
1857 | },
1858 | "outputs": [],
1859 | "source": [
1860 | "# save_dict SaveOutputDictEveryIteration"
1861 | ]
1862 | },
1863 | {
1864 | "cell_type": "markdown",
1865 | "id": "72ef54c8-c877-4f53-bedf-4a5e448278aa",
1866 | "metadata": {
1867 | "editable": true,
1868 | "slideshow": {
1869 | "slide_type": "fragment"
1870 | },
1871 | "tags": []
1872 | },
1873 | "source": [
1874 | "We combine these into a list, and pass them to the inversion."
1875 | ]
1876 | },
1877 | {
1878 | "cell_type": "code",
1879 | "execution_count": null,
1880 | "id": "93654801-2790-484f-b84c-42882e82bb65",
1881 | "metadata": {
1882 | "editable": true,
1883 | "slideshow": {
1884 | "slide_type": "subslide"
1885 | },
1886 | "tags": []
1887 | },
1888 | "outputs": [],
1889 | "source": [
1890 | "# create all of the same components of the inversion problem as before\n",
1891 | "# increase the number of iterations from 10 to 20, maybe do a few more CG iterations\n",
1892 | "minimizer_reg = opt = optimization.InexactGaussNewton(\n",
1893 | " maxIter=10, maxIterLS=20, maxIterCG=10, tolCG=1e-3\n",
1894 | ")\n",
1895 | "\n",
1896 | "# choose a starting beta\n",
1897 | "inv_prob_4 = inverse_problem.BaseInvProblem(\n",
1898 | " phi_d_reg, reg=reg, opt=minimizer_reg, beta=10\n",
1899 | ")\n",
1900 | "\n",
1901 | "inv4 = inversion.BaseInversion(inv_prob_4, [save_dict, beta_cooler])\n",
1902 | "\n",
1903 | "# Run inversion\n",
1904 | "recovered_model_4 = inv4.run(m_0_reg)"
1905 | ]
1906 | },
1907 | {
1908 | "cell_type": "markdown",
1909 | "id": "22aaa446-16c7-4037-9117-dda0d41718ac",
1910 | "metadata": {},
1911 | "source": [
1912 | "The returned model here is just the last model from the inversion run, it might not necessarily be the best one though..."
1913 | ]
1914 | },
1915 | {
1916 | "cell_type": "markdown",
1917 | "id": "78264d1b-ef41-4f72-81de-81e6a1486b3f",
1918 | "metadata": {},
1919 | "source": [
1920 | "The `save_dict.outDict` dictionary is indexed per iteration, returning another dictionary containing the parameters and function evaluations. Let's collect all of the $\\phi_d$, $\\phi_m$ and $\\beta$"
1921 | ]
1922 | },
1923 | {
1924 | "cell_type": "code",
1925 | "execution_count": null,
1926 | "id": "29513afe-85bd-4c39-8577-f5f376d0702b",
1927 | "metadata": {
1928 | "editable": true,
1929 | "slideshow": {
1930 | "slide_type": "subslide"
1931 | },
1932 | "tags": []
1933 | },
1934 | "outputs": [],
1935 | "source": [
1936 | "n_iter = save_dict.opt.iter\n",
1937 | "phi_ds = [save_dict.outDict[i]['phi_d'] for i in range(1, n_iter)]\n",
1938 | "phi_ms = [save_dict.outDict[i]['phi_m'] for i in range(1, n_iter)]\n",
1939 | "betas = [save_dict.outDict[i]['beta'] for i in range(1, n_iter)]"
1940 | ]
1941 | },
1942 | {
1943 | "cell_type": "markdown",
1944 | "id": "383e7c41-7ded-4f79-8d58-33ba9481adb2",
1945 | "metadata": {},
1946 | "source": [
1947 | "We can make a few plots to investigate how these function evaluations change per iteration"
1948 | ]
1949 | },
1950 | {
1951 | "cell_type": "code",
1952 | "execution_count": null,
1953 | "id": "36b27ed3-cc43-4a0a-b08b-7aff531a1b14",
1954 | "metadata": {
1955 | "editable": true,
1956 | "slideshow": {
1957 | "slide_type": "fragment"
1958 | },
1959 | "tags": []
1960 | },
1961 | "outputs": [],
1962 | "source": [
1963 | "# Make some plots of phi_d, phi_m and beta\n",
1964 | "# choose a \"final\" model\n"
1965 | ]
1966 | },
1967 | {
1968 | "cell_type": "markdown",
1969 | "id": "1ae33479-be31-456f-8e71-458d10bac6e4",
1970 | "metadata": {},
1971 | "source": [
1972 | "And what does the model and data look like at this point?"
1973 | ]
1974 | },
1975 | {
1976 | "cell_type": "code",
1977 | "execution_count": null,
1978 | "id": "8c71501c-1a07-4041-b0f2-e6b4eb807119",
1979 | "metadata": {
1980 | "editable": true,
1981 | "slideshow": {
1982 | "slide_type": "subslide"
1983 | },
1984 | "tags": []
1985 | },
1986 | "outputs": [],
1987 | "source": [
1988 | "# m_final\n",
1989 | "d_final = sim_reg.make_synthetic_data(m_final)\n",
1990 | "ax = plot_1d_layer_model(sim_reg.thicknesses, exp_map * m_final)\n",
1991 | "plot_1d_layer_model(sim_inv2.thicknesses, sim_inv2.sigma, ax=ax)\n",
1992 | "ax.set_ylim([100, 0])\n",
1993 | "ax.set_xlim([1E-2, 1E0])"
1994 | ]
1995 | },
1996 | {
1997 | "cell_type": "code",
1998 | "execution_count": null,
1999 | "id": "ba404fc7-e7fb-4539-930c-b9e3926af9ba",
2000 | "metadata": {
2001 | "editable": true,
2002 | "slideshow": {
2003 | "slide_type": "subslide"
2004 | },
2005 | "tags": []
2006 | },
2007 | "outputs": [],
2008 | "source": [
2009 | "plot_data(d_final)"
2010 | ]
2011 | },
2012 | {
2013 | "cell_type": "markdown",
2014 | "id": "27c72d17-991c-453a-babe-1c42cd67421c",
2015 | "metadata": {},
2016 | "source": [
2017 | "**note** The overall fit looks fairly close."
2018 | ]
2019 | },
2020 | {
2021 | "cell_type": "markdown",
2022 | "id": "05ef7c09-7783-4f45-8e39-41031df55d06",
2023 | "metadata": {
2024 | "editable": true,
2025 | "slideshow": {
2026 | "slide_type": "subslide"
2027 | },
2028 | "tags": []
2029 | },
2030 | "source": [
2031 | "You might ask..\n",
2032 | "\"What is the noise level corresponding to this regularization parameter?\"\n",
2033 | "There's a few ways you can define it, let's compare the relative sizes of the data residual vector to the observed data vector."
2034 | ]
2035 | },
2036 | {
2037 | "cell_type": "code",
2038 | "execution_count": null,
2039 | "id": "35704fe8-d9f5-40ea-bd31-3a1567f9c739",
2040 | "metadata": {
2041 | "editable": true,
2042 | "slideshow": {
2043 | "slide_type": "fragment"
2044 | },
2045 | "tags": []
2046 | },
2047 | "outputs": [],
2048 | "source": [
2049 | "# rel_diff"
2050 | ]
2051 | }
2052 | ],
2053 | "metadata": {
2054 | "kernelspec": {
2055 | "display_name": "Python 3 (ipykernel)",
2056 | "language": "python",
2057 | "name": "python3"
2058 | },
2059 | "language_info": {
2060 | "codemirror_mode": {
2061 | "name": "ipython",
2062 | "version": 3
2063 | },
2064 | "file_extension": ".py",
2065 | "mimetype": "text/x-python",
2066 | "name": "python",
2067 | "nbconvert_exporter": "python",
2068 | "pygments_lexer": "ipython3",
2069 | "version": "3.11.7"
2070 | }
2071 | },
2072 | "nbformat": 4,
2073 | "nbformat_minor": 5
2074 | }
2075 |
--------------------------------------------------------------------------------