mirror of
https://github.com/wassname/simpeg.git
synced 2026-09-13 13:03:14 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f329f65a | ||
|
|
82c1afd128 | ||
|
|
7fd6ddf62a | ||
|
|
46a8707b64 | ||
|
|
6a2caf5efc | ||
|
|
ab1108c9c2 | ||
|
|
b30fe88a7a | ||
|
|
25cd9c16a7 | ||
|
|
8412ad90c2 | ||
|
|
cb2151b1d6 | ||
|
|
20c35f3d16 | ||
|
|
5d67f79257 | ||
|
|
9062f6d326 | ||
|
|
db63779b9b | ||
|
|
d31ef027d7 | ||
|
|
f73d0a3b4a | ||
|
|
e4506a8773 | ||
|
|
e4cbc584dc | ||
|
|
834de58284 | ||
|
|
cba52c3b81 | ||
|
|
6e0a54b319 | ||
|
|
5c8fba4242 | ||
|
|
020332aec5 | ||
|
|
ac008e7253 | ||
|
|
52cc92f699 | ||
|
|
8bd4eedb83 | ||
|
|
bcda60815e | ||
|
|
f37235973b | ||
|
|
feff936c92 | ||
|
|
6aa9b533ba | ||
|
|
4df932ccfc | ||
|
|
3e673d34f1 | ||
|
|
f1527f994b | ||
|
|
0646a930ab | ||
|
|
0edbc9f6ca | ||
|
|
ce49249664 | ||
|
|
4c3c2c361c | ||
|
|
40d39d751a | ||
|
|
649525fa88 | ||
|
|
922bdf93e1 | ||
|
|
cd352dc5f7 | ||
|
|
a7c35abd56 | ||
|
|
a5be262171 | ||
|
|
16239aa414 | ||
|
|
9bf4a6228d | ||
|
|
226a28f611 | ||
|
|
4c547d22c6 | ||
|
|
fb1973cfe4 | ||
|
|
4067106956 | ||
|
|
980a382613 | ||
|
|
e464fc0dfd | ||
|
|
015b940130 | ||
|
|
6ccfa71dd7 | ||
|
|
1d06b768e2 | ||
|
|
cb9e820eb3 | ||
|
|
d6664155c6 | ||
|
|
e4c30705ff | ||
|
|
059850f282 | ||
|
|
ea6ec4cb55 | ||
|
|
20f7f9be1d | ||
|
|
44359d4306 | ||
|
|
2c4055aec1 | ||
|
|
3f2396ad33 | ||
|
|
8b152f3890 | ||
|
|
64d89cfb6c | ||
|
|
c1bd8edf77 | ||
|
|
d9c94b9793 | ||
|
|
186708f8de | ||
|
|
c8c034cc2d | ||
|
|
60e18eef01 | ||
|
|
8ba9815137 | ||
|
|
463b9b6164 | ||
|
|
97336129de | ||
|
|
cdf7c0edc1 | ||
|
|
92c83c88d4 | ||
|
|
5da9235eb4 | ||
|
|
e8712a9f4a | ||
|
|
00774176f3 | ||
|
|
f9359b7f08 | ||
|
|
81c13b12e3 | ||
|
|
cb9a70bacc | ||
|
|
e7b69dccfd | ||
|
|
e4fc128383 | ||
|
|
a80eac7dc3 | ||
|
|
994cba529b | ||
|
|
7a6d0c9fdd | ||
|
|
bdfabe620a | ||
|
|
9b67956630 | ||
|
|
ac8f26be3e | ||
|
|
2e78e09bd0 | ||
|
|
72a5009d9d | ||
|
|
5515f7dadd | ||
|
|
3de8fb5f15 | ||
|
|
5705fabc5a | ||
|
|
73edef5eb7 | ||
|
|
8a7e72f3e8 | ||
|
|
2fb8de708a | ||
|
|
8262ee94bc | ||
|
|
d49a16d7e9 | ||
|
|
1f1522da01 | ||
|
|
0ad24f175b | ||
|
|
7f14c32124 | ||
|
|
dc6009f9c8 | ||
|
|
e2f30ffbb6 | ||
|
|
34acd4876b | ||
|
|
ac7ba39732 | ||
|
|
269269daf7 | ||
|
|
9b65fa80c9 | ||
|
|
2a84f1556e | ||
|
|
5b78d69c6d | ||
|
|
b8bd011662 | ||
|
|
345e39ed43 | ||
|
|
b81b5af461 | ||
|
|
32c936c4e3 | ||
|
|
01bf46c8bf | ||
|
|
664589f856 | ||
|
|
05ce38013e | ||
|
|
7c2d803dfe | ||
|
|
abc464ab46 | ||
|
|
2adc7d4eb9 | ||
|
|
92b5435149 | ||
|
|
78584ae49d | ||
|
|
b16b1b7526 | ||
|
|
28a8a2ea80 | ||
|
|
04d977f861 | ||
|
|
254fd1c029 | ||
|
|
2e3a6ddd96 | ||
|
|
8340214d73 | ||
|
|
451cd46801 | ||
|
|
b4c4841976 | ||
|
|
ef201be832 | ||
|
|
54ec7187cb | ||
|
|
1457e51fc1 | ||
|
|
5dab7ac1a8 | ||
|
|
967dc83fa8 | ||
|
|
3431fb46eb | ||
|
|
0704c6dc25 | ||
|
|
37d37369d8 | ||
|
|
b013508270 | ||
|
|
20d5582c0f | ||
|
|
caba2b6752 | ||
|
|
02d840a40e | ||
|
|
9f5b2e2dc1 | ||
|
|
85b55139e8 | ||
|
|
43c49d5f15 | ||
|
|
1bcb572c45 | ||
|
|
67d3cb4d9b | ||
|
|
860bd5638a | ||
|
|
4bcc9e0850 | ||
|
|
09161ff68e | ||
|
|
c24416ea12 | ||
|
|
f5333f35a2 | ||
|
|
2702c3573b | ||
|
|
d50232b385 | ||
|
|
b67bcfad31 | ||
|
|
d7be0ada31 | ||
|
|
7b94021f7e | ||
|
|
e8a6a028c2 | ||
|
|
06c82ba16d | ||
|
|
a8539cc234 | ||
|
|
7e45390611 | ||
|
|
e276ca70d6 | ||
|
|
814bd00346 | ||
|
|
096a72fdb4 | ||
|
|
78d710de57 | ||
|
|
bd488a4f0b | ||
|
|
d363a125b6 | ||
|
|
f3bf17ebe5 | ||
|
|
37ec0835c2 | ||
|
|
84400650fa | ||
|
|
6fcd826673 | ||
|
|
91e37c5fa7 | ||
|
|
98f209d1f0 | ||
|
|
7107cd9d94 | ||
|
|
e254c04297 | ||
|
|
bb3f9a6a87 | ||
|
|
fac3f63fba | ||
|
|
8da4d78bde | ||
|
|
0b971f4a50 | ||
|
|
7a4aa4ddc3 | ||
|
|
28b8a69d7b | ||
|
|
570dfb7aba | ||
|
|
c6e90230d4 | ||
|
|
b63434b89d | ||
|
|
558e8879fe | ||
|
|
e15913cf84 | ||
|
|
01b1122fcf | ||
|
|
1700f4f9c0 | ||
|
|
d77b393d42 | ||
|
|
b9e1d794f6 | ||
|
|
32f3ef301a | ||
|
|
2dfacdc904 | ||
|
|
2cf3d5d195 | ||
|
|
02afcabd7d | ||
|
|
17348e14e4 | ||
|
|
cdf0ebf8d0 | ||
|
|
dedabcc15f | ||
|
|
ff0c81b96e | ||
|
|
2c5b19b7a0 | ||
|
|
8a61259cab | ||
|
|
f482f9d877 | ||
|
|
1ba96ba8d0 | ||
|
|
2dceea25ec | ||
|
|
d332d3068a | ||
|
|
5d38ea5e11 | ||
|
|
27edc7742f | ||
|
|
5b9dae2930 | ||
|
|
f82efee096 | ||
|
|
9d861267e1 | ||
|
|
66156481da | ||
|
|
4342450360 | ||
|
|
77f476e248 | ||
|
|
79f7ca7a1e | ||
|
|
fc3893c72f | ||
|
|
e42727610a | ||
|
|
84eb69f626 | ||
|
|
69d109524e | ||
|
|
2a76852e33 | ||
|
|
2aa523f0ad | ||
|
|
3d52778274 | ||
|
|
22a6310a59 | ||
|
|
ba2ac74740 | ||
|
|
4b7f7c3c14 | ||
|
|
d6585dcfcd | ||
|
|
e678affe41 | ||
|
|
1a40e35c26 | ||
|
|
f2c7cfab78 | ||
|
|
9e69455d6e | ||
|
|
b620c4286b | ||
|
|
adda7a43dc | ||
|
|
884d27b541 | ||
|
|
b3ceccf303 | ||
|
|
20585bad09 | ||
|
|
65d221ee36 | ||
|
|
8b53fd5965 | ||
|
|
c298ebe8d8 | ||
|
|
3972178069 | ||
|
|
1c39fcf680 | ||
|
|
3215730d23 | ||
|
|
25ad1488f5 | ||
|
|
2fbcc076f9 | ||
|
|
a8551f3e04 | ||
|
|
589cd655af | ||
|
|
7da637e883 | ||
|
|
a7ab0dc1e2 | ||
|
|
e30a7bcafc | ||
|
|
c4d34c4e0d | ||
|
|
cfc921b667 | ||
|
|
83cb5ce46a | ||
|
|
528253a8cb | ||
|
|
898215222a | ||
|
|
058de2fd96 | ||
|
|
44febb35b8 | ||
|
|
4a8bc16634 | ||
|
|
4de169c591 | ||
|
|
4079f28e00 | ||
|
|
58efd8710f | ||
|
|
ad20c73f18 | ||
|
|
fa20309bc9 | ||
|
|
592d27ecc5 | ||
|
|
6a744bab30 | ||
|
|
3b88366681 | ||
|
|
015099e72e | ||
|
|
fc0a413b3c | ||
|
|
d7234cea9e | ||
|
|
9f69a33512 | ||
|
|
fc8f3dd956 | ||
|
|
3a51be6500 | ||
|
|
0e81faf217 | ||
|
|
960cb0a3e5 | ||
|
|
42bc4404ba | ||
|
|
9d6a1dcac6 | ||
|
|
7d913ef178 | ||
|
|
a963514f7e | ||
|
|
c36dce943e | ||
|
|
0e45d3674a | ||
|
|
1654c1c8b5 | ||
|
|
dea7eba5e0 | ||
|
|
5eccffffb5 | ||
|
|
b26412e7a9 | ||
|
|
362638cc39 | ||
|
|
295eeb6a25 | ||
|
|
ba1a85e269 | ||
|
|
50ffc734c5 | ||
|
|
3990459c7c | ||
|
|
452cd7e1da | ||
|
|
57fcf53542 | ||
|
|
c72e3f5a80 | ||
|
|
2dfce560f7 | ||
|
|
30a1f10b29 | ||
|
|
7703f8fa89 | ||
|
|
6ac63f12c6 | ||
|
|
855cf60ca0 | ||
|
|
1ab91fc2f4 | ||
|
|
7e746870a9 | ||
|
|
5aefbb3a4c | ||
|
|
2b37f27602 | ||
|
|
c78d5beef8 | ||
|
|
33d76346d1 | ||
|
|
08cbcd6ac2 | ||
|
|
b153053119 | ||
|
|
40925a49cc | ||
|
|
26efae1f4a | ||
|
|
ed72fba063 | ||
|
|
205ee000a2 | ||
|
|
4d3351e99c | ||
|
|
81371e54ee | ||
|
|
4a39602ca4 | ||
|
|
d233e40a95 | ||
|
|
e3a2ec6c8d | ||
|
|
f2a8cf0a62 | ||
|
|
2cbfe2d6b9 | ||
|
|
60b6c24e19 | ||
|
|
8ed4d41b2d | ||
|
|
be0d269af1 | ||
|
|
422911a95f | ||
|
|
21d788edf4 | ||
|
|
c4229b4906 | ||
|
|
e0d9c27d87 | ||
|
|
820bf85930 | ||
|
|
1455901474 | ||
|
|
bcc3394079 | ||
|
|
599847126c | ||
|
|
fdd3a43d18 | ||
|
|
4b630df3ca | ||
|
|
59c2216ec0 | ||
|
|
5ea1aa34b9 | ||
|
|
7f0d8e4920 | ||
|
|
3b427d8a85 | ||
|
|
64574369c3 | ||
|
|
10f098c0b5 | ||
|
|
22febe331b | ||
|
|
03383cf092 | ||
|
|
6eafbdca80 | ||
|
|
6746af4c3c | ||
|
|
88556af40c | ||
|
|
6eabd68a1d | ||
|
|
c8b9611fca | ||
|
|
25799f3680 | ||
|
|
69966eae60 | ||
|
|
028714b27d | ||
|
|
3f338ddb43 | ||
|
|
25acc67c43 | ||
|
|
d7c062c1c4 | ||
|
|
8123dff07a | ||
|
|
6b3f9b9478 | ||
|
|
d7aa612a23 | ||
|
|
fba5e75355 | ||
|
|
b83421a27a | ||
|
|
9049215811 | ||
|
|
63ef5ef380 | ||
|
|
52618bac35 | ||
|
|
1ed4d5bc8d | ||
|
|
fbf4370a78 | ||
|
|
1549ced52d | ||
|
|
f25681ce80 | ||
|
|
36cde4fe4f | ||
|
|
6561376b51 | ||
|
|
9b650041d1 | ||
|
|
fb717b5f31 | ||
|
|
9aa94c95b1 | ||
|
|
852a7295ef | ||
|
|
a8dc7ddd25 | ||
|
|
8aa823ba5d | ||
|
|
0d1073367c | ||
|
|
adfaf1b12a | ||
|
|
217d5fa79e | ||
|
|
05a64110fa | ||
|
|
d507a330bd | ||
|
|
9485c4c549 | ||
|
|
c421d3bf5d | ||
|
|
d3baf03c47 | ||
|
|
5b5f83c58e | ||
|
|
0e9a07c018 | ||
|
|
8a8ccebe3a | ||
|
|
48ada2d63c | ||
|
|
bae79ecb25 | ||
|
|
1aab6aaee5 | ||
|
|
7f817c96ad | ||
|
|
d188408b88 | ||
|
|
4955b45c1f | ||
|
|
46d4e1bc01 | ||
|
|
291fbb9ecb | ||
|
|
90f98a6fd6 | ||
|
|
8e14db7dba | ||
|
|
47e7132f26 | ||
|
|
59ffc57da3 | ||
|
|
3f8ec7b07e | ||
|
|
c32347c726 | ||
|
|
9d120d7037 | ||
|
|
6f9a3e6598 | ||
|
|
90957676b4 | ||
|
|
f60a415b59 | ||
|
|
b55e960e0d | ||
|
|
adc2360a11 | ||
|
|
77906e2d57 | ||
|
|
8e616dc4c8 | ||
|
|
5886b4b62a | ||
|
|
aa1a864ded | ||
|
|
f23eb17d78 | ||
|
|
4b2800ccef | ||
|
|
1af495b910 | ||
|
|
f3bd31b258 | ||
|
|
4dcd831b22 | ||
|
|
bbe2a8563c | ||
|
|
1eae7a6807 | ||
|
|
10a39bc10c | ||
|
|
58a1101448 | ||
|
|
828960c29f | ||
|
|
b91bc5fec4 | ||
|
|
e8b27c409d | ||
|
|
d22e9eeb93 | ||
|
|
38e336c2f0 | ||
|
|
b3fef245c8 | ||
|
|
62abd1e5c7 | ||
|
|
caa2b5dac5 | ||
|
|
98c8d1483a | ||
|
|
45b4faf885 | ||
|
|
daf0aac544 | ||
|
|
06a2f55294 | ||
|
|
631a416f89 | ||
|
|
e504f3dc2f | ||
|
|
827349d09d |
+1
-1
@@ -1,4 +1,4 @@
|
||||
[bumpversion]
|
||||
current_version = 0.1.9
|
||||
current_version = 0.1.10
|
||||
files = setup.py SimPEG/__init__.py docs/conf.py
|
||||
|
||||
|
||||
+8
-4
@@ -16,12 +16,14 @@ addons:
|
||||
|
||||
env:
|
||||
- TEST_DIR="tests/mesh tests/base tests/utils"
|
||||
- TEST_DIR=tests/examples
|
||||
- TEST_DIR=tests/em/fdem/forward
|
||||
- TEST_DIR=tests/em/fdem/inverse/derivs
|
||||
- TEST_DIR=tests/em/fdem/inverse/adjoint
|
||||
- TEST_DIR=tests/em/tdem
|
||||
- TEST_DIR=tests/dcip
|
||||
- TEST_DIR=tests/flow
|
||||
- TEST_DIR=tests/mt
|
||||
- TEST_DIR=tests/examples
|
||||
- TEST_DIR=tests/em/fdem/inverse/adjoint
|
||||
- TEST_DIR=tests/em/fdem/forward
|
||||
|
||||
# Setup anaconda
|
||||
before_install:
|
||||
@@ -33,7 +35,7 @@ before_install:
|
||||
|
||||
# Install packages
|
||||
install:
|
||||
- conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython ipython nose
|
||||
- conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython ipython nose vtk
|
||||
- pip install nose-cov python-coveralls
|
||||
|
||||
- git clone https://github.com/rowanc1/pymatsolver.git
|
||||
@@ -54,3 +56,5 @@ notifications:
|
||||
email:
|
||||
- rowanc1@gmail.com
|
||||
- lindseyheagy@gmail.com
|
||||
- gkrosen@gmail.com
|
||||
- sgkang09@gmail.com
|
||||
|
||||
+7
-3
@@ -1,9 +1,13 @@
|
||||
- Luz Angelica Caudillo-Mata, (`@lacmajedrez <https://github.com/lacmajedrez/>`_)
|
||||
- Rowan Cockett, (`@rowanc1 <https://github.com/rowanc1/>`_)
|
||||
- Eldad Haber, (`@ehaber99 <https://github.com/ehaber99/>`_)
|
||||
- Lindsey Heagy, (`@lheagy <https://github.com/lheagy/>`_)
|
||||
- Seogi Kang, (`@sgkang <https://github.com/sgkang/>`_)
|
||||
- Dave Marchant, (`@dwfmarchant <https://github.com/dwfmarchant/>`_)
|
||||
- Brendan Smithyman, (`@bsmithyman <https://github.com/bsmithyman/>`_)
|
||||
- Gudni Rosenkjaer, (`@grosenkj <https://github.com/grosenkj/>`_)
|
||||
- Dom Fournier, (`@fourndo <https://github.com/fourndo/>`_)
|
||||
- Dave Marchant, (`@dwfmarchant <https://github.com/dwfmarchant/>`_)
|
||||
- Lars Ruthotto, (`@lruthotto <https://github.com/lruthotto/>`_)
|
||||
- Mike Wathen, (`@mrwathen <https://github.com/mrwathen/>`_)
|
||||
- Luz Angelica Caudillo-Mata, (`@lacmajedrez <https://github.com/lacmajedrez/>`_)
|
||||
- Eldad Haber, (`@ehaber99 <https://github.com/ehaber99/>`_)
|
||||
- Doug Oldenburg, (`@dougoldenburg <https://github.com/dougoldenburg/>`_)
|
||||
- Adam Pidlisecky, (`@aPid1 <https://github.com/aPid1/>`_)
|
||||
|
||||
+2
-3
@@ -1,14 +1,13 @@
|
||||
Citing SimPEG
|
||||
=============
|
||||
-------------
|
||||
|
||||
There is a paper about SimPEG!
|
||||
There is a `paper about SimPEG <http://dx.doi.org/10.1016/j.cageo.2015.09.015>`_, if you use this code, please help our scientific visibility by citing our work!
|
||||
|
||||
|
||||
Cockett, R., Kang, S., Heagy, L. J., Pidlisecky, A., & Oldenburg, D. W. (2015). SimPEG: An open source framework for simulation and gradient based parameter estimation in geophysical applications. Computers & Geosciences.
|
||||
|
||||
|
||||
BibTex:
|
||||
-------
|
||||
|
||||
.. code::
|
||||
|
||||
|
||||
@@ -1,470 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
Input and output functions.
|
||||
"""
|
||||
|
||||
|
||||
import os as _os
|
||||
import errno as _errno
|
||||
import sys as _sys
|
||||
import numpy as _np
|
||||
|
||||
from petsc4py import PETSc as _PETSc
|
||||
import fileinput as _fl
|
||||
|
||||
def vecToArray(obj):
|
||||
""" Converts a PETSc vector to a numpy array, available on *all* MPI nodes.
|
||||
|
||||
Args:
|
||||
obj (petsc4py.PETSc.Vec): input vector.
|
||||
|
||||
Returns:
|
||||
numpy.array :
|
||||
"""
|
||||
# scatter vector 'obj' to all processes
|
||||
comm = obj.getComm()
|
||||
scatter, obj0 = _PETSc.Scatter.toAll(obj)
|
||||
scatter.scatter(obj, obj0, False, _PETSc.Scatter.Mode.FORWARD)
|
||||
|
||||
return _np.asarray(obj0)
|
||||
|
||||
# deallocate
|
||||
comm.barrier()
|
||||
scatter.destroy()
|
||||
obj0.destroy()
|
||||
|
||||
|
||||
def vecToArray0(obj):
|
||||
""" Converts a PETSc vector to a numpy array available on MPI node 0.
|
||||
|
||||
Args:
|
||||
obj (petsc4py.PETSc.Vec): input vector.
|
||||
|
||||
Returns:
|
||||
numpy.array :
|
||||
"""
|
||||
# scatter vector 'obj' to process 0
|
||||
comm = obj.getComm()
|
||||
rank = comm.getRank()
|
||||
scatter, obj0 = _PETSc.Scatter.toZero(obj)
|
||||
scatter.scatter(obj, obj0, False, _PETSc.Scatter.Mode.FORWARD)
|
||||
|
||||
if rank == 0: return _np.asarray(obj0)
|
||||
|
||||
# deallocate
|
||||
comm.barrier()
|
||||
scatter.destroy()
|
||||
obj0.destroy()
|
||||
|
||||
|
||||
def arrayToVec(vecArray):
|
||||
""" Converts a (global) array to a PETSc vector over :attr:`petsc4py.PETSc.COMM_WORLD`.
|
||||
|
||||
Args:
|
||||
vecArray (array or numpy.array): input vector.
|
||||
|
||||
Returns:
|
||||
petsc4py.PETSc.Vec() :
|
||||
"""
|
||||
vec = _PETSc.Vec().create(comm=_PETSc.COMM_WORLD)
|
||||
vec.setSizes(len(vecArray))
|
||||
vec.setUp()
|
||||
(Istart,Iend) = vec.getOwnershipRange()
|
||||
return vec.createWithArray(vecArray[Istart:Iend],
|
||||
comm=_PETSc.COMM_WORLD)
|
||||
vec.destroy()
|
||||
|
||||
|
||||
def arrayToMat(matArray):
|
||||
""" Converts a (global) 2D array to a PETSc matrix over :attr:`petsc4py.PETSc.COMM_WORLD`.
|
||||
|
||||
Args:
|
||||
matArray (array or numpy.array): input square array.
|
||||
|
||||
:rtype: petsc4py.PETSc.Mat()
|
||||
|
||||
.. important::
|
||||
Requires `SciPy <http://www.scipy.org>`_.
|
||||
|
||||
"""
|
||||
try:
|
||||
import scipy.sparse as sparse
|
||||
except:
|
||||
print '\nERROR: loading matrices from txt files requires Scipy!'
|
||||
return
|
||||
|
||||
matSparse =matArray
|
||||
|
||||
mat = _PETSc.Mat().createAIJ(size=matSparse.shape,comm=_PETSc.COMM_WORLD)
|
||||
(Istart,Iend) = mat.getOwnershipRange()
|
||||
|
||||
ai = matSparse.indptr[Istart:Iend+1] - matSparse.indptr[Istart]
|
||||
aj = matSparse.indices[matSparse.indptr[Istart]:matSparse.indptr[Iend]]
|
||||
av = matSparse.data[matSparse.indptr[Istart]:matSparse.indptr[Iend]]
|
||||
|
||||
mat.setValuesCSR(ai,aj,av)
|
||||
mat.assemble()
|
||||
|
||||
return mat
|
||||
mat.destroy()
|
||||
|
||||
def matToSparse(mat):
|
||||
""" Converts a PETSc matrix to a (global) sparse matrix.
|
||||
|
||||
Args:
|
||||
mat (petsc4py.PETSc.Mat): input PETSc matrix.
|
||||
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
|
||||
.. important::
|
||||
Requires `SciPy <http://www.scipy.org>`_.
|
||||
|
||||
"""
|
||||
import scipy.sparse as sparse
|
||||
|
||||
data = mat.getValuesCSR()
|
||||
|
||||
(Istart,Iend) = mat.getOwnershipRange()
|
||||
columns = mat.getSize()[0]
|
||||
sparseSubMat = sparse.csr_matrix(data[::-1],shape=(Iend-Istart,columns))
|
||||
|
||||
comm = _PETSc.COMM_WORLD
|
||||
|
||||
sparseSubMat = comm.tompi4py().allgather(sparseSubMat)
|
||||
|
||||
return sparse.vstack(sparseSubMat)
|
||||
|
||||
def adjToH(adj,d=[0],amp=[0.]):
|
||||
""" Creates a 1 particle PETSc-type Hamiltonian matrix from a PETSc adjacency matrix.
|
||||
|
||||
Args:
|
||||
adj (petsc4py.PETSc.Mat): input PETSc-type adjacency matrix.
|
||||
|
||||
d (array of ints): an array containing *integers* indicating the nodes
|
||||
where diagonal defects are to be placed (e.g. ``d=[0,1,4]``).
|
||||
|
||||
amp (array of floats): an array containing *floats* indicating the diagonal defect
|
||||
amplitudes corresponding to each element in ``d`` (e.g. ``amp=[0.5,-1,4.2]``).
|
||||
|
||||
Returns:
|
||||
: 1 particle Hamiltonian matrix
|
||||
:rtype: petsc4py.PETSc.Mat()
|
||||
|
||||
Warning:
|
||||
* The size of ``a`` and ``d`` must be identical
|
||||
|
||||
>>> amp = [0.5,-1.,4.2]
|
||||
>>> len(d) == len(amp)
|
||||
True
|
||||
|
||||
* Elements of ``d`` can range from :math:`[0,N-1]` where the adjacency matrix is :math:`N\\times N`.
|
||||
|
||||
"""
|
||||
(Istart,Iend) = adj.getOwnershipRange()
|
||||
diagSum = []
|
||||
for i in range(Istart,Iend):
|
||||
diagSum.append(_np.sum(adj.getRow(i)[-1]))
|
||||
for j,val in enumerate(d):
|
||||
if i==val: diagSum[i-Istart] += amp[j]
|
||||
|
||||
mat = _PETSc.Mat().create(comm=_PETSc.COMM_WORLD)
|
||||
mat.setSizes(adj.getSize())
|
||||
mat.setUp()
|
||||
|
||||
for i in range(Istart,Iend):
|
||||
mat.setValue(i,i,diagSum[i-Istart])
|
||||
|
||||
mat.assemble()
|
||||
mat.axpy(-1,adj)
|
||||
|
||||
return mat
|
||||
mat.destroy()
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#---------------------- Vec I/O functions ---------------------------
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def exportVec(vec,filename,filetype):
|
||||
""" Export a PETSc vector to a file.
|
||||
|
||||
Args:
|
||||
vec (petsc4py.PETSc.Vec): input vector.
|
||||
filename (str): path to desired output file.
|
||||
filetype (str): the filetype of the exported vector.
|
||||
|
||||
* ``'txt'`` - a column vector in text format.
|
||||
* ``'bin'`` - a PETSc binary vector.
|
||||
"""
|
||||
if _os.path.isabs(filename):
|
||||
outDir = _os.path.dirname(filename)
|
||||
else:
|
||||
outDir = './'+_os.path.dirname(filename)
|
||||
|
||||
# create output directory if it doesn't exist
|
||||
try:
|
||||
_os.mkdir(outDir)
|
||||
except OSError as exception:
|
||||
if exception.errno != _errno.EEXIST:
|
||||
raise
|
||||
|
||||
if filetype == 'txt':
|
||||
# scatter prob to process 0
|
||||
comm = vec.getComm()
|
||||
rank = comm.getRank()
|
||||
scatter, vec0 = _PETSc.Scatter.toZero(vec)
|
||||
scatter.scatter(vec, vec0, False, _PETSc.Scatter.Mode.FORWARD)
|
||||
|
||||
# use process 0 to write to text file
|
||||
if rank == 0:
|
||||
array0 = _np.asarray(vec0)
|
||||
with open(filename,'w') as f:
|
||||
for i in range(len(array0)):
|
||||
f.write('{0: .12e}\n'.format(array0[i]))
|
||||
|
||||
# deallocate
|
||||
comm.barrier()
|
||||
scatter.destroy()
|
||||
vec0.destroy()
|
||||
|
||||
elif filetype == 'bin':
|
||||
binSave = _PETSc.Viewer().createBinary(filename, 'w')
|
||||
binSave(vec)
|
||||
binSave.destroy()
|
||||
|
||||
vec.comm.barrier()
|
||||
|
||||
def loadVec(filename,filetype):
|
||||
""" Import a PETSc vector from a file.
|
||||
|
||||
Args:
|
||||
filename (str): path to input file.
|
||||
filetype (str): the filetype.
|
||||
|
||||
* ``'txt'`` - a column vector in text format.
|
||||
* ``'bin'`` - a PETSc binary vector.
|
||||
"""
|
||||
if filetype == 'txt':
|
||||
try:
|
||||
vecArray = _np.loadtxt(filename,dtype=_PETSc.ScalarType)
|
||||
return arrayToVec(vecArray)
|
||||
except:
|
||||
print "\nERROR: input state space file " + filename\
|
||||
+ " does not exist or is in an incorrect format"
|
||||
_sys.exit()
|
||||
|
||||
elif filetype == 'bin':
|
||||
binLoad = _PETSc.Viewer().createBinary(filename, 'r')
|
||||
try:
|
||||
return _PETSc.Vec().load(binLoad)
|
||||
except:
|
||||
print "\nERROR: input state space file " + filename\
|
||||
+ " does not exist or is in an incorrect format"
|
||||
_sys.exit()
|
||||
binLoad.destroy()
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#---------------------- Mat I/O functions ---------------------------
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def exportMat(mat,filename,filetype,mattype=None):
|
||||
""" Export a PETSc matrix to a file.
|
||||
|
||||
Args:
|
||||
mat (petsc4py.PETSc.Mat): input matrix.
|
||||
filename (str): path to desired output file.
|
||||
filetype (str): the filetype of the exported vector.
|
||||
|
||||
* ``'txt'`` - a 2D matrix array in text format.
|
||||
* ``'bin'`` - a PETSc binary matrix.
|
||||
mattype (str): (``None``,``'adj'``) - if set to ``adj``, only
|
||||
integers ``0`` and ``1`` are written. Note
|
||||
that this only applied in ``txt`` mode.
|
||||
"""
|
||||
rank = _PETSc.Comm.Get_rank(_PETSc.COMM_WORLD)
|
||||
|
||||
if _os.path.isabs(filename):
|
||||
outDir = _os.path.dirname(filename)
|
||||
else:
|
||||
outDir = './'+_os.path.dirname(filename)
|
||||
|
||||
# create output directory if it doesn't exist
|
||||
try:
|
||||
_os.mkdir(outDir)
|
||||
except OSError as exception:
|
||||
if exception.errno != _errno.EEXIST:
|
||||
raise
|
||||
|
||||
if filetype == 'txt':
|
||||
txtSave = _PETSc.Viewer().createASCII(filename, 'w',
|
||||
format=_PETSc.Viewer.Format.ASCII_DENSE, comm=_PETSc.COMM_WORLD)
|
||||
txtSave(mat)
|
||||
txtSave.destroy()
|
||||
|
||||
if rank == 0:
|
||||
for line in _fl.FileInput(filename,inplace=1):
|
||||
if line[2] != 't':
|
||||
if mattype == 'adj':
|
||||
line = line.replace(" i","j")
|
||||
line = line.replace(" -","-")
|
||||
line = line.replace("+-","-")
|
||||
line = line.replace("0000e+01+0.00000e+00j","")
|
||||
line = line.replace(".00000e+00+0.00000e+00j","")
|
||||
line = line.replace(".","")
|
||||
line = line.replace(" -","\t-")
|
||||
line = line.replace(" ","\t")
|
||||
line = line.replace(" ","")
|
||||
line = line.replace("\t"," ")
|
||||
print line,
|
||||
else:
|
||||
line = line.replace(" i","j")
|
||||
line = line.replace(" -","-")
|
||||
line = line.replace("+-","-")
|
||||
print line,
|
||||
|
||||
elif filetype == 'bin':
|
||||
binSave = _PETSc.Viewer().createBinary(filename, 'w', comm=_PETSc.COMM_WORLD)
|
||||
binSave(mat)
|
||||
binSave.destroy()
|
||||
|
||||
mat.comm.barrier()
|
||||
|
||||
def loadMat(filename,filetype,delimiter=None):
|
||||
""" Import a PETSc matrix from a file.
|
||||
|
||||
Args:
|
||||
filename (str): path to input file.
|
||||
filetype (str): the filetype.
|
||||
|
||||
* ``'txt'`` - a 2D matrix array in text format.
|
||||
* ``'bin'`` - a PETSc matrix vector.
|
||||
|
||||
delimiter (str): this is passed to `numpy.genfromtxt\
|
||||
<http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html>`_
|
||||
in the case of strange delimiters in an imported ``txt`` file.
|
||||
"""
|
||||
if filetype == 'txt':
|
||||
try:
|
||||
try:
|
||||
if delimiter is None:
|
||||
matArray = _np.genfromtxt(filename,dtype=_PETSc.ScalarType)
|
||||
else:
|
||||
matArray = _np.genfromtxt(filename,dtype=_PETSc.ScalarType,delimiter=delimiter)
|
||||
except:
|
||||
filefix = []
|
||||
for line in _fl.FileInput(filename,inplace=0):
|
||||
if line[2] != 't':
|
||||
line = line.replace(" i","j")
|
||||
line = line.replace(" -","-")
|
||||
line = line.replace("+-","-")
|
||||
filefix.append(line)
|
||||
|
||||
matArray = _np.genfromtxt(filefix,dtype=_PETSc.ScalarType)
|
||||
|
||||
return arrayToMat(matArray)
|
||||
except:
|
||||
print "\nERROR: input state space file " + filename\
|
||||
+ " does not exist or is in an incorrect format"
|
||||
_sys.exit()
|
||||
|
||||
elif filetype == 'bin':
|
||||
binLoad = _PETSc.Viewer().createBinary(filename, 'r')
|
||||
try:
|
||||
return _PETSc.Mat().load(binLoad)
|
||||
except:
|
||||
print "\nERROR: input state space file " + filename\
|
||||
+ " does not exist or is in an incorrect format"
|
||||
_sys.exit()
|
||||
binLoad.destroy()
|
||||
|
||||
def exportVecToMat(vec,filename,filetype):
|
||||
""" Export a :math:`N^2` element PETSc vector as a :math:`N\\times N` matrix.
|
||||
|
||||
This is useful when wanting to view the full statespace of a 2 particle
|
||||
quantum walk.
|
||||
|
||||
Args:
|
||||
vec (petsc4py.PETSc.Vec): input :math:`N^2` element vector.
|
||||
filename (str): path to desired output file.
|
||||
filetype (str): the filetype of the exported vector.
|
||||
|
||||
* ``'txt'`` - an :math:`N\\times N` 2D matrix array in text format.
|
||||
* ``'bin'`` - an :math:`N\\times N` PETSc binary matrix.
|
||||
"""
|
||||
rank = _PETSc.Comm.Get_rank(_PETSc.COMM_WORLD)
|
||||
|
||||
if _os.path.isabs(filename):
|
||||
outDir = _os.path.dirname(filename)
|
||||
else:
|
||||
outDir = './'+_os.path.dirname(filename)
|
||||
|
||||
# create output directory if it doesn't exist
|
||||
try:
|
||||
_os.mkdir(outDir)
|
||||
except OSError as exception:
|
||||
if exception.errno != _errno.EEXIST:
|
||||
raise
|
||||
|
||||
vecArray = vecToArray(vec)
|
||||
matArray = vecArray.reshape([_np.sqrt(vecArray.size),_np.sqrt(vecArray.size)])
|
||||
|
||||
if filetype == 'txt':
|
||||
#if rank == 0: _np.savetxt(filename,matArray)
|
||||
txtSave = _PETSc.Viewer().createASCII(filename, 'w',
|
||||
format=_PETSc.Viewer.Format.ASCII_DENSE, comm=_PETSc.COMM_WORLD)
|
||||
txtSave(arrayToMat(matArray))
|
||||
txtSave.destroy()
|
||||
|
||||
if rank == 0:
|
||||
for line in _fl.FileInput(filename,inplace=1):
|
||||
if line[2] != 't':
|
||||
line = line.replace(" i","j")
|
||||
line = line.replace(" -","-")
|
||||
line = line.replace("+-","-")
|
||||
print line,
|
||||
|
||||
elif filetype == 'bin':
|
||||
binSave = _PETSc.Viewer().createBinary(filename, 'w', comm=_PETSc.COMM_WORLD)
|
||||
binSave(arrayToMat(matArray))
|
||||
binSave.destroy()
|
||||
vec.comm.barrier()
|
||||
|
||||
|
||||
def loadMatToVec(filename,filetype):
|
||||
""" Load a :math:`N\\times N` matrix as a :math:`N^2` element PETSc vector.
|
||||
|
||||
This is useful when wanting to import the full statespace of a 2 particle
|
||||
quantum walk to use for propagation.
|
||||
|
||||
Args:
|
||||
filename (str): path to the input file.
|
||||
filetype (str): the filetype
|
||||
|
||||
* ``'txt'`` - an :math:`N\\times N` 2D matrix array in text format.
|
||||
* ``'bin'`` - **Not yet implemented! Please use a txt \
|
||||
format for this type of import**.
|
||||
"""
|
||||
if filetype == 'txt':
|
||||
try:
|
||||
try:
|
||||
matArray = _np.loadtxt(filename,dtype=_PETSc.ScalarType)
|
||||
except:
|
||||
filefix = []
|
||||
for line in _fl.FileInput(filename,inplace=0):
|
||||
if line[2] != 't':
|
||||
line = line.replace(" i","j")
|
||||
line = line.replace(" -","-")
|
||||
line = line.replace("+-","-")
|
||||
filefix.append(line)
|
||||
|
||||
matArray = _np.loadtxt(filefix,dtype=_PETSc.ScalarType)
|
||||
|
||||
vecArray = matArray.reshape(matArray.shape[0]**2)
|
||||
return arrayToVec(vecArray)
|
||||
except:
|
||||
print "\nERROR: input state space file " + filename\
|
||||
+ " does not exist or is in an incorrect format"
|
||||
_sys.exit()
|
||||
|
||||
elif filetype == 'bin':
|
||||
print '\nERROR: only works for txt storage!'
|
||||
_sys.exit()
|
||||
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
||||
# Check project status
|
||||
gcutil getproject --project=<ProjectName> --cache_flag_values
|
||||
|
||||
# Start an instance
|
||||
gcutil addinstance <instanceName>
|
||||
|
||||
# Log in
|
||||
gcutil ssh <instanceName>
|
||||
|
||||
# Shut down
|
||||
gcutil deleteinstance <instanceName>
|
||||
@@ -1,145 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
locale-gen en_US en_US.UTF-8 hu_HU hu_HU.UTF-8 > output.t
|
||||
dpkg-reconfigure locales >> output.t
|
||||
|
||||
sudo apt-get update >> output.t
|
||||
echo " "
|
||||
echo " "
|
||||
echo " ============================================"
|
||||
echo " | Installing packages form package manager |"
|
||||
echo " ============================================"
|
||||
echo " "
|
||||
echo " "
|
||||
|
||||
sudo apt-get -y install aptitude >> output.t
|
||||
|
||||
packages=(gcc gfortran git libopenmpi-dev python-pip python-dev git flex bison cmake vim cython ipython python-scipy python-numpy python-nose python-pip python-matplotlib python-vtk python-h5py libmumps-ptscotch-4.10.0 libmumps-ptscotch-dev libblas-dev liblapack-dev )
|
||||
|
||||
|
||||
for item in ${packages[*]}
|
||||
do
|
||||
printf " %-30s\n" $item
|
||||
|
||||
done
|
||||
|
||||
for item in ${packages[*]}
|
||||
do
|
||||
tput cuu1
|
||||
done
|
||||
|
||||
for item in ${packages[*]}
|
||||
do
|
||||
sudo aptitude -y install $item >> output.t
|
||||
printf " %-30s %-4s\n" $item done
|
||||
done
|
||||
|
||||
|
||||
echo " "
|
||||
echo " "
|
||||
echo " ====================================="
|
||||
echo " | Installing extra Python libraries |"
|
||||
echo " ====================================="
|
||||
echo " "
|
||||
echo " "
|
||||
|
||||
|
||||
pipPackages=(mpi4py pymumps)
|
||||
|
||||
for item in ${pipPackages[*]}
|
||||
do
|
||||
printf " %-30s\n" $item
|
||||
done
|
||||
|
||||
for item in ${pipPackages[*]}
|
||||
do
|
||||
tput cuu1
|
||||
done
|
||||
|
||||
for item in ${pipPackages[*]}
|
||||
do
|
||||
sudo pip install $item >> output.t
|
||||
printf " %-30s %-4s\n" $item done
|
||||
done
|
||||
|
||||
Upgrade=(scipy numpy ipython)
|
||||
|
||||
for item in ${Upgrade[*]}
|
||||
do
|
||||
printf " %-8s%-7s\n" $item upgrade
|
||||
|
||||
done
|
||||
|
||||
for item in ${Upgrade[*]}
|
||||
do
|
||||
tput cuu1
|
||||
done
|
||||
|
||||
for item in ${Upgrade[*]}
|
||||
do
|
||||
sudo pip install $item --upgrade >> output.t
|
||||
printf " %-8s%-7s %-4s\n" $item upgrade done
|
||||
done
|
||||
|
||||
|
||||
|
||||
echo " "
|
||||
echo " "
|
||||
echo " ====================="
|
||||
echo " | Installing SimPEG |"
|
||||
echo " ====================="
|
||||
echo " "
|
||||
echo " "
|
||||
cd ~
|
||||
|
||||
|
||||
git clone https://github.com/simpeg/simpeg.git >> output.t
|
||||
cd simpeg/SimPEG/
|
||||
python setup.py >> output.t
|
||||
cd ~
|
||||
|
||||
mkdir petsc
|
||||
cd petsc
|
||||
|
||||
echo " "
|
||||
echo " "
|
||||
echo " ===================="
|
||||
echo " | Installing PETSc |"
|
||||
echo " ===================="
|
||||
echo " "
|
||||
echo " "
|
||||
wget http://ftp.mcs.anl.gov/pub/petsc/release-snapshots/petsc-3.4.3.tar.gz
|
||||
|
||||
tar -zxf petsc-3.4.3.tar.gz
|
||||
|
||||
cd petsc-3.4.3
|
||||
|
||||
./configure --with-debugging=no --dowload-mpich=yes --download-blacs=yes --download-f-blas-lapack=yes --download-scalapack=yes --download-mumps=yes --download-ml=yes --download-spooles=yes --download-hypre=yes --dowload-trilinos=yes --download-metis=yes --download-parmetis=yes --download-umfpack=yes --download-ptscotch=yes --download-superlu=yes --download-superlu_dist=yes --download-essl=yes --download-eucild=yes --download-spai=yes --download-mpi4py=yes --download-petsc4py=yes --download-scientificpython=yes
|
||||
|
||||
|
||||
echo "export PETSC_DIR=/home/${USER}/petsc/petsc-3.4.3" >> ~/.bashrc
|
||||
echo "export PETSC_ARCH=arch-linux2-c-opt" >> ~/.bashrc
|
||||
export PETSC_DIR=/home/${USER}/petsc/petsc-3.4.3
|
||||
export PETSC_ARCH=arch-linux2-c-opt
|
||||
. ~/.bashrc
|
||||
|
||||
make PETSC_DIR=/home/${USER}/petsc/petsc-3.4.3 PETSC_ARCH=arch-linux2-c-opt all
|
||||
make PETSC_DIR=/home/${USER}/petsc/petsc-3.4.3 PETSC_ARCH=arch-linux2-c-opt test
|
||||
|
||||
cd ~/petsc
|
||||
echo " "
|
||||
echo " "
|
||||
echo " ======================="
|
||||
echo " | Installing PETSc4PY |"
|
||||
echo " ======================="
|
||||
echo " "
|
||||
echo " "
|
||||
git clone https://bitbucket.org/petsc/petsc4py.git
|
||||
cd petsc4py/
|
||||
python setup.py build >> output.t
|
||||
python setup.py install --prefix=~/petsc >> output.t
|
||||
|
||||
echo "export PYTHONPATH=~/petsc/lib/python2.7/site-packages:/home/$USER/simpeg:${PYTHONPATH}" >> ~/.bashrc
|
||||
|
||||
cd ~
|
||||
source ~/.bashrc
|
||||
@@ -1,22 +0,0 @@
|
||||
#! /bin/bash
|
||||
sudo aptitude -y update
|
||||
sudo aptitude -y upgrade
|
||||
sudo aptitude -y install gcc gfortran git libopenmpi-dev python-pip python-dev
|
||||
sudo aptitude -y install ipython python-scipy python-numpy python-nose python-pip python-matplotlib
|
||||
sudo aptitude -y install libmumps-ptscotch-4.10.0 libmumps-ptscotch-dev
|
||||
sudo aptitude -y install libblas-dev liblapack-dev
|
||||
|
||||
sudo pip install mpi4py
|
||||
sudo pip install pymumps
|
||||
|
||||
sudo pip install scipy --upgrade
|
||||
sudo pip install numpy --upgrade
|
||||
sudo pip install ipython --upgrade
|
||||
|
||||
git clone https://github.com/simpeg/simpeg.git
|
||||
cd simpeg/SimPEG/
|
||||
python setup.py
|
||||
cd ~
|
||||
|
||||
echo export PYTHONPATH=/home/$USER/simpeg/ >> .bashrc
|
||||
source .bashrc
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2015 SimPEG Developers
|
||||
Copyright (c) 2013-2016 SimPEG Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
- Electromagnetics (`simpegEM <http://simpegem.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegem.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegem
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegem/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegem?branch=master
|
||||
- Potential Fields (`simpegPF <http://simpegpf.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegpf.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegpf
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegpf/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegpf?branch=master
|
||||
- Ground Water Flow (`simpegFLOW <http://simpegflow.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegflow.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegflow
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegflow/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegflow?branch=master
|
||||
- Direct Current Resistivity (`simpegDC <http://simpeg-dc.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegdc.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegdc
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegdc/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegdc?branch=master
|
||||
- Electromagnetics 1D (`simpegEM1D <http://simpegem1d.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegEM1D.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegEM1D
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegEM1D/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegEM1D?branch=master
|
||||
- Magnetotellurics (`simpegMT <http://simpegmt.rtfd.org/>`_)
|
||||
.. image:: https://travis-ci.org/simpeg/simpegmt.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpegmt
|
||||
:alt: Master Branch
|
||||
.. image:: https://coveralls.io/repos/simpeg/simpegmt/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/simpeg/simpegmt?branch=master
|
||||
+1
-1
@@ -17,7 +17,7 @@ SimPEG
|
||||
:target: https://github.com/simpeg/simpeg/blob/master/LICENSE
|
||||
:alt: BSD 3 clause license.
|
||||
|
||||
.. image:: https://img.shields.io/travis/simpeg/simpeg.svg
|
||||
.. image:: https://api.travis-ci.org/simpeg/simpeg.svg?branch=master
|
||||
:target: https://travis-ci.org/simpeg/simpeg
|
||||
:alt: Travis CI build status
|
||||
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
from SimPEG import *
|
||||
|
||||
class FieldsDC_CC(Problem.Fields):
|
||||
knownFields = {'phi_sol':'CC'}
|
||||
aliasFields = {
|
||||
'phi' : ['phi_sol','CC','_phi'],
|
||||
'e' : ['phi_sol','F','_e'],
|
||||
'j' : ['phi_sol','F','_j']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
super(FieldsDC_CC, self).__init__(mesh, survey, **kwargs)
|
||||
|
||||
def startup(self):
|
||||
self._cellGrad = self.survey.prob.mesh.cellGrad
|
||||
self._Mfinv = self.survey.prob.mesh.getFaceInnerProduct(invMat=True)
|
||||
|
||||
def _phi(self, phi_sol, srcList):
|
||||
phi = phi_sol
|
||||
# for i, src in enumerate(srcList):
|
||||
# phi_p = src.phi_p(self.survey.prob)
|
||||
# if phi_p is not None:
|
||||
# phi[:,i] += phi_p
|
||||
return phi
|
||||
|
||||
def _e(self, phi_sol, srcList):
|
||||
e = -self._cellGrad*phi_sol
|
||||
# for i, src in enumerate(srcList):
|
||||
# e_p = src.e_p(self.survey.prob)
|
||||
# if e_p is not None:
|
||||
# e[:,i] += e_p
|
||||
return e
|
||||
|
||||
def _j(self, phi_sol, srcList):
|
||||
|
||||
j = -self._Mfinv*self.survey.prob.Msig*self._cellGrad*phi_sol
|
||||
# for i, src in enumerate(srcList):
|
||||
# j_p = src.j_p(self.survey.prob)
|
||||
# if j_p is not None:
|
||||
# j[:,i] += j_p
|
||||
return j
|
||||
|
||||
|
||||
|
||||
class SrcDipole(Survey.BaseSrc):
|
||||
"""A dipole source, locA and locB are moved to the closest cell-centers"""
|
||||
|
||||
current = 1
|
||||
loc = None
|
||||
# _rhsDict = None
|
||||
|
||||
def __init__(self, rxList, locA, locB, **kwargs):
|
||||
self.loc = (locA, locB)
|
||||
super(SrcDipole, self).__init__(rxList, **kwargs)
|
||||
|
||||
def eval(self, prob):
|
||||
# Recompute rhs
|
||||
# if getattr(self, '_rhsDict', None) is None:
|
||||
# self._rhsDict = {}
|
||||
# if mesh not in self._rhsDict:
|
||||
pts = [self.loc[0], self.loc[1]]
|
||||
inds = Utils.closestPoints(prob.mesh, pts)
|
||||
q = np.zeros(prob.mesh.nC)
|
||||
q[inds] = - self.current * ( np.r_[1., -1.] / prob.mesh.vol[inds] )
|
||||
# self._rhsDict[mesh] = q
|
||||
# return self._rhsDict[mesh]
|
||||
return q
|
||||
|
||||
|
||||
class RxDipole(Survey.BaseRx):
|
||||
"""A dipole source, locA and locB are moved to the closest cell-centers"""
|
||||
def __init__(self, locsM, locsN, **kwargs):
|
||||
locs = (locsM, locsN)
|
||||
assert locsM.shape == locsN.shape, 'locs must be the same shape.'
|
||||
super(RxDipole, self).__init__(locs, 'dipole', storeProjections=False, **kwargs)
|
||||
|
||||
@property
|
||||
def nD(self):
|
||||
"""Number of data in the receiver."""
|
||||
return self.locs[0].shape[0]
|
||||
|
||||
def getP(self, mesh):
|
||||
P0 = mesh.getInterpolationMat(self.locs[0], self.projGLoc)
|
||||
P1 = mesh.getInterpolationMat(self.locs[1], self.projGLoc)
|
||||
return P0 - P1
|
||||
|
||||
|
||||
class SurveyDC(Survey.BaseSurvey):
|
||||
"""
|
||||
**SurveyDC**
|
||||
|
||||
Geophysical DC resistivity data.
|
||||
|
||||
"""
|
||||
uncert = None
|
||||
def __init__(self, srcList, **kwargs):
|
||||
self.srcList = srcList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
# self._rhsDict = {}
|
||||
self._Ps = {}
|
||||
|
||||
def eval(self, u):
|
||||
"""
|
||||
Predicted data.
|
||||
|
||||
.. math::
|
||||
d_\\text{pred} = Pu(m)
|
||||
"""
|
||||
P = self.getP(self.prob.mesh)
|
||||
return P*mkvc(u[self.srcList, 'phi_sol'])
|
||||
|
||||
def getP(self, mesh):
|
||||
if mesh in self._Ps:
|
||||
return self._Ps[mesh]
|
||||
|
||||
P_src = [sp.vstack([rx.getP(mesh) for rx in src.rxList]) for src in self.srcList]
|
||||
|
||||
self._Ps[mesh] = sp.block_diag(P_src)
|
||||
return self._Ps[mesh]
|
||||
|
||||
|
||||
class ProblemDC_CC(Problem.BaseProblem):
|
||||
"""
|
||||
**ProblemDC**
|
||||
|
||||
Geophysical DC resistivity problem.
|
||||
|
||||
"""
|
||||
|
||||
surveyPair = SurveyDC
|
||||
Solver = Solver
|
||||
fieldsPair = FieldsDC_CC
|
||||
Ainv = None
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
Problem.BaseProblem.__init__(self, mesh)
|
||||
self.mesh.setCellGradBC('neumann')
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
|
||||
deleteTheseOnModelUpdate = ['_A', '_Msig', '_dMdsig']
|
||||
|
||||
@property
|
||||
def Msig(self):
|
||||
if getattr(self, '_Msig', None) is None:
|
||||
sigma = self.curModel.transform
|
||||
Av = self.mesh.aveF2CC
|
||||
self._Msig = Utils.sdiag(1/(self.mesh.dim * Av.T * (1/sigma)))
|
||||
return self._Msig
|
||||
|
||||
@property
|
||||
def dMdsig(self):
|
||||
if getattr(self, '_dMdsig', None) is None:
|
||||
sigma = self.curModel.transform
|
||||
Av = self.mesh.aveF2CC
|
||||
dMdprop = self.mesh.dim * Utils.sdiag(self.Msig.diagonal()**2) * Av.T * Utils.sdiag(1./sigma**2)
|
||||
self._dMdsig = lambda Gu: Utils.sdiag(Gu) * dMdprop
|
||||
return self._dMdsig
|
||||
|
||||
@property
|
||||
def A(self):
|
||||
"""
|
||||
Makes the matrix A(m) for the DC resistivity problem.
|
||||
|
||||
:param numpy.array m: model
|
||||
:rtype: scipy.csc_matrix
|
||||
:return: A(m)
|
||||
|
||||
.. math::
|
||||
c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0
|
||||
|
||||
Where M() is the mass matrix and mT is the model transform.
|
||||
"""
|
||||
if getattr(self, '_A', None) is None:
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
self._A = D*self.Msig*G
|
||||
# Remove the null space from the matrix.
|
||||
self._A[0,0] /= self.mesh.vol[0]
|
||||
self._A = self._A.tocsc()
|
||||
return self._A
|
||||
|
||||
def getRHS(self):
|
||||
# if self.mesh not in self._rhsDict:
|
||||
RHS = np.array([src.eval(self) for src in self.survey.srcList]).T
|
||||
# self._rhsDict[mesh] = RHS
|
||||
# return self._rhsDict[mesh]
|
||||
return RHS
|
||||
|
||||
def fields(self, m):
|
||||
|
||||
F = self.fieldsPair(self.mesh, self.survey)
|
||||
self.curModel = m
|
||||
A = self.A
|
||||
self.Ainv = self.Solver(A, **self.solverOpts)
|
||||
RHS = self.getRHS()
|
||||
Phi = self.Ainv * RHS
|
||||
Srcs = self.survey.srcList
|
||||
F[Srcs, 'phi_sol'] = Phi
|
||||
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
:param numpy.array m: model
|
||||
:param numpy.array v: vector to multiply
|
||||
:param numpy.array u: fields
|
||||
:rtype: numpy.array
|
||||
:return: Jv
|
||||
|
||||
.. math::
|
||||
c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0
|
||||
|
||||
\\nabla_u (A(m)u - q) = A(m)
|
||||
|
||||
\\nabla_m (A(m)u - q) = G\\text{sdiag}(Du)\\nabla_m(M(mT(m)))
|
||||
|
||||
Where M() is the mass matrix and mT is the model transform.
|
||||
|
||||
.. math::
|
||||
J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u)
|
||||
|
||||
J(v) = - P ( A(m)^{-1} ( G\\text{sdiag}(Du)\\nabla_m(M(mT(m))) v ) )
|
||||
"""
|
||||
# Set current model; clear dependent property $\mathbf{A(m)}$
|
||||
self.curModel = m
|
||||
sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$
|
||||
if u is None:
|
||||
# Run forward simulation if $u$ not provided
|
||||
u = self.fields(self.curModel)[self.survey.srcList, 'phi_sol']
|
||||
else:
|
||||
u = u[self.survey.srcList, 'phi_sol']
|
||||
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
# Derivative of model transform, $\deriv{\sigma}{\m}$
|
||||
dsigdm_x_v = self.curModel.transformDeriv * v
|
||||
|
||||
# Take derivative of $C(m,u)$ w.r.t. $m$
|
||||
dCdm_x_v = np.empty_like(u)
|
||||
# loop over fields for each source
|
||||
for i in range(self.survey.nSrc):
|
||||
# Derivative of inner product, $\left(\mathbf{M}_{1/\sigma}^f\right)^{-1}$
|
||||
dAdsig = D * self.dMdsig( G * u[:,i] )
|
||||
dCdm_x_v[:, i] = dAdsig * dsigdm_x_v
|
||||
|
||||
# Take derivative of $C(m,u)$ w.r.t. $u$
|
||||
dA_du = self.A
|
||||
# Solve for $\deriv{u}{m}$
|
||||
# dCdu_inv = self.Solver(dCdu, **self.solverOpts)
|
||||
if self.Ainv is None:
|
||||
self.Ainv = self.Solver(dA_du, **self.solverOpts)
|
||||
|
||||
P = self.survey.getP(self.mesh)
|
||||
Jv = - P * mkvc( self.Ainv * dCdm_x_v )
|
||||
return Jv
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
|
||||
self.curModel = m
|
||||
sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$
|
||||
if u is None:
|
||||
# Run forward simulation if $u$ not provided
|
||||
u = self.fields(self.curModel)[self.survey.srcList, 'phi_sol']
|
||||
else:
|
||||
u = u[self.survey.srcList, 'phi_sol']
|
||||
|
||||
shp = u.shape
|
||||
P = self.survey.getP(self.mesh)
|
||||
PT_x_v = (P.T*v).reshape(shp, order='F')
|
||||
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
dA_du = self.A
|
||||
mT_dm = self.mapping.deriv(m)
|
||||
|
||||
# We probably always need this due to the linesearch .. (?)
|
||||
self.Ainv = self.Solver(dA_du.T, **self.solverOpts)
|
||||
# if self.Ainv is None:
|
||||
# self.Ainv = self.Solver(dCdu, **self.solverOpts)
|
||||
|
||||
w = self.Ainv * PT_x_v
|
||||
|
||||
Jtv = 0
|
||||
for i, ui in enumerate(u.T): # loop over each column
|
||||
Jtv += self.dMdsig( G * ui ).T * ( D.T * w[:,i] )
|
||||
|
||||
Jtv = - mT_dm.T * ( Jtv )
|
||||
return Jtv
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
from SimPEG import *
|
||||
from BaseDC import SurveyDC, FieldsDC_CC
|
||||
|
||||
class SurveyIP(SurveyDC):
|
||||
"""
|
||||
**SurveyDC**
|
||||
|
||||
Geophysical DC resistivity data.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
self.srcList = srcList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
self._Ps = {}
|
||||
|
||||
def dpred(self, m, u=None):
|
||||
"""
|
||||
Predicted data.
|
||||
|
||||
.. math::
|
||||
d_\\text{pred} = Pu(m)
|
||||
"""
|
||||
|
||||
return self.prob.forward(m)
|
||||
|
||||
|
||||
class ProblemIP(Problem.BaseProblem):
|
||||
"""
|
||||
**ProblemIP**
|
||||
|
||||
Geophysical IP resistivity problem.
|
||||
|
||||
"""
|
||||
|
||||
surveyPair = SurveyDC
|
||||
Solver = Solver
|
||||
sigma = None
|
||||
Ainv = None
|
||||
u = None
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
Problem.BaseProblem.__init__(self, mesh)
|
||||
self.mesh.setCellGradBC('neumann')
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
# deleteTheseOnModelUpdate = ['_A', '_Msig', '_dMdsig']
|
||||
|
||||
@property
|
||||
def Msig(self):
|
||||
if getattr(self, '_Msig', None) is None:
|
||||
# sigma = self.curModel.transform
|
||||
sigma = self.sigma
|
||||
Av = self.mesh.aveF2CC
|
||||
self._Msig = Utils.sdiag(1/(self.mesh.dim * Av.T * (1/sigma)))
|
||||
return self._Msig
|
||||
|
||||
@property
|
||||
def dMdsig(self):
|
||||
if getattr(self, '_dMdsig', None) is None:
|
||||
# sigma = self.curModel.transform
|
||||
sigma = self.sigma
|
||||
Av = self.mesh.aveF2CC
|
||||
dMdprop = self.mesh.dim * Utils.sdiag(self.Msig.diagonal()**2) * Av.T * Utils.sdiag(1./sigma**2)
|
||||
self._dMdsig = lambda Gu: Utils.sdiag(Gu) * dMdprop
|
||||
return self._dMdsig
|
||||
|
||||
@property
|
||||
def A(self):
|
||||
"""
|
||||
Makes the matrix A(m) for the DC resistivity problem.
|
||||
|
||||
:param numpy.array m: model
|
||||
:rtype: scipy.csc_matrix
|
||||
:return: A(m)
|
||||
|
||||
.. math::
|
||||
c(m,u) = A(m)u - q = G\\text{sdiag}(M(mT(m)))Du - q = 0
|
||||
|
||||
Where M() is the mass matrix and mT is the model transform.
|
||||
"""
|
||||
if getattr(self, '_A', None) is None:
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
self._A = D*self.Msig*G
|
||||
# Remove the null space from the matrix.
|
||||
self._A[-1,-1] /= self.mesh.vol[-1]
|
||||
self._A = self._A.tocsc()
|
||||
return self._A
|
||||
|
||||
def getRHS(self):
|
||||
# if self.mesh not in self._rhsDict:
|
||||
RHS = np.array([src.eval(self) for src in self.survey.srcList]).T
|
||||
# self._rhsDict[mesh] = RHS
|
||||
# return self._rhsDict[mesh]
|
||||
return RHS
|
||||
|
||||
def fields(self, m):
|
||||
if self.u is None:
|
||||
A = self.A
|
||||
if self.Ainv == None:
|
||||
self.Ainv = self.Solver(A, **self.solverOpts)
|
||||
Q = self.getRHS()
|
||||
self.u = self.Ainv * Q
|
||||
return self.u
|
||||
|
||||
def forward(self, m, u=None):
|
||||
# Set current model; clear dependent property $\mathbf{A(m)}$
|
||||
self.curModel = m
|
||||
# sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$
|
||||
sigma = self.sigma
|
||||
if self.u is None:
|
||||
# Run forward simulation if $u$ not provided
|
||||
u = self.fields(sigma)
|
||||
|
||||
shp = (self.mesh.nC, self.survey.nSrc)
|
||||
u = self.u.reshape(shp, order='F')
|
||||
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
# Derivative of model transform, $\deriv{\sigma}{\m}$
|
||||
# dsigdm_x_v = self.curModel.transformDeriv * v
|
||||
|
||||
dsigdm_x_v = Utils.sdiag(sigma) * self.curModel.transformDeriv * m
|
||||
|
||||
# Take derivative of $C(m,u)$ w.r.t. $m$
|
||||
dCdm_x_v = np.empty_like(u)
|
||||
# loop over fields for each source
|
||||
for i in range(self.survey.nSrc):
|
||||
# Derivative of inner product, $\left(\mathbf{M}_{1/\sigma}^f\right)^{-1}$
|
||||
dAdsig = D * self.dMdsig( G * u[:,i] )
|
||||
dCdm_x_v[:, i] = dAdsig * dsigdm_x_v
|
||||
|
||||
# Take derivative of $C(m,u)$ w.r.t. $u$
|
||||
|
||||
if self.Ainv == None:
|
||||
self.Ainv = self.Solver(A, **self.solverOpts)
|
||||
|
||||
# dCdu = self.A
|
||||
# Solve for $\deriv{u}{m}$
|
||||
# dCdu_inv = self.Solver(dCdu, **self.solverOpts)
|
||||
P = self.survey.getP(self.mesh)
|
||||
J_x_v = - P * mkvc( self.Ainv * dCdm_x_v )
|
||||
return -J_x_v
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
return self.forward(v)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
|
||||
self.curModel = m
|
||||
# sigma = self.curModel.transform # $\sigma = \mathcal{M}(\m)$
|
||||
sigma = self.sigma
|
||||
if self.u is None:
|
||||
u = self.fields(sigma)
|
||||
else:
|
||||
u = self.u
|
||||
shp = (self.mesh.nC, self.survey.nSrc)
|
||||
u = u.reshape(shp, order='F')
|
||||
P = self.survey.getP(self.mesh)
|
||||
PT_x_v = (P.T*v).reshape(shp, order='F')
|
||||
|
||||
D = self.mesh.faceDiv
|
||||
G = self.mesh.cellGrad
|
||||
A = self.A
|
||||
mT_dm = Utils.sdiag(sigma)*self.mapping.deriv(m)
|
||||
# mT_dm = self.mapping.deriv(m)
|
||||
|
||||
# dCdu = A.T
|
||||
# Ainv = self.Solver(dCdu, **self.solverOpts)
|
||||
# if self.Ainv == None:
|
||||
self.Ainv = self.Solver(A.T, **self.solverOpts)
|
||||
|
||||
w = self.Ainv * PT_x_v
|
||||
|
||||
Jtv = 0
|
||||
for i, ui in enumerate(u.T): # loop over each column
|
||||
Jtv += self.dMdsig( G * ui ).T * ( D.T * w[:,i] )
|
||||
|
||||
Jtv = - mT_dm.T * ( Jtv )
|
||||
return -Jtv
|
||||
|
||||
@@ -0,0 +1,934 @@
|
||||
from SimPEG import np
|
||||
import BaseDC as DC
|
||||
import BaseDC as IP
|
||||
|
||||
def getActiveindfromTopo(mesh, topo):
|
||||
# def genActiveindfromTopo(mesh, topo):
|
||||
"""
|
||||
Get active indices from topography
|
||||
"""
|
||||
from scipy.interpolate import NearestNDInterpolator
|
||||
if mesh.dim==3:
|
||||
nCxy = mesh.nCx*mesh.nCy
|
||||
Zcc = mesh.gridCC[:,2].reshape((nCxy, mesh.nCz), order='F')
|
||||
Ftopo = NearestNDInterpolator(topo[:,:2], topo[:,2])
|
||||
XY = Utils.ndgrid(mesh.vectorCCx, mesh.vectorCCy)
|
||||
XY.shape
|
||||
topo = Ftopo(XY)
|
||||
actind = []
|
||||
for ixy in range(nCxy):
|
||||
actind.append(topo[ixy] <= Zcc[ixy,:])
|
||||
else:
|
||||
raise NotImplementedError("Only 3D is working")
|
||||
|
||||
return Utils.mkvc(np.vstack(actind))
|
||||
|
||||
def gettopoCC(mesh, airind):
|
||||
# def gettopoCC(mesh, airind):
|
||||
"""
|
||||
Get topography from active indices of mesh.
|
||||
"""
|
||||
mesh2D = Mesh.TensorMesh([mesh.hx, mesh.hy], mesh.x0[:2])
|
||||
zc = mesh.gridCC[:,2]
|
||||
AIRIND = airind.reshape((mesh.vnC[0]*mesh.vnC[1],mesh.vnC[2]), order='F')
|
||||
ZC = zc.reshape((mesh.vnC[0]*mesh.vnC[1], mesh.vnC[2]), order='F')
|
||||
topo = np.zeros(ZC.shape[0])
|
||||
topoCC = np.zeros(ZC.shape[0])
|
||||
for i in range(ZC.shape[0]):
|
||||
ind = np.argmax(ZC[i,:][~AIRIND[i,:]])
|
||||
topo[i] = ZC[i,:][~AIRIND[i,:]].max() + mesh.hz[~AIRIND[i,:]][ind]*0.5
|
||||
topoCC[i] = ZC[i,:][~AIRIND[i,:]].max()
|
||||
XY = Utils.ndgrid(mesh.vectorCCx, mesh.vectorCCy)
|
||||
return mesh2D, topoCC
|
||||
|
||||
def readUBC_DC3Dobstopo(filename,mesh,topo,probType="CC"):
|
||||
"""
|
||||
Seogi's personal readObs function.
|
||||
|
||||
"""
|
||||
text_file = open(filename, "r")
|
||||
lines = text_file.readlines()
|
||||
text_file.close()
|
||||
SRC = []
|
||||
DATA = []
|
||||
srcLists = []
|
||||
isrc = 0
|
||||
# airind = getActiveindfromTopo(mesh, topo)
|
||||
# mesh2D, topoCC = gettopoCC(mesh, airind)
|
||||
|
||||
for line in lines:
|
||||
if "!" in line.split(): continue
|
||||
elif line == '\n': continue
|
||||
elif line == ' \n': continue
|
||||
temp = map(float, line.split())
|
||||
# Read a line for the current electrode
|
||||
if len(temp) == 5: # SRC: Only X and Y are provided (assume no topography)
|
||||
#TODO consider topography and assign the closest cell center in the earth
|
||||
if isrc == 0:
|
||||
DATA_temp = []
|
||||
else:
|
||||
DATA.append(np.asarray(DATA_temp))
|
||||
DATA_temp = []
|
||||
indM = Utils.closestPoints(mesh2D, DATA[isrc-1][:,1:3])
|
||||
indN = Utils.closestPoints(mesh2D, DATA[isrc-1][:,3:5])
|
||||
rx = DCIP.RxDipole(np.c_[DATA[isrc-1][:,1:3], topoCC[indM]], np.c_[DATA[isrc-1][:,3:5], topoCC[indN]])
|
||||
temp = np.asarray(temp)
|
||||
if [SRC[isrc-1][0], SRC[isrc-1][1]] == [SRC[isrc-1][2], SRC[isrc-1][3]]:
|
||||
indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]])
|
||||
tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[mesh.vectorCCx.max(), mesh.vectorCCy.max(), topoCC[-1]])
|
||||
else:
|
||||
indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]])
|
||||
indB = Utils.closestPoints(mesh2D, [SRC[isrc-1][2], SRC[isrc-1][3]])
|
||||
tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[SRC[isrc-1][2], SRC[isrc-1][3], topoCC[indB]])
|
||||
srcLists.append(tx)
|
||||
SRC.append(temp)
|
||||
isrc += 1
|
||||
elif len(temp) == 7: # SRC: X, Y and Z are provided
|
||||
SRC.append(temp)
|
||||
isrc += 1
|
||||
elif len(temp) == 6: #
|
||||
DATA_temp.append(np.r_[isrc, np.asarray(temp)])
|
||||
elif len(temp) > 7:
|
||||
DATA_temp.append(np.r_[isrc, np.asarray(temp)])
|
||||
|
||||
DATA.append(np.asarray(DATA_temp))
|
||||
DATA_temp = []
|
||||
indM = Utils.closestPoints(mesh2D, DATA[isrc-1][:,1:3])
|
||||
indN = Utils.closestPoints(mesh2D, DATA[isrc-1][:,3:5])
|
||||
rx = DCIP.RxDipole(np.c_[DATA[isrc-1][:,1:3], topoCC[indM]], np.c_[DATA[isrc-1][:,3:5], topoCC[indN]])
|
||||
temp = np.asarray(temp)
|
||||
if [SRC[isrc-1][0], SRC[isrc-1][1]] == [SRC[isrc-1][2], SRC[isrc-1][3]]:
|
||||
indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]])
|
||||
tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[mesh.vectorCCx.max(), mesh.vectorCCy.max(), topoCC[-1]])
|
||||
else:
|
||||
indA = Utils.closestPoints(mesh2D, [SRC[isrc-1][0], SRC[isrc-1][1]])
|
||||
indB = Utils.closestPoints(mesh2D, [SRC[isrc-1][2], SRC[isrc-1][3]])
|
||||
tx = DCIP.SrcDipole([rx], [SRC[isrc-1][0], SRC[isrc-1][1], topoCC[indA]],[SRC[isrc-1][2], SRC[isrc-1][3], topoCC[indB]])
|
||||
srcLists.append(tx)
|
||||
text_file.close()
|
||||
survey = DCIP.SurveyDC(srcLists)
|
||||
|
||||
# Do we need this?
|
||||
SRC = np.asarray(SRC)
|
||||
DATA = np.vstack(DATA)
|
||||
survey.dobs = np.vstack(DATA)[:,-2]
|
||||
|
||||
|
||||
return {'DCsurvey':survey, 'airind':airind, 'topoCC':topoCC, 'SRC':SRC}
|
||||
|
||||
def readUBC_DC2DModel(fileName):
|
||||
"""
|
||||
Read UBC GIF 2DTensor model and generate 2D Tensor model in simpeg
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF 2D model file
|
||||
|
||||
Output:
|
||||
:param SimPEG TensorMesh 2D object
|
||||
:return
|
||||
|
||||
Created on Thu Nov 12 13:14:10 2015
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
from SimPEG import np, mkvc
|
||||
|
||||
# Open fileand skip header... assume that we know the mesh already
|
||||
obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!')
|
||||
|
||||
dim = np.array(obsfile[0].split(),dtype=float)
|
||||
|
||||
temp = np.array(obsfile[1].split(),dtype=float)
|
||||
|
||||
if len(temp) > 1:
|
||||
model = np.zeros(dim)
|
||||
|
||||
for ii in range(len(obsfile)-1):
|
||||
mm = np.array(obsfile[ii+1].split(),dtype=float)
|
||||
model[:,ii] = mm
|
||||
|
||||
model = model[:,::-1]
|
||||
|
||||
else:
|
||||
|
||||
if len(obsfile[1:])==1:
|
||||
mm = np.array(obsfile[1:].split(),dtype=float)
|
||||
|
||||
else:
|
||||
mm = np.array(obsfile[1:],dtype=float)
|
||||
|
||||
# Permute the second dimension to flip the order
|
||||
model = mm.reshape(dim[1],dim[0])
|
||||
|
||||
model = model[::-1,:]
|
||||
model = np.transpose(model, (1, 0))
|
||||
|
||||
model = mkvc(model)
|
||||
|
||||
|
||||
return model
|
||||
|
||||
def plot_pseudoSection(DCsurvey, axs, stype):
|
||||
"""
|
||||
Read list of 2D tx-rx location and plot a speudo-section of apparent
|
||||
resistivity.
|
||||
|
||||
Assumes flat topo for now...
|
||||
|
||||
Input:
|
||||
:param d2D, z0
|
||||
:switch stype -> Either 'pdp' (pole-dipole) | 'dpdp' (dipole-dipole)
|
||||
|
||||
Output:
|
||||
:figure scatter plot overlayed on image
|
||||
|
||||
Edited Feb 17th, 2016
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
from SimPEG import np
|
||||
from scipy.interpolate import griddata
|
||||
import pylab as plt
|
||||
|
||||
# Set depth to 0 for now
|
||||
z0 = 0.
|
||||
|
||||
# Pre-allocate
|
||||
midx = []
|
||||
midz = []
|
||||
rho = []
|
||||
count = 0 # Counter for data
|
||||
for ii in range(DCsurvey.nSrc):
|
||||
|
||||
Tx = DCsurvey.srcList[ii].loc
|
||||
Rx = DCsurvey.srcList[ii].rxList[0].locs
|
||||
|
||||
nD = DCsurvey.srcList[ii].rxList[0].nD
|
||||
|
||||
data = DCsurvey.dobs[count:count+nD]
|
||||
count += nD
|
||||
|
||||
# Get distances between each poles A-B-M-N
|
||||
MA = np.abs(Tx[0][0] - Rx[0][:,0])
|
||||
MB = np.abs(Tx[1][0] - Rx[0][:,0])
|
||||
NB = np.abs(Tx[1][0] - Rx[1][:,0])
|
||||
NA = np.abs(Tx[0][0] - Rx[1][:,0])
|
||||
MN = np.abs(Rx[1][:,0] - Rx[0][:,0])
|
||||
|
||||
# Create mid-point location
|
||||
Cmid = (Tx[0][0] + Tx[1][0])/2
|
||||
Pmid = (Rx[0][:,0] + Rx[1][:,0])/2
|
||||
|
||||
# Compute pant leg of apparent rho
|
||||
if stype == 'pdp':
|
||||
leg = data * 2*np.pi * MA * ( MA + MN ) / MN
|
||||
|
||||
leg = np.log10(abs(1/leg))
|
||||
|
||||
elif stype == 'dpdp':
|
||||
leg = data * 2*np.pi / ( 1/MA - 1/MB - 1/NB + 1/NA )
|
||||
|
||||
|
||||
midx = np.hstack([midx, ( Cmid + Pmid )/2 ])
|
||||
midz = np.hstack([midz, -np.abs(Cmid-Pmid)/2 + z0 ])
|
||||
rho = np.hstack([rho,leg])
|
||||
|
||||
|
||||
ax = axs
|
||||
|
||||
# Grid points
|
||||
grid_x, grid_z = np.mgrid[np.min(midx):np.max(midx), np.min(midz):np.max(midz)]
|
||||
grid_rho = griddata(np.c_[midx,midz], rho.T, (grid_x, grid_z), method='linear')
|
||||
|
||||
|
||||
plt.imshow(grid_rho.T, extent = (np.min(midx),np.max(midx),np.min(midz),np.max(midz)), origin='lower', alpha=0.8, vmin = np.min(rho), vmax = np.max(rho))
|
||||
cbar = plt.colorbar(format = '%.2f',fraction=0.04,orientation="horizontal")
|
||||
|
||||
cmin,cmax = cbar.get_clim()
|
||||
ticks = np.linspace(cmin,cmax,3)
|
||||
cbar.set_ticks(ticks)
|
||||
|
||||
# Plot apparent resistivity
|
||||
plt.scatter(midx,midz,s=50,c=rho.T)
|
||||
|
||||
ax.set_xticklabels([])
|
||||
|
||||
ax.set_ylabel('Z')
|
||||
ax.yaxis.tick_right()
|
||||
ax.yaxis.set_label_position('right')
|
||||
plt.gca().set_aspect('equal', adjustable='box')
|
||||
|
||||
|
||||
return ax
|
||||
|
||||
def gen_DCIPsurvey(endl, mesh, stype, a, b, n):
|
||||
"""
|
||||
Load in endpoints and survey specifications to generate Tx, Rx location
|
||||
stations.
|
||||
|
||||
Assumes flat topo for now...
|
||||
|
||||
Input:
|
||||
:param endl -> input endpoints [x1, y1, z1, x2, y2, z2]
|
||||
:object mesh -> SimPEG mesh object
|
||||
:switch stype -> "dpdp" (dipole-dipole) | "pdp" (pole-dipole) | 'gradient'
|
||||
: param a, n -> pole seperation, number of rx dipoles per tx
|
||||
|
||||
Output:
|
||||
:param Tx, Rx -> List objects for each tx location
|
||||
Lines: P1x, P1y, P1z, P2x, P2y, P2z
|
||||
|
||||
Created on Wed December 9th, 2015
|
||||
|
||||
@author: dominiquef
|
||||
!! Require clean up to deal with DCsurvey
|
||||
"""
|
||||
|
||||
from SimPEG import np
|
||||
|
||||
def xy_2_r(x1,x2,y1,y2):
|
||||
r = np.sqrt( np.sum((x2 - x1)**2 + (y2 - y1)**2) )
|
||||
return r
|
||||
|
||||
## Evenly distribute electrodes and put on surface
|
||||
# Mesure survey length and direction
|
||||
dl_len = xy_2_r(endl[0,0],endl[1,0],endl[0,1],endl[1,1])
|
||||
|
||||
dl_x = ( endl[1,0] - endl[0,0] ) / dl_len
|
||||
dl_y = ( endl[1,1] - endl[0,1] ) / dl_len
|
||||
|
||||
nstn = np.floor( dl_len / a )
|
||||
|
||||
# Compute discrete pole location along line
|
||||
stn_x = endl[0,0] + np.array(range(int(nstn)))*dl_x*a
|
||||
stn_y = endl[0,1] + np.array(range(int(nstn)))*dl_y*a
|
||||
|
||||
# Create line of P1 locations
|
||||
M = np.c_[stn_x, stn_y, np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
|
||||
# Create line of P2 locations
|
||||
N = np.c_[stn_x+a*dl_x, stn_y+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
|
||||
## Build list of Tx-Rx locations depending on survey type
|
||||
# Dipole-dipole: Moving tx with [a] spacing -> [AB a MN1 a MN2 ... a MNn]
|
||||
# Pole-dipole: Moving pole on one end -> [A a MN1 a MN2 ... MNn a B]
|
||||
Tx = []
|
||||
Rx = []
|
||||
SrcList = []
|
||||
|
||||
|
||||
if stype != 'gradient':
|
||||
|
||||
for ii in range(0, int(nstn)-1):
|
||||
|
||||
|
||||
if stype == 'dpdp':
|
||||
tx = np.c_[M[ii,:],N[ii,:]]
|
||||
elif stype == 'pdp':
|
||||
tx = np.c_[M[ii,:],M[ii,:]]
|
||||
|
||||
# Rx.append(np.c_[M[ii+1:indx,:],N[ii+1:indx,:]])
|
||||
|
||||
# Current elctrode seperation
|
||||
AB = xy_2_r(tx[0,1],endl[1,0],tx[1,1],endl[1,1])
|
||||
|
||||
# Number of receivers to fit
|
||||
nstn = np.min([np.floor( (AB - b) / a ) , n])
|
||||
|
||||
# Check if there is enough space, else break the loop
|
||||
if nstn <= 0:
|
||||
continue
|
||||
|
||||
# Compute discrete pole location along line
|
||||
stn_x = N[ii,0] + dl_x*b + np.array(range(int(nstn)))*dl_x*a
|
||||
stn_y = N[ii,1] + dl_y*b + np.array(range(int(nstn)))*dl_y*a
|
||||
|
||||
# Create receiver poles
|
||||
# Create line of P1 locations
|
||||
P1 = np.c_[stn_x, stn_y, np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
|
||||
# Create line of P2 locations
|
||||
P2 = np.c_[stn_x+a*dl_x, stn_y+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
|
||||
Rx.append(np.c_[P1,P2])
|
||||
rxClass = DC.RxDipole(P1, P2)
|
||||
Tx.append(tx)
|
||||
if stype == 'dpdp':
|
||||
srcClass = DC.SrcDipole([rxClass], M[ii,:],N[ii,:])
|
||||
elif stype == 'pdp':
|
||||
srcClass = DC.SrcDipole([rxClass], M[ii,:],M[ii,:])
|
||||
SrcList.append(srcClass)
|
||||
|
||||
#==============================================================================
|
||||
# elif re.match(stype,'dpdp'):
|
||||
#
|
||||
# for ii in range(0, int(nstn)-2):
|
||||
#
|
||||
# indx = np.min([ii+n+1,nstn])
|
||||
# Tx.append(np.c_[M[ii,:],N[ii,:]])
|
||||
# Rx.append(np.c_[M[ii+2:indx,:],N[ii+2:indx,:]])
|
||||
#==============================================================================
|
||||
|
||||
elif stype == 'gradient':
|
||||
|
||||
# Gradient survey only requires Tx at end of line and creates a square
|
||||
# grid of receivers at in the middle at a pre-set minimum distance
|
||||
|
||||
Tx.append(np.c_[M[0,:],N[-1,:]])
|
||||
|
||||
# Get the edge limit of survey area
|
||||
min_x = endl[0,0] + dl_x * b
|
||||
min_y = endl[0,1] + dl_y * b
|
||||
|
||||
max_x = endl[1,0] - dl_x * b
|
||||
max_y = endl[1,1] - dl_y * b
|
||||
|
||||
box_l = np.sqrt( (min_x - max_x)**2 + (min_y - max_y)**2 )
|
||||
box_w = box_l/2.
|
||||
|
||||
nstn = np.floor( box_l / a )
|
||||
|
||||
# Compute discrete pole location along line
|
||||
stn_x = min_x + np.array(range(int(nstn)))*dl_x*a
|
||||
stn_y = min_y + np.array(range(int(nstn)))*dl_y*a
|
||||
|
||||
# Define number of cross lines
|
||||
nlin = int(np.floor( box_w / a ))
|
||||
lind = range(-nlin,nlin+1)
|
||||
|
||||
ngrad = nstn * len(lind)
|
||||
|
||||
rx = np.zeros([ngrad,6])
|
||||
for ii in range( len(lind) ):
|
||||
|
||||
# Move line in perpendicular direction by dipole spacing
|
||||
lxx = stn_x - lind[ii]*a*dl_y
|
||||
lyy = stn_y + lind[ii]*a*dl_x
|
||||
|
||||
|
||||
M = np.c_[ lxx, lyy , np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
N = np.c_[ lxx+a*dl_x, lyy+a*dl_y, np.ones(nstn).T*mesh.vectorNz[-1]]
|
||||
|
||||
rx[(ii*nstn):((ii+1)*nstn),:] = np.c_[M,N]
|
||||
|
||||
Rx.append(rx)
|
||||
rxClass = DC.RxDipole(rx[:,:3], rx[:,3:])
|
||||
srcClass = DC.SrcDipole([rxClass], M[0,:], N[-1,:])
|
||||
SrcList.append(srcClass)
|
||||
else:
|
||||
print """stype must be either 'pdp', 'dpdp' or 'gradient'. """
|
||||
|
||||
survey = DC.SurveyDC(SrcList)
|
||||
return survey, Tx, Rx
|
||||
|
||||
def writeUBC_DCobs(fileName, DCsurvey, dtype, stype):
|
||||
"""
|
||||
Write UBC GIF DCIP 2D or 3D observation file
|
||||
|
||||
Input:
|
||||
:string fileName -> including path where the file is written out
|
||||
:DCsurvey -> DC survey class object
|
||||
:string dtype -> either '2D' | '3D'
|
||||
:string stype -> either 'SURFACE' | 'GENERAL'
|
||||
|
||||
Output:
|
||||
:param UBC2D-Data file
|
||||
:return
|
||||
|
||||
Last edit: February 16th, 2016
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
from SimPEG import mkvc
|
||||
|
||||
assert (dtype=='2D') | (dtype=='3D'), "Data must be either '2D' | '3D'"
|
||||
assert (stype=='SURFACE') | (stype=='GENERAL') | (stype=='SIMPLE'), "Data must be either 'SURFACE' | 'GENERAL' | 'SIMPLE'"
|
||||
|
||||
fid = open(fileName,'w')
|
||||
fid.write('! ' + stype + ' FORMAT\n')
|
||||
|
||||
count = 0
|
||||
|
||||
for ii in range(DCsurvey.nSrc):
|
||||
|
||||
tx = np.c_[DCsurvey.srcList[ii].loc]
|
||||
|
||||
rx = DCsurvey.srcList[ii].rxList[0].locs
|
||||
|
||||
nD = DCsurvey.srcList[ii].nD
|
||||
|
||||
M = rx[0]
|
||||
N = rx[1]
|
||||
|
||||
# Adapt source-receiver location for dtype and stype
|
||||
if dtype=='2D':
|
||||
|
||||
if stype == 'SIMPLE':
|
||||
|
||||
#fid.writelines("%e " % ii for ii in mkvc(tx[0,:]))
|
||||
A = np.repeat(tx[0,0],M.shape[0],axis=0)
|
||||
B = np.repeat(tx[0,1],M.shape[0],axis=0)
|
||||
M = M[:,0]
|
||||
N = N[:,0]
|
||||
|
||||
np.savetxt(fid, np.c_[A, B, M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n')
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if stype == 'SURFACE':
|
||||
|
||||
fid.writelines("%e " % ii for ii in mkvc(tx[0,:]))
|
||||
M = M[:,0]
|
||||
N = N[:,0]
|
||||
|
||||
if stype == 'GENERAL':
|
||||
|
||||
fid.writelines("%e " % ii for ii in mkvc(tx[::2,:]))
|
||||
M = M[:,0::2]
|
||||
N = N[:,0::2]
|
||||
|
||||
fid.write('%i\n'% nD)
|
||||
np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n')
|
||||
|
||||
if dtype=='3D':
|
||||
|
||||
if stype == 'SURFACE':
|
||||
|
||||
fid.writelines("%e " % ii for ii in mkvc(tx[0:2,:]))
|
||||
M = M[:,0:2]
|
||||
N = N[:,0:2]
|
||||
|
||||
if stype == 'GENERAL':
|
||||
|
||||
fid.writelines("%e " % ii for ii in mkvc(tx))
|
||||
|
||||
fid.write('%i\n'% nD)
|
||||
np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n')
|
||||
|
||||
count += nD
|
||||
|
||||
fid.close()
|
||||
|
||||
def convertObs_DC3D_to_2D(DCsurvey,lineID):
|
||||
"""
|
||||
Read DC survey and data and change
|
||||
coordinate system to distance along line assuming
|
||||
all data is acquired along line.
|
||||
First transmitter pole is assumed to be at the origin
|
||||
|
||||
Assumes flat topo for now...
|
||||
|
||||
Input:
|
||||
:param Tx, Rx
|
||||
|
||||
Output:
|
||||
:figure Tx2d, Rx2d
|
||||
|
||||
Edited Feb 17th, 2016
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
from SimPEG import np
|
||||
|
||||
def stn_id(v0,v1,r):
|
||||
"""
|
||||
Compute station ID along line
|
||||
"""
|
||||
|
||||
dl = int(v0.dot(v1)) * r
|
||||
|
||||
return dl
|
||||
|
||||
srcLists = []
|
||||
|
||||
srcMat = getSrc_locs(DCsurvey)
|
||||
|
||||
# Find all unique line id
|
||||
uniqueID = np.unique(lineID)
|
||||
|
||||
for jj in range(len(uniqueID)):
|
||||
|
||||
indx = np.where(lineID==uniqueID[jj])[0]
|
||||
|
||||
# Find origin of survey
|
||||
r = 1e+8 # Initialize to some large number
|
||||
|
||||
Tx = srcMat[indx]
|
||||
|
||||
x0 = Tx[0][0,0:2] # Define station zero along line
|
||||
|
||||
vecTx, r1 = r_unit(x0,Tx[-1][1,0:2])
|
||||
|
||||
for ii in range(len(indx)):
|
||||
|
||||
# Get all receivers
|
||||
Rx = DCsurvey.srcList[indx[ii]].rxList[0].locs
|
||||
nrx = Rx[0].shape[0]
|
||||
|
||||
# Find A electrode along line
|
||||
vec, r = r_unit(x0,Tx[ii][0,0:2])
|
||||
A = stn_id(vecTx,vec,r)
|
||||
|
||||
# Find B electrode along line
|
||||
vec, r = r_unit(x0,Tx[ii][1,0:2])
|
||||
B = stn_id(vecTx,vec,r)
|
||||
|
||||
M = np.zeros(nrx)
|
||||
N = np.zeros(nrx)
|
||||
for kk in range(nrx):
|
||||
|
||||
# Find all M electrodes along line
|
||||
vec, r = r_unit(x0,Rx[0][kk,0:2])
|
||||
M[kk] = stn_id(vecTx,vec,r)
|
||||
|
||||
# Find all N electrodes along line
|
||||
vec, r = r_unit(x0,Rx[1][kk,0:2])
|
||||
N[kk] = stn_id(vecTx,vec,r)
|
||||
|
||||
Rx = DC.RxDipole(np.c_[M,np.zeros(nrx),Rx[0][:,2]],np.c_[N,np.zeros(nrx),Rx[1][:,2]])
|
||||
|
||||
srcLists.append( DC.SrcDipole( [Rx], np.asarray([A,0,Tx[ii][0,2]]),np.asarray([B,0,Tx[ii][1,2]]) ) )
|
||||
|
||||
|
||||
DCsurvey2D = DC.SurveyDC(srcLists)
|
||||
|
||||
DCsurvey2D.dobs = np.asarray(DCsurvey.dobs)
|
||||
DCsurvey2D.std = np.asarray(DCsurvey.std)
|
||||
|
||||
return DCsurvey2D
|
||||
|
||||
def readUBC_DC3Dobs(fileName):
|
||||
"""
|
||||
Read UBC GIF DCIP 3D observation file and generate arrays for tx-rx location
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF 3D obs file
|
||||
|
||||
Output:
|
||||
:param rx, tx, d, wd
|
||||
:return
|
||||
|
||||
Created on Mon December 7th, 2015
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
|
||||
# Load file
|
||||
obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!')
|
||||
|
||||
# Pre-allocate
|
||||
srcLists = []
|
||||
Rx = []
|
||||
d = []
|
||||
wd = []
|
||||
zflag = True # Flag for z value provided
|
||||
|
||||
# Countdown for number of obs/tx
|
||||
count = 0
|
||||
for ii in range(obsfile.shape[0]):
|
||||
|
||||
if not obsfile[ii]:
|
||||
continue
|
||||
|
||||
# First line is transmitter with number of receivers
|
||||
if count==0:
|
||||
|
||||
temp = (np.fromstring(obsfile[ii], dtype=float,sep=' ').T)
|
||||
count = int(temp[-1])
|
||||
|
||||
# Check if z value is provided, if False -> nan
|
||||
if len(temp)==5:
|
||||
tx = np.r_[temp[0:2],np.nan,temp[0:2],np.nan]
|
||||
zflag = False
|
||||
|
||||
else:
|
||||
tx = temp[:-1]
|
||||
|
||||
rx = []
|
||||
continue
|
||||
|
||||
temp = np.fromstring(obsfile[ii], dtype=float,sep=' ')
|
||||
|
||||
if zflag:
|
||||
|
||||
rx.append(temp[:-2])
|
||||
# Check if there is data with the location
|
||||
if len(temp)==8:
|
||||
d.append(temp[-2])
|
||||
wd.append(temp[-1])
|
||||
|
||||
else:
|
||||
rx.append(np.r_[temp[0:2],np.nan,temp[0:2],np.nan] )
|
||||
# Check if there is data with the location
|
||||
if len(temp)==6:
|
||||
d.append(temp[-2])
|
||||
wd.append(temp[-1])
|
||||
|
||||
count = count -1
|
||||
|
||||
# Reach the end of transmitter block
|
||||
if count == 0:
|
||||
rx = np.asarray(rx)
|
||||
Rx = DC.RxDipole(rx[:,:3],rx[:,3:])
|
||||
srcLists.append( DC.SrcDipole( [Rx], tx[:3],tx[3:]) )
|
||||
|
||||
# Create survey class
|
||||
survey = DC.SurveyDC(srcLists)
|
||||
|
||||
survey.dobs = np.asarray(d)
|
||||
survey.std = np.asarray(wd)
|
||||
|
||||
return {'DCsurvey':survey}
|
||||
|
||||
def readUBC_DC2Dobs(fileName):
|
||||
"""
|
||||
Read UBC GIF 2D observation file and generate arrays for tx-rx location
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF 2D model file
|
||||
|
||||
Output:
|
||||
:param rx, tx
|
||||
:return
|
||||
|
||||
Created on Thu Nov 12 13:14:10 2015
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
|
||||
from SimPEG import np
|
||||
|
||||
# Load file
|
||||
obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!')
|
||||
|
||||
# Check first line and figure out if 2D or 3D file format
|
||||
line = np.array(obsfile[0].split(),dtype=float)
|
||||
|
||||
tx_A = []
|
||||
tx_B = []
|
||||
rx_M = []
|
||||
rx_N = []
|
||||
d = []
|
||||
wd = []
|
||||
|
||||
for ii in range(obsfile.shape[0]):
|
||||
|
||||
# If len==3, then simple format where tx-rx is listed on each line
|
||||
if len(line) == 4:
|
||||
|
||||
temp = np.fromstring(obsfile[ii], dtype=float,sep=' ')
|
||||
tx_A = np.hstack((tx_A,temp[0]))
|
||||
tx_B = np.hstack((tx_B,temp[1]))
|
||||
rx_M = np.hstack((rx_M,temp[2]))
|
||||
rx_N = np.hstack((rx_N,temp[3]))
|
||||
|
||||
|
||||
rx = np.transpose(np.array((rx_M,rx_N)))
|
||||
tx = np.transpose(np.array((tx_A,tx_B)))
|
||||
|
||||
return tx, rx, d, wd
|
||||
|
||||
def readUBC_DC2DMesh(fileName):
|
||||
"""
|
||||
Read UBC GIF 2DTensor mesh and generate 2D Tensor mesh in simpeg
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF mesh file
|
||||
|
||||
Output:
|
||||
:param SimPEG TensorMesh 2D object
|
||||
:return
|
||||
|
||||
Created on Thu Nov 12 13:14:10 2015
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
|
||||
from SimPEG import np
|
||||
# Open file
|
||||
fopen = open(fileName,'r')
|
||||
|
||||
# Read down the file and unpack dx vector
|
||||
def unpackdx(fid,nrows):
|
||||
for ii in range(nrows):
|
||||
|
||||
line = fid.readline()
|
||||
var = np.array(line.split(),dtype=float)
|
||||
|
||||
if ii==0:
|
||||
x0= var[0]
|
||||
xvec = np.ones(int(var[2])) * (var[1] - var[0]) / int(var[2])
|
||||
xend = var[1]
|
||||
|
||||
else:
|
||||
xvec = np.hstack((xvec,np.ones(int(var[1])) * (var[0] - xend) / int(var[1])))
|
||||
xend = var[0]
|
||||
|
||||
return x0, xvec
|
||||
|
||||
#%% Start with dx block
|
||||
# First line specifies the number of rows for x-cells
|
||||
line = fopen.readline()
|
||||
nl = np.array(line.split(),dtype=float)
|
||||
|
||||
[x0, dx] = unpackdx(fopen,nl)
|
||||
|
||||
|
||||
#%% Move down the file until reaching the z-block
|
||||
line = fopen.readline()
|
||||
if not line:
|
||||
line = fopen.readline()
|
||||
|
||||
#%% End with dz block
|
||||
# First line specifies the number of rows for z-cells
|
||||
line = fopen.readline()
|
||||
nl = np.array(line.split(),dtype=float)
|
||||
|
||||
[z0, dz] = unpackdx(fopen,nl)
|
||||
|
||||
# Flip z0 to be the bottom of the mesh for SimPEG
|
||||
z0 = z0 - sum(dz)
|
||||
dz = dz[::-1]
|
||||
#%% Make the mesh using SimPEG
|
||||
|
||||
from SimPEG import Mesh
|
||||
tensMsh = Mesh.TensorMesh([dx,dz],(x0, z0))
|
||||
return tensMsh
|
||||
|
||||
def xy_2_lineID(DCsurvey):
|
||||
"""
|
||||
Read DC survey class and append line ID.
|
||||
Assumes that the locations are listed in the order
|
||||
they were collected. May need to generalize for random
|
||||
point locations, but will be more expensive
|
||||
|
||||
Input:
|
||||
:param DCdict Vectors of station location
|
||||
|
||||
Output:
|
||||
:param LineID Vector of integers
|
||||
:return
|
||||
|
||||
Created on Thu Feb 11, 2015
|
||||
|
||||
@author: dominiquef
|
||||
|
||||
"""
|
||||
|
||||
# Compute unit vector between two points
|
||||
nstn = DCsurvey.nSrc
|
||||
|
||||
# Pre-allocate space
|
||||
lineID = np.zeros(nstn)
|
||||
|
||||
linenum = 0
|
||||
indx = 0
|
||||
|
||||
for ii in range(nstn):
|
||||
|
||||
if ii == 0:
|
||||
|
||||
A = DCsurvey.srcList[ii].loc[0]
|
||||
B = DCsurvey.srcList[ii].loc[1]
|
||||
|
||||
xout = np.mean([A[0:2],B[0:2]], axis = 0)
|
||||
|
||||
xy0 = A[:2]
|
||||
xym = xout
|
||||
|
||||
# Deal with replicate pole location
|
||||
if np.all(xy0==xym):
|
||||
|
||||
xym[0] = xym[0] + 1e-3
|
||||
|
||||
continue
|
||||
|
||||
A = DCsurvey.srcList[ii].loc[0]
|
||||
B = DCsurvey.srcList[ii].loc[1]
|
||||
|
||||
xin = np.mean([A[0:2],B[0:2]], axis = 0)
|
||||
|
||||
# Compute vector between neighbours
|
||||
vec1, r1 = r_unit(xout,xin)
|
||||
|
||||
# Compute vector between current stn and mid-point
|
||||
vec2, r2 = r_unit(xym,xin)
|
||||
|
||||
# Compute vector between current stn and start line
|
||||
vec3, r3 = r_unit(xy0,xin)
|
||||
|
||||
# Compute vector between mid-point and start line
|
||||
vec4, r4 = r_unit(xym,xy0)
|
||||
|
||||
# Compute dot product
|
||||
ang1 = np.abs(vec1.dot(vec2))
|
||||
ang2 = np.abs(vec3.dot(vec4))
|
||||
|
||||
# If the angles are smaller then 45d, than next point is on a new line
|
||||
if ((ang1 < np.cos(np.pi/4.)) | (ang2 < np.cos(np.pi/4.))) & (np.all(np.r_[r1,r2,r3,r4] > 0)):
|
||||
|
||||
# Re-initiate start and mid-point location
|
||||
xy0 = A[:2]
|
||||
xym = xin
|
||||
|
||||
# Deal with replicate pole location
|
||||
if np.all(xy0==xym):
|
||||
|
||||
xym[0] = xym[0] + 1e-3
|
||||
|
||||
linenum += 1
|
||||
indx = ii
|
||||
|
||||
else:
|
||||
xym = np.mean([xy0,xin], axis = 0)
|
||||
|
||||
lineID[ii] = linenum
|
||||
xout = xin
|
||||
|
||||
return lineID
|
||||
|
||||
def r_unit(p1,p2):
|
||||
"""
|
||||
r_unit(x,y) : Function computes the unit vector
|
||||
between two points with coordinates p1(x1,y1) and p2(x2,y2)
|
||||
|
||||
"""
|
||||
|
||||
assert len(p1)==len(p2), 'locs must be the same shape.'
|
||||
|
||||
dx = []
|
||||
for ii in range(len(p1)):
|
||||
dx.append((p2[ii] - p1[ii]))
|
||||
|
||||
# Compute length of vector
|
||||
r = np.linalg.norm(np.asarray(dx))
|
||||
|
||||
|
||||
if r!=0:
|
||||
vec = dx/r
|
||||
|
||||
else:
|
||||
vec = np.zeros(len(p1))
|
||||
|
||||
return vec, r
|
||||
|
||||
def getSrc_locs(DCsurvey):
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
|
||||
srcMat = np.zeros((DCsurvey.nSrc,2,3))
|
||||
for ii in range(DCsurvey.nSrc):
|
||||
print np.asarray(DCsurvey.srcList[ii].loc).shape
|
||||
srcMat[ii,:,:] = np.asarray(DCsurvey.srcList[ii].loc)
|
||||
|
||||
return srcMat
|
||||
@@ -0,0 +1,38 @@
|
||||
import numpy as np
|
||||
|
||||
def WennerSrcList(nElecs, aSpacing, in2D=False, plotIt=False):
|
||||
|
||||
import SimPEG.DCIP as DC
|
||||
|
||||
elocs = np.arange(0,aSpacing*nElecs,aSpacing)
|
||||
elocs -= (nElecs*aSpacing - aSpacing)/2
|
||||
space = 1
|
||||
WENNER = np.zeros((0,),dtype=int)
|
||||
for ii in range(nElecs):
|
||||
for jj in range(nElecs):
|
||||
test = np.r_[jj,jj+space,jj+space*2,jj+space*3]
|
||||
if np.any(test >= nElecs):
|
||||
break
|
||||
WENNER = np.r_[WENNER, test]
|
||||
space += 1
|
||||
WENNER = WENNER.reshape((-1,4))
|
||||
|
||||
|
||||
if plotIt:
|
||||
for i, s in enumerate('rbkg'):
|
||||
plt.plot(elocs[WENNER[:,i]],s+'.')
|
||||
plt.show()
|
||||
|
||||
# Create sources and receivers
|
||||
i = 0
|
||||
if in2D:
|
||||
getLoc = lambda ii, abmn: np.r_[elocs[WENNER[ii,abmn]],0]
|
||||
else:
|
||||
getLoc = lambda ii, abmn: np.r_[elocs[WENNER[ii,abmn]],0, 0]
|
||||
srcList = []
|
||||
for i in range(WENNER.shape[0]):
|
||||
rx = DC.RxDipole(getLoc(i,1),getLoc(i,2))
|
||||
src = DC.SrcDipole([rx], getLoc(i,0),getLoc(i,3))
|
||||
srcList += [src]
|
||||
|
||||
return srcList
|
||||
@@ -0,0 +1,4 @@
|
||||
from BaseDC import *
|
||||
from BaseIP import *
|
||||
from DCIPUtils import *
|
||||
import Utils
|
||||
+11
-17
@@ -59,20 +59,6 @@ class BaseDataMisfit(object):
|
||||
"""
|
||||
raise NotImplementedError('This method should be overwritten.')
|
||||
|
||||
# TODO: implement target misfit as a property, or possibly as an inversion directive.
|
||||
|
||||
# def target(self, forward):
|
||||
# """target(forward)
|
||||
|
||||
# Target for data misfit. By default this is the number of data,
|
||||
# which satisfies the Discrepancy Principle.
|
||||
|
||||
# :rtype: float
|
||||
# :return: data misfit target
|
||||
|
||||
# """
|
||||
# prob, survey = self.splitForward(forward)
|
||||
# return survey.nD
|
||||
|
||||
|
||||
class l2_DataMisfit(BaseDataMisfit):
|
||||
@@ -103,10 +89,18 @@ class l2_DataMisfit(BaseDataMisfit):
|
||||
"""
|
||||
|
||||
if getattr(self, '_Wd', None) is None:
|
||||
print 'SimPEG.l2_DataMisfit is creating default weightings for Wd.'
|
||||
|
||||
survey = self.survey
|
||||
eps = np.linalg.norm(Utils.mkvc(survey.dobs),2)*1e-5
|
||||
self._Wd = Utils.sdiag(1/(abs(survey.dobs)*survey.std+eps))
|
||||
|
||||
if getattr(survey,'std', None) is None:
|
||||
print 'SimPEG.DataMisfit.l2_DataMisfit assigning default std of 5%'
|
||||
survey.std = 0.05
|
||||
|
||||
if getattr(survey, 'eps', None) is None:
|
||||
print 'SimPEG.DataMisfit.l2_DataMisfit assigning default eps of 1e-5 * ||dobs||'
|
||||
survey.eps = np.linalg.norm(Utils.mkvc(survey.dobs),2)*1e-5
|
||||
|
||||
self._Wd = Utils.sdiag(1/(abs(survey.dobs)*survey.std+survey.eps))
|
||||
return self._Wd
|
||||
|
||||
@Wd.setter
|
||||
|
||||
@@ -206,6 +206,69 @@ class SaveOutputEveryIteration(_SaveEveryIteration):
|
||||
f.write(' %3d %1.4e %1.4e %1.4e %1.4e\n'%(self.opt.iter, self.invProb.beta, self.invProb.phi_d, self.invProb.phi_m, self.opt.f))
|
||||
f.close()
|
||||
|
||||
class SaveOutputDictEveryIteration(_SaveEveryIteration):
|
||||
"""SaveOutputDictEveryIteration"""
|
||||
|
||||
def initialize(self):
|
||||
print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '###-%s.npz'"%self.fileName
|
||||
|
||||
def endIter(self):
|
||||
# Save the data.
|
||||
ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) )
|
||||
phi_ms = 0.5*ms.dot(ms)
|
||||
if self.reg.smoothModel == True:
|
||||
mref = self.reg.mref
|
||||
else:
|
||||
mref = 0
|
||||
mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_mx = 0.5 * mx.dot(mx)
|
||||
if self.prob.mesh.dim==2:
|
||||
my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_my = 0.5 * my.dot(my)
|
||||
else:
|
||||
phi_my = 'NaN'
|
||||
if self.prob.mesh.dim==3:
|
||||
mz = self.reg.Wz * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_mz = 0.5 * mz.dot(mz)
|
||||
else:
|
||||
phi_mz = 'NaN'
|
||||
|
||||
|
||||
# Save the file as a npz
|
||||
np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred)
|
||||
|
||||
class SaveOutputDictEveryIteration(_SaveEveryIteration):
|
||||
"""SaveOutputDictEveryIteration
|
||||
A directive that saves some relevant information from the inversion run to a numpy .npz dictionary file (see numpy.savez function for further info).
|
||||
"""
|
||||
|
||||
def initialize(self):
|
||||
print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '%s-###.npz'"%self.fileName
|
||||
|
||||
def endIter(self):
|
||||
# Save the data.
|
||||
ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) )
|
||||
phi_ms = 0.5*ms.dot(ms)
|
||||
if self.reg.smoothModel == True:
|
||||
mref = self.reg.mref
|
||||
else:
|
||||
mref = 0
|
||||
mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_mx = 0.5 * mx.dot(mx)
|
||||
if self.prob.mesh.dim==2:
|
||||
my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_my = 0.5 * my.dot(my)
|
||||
else:
|
||||
phi_my = 'NaN'
|
||||
if self.prob.mesh.dim==3 and 'CYL' not in self.prob.mesh._meshType:
|
||||
mz = self.reg.Wz * ( self.reg.mapping * (self.invProb.curModel - mref) )
|
||||
phi_mz = 0.5 * mz.dot(mz)
|
||||
else:
|
||||
phi_mz = 'NaN'
|
||||
|
||||
|
||||
# Save the file as a npz
|
||||
np.savez('{:s}-{:03d}'.format(self.fileName,self.opt.iter), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,8 +58,9 @@ def MagneticDipoleWholeSpace(XYZ, srcLoc, sig, f, moment=1., orientation='X', mu
|
||||
|
||||
from SimPEG import EM
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.constants import mu_0
|
||||
freqs = np.logspace(-2,5,100)
|
||||
Bx, By, Bz = EM.Analytics.FDEM.AnalyticMagDipoleWholeSpace([0,100,0], [0,0,0], 1e-2, freqs, m=1, orientation='Z')
|
||||
Bx, By, Bz = EM.Analytics.FDEM.MagneticDipoleWholeSpace([0,100,0], [0,0,0], 1e-2, freqs, moment=1, orientation='Z')
|
||||
plt.loglog(freqs, np.abs(Bz.real)/mu_0, 'b')
|
||||
plt.loglog(freqs, np.abs(Bz.imag)/mu_0, 'r')
|
||||
plt.legend(('real','imag'))
|
||||
|
||||
+285
-166
@@ -15,18 +15,20 @@ class BaseFDEMProblem(BaseEMProblem):
|
||||
.. math ::
|
||||
|
||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
|
||||
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}}
|
||||
{\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}}
|
||||
|
||||
if using the E-B formulation (:code:`Problem_e`
|
||||
or :code:`Problem_b`) or the magnetic field
|
||||
or :code:`Problem_b`). Note that in this case, :math:`\mathbf{s_e}` is an integrated quantity.
|
||||
|
||||
If we write Maxwell's equations in terms of
|
||||
\\\(\\\mathbf{h}\\\) and current density \\\(\\\mathbf{j}\\\)
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{j} + i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\\\
|
||||
\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + i \omega \mathbf{M_{\mu}^e} \mathbf{h} = \mathbf{s_m} \\\\
|
||||
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||
|
||||
if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`).
|
||||
if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity.
|
||||
|
||||
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
|
||||
"""
|
||||
@@ -36,7 +38,11 @@ class BaseFDEMProblem(BaseEMProblem):
|
||||
|
||||
def fields(self, m=None):
|
||||
"""
|
||||
Solve the forward problem for the fields.
|
||||
Solve the forward problem for the fields.
|
||||
|
||||
:param numpy.array m: inversion model (nP,)
|
||||
:rtype numpy.array:
|
||||
:return F: forward solution
|
||||
"""
|
||||
|
||||
self.curModel = m
|
||||
@@ -48,56 +54,58 @@ class BaseFDEMProblem(BaseEMProblem):
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
sol = Ainv * rhs
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
ftype = self._fieldType + 'Solution'
|
||||
F[Srcs, ftype] = sol
|
||||
|
||||
F[Srcs, self._solutionType] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, f=None):
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
Sensitivity times a vector
|
||||
Sensitivity times a vector.
|
||||
|
||||
:param numpy.array m: inversion model (nP,)
|
||||
:param numpy.array v: vector which we take sensitivity product with (nP,)
|
||||
:param SimPEG.EM.FDEM.Fields u: fields object
|
||||
:rtype numpy.array:
|
||||
:return: Jv (ndata,)
|
||||
"""
|
||||
|
||||
if f is None:
|
||||
f = self.fields(m)
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
self.curModel = m
|
||||
|
||||
Jv = self.dataPair(self.survey)
|
||||
|
||||
for freq in self.survey.freqs:
|
||||
A = self.getA(freq) #
|
||||
A = self.getA(freq)
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
|
||||
for src in self.survey.getSrcByFreq(freq):
|
||||
ftype = self._fieldType + 'Solution'
|
||||
u_src = f[src, ftype]
|
||||
dA_dm = self.getADeriv_m(freq, u_src, v)
|
||||
dRHS_dm = self.getRHSDeriv_m(freq, src, v)
|
||||
du_dm = Ainv * ( - dA_dm + dRHS_dm )
|
||||
u_src = u[src, self._solutionType]
|
||||
dA_dm_v = self.getADeriv(freq, u_src, v)
|
||||
dRHS_dm_v = self.getRHSDeriv(freq, src, v)
|
||||
du_dm_v = Ainv * ( - dA_dm_v + dRHS_dm_v )
|
||||
|
||||
for rx in src.rxList:
|
||||
df_duFun = getattr(f, '_%sDeriv_u'%rx.projField, None)
|
||||
df_dudu_dm = df_duFun(src, du_dm, adjoint=False)
|
||||
|
||||
df_dmFun = getattr(f, '_%sDeriv_m'%rx.projField, None)
|
||||
df_dm = df_dmFun(src, v, adjoint=False)
|
||||
|
||||
Df_Dm = np.array(df_dudu_dm + df_dm,dtype=complex)
|
||||
|
||||
P = lambda v: rx.projectFieldsDeriv(src, self.mesh, f, v) # wrt u, also have wrt m
|
||||
|
||||
Jv[src, rx] = P(Df_Dm)
|
||||
|
||||
df_dmFun = getattr(u, '_%sDeriv'%rx.projField, None)
|
||||
df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
|
||||
Jv[src, rx] = rx.evalDeriv(src, self.mesh, u, df_dm_v)
|
||||
Ainv.clean()
|
||||
return Utils.mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, f=None):
|
||||
def Jtvec(self, m, v, u=None):
|
||||
"""
|
||||
Sensitivity transpose times a vector
|
||||
Sensitivity transpose times a vector
|
||||
|
||||
:param numpy.array m: inversion model (nP,)
|
||||
:param numpy.array v: vector which we take adjoint product with (nP,)
|
||||
:param SimPEG.EM.FDEM.Fields u: fields object
|
||||
:rtype numpy.array:
|
||||
:return: Jv (ndata,)
|
||||
"""
|
||||
|
||||
if f is None:
|
||||
f = self.fields(m)
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
self.curModel = m
|
||||
|
||||
@@ -112,49 +120,48 @@ class BaseFDEMProblem(BaseEMProblem):
|
||||
ATinv = self.Solver(AT, **self.solverOpts)
|
||||
|
||||
for src in self.survey.getSrcByFreq(freq):
|
||||
ftype = self._fieldType + 'Solution'
|
||||
u_src = f[src, ftype]
|
||||
u_src = u[src, self._solutionType]
|
||||
|
||||
for rx in src.rxList:
|
||||
PTv = rx.projectFieldsDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt u, need possibility wrt m
|
||||
PTv = rx.evalDeriv(src, self.mesh, u, v[src, rx], adjoint=True) # wrt u, need possibility wrt m
|
||||
|
||||
df_duTFun = getattr(u, '_%sDeriv'%rx.projField, None)
|
||||
df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
|
||||
|
||||
df_duTFun = getattr(f, '_%sDeriv_u'%rx.projField, None)
|
||||
df_duT = df_duTFun(src, PTv, adjoint=True)
|
||||
|
||||
ATinvdf_duT = ATinv * df_duT
|
||||
|
||||
dA_dmT = self.getADeriv_m(freq, u_src, ATinvdf_duT, adjoint=True)
|
||||
dRHS_dmT = self.getRHSDeriv_m(freq,src, ATinvdf_duT, adjoint=True)
|
||||
dA_dmT = self.getADeriv(freq, u_src, ATinvdf_duT, adjoint=True)
|
||||
dRHS_dmT = self.getRHSDeriv(freq, src, ATinvdf_duT, adjoint=True)
|
||||
du_dmT = -dA_dmT + dRHS_dmT
|
||||
|
||||
df_dmFun = getattr(f, '_%sDeriv_m'%rx.projField, None)
|
||||
dfT_dm = df_dmFun(src, PTv, adjoint=True)
|
||||
|
||||
du_dmT += dfT_dm
|
||||
df_dmT = df_dmT + du_dmT
|
||||
|
||||
# TODO: this should be taken care of by the reciever?
|
||||
real_or_imag = rx.projComp
|
||||
if real_or_imag is 'real':
|
||||
Jtv += np.array(du_dmT,dtype=complex).real
|
||||
Jtv += np.array(df_dmT, dtype=complex).real
|
||||
elif real_or_imag is 'imag':
|
||||
Jtv += - np.array(du_dmT,dtype=complex).real
|
||||
Jtv += - np.array(df_dmT, dtype=complex).real
|
||||
else:
|
||||
raise Exception('Must be real or imag')
|
||||
|
||||
ATinv.clean()
|
||||
|
||||
return Jtv
|
||||
return Utils.mkvc(Jtv)
|
||||
|
||||
def getSourceTerm(self, freq):
|
||||
"""
|
||||
Evaluates the sources for a given frequency and puts them in matrix form
|
||||
Evaluates the sources for a given frequency and puts them in matrix form
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE or nF, nSrc)
|
||||
:return: S_m, S_e
|
||||
:param float freq: Frequency
|
||||
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||
:return: S_m, S_e (nE or nF, nSrc)
|
||||
"""
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
if self._eqLocs is 'FE':
|
||||
if self._formulation is 'EB':
|
||||
S_m = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex)
|
||||
S_e = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex)
|
||||
elif self._eqLocs is 'EF':
|
||||
elif self._formulation is 'HJ':
|
||||
S_m = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex)
|
||||
S_e = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex)
|
||||
|
||||
@@ -172,38 +179,43 @@ class BaseFDEMProblem(BaseEMProblem):
|
||||
|
||||
class Problem_e(BaseFDEMProblem):
|
||||
"""
|
||||
By eliminating the magnetic flux density using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} + \mathbf{s_m}\\right)
|
||||
|
||||
|
||||
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||
By eliminating the magnetic flux density using
|
||||
|
||||
.. math ::
|
||||
|
||||
\\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{C}+ i \omega \mathbf{M^e_{\sigma}} \\right)\mathbf{e} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M^e}\mathbf{s_e}
|
||||
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} + \mathbf{s_m}\\right)
|
||||
|
||||
which we solve for \\\(\\\mathbf{e}\\\).
|
||||
|
||||
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||
|
||||
.. math ::
|
||||
|
||||
\\left(\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{C}+ i \omega \mathbf{M^e_{\sigma}} \\right)\mathbf{e} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M^e}\mathbf{s_e}
|
||||
|
||||
which we solve for :math:`\mathbf{e}`.
|
||||
|
||||
:param SimPEG.Mesh mesh: mesh
|
||||
"""
|
||||
|
||||
_fieldType = 'e'
|
||||
_eqLocs = 'FE'
|
||||
fieldsPair = Fields_e
|
||||
_solutionType = 'eSolution'
|
||||
_formulation = 'EB'
|
||||
fieldsPair = Fields_e
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{C} + i \omega \mathbf{M^e_{\sigma}}
|
||||
System matrix
|
||||
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{C} + i \omega \mathbf{M^e_{\sigma}}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MfMui = self.MfMui
|
||||
MeSigma = self.MeSigma
|
||||
C = self.mesh.edgeCurl
|
||||
@@ -211,7 +223,21 @@ class Problem_e(BaseFDEMProblem):
|
||||
return C.T*MfMui*C + 1j*omega(freq)*MeSigma
|
||||
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
def getADeriv(self, freq, u, v, adjoint=False):
|
||||
"""
|
||||
Product of the derivative of our system matrix with respect to the model and a vector
|
||||
|
||||
.. math ::
|
||||
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = i \omega \\frac{d \mathbf{M^e_{\sigma}}\mathbf{v} }{d\mathbf{m}}
|
||||
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray u: solution vector (nE,)
|
||||
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||
"""
|
||||
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
dMe_dsig = self.MeSigmaDeriv(u)
|
||||
|
||||
@@ -222,26 +248,37 @@ class Problem_e(BaseFDEMProblem):
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M_e}\mathbf{s_e}
|
||||
Right hand side for the system
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f}\mathbf{s_m} -i\omega\mathbf{M_e}\mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray
|
||||
:return: RHS (nE, nSrc)
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MfMui = self.MfMui
|
||||
|
||||
RHS = C.T * (MfMui * S_m) -1j * omega(freq) * S_e
|
||||
return C.T * (MfMui * S_m) -1j * omega(freq) * S_e
|
||||
|
||||
return RHS
|
||||
def getRHSDeriv(self, freq, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the right hand side with respect to the model
|
||||
|
||||
:param float freq: frequency
|
||||
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of rhs deriv with a vector
|
||||
"""
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
MfMui = self.MfMui
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||
|
||||
if adjoint:
|
||||
dRHS = MfMui * (C * v)
|
||||
@@ -253,37 +290,41 @@ class Problem_e(BaseFDEMProblem):
|
||||
|
||||
class Problem_b(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{e}\\\) using
|
||||
We eliminate :math:`\mathbf{e}` using
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
||||
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
||||
|
||||
and solve for \\\(\\\mathbf{b}\\\) using:
|
||||
and solve for :math:`\mathbf{b}` using:
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\\left(\mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} + i \omega \\right)\mathbf{b} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{M^e}\mathbf{s_e}
|
||||
\\left(\mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} + i \omega \\right)\mathbf{b} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{M^e}\mathbf{s_e}
|
||||
|
||||
.. note ::
|
||||
The inverse problem will not work with full anisotropy
|
||||
.. note ::
|
||||
The inverse problem will not work with full anisotropy
|
||||
|
||||
:param SimPEG.Mesh mesh: mesh
|
||||
"""
|
||||
|
||||
_fieldType = 'b'
|
||||
_eqLocs = 'FE'
|
||||
fieldsPair = Fields_b
|
||||
_solutionType = 'bSolution'
|
||||
_formulation = 'EB'
|
||||
fieldsPair = Fields_b
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} + i \omega
|
||||
System matrix
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
.. math ::
|
||||
\mathbf{A} = \mathbf{C} \mathbf{M^e_{\sigma}}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\mu^{-1}}^f} + i \omega
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MfMui = self.MfMui
|
||||
@@ -297,7 +338,21 @@ class Problem_b(BaseFDEMProblem):
|
||||
return MfMui.T*A
|
||||
return A
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
def getADeriv(self, freq, u, v, adjoint=False):
|
||||
|
||||
"""
|
||||
Product of the derivative of our system matrix with respect to the model and a vector
|
||||
|
||||
.. math ::
|
||||
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = \mathbf{C} \\frac{\mathbf{M^e_{\sigma}} \mathbf{v}}{d\mathbf{m}}
|
||||
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray u: solution vector (nF,)
|
||||
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||
"""
|
||||
|
||||
MfMui = self.MfMui
|
||||
C = self.mesh.edgeCurl
|
||||
@@ -318,12 +373,14 @@ class Problem_b(BaseFDEMProblem):
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
||||
Right hand side for the system
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray
|
||||
:return: RHS (nE, nSrc)
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
@@ -338,7 +395,18 @@ class Problem_b(BaseFDEMProblem):
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
def getRHSDeriv(self, freq, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the right hand side with respect to the model
|
||||
|
||||
:param float freq: frequency
|
||||
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of rhs deriv with a vector
|
||||
"""
|
||||
|
||||
C = self.mesh.edgeCurl
|
||||
S_m, S_e = src.eval(self)
|
||||
MfMui = self.MfMui
|
||||
@@ -347,7 +415,7 @@ class Problem_b(BaseFDEMProblem):
|
||||
v = self.MfMui * v
|
||||
|
||||
MeSigmaIDeriv = self.MeSigmaIDeriv(S_e)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||
|
||||
if not adjoint:
|
||||
RHSderiv = C * (MeSigmaIDeriv * v)
|
||||
@@ -370,38 +438,41 @@ class Problem_b(BaseFDEMProblem):
|
||||
|
||||
class Problem_j(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{h}\\\) using
|
||||
We eliminate \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right)
|
||||
\mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right)
|
||||
|
||||
and solve for \\\(\\\mathbf{j}\\\) using
|
||||
and solve for \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\\left(\mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{C}^T \mathbf{M_{\\rho}^f} + i \omega\\right)\mathbf{j} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{M^e} \mathbf{s_m} -i\omega\mathbf{s_e}
|
||||
\\left(\mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} + i \omega\\right)\mathbf{j} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1} \mathbf{M^e} \mathbf{s_m} -i\omega\mathbf{s_e}
|
||||
|
||||
.. note::
|
||||
This implementation does not yet work with full anisotropy!!
|
||||
.. note::
|
||||
This implementation does not yet work with full anisotropy!!
|
||||
|
||||
:param SimPEG.Mesh mesh: mesh
|
||||
"""
|
||||
|
||||
_fieldType = 'j'
|
||||
_eqLocs = 'EF'
|
||||
fieldsPair = Fields_j
|
||||
_solutionType = 'jSolution'
|
||||
_formulation = 'HJ'
|
||||
fieldsPair = Fields_j
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\\mathbf{A} = \\mathbf{C} \\mathbf{M^e_{mu^{-1}}} \\mathbf{C}^T \\mathbf{M^f_{\\sigma^{-1}}} + i\\omega
|
||||
System matrix
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
.. math ::
|
||||
\\mathbf{A} = \\mathbf{C} \\mathbf{M^e_{\\mu^{-1}}} \\mathbf{C}^{\\top} \\mathbf{M^f_{\\sigma^{-1}}} + i\\omega
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
@@ -416,39 +487,50 @@ class Problem_j(BaseFDEMProblem):
|
||||
return A
|
||||
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
def getADeriv(self, freq, u, v, adjoint=False):
|
||||
"""
|
||||
In this case, we assume that electrical conductivity, \\\(\\\sigma\\\) is the physical property of interest (i.e. \\\(\\\sigma\\\) = model.transform). Then we want
|
||||
Product of the derivative of our system matrix with respect to the model and a vector
|
||||
|
||||
.. math ::
|
||||
In this case, we assume that electrical conductivity, :math:`\sigma` is the physical property of interest (i.e. :math:`\sigma` = model.transform). Then we want
|
||||
|
||||
\\frac{\mathbf{A(\sigma)} \mathbf{v}}{d \\mathbf{m}} &= \\mathbf{C} \\mathbf{M^e_{mu^{-1}}} \\mathbf{C^T} \\frac{d \\mathbf{M^f_{\\sigma^{-1}}}}{d \\mathbf{m}}
|
||||
&= \\mathbf{C} \\mathbf{M^e_{mu}^{-1}} \\mathbf{C^T} \\frac{d \\mathbf{M^f_{\\sigma^{-1}}}}{d \\mathbf{\\sigma^{-1}}} \\frac{d \\mathbf{\\sigma^{-1}}}{d \\mathbf{\\sigma}} \\frac{d \\mathbf{\\sigma}}{d \\mathbf{m}}
|
||||
.. math ::
|
||||
|
||||
\\frac{\mathbf{A(\sigma)} \mathbf{v}}{d \mathbf{m}} = \mathbf{C} \mathbf{M^e_{mu^{-1}}} \mathbf{C^{\\top}} \\frac{d \mathbf{M^f_{\sigma^{-1}}}\mathbf{v} }{d \mathbf{m}}
|
||||
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray u: solution vector (nF,)
|
||||
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(u)
|
||||
MfRhoDeriv = self.MfRhoDeriv(u)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric is True:
|
||||
v = MfRho * v
|
||||
return MfRhoDeriv_m.T * (C * (MeMuI.T * (C.T * v)))
|
||||
return MfRhoDeriv.T * (C * (MeMuI.T * (C.T * v)))
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfRho.T * (C * ( MeMuI * (C.T * (MfRhoDeriv_m * v) )))
|
||||
return C * (MeMuI * (C.T * (MfRhoDeriv_m * v)))
|
||||
return MfRho.T * (C * ( MeMuI * (C.T * (MfRhoDeriv * v) )))
|
||||
return C * (MeMuI * (C.T * (MfRhoDeriv * v)))
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
Right hand side for the system
|
||||
|
||||
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
.. math ::
|
||||
|
||||
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
@@ -462,10 +544,21 @@ class Problem_j(BaseFDEMProblem):
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
def getRHSDeriv(self, freq, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the right hand side with respect to the model
|
||||
|
||||
:param float freq: frequency
|
||||
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of rhs deriv with a vector
|
||||
"""
|
||||
|
||||
C = self.mesh.edgeCurl
|
||||
MeMuI = self.MeMuI
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric:
|
||||
@@ -486,36 +579,38 @@ class Problem_j(BaseFDEMProblem):
|
||||
|
||||
class Problem_h(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{j}\\\) using
|
||||
We eliminate \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
||||
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
||||
|
||||
and solve for \\\(\\\mathbf{h}\\\) using
|
||||
and solve for \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. math ::
|
||||
.. math ::
|
||||
|
||||
\\left(\mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
\\left(\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
|
||||
:param SimPEG.Mesh mesh: mesh
|
||||
"""
|
||||
|
||||
_fieldType = 'h'
|
||||
_eqLocs = 'EF'
|
||||
fieldsPair = Fields_h
|
||||
_solutionType = 'hSolution'
|
||||
_formulation = 'HJ'
|
||||
fieldsPair = Fields_h
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
System matrix
|
||||
|
||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
||||
.. math::
|
||||
\mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMu = self.MeMu
|
||||
@@ -524,36 +619,60 @@ class Problem_h(BaseFDEMProblem):
|
||||
|
||||
return C.T * (MfRho * C) + 1j*omega(freq)*MeMu
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
def getADeriv(self, freq, u, v, adjoint=False):
|
||||
"""
|
||||
Product of the derivative of our system matrix with respect to the model and a vector
|
||||
|
||||
.. math::
|
||||
\\frac{\mathbf{A}(\mathbf{m}) \mathbf{v}}{d \mathbf{m}} = \mathbf{C}^{\\top}\\frac{d \mathbf{M^f_{\\rho}}\mathbf{v} }{d\mathbf{m}}
|
||||
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray u: solution vector (nE,)
|
||||
:param numpy.ndarray v: vector to take prodct with (nP,) or (nD,) for adjoint
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: derivative of the system matrix times a vector (nP,) or adjoint (nD,)
|
||||
"""
|
||||
|
||||
MeMu = self.MeMu
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(C*u)
|
||||
MfRhoDeriv = self.MfRhoDeriv(C*u)
|
||||
|
||||
if adjoint:
|
||||
return MfRhoDeriv_m.T * (C * v)
|
||||
return C.T * (MfRhoDeriv_m * v)
|
||||
return MfRhoDeriv.T * (C * v)
|
||||
return C.T * (MfRhoDeriv * v)
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
Right hand side for the system
|
||||
|
||||
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
.. math ::
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray
|
||||
:return: RHS (nE, nSrc)
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
|
||||
RHS = S_m + C.T * ( MfRho * S_e )
|
||||
return S_m + C.T * ( MfRho * S_e )
|
||||
|
||||
return RHS
|
||||
def getRHSDeriv(self, freq, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the right hand side with respect to the model
|
||||
|
||||
:param float freq: frequency
|
||||
:param SimPEG.EM.FDEM.Src src: FDEM source
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of rhs deriv with a vector
|
||||
"""
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
_, S_e = src.eval(self)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
@@ -564,7 +683,7 @@ class Problem_h(BaseFDEMProblem):
|
||||
elif adjoint:
|
||||
RHSDeriv = MfRhoDeriv.T * (C * v)
|
||||
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint=adjoint)
|
||||
|
||||
return RHSDeriv + S_mDeriv(v) + C.T * (MfRho * S_eDeriv(v))
|
||||
|
||||
|
||||
+1001
-113
File diff suppressed because it is too large
Load Diff
+349
-56
@@ -2,134 +2,309 @@ from SimPEG import Survey, Problem, Utils, np, sp
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.EM.Utils import *
|
||||
from SimPEG.Utils import Zero
|
||||
# from SurveyFDEM import Rx
|
||||
|
||||
|
||||
class BaseSrc(Survey.BaseSrc):
|
||||
"""
|
||||
Base source class for FDEM Survey
|
||||
"""
|
||||
|
||||
freq = None
|
||||
# rxPair = Rx
|
||||
# rxPair = RxFDEM
|
||||
integrate = True
|
||||
|
||||
def eval(self, prob):
|
||||
"""
|
||||
Evaluate the source terms.
|
||||
- :math:`S_m` : magnetic source term
|
||||
- :math:`S_e` : electric source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||
:return: tuple with magnetic source term and electric source term
|
||||
"""
|
||||
S_m = self.S_m(prob)
|
||||
S_e = self.S_e(prob)
|
||||
return S_m, S_e
|
||||
|
||||
def evalDeriv(self, prob, v, adjoint=False):
|
||||
return lambda v: self.S_mDeriv(prob,v,adjoint), lambda v: self.S_eDeriv(prob,v,adjoint)
|
||||
def evalDeriv(self, prob, v=None, adjoint=False):
|
||||
"""
|
||||
Derivatives of the source terms with respect to the inversion model
|
||||
- :code:`S_mDeriv` : derivative of the magnetic source term
|
||||
- :code:`S_eDeriv` : derivative of the electric source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: (numpy.ndarray, numpy.ndarray)
|
||||
:return: tuple with magnetic source term and electric source term derivatives times a vector
|
||||
"""
|
||||
if v is not None:
|
||||
return self.S_mDeriv(prob, v, adjoint), self.S_eDeriv(prob, v, adjoint)
|
||||
else:
|
||||
return lambda v: self.S_mDeriv(prob, v, adjoint), lambda v: self.S_eDeriv(prob, v, adjoint)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
"""
|
||||
Primary magnetic flux density
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic flux density
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def hPrimary(self, prob):
|
||||
"""
|
||||
Primary magnetic field
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def ePrimary(self, prob):
|
||||
"""
|
||||
Primary electric field
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary electric field
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def jPrimary(self, prob):
|
||||
"""
|
||||
Primary current density
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary current density
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def S_m(self, prob):
|
||||
"""
|
||||
Magnetic source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: magnetic source term on mesh
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def S_e(self, prob):
|
||||
"""
|
||||
Electric source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: electric source term on mesh
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
def S_mDeriv(self, prob, v, adjoint = False):
|
||||
"""
|
||||
Derivative of magnetic source term with respect to the inversion model
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of magnetic source term derivative with a vector
|
||||
"""
|
||||
|
||||
return Zero()
|
||||
|
||||
def S_eDeriv(self, prob, v, adjoint = False):
|
||||
"""
|
||||
Derivative of electric source term with respect to the inversion model
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:param numpy.ndarray v: vector to take product with
|
||||
:param bool adjoint: adjoint?
|
||||
:rtype: numpy.ndarray
|
||||
:return: product of electric source term derivative with a vector
|
||||
"""
|
||||
return Zero()
|
||||
|
||||
|
||||
class RawVec_e(BaseSrc):
|
||||
"""
|
||||
RawVec electric source. It is defined by the user provided vector S_e
|
||||
RawVec electric source. It is defined by the user provided vector S_e
|
||||
|
||||
:param numpy.array S_e: electric source term
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
:param list rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param numpy.array S_e: electric source term
|
||||
:param bool integrate: Integrate the source term (multiply by Me) [True]
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, S_e): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||
self._S_e = np.array(S_e,dtype=complex)
|
||||
def __init__(self, rxList, freq, S_e, integrate=True): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||
self._S_e = np.array(S_e, dtype=complex)
|
||||
self.freq = float(freq)
|
||||
self.integrate = integrate
|
||||
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def S_e(self, prob):
|
||||
"""
|
||||
Electric source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: electric source term on mesh
|
||||
"""
|
||||
if prob._formulation is 'EB' and self.integrate is True:
|
||||
return prob.Me * self._S_e
|
||||
return self._S_e
|
||||
|
||||
|
||||
class RawVec_m(BaseSrc):
|
||||
"""
|
||||
RawVec magnetic source. It is defined by the user provided vector S_m
|
||||
RawVec magnetic source. It is defined by the user provided vector S_m
|
||||
|
||||
:param numpy.array S_m: magnetic source term
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
:param numpy.array S_m: magnetic source term
|
||||
:param bool integrate: Integrate the source term (multiply by Me) [True]
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, S_m, integrate = True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()):
|
||||
self._S_m = np.array(S_m,dtype=complex)
|
||||
def __init__(self, rxList, freq, S_m, integrate=True): #ePrimary=Zero(), bPrimary=Zero(), hPrimary=Zero(), jPrimary=Zero()):
|
||||
self._S_m = np.array(S_m, dtype=complex)
|
||||
self.freq = float(freq)
|
||||
self.integrate = integrate
|
||||
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def S_m(self, prob):
|
||||
"""
|
||||
Magnetic source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: magnetic source term on mesh
|
||||
"""
|
||||
if prob._formulation is 'HJ' and self.integrate is True:
|
||||
return prob.Me * self._S_m
|
||||
return self._S_m
|
||||
|
||||
|
||||
class RawVec(BaseSrc):
|
||||
"""
|
||||
RawVec source. It is defined by the user provided vectors S_m, S_e
|
||||
RawVec source. It is defined by the user provided vectors S_m, S_e
|
||||
|
||||
:param numpy.array S_m: magnetic source term
|
||||
:param numpy.array S_e: electric source term
|
||||
:param float freq: frequency
|
||||
:param rxList: receiver list
|
||||
:param rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param numpy.array S_m: magnetic source term
|
||||
:param numpy.array S_e: electric source term
|
||||
:param bool integrate: Integrate the source term (multiply by Me) [True]
|
||||
"""
|
||||
def __init__(self, rxList, freq, S_m, S_e, integrate = True):
|
||||
self._S_m = np.array(S_m,dtype=complex)
|
||||
self._S_e = np.array(S_e,dtype=complex)
|
||||
def __init__(self, rxList, freq, S_m, S_e, integrate=True):
|
||||
self._S_m = np.array(S_m, dtype=complex)
|
||||
self._S_e = np.array(S_e, dtype=complex)
|
||||
self.freq = float(freq)
|
||||
self.integrate = integrate
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def S_m(self, prob):
|
||||
if prob._eqLocs is 'EF' and self.integrate is True:
|
||||
"""
|
||||
Magnetic source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: magnetic source term on mesh
|
||||
"""
|
||||
if prob._formulation is 'HJ' and self.integrate is True:
|
||||
return prob.Me * self._S_m
|
||||
return self._S_m
|
||||
|
||||
def S_e(self, prob):
|
||||
if prob._eqLocs is 'FE' and self.integrate is True:
|
||||
"""
|
||||
Electric source term
|
||||
|
||||
:param Problem prob: FDEM Problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: electric source term on mesh
|
||||
"""
|
||||
if prob._formulation is 'EB' and self.integrate is True:
|
||||
return prob.Me * self._S_e
|
||||
return self._S_e
|
||||
|
||||
|
||||
class MagDipole(BaseSrc):
|
||||
"""
|
||||
Point magnetic dipole source calculated by taking the curl of a magnetic
|
||||
vector potential. By taking the discrete curl, we ensure that the magnetic
|
||||
flux density is divergence free (no magnetic monopoles!).
|
||||
|
||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||
This approach uses a primary-secondary in frequency. Here we show the
|
||||
derivation for E-B formulation noting that similar steps are followed for
|
||||
the H-J formulation.
|
||||
|
||||
.. math::
|
||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\\\
|
||||
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{M_{\sigma}^e} \mathbf{e} = \mathbf{s_e}}
|
||||
|
||||
We split up the fields and :math:`\mu^{-1}` into primary (:math:`\mathbf{P}`) and secondary (:math:`\mathbf{S}`) components
|
||||
|
||||
- :math:`\mathbf{e} = \mathbf{e^P} + \mathbf{e^S}`
|
||||
- :math:`\mathbf{b} = \mathbf{b^P} + \mathbf{b^S}`
|
||||
- :math:`\\boldsymbol{\mu}^{\mathbf{-1}} = \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{P}} + \\boldsymbol{\mu}^{\mathbf{-1}^\mathbf{S}}`
|
||||
|
||||
and define a zero-frequency primary problem, noting that the source is
|
||||
generated by a divergence free electric current
|
||||
|
||||
.. math::
|
||||
\mathbf{C} \mathbf{e^P} = \mathbf{s_m^P} = 0 \\\\
|
||||
{\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} - \mathbf{M_{\sigma}^e} \mathbf{e^P} = \mathbf{M^e} \mathbf{s_e^P}}
|
||||
|
||||
Since :math:`\mathbf{e^P}` is curl-free, divergence-free, we assume that there is no constant field background, the :math:`\mathbf{e^P} = 0`, so our primary problem is
|
||||
|
||||
.. math::
|
||||
\mathbf{e^P} = 0 \\\\
|
||||
{\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^P} \mathbf{b^P} = \mathbf{s_e^P}}
|
||||
|
||||
Our secondary problem is then
|
||||
|
||||
.. math::
|
||||
\mathbf{C} \mathbf{e^S} + i \omega \mathbf{b^S} = - i \omega \mathbf{b^P} \\\\
|
||||
{\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b^S} - \mathbf{M_{\sigma}^e} \mathbf{e^S} = -\mathbf{C}^T \mathbf{{M_{\mu^{-1}}^f}^S} \mathbf{b^P}}
|
||||
|
||||
:param list rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||
:param string orientation: 'X', 'Y', 'Z'
|
||||
:param float moment: magnetic dipole moment
|
||||
:param float mu: background magnetic permeability
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu=mu_0):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
self.orientation = orientation
|
||||
assert orientation in ['X','Y','Z'], "Orientation (right now) doesn't actually do anything! The methods in SrcUtils should take care of this..."
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
self.integrate = False
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
"""
|
||||
The primary magnetic flux density from a magnetic vector potential
|
||||
|
||||
if eqLocs is 'FE':
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
formulation = prob._formulation
|
||||
|
||||
if formulation is 'EB':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
elif formulation is 'HJ':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
@@ -152,26 +327,51 @@ class MagDipole(BaseSrc):
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
"""
|
||||
The primary magnetic field from a magnetic vector potential
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob,b)
|
||||
return 1./self.mu * b
|
||||
|
||||
def S_m(self, prob):
|
||||
"""
|
||||
The magnetic source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
|
||||
b_p = self.bPrimary(prob)
|
||||
if prob._formulation is 'HJ':
|
||||
b_p = prob.Me * b_p
|
||||
return -1j*omega(self.freq)*b_p
|
||||
|
||||
def S_e(self, prob):
|
||||
"""
|
||||
The electric source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
formulation = prob._formulation
|
||||
|
||||
if eqLocs is 'FE':
|
||||
if formulation is 'EB':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
elif formulation is 'HJ':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
@@ -179,26 +379,48 @@ class MagDipole(BaseSrc):
|
||||
|
||||
class MagDipole_Bfield(BaseSrc):
|
||||
|
||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||
#TODO: neither does moment
|
||||
"""
|
||||
Point magnetic dipole source calculated with the analytic solution for the
|
||||
fields from a magnetic dipole. No discrete curl is taken, so the magnetic
|
||||
flux density may not be strictly divergence free.
|
||||
|
||||
This approach uses a primary-secondary in frequency in the same fashion as the MagDipole.
|
||||
|
||||
:param list rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||
:param string orientation: 'X', 'Y', 'Z'
|
||||
:param float moment: magnetic dipole moment
|
||||
:param float mu: background magnetic permeability
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
assert orientation in ['X','Y','Z'], "Orientation (right now) doesn't actually do anything! The methods in SrcUtils should take care of this..."
|
||||
self.orientation = orientation
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
"""
|
||||
The primary magnetic flux density from the analytic solution for magnetic fields from a dipole
|
||||
|
||||
if eqLocs is 'FE':
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
|
||||
formulation = prob._formulation
|
||||
|
||||
if formulation is 'EB':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
elif formulation is 'HJ':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
@@ -221,37 +443,74 @@ class MagDipole_Bfield(BaseSrc):
|
||||
return b
|
||||
|
||||
def hPrimary(self, prob):
|
||||
"""
|
||||
The primary magnetic field from a magnetic vector potential
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob, b)
|
||||
return 1/self.mu * b
|
||||
|
||||
def S_m(self, prob):
|
||||
"""
|
||||
The magnetic source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
b = self.bPrimary(prob)
|
||||
if prob._formulation is 'HJ':
|
||||
b = prob.Me * b
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
"""
|
||||
The electric source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
formulation = prob._formulation
|
||||
|
||||
if eqLocs is 'FE':
|
||||
if formulation is 'EB':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
elif formulation is 'HJ':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
class CircularLoop(BaseSrc):
|
||||
"""
|
||||
Circular loop magnetic source calculated by taking the curl of a magnetic
|
||||
vector potential. By taking the discrete curl, we ensure that the magnetic
|
||||
flux density is divergence free (no magnetic monopoles!).
|
||||
|
||||
#TODO: right now, orientation doesn't actually do anything! The methods in SrcUtils should take care of that
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', radius = 1., mu=mu_0):
|
||||
This approach uses a primary-secondary in frequency in the same fashion as the MagDipole.
|
||||
|
||||
:param list rxList: receiver list
|
||||
:param float freq: frequency
|
||||
:param numpy.ndarray loc: source location (ie: :code:`np.r_[xloc,yloc,zloc]`)
|
||||
:param string orientation: 'X', 'Y', 'Z'
|
||||
:param float moment: magnetic dipole moment
|
||||
:param float mu: background magnetic permeability
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', radius=1., mu=mu_0):
|
||||
self.freq = float(freq)
|
||||
self.orientation = orientation
|
||||
assert orientation in ['X','Y','Z'], "Orientation (right now) doesn't actually do anything! The methods in SrcUtils should take care of this..."
|
||||
self.radius = radius
|
||||
self.mu = mu
|
||||
self.loc = loc
|
||||
@@ -259,15 +518,22 @@ class CircularLoop(BaseSrc):
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
"""
|
||||
The primary magnetic flux density from a magnetic vector potential
|
||||
|
||||
if eqLocs is 'FE':
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
formulation = prob._formulation
|
||||
|
||||
if formulation is 'EB':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
elif formulation is 'HJ':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
@@ -289,28 +555,55 @@ class CircularLoop(BaseSrc):
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
"""
|
||||
The primary magnetic field from a magnetic vector potential
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
b = self.bPrimary(prob)
|
||||
return 1./self.mu*b
|
||||
|
||||
def S_m(self, prob):
|
||||
"""
|
||||
The magnetic source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
b = self.bPrimary(prob)
|
||||
if prob._formulation is 'HJ':
|
||||
b = prob.Me * b
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
"""
|
||||
The electric source term
|
||||
|
||||
:param Problem prob: FDEM problem
|
||||
:rtype: numpy.ndarray
|
||||
:return: primary magnetic field
|
||||
"""
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
formulation = prob._formulation
|
||||
|
||||
if eqLocs is 'FE':
|
||||
if formulation is 'EB':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
|
||||
|
||||
elif formulation is 'HJ':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s, invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from SimPEG.EM.Utils import *
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.Utils import Zero, Identity
|
||||
import SrcFDEM as Src
|
||||
from SimPEG import sp
|
||||
|
||||
|
||||
####################################################
|
||||
@@ -10,35 +11,41 @@ import SrcFDEM as Src
|
||||
####################################################
|
||||
|
||||
class Rx(SimPEG.Survey.BaseRx):
|
||||
"""
|
||||
Frequency domain receivers
|
||||
|
||||
:param numpy.ndarray locs: receiver locations (ie. :code:`np.r_[x,y,z]`)
|
||||
:param string rxType: reciever type from knownRxTypes
|
||||
"""
|
||||
|
||||
knownRxTypes = {
|
||||
'exr':['e', 'Ex', 'real'],
|
||||
'eyr':['e', 'Ey', 'real'],
|
||||
'ezr':['e', 'Ez', 'real'],
|
||||
'exi':['e', 'Ex', 'imag'],
|
||||
'eyi':['e', 'Ey', 'imag'],
|
||||
'ezi':['e', 'Ez', 'imag'],
|
||||
'exr':['e', 'x', 'real'],
|
||||
'eyr':['e', 'y', 'real'],
|
||||
'ezr':['e', 'z', 'real'],
|
||||
'exi':['e', 'x', 'imag'],
|
||||
'eyi':['e', 'y', 'imag'],
|
||||
'ezi':['e', 'z', 'imag'],
|
||||
|
||||
'bxr':['b', 'Fx', 'real'],
|
||||
'byr':['b', 'Fy', 'real'],
|
||||
'bzr':['b', 'Fz', 'real'],
|
||||
'bxi':['b', 'Fx', 'imag'],
|
||||
'byi':['b', 'Fy', 'imag'],
|
||||
'bzi':['b', 'Fz', 'imag'],
|
||||
'bxr':['b', 'x', 'real'],
|
||||
'byr':['b', 'y', 'real'],
|
||||
'bzr':['b', 'z', 'real'],
|
||||
'bxi':['b', 'x', 'imag'],
|
||||
'byi':['b', 'y', 'imag'],
|
||||
'bzi':['b', 'z', 'imag'],
|
||||
|
||||
'jxr':['j', 'Fx', 'real'],
|
||||
'jyr':['j', 'Fy', 'real'],
|
||||
'jzr':['j', 'Fz', 'real'],
|
||||
'jxi':['j', 'Fx', 'imag'],
|
||||
'jyi':['j', 'Fy', 'imag'],
|
||||
'jzi':['j', 'Fz', 'imag'],
|
||||
'jxr':['j', 'x', 'real'],
|
||||
'jyr':['j', 'y', 'real'],
|
||||
'jzr':['j', 'z', 'real'],
|
||||
'jxi':['j', 'x', 'imag'],
|
||||
'jyi':['j', 'y', 'imag'],
|
||||
'jzi':['j', 'z', 'imag'],
|
||||
|
||||
'hxr':['h', 'Ex', 'real'],
|
||||
'hyr':['h', 'Ey', 'real'],
|
||||
'hzr':['h', 'Ez', 'real'],
|
||||
'hxi':['h', 'Ex', 'imag'],
|
||||
'hyi':['h', 'Ey', 'imag'],
|
||||
'hzi':['h', 'Ez', 'imag'],
|
||||
'hxr':['h', 'x', 'real'],
|
||||
'hyr':['h', 'y', 'real'],
|
||||
'hzr':['h', 'z', 'real'],
|
||||
'hxi':['h', 'x', 'imag'],
|
||||
'hyi':['h', 'y', 'imag'],
|
||||
'hzi':['h', 'z', 'imag'],
|
||||
}
|
||||
radius = None
|
||||
|
||||
@@ -50,26 +57,49 @@ class Rx(SimPEG.Survey.BaseRx):
|
||||
"""Field Type projection (e.g. e b ...)"""
|
||||
return self.knownRxTypes[self.rxType][0]
|
||||
|
||||
@property
|
||||
def projGLoc(self):
|
||||
"""Grid Location projection (e.g. Ex Fy ...)"""
|
||||
return self.knownRxTypes[self.rxType][1]
|
||||
|
||||
@property
|
||||
def projComp(self):
|
||||
"""Component projection (real/imag)"""
|
||||
return self.knownRxTypes[self.rxType][2]
|
||||
|
||||
def projectFields(self, src, mesh, u):
|
||||
P = self.getP(mesh)
|
||||
def projGLoc(self, u):
|
||||
"""Grid Location projection (e.g. Ex Fy ...)"""
|
||||
return u._GLoc(self.rxType[0]) + self.knownRxTypes[self.rxType][1]
|
||||
|
||||
def eval(self, src, mesh, u):
|
||||
"""
|
||||
Project fields to recievers to get data.
|
||||
|
||||
:param Source src: FDEM source
|
||||
:param Mesh mesh: mesh used
|
||||
:param Fields f: fields object
|
||||
:rtype: numpy.ndarray
|
||||
:return: fields projected to recievers
|
||||
"""
|
||||
# projGLoc = u._GLoc(self.knownRxTypes[self.rxType][0])
|
||||
# projGLoc += self.knownRxTypes[self.rxType][1]
|
||||
|
||||
P = self.getP(mesh, self.projGLoc(u))
|
||||
u_part_complex = u[src, self.projField]
|
||||
# get the real or imag component
|
||||
real_or_imag = self.projComp
|
||||
u_part = getattr(u_part_complex, real_or_imag)
|
||||
|
||||
return P*u_part
|
||||
|
||||
def projectFieldsDeriv(self, src, mesh, u, v, adjoint=False):
|
||||
P = self.getP(mesh)
|
||||
def evalDeriv(self, src, mesh, u, v, adjoint=False):
|
||||
"""
|
||||
Derivative of projected fields with respect to the inversion model times a vector.
|
||||
|
||||
:param Source src: FDEM source
|
||||
:param Mesh mesh: mesh used
|
||||
:param Fields u: fields object
|
||||
:param numpy.ndarray v: vector to multiply
|
||||
:rtype: numpy.ndarray
|
||||
:return: fields projected to recievers
|
||||
"""
|
||||
|
||||
P = self.getP(mesh, self.projGLoc(u))
|
||||
|
||||
if not adjoint:
|
||||
Pv_complex = P * v
|
||||
@@ -95,10 +125,13 @@ class Rx(SimPEG.Survey.BaseRx):
|
||||
|
||||
class Survey(SimPEG.Survey.BaseSurvey):
|
||||
"""
|
||||
docstring for SurveyFDEM
|
||||
Frequency domain electromagnetic survey
|
||||
|
||||
:param list srcList: list of FDEM sources used in the survey
|
||||
"""
|
||||
|
||||
srcPair = Src.BaseSrc
|
||||
rxPaair = Rx
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
# Sort these by frequency
|
||||
@@ -126,6 +159,7 @@ class Survey(SimPEG.Survey.BaseSurvey):
|
||||
|
||||
@property
|
||||
def nSrcByFreq(self):
|
||||
"""Number of sources at each frequency"""
|
||||
if getattr(self, '_nSrcByFreq', None) is None:
|
||||
self._nSrcByFreq = {}
|
||||
for freq in self.freqs:
|
||||
@@ -133,16 +167,28 @@ class Survey(SimPEG.Survey.BaseSurvey):
|
||||
return self._nSrcByFreq
|
||||
|
||||
def getSrcByFreq(self, freq):
|
||||
"""Returns the sources associated with a specific frequency."""
|
||||
"""
|
||||
Returns the sources associated with a specific frequency.
|
||||
:param float freq: frequency for which we look up sources
|
||||
:rtype: dictionary
|
||||
:return: sources at the sepcified frequency
|
||||
"""
|
||||
assert freq in self._freqDict, "The requested frequency is not in this survey."
|
||||
return self._freqDict[freq]
|
||||
|
||||
def projectFields(self, u):
|
||||
def eval(self, u):
|
||||
"""
|
||||
Project fields to receiver locations
|
||||
:param Fields u: fields object
|
||||
:rtype: numpy.ndarray
|
||||
:return: data
|
||||
"""
|
||||
data = SimPEG.Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFields(src, self.mesh, u)
|
||||
data[src, rx] = rx.eval(src, self.mesh, u)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(self, u):
|
||||
raise Exception('Use Sources to project fields deriv.')
|
||||
def evalDeriv(self, u):
|
||||
raise Exception('Use Receivers to project fields deriv.')
|
||||
|
||||
|
||||
@@ -27,16 +27,25 @@ class FieldsTDEM(Problem.TimeFields):
|
||||
else:
|
||||
e = np.zeros((nE,nSrc)) # if nSrc == 1 else (nE, nSrc))
|
||||
u = np.concatenate((u, b, e))
|
||||
|
||||
return Utils.mkvc(u,nSrc)
|
||||
|
||||
|
||||
class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
|
||||
"""docstring for ProblemTDEM1D"""
|
||||
"""docstring for BaseTDEMProblem"""
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
_FieldsForward_pair = FieldsTDEM #: used for the forward calculation only
|
||||
|
||||
waveformType = "STEPOFF"
|
||||
current = None
|
||||
|
||||
def currentwaveform(self, wave):
|
||||
self._timeSteps = np.diff(wave[:,0])
|
||||
self.current = wave[:,1]
|
||||
self.waveformType = "GENERAL"
|
||||
|
||||
def fields(self, m):
|
||||
if self.verbose: print '%s\nCalculating fields(m)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
@@ -120,7 +129,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
|
||||
u = self.fields(m)
|
||||
p = self.Gvec(m, v, u)
|
||||
y = self.solveAh(m, p)
|
||||
Jv = self.survey.projectFieldsDeriv(u, v=y)
|
||||
Jv = self.survey.evalDeriv(u, v=y)
|
||||
if self.verbose: print '%s\nDone calculating J(v)\n%s'%('*'*50,'*'*50)
|
||||
return - mkvc(Jv)
|
||||
|
||||
@@ -147,7 +156,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
|
||||
if not isinstance(v, self.dataPair):
|
||||
v = self.dataPair(self.survey, v)
|
||||
|
||||
p = self.survey.projectFieldsDeriv(u, v=v, adjoint=True)
|
||||
p = self.survey.evalDeriv(u, v=v, adjoint=True)
|
||||
y = self.solveAht(m, p)
|
||||
w = self.Gtvec(m, y, u)
|
||||
if self.verbose: print '%s\nDone calculating J^T(v)\n%s'%('*'*50,'*'*50)
|
||||
|
||||
@@ -51,12 +51,12 @@ class RxTDEM(Survey.BaseTimeRx):
|
||||
else:
|
||||
return timeMesh.getInterpolationMat(self.times, self.projTLoc)
|
||||
|
||||
def projectFields(self, src, mesh, timeMesh, u):
|
||||
def eval(self, src, mesh, timeMesh, u):
|
||||
P = self.getP(mesh, timeMesh)
|
||||
u_part = Utils.mkvc(u[src, self.projField, :])
|
||||
return P*u_part
|
||||
|
||||
def projectFieldsDeriv(self, src, mesh, timeMesh, u, v, adjoint=False):
|
||||
def evalDeriv(self, src, mesh, timeMesh, u, v, adjoint=False):
|
||||
P = self.getP(mesh, timeMesh)
|
||||
|
||||
if not adjoint:
|
||||
@@ -79,12 +79,32 @@ class SrcTDEM(Survey.BaseSrc):
|
||||
|
||||
class SrcTDEM_VMD_MVP(SrcTDEM):
|
||||
|
||||
def __init__(self,rxList,loc):
|
||||
def __init__(self,rxList,loc,waveformType="STEPOFF"):
|
||||
self.loc = loc
|
||||
self.waveformType = waveformType
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
"""Vertical magnetic dipole, magnetic vector potential"""
|
||||
if self.waveformType == "STEPOFF":
|
||||
print ">> Step waveform: Non-zero initial condition"
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
||||
else:
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
||||
else:
|
||||
raise Exception('Unknown mesh for VMD')
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
elif self.waveformType == "GENERAL":
|
||||
print ">> General waveform: Zero initial condition"
|
||||
return {"b": np.zeros(mesh.nF)}
|
||||
else:
|
||||
raise NotImplementedError("Only use STEPOFF or GENERAL")
|
||||
|
||||
def getMeS(self, mesh, MfMui):
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, 'Ey')
|
||||
@@ -93,20 +113,38 @@ class SrcTDEM_VMD_MVP(SrcTDEM):
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = MagneticDipoleVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'])
|
||||
else:
|
||||
raise Exception('Unknown mesh for VMD')
|
||||
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
raise Exception('Unknown mesh for VMD')
|
||||
return mesh.edgeCurl.T*MfMui*mesh.edgeCurl*MVP
|
||||
|
||||
|
||||
class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
||||
|
||||
def __init__(self,rxList,loc,radius):
|
||||
def __init__(self,rxList,loc,radius,waveformType="STEPOFF"):
|
||||
self.loc = loc
|
||||
self.radius = radius
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
self.waveformType = waveformType
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
"""Circular Loop, magnetic vector potential"""
|
||||
if self.waveformType == "STEPOFF":
|
||||
print ">> Step waveform: Non-zero initial condition"
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = MagneticLoopVectorPotential(self.loc, mesh, 'Ey', self.radius)
|
||||
else:
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = MagneticLoopVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'], self.radius)
|
||||
else:
|
||||
raise Exception('Unknown mesh for CircularLoop')
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
elif self.waveformType == "GENERAL":
|
||||
print ">> General waveform: Zero initial condition"
|
||||
return {"b": np.zeros(mesh.nF)}
|
||||
else:
|
||||
raise NotImplementedError("Only use STEPOFF or GENERAL")
|
||||
|
||||
def getMeS(self, mesh, MfMui):
|
||||
if mesh._meshType is 'CYL':
|
||||
if mesh.isSymmetric:
|
||||
MVP = MagneticLoopVectorPotential(self.loc, mesh, 'Ey', self.radius)
|
||||
@@ -115,9 +153,8 @@ class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
||||
elif mesh._meshType is 'TENSOR':
|
||||
MVP = MagneticLoopVectorPotential(self.loc, mesh, ['Ex','Ey','Ez'], self.radius)
|
||||
else:
|
||||
raise Exception('Unknown mesh for CircularLoop')
|
||||
|
||||
return {"b": mesh.edgeCurl*MVP}
|
||||
raise Exception('Unknown mesh for CircularLoop')
|
||||
return mesh.edgeCurl.T*MfMui*mesh.edgeCurl*MVP
|
||||
|
||||
|
||||
class SurveyTDEM(Survey.BaseSurvey):
|
||||
@@ -131,27 +168,27 @@ class SurveyTDEM(Survey.BaseSurvey):
|
||||
self.srcList = srcList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
|
||||
def projectFields(self, u):
|
||||
def eval(self, u):
|
||||
data = Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFields(src, self.mesh, self.prob.timeMesh, u)
|
||||
data[src, rx] = rx.eval(src, self.mesh, self.prob.timeMesh, u)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(self, u, v=None, adjoint=False):
|
||||
def evalDeriv(self, u, v=None, adjoint=False):
|
||||
assert v is not None, 'v to multiply must be provided.'
|
||||
|
||||
if not adjoint:
|
||||
data = Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v)
|
||||
data[src, rx] = rx.evalDeriv(src, self.mesh, self.prob.timeMesh, u, v)
|
||||
return data
|
||||
else:
|
||||
f = FieldsTDEM(self.mesh, self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
Ptv = rx.projectFieldsDeriv(src, self.mesh, self.prob.timeMesh, u, v, adjoint=True)
|
||||
Ptv = rx.evalDeriv(src, self.mesh, self.prob.timeMesh, u, v, adjoint=True)
|
||||
Ptv = Ptv.reshape((-1, self.prob.timeMesh.nN), order='F')
|
||||
if rx.projField not in f: # first time we are projecting
|
||||
f[src, rx.projField, :] = Ptv
|
||||
|
||||
@@ -13,37 +13,4 @@ def k(freq, sigma, mu=mu_0, eps=epsilon_0):
|
||||
beta = w * np.sqrt( mu*eps/2 * ( np.sqrt(1. + (sigma / (eps*w))**2 ) - 1) )
|
||||
return alp - 1j*beta
|
||||
|
||||
# Constitutive relations
|
||||
def e_from_j(prob,j):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MSigmaI = prob.MeSigmaI
|
||||
elif eqLocs is 'EF':
|
||||
MSigmaI = prob.MfRho
|
||||
return MSigmaI*j
|
||||
|
||||
def j_from_e(prob,e):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MSigma = prob.MeSigma
|
||||
elif eqLocs is 'EF':
|
||||
MSigma = prob.MfRhoI
|
||||
return MSigma*e
|
||||
|
||||
def b_from_h(prob,h):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MMu = prob.MfMuiI
|
||||
elif eqLocs is 'EF':
|
||||
MMu = prob.MeMu
|
||||
return MMu*h
|
||||
|
||||
def h_from_b(prob,b):
|
||||
eqLocs = prob._eqLocs
|
||||
if eqLocs is 'FE':
|
||||
MMuI = prob.MfMui
|
||||
elif eqLocs is 'EF':
|
||||
MMuI = prob.MeMuI
|
||||
return MMuI*b
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
# import Sources
|
||||
# import Ana
|
||||
# import Solver
|
||||
from EMUtils import omega, e_from_j, j_from_e, b_from_h, h_from_b
|
||||
from EMUtils import omega, k
|
||||
from AnalyticUtils import MagneticDipoleFields, MagneticDipoleVectorPotential, MagneticLoopVectorPotential
|
||||
@@ -4,19 +4,28 @@ from SimPEG import EM
|
||||
import sys
|
||||
from scipy.constants import mu_0
|
||||
|
||||
def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False):
|
||||
cs = 5.
|
||||
ncx, ncy, ncz = 6, 6, 6
|
||||
npad = 3
|
||||
FLR = 1e-20 # "zero", so if residual below this --> pass regardless of order
|
||||
CONDUCTIVITY = 1e1
|
||||
MU = mu_0
|
||||
freq = 5e-1
|
||||
|
||||
|
||||
def getFDEMProblem(fdemType, comp, SrcList, freq, useMu=False, verbose=False):
|
||||
cs = 10.
|
||||
ncx, ncy, ncz = 0, 0, 0
|
||||
npad = 8
|
||||
hx = [(cs,npad,-1.3), (cs,ncx), (cs,npad,1.3)]
|
||||
hy = [(cs,npad,-1.3), (cs,ncy), (cs,npad,1.3)]
|
||||
hz = [(cs,npad,-1.3), (cs,ncz), (cs,npad,1.3)]
|
||||
mesh = Mesh.TensorMesh([hx,hy,hz],['C','C','C'])
|
||||
|
||||
mapping = Maps.ExpMap(mesh)
|
||||
if useMu is True:
|
||||
mapping = [('sigma', Maps.ExpMap(mesh)), ('mu', Maps.IdentityMap(mesh))]
|
||||
else:
|
||||
mapping = Maps.ExpMap(mesh)
|
||||
|
||||
x = np.array([np.linspace(-30,-15,3),np.linspace(15,30,3)]) #don't sample right by the source
|
||||
XYZ = Utils.ndgrid(x,x,np.r_[0.])
|
||||
x = np.array([np.linspace(-5.*cs,-2.*cs,3),np.linspace(5.*cs,2.*cs,3)]) + cs/4. #don't sample right by the source, slightly off alignment from either staggered grid
|
||||
XYZ = Utils.ndgrid(x,x,np.linspace(-2.*cs,2.*cs,5))
|
||||
Rx0 = EM.FDEM.Rx(XYZ, comp)
|
||||
|
||||
Src = []
|
||||
@@ -32,15 +41,15 @@ def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False):
|
||||
if fdemType is 'e' or fdemType is 'b':
|
||||
S_m = np.zeros(mesh.nF)
|
||||
S_e = np.zeros(mesh.nE)
|
||||
S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1.
|
||||
S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1.
|
||||
S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1e-3
|
||||
S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1e-3
|
||||
Src.append(EM.FDEM.Src.RawVec([Rx0], freq, S_m, S_e))
|
||||
|
||||
elif fdemType is 'h' or fdemType is 'j':
|
||||
S_m = np.zeros(mesh.nE)
|
||||
S_e = np.zeros(mesh.nF)
|
||||
S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1.
|
||||
S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1.
|
||||
S_m[Utils.closestPoints(mesh,[0.,0.,0.],'Ez') + np.sum(mesh.vnE[:1])] = 1e-3
|
||||
S_e[Utils.closestPoints(mesh,[0.,0.,0.],'Fz') + np.sum(mesh.vnF[:1])] = 1e-3
|
||||
Src.append(EM.FDEM.Src.RawVec([Rx0], freq, S_m, S_e))
|
||||
|
||||
if verbose:
|
||||
@@ -70,6 +79,48 @@ def getFDEMProblem(fdemType, comp, SrcList, freq, verbose=False):
|
||||
from pymatsolver import MumpsSolver
|
||||
prb.Solver = MumpsSolver
|
||||
except ImportError, e:
|
||||
pass
|
||||
prb.Solver = SolverLU
|
||||
|
||||
return prb
|
||||
return prb
|
||||
|
||||
def crossCheckTest(SrcList, fdemType1, fdemType2, comp, addrandoms = False, useMu=False, TOL=1e-5, verbose=False):
|
||||
|
||||
l2norm = lambda r: np.sqrt(r.dot(r))
|
||||
|
||||
prb1 = getFDEMProblem(fdemType1, comp, SrcList, freq, useMu, verbose)
|
||||
mesh = prb1.mesh
|
||||
print 'Cross Checking Forward: %s, %s formulations - %s' % (fdemType1, fdemType2, comp)
|
||||
|
||||
logsig = np.log(np.ones(mesh.nC)*CONDUCTIVITY)
|
||||
mu = np.ones(mesh.nC)*MU
|
||||
|
||||
if addrandoms is True:
|
||||
logsig += np.random.randn(mesh.nC)*np.log(CONDUCTIVITY)*1e-1
|
||||
mu += np.random.randn(mesh.nC)*MU*1e-1
|
||||
|
||||
if useMu is True:
|
||||
m = np.r_[logsig, mu]
|
||||
else:
|
||||
m = logsig
|
||||
|
||||
survey1 = prb1.survey
|
||||
d1 = survey1.dpred(m)
|
||||
|
||||
if verbose:
|
||||
print ' Problem 1 solved'
|
||||
|
||||
|
||||
prb2 = getFDEMProblem(fdemType2, comp, SrcList, freq, useMu, verbose)
|
||||
|
||||
survey2 = prb2.survey
|
||||
d2 = survey2.dpred(m)
|
||||
|
||||
if verbose:
|
||||
print ' Problem 2 solved'
|
||||
|
||||
r = d2-d1
|
||||
l2r = l2norm(r)
|
||||
|
||||
tol = np.max([TOL*(10**int(np.log10(0.5* (l2norm(d1) + l2norm(d2)) ))),FLR])
|
||||
print l2norm(d1), l2norm(d2), l2r , tol, l2r < tol
|
||||
return l2r < tol
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# from EM import *
|
||||
import TDEM
|
||||
import FDEM
|
||||
import Base
|
||||
import Analytics
|
||||
import Utils
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.DCIP as DC
|
||||
|
||||
def run(plotIt=False):
|
||||
cs = 25.
|
||||
hx = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)]
|
||||
hy = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)]
|
||||
hz = [(cs,7, -1.3),(cs,20)]
|
||||
mesh = Mesh.TensorMesh([hx, hy, hz], 'CCN')
|
||||
sighalf = 1e-2
|
||||
sigma = np.ones(mesh.nC)*sighalf
|
||||
xtemp = np.linspace(-150, 150, 21)
|
||||
ytemp = np.linspace(-150, 150, 21)
|
||||
xyz_rxP = Utils.ndgrid(xtemp-10., ytemp, np.r_[0.])
|
||||
xyz_rxN = Utils.ndgrid(xtemp+10., ytemp, np.r_[0.])
|
||||
xyz_rxM = Utils.ndgrid(xtemp, ytemp, np.r_[0.])
|
||||
|
||||
# if plotIt:
|
||||
# fig, ax = plt.subplots(1,1, figsize = (5,5))
|
||||
# mesh.plotSlice(sigma, grid=True, ax = ax)
|
||||
# ax.plot(xyz_rxP[:,0],xyz_rxP[:,1], 'w.')
|
||||
# ax.plot(xyz_rxN[:,0],xyz_rxN[:,1], 'r.', ms = 3)
|
||||
|
||||
rx = DC.RxDipole(xyz_rxP, xyz_rxN)
|
||||
src = DC.SrcDipole([rx], [-200, 0, -12.5], [+200, 0, -12.5])
|
||||
survey = DC.SurveyDC([src])
|
||||
problem = DC.ProblemDC_CC(mesh)
|
||||
problem.pair(survey)
|
||||
try:
|
||||
from pymatsolver import MumpsSolver
|
||||
problem.Solver = MumpsSolver
|
||||
except Exception, e:
|
||||
pass
|
||||
data = survey.dpred(sigma)
|
||||
|
||||
def DChalf(srclocP, srclocN, rxloc, sigma, I=1.):
|
||||
rp = (srclocP.reshape([1,-1])).repeat(rxloc.shape[0], axis = 0)
|
||||
rn = (srclocN.reshape([1,-1])).repeat(rxloc.shape[0], axis = 0)
|
||||
rP = np.sqrt(((rxloc-rp)**2).sum(axis=1))
|
||||
rN = np.sqrt(((rxloc-rn)**2).sum(axis=1))
|
||||
return I/(sigma*2.*np.pi)*(1/rP-1/rN)
|
||||
|
||||
data_anaP = DChalf(np.r_[-200, 0, 0.],np.r_[+200, 0, 0.], xyz_rxP, sighalf)
|
||||
data_anaN = DChalf(np.r_[-200, 0, 0.],np.r_[+200, 0, 0.], xyz_rxN, sighalf)
|
||||
data_ana = data_anaP-data_anaN
|
||||
Data_ana = data_ana.reshape((21, 21), order = 'F')
|
||||
Data = data.reshape((21, 21), order = 'F')
|
||||
X = xyz_rxM[:,0].reshape((21, 21), order = 'F')
|
||||
Y = xyz_rxM[:,1].reshape((21, 21), order = 'F')
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,2, figsize = (12, 5))
|
||||
vmin = np.r_[data, data_ana].min()
|
||||
vmax = np.r_[data, data_ana].max()
|
||||
dat1 = ax[1].contourf(X, Y, Data, 60, vmin = vmin, vmax = vmax)
|
||||
dat0 = ax[0].contourf(X, Y, Data_ana, 60, vmin = vmin, vmax = vmax)
|
||||
cb0 = plt.colorbar(dat1, orientation = 'horizontal', ax = ax[0])
|
||||
cb1 = plt.colorbar(dat1, orientation = 'horizontal', ax = ax[1])
|
||||
ax[1].set_title('Analytic')
|
||||
ax[0].set_title('Computed')
|
||||
plt.show()
|
||||
|
||||
return np.linalg.norm(data-data_ana)/np.linalg.norm(data_ana)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print run(plotIt=True)
|
||||
@@ -0,0 +1,187 @@
|
||||
from SimPEG import Mesh, Utils, np, sp
|
||||
import SimPEG.DCIP as DC
|
||||
import time
|
||||
|
||||
def run(loc=None, sig=None, radi=None, param=None, stype='dpdp', plotIt=True):
|
||||
"""
|
||||
DC Forward Simulation
|
||||
=====================
|
||||
|
||||
Forward model conductive spheres in a half-space and plot a pseudo-section
|
||||
|
||||
Created by @fourndo on Mon Feb 01 19:28:06 2016
|
||||
|
||||
"""
|
||||
|
||||
assert stype in ['pdp', 'dpdp'], "Source type (stype) must be pdp or dpdp (pole dipole or dipole dipole)"
|
||||
|
||||
|
||||
if loc is None:
|
||||
loc = np.c_[[-50.,0.,-50.],[50.,0.,-50.]]
|
||||
if sig is None:
|
||||
sig = np.r_[1e-2,1e-1,1e-3]
|
||||
if radi is None:
|
||||
radi = np.r_[25.,25.]
|
||||
if param is None:
|
||||
param = np.r_[30.,30.,5]
|
||||
|
||||
|
||||
# First we need to create a mesh and a model.
|
||||
|
||||
# This is our mesh
|
||||
dx = 5.
|
||||
|
||||
hxind = [(dx,15,-1.3), (dx, 75), (dx,15,1.3)]
|
||||
hyind = [(dx,15,-1.3), (dx, 10), (dx,15,1.3)]
|
||||
hzind = [(dx,15,-1.3),(dx, 15)]
|
||||
|
||||
mesh = Mesh.TensorMesh([hxind, hyind, hzind], 'CCN')
|
||||
|
||||
|
||||
# Set background conductivity
|
||||
model = np.ones(mesh.nC) * sig[0]
|
||||
|
||||
# First anomaly
|
||||
ind = Utils.ModelBuilder.getIndicesSphere(loc[:,0],radi[0],mesh.gridCC)
|
||||
model[ind] = sig[1]
|
||||
|
||||
# Second anomaly
|
||||
ind = Utils.ModelBuilder.getIndicesSphere(loc[:,1],radi[1],mesh.gridCC)
|
||||
model[ind] = sig[2]
|
||||
|
||||
# Get index of the center
|
||||
indy = int(mesh.nCy/2)
|
||||
|
||||
|
||||
# Plot the model for reference
|
||||
# Define core mesh extent
|
||||
xlim = 200
|
||||
zlim = 125
|
||||
|
||||
# Specify the survey type: "pdp" | "dpdp"
|
||||
|
||||
|
||||
# Then specify the end points of the survey. Let's keep it simple for now and survey above the anomalies, top of the mesh
|
||||
ends = [(-175,0),(175,0)]
|
||||
ends = np.c_[np.asarray(ends),np.ones(2).T*mesh.vectorNz[-1]]
|
||||
|
||||
# Snap the endpoints to the grid. Easier to create 2D section.
|
||||
indx = Utils.closestPoints(mesh, ends )
|
||||
locs = np.c_[mesh.gridCC[indx,0],mesh.gridCC[indx,1],np.ones(2).T*mesh.vectorNz[-1]]
|
||||
|
||||
# We will handle the geometry of the survey for you and create all the combination of tx-rx along line
|
||||
# [Tx, Rx] = DC.gen_DCIPsurvey(locs, mesh, stype, param[0], param[1], param[2])
|
||||
survey, Tx, Rx = DC.gen_DCIPsurvey(locs, mesh, stype, param[0], param[1], param[2])
|
||||
|
||||
# Define some global geometry
|
||||
dl_len = np.sqrt( np.sum((locs[0,:] - locs[1,:])**2) )
|
||||
dl_x = ( Tx[-1][0,1] - Tx[0][0,0] ) / dl_len
|
||||
dl_y = ( Tx[-1][1,1] - Tx[0][1,0] ) / dl_len
|
||||
azm = np.arctan(dl_y/dl_x)
|
||||
|
||||
#Set boundary conditions
|
||||
mesh.setCellGradBC('neumann')
|
||||
|
||||
# Define the differential operators needed for the DC problem
|
||||
Div = mesh.faceDiv
|
||||
Grad = mesh.cellGrad
|
||||
Msig = Utils.sdiag(1./(mesh.aveF2CC.T*(1./model)))
|
||||
|
||||
A = Div*Msig*Grad
|
||||
|
||||
# Change one corner to deal with nullspace
|
||||
A[0,0] = 1
|
||||
A = sp.csc_matrix(A)
|
||||
|
||||
# We will solve the system iteratively, so a pre-conditioner is helpful
|
||||
# This is simply a Jacobi preconditioner (inverse of the main diagonal)
|
||||
dA = A.diagonal()
|
||||
P = sp.spdiags(1/dA,0,A.shape[0],A.shape[0])
|
||||
|
||||
# Now we can solve the system for all the transmitters
|
||||
# We want to store the data
|
||||
data = []
|
||||
|
||||
# There is probably a more elegant way to do this, but we can just for-loop through the transmitters
|
||||
for ii in range(len(Tx)):
|
||||
|
||||
start_time = time.time() # Let's time the calculations
|
||||
|
||||
#print("Transmitter %i / %i\r" % (ii+1,len(Tx)))
|
||||
|
||||
# Select dipole locations for receiver
|
||||
rxloc_M = np.asarray(Rx[ii][:,0:3])
|
||||
rxloc_N = np.asarray(Rx[ii][:,3:])
|
||||
|
||||
|
||||
# For usual cases "dpdp" or "gradient"
|
||||
if stype == 'pdp':
|
||||
# Create an "inifinity" pole
|
||||
tx = np.squeeze(Tx[ii][:,0:1])
|
||||
tinf = tx + np.array([dl_x,dl_y,0])*dl_len*2
|
||||
inds = Utils.closestPoints(mesh, np.c_[tx,tinf].T)
|
||||
RHS = mesh.getInterpolationMat(np.asarray(Tx[ii]).T, 'CC').T*( [-1] / mesh.vol[inds] )
|
||||
else:
|
||||
inds = Utils.closestPoints(mesh, np.asarray(Tx[ii]).T )
|
||||
RHS = mesh.getInterpolationMat(np.asarray(Tx[ii]).T, 'CC').T*( [-1,1] / mesh.vol[inds] )
|
||||
|
||||
# Iterative Solve
|
||||
Ainvb = sp.linalg.bicgstab(P*A,P*RHS, tol=1e-5)
|
||||
|
||||
# We now have the potential everywhere
|
||||
phi = Utils.mkvc(Ainvb[0])
|
||||
|
||||
# Solve for phi on pole locations
|
||||
P1 = mesh.getInterpolationMat(rxloc_M, 'CC')
|
||||
P2 = mesh.getInterpolationMat(rxloc_N, 'CC')
|
||||
|
||||
# Compute the potential difference
|
||||
dtemp = (P1*phi - P2*phi)*np.pi
|
||||
|
||||
data.append( dtemp )
|
||||
print '\rTransmitter {0} of {1} -> Time:{2} sec'.format(ii,len(Tx),time.time()- start_time),
|
||||
|
||||
print 'Transmitter {0} of {1}'.format(ii,len(Tx))
|
||||
print 'Forward completed'
|
||||
|
||||
# Let's just convert the 3D format into 2D (distance along line) and plot
|
||||
# [Tx2d, Rx2d] = DC.convertObs_DC3D_to_2D(survey, np.ones(survey.nSrc))
|
||||
survey2D = DC.convertObs_DC3D_to_2D(survey, np.ones(survey.nSrc))
|
||||
survey2D.dobs =np.hstack(data)
|
||||
# Here is an example for the first tx-rx array
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
ax = plt.subplot(2,1,1, aspect='equal')
|
||||
mesh.plotSlice(np.log10(model), ax =ax, normal = 'Y', ind = indy,grid=True)
|
||||
ax.set_title('E-W section at '+str(mesh.vectorCCy[indy])+' m')
|
||||
plt.gca().set_aspect('equal', adjustable='box')
|
||||
|
||||
plt.scatter(Tx[0][0,:],Tx[0][2,:],s=40,c='g', marker='v')
|
||||
plt.scatter(Rx[0][:,0::3],Rx[0][:,2::3],s=40,c='y')
|
||||
plt.xlim([-xlim,xlim])
|
||||
plt.ylim([-zlim,mesh.vectorNz[-1]+dx])
|
||||
|
||||
|
||||
ax = plt.subplot(2,1,2, aspect='equal')
|
||||
|
||||
# Plot the location of the spheres for reference
|
||||
circle1=plt.Circle((loc[0,0]-Tx[0][0,0],loc[2,0]),radi[0],color='w',fill=False, lw=3)
|
||||
circle2=plt.Circle((loc[0,1]-Tx[0][0,0],loc[2,1]),radi[1],color='k',fill=False, lw=3)
|
||||
ax.add_artist(circle1)
|
||||
ax.add_artist(circle2)
|
||||
|
||||
# Add the speudo section
|
||||
DC.plot_pseudoSection(survey2D,ax,stype)
|
||||
|
||||
# plt.scatter(Tx2d[0][:],Tx[0][2,:],s=40,c='g', marker='v')
|
||||
# plt.scatter(Rx2d[0][:],Rx[0][:,2::3],s=40,c='y')
|
||||
# plt.plot(np.r_[Tx2d[0][0],Rx2d[-1][-1,-1]],np.ones(2)*mesh.vectorNz[-1], color='k')
|
||||
plt.ylim([-zlim,mesh.vectorNz[-1]+dx])
|
||||
|
||||
plt.show()
|
||||
|
||||
return fig, ax
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -1,6 +1,6 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.EM as EM
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.EM import mu_0
|
||||
|
||||
|
||||
def run(plotIt=True):
|
||||
@@ -17,54 +17,62 @@ def run(plotIt=True):
|
||||
hz = [(cs,npad,-1.3), (cs,ncz), (cs,npad,1.3)]
|
||||
mesh = Mesh.CylMesh([hx,1,hz], '00C')
|
||||
|
||||
layerz = -100.
|
||||
|
||||
active = mesh.vectorCCz<0.
|
||||
layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=-100.)
|
||||
actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap
|
||||
sig_half = 2e-3
|
||||
layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=layerz)
|
||||
actMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * actMap
|
||||
sig_half = 2e-2
|
||||
sig_air = 1e-8
|
||||
sig_layer = 1e-3
|
||||
sig_layer = 1e-2
|
||||
sigma = np.ones(mesh.nCz)*sig_air
|
||||
sigma[active] = sig_half
|
||||
sigma[layer] = sig_layer
|
||||
mtrue = np.log(sigma[active])
|
||||
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_ylim(-500, 0)
|
||||
ax.set_xlim(1e-3, 1e-1)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
|
||||
|
||||
rxOffset=1e-3
|
||||
rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 30]]), np.logspace(-5,-3, 31), 'bz')
|
||||
src = EM.TDEM.SrcTDEM_VMD_MVP([rx], np.array([0., 0., 80]))
|
||||
survey = EM.TDEM.SurveyTDEM([src])
|
||||
prb = EM.TDEM.ProblemTDEM_b(mesh, mapping=mapping)
|
||||
rxOffset=10.
|
||||
bzi = EM.FDEM.Rx(np.array([[rxOffset, 0., 1e-3]]), 'bzi')
|
||||
|
||||
freqs = np.logspace(1,3,10)
|
||||
srcLoc = np.array([0., 0., 10.])
|
||||
|
||||
srcList = []
|
||||
[srcList.append(EM.FDEM.Src.MagDipole([bzi],freq, srcLoc,orientation='Z')) for freq in freqs]
|
||||
|
||||
survey = EM.FDEM.Survey(srcList)
|
||||
prb = EM.FDEM.Problem_b(mesh, mapping=mapping)
|
||||
|
||||
try:
|
||||
from pymatsolver import MumpsSolver
|
||||
prb.Solver = MumpsSolver
|
||||
except ImportError, e:
|
||||
prb.Solver = SolverLU
|
||||
|
||||
prb.Solver = SolverLU
|
||||
prb.timeSteps = [(1e-06, 20),(1e-05, 20), (0.0001, 20)]
|
||||
prb.pair(survey)
|
||||
dtrue = survey.dpred(mtrue)
|
||||
|
||||
|
||||
survey.dtrue = dtrue
|
||||
std = 0.05
|
||||
noise = std*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
|
||||
survey.dobs = survey.dtrue+noise
|
||||
survey.std = survey.dobs*0 + std
|
||||
survey.Wd = 1/(abs(survey.dobs)*std)
|
||||
survey.makeSyntheticData(mtrue, std)
|
||||
|
||||
survey.std = std
|
||||
survey.eps = np.linalg.norm(survey.dtrue)*1e-5
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,1, figsize = (10, 6))
|
||||
ax.loglog(rx.times, dtrue, 'b.-')
|
||||
ax.loglog(rx.times, survey.dobs, 'r.-')
|
||||
fig, ax = plt.subplots(1,1, figsize = (6, 6))
|
||||
ax.semilogx(freqs,survey.dtrue[:freqs.size], 'b.-')
|
||||
ax.semilogx(freqs,survey.dobs[:freqs.size], 'r.-')
|
||||
ax.legend(('Noisefree', '$d^{obs}$'), fontsize = 16)
|
||||
ax.set_xlabel('Time (s)', fontsize = 14)
|
||||
ax.set_ylabel('$B_z$ (T)', fontsize = 16)
|
||||
@@ -74,14 +82,15 @@ def run(plotIt=True):
|
||||
dmisfit = DataMisfit.l2_DataMisfit(survey)
|
||||
regMesh = Mesh.TensorMesh([mesh.hz[mapping.maps[-1].indActive]])
|
||||
reg = Regularization.Tikhonov(regMesh)
|
||||
opt = Optimization.InexactGaussNewton(maxIter = 5)
|
||||
opt = Optimization.InexactGaussNewton(maxIter = 6)
|
||||
invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt)
|
||||
|
||||
# Create an inversion object
|
||||
beta = Directives.BetaSchedule(coolingFactor=5, coolingRate=2)
|
||||
betaest = Directives.BetaEstimate_ByEig(beta0_ratio=1e0)
|
||||
inv = Inversion.BaseInversion(invProb, directiveList=[beta,betaest])
|
||||
m0 = np.log(np.ones(mtrue.size)*sig_half)
|
||||
reg.alpha_s = 1e-2
|
||||
reg.alpha_s = 1e-3
|
||||
reg.alpha_x = 1.
|
||||
prb.counter = opt.counter = Utils.Counter()
|
||||
opt.LSshorten = 0.5
|
||||
@@ -94,12 +103,12 @@ def run(plotIt=True):
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
plt.semilogx(np.exp(mopt), mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_ylim(-500, 0)
|
||||
ax.set_xlim(1e-3, 1e-1)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
plt.legend(['$\sigma_{true}$', '$\sigma_{pred}$'])
|
||||
plt.legend(['$\sigma_{true}$', '$\sigma_{pred}$'],loc='best')
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.EM as EM
|
||||
|
||||
def run(XYZ=None, loc=np.r_[0.,0.,0.], sig=1.0, freq=1.0, orientation='Z', plotIt=True):
|
||||
"""
|
||||
EM: Magnetic Dipole in a Whole-Space
|
||||
====================================
|
||||
|
||||
Here we plot the magnetic flux density from a harmonic dipole in a wholespace.
|
||||
|
||||
"""
|
||||
|
||||
if XYZ is None:
|
||||
x = np.arange(-100.5,100.5,step = 1.) #(avoid putting measurement points where source is located)
|
||||
y = np.r_[0]
|
||||
z = x
|
||||
XYZ = Utils.ndgrid(x,y,z)
|
||||
|
||||
|
||||
Bx, By, Bz = EM.Analytics.FDEM.MagneticDipoleWholeSpace(XYZ, loc, sig, freq, orientation=orientation)
|
||||
absB = np.sqrt(Bx*Bx.conj()+By*By.conj()+Bz*Bz.conj()).real
|
||||
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.colors import LogNorm
|
||||
fig, ax = plt.subplots(1,1,figsize=(6,5))
|
||||
bxplt = Bx.reshape(x.size,z.size)
|
||||
bzplt = Bz.reshape(x.size,z.size)
|
||||
pc = ax.pcolor(x,z,absB.reshape(x.size,z.size),norm=LogNorm())
|
||||
ax.streamplot(x,z,bxplt.real,bzplt.real,color='k',density=1)
|
||||
ax.set_xlim([x.min(),x.max()])
|
||||
ax.set_ylim([z.min(),z.max()])
|
||||
ax.set_xlabel('x')
|
||||
ax.set_ylabel('z')
|
||||
cb = plt.colorbar(pc,ax = ax)
|
||||
cb.set_label('|B| (T)')
|
||||
plt.show()
|
||||
|
||||
return fig, ax
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,106 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.EM as EM
|
||||
from SimPEG.EM import mu_0
|
||||
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
EM: TDEM: 1D: Inversion
|
||||
=======================
|
||||
|
||||
Here we will create and run a TDEM 1D inversion.
|
||||
|
||||
"""
|
||||
|
||||
cs, ncx, ncz, npad = 5., 25, 15, 15
|
||||
hx = [(cs,ncx), (cs,npad,1.3)]
|
||||
hz = [(cs,npad,-1.3), (cs,ncz), (cs,npad,1.3)]
|
||||
mesh = Mesh.CylMesh([hx,1,hz], '00C')
|
||||
|
||||
active = mesh.vectorCCz<0.
|
||||
layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=-100.)
|
||||
actMap = Maps.InjectActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.SurjectVertical1D(mesh) * actMap
|
||||
sig_half = 2e-3
|
||||
sig_air = 1e-8
|
||||
sig_layer = 1e-3
|
||||
sigma = np.ones(mesh.nCz)*sig_air
|
||||
sigma[active] = sig_half
|
||||
sigma[layer] = sig_layer
|
||||
mtrue = np.log(sigma[active])
|
||||
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
|
||||
|
||||
rxOffset=1e-3
|
||||
rx = EM.TDEM.RxTDEM(np.array([[rxOffset, 0., 30]]), np.logspace(-5,-3, 31), 'bz')
|
||||
src = EM.TDEM.SrcTDEM_VMD_MVP([rx], np.array([0., 0., 80]))
|
||||
survey = EM.TDEM.SurveyTDEM([src])
|
||||
prb = EM.TDEM.ProblemTDEM_b(mesh, mapping=mapping)
|
||||
|
||||
prb.Solver = SolverLU
|
||||
prb.timeSteps = [(1e-06, 20),(1e-05, 20), (0.0001, 20)]
|
||||
prb.pair(survey)
|
||||
|
||||
# create observed data
|
||||
std = 0.05
|
||||
|
||||
survey.dobs = survey.makeSyntheticData(mtrue,std)
|
||||
survey.std = std
|
||||
survey.eps = 1e-5*np.linalg.norm(survey.dobs)
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,1, figsize = (10, 6))
|
||||
ax.loglog(rx.times, survey.dtrue, 'b.-')
|
||||
ax.loglog(rx.times, survey.dobs, 'r.-')
|
||||
ax.legend(('Noisefree', '$d^{obs}$'), fontsize = 16)
|
||||
ax.set_xlabel('Time (s)', fontsize = 14)
|
||||
ax.set_ylabel('$B_z$ (T)', fontsize = 16)
|
||||
ax.set_xlabel('Time (s)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
|
||||
dmisfit = DataMisfit.l2_DataMisfit(survey)
|
||||
regMesh = Mesh.TensorMesh([mesh.hz[mapping.maps[-1].indActive]])
|
||||
reg = Regularization.Tikhonov(regMesh)
|
||||
opt = Optimization.InexactGaussNewton(maxIter = 5)
|
||||
invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt)
|
||||
|
||||
# Create an inversion object
|
||||
beta = Directives.BetaSchedule(coolingFactor=5, coolingRate=2)
|
||||
betaest = Directives.BetaEstimate_ByEig(beta0_ratio=1e0)
|
||||
inv = Inversion.BaseInversion(invProb, directiveList=[beta,betaest])
|
||||
m0 = np.log(np.ones(mtrue.size)*sig_half)
|
||||
reg.alpha_s = 1e-2
|
||||
reg.alpha_x = 1.
|
||||
prb.counter = opt.counter = Utils.Counter()
|
||||
opt.LSshorten = 0.5
|
||||
opt.remember('xc')
|
||||
|
||||
mopt = inv.run(m0)
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, ax = plt.subplots(1,1, figsize = (3, 6))
|
||||
plt.semilogx(sigma[active], mesh.vectorCCz[active])
|
||||
plt.semilogx(np.exp(mopt), mesh.vectorCCz[active])
|
||||
ax.set_ylim(-600, 0)
|
||||
ax.set_xlim(1e-4, 1e-2)
|
||||
ax.set_xlabel('Conductivity (S/m)', fontsize = 14)
|
||||
ax.set_ylabel('Depth (m)', fontsize = 14)
|
||||
ax.grid(color='k', alpha=0.5, linestyle='dashed', linewidth=0.5)
|
||||
plt.legend(['$\sigma_{true}$', '$\sigma_{pred}$'])
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -10,28 +10,6 @@ def run(N=100, plotIt=True):
|
||||
|
||||
"""
|
||||
|
||||
class LinearSurvey(Survey.BaseSurvey):
|
||||
def projectFields(self, u):
|
||||
return u
|
||||
|
||||
class LinearProblem(Problem.BaseProblem):
|
||||
|
||||
surveyPair = LinearSurvey
|
||||
|
||||
def __init__(self, mesh, G, **kwargs):
|
||||
Problem.BaseProblem.__init__(self, mesh, **kwargs)
|
||||
self.G = G
|
||||
|
||||
def fields(self, m, u=None):
|
||||
return self.G.dot(m)
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
return self.G.dot(v)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
return self.G.T.dot(v)
|
||||
|
||||
|
||||
np.random.seed(1)
|
||||
|
||||
mesh = Mesh.TensorMesh([N])
|
||||
@@ -53,8 +31,8 @@ def run(N=100, plotIt=True):
|
||||
mtrue[mesh.vectorCCx > 0.45] = -0.5
|
||||
mtrue[mesh.vectorCCx > 0.6] = 0
|
||||
|
||||
prob = LinearProblem(mesh, G)
|
||||
survey = LinearSurvey()
|
||||
prob = Problem.LinearProblem(mesh, G)
|
||||
survey = Survey.LinearSurvey()
|
||||
survey.pair(prob)
|
||||
survey.makeSyntheticData(mtrue, std=0.01)
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import SimPEG as simpeg
|
||||
import numpy as np
|
||||
import SimPEG.MT as MT
|
||||
from scipy.constants import mu_0
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
MT: 1D: Inversion
|
||||
=======================
|
||||
|
||||
Forward model 1D MT data.
|
||||
Setup and run a MT 1D inversion.
|
||||
|
||||
"""
|
||||
|
||||
## Setup the forward modeling
|
||||
# Setting up 1D mesh and conductivity models to forward model data.
|
||||
# Frequency
|
||||
nFreq = 31
|
||||
freqs = np.logspace(3,-3,nFreq)
|
||||
# Set mesh parameters
|
||||
ct = 20
|
||||
air = simpeg.Utils.meshTensor([(ct,16,1.4)])
|
||||
core = np.concatenate( ( np.kron(simpeg.Utils.meshTensor([(ct,10,-1.3)]),np.ones((5,))) , simpeg.Utils.meshTensor([(ct,5)]) ) )
|
||||
bot = simpeg.Utils.meshTensor([(core[0],10,-1.4)])
|
||||
x0 = -np.array([np.sum(np.concatenate((core,bot)))])
|
||||
# Make the model
|
||||
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
|
||||
|
||||
# Setup model varibles
|
||||
active = m1d.vectorCCx<0.
|
||||
layer1 = (m1d.vectorCCx<-500.) & (m1d.vectorCCx>=-800.)
|
||||
layer2 = (m1d.vectorCCx<-3500.) & (m1d.vectorCCx>=-5000.)
|
||||
# Set the conductivity values
|
||||
sig_half = 2e-3
|
||||
sig_air = 1e-8
|
||||
sig_layer1 = .2
|
||||
sig_layer2 = .2
|
||||
# Make the true model
|
||||
sigma_true = np.ones(m1d.nCx)*sig_air
|
||||
sigma_true[active] = sig_half
|
||||
sigma_true[layer1] = sig_layer1
|
||||
sigma_true[layer2] = sig_layer2
|
||||
# Extract the model
|
||||
m_true = np.log(sigma_true[active])
|
||||
# Make the background model
|
||||
sigma_0 = np.ones(m1d.nCx)*sig_air
|
||||
sigma_0[active] = sig_half
|
||||
m_0 = np.log(sigma_0[active])
|
||||
|
||||
# Set the mapping
|
||||
actMap = simpeg.Maps.ActiveCells(m1d, active, np.log(1e-8), nC=m1d.nCx)
|
||||
mappingExpAct = simpeg.Maps.ExpMap(m1d) * actMap
|
||||
|
||||
## Setup the layout of the survey, set the sources and the connected receivers
|
||||
# Receivers
|
||||
rxList = []
|
||||
for rxType in ['z1dr','z1di']:
|
||||
rxList.append(MT.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType))
|
||||
# Source list
|
||||
srcList =[]
|
||||
for freq in freqs:
|
||||
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||
# Make the survey
|
||||
survey = MT.Survey(srcList)
|
||||
survey.mtrue = m_true
|
||||
|
||||
## Set the problem
|
||||
problem = MT.Problem1D.eForm_psField(m1d,sigmaPrimary=sigma_0,mapping=mappingExpAct)
|
||||
problem.pair(survey)
|
||||
|
||||
## Forward model data
|
||||
# Project the data
|
||||
survey.dtrue = survey.dpred(m_true)
|
||||
survey.dobs = survey.dtrue + 0.025*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
|
||||
|
||||
if plotIt:
|
||||
fig = MT.Utils.dataUtils.plotMT1DModelData(problem)
|
||||
fig.suptitle('Target - smooth true')
|
||||
|
||||
|
||||
# Assign uncertainties
|
||||
std = 0.05 # 5% std
|
||||
survey.std = np.abs(survey.dobs*std)
|
||||
# Assign the data weight
|
||||
Wd = 1./survey.std
|
||||
|
||||
## Setup the inversion proceedure
|
||||
# Define a counter
|
||||
C = simpeg.Utils.Counter()
|
||||
# Set the optimization
|
||||
opt = simpeg.Optimization.InexactGaussNewton(maxIter = 30)
|
||||
opt.counter = C
|
||||
opt.LSshorten = 0.5
|
||||
opt.remember('xc')
|
||||
# Data misfit
|
||||
dmis = simpeg.DataMisfit.l2_DataMisfit(survey)
|
||||
dmis.Wd = Wd
|
||||
# Regularization - with a regularization mesh
|
||||
regMesh = simpeg.Mesh.TensorMesh([m1d.hx[problem.mapping.sigmaMap.maps[-1].indActive]],m1d.x0)
|
||||
reg = simpeg.Regularization.Tikhonov(regMesh)
|
||||
reg.smoothModel = True
|
||||
reg.alpha_s = 1e-7
|
||||
reg.alpha_x = 1.
|
||||
# Inversion problem
|
||||
invProb = simpeg.InvProblem.BaseInvProblem(dmis, reg, opt)
|
||||
invProb.counter = C
|
||||
# Beta cooling
|
||||
beta = simpeg.Directives.BetaSchedule()
|
||||
beta.coolingRate = 4
|
||||
betaest = simpeg.Directives.BetaEstimate_ByEig(beta0_ratio=0.75)
|
||||
targmis = simpeg.Directives.TargetMisfit()
|
||||
targmis.target = survey.nD
|
||||
saveModel = simpeg.Directives.SaveModelEveryIteration()
|
||||
saveModel.fileName = 'Inversion_TargMisEqnD_smoothTrue'
|
||||
# Create an inversion object
|
||||
inv = simpeg.Inversion.BaseInversion(invProb, directiveList=[beta,betaest,targmis])
|
||||
|
||||
## Run the inversion
|
||||
mopt = inv.run(m_0)
|
||||
|
||||
if plotIt:
|
||||
fig = MT.Utils.dataUtils.plotMT1DModelData(problem,[mopt])
|
||||
fig.suptitle('Target - smooth true')
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,64 @@
|
||||
# Test script to use SimPEG.MT platform to forward model synthetic data.
|
||||
|
||||
# Import
|
||||
import SimPEG as simpeg
|
||||
from SimPEG import MT
|
||||
import numpy as np
|
||||
try:
|
||||
from pymatsolver import MumpsSolver as Solver
|
||||
except:
|
||||
from SimPEG import Solver
|
||||
|
||||
def run(plotIt=True, nFreq=1):
|
||||
"""
|
||||
MT: 3D: Forward
|
||||
=======================
|
||||
|
||||
Forward model 3D MT data.
|
||||
|
||||
"""
|
||||
|
||||
# Make a mesh
|
||||
M = simpeg.Mesh.TensorMesh([[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,1.6),(100.,10),(100,3,2)]], x0=['C','C',-3529.5360])
|
||||
# Setup the model
|
||||
conds = [1e-2,1]
|
||||
sig = simpeg.Utils.ModelBuilder.defineBlock(M.gridCC,[-1000,-1000,-400],[1000,1000,-200],conds)
|
||||
sig[M.gridCC[:,2]>0] = 1e-8
|
||||
sig[M.gridCC[:,2]<-600] = 1e-1
|
||||
sigBG = np.zeros(M.nC) + conds[0]
|
||||
sigBG[M.gridCC[:,2]>0] = 1e-8
|
||||
|
||||
## Setup the the survey object
|
||||
# Receiver locations
|
||||
rx_x, rx_y = np.meshgrid(np.arange(-500,501,50),np.arange(-500,501,50))
|
||||
rx_loc = np.hstack((simpeg.Utils.mkvc(rx_x,2),simpeg.Utils.mkvc(rx_y,2),np.zeros((np.prod(rx_x.shape),1))))
|
||||
# Make a receiver list
|
||||
rxList = []
|
||||
for loc in rx_loc:
|
||||
# NOTE: loc has to be a (1,3) np.ndarray otherwise errors accure
|
||||
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
|
||||
rxList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType))
|
||||
# Source list
|
||||
srcList =[]
|
||||
for freq in np.logspace(3,-3,nFreq):
|
||||
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq))
|
||||
# Survey MT
|
||||
survey = MT.Survey(srcList)
|
||||
|
||||
## Setup the problem object
|
||||
problem = MT.Problem3D.eForm_ps(M, sigmaPrimary=sigBG)
|
||||
problem.pair(survey)
|
||||
problem.Solver = Solver
|
||||
|
||||
# Calculate the data
|
||||
fields = problem.fields(sig)
|
||||
dataVec = survey.eval(fields)
|
||||
|
||||
# Make the data
|
||||
mtData = MT.Data(survey,dataVec)
|
||||
# Add plots
|
||||
if plotIt:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -1,7 +1,11 @@
|
||||
# Run this file to add imports.
|
||||
|
||||
##### AUTOIMPORTS #####
|
||||
import DC_Analytic_Dipole
|
||||
import DC_Forward_PseudoSection
|
||||
import EM_FDEM_1D_Inversion
|
||||
import EM_FDEM_Analytic_MagDipoleWholespace
|
||||
import EM_TDEM_1D_Inversion
|
||||
import FLOW_Richards_1D_Celia1990
|
||||
import Forward_BasicDirectCurrent
|
||||
import Inversion_Linear
|
||||
@@ -12,8 +16,10 @@ import Mesh_QuadTree_Creation
|
||||
import Mesh_QuadTree_FaceDiv
|
||||
import Mesh_QuadTree_HangingNodes
|
||||
import Mesh_Tensor_Creation
|
||||
import MT_1D_ForwardAndInversion
|
||||
import MT_3D_Foward
|
||||
|
||||
__examples__ = ["EM_FDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation"]
|
||||
__examples__ = ["DC_Analytic_Dipole", "DC_Forward_PseudoSection", "EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Forward_BasicDirectCurrent", "Inversion_Linear", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward"]
|
||||
|
||||
##### AUTOIMPORTS #####
|
||||
|
||||
@@ -28,16 +34,17 @@ if __name__ == '__main__':
|
||||
from SimPEG import Examples
|
||||
|
||||
# Create the examples dir in the docs folder.
|
||||
docExamplesDir = os.path.sep.join(os.path.realpath(__file__).split(os.path.sep)[:-3] + ['docs', 'examples'])
|
||||
fName = os.path.realpath(__file__)
|
||||
docExamplesDir = os.path.sep.join(fName.split(os.path.sep)[:-3] + ['docs', 'examples'])
|
||||
shutil.rmtree(docExamplesDir)
|
||||
os.makedirs(docExamplesDir)
|
||||
|
||||
# Get all the python examples in this folder
|
||||
thispath = os.path.sep.join(__file__.split(os.path.sep)[:-1])
|
||||
thispath = os.path.sep.join(fName.split(os.path.sep)[:-1])
|
||||
exfiles = [f[:-3] for f in os.listdir(thispath) if os.path.isfile(os.path.join(thispath, f)) and f.endswith('.py') and not f.startswith('_')]
|
||||
|
||||
# Add the imports to the top in the AUTOIMPORTS section
|
||||
f = file(__file__, 'r')
|
||||
f = file(fName, 'r')
|
||||
inimports = False
|
||||
out = ''
|
||||
for line in f:
|
||||
@@ -52,7 +59,7 @@ if __name__ == '__main__':
|
||||
out += '\n##### AUTOIMPORTS #####\n'
|
||||
f.close()
|
||||
|
||||
f = file(__file__, 'w')
|
||||
f = file(fName, 'w')
|
||||
f.write(out)
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class RichardsRx(Survey.BaseTimeRx):
|
||||
|
||||
knownRxTypes = ['saturation','pressureHead']
|
||||
|
||||
def projectFields(self, U, m, mapping, mesh, timeMesh):
|
||||
def eval(self, U, m, mapping, mesh, timeMesh):
|
||||
|
||||
if self.rxType == 'pressureHead':
|
||||
u = np.concatenate(U)
|
||||
@@ -17,7 +17,7 @@ class RichardsRx(Survey.BaseTimeRx):
|
||||
|
||||
return self.getP(mesh, timeMesh) * u
|
||||
|
||||
def projectFieldsDeriv(self, U, m, mapping, mesh, timeMesh):
|
||||
def evalDeriv(self, U, m, mapping, mesh, timeMesh):
|
||||
|
||||
P = self.getP(mesh, timeMesh)
|
||||
if self.rxType == 'pressureHead':
|
||||
@@ -57,13 +57,13 @@ class RichardsSurvey(Survey.BaseSurvey):
|
||||
Where P is a projection of the fields onto the data space.
|
||||
"""
|
||||
if u is None: u = self.prob.fields(m)
|
||||
return Utils.mkvc(self.projectFields(u, m))
|
||||
return Utils.mkvc(self.eval(u, m))
|
||||
|
||||
@Utils.requires('prob')
|
||||
def projectFields(self, U, m):
|
||||
def eval(self, U, m):
|
||||
Ds = range(len(self.rxList))
|
||||
for ii, rx in enumerate(self.rxList):
|
||||
Ds[ii] = rx.projectFields(U, m,
|
||||
Ds[ii] = rx.eval(U, m,
|
||||
self.prob.mapping,
|
||||
self.prob.mesh,
|
||||
self.prob.timeMesh)
|
||||
@@ -71,11 +71,11 @@ class RichardsSurvey(Survey.BaseSurvey):
|
||||
return np.concatenate(Ds)
|
||||
|
||||
@Utils.requires('prob')
|
||||
def projectFieldsDeriv(self, U, m):
|
||||
def evalDeriv(self, U, m):
|
||||
"""The Derivative with respect to the fields."""
|
||||
Ds = range(len(self.rxList))
|
||||
for ii, rx in enumerate(self.rxList):
|
||||
Ds[ii] = rx.projectFieldsDeriv(U, m,
|
||||
Ds[ii] = rx.evalDeriv(U, m,
|
||||
self.prob.mapping,
|
||||
self.prob.mesh,
|
||||
self.prob.timeMesh)
|
||||
@@ -251,7 +251,7 @@ class RichardsProblem(Problem.BaseTimeProblem):
|
||||
B = np.array(sp.vstack(Bs).todense())
|
||||
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
P = self.survey.evalDeriv(u, m)
|
||||
AinvB = Ainv * B
|
||||
z = np.zeros((self.mesh.nC, B.shape[1]))
|
||||
zAinvB = np.vstack((z, AinvB))
|
||||
@@ -277,7 +277,7 @@ class RichardsProblem(Problem.BaseTimeProblem):
|
||||
Adiaginv = self.Solver(Adiag, **self.solverOpts)
|
||||
JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1])
|
||||
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
P = self.survey.evalDeriv(u, m)
|
||||
return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC)
|
||||
|
||||
@Utils.timeIt
|
||||
@@ -285,7 +285,7 @@ class RichardsProblem(Problem.BaseTimeProblem):
|
||||
if u is None:
|
||||
u = self.field(m)
|
||||
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
P = self.survey.evalDeriv(u, m)
|
||||
PTv = P.T*v
|
||||
|
||||
# This is done via backward substitution.
|
||||
|
||||
@@ -66,8 +66,8 @@ class BaseInvProblem(object):
|
||||
self.curModel = m0
|
||||
|
||||
print """SimPEG.InvProblem is setting bfgsH0 to the inverse of the eval2Deriv.
|
||||
***Done using same solver as the problem***"""
|
||||
self.opt.bfgsH0 = self.prob.Solver(self.reg.eval2Deriv(self.curModel))
|
||||
***Done using same Solver and solverOpts as the problem***"""
|
||||
self.opt.bfgsH0 = self.prob.Solver(self.reg.eval2Deriv(self.curModel), **self.prob.solverOpts)
|
||||
|
||||
@property
|
||||
def warmstart(self):
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
from SimPEG import SolverLU as SimpegSolver, PropMaps, Utils, mkvc, sp, np
|
||||
from SimPEG.EM.FDEM.FDEM import BaseFDEMProblem
|
||||
from SurveyMT import Survey, Data
|
||||
from FieldsMT import BaseMTFields
|
||||
|
||||
|
||||
class BaseMTProblem(BaseFDEMProblem):
|
||||
"""
|
||||
Base class for all Natural source problems.
|
||||
"""
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
# Set the default pairs of the problem
|
||||
surveyPair = Survey
|
||||
dataPair = Data
|
||||
fieldsPair = BaseMTFields
|
||||
|
||||
# Set the solver
|
||||
Solver = SimpegSolver
|
||||
solverOpts = {}
|
||||
|
||||
verbose = False
|
||||
# Notes:
|
||||
# Use the forward and devs from BaseFDEMProblem
|
||||
# Might need to add more stuff here.
|
||||
|
||||
## NEED to clean up the Jvec and Jtvec to use Zero and Identities for None components.
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
Function to calculate the data sensitivities dD/dm times a vector.
|
||||
|
||||
:param numpy.ndarray m (nC, 1) - conductive model
|
||||
:param numpy.ndarray v (nC, 1) - random vector
|
||||
:param MTfields object (optional) - MT fields object, if not given it is calculated
|
||||
:rtype: MTdata object
|
||||
:return: Data sensitivities wrt m
|
||||
"""
|
||||
|
||||
# Calculate the fields
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
# Set current model
|
||||
self.curModel = m
|
||||
# Initiate the Jv object
|
||||
Jv = self.dataPair(self.survey)
|
||||
|
||||
# Loop all the frequenies
|
||||
for freq in self.survey.freqs:
|
||||
dA_du = self.getA(freq) #
|
||||
|
||||
dA_duI = self.Solver(dA_du, **self.solverOpts)
|
||||
|
||||
for src in self.survey.getSrcByFreq(freq):
|
||||
# We need fDeriv_m = df/du*du/dm + df/dm
|
||||
# Construct du/dm, it requires a solve
|
||||
# NOTE: need to account for the 2 polarizations in the derivatives.
|
||||
u_src = u[src,:]
|
||||
# dA_dm and dRHS_dm should be of size nE,2, so that we can multiply by dA_duI. The 2 columns are each of the polarizations.
|
||||
dA_dm = self.getADeriv_m(freq, u_src, v) # Size: nE,2 (u_px,u_py) in the columns.
|
||||
dRHS_dm = self.getRHSDeriv_m(freq, v) # Size: nE,2 (u_px,u_py) in the columns.
|
||||
if dRHS_dm is None:
|
||||
du_dm = dA_duI * ( -dA_dm )
|
||||
else:
|
||||
du_dm = dA_duI * ( -dA_dm + dRHS_dm )
|
||||
# Calculate the projection derivatives
|
||||
for rx in src.rxList:
|
||||
# Get the projection derivative
|
||||
# v should be of size 2*nE (for 2 polarizations)
|
||||
PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, u, t) # wrt u, we don't have have PDeriv wrt m
|
||||
Jv[src, rx] = PDeriv_u(mkvc(du_dm))
|
||||
dA_duI.clean()
|
||||
# Return the vectorized sensitivities
|
||||
return mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
"""
|
||||
Function to calculate the transpose of the data sensitivities (dD/dm)^T times a vector.
|
||||
|
||||
:param numpy.ndarray m (nC, 1) - conductive model
|
||||
:param numpy.ndarray v (nD, 1) - vector
|
||||
:param MTfields object u (optional) - MT fields object, if not given it is calculated
|
||||
:rtype: MTdata object
|
||||
:return: Data sensitivities wrt m
|
||||
"""
|
||||
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
self.curModel = m
|
||||
|
||||
# Ensure v is a data object.
|
||||
if not isinstance(v, self.dataPair):
|
||||
v = self.dataPair(self.survey, v)
|
||||
|
||||
Jtv = np.zeros(m.size)
|
||||
|
||||
for freq in self.survey.freqs:
|
||||
AT = self.getA(freq).T
|
||||
|
||||
ATinv = self.Solver(AT, **self.solverOpts)
|
||||
|
||||
for src in self.survey.getSrcByFreq(freq):
|
||||
ftype = self._fieldType + 'Solution'
|
||||
u_src = u[src, :]
|
||||
|
||||
for rx in src.rxList:
|
||||
# Get the adjoint evalDeriv
|
||||
# PTv needs to be nE,
|
||||
PTv = rx.evalDeriv(src, self.mesh, u, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m
|
||||
# Get the
|
||||
dA_duIT = ATinv * PTv
|
||||
dA_dmT = self.getADeriv_m(freq, u_src, mkvc(dA_duIT), adjoint=True)
|
||||
dRHS_dmT = self.getRHSDeriv_m(freq, mkvc(dA_duIT), adjoint=True)
|
||||
# Make du_dmT
|
||||
if dRHS_dmT is None:
|
||||
du_dmT = -dA_dmT
|
||||
else:
|
||||
du_dmT = -dA_dmT + dRHS_dmT
|
||||
# Select the correct component
|
||||
# du_dmT needs to be of size nC,
|
||||
real_or_imag = rx.projComp
|
||||
if real_or_imag == 'real':
|
||||
Jtv += du_dmT.real
|
||||
elif real_or_imag == 'imag':
|
||||
Jtv += -du_dmT.real
|
||||
else:
|
||||
raise Exception('Must be real or imag')
|
||||
# Clean the factorization, clear memory.
|
||||
ATinv.clean()
|
||||
return Jtv
|
||||
@@ -0,0 +1,351 @@
|
||||
from SimPEG import Survey, Utils, Problem, np, sp, mkvc
|
||||
from scipy.constants import mu_0
|
||||
import sys
|
||||
from numpy.lib import recfunctions as recFunc
|
||||
from SimPEG.EM.Utils import omega
|
||||
|
||||
##############
|
||||
### Fields ###
|
||||
##############
|
||||
class BaseMTFields(Problem.Fields):
|
||||
"""Field Storage for a MT survey."""
|
||||
knownFields = {}
|
||||
dtype = complex
|
||||
|
||||
|
||||
class Fields1D_e(BaseMTFields):
|
||||
"""
|
||||
Fields storage for the 1D MT solution.
|
||||
"""
|
||||
knownFields = {'e_1dSolution':'F'}
|
||||
aliasFields = {
|
||||
'e_1d' : ['e_1dSolution','F','_e'],
|
||||
'e_1dPrimary' : ['e_1dSolution','F','_ePrimary'],
|
||||
'e_1dSecondary' : ['e_1dSolution','F','_eSecondary'],
|
||||
'b_1d' : ['e_1dSolution','E','_b'],
|
||||
'b_1dPrimary' : ['e_1dSolution','E','_bPrimary'],
|
||||
'b_1dSecondary' : ['e_1dSolution','E','_bSecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
BaseMTFields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def _ePrimary(self, eSolution, srcList):
|
||||
ePrimary = np.zeros_like(eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.survey.prob)
|
||||
if ep is not None:
|
||||
ePrimary[:,i] = ep[:,-1]
|
||||
return ePrimary
|
||||
|
||||
def _eSecondary(self, eSolution, srcList):
|
||||
return eSolution
|
||||
|
||||
def _e(self, eSolution, srcList):
|
||||
return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList)
|
||||
|
||||
def _eDeriv_u(self, src, v, adjoint = False):
|
||||
return v
|
||||
|
||||
def _eDeriv_m(self, src, v, adjoint = False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
|
||||
def _bPrimary(self, eSolution, srcList):
|
||||
bPrimary = np.zeros([self.survey.mesh.nE,eSolution.shape[1]], dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.survey.prob)
|
||||
if bp is not None:
|
||||
bPrimary[:,i] += bp[:,-1]
|
||||
return bPrimary
|
||||
|
||||
def _bSecondary(self, eSolution, srcList):
|
||||
C = self.mesh.nodalGrad
|
||||
b = (C * eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||
# There is no magnetic source in the MT problem
|
||||
# S_m, _ = src.eval(self.survey.prob)
|
||||
# if S_m is not None:
|
||||
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||
return b
|
||||
|
||||
def _b(self, eSolution, srcList):
|
||||
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
||||
|
||||
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||
C = self.mesh.nodalGrad
|
||||
if adjoint:
|
||||
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||
|
||||
def _bSecondaryDeriv_m(self, src, v, adjoint = False):
|
||||
# Doesn't depend on m
|
||||
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||
# S_eDeriv = S_eDeriv(v)
|
||||
# if S_eDeriv is not None:
|
||||
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||
return None
|
||||
|
||||
def _bDeriv_u(self, src, v, adjoint=False):
|
||||
# Primary does not depend on u
|
||||
return self._bSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _bDeriv_m(self, src, v, adjoint=False):
|
||||
# Assuming the primary does not depend on the model
|
||||
return self._bSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
def _fDeriv_u(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt u.
|
||||
|
||||
:param MTsrc src: MT source
|
||||
:param numpy.ndarray v: random vector of f_sol.size
|
||||
This function stacks the fields derivatives appropriately
|
||||
|
||||
return a vector of size (nreEle+nrbEle)
|
||||
"""
|
||||
|
||||
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||
db_du = self._bDeriv_u(src, v, adjoint)
|
||||
# Return the stack
|
||||
# This doesn't work...
|
||||
return np.vstack((de_du,db_du))
|
||||
|
||||
def _fDeriv_m(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt m.
|
||||
|
||||
This function stacks the fields derivatives appropriately
|
||||
"""
|
||||
return None
|
||||
|
||||
class Fields3D_e(BaseMTFields):
|
||||
"""
|
||||
Fields storage for the 3D MT solution. Labels polarizations by px and py.
|
||||
|
||||
:param SimPEG object mesh: The solution mesh
|
||||
:param SimPEG object survey: A survey object
|
||||
"""
|
||||
# Define the known the alias fields
|
||||
# Assume that the solution of e on the E.
|
||||
## NOTE: Need to make this more general, to allow for other solutions formats.
|
||||
knownFields = {'e_pxSolution':'E','e_pySolution':'E'}
|
||||
aliasFields = {
|
||||
'e_px' : ['e_pxSolution','E','_e_px'],
|
||||
'e_pxPrimary' : ['e_pxSolution','E','_e_pxPrimary'],
|
||||
'e_pxSecondary' : ['e_pxSolution','E','_e_pxSecondary'],
|
||||
'e_py' : ['e_pySolution','E','_e_py'],
|
||||
'e_pyPrimary' : ['e_pySolution','E','_e_pyPrimary'],
|
||||
'e_pySecondary' : ['e_pySolution','E','_e_pySecondary'],
|
||||
'b_px' : ['e_pxSolution','F','_b_px'],
|
||||
'b_pxPrimary' : ['e_pxSolution','F','_b_pxPrimary'],
|
||||
'b_pxSecondary' : ['e_pxSolution','F','_b_pxSecondary'],
|
||||
'b_py' : ['e_pySolution','F','_b_py'],
|
||||
'b_pyPrimary' : ['e_pySolution','F','_b_pyPrimary'],
|
||||
'b_pySecondary' : ['e_pySolution','F','_b_pySecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
BaseMTFields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def _e_pxPrimary(self, e_pxSolution, srcList):
|
||||
e_pxPrimary = np.zeros_like(e_pxSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.survey.prob)
|
||||
if ep is not None:
|
||||
e_pxPrimary[:,i] = ep[:,0]
|
||||
return e_pxPrimary
|
||||
|
||||
def _e_pyPrimary(self, e_pySolution, srcList):
|
||||
e_pyPrimary = np.zeros_like(e_pySolution)
|
||||
for i, src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.survey.prob)
|
||||
if ep is not None:
|
||||
e_pyPrimary[:,i] = ep[:,1]
|
||||
return e_pyPrimary
|
||||
|
||||
def _e_pxSecondary(self, e_pxSolution, srcList):
|
||||
return e_pxSolution
|
||||
|
||||
def _e_pySecondary(self, e_pySolution, srcList):
|
||||
return e_pySolution
|
||||
|
||||
def _e_px(self, e_pxSolution, srcList):
|
||||
return self._e_pxPrimary(e_pxSolution,srcList) + self._e_pxSecondary(e_pxSolution,srcList)
|
||||
|
||||
def _e_py(self, e_pySolution, srcList):
|
||||
return self._e_pyPrimary(e_pySolution,srcList) + self._e_pySecondary(e_pySolution,srcList)
|
||||
|
||||
#NOTE: For e_p?Deriv_u,
|
||||
# v has to be u(2*nE) long for the not adjoint and nE long for adjoint.
|
||||
# Returns nE long for not adjoint and 2*nE long for adjoint
|
||||
def _e_pxDeriv_u(self, src, v, adjoint = False):
|
||||
'''
|
||||
Takes the derivative of e_px wrt u
|
||||
'''
|
||||
if adjoint:
|
||||
# adjoint: returns a 2*nE long vector with zero's for py
|
||||
return np.vstack((v,np.zeros_like(v)))
|
||||
# Not adjoint: return only the px part of the vector
|
||||
return v[:len(v)/2]
|
||||
|
||||
def _e_pyDeriv_u(self, src, v, adjoint = False):
|
||||
'''
|
||||
Takes the derivative of e_py wrt u
|
||||
'''
|
||||
if adjoint:
|
||||
# adjoint: returns a 2*nE long vector with zero's for px
|
||||
return np.vstack((np.zeros_like(v),v))
|
||||
# Not adjoint: return only the px part of the vector
|
||||
return v[len(v)/2::]
|
||||
|
||||
def _e_pxDeriv_m(self, src, v, adjoint = False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
def _e_pyDeriv_m(self, src, v, adjoint = False):
|
||||
# assuming primary does not depend on the model
|
||||
return None
|
||||
|
||||
def _b_pxPrimary(self, e_pxSolution, srcList):
|
||||
b_pxPrimary = np.zeros([self.survey.mesh.nF,e_pxSolution.shape[1]], dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.survey.prob)
|
||||
if bp is not None:
|
||||
b_pxPrimary[:,i] += bp[:,0]
|
||||
return b_pxPrimary
|
||||
|
||||
def _b_pyPrimary(self, e_pySolution, srcList):
|
||||
b_pyPrimary = np.zeros([self.survey.mesh.nF,e_pySolution.shape[1]], dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.survey.prob)
|
||||
if bp is not None:
|
||||
b_pyPrimary[:,i] += bp[:,1]
|
||||
return b_pyPrimary
|
||||
|
||||
def _b_pxSecondary(self, e_pxSolution, srcList):
|
||||
C = self.mesh.edgeCurl
|
||||
b = (C * e_pxSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||
# There is no magnetic source in the MT problem
|
||||
# S_m, _ = src.eval(self.survey.prob)
|
||||
# if S_m is not None:
|
||||
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||
return b
|
||||
|
||||
def _b_pySecondary(self, e_pySolution, srcList):
|
||||
C = self.mesh.edgeCurl
|
||||
b = (C * e_pySolution)
|
||||
for i, src in enumerate(srcList):
|
||||
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||
# There is no magnetic source in the MT problem
|
||||
# S_m, _ = src.eval(self.survey.prob)
|
||||
# if S_m is not None:
|
||||
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
|
||||
return b
|
||||
|
||||
def _b_px(self, eSolution, srcList):
|
||||
return self._b_pxPrimary(eSolution, srcList) + self._b_pxSecondary(eSolution, srcList)
|
||||
|
||||
def _b_py(self, eSolution, srcList):
|
||||
return self._b_pyPrimary(eSolution, srcList) + self._b_pySecondary(eSolution, srcList)
|
||||
|
||||
# NOTE: v needs to be length 2*nE to account for both polarizations
|
||||
def _b_pxSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||
# C = sp.kron(self.mesh.edgeCurl,[[1,0],[0,0]])
|
||||
C = sp.hstack((self.mesh.edgeCurl,Utils.spzeros(self.mesh.nF,self.mesh.nE))) # This works for adjoint = None
|
||||
if adjoint:
|
||||
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||
|
||||
def _b_pySecondaryDeriv_u(self, src, v, adjoint = False):
|
||||
# C = sp.kron(self.mesh.edgeCurl,[[0,0],[0,1]])
|
||||
C = sp.hstack((Utils.spzeros(self.mesh.nF,self.mesh.nE),self.mesh.edgeCurl)) # This works for adjoint = None
|
||||
if adjoint:
|
||||
return - 1./(1j*omega(src.freq)) * (C.T * v)
|
||||
return - 1./(1j*omega(src.freq)) * (C * v)
|
||||
|
||||
def _b_pxSecondaryDeriv_m(self, src, v, adjoint = False):
|
||||
# Doesn't depend on m
|
||||
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||
# S_eDeriv = S_eDeriv(v)
|
||||
# if S_eDeriv is not None:
|
||||
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||
return None
|
||||
|
||||
def _b_pySecondaryDeriv_m(self, src, v, adjoint = False):
|
||||
# Doesn't depend on m
|
||||
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
|
||||
# S_eDeriv = S_eDeriv(v)
|
||||
# if S_eDeriv is not None:
|
||||
# return 1./(1j * omega(src.freq)) * S_eDeriv
|
||||
return None
|
||||
|
||||
def _b_pxDeriv_u(self, src, v, adjoint=False):
|
||||
# Primary does not depend on u
|
||||
return self._b_pxSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _b_pyDeriv_u(self, src, v, adjoint=False):
|
||||
# Primary does not depend on u
|
||||
return self._b_pySecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _b_pxDeriv_m(self, src, v, adjoint=False):
|
||||
# Assuming the primary does not depend on the model
|
||||
return self._b_pxSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
def _b_pyDeriv_m(self, src, v, adjoint=False):
|
||||
# Assuming the primary does not depend on the model
|
||||
return self._b_pySecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
def _f_pxDeriv_u(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt u.
|
||||
|
||||
:param MTsrc src: MT source
|
||||
:param numpy.ndarray v: random vector of f_sol.size
|
||||
This function stacks the fields derivatives appropriately
|
||||
|
||||
return a vector of size (nreEle+nrbEle)
|
||||
"""
|
||||
|
||||
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||
db_du = self._b_pxDeriv_u(src, v, adjoint)
|
||||
# Return the stack
|
||||
# This doesn't work...
|
||||
return np.vstack((de_du,db_du))
|
||||
|
||||
def _f_pyDeriv_u(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt u.
|
||||
|
||||
:param MTsrc src: MT source
|
||||
:param numpy.ndarray v: random vector of f_sol.size
|
||||
This function stacks the fields derivatives appropriately
|
||||
|
||||
return a vector of size (nreEle+nrbEle)
|
||||
"""
|
||||
|
||||
de_du = v #Utils.spdiag(np.ones((self.nF,)))
|
||||
db_du = self._b_pyDeriv_u(src, v, adjoint)
|
||||
# Return the stack
|
||||
# This doesn't work...
|
||||
return np.vstack((de_du,db_du))
|
||||
|
||||
def _f_pxDeriv_m(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt m.
|
||||
|
||||
This function stacks the fields derivatives appropriately
|
||||
"""
|
||||
# The fields have no dependance to the model.
|
||||
return None
|
||||
|
||||
def _f_pyDeriv_m(self, src, v, adjoint=False):
|
||||
"""
|
||||
Derivative of the fields object wrt m.
|
||||
|
||||
This function stacks the fields derivatives appropriately
|
||||
"""
|
||||
# The fields have no dependance to the model.
|
||||
return None
|
||||
@@ -0,0 +1,291 @@
|
||||
from SimPEG.EM.Utils import omega
|
||||
from SimPEG import mkvc
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.MT.BaseMT import BaseMTProblem
|
||||
from SimPEG.MT.SurveyMT import Survey, Data
|
||||
from SimPEG.MT.FieldsMT import Fields1D_e
|
||||
from SimPEG.MT.Utils.MT1Danalytic import getEHfields
|
||||
import numpy as np
|
||||
import multiprocessing, sys, time
|
||||
|
||||
|
||||
class eForm_psField(BaseMTProblem):
|
||||
"""
|
||||
A MT problem soving a e formulation and primary/secondary fields decomposion.
|
||||
|
||||
By eliminating the magnetic flux density using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
|
||||
|
||||
|
||||
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||
|
||||
.. math ::
|
||||
\\left(\mathbf{C}^T \mathbf{M^e_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^f_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^f_{\delta \sigma}} \mathbf{e}_{p}
|
||||
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
|
||||
|
||||
The primary field is estimated from a background model (commonly half space ).
|
||||
|
||||
|
||||
"""
|
||||
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||
_fieldType = 'e_1d'
|
||||
_eqLocs = 'EF'
|
||||
_sigmaPrimary = None
|
||||
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||
self.fieldsPair = Fields1D_e
|
||||
# self._sigmaPrimary = sigmaPrimary
|
||||
@property
|
||||
def MeMui(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_MeMui', None) is None:
|
||||
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
|
||||
return self._MeMui
|
||||
|
||||
@property
|
||||
def MfSigma(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_MfSigma', None) is None:
|
||||
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
|
||||
return self._MfSigma
|
||||
|
||||
@property
|
||||
def sigmaPrimary(self):
|
||||
"""
|
||||
A background model, use for the calculation of the primary fields.
|
||||
|
||||
"""
|
||||
return self._sigmaPrimary
|
||||
|
||||
@sigmaPrimary.setter
|
||||
def sigmaPrimary(self, val):
|
||||
# Note: TODO add logic for val, make sure it is the correct size.
|
||||
self._sigmaPrimary = val
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
Function to get the A matrix.
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
# Note: need to use the code above since in the 1D problem I want
|
||||
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
|
||||
# Possible that _fieldType and _eqLocs can fix this
|
||||
MeMui = self.MeMui
|
||||
MfSigma = self.MfSigma
|
||||
C = self.mesh.nodalGrad
|
||||
# Make A
|
||||
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
|
||||
# Either return full or only the inner part of A
|
||||
return A
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
"""
|
||||
The derivative of A wrt sigma
|
||||
"""
|
||||
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
MeMui = self.MeMui
|
||||
#
|
||||
u_src = u['e_1dSolution']
|
||||
dMfSigma_dm = self.mesh.getFaceInnerProductDeriv(self.curModel.sigma)(u_src) * self.curModel.sigmaDeriv
|
||||
if adjoint:
|
||||
return 1j * omega(freq) * ( dMfSigma_dm.T * v )
|
||||
# Note: output has to be nN/nF, not nC/nE.
|
||||
# v should be nC
|
||||
return 1j * omega(freq) * ( dMfSigma_dm * v )
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
Function to return the right hand side for the system.
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nF, 1), numpy.ndarray (nF, 1)
|
||||
:return: RHS for 1 polarizations, primary fields
|
||||
"""
|
||||
|
||||
# Get sources for the frequncy(polarizations)
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
S_e = Src.S_e(self)
|
||||
return -1j * omega(freq) * S_e
|
||||
|
||||
def getRHSDeriv_m(self, freq, v, adjoint=False):
|
||||
"""
|
||||
The derivative of the RHS wrt sigma
|
||||
"""
|
||||
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
|
||||
return -1j * omega(freq) * S_eDeriv
|
||||
|
||||
def fields(self, m):
|
||||
'''
|
||||
Function to calculate all the fields for the model m.
|
||||
|
||||
:param np.ndarray (nC,) m: Conductivity model
|
||||
'''
|
||||
# Set the current model
|
||||
self.curModel = m
|
||||
|
||||
F = Fields1D_e(self.mesh, self.survey)
|
||||
for freq in self.survey.freqs:
|
||||
if self.verbose:
|
||||
startTime = time.time()
|
||||
print 'Starting work for {:.3e}'.format(freq)
|
||||
sys.stdout.flush()
|
||||
A = self.getA(freq)
|
||||
rhs = self.getRHS(freq)
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
e_s = Ainv * rhs
|
||||
|
||||
# Store the fields
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
# NOTE: only store the e_solution(secondary), all other components calculated in the fields object
|
||||
F[Src, 'e_1dSolution'] = e_s[:,-1] # Only storing the yx polarization as 1d
|
||||
|
||||
# Note curl e = -iwb so b = -curl e /iw
|
||||
# b = -( self.mesh.nodalGrad * e )/( 1j*omega(freq) )
|
||||
# F[Src, 'b_1d'] = b[:,1]
|
||||
if self.verbose:
|
||||
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||
sys.stdout.flush()
|
||||
return F
|
||||
|
||||
# Note this is not fully functional.
|
||||
# Missing:
|
||||
# Fields class corresponding to the fields
|
||||
# Update Jvec and Jtvec to include all the derivatives components
|
||||
# Other things ...
|
||||
class eForm_TotalField(BaseMTProblem):
|
||||
"""
|
||||
A MT problem solving a e formulation and a Total bondary domain decompostion.
|
||||
|
||||
Solves the equation:
|
||||
|
||||
Math:
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||
_fieldType = 'e'
|
||||
_eqLocs = 'EF'
|
||||
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||
@property
|
||||
def MeMui(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_MeMui', None) is None:
|
||||
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
|
||||
return self._MeMui
|
||||
|
||||
@property
|
||||
def MfSigma(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_MfSigma', None) is None:
|
||||
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
|
||||
return self._MfSigma
|
||||
|
||||
def getA(self, freq, full=False):
|
||||
"""
|
||||
Function to get the A matrix.
|
||||
|
||||
:param float freq: Frequency
|
||||
:param logic full: Return full A or the inner part
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMui = self.MeMui
|
||||
MfSigma = self.MfSigma
|
||||
# Note: need to use the code above since in the 1D problem I want
|
||||
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
|
||||
# Possible that _fieldType and _eqLocs can fix this
|
||||
# MeMui = self.MfMui
|
||||
# MfSigma = self.MfSigma
|
||||
C = self.mesh.nodalGrad
|
||||
# Make A
|
||||
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
|
||||
# Either return full or only the inner part of A
|
||||
if full:
|
||||
return A
|
||||
else:
|
||||
return A[1:-1,1:-1]
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
raise NotImplementedError('getADeriv is not implemented')
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
Function to return the right hand side for the system.
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
|
||||
:return: RHS for both polarizations, primary fields
|
||||
"""
|
||||
# Get sources for the frequency
|
||||
# NOTE: Need to use the source information, doesn't really apply in 1D
|
||||
src = self.survey.getSrcByFreq(freq)
|
||||
# Get the full A
|
||||
A = self.getA(freq,full=True)
|
||||
# Define the outer part of the solution matrix
|
||||
Aio = A[1:-1,[0,-1]]
|
||||
Ed, Eu, Hd, Hu = getEHfields(self.mesh,self.curModel.sigma,freq,self.mesh.vectorNx)
|
||||
Etot = (Ed + Eu)
|
||||
sourceAmp = 1.0
|
||||
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
|
||||
## Note: The analytic solution is derived with e^iwt
|
||||
eBC = np.r_[Etot[0],Etot[-1]]
|
||||
# The right hand side
|
||||
|
||||
return -Aio*eBC, eBC
|
||||
|
||||
def getRHSderiv_m(self, freq, backSigma, u, v, adjoint=False):
|
||||
raise NotImplementedError('getRHSDeriv not implemented yet')
|
||||
return None
|
||||
|
||||
def fields(self, m):
|
||||
'''
|
||||
Function to calculate all the fields for the model m.
|
||||
|
||||
:param np.ndarray (nC,) m: Conductivity model
|
||||
:param np.ndarray (nC,) m_back: Background conductivity model
|
||||
'''
|
||||
self.curModel = m
|
||||
# RHS, CalcFields = self.getRHS(freq,m_back), self.calcFields
|
||||
|
||||
F = Fields1D_e(self.mesh, self.survey)
|
||||
for freq in self.survey.freqs:
|
||||
if self.verbose:
|
||||
startTime = time.time()
|
||||
print 'Starting work for {:.3e}'.format(freq)
|
||||
sys.stdout.flush()
|
||||
A = self.getA(freq)
|
||||
rhs, e_o = self.getRHS(freq)
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
e_i = Ainv * rhs
|
||||
e = mkvc(np.r_[e_o[0], e_i, e_o[1]],2)
|
||||
# Store the fields
|
||||
Src = self.survey.getSrcByFreq(freq)
|
||||
# NOTE: only store e fields
|
||||
F[Src, 'e_1dSolution'] = e[:,0]
|
||||
if self.verbose:
|
||||
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||
sys.stdout.flush()
|
||||
return F
|
||||
@@ -0,0 +1 @@
|
||||
from Probs import eForm_TotalField, eForm_psField
|
||||
@@ -0,0 +1 @@
|
||||
pass
|
||||
@@ -0,0 +1,138 @@
|
||||
from SimPEG import Survey, Problem, Utils, Models, np, sp, mkvc, SolverLU as SimpegSolver
|
||||
from SimPEG.EM.Utils import omega
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.MT.BaseMT import BaseMTProblem
|
||||
from SimPEG.MT.SurveyMT import Survey, Data
|
||||
from SimPEG.MT.FieldsMT import Fields3D_e
|
||||
import multiprocessing, sys, time
|
||||
|
||||
|
||||
|
||||
class eForm_ps(BaseMTProblem):
|
||||
"""
|
||||
A MT problem solving a e formulation and a primary/secondary fields decompostion.
|
||||
|
||||
By eliminating the magnetic flux density using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
|
||||
|
||||
|
||||
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
|
||||
|
||||
.. math ::
|
||||
\\left(\mathbf{C}^T \mathbf{M^f_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^e_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^e_{\delta \sigma}} \mathbf{e}_{p}
|
||||
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
|
||||
|
||||
The primary field is estimated from a background model (commonly as a 1D model).
|
||||
|
||||
"""
|
||||
|
||||
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
|
||||
_fieldType = 'e'
|
||||
_eqLocs = 'FE'
|
||||
fieldsPair = Fields3D_e
|
||||
_sigmaPrimary = None
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseMTProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
@property
|
||||
def sigmaPrimary(self):
|
||||
"""
|
||||
A background model, use for the calculation of the primary fields.
|
||||
|
||||
"""
|
||||
return self._sigmaPrimary
|
||||
@sigmaPrimary.setter
|
||||
def sigmaPrimary(self, val):
|
||||
# Note: TODO add logic for val, make sure it is the correct size.
|
||||
self._sigmaPrimary = val
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
Function to get the A system.
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
Mmui = self.MfMui
|
||||
Msig = self.MeSigma
|
||||
C = self.mesh.edgeCurl
|
||||
|
||||
return C.T*Mmui*C + 1j*omega(freq)*Msig
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
"""
|
||||
Calculate the derivative of A wrt m.
|
||||
|
||||
"""
|
||||
|
||||
# This considers both polarizations and returns a nE,2 matrix for each polarization
|
||||
if adjoint:
|
||||
dMe_dsigV = sp.hstack(( self.MeSigmaDeriv( u['e_pxSolution'] ).T, self.MeSigmaDeriv(u['e_pySolution'] ).T ))*v
|
||||
else:
|
||||
# Need a nE,2 matrix to be returned
|
||||
dMe_dsigV = np.hstack(( mkvc(self.MeSigmaDeriv( u['e_pxSolution'] )*v,2), mkvc( self.MeSigmaDeriv(u['e_pySolution'] )*v,2) ))
|
||||
return 1j * omega(freq) * dMe_dsigV
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
Function to return the right hand side for the system.
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
|
||||
:return: RHS for both polarizations, primary fields
|
||||
"""
|
||||
|
||||
# Get sources for the frequncy(polarizations)
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
S_e = Src.S_e(self)
|
||||
return -1j * omega(freq) * S_e
|
||||
|
||||
def getRHSDeriv_m(self, freq, v, adjoint=False):
|
||||
"""
|
||||
The derivative of the RHS with respect to sigma
|
||||
"""
|
||||
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
|
||||
return -1j * omega(freq) * S_eDeriv
|
||||
|
||||
def fields(self, m):
|
||||
'''
|
||||
Function to calculate all the fields for the model m.
|
||||
|
||||
:param np.ndarray (nC,) m: Conductivity model
|
||||
'''
|
||||
# Set the current model
|
||||
self.curModel = m
|
||||
|
||||
F = Fields3D_e(self.mesh, self.survey)
|
||||
for freq in self.survey.freqs:
|
||||
if self.verbose:
|
||||
startTime = time.time()
|
||||
print 'Starting work for {:.3e}'.format(freq)
|
||||
sys.stdout.flush()
|
||||
A = self.getA(freq)
|
||||
rhs = self.getRHS(freq)
|
||||
# Solve the system
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
e_s = Ainv * rhs
|
||||
|
||||
# Store the fields
|
||||
Src = self.survey.getSrcByFreq(freq)[0]
|
||||
# Store the fieldss
|
||||
F[Src, 'e_pxSolution'] = e_s[:,0]
|
||||
F[Src, 'e_pySolution'] = e_s[:,1]
|
||||
# Note curl e = -iwb so b = -curl/iw
|
||||
|
||||
if self.verbose:
|
||||
print 'Ran for {:f} seconds'.format(time.time()-startTime)
|
||||
sys.stdout.flush()
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from Probs import eForm_ps
|
||||
@@ -0,0 +1,206 @@
|
||||
from SimPEG import Utils, Problem, Maps, np, sp, mkvc
|
||||
from SimPEG.EM.FDEM.SrcFDEM import BaseSrc as FDEMBaseSrc
|
||||
from SimPEG.EM.Utils import omega
|
||||
from scipy.constants import mu_0
|
||||
from numpy.lib import recfunctions as recFunc
|
||||
from Utils.sourceUtils import homo1DModelSource
|
||||
from Utils import rec2ndarr
|
||||
import sys
|
||||
|
||||
#################
|
||||
### Sources ###
|
||||
#################
|
||||
|
||||
class BaseMTSrc(FDEMBaseSrc):
|
||||
'''
|
||||
Sources for the MT problem.
|
||||
Use the SimPEG BaseSrc, since the source fields share properties with the transmitters.
|
||||
|
||||
:param float freq: The frequency of the source
|
||||
:param list rxList: A list of receivers associated with the source
|
||||
'''
|
||||
|
||||
freq = None #: Frequency (float)
|
||||
|
||||
|
||||
def __init__(self, rxList, freq):
|
||||
|
||||
self.freq = float(freq)
|
||||
FDEMBaseSrc.__init__(self, rxList)
|
||||
|
||||
# 1D sources
|
||||
class polxy_1DhomotD(BaseMTSrc):
|
||||
"""
|
||||
MT source for both polarizations (x and y) for the total Domain.
|
||||
|
||||
It calculates fields calculated based on conditions on the boundary of the domain.
|
||||
"""
|
||||
def __init__(self, rxList, freq):
|
||||
BaseMTSrc.__init__(self, rxList, freq)
|
||||
|
||||
|
||||
# TODO: need to add the primary fields calc and source terms into the problem.
|
||||
|
||||
# Need to implement such that it works for all dims.
|
||||
class polxy_1Dprimary(BaseMTSrc):
|
||||
"""
|
||||
MT source for both polarizations (x and y) given a 1D primary models.
|
||||
It assigns fields calculated from the 1D model as fields in the full space of the problem.
|
||||
"""
|
||||
def __init__(self, rxList, freq):
|
||||
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
|
||||
self.sigma1d = None
|
||||
BaseMTSrc.__init__(self, rxList, freq)
|
||||
# Hidden property of the ePrimary
|
||||
self._ePrimary = None
|
||||
|
||||
def ePrimary(self,problem):
|
||||
# Get primary fields for both polarizations
|
||||
if self.sigma1d is None:
|
||||
# Set the sigma1d as the 1st column in the background model
|
||||
if len(problem._sigmaPrimary) == problem.mesh.nC:
|
||||
if problem.mesh.dim == 1:
|
||||
self.sigma1d = problem.mesh.r(problem._sigmaPrimary,'CC','CC','M')[:]
|
||||
elif problem.mesh.dim == 3:
|
||||
self.sigma1d = problem.mesh.r(problem._sigmaPrimary,'CC','CC','M')[0,0,:]
|
||||
# Or as the 1D model that matches the vertical cell number
|
||||
elif len(problem._sigmaPrimary) == problem.mesh.nCz:
|
||||
self.sigma1d = problem._sigmaPrimary
|
||||
|
||||
if self._ePrimary is None:
|
||||
self._ePrimary = homo1DModelSource(problem.mesh,self.freq,self.sigma1d)
|
||||
return self._ePrimary
|
||||
|
||||
def bPrimary(self,problem):
|
||||
# Project ePrimary to bPrimary
|
||||
# Satisfies the primary(background) field conditions
|
||||
if problem.mesh.dim == 1:
|
||||
C = problem.mesh.nodalGrad
|
||||
elif problem.mesh.dim == 3:
|
||||
C = problem.mesh.edgeCurl
|
||||
bBG_bp = (- C * self.ePrimary(problem) )*(1/( 1j*omega(self.freq) ))
|
||||
return bBG_bp
|
||||
|
||||
def S_e(self,problem):
|
||||
"""
|
||||
Get the electrical field source
|
||||
"""
|
||||
e_p = self.ePrimary(problem)
|
||||
Map_sigma_p = Maps.Vertical1DMap(problem.mesh)
|
||||
sigma_p = Map_sigma_p._transform(self.sigma1d)
|
||||
# Make mass matrix
|
||||
# Note: M(sig) - M(sig_p) = M(sig - sig_p)
|
||||
# Need to deal with the edge/face discrepencies between 1d/2d/3d
|
||||
if problem.mesh.dim == 1:
|
||||
Mesigma = problem.mesh.getFaceInnerProduct(problem.curModel.sigma)
|
||||
Mesigma_p = problem.mesh.getFaceInnerProduct(sigma_p)
|
||||
if problem.mesh.dim == 2:
|
||||
pass
|
||||
if problem.mesh.dim == 3:
|
||||
Mesigma = problem.MeSigma
|
||||
Mesigma_p = problem.mesh.getEdgeInnerProduct(sigma_p)
|
||||
return (Mesigma - Mesigma_p) * e_p
|
||||
|
||||
def S_eDeriv_m(self, problem, v, adjoint = False):
|
||||
'''
|
||||
Get the derivative of S_e wrt to sigma (m)
|
||||
'''
|
||||
# Need to deal with
|
||||
if problem.mesh.dim == 1:
|
||||
# Need to use the faceInnerProduct
|
||||
MsigmaDeriv = problem.mesh.getFaceInnerProductDeriv(problem.curModel.sigma)(self.ePrimary(problem)[:,1]) * problem.curModel.sigmaDeriv
|
||||
# MsigmaDeriv = ( MsigmaDeriv * MsigmaDeriv.T)**2
|
||||
if problem.mesh.dim == 2:
|
||||
pass
|
||||
if problem.mesh.dim == 3:
|
||||
# Need to take the derivative of both u_px and u_py
|
||||
ePri = self.ePrimary(problem)
|
||||
# MsigmaDeriv = problem.MeSigmaDeriv(ePri[:,0]) + problem.MeSigmaDeriv(ePri[:,1])
|
||||
# MsigmaDeriv = problem.MeSigmaDeriv(np.sum(ePri,axis=1))
|
||||
if adjoint:
|
||||
return sp.hstack(( problem.MeSigmaDeriv(ePri[:,0]).T, problem.MeSigmaDeriv(ePri[:,1]).T ))*v
|
||||
else:
|
||||
return np.hstack(( mkvc(problem.MeSigmaDeriv(ePri[:,0]) * v,2), mkvc(problem.MeSigmaDeriv(ePri[:,1])*v,2) ))
|
||||
if adjoint:
|
||||
#
|
||||
return MsigmaDeriv.T * v
|
||||
else:
|
||||
# v should be nC size
|
||||
return MsigmaDeriv * v
|
||||
|
||||
class polxy_3Dprimary(BaseMTSrc):
|
||||
"""
|
||||
MT source for both polarizations (x and y) given a 3D primary model. It assigns fields calculated from the 1D model
|
||||
as fields in the full space of the problem.
|
||||
"""
|
||||
def __init__(self, rxList, freq):
|
||||
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
|
||||
self.sigmaPrimary = None
|
||||
BaseMTSrc.__init__(self, rxList, freq)
|
||||
# Hidden property of the ePrimary
|
||||
self._ePrimary = None
|
||||
|
||||
def ePrimary(self,problem):
|
||||
# Get primary fields for both polarizations
|
||||
self.sigmaPrimary = problem._sigmaPrimary
|
||||
|
||||
if self._ePrimary is None:
|
||||
self._ePrimary = homo3DModelSource(problem.mesh,self.sigmaPrimary,self.freq)
|
||||
return self._ePrimary
|
||||
|
||||
def bPrimary(self,problem):
|
||||
# Project ePrimary to bPrimary
|
||||
# Satisfies the primary(background) field conditions
|
||||
if problem.mesh.dim == 1:
|
||||
C = problem.mesh.nodalGrad
|
||||
elif problem.mesh.dim == 3:
|
||||
C = problem.mesh.edgeCurl
|
||||
bBG_bp = (- C * self.ePrimary(problem) )*(1/( 1j*omega(self.freq) ))
|
||||
return bBG_bp
|
||||
|
||||
def S_e(self,problem):
|
||||
"""
|
||||
Get the electrical field source
|
||||
"""
|
||||
e_p = self.ePrimary(problem)
|
||||
Map_sigma_p = Maps.Vertical1DMap(problem.mesh)
|
||||
sigma_p = Map_sigma_p._transform(self.sigma1d)
|
||||
# Make mass matrix
|
||||
# Note: M(sig) - M(sig_p) = M(sig - sig_p)
|
||||
# Need to deal with the edge/face discrepencies between 1d/2d/3d
|
||||
if problem.mesh.dim == 1:
|
||||
Mesigma = problem.mesh.getFaceInnerProduct(problem.curModel.sigma)
|
||||
Mesigma_p = problem.mesh.getFaceInnerProduct(sigma_p)
|
||||
if problem.mesh.dim == 2:
|
||||
pass
|
||||
if problem.mesh.dim == 3:
|
||||
Mesigma = problem.MeSigma
|
||||
Mesigma_p = problem.mesh.getEdgeInnerProduct(sigma_p)
|
||||
return (Mesigma - Mesigma_p) * e_p
|
||||
|
||||
def S_eDeriv_m(self, problem, v, adjoint = False):
|
||||
'''
|
||||
Get the derivative of S_e wrt to sigma (m)
|
||||
'''
|
||||
# Need to deal with
|
||||
if problem.mesh.dim == 1:
|
||||
# Need to use the faceInnerProduct
|
||||
MsigmaDeriv = problem.mesh.getFaceInnerProductDeriv(problem.curModel.sigma)(self.ePrimary(problem)[:,1]) * problem.curModel.sigmaDeriv
|
||||
# MsigmaDeriv = ( MsigmaDeriv * MsigmaDeriv.T)**2
|
||||
if problem.mesh.dim == 2:
|
||||
pass
|
||||
if problem.mesh.dim == 3:
|
||||
# Need to take the derivative of both u_px and u_py
|
||||
ePri = self.ePrimary(problem)
|
||||
# MsigmaDeriv = problem.MeSigmaDeriv(ePri[:,0]) + problem.MeSigmaDeriv(ePri[:,1])
|
||||
# MsigmaDeriv = problem.MeSigmaDeriv(np.sum(ePri,axis=1))
|
||||
if adjoint:
|
||||
return sp.hstack(( problem.MeSigmaDeriv(ePri[:,0]).T, problem.MeSigmaDeriv(ePri[:,1]).T ))*v
|
||||
else:
|
||||
return np.hstack(( mkvc(problem.MeSigmaDeriv(ePri[:,0]) * v,2), mkvc(problem.MeSigmaDeriv(ePri[:,1])*v,2) ))
|
||||
if adjoint:
|
||||
#
|
||||
return MsigmaDeriv.T * v
|
||||
else:
|
||||
# v should be nC size
|
||||
return MsigmaDeriv * v
|
||||
@@ -0,0 +1,562 @@
|
||||
from SimPEG import Survey as SimPEGsurvey, Utils, Problem, Maps, np, sp, mkvc
|
||||
from SimPEG.EM.FDEM.SrcFDEM import BaseSrc as FDEMBaseSrc
|
||||
from SimPEG.EM.Utils import omega
|
||||
from scipy.constants import mu_0
|
||||
from numpy.lib import recfunctions as recFunc
|
||||
from Utils import rec2ndarr
|
||||
import SrcMT
|
||||
import sys
|
||||
|
||||
#################
|
||||
### Receivers ###
|
||||
#################
|
||||
class Rx(SimPEGsurvey.BaseRx):
|
||||
"""
|
||||
Class that defines natural source receivers.
|
||||
|
||||
See knownRxTypes for types of allowed receivers.
|
||||
|
||||
:param ndArray locs: Locations of the receivers
|
||||
:param str rxType: The type of receiver
|
||||
|
||||
"""
|
||||
|
||||
knownRxTypes = {
|
||||
# 3D impedance
|
||||
'zxxr':['Z3D', 'real'],
|
||||
'zxyr':['Z3D', 'real'],
|
||||
'zyxr':['Z3D', 'real'],
|
||||
'zyyr':['Z3D', 'real'],
|
||||
'zxxi':['Z3D', 'imag'],
|
||||
'zxyi':['Z3D', 'imag'],
|
||||
'zyxi':['Z3D', 'imag'],
|
||||
'zyyi':['Z3D', 'imag'],
|
||||
# 2D impedance
|
||||
# TODO:
|
||||
# 1D impedance
|
||||
'z1dr':['Z1D', 'real'],
|
||||
'z1di':['Z1D', 'imag'],
|
||||
# Tipper
|
||||
'tzxr':['T3D','real'],
|
||||
'tzxi':['T3D','imag'],
|
||||
'tzyr':['T3D','real'],
|
||||
'tzyi':['T3D','imag']
|
||||
}
|
||||
# TODO: Have locs as single or double coordinates for both or numerator and denominator separately, respectively.
|
||||
def __init__(self, locs, rxType):
|
||||
SimPEGsurvey.BaseRx.__init__(self, locs, rxType)
|
||||
|
||||
@property
|
||||
def projType(self):
|
||||
"""
|
||||
Receiver type for projection.
|
||||
|
||||
"""
|
||||
return self.knownRxTypes[self.rxType][0]
|
||||
|
||||
@property
|
||||
def projComp(self):
|
||||
"""Component projection (real/imag)"""
|
||||
return self.knownRxTypes[self.rxType][1]
|
||||
|
||||
def eval(self, src, mesh, f):
|
||||
'''
|
||||
Project the fields to natural source data.
|
||||
|
||||
:param SrcMT src: The source of the fields to project
|
||||
:param SimPEG.Mesh mesh:
|
||||
:param FieldsMT f: Natural source fields object to project
|
||||
'''
|
||||
|
||||
## NOTE: Assumes that e is on t
|
||||
if self.projType is 'Z1D':
|
||||
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||
ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||
bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||
# Note: Has a minus sign in front, to comply with quadrant calculations.
|
||||
# Can be derived from zyx case for the 3D case.
|
||||
f_part_complex = -ex/bx
|
||||
# elif self.projType is 'Z2D':
|
||||
elif self.projType is 'Z3D':
|
||||
## NOTE: Assumes that e is on edges and b on the faces. Need to generalize that or use a prop of fields to determine that.
|
||||
if self.locs.ndim == 3:
|
||||
eFLocs = self.locs[:,:,0]
|
||||
bFLocs = self.locs[:,:,1]
|
||||
else:
|
||||
eFLocs = self.locs
|
||||
bFLocs = self.locs
|
||||
# Get the projection
|
||||
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||
# Get the fields at location
|
||||
# px: x-polaration and py: y-polaration.
|
||||
ex_px = Pex*f[src,'e_px']
|
||||
ey_px = Pey*f[src,'e_px']
|
||||
ex_py = Pex*f[src,'e_py']
|
||||
ey_py = Pey*f[src,'e_py']
|
||||
hx_px = Pbx*f[src,'b_px']/mu_0
|
||||
hy_px = Pby*f[src,'b_px']/mu_0
|
||||
hx_py = Pbx*f[src,'b_py']/mu_0
|
||||
hy_py = Pby*f[src,'b_py']/mu_0
|
||||
# Make the complex data
|
||||
if 'zxx' in self.rxType:
|
||||
f_part_complex = ( ex_px*hy_py - ex_py*hy_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||
elif 'zxy' in self.rxType:
|
||||
f_part_complex = (-ex_px*hx_py + ex_py*hx_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||
elif 'zyx' in self.rxType:
|
||||
f_part_complex = ( ey_px*hy_py - ey_py*hy_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||
elif 'zyy' in self.rxType:
|
||||
f_part_complex = (-ey_px*hx_py + ey_py*hx_px)/(hx_px*hy_py - hx_py*hy_px)
|
||||
elif self.projType is 'T3D':
|
||||
if self.locs.ndim == 3:
|
||||
horLoc = self.locs[:,:,0]
|
||||
vertLoc = self.locs[:,:,1]
|
||||
else:
|
||||
horLoc = self.locs
|
||||
vertLoc = self.locs
|
||||
Pbx = mesh.getInterpolationMat(horLoc,'Fx')
|
||||
Pby = mesh.getInterpolationMat(horLoc,'Fy')
|
||||
Pbz = mesh.getInterpolationMat(vertLoc,'Fz')
|
||||
bx_px = Pbx*f[src,'b_px']
|
||||
by_px = Pby*f[src,'b_px']
|
||||
bz_px = Pbz*f[src,'b_px']
|
||||
bx_py = Pbx*f[src,'b_py']
|
||||
by_py = Pby*f[src,'b_py']
|
||||
bz_py = Pbz*f[src,'b_py']
|
||||
if 'tzx' in self.rxType:
|
||||
f_part_complex = (- by_px*bz_py + by_py*bz_px)/(bx_px*by_py - bx_py*by_px)
|
||||
if 'tzy' in self.rxType:
|
||||
f_part_complex = ( bx_px*bz_py - bx_py*bz_px)/(bx_px*by_py - bx_py*by_px)
|
||||
|
||||
else:
|
||||
NotImplementedError('Projection of {:s} receiver type is not implemented.'.format(self.rxType))
|
||||
# Get the real or imag component
|
||||
real_or_imag = self.projComp
|
||||
f_part = getattr(f_part_complex, real_or_imag)
|
||||
# print f_part
|
||||
return f_part
|
||||
|
||||
def evalDeriv(self, src, mesh, f, v, adjoint=False):
|
||||
"""
|
||||
The derivative of the projection wrt u
|
||||
|
||||
:param MTsrc src: MT source
|
||||
:param TensorMesh mesh: Mesh defining the topology of the problem
|
||||
:param MTfields f: MT fields object of the source
|
||||
:param numpy.ndarray v: Random vector of size
|
||||
"""
|
||||
|
||||
real_or_imag = self.projComp
|
||||
|
||||
if not adjoint:
|
||||
if self.projType is 'Z1D':
|
||||
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||
# ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||
# bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||
dP_de = -mkvc(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))*(Pex*v),2)
|
||||
dP_db = mkvc( Utils.sdiag(Pex*mkvc(f[src,'e_1d'],2))*(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)).T*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)))*(Pbx*f._bDeriv_u(src,v)/mu_0),2)
|
||||
PDeriv_complex = np.sum(np.hstack((dP_de,dP_db)),1)
|
||||
elif self.projType is 'Z2D':
|
||||
raise NotImplementedError('Has not been implement for 2D impedance tensor')
|
||||
elif self.projType is 'Z3D':
|
||||
if self.locs.ndim == 3:
|
||||
eFLocs = self.locs[:,:,0]
|
||||
bFLocs = self.locs[:,:,1]
|
||||
else:
|
||||
eFLocs = self.locs
|
||||
bFLocs = self.locs
|
||||
# Get the projection
|
||||
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||
# Get the fields at location
|
||||
# px: x-polaration and py: y-polaration.
|
||||
ex_px = Pex*f[src,'e_px']
|
||||
ey_px = Pey*f[src,'e_px']
|
||||
ex_py = Pex*f[src,'e_py']
|
||||
ey_py = Pey*f[src,'e_py']
|
||||
hx_px = Pbx*f[src,'b_px']/mu_0
|
||||
hy_px = Pby*f[src,'b_px']/mu_0
|
||||
hx_py = Pbx*f[src,'b_py']/mu_0
|
||||
hy_py = Pby*f[src,'b_py']/mu_0
|
||||
# Derivatives as lambda functions
|
||||
# The size of the diratives should be nD,nU
|
||||
ex_px_u = lambda vec: Pex*f._e_pxDeriv_u(src,vec)
|
||||
ey_px_u = lambda vec: Pey*f._e_pxDeriv_u(src,vec)
|
||||
ex_py_u = lambda vec: Pex*f._e_pyDeriv_u(src,vec)
|
||||
ey_py_u = lambda vec: Pey*f._e_pyDeriv_u(src,vec)
|
||||
# NOTE: Think b_p?Deriv_u should return a 2*nF size matrix
|
||||
hx_px_u = lambda vec: Pbx*f._b_pxDeriv_u(src,vec)/mu_0
|
||||
hy_px_u = lambda vec: Pby*f._b_pxDeriv_u(src,vec)/mu_0
|
||||
hx_py_u = lambda vec: Pbx*f._b_pyDeriv_u(src,vec)/mu_0
|
||||
hy_py_u = lambda vec: Pby*f._b_pyDeriv_u(src,vec)/mu_0
|
||||
# Update the input vector
|
||||
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||
# Define the components of the derivative
|
||||
Hd = sDiag(1./(sDiag(hx_px)*hy_py - sDiag(hx_py)*hy_px))
|
||||
Hd_uV = sDiag(hy_py)*hx_px_u(v) + sDiag(hx_px)*hy_py_u(v) - sDiag(hx_py)*hy_px_u(v) - sDiag(hy_px)*hx_py_u(v)
|
||||
# Calculate components
|
||||
if 'zxx' in self.rxType:
|
||||
Zij = sDiag(Hd*( sDiag(ex_px)*hy_py - sDiag(ex_py)*hy_px ))
|
||||
ZijN_uV = sDiag(hy_py)*ex_px_u(v) + sDiag(ex_px)*hy_py_u(v) - sDiag(ex_py)*hy_px_u(v) - sDiag(hy_px)*ex_py_u(v)
|
||||
elif 'zxy' in self.rxType:
|
||||
Zij = sDiag(Hd*(-sDiag(ex_px)*hx_py + sDiag(ex_py)*hx_px ))
|
||||
ZijN_uV = -sDiag(hx_py)*ex_px_u(v) - sDiag(ex_px)*hx_py_u(v) + sDiag(ex_py)*hx_px_u(v) + sDiag(hx_px)*ex_py_u(v)
|
||||
elif 'zyx' in self.rxType:
|
||||
Zij = sDiag(Hd*( sDiag(ey_px)*hy_py - sDiag(ey_py)*hy_px ))
|
||||
ZijN_uV = sDiag(hy_py)*ey_px_u(v) + sDiag(ey_px)*hy_py_u(v) - sDiag(ey_py)*hy_px_u(v) - sDiag(hy_px)*ey_py_u(v)
|
||||
elif 'zyy' in self.rxType:
|
||||
Zij = sDiag(Hd*(-sDiag(ey_px)*hx_py + sDiag(ey_py)*hx_px ))
|
||||
ZijN_uV = -sDiag(hx_py)*ey_px_u(v) - sDiag(ey_px)*hx_py_u(v) + sDiag(ey_py)*hx_px_u(v) + sDiag(hx_px)*ey_py_u(v)
|
||||
|
||||
# Calculate the complex derivative
|
||||
PDeriv_complex = Hd * (ZijN_uV - Zij * Hd_uV )
|
||||
elif self.projType is 'T3D':
|
||||
if self.locs.ndim == 3:
|
||||
eFLocs = self.locs[:,:,0]
|
||||
bFLocs = self.locs[:,:,1]
|
||||
else:
|
||||
eFLocs = self.locs
|
||||
bFLocs = self.locs
|
||||
# Get the projection
|
||||
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||
Pbz = mesh.getInterpolationMat(bFLocs,'Fz')
|
||||
|
||||
# Get the fields at location
|
||||
# px: x-polaration and py: y-polaration.
|
||||
bx_px = Pbx*f[src,'b_px']
|
||||
by_px = Pby*f[src,'b_px']
|
||||
bz_px = Pbz*f[src,'b_px']
|
||||
bx_py = Pbx*f[src,'b_py']
|
||||
by_py = Pby*f[src,'b_py']
|
||||
bz_py = Pbz*f[src,'b_py']
|
||||
# Derivatives as lambda functions
|
||||
# NOTE: Think b_p?Deriv_u should return a 2*nF size matrix
|
||||
bx_px_u = lambda vec: Pbx*f._b_pxDeriv_u(src,vec)
|
||||
by_px_u = lambda vec: Pby*f._b_pxDeriv_u(src,vec)
|
||||
bz_px_u = lambda vec: Pbz*f._b_pxDeriv_u(src,vec)
|
||||
bx_py_u = lambda vec: Pbx*f._b_pyDeriv_u(src,vec)
|
||||
by_py_u = lambda vec: Pby*f._b_pyDeriv_u(src,vec)
|
||||
bz_py_u = lambda vec: Pbz*f._b_pyDeriv_u(src,vec)
|
||||
# Update the input vector
|
||||
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||
# Define the components of the derivative
|
||||
Hd = sDiag(1./(sDiag(bx_px)*by_py - sDiag(bx_py)*by_px))
|
||||
Hd_uV = sDiag(by_py)*bx_px_u(v) + sDiag(bx_px)*by_py_u(v) - sDiag(bx_py)*by_px_u(v) - sDiag(by_px)*bx_py_u(v)
|
||||
if 'tzx' in self.rxType:
|
||||
Tij = sDiag(Hd*( - sDiag(by_px)*bz_py + sDiag(by_py)*bz_px ))
|
||||
TijN_uV = -sDiag(by_px)*bz_py_u(v) - sDiag(bz_py)*by_px_u(v) + sDiag(by_py)*bz_px_u(v) + sDiag(bz_px)*by_py_u(v)
|
||||
elif 'tzy' in self.rxType:
|
||||
Tij = sDiag(Hd*( sDiag(bx_px)*bz_py - sDiag(bx_py)*bz_px ))
|
||||
TijN_uV = sDiag(bz_py)*bx_px_u(v) + sDiag(bx_px)*bz_py_u(v) - sDiag(bx_py)*bz_px_u(v) - sDiag(bz_px)*bx_py_u(v)
|
||||
# Calculate the complex derivative
|
||||
PDeriv_complex = Hd * (TijN_uV - Tij * Hd_uV )
|
||||
|
||||
# Extract the real number for the real/imag components.
|
||||
Pv = np.array(getattr(PDeriv_complex, real_or_imag))
|
||||
elif adjoint:
|
||||
# Note: The v vector is real and the return should be complex
|
||||
if self.projType is 'Z1D':
|
||||
Pex = mesh.getInterpolationMat(self.locs[:,-1],'Fx')
|
||||
Pbx = mesh.getInterpolationMat(self.locs[:,-1],'Ex')
|
||||
# ex = Pex*mkvc(f[src,'e_1d'],2)
|
||||
# bx = Pbx*mkvc(f[src,'b_1d'],2)/mu_0
|
||||
dP_deTv = -mkvc(Pex.T*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0)).T*v,2)
|
||||
db_duv = Pbx.T/mu_0*Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))*(Utils.sdiag(1./(Pbx*mkvc(f[src,'b_1d'],2)/mu_0))).T*Utils.sdiag(Pex*mkvc(f[src,'e_1d'],2)).T*v
|
||||
dP_dbTv = mkvc(f._bDeriv_u(src,db_duv,adjoint=True),2)
|
||||
PDeriv_real = np.sum(np.hstack((dP_deTv,dP_dbTv)),1)
|
||||
elif self.projType is 'Z2D':
|
||||
raise NotImplementedError('Has not be implement for 2D impedance tensor')
|
||||
elif self.projType is 'Z3D':
|
||||
if self.locs.ndim == 3:
|
||||
eFLocs = self.locs[:,:,0]
|
||||
bFLocs = self.locs[:,:,1]
|
||||
else:
|
||||
eFLocs = self.locs
|
||||
bFLocs = self.locs
|
||||
# Get the projection
|
||||
Pex = mesh.getInterpolationMat(eFLocs,'Ex')
|
||||
Pey = mesh.getInterpolationMat(eFLocs,'Ey')
|
||||
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||
# Get the fields at location
|
||||
# px: x-polaration and py: y-polaration.
|
||||
aex_px = mkvc(mkvc(f[src,'e_px'],2).T*Pex.T)
|
||||
aey_px = mkvc(mkvc(f[src,'e_px'],2).T*Pey.T)
|
||||
aex_py = mkvc(mkvc(f[src,'e_py'],2).T*Pex.T)
|
||||
aey_py = mkvc(mkvc(f[src,'e_py'],2).T*Pey.T)
|
||||
ahx_px = mkvc(mkvc(f[src,'b_px'],2).T/mu_0*Pbx.T)
|
||||
ahy_px = mkvc(mkvc(f[src,'b_px'],2).T/mu_0*Pby.T)
|
||||
ahx_py = mkvc(mkvc(f[src,'b_py'],2).T/mu_0*Pbx.T)
|
||||
ahy_py = mkvc(mkvc(f[src,'b_py'],2).T/mu_0*Pby.T)
|
||||
# Derivatives as lambda functions
|
||||
aex_px_u = lambda vec: f._e_pxDeriv_u(src,Pex.T*vec,adjoint=True)
|
||||
aey_px_u = lambda vec: f._e_pxDeriv_u(src,Pey.T*vec,adjoint=True)
|
||||
aex_py_u = lambda vec: f._e_pyDeriv_u(src,Pex.T*vec,adjoint=True)
|
||||
aey_py_u = lambda vec: f._e_pyDeriv_u(src,Pey.T*vec,adjoint=True)
|
||||
ahx_px_u = lambda vec: f._b_pxDeriv_u(src,Pbx.T*vec,adjoint=True)/mu_0
|
||||
ahy_px_u = lambda vec: f._b_pxDeriv_u(src,Pby.T*vec,adjoint=True)/mu_0
|
||||
ahx_py_u = lambda vec: f._b_pyDeriv_u(src,Pbx.T*vec,adjoint=True)/mu_0
|
||||
ahy_py_u = lambda vec: f._b_pyDeriv_u(src,Pby.T*vec,adjoint=True)/mu_0
|
||||
|
||||
# Update the input vector
|
||||
# Define shortcuts
|
||||
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||
sVec = lambda t: Utils.sp.csr_matrix(mkvc(t,2))
|
||||
# Define the components of the derivative
|
||||
aHd = sDiag(1./(sDiag(ahx_px)*ahy_py - sDiag(ahx_py)*ahy_px))
|
||||
aHd_uV = lambda x: ahx_px_u(sDiag(ahy_py)*x) + ahx_px_u(sDiag(ahy_py)*x) - ahy_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(ahy_px)*x)
|
||||
# Need to fix this to reflect the adjoint
|
||||
if 'zxx' in self.rxType:
|
||||
Zij = sDiag(aHd*( sDiag(ahy_py)*aex_px - sDiag(ahy_px)*aex_py))
|
||||
ZijN_uV = lambda x: aex_px_u(sDiag(ahy_py)*x) + ahy_py_u(sDiag(aex_px)*x) - ahy_px_u(sDiag(aex_py)*x) - aex_py_u(sDiag(ahy_px)*x)
|
||||
elif 'zxy' in self.rxType:
|
||||
Zij = sDiag(aHd*(-sDiag(ahx_py)*aex_px + sDiag(ahx_px)*aex_py))
|
||||
ZijN_uV = lambda x:-aex_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(aex_px)*x) + ahx_px_u(sDiag(aex_py)*x) + aex_py_u(sDiag(ahx_px)*x)
|
||||
elif 'zyx' in self.rxType:
|
||||
Zij = sDiag(aHd*( sDiag(ahy_py)*aey_px - sDiag(ahy_px)*aey_py))
|
||||
ZijN_uV = lambda x: aey_px_u(sDiag(ahy_py)*x) + ahy_py_u(sDiag(aey_px)*x) - ahy_px_u(sDiag(aey_py)*x) - aey_py_u(sDiag(ahy_px)*x)
|
||||
elif 'zyy' in self.rxType:
|
||||
Zij = sDiag(aHd*(-sDiag(ahx_py)*aey_px + sDiag(ahx_px)*aey_py))
|
||||
ZijN_uV = lambda x:-aey_px_u(sDiag(ahx_py)*x) - ahx_py_u(sDiag(aey_px)*x) + ahx_px_u(sDiag(aey_py)*x) + aey_py_u(sDiag(ahx_px)*x)
|
||||
|
||||
# Calculate the complex derivative
|
||||
PDeriv_real = ZijN_uV(aHd*v) - aHd_uV(Zij.T*aHd*v)#
|
||||
# NOTE: Need to reshape the output to go from 2*nU array to a (nU,2) matrix for each polarization
|
||||
# PDeriv_real = np.hstack((mkvc(PDeriv_real[:len(PDeriv_real)/2],2),mkvc(PDeriv_real[len(PDeriv_real)/2::],2)))
|
||||
PDeriv_real = PDeriv_real.reshape((2,mesh.nE)).T
|
||||
|
||||
elif self.projType is 'T3D':
|
||||
if self.locs.ndim == 3:
|
||||
bFLocs = self.locs[:,:,1]
|
||||
else:
|
||||
bFLocs = self.locs
|
||||
# Get the projection
|
||||
Pbx = mesh.getInterpolationMat(bFLocs,'Fx')
|
||||
Pby = mesh.getInterpolationMat(bFLocs,'Fy')
|
||||
Pbz = mesh.getInterpolationMat(bFLocs,'Fz')
|
||||
# Get the fields at location
|
||||
# px: x-polaration and py: y-polaration.
|
||||
abx_px = mkvc(mkvc(f[src,'b_px'],2).T*Pbx.T)
|
||||
aby_px = mkvc(mkvc(f[src,'b_px'],2).T*Pby.T)
|
||||
abz_px = mkvc(mkvc(f[src,'b_px'],2).T*Pbz.T)
|
||||
abx_py = mkvc(mkvc(f[src,'b_py'],2).T*Pbx.T)
|
||||
aby_py = mkvc(mkvc(f[src,'b_py'],2).T*Pby.T)
|
||||
abz_py = mkvc(mkvc(f[src,'b_py'],2).T*Pbz.T)
|
||||
# Derivatives as lambda functions
|
||||
abx_px_u = lambda vec: f._b_pxDeriv_u(src,Pbx.T*vec,adjoint=True)
|
||||
aby_px_u = lambda vec: f._b_pxDeriv_u(src,Pby.T*vec,adjoint=True)
|
||||
abz_px_u = lambda vec: f._b_pxDeriv_u(src,Pbz.T*vec,adjoint=True)
|
||||
abx_py_u = lambda vec: f._b_pyDeriv_u(src,Pbx.T*vec,adjoint=True)
|
||||
aby_py_u = lambda vec: f._b_pyDeriv_u(src,Pby.T*vec,adjoint=True)
|
||||
abz_py_u = lambda vec: f._b_pyDeriv_u(src,Pbz.T*vec,adjoint=True)
|
||||
|
||||
# Update the input vector
|
||||
# Define shortcuts
|
||||
sDiag = lambda t: Utils.sdiag(mkvc(t,2))
|
||||
sVec = lambda t: Utils.sp.csr_matrix(mkvc(t,2))
|
||||
# Define the components of the derivative
|
||||
aHd = sDiag(1./(sDiag(abx_px)*aby_py - sDiag(abx_py)*aby_px))
|
||||
aHd_uV = lambda x: abx_px_u(sDiag(aby_py)*x) + abx_px_u(sDiag(aby_py)*x) - aby_px_u(sDiag(abx_py)*x) - abx_py_u(sDiag(aby_px)*x)
|
||||
# Need to fix this to reflect the adjoint
|
||||
if 'tzx' in self.rxType:
|
||||
Tij = sDiag(aHd*( -sDiag(abz_py)*aby_px + sDiag(abz_px)*aby_py))
|
||||
TijN_uV = lambda x: -abz_py_u(sDiag(aby_px)*x) - aby_px_u(sDiag(abz_py)*x) + aby_py_u(sDiag(abz_px)*x) + abz_px_u(sDiag(aby_py)*x)
|
||||
elif 'tzy' in self.rxType:
|
||||
Tij = sDiag(aHd*( sDiag(abz_py)*abx_px - sDiag(abz_px)*abx_py))
|
||||
TijN_uV = lambda x: abx_px_u(sDiag(abz_py)*x) + abz_py_u(sDiag(abx_px)*x) - abx_py_u(sDiag(abz_px)*x) - abz_px_u(sDiag(abx_py)*x)
|
||||
# Calculate the complex derivative
|
||||
PDeriv_real = TijN_uV(aHd*v) - aHd_uV(Tij.T*aHd*v)#
|
||||
# NOTE: Need to reshape the output to go from 2*nU array to a (nU,2) matrix for each polarization
|
||||
# PDeriv_real = np.hstack((mkvc(PDeriv_real[:len(PDeriv_real)/2],2),mkvc(PDeriv_real[len(PDeriv_real)/2::],2)))
|
||||
PDeriv_real = PDeriv_real.reshape((2,mesh.nE)).T
|
||||
# Extract the data
|
||||
if real_or_imag == 'imag':
|
||||
Pv = 1j*PDeriv_real
|
||||
elif real_or_imag == 'real':
|
||||
Pv = PDeriv_real.astype(complex)
|
||||
|
||||
|
||||
return Pv
|
||||
|
||||
#################
|
||||
### Survey ###
|
||||
#################
|
||||
class Survey(SimPEGsurvey.BaseSurvey):
|
||||
"""
|
||||
Survey class for MT. Contains all the sources associated with the survey.
|
||||
|
||||
:param list srcList: List of sources associated with the survey
|
||||
|
||||
"""
|
||||
srcPair = SrcMT.BaseMTSrc
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
# Sort these by frequency
|
||||
self.srcList = srcList
|
||||
SimPEGsurvey.BaseSurvey.__init__(self, **kwargs)
|
||||
|
||||
_freqDict = {}
|
||||
for src in srcList:
|
||||
if src.freq not in _freqDict:
|
||||
_freqDict[src.freq] = []
|
||||
_freqDict[src.freq] += [src]
|
||||
|
||||
self._freqDict = _freqDict
|
||||
self._freqs = sorted([f for f in self._freqDict])
|
||||
|
||||
@property
|
||||
def freqs(self):
|
||||
"""Frequencies"""
|
||||
return self._freqs
|
||||
|
||||
@property
|
||||
def nFreq(self):
|
||||
"""Number of frequencies"""
|
||||
return len(self._freqDict)
|
||||
|
||||
# TODO: Rename to getSources
|
||||
def getSrcByFreq(self, freq):
|
||||
"""Returns the sources associated with a specific frequency."""
|
||||
assert freq in self._freqDict, "The requested frequency is not in this survey."
|
||||
return self._freqDict[freq]
|
||||
|
||||
def eval(self, u):
|
||||
data = Data(self)
|
||||
for src in self.srcList:
|
||||
sys.stdout.flush()
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.eval(src, self.mesh, u)
|
||||
return data
|
||||
|
||||
def evalDeriv(self, u):
|
||||
raise Exception('Use Transmitters to project fields deriv.')
|
||||
|
||||
#################
|
||||
### Data ###
|
||||
#################
|
||||
class Data(SimPEGsurvey.Data):
|
||||
'''
|
||||
Data class for MTdata. Stores the data vector indexed by the survey.
|
||||
|
||||
:param SimPEG survey object survey:
|
||||
:param v vector of the data in order matching of the survey
|
||||
|
||||
|
||||
'''
|
||||
def __init__(self, survey, v=None):
|
||||
# Pass the variables to the "parent" method
|
||||
SimPEGsurvey.Data.__init__(self, survey, v)
|
||||
|
||||
# # Import data
|
||||
# @classmethod
|
||||
# def fromEDIFiles():
|
||||
# pass
|
||||
|
||||
def toRecArray(self,returnType='RealImag'):
|
||||
'''
|
||||
Function that returns a numpy.recarray for a SimpegMT impedance data object.
|
||||
|
||||
:param str returnType: Switches between returning a rec array where the impedance is split to real and imaginary ('RealImag') or is a complex ('Complex')
|
||||
|
||||
'''
|
||||
|
||||
# Define the record fields
|
||||
dtRI = [('freq',float),('x',float),('y',float),('z',float),('zxxr',float),('zxxi',float),('zxyr',float),('zxyi',float),
|
||||
('zyxr',float),('zyxi',float),('zyyr',float),('zyyi',float),('tzxr',float),('tzxi',float),('tzyr',float),('tzyi',float)]
|
||||
dtCP = [('freq',float),('x',float),('y',float),('z',float),('zxx',complex),('zxy',complex),('zyx',complex),('zyy',complex),('tzx',complex),('tzy',complex)]
|
||||
impList = ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi']
|
||||
for src in self.survey.srcList:
|
||||
# Temp array for all the receivers of the source.
|
||||
# Note: needs to be written more generally, using diffterent rxTypes and not all the data at the locaitons
|
||||
# Assume the same locs for all RX
|
||||
locs = src.rxList[0].locs
|
||||
if locs.shape[1] == 1:
|
||||
locs = np.hstack((np.array([[0.0,0.0]]),locs))
|
||||
elif locs.shape[1] == 2:
|
||||
locs = np.hstack((np.array([[0.0]]),locs))
|
||||
tArrRec = np.concatenate((src.freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],12))),axis=1).view(dtRI)
|
||||
# np.array([(src.freq,rx.locs[0,0],rx.locs[0,1],rx.locs[0,2],np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ) for rx in src.rxList],dtype=dtRI)
|
||||
# Get the type and the value for the DataMT object as a list
|
||||
typeList = [[rx.rxType.replace('z1d','zyx'),self[src,rx]] for rx in src.rxList]
|
||||
# Insert the values to the temp array
|
||||
for nr,(key,val) in enumerate(typeList):
|
||||
tArrRec[key] = mkvc(val,2)
|
||||
# Masked array
|
||||
mArrRec = np.ma.MaskedArray(rec2ndarr(tArrRec),mask=np.isnan(rec2ndarr(tArrRec))).view(dtype=tArrRec.dtype)
|
||||
# Unique freq and loc of the masked array
|
||||
uniFLmarr = np.unique(mArrRec[['freq','x','y','z']]).copy()
|
||||
|
||||
try:
|
||||
outTemp = recFunc.stack_arrays((outTemp,mArrRec))
|
||||
#outTemp = np.concatenate((outTemp,dataBlock),axis=0)
|
||||
except NameError as e:
|
||||
outTemp = mArrRec
|
||||
|
||||
if 'RealImag' in returnType:
|
||||
outArr = outTemp
|
||||
elif 'Complex' in returnType:
|
||||
# Add the real and imaginary to a complex number
|
||||
outArr = np.empty(outTemp.shape,dtype=dtCP)
|
||||
for comp in ['freq','x','y','z']:
|
||||
outArr[comp] = outTemp[comp].copy()
|
||||
for comp in ['zxx','zxy','zyx','zyy','tzx','tzy']:
|
||||
outArr[comp] = outTemp[comp+'r'].copy() + 1j*outTemp[comp+'i'].copy()
|
||||
else:
|
||||
raise NotImplementedError('{:s} is not implemented, as to be RealImag or Complex.')
|
||||
|
||||
# Return
|
||||
return outArr
|
||||
|
||||
@classmethod
|
||||
def fromRecArray(cls, recArray, srcType='primary'):
|
||||
"""
|
||||
Class method that reads in a numpy record array to MTdata object.
|
||||
|
||||
Only imports the impedance data.
|
||||
|
||||
"""
|
||||
if srcType=='primary':
|
||||
src = SrcMT.polxy_1Dprimary
|
||||
elif srcType=='total':
|
||||
src = SrcMT.polxy_1DhomotD
|
||||
else:
|
||||
raise NotImplementedError('{:s} is not a valid source type for MTdata')
|
||||
|
||||
# Find all the frequencies in recArray
|
||||
uniFreq = np.unique(recArray['freq'])
|
||||
srcList = []
|
||||
dataList = []
|
||||
for freq in uniFreq:
|
||||
# Initiate rxList
|
||||
rxList = []
|
||||
# Find that data for freq
|
||||
dFreq = recArray[recArray['freq'] == freq].copy()
|
||||
# Find the impedance rxTypes in the recArray.
|
||||
rxTypes = [ comp for comp in recArray.dtype.names if (len(comp)==4 or len(comp)==3) and 'z' in comp]
|
||||
for rxType in rxTypes:
|
||||
# Find index of not nan values in rxType
|
||||
notNaNind = ~np.isnan(dFreq[rxType])
|
||||
if np.any(notNaNind): # Make sure that there is any data to add.
|
||||
locs = rec2ndarr(dFreq[['x','y','z']][notNaNind].copy())
|
||||
if dFreq[rxType].dtype.name in 'complex128':
|
||||
rxList.append(Rx(locs,rxType+'r'))
|
||||
dataList.append(dFreq[rxType][notNaNind].real.copy())
|
||||
rxList.append(Rx(locs,rxType+'i'))
|
||||
dataList.append(dFreq[rxType][notNaNind].imag.copy())
|
||||
else:
|
||||
rxList.append(Rx(locs,rxType))
|
||||
dataList.append(dFreq[rxType][notNaNind].copy())
|
||||
srcList.append(src(rxList,freq))
|
||||
|
||||
# Make a survey
|
||||
survey = Survey(srcList)
|
||||
dataVec = np.hstack(dataList)
|
||||
return cls(survey,dataVec)
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Analytic solution of EM fields due to a plane wave
|
||||
|
||||
import numpy as np, SimPEG as simpeg
|
||||
from scipy.constants import mu_0, epsilon_0 as eps_0
|
||||
|
||||
def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
|
||||
'''Analytic solution for MT 1D layered earth. Returns E and H fields.
|
||||
|
||||
:param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information.
|
||||
:param numpy.array, vector sigma: Physical property of conductivity corresponding with the mesh.
|
||||
:param float, freq: Frequency to calculate data at.
|
||||
:param numpy array, vector zd: location to calculate EH fields at
|
||||
:param bollean, scaleUD: scales the output to be 1 at the top, increases numeracal stability.
|
||||
|
||||
Assumes a halfspace with the same conductive as the last cell below.
|
||||
|
||||
'''
|
||||
# Note add an error check for the mesh and sigma are the same size.
|
||||
|
||||
# Constants: Assume constant
|
||||
mu = mu_0*np.ones((m1d.nC+1))
|
||||
eps = eps_0*np.ones((m1d.nC+1))
|
||||
# Angular freq
|
||||
w = 2*np.pi*freq
|
||||
# Add the halfspace value to the property
|
||||
sig = np.concatenate((np.array([sigma[0]]),sigma))
|
||||
# Calculate the wave number
|
||||
k = np.sqrt(eps*mu*w**2-1j*mu*sig*w)
|
||||
|
||||
# Initiate the propagation matrix, in the order down up.
|
||||
UDp = np.zeros((2,m1d.nC+1),dtype=complex)
|
||||
UDp[1,0] = 1. # Set the wave amplitude as 1 into the half-space at the bottom of the mesh
|
||||
# Loop over all the layers, starting at the bottom layer
|
||||
for lnr, h in enumerate(m1d.hx): # lnr-number of layer, h-thickness of the layer
|
||||
# Calculate
|
||||
yp1 = k[lnr]/(w*mu[lnr]) # Admittance of the layer below the current layer
|
||||
zp = (w*mu[lnr+1])/k[lnr+1] # Impedance in the current layer
|
||||
# Build the propagation matrix
|
||||
|
||||
# Convert fields to down/up going components in layer below current layer
|
||||
Pj1 = np.array([[1,1],[yp1,-yp1]])
|
||||
# Convert fields to down/up going components in current layer
|
||||
Pjinv = 1./2*np.array([[1,zp],[1,-zp]])
|
||||
# Propagate down and up components through the current layer
|
||||
elamh = np.array([[np.exp(-1j*k[lnr+1]*h),0],[0,np.exp(1j*k[lnr+1]*h)]])
|
||||
|
||||
# The down and up component in current layer.
|
||||
UDp[:,lnr+1] = elamh.dot(Pjinv.dot(Pj1)).dot(UDp[:,lnr])
|
||||
|
||||
if scaleUD:
|
||||
UDp[:,lnr+1::-1] = UDp[:,lnr+1::-1]/UDp[1,lnr+1]
|
||||
|
||||
# Calculate the fields
|
||||
Ed = np.empty((zd.size,),dtype=complex)
|
||||
Eu = np.empty((zd.size,),dtype=complex)
|
||||
Hd = np.empty((zd.size,),dtype=complex)
|
||||
Hu = np.empty((zd.size,),dtype=complex)
|
||||
|
||||
# Loop over the layers and calculate the fields
|
||||
# In the halfspace below the mesh
|
||||
dup = m1d.vectorNx[0]
|
||||
dind = dup >= zd
|
||||
Ed[dind] = UDp[1,0]*np.exp(-1j*k[0]*(dup-zd[dind]))
|
||||
Eu[dind] = UDp[0,0]*np.exp(1j*k[0]*(dup-zd[dind]))
|
||||
Hd[dind] = (k[0]/(w*mu[0]))*UDp[1,0]*np.exp(-1j*k[0]*(dup-zd[dind]))
|
||||
Hu[dind] = -(k[0]/(w*mu[0]))*UDp[0,0]*np.exp(1j*k[0]*(dup-zd[dind]))
|
||||
for ki,mui,epsi,dlow,dup,Up,Dp in zip(k[1::],mu[1::],eps[1::],m1d.vectorNx[:-1],m1d.vectorNx[1::],UDp[0,1::],UDp[1,1::]):
|
||||
dind = np.logical_and(dup >= zd, zd > dlow)
|
||||
Ed[dind] = Dp*np.exp(-1j*ki*(dup-zd[dind]))
|
||||
Eu[dind] = Up*np.exp(1j*ki*(dup-zd[dind]))
|
||||
Hd[dind] = (ki/(w*mui))*Dp*np.exp(-1j*ki*(dup-zd[dind]))
|
||||
Hu[dind] = -(ki/(w*mui))*Up*np.exp(1j*ki*(dup-zd[dind]))
|
||||
|
||||
# Return return the fields
|
||||
return Ed, Eu, Hd, Hu
|
||||
|
||||
def getImpedance(m1d,sigma,freq):
|
||||
"""Analytic solution for MT 1D layered earth. Returns the impedance at the surface.
|
||||
|
||||
:param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information.
|
||||
:param numpy.array, vector sigma: Physical property corresponding with the mesh.
|
||||
:param numpy.array, vector freq: Frequencies to calculate data at.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Initiate the impedances
|
||||
Z1d = np.empty(len(freq) , dtype='complex')
|
||||
h = m1d.hx #vectorNx[:-1]
|
||||
# Start the process
|
||||
for nrFr, fr in enumerate(freq):
|
||||
om = 2*np.pi*fr
|
||||
Zall = np.empty(len(h)+1,dtype='complex')
|
||||
# Calculate the impedance for the bottom layer
|
||||
Zall[0] = (mu_0*om)/np.sqrt(mu_0*eps_0*(om)**2 - 1j*mu_0*sigma[0]*om)
|
||||
|
||||
for nr,hi in enumerate(h):
|
||||
# Calculate the wave number
|
||||
# print nr,sigma[nr]
|
||||
k = np.sqrt(mu_0*eps_0*om**2 - 1j*mu_0*sigma[nr]*om)
|
||||
Z = (mu_0*om)/k
|
||||
|
||||
Zall[nr+1] = Z *((Zall[nr] + Z*np.tanh(1j*k*hi))/(Z + Zall[nr]*np.tanh(1j*k*hi)))
|
||||
|
||||
#pdb.set_trace()
|
||||
Z1d[nrFr] = Zall[-1]
|
||||
|
||||
return Z1d
|
||||
@@ -0,0 +1,45 @@
|
||||
import numpy as np, SimPEG as simpeg
|
||||
from MT1Danalytic import getEHfields
|
||||
from scipy.constants import mu_0
|
||||
|
||||
def get1DEfields(m1d,sigma,freq,sourceAmp=1.0):
|
||||
"""Function to get 1D electrical fields"""
|
||||
|
||||
# Get the gradient
|
||||
G = m1d.nodalGrad
|
||||
# Mass matrices
|
||||
# Magnetic permeability
|
||||
Mmu = simpeg.Utils.sdiag(m1d.vol*(1.0/mu_0))
|
||||
# Conductivity
|
||||
Msig = m1d.getFaceInnerProduct(sigma)
|
||||
# Set up the solution matrix
|
||||
A = G.T*Mmu*G + 1j*2.*np.pi*freq*Msig
|
||||
# Define the inner part of the solution matrix
|
||||
Aii = A[1:-1,1:-1]
|
||||
# Define the outer part of the solution matrix
|
||||
Aio = A[1:-1,[0,-1]]
|
||||
|
||||
# Set the boundary conditions
|
||||
Ed, Eu, Hd, Hu = getEHfields(m1d,sigma,freq,m1d.vectorNx)
|
||||
Etot = (Ed + Eu)
|
||||
if sourceAmp is not None:
|
||||
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
|
||||
## Note: The analytic solution is derived with e^iwt
|
||||
bc = np.r_[Etot[0],Etot[-1]]
|
||||
# The right hand side
|
||||
rhs = Aio*bc
|
||||
# Solve the system
|
||||
Aii_inv = simpeg.Solver(Aii)
|
||||
eii = Aii_inv*rhs
|
||||
# Assign the boundary conditions
|
||||
e = np.r_[bc[0],eii,bc[1]]
|
||||
# Return the electrical fields
|
||||
return e
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
hz = [(100.,18)]
|
||||
M = simpeg.Mesh.TensorMesh([hz],'C')
|
||||
sig = np.zeros(M.nC) + 1e-8
|
||||
sig[M.vectorCCx<=0] = sigHalf
|
||||
@@ -0,0 +1,4 @@
|
||||
from MT1Dsolutions import * # Add the names of the functions
|
||||
from MT1Danalytic import *
|
||||
from dataUtils import *
|
||||
from ediFilesUtils import *
|
||||
@@ -0,0 +1,245 @@
|
||||
# Utils used for the data,
|
||||
import numpy as np, matplotlib.pyplot as plt, sys
|
||||
import SimPEG as simpeg
|
||||
import numpy.lib.recfunctions as recFunc
|
||||
from scipy.constants import mu_0
|
||||
from scipy import interpolate as sciint
|
||||
|
||||
def getAppRes(MTdata):
|
||||
# Make impedance
|
||||
zList = []
|
||||
for src in MTdata.survey.srcList:
|
||||
zc = [src.freq]
|
||||
for rx in src.rxList:
|
||||
if 'i' in rx.rxType:
|
||||
m=1j
|
||||
else:
|
||||
m = 1
|
||||
zc.append(m*MTdata[src,rx])
|
||||
zList.append(zc)
|
||||
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
|
||||
|
||||
def rotateData(MTdata,rotAngle):
|
||||
'''
|
||||
Function that rotates clockwist by rotAngle (- negative for a counter-clockwise rotation)
|
||||
'''
|
||||
recData = MTdata.toRecArray('Complex')
|
||||
impData = rec2ndarr(recData[['zxx','zxy','zyx','zyy']],complex)
|
||||
# Make the rotation matrix
|
||||
# c,s,zxx,zxy,zyx,zyy = sympy.symbols('c,s,zxx,zxy,zyx,zyy')
|
||||
# rotM = sympy.Matrix([[c,-s],[s, c]])
|
||||
# zM = sympy.Matrix([[zxx,zxy],[zyx,zyy]])
|
||||
# rotM*zM*rotM.T
|
||||
# [c*(c*zxx - s*zyx) - s*(c*zxy - s*zyy), c*(c*zxy - s*zyy) + s*(c*zxx - s*zyx)],
|
||||
# [c*(c*zyx + s*zxx) - s*(c*zyy + s*zxy), c*(c*zyy + s*zxy) + s*(c*zyx + s*zxx)]])
|
||||
s = np.sin(-np.deg2rad(rotAngle))
|
||||
c = np.cos(-np.deg2rad(rotAngle))
|
||||
rotMat = np.array([[c,-s],[s,c]])
|
||||
rotData = (rotMat.dot(impData.reshape(-1,2,2).dot(rotMat.T))).transpose(1,0,2).reshape(-1,4)
|
||||
outRec = recData.copy()
|
||||
for nr,comp in enumerate(['zxx','zxy','zyx','zyy']):
|
||||
outRec[comp] = rotData[:,nr]
|
||||
|
||||
from SimPEG import MT
|
||||
return MT.Data.fromRecArray(outRec)
|
||||
|
||||
|
||||
def appResPhs(freq,z):
|
||||
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
|
||||
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
|
||||
return app_res, app_phs
|
||||
|
||||
def skindepth(rho,freq):
|
||||
''' Function to calculate the skindepth of EM waves'''
|
||||
return np.sqrt( (rho*((1/(freq * mu_0 * np.pi )))))
|
||||
|
||||
def rec2ndarr(x,dt=float):
|
||||
return x.view((dt, len(x.dtype.names)))
|
||||
|
||||
def makeAnalyticSolution(mesh,model,elev,freqs):
|
||||
from SimPEG import MT
|
||||
data1D = []
|
||||
for freq in freqs:
|
||||
anaEd, anaEu, anaHd, anaHu = MT.Utils.MT1Danalytic.getEHfields(mesh,model,freq,elev)
|
||||
anaE = anaEd+anaEu
|
||||
anaH = anaHd+anaHu
|
||||
|
||||
anaZ = anaE/anaH
|
||||
# Add to the list
|
||||
data1D.append((freq,0,0,elev,anaZ[0]))
|
||||
dataRec = np.array(data1D,dtype=[('freq',float),('x',float),('y',float),('z',float),('zyx',complex)])
|
||||
return dataRec
|
||||
|
||||
def plotMT1DModelData(problem,models,symList=None):
|
||||
from SimPEG import MT
|
||||
# Setup the figure
|
||||
fontSize = 15
|
||||
|
||||
fig = plt.figure(figsize=[9,7])
|
||||
axM = fig.add_axes([0.075,.1,.25,.875])
|
||||
axM.set_xlabel('Resistivity [Ohm*m]',fontsize=fontSize)
|
||||
axM.set_xlim(1e-1,1e5)
|
||||
axM.set_ylim(-10000,5000)
|
||||
axM.set_ylabel('Depth [km]',fontsize=fontSize)
|
||||
axR = fig.add_axes([0.42,.575,.5,.4])
|
||||
axR.set_xscale('log')
|
||||
axR.set_yscale('log')
|
||||
axR.invert_xaxis()
|
||||
# axR.set_xlabel('Frequency [Hz]')
|
||||
axR.set_ylabel('Apparent resistivity [Ohm m]',fontsize=fontSize)
|
||||
|
||||
axP = fig.add_axes([0.42,.1,.5,.4])
|
||||
axP.set_xscale('log')
|
||||
axP.invert_xaxis()
|
||||
axP.set_ylim(0,90)
|
||||
axP.set_xlabel('Frequency [Hz]',fontsize=fontSize)
|
||||
axP.set_ylabel('Apparent phase [deg]',fontsize=fontSize)
|
||||
|
||||
# if not symList:
|
||||
# symList = ['x']*len(models)
|
||||
import plotDataTypes as pDt
|
||||
# Loop through the models.
|
||||
modelList = [problem.survey.mtrue]
|
||||
modelList.extend(models)
|
||||
if False:
|
||||
modelList = [problem.mapping.sigmaMap*mod for mod in modelList]
|
||||
for nr, model in enumerate(modelList):
|
||||
# Calculate the data
|
||||
if nr==0:
|
||||
data1D = problem.dataPair(problem.survey,problem.survey.dobs).toRecArray('Complex')
|
||||
else:
|
||||
data1D = problem.dataPair(problem.survey,problem.survey.dpred(model)).toRecArray('Complex')
|
||||
# Plot the data and the model
|
||||
colRat = nr/((len(modelList)-1.999)*1.)
|
||||
if colRat > 1.:
|
||||
col = 'k'
|
||||
else:
|
||||
col = plt.cm.seismic(1-colRat)
|
||||
# The model - make the pts to plot
|
||||
meshPts = np.concatenate((problem.mesh.gridN[0:1],np.kron(problem.mesh.gridN[1::],np.ones(2))[:-1]))
|
||||
modelPts = np.kron(1./(problem.mapping.sigmaMap*model),np.ones(2,))
|
||||
axM.semilogx(modelPts,meshPts,color=col)
|
||||
|
||||
## Data
|
||||
# Appres
|
||||
pDt.plotIsoStaImpedance(axR,np.array([0,0]),data1D,'zyx','res',pColor=col)
|
||||
# Appphs
|
||||
pDt.plotIsoStaImpedance(axP,np.array([0,0]),data1D,'zyx','phs',pColor=col)
|
||||
try:
|
||||
allData = np.concatenate((allData,simpeg.mkvc(data1D['zyx'],2)),1)
|
||||
except:
|
||||
allData = simpeg.mkvc(data1D['zyx'],2)
|
||||
freq = simpeg.mkvc(data1D['freq'],2)
|
||||
res, phs = appResPhs(freq,allData)
|
||||
|
||||
stdCol = 'gray'
|
||||
axRtw = axR.twinx()
|
||||
axRtw.set_ylabel('Std of log10',color=stdCol)
|
||||
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
|
||||
axPtw = axP.twinx()
|
||||
axPtw.set_ylabel('Std ',color=stdCol)
|
||||
[t.set_color(stdCol) for t in axPtw.get_yticklabels()]
|
||||
axRtw.plot(freq, np.std(np.log10(res),1),'--',color=stdCol)
|
||||
axPtw.plot(freq, np.std(phs,1),'--',color=stdCol)
|
||||
|
||||
# Fix labels and ticks
|
||||
|
||||
yMtick = [l/1000 for l in axM.get_yticks().tolist()]
|
||||
axM.set_yticklabels(yMtick)
|
||||
[ l.set_rotation(90) for l in axM.get_yticklabels()]
|
||||
[ l.set_rotation(90) for l in axR.get_yticklabels()]
|
||||
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
|
||||
[t.set_color(stdCol) for t in axPtw.get_yticklabels()]
|
||||
for ax in [axM,axR,axP]:
|
||||
ax.xaxis.set_tick_params(labelsize=fontSize)
|
||||
ax.yaxis.set_tick_params(labelsize=fontSize)
|
||||
return fig
|
||||
|
||||
def printTime():
|
||||
import time
|
||||
print time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())
|
||||
|
||||
def convert3Dto1Dobject(MTdata,rxType3D='zyx'):
|
||||
from SimPEG import MT
|
||||
# Find the unique locations
|
||||
# Need to find the locations
|
||||
recDataTemp = MTdata.toRecArray()
|
||||
# Check if survey.std has been assigned.
|
||||
## NEED TO: write this...
|
||||
# Calculte and add the DET of the tensor to the recArray
|
||||
if 'det' in rxType3D:
|
||||
Zon = (recDataTemp['zxxr']+1j*recDataTemp['zxxi'])*(recDataTemp['zyyr']+1j*recDataTemp['zyyi'])
|
||||
Zoff = (recDataTemp['zxyr']+1j*recDataTemp['zxyi'])*(recDataTemp['zyxr']+1j*recDataTemp['zyxi'])
|
||||
det = np.sqrt(Zon.data - Zoff.data)
|
||||
recData = recFunc.append_fields(recDataTemp,['zdetr','zdeti'],[det.real,det.imag] )
|
||||
else:
|
||||
recData = recDataTemp
|
||||
|
||||
uniLocs = rec2ndarr(np.unique(recData[['x','y','z']])).data
|
||||
mtData1DList = []
|
||||
if 'zxy' in rxType3D:
|
||||
corr = -1 # Shift the data to comply with the quadtrature of the 1d problem
|
||||
else:
|
||||
corr = 1
|
||||
for loc in uniLocs:
|
||||
# Make the receiver list
|
||||
rx1DList = []
|
||||
for rxType in ['z1dr','z1di']:
|
||||
rx1DList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType))
|
||||
# Source list
|
||||
locrecData = recData[np.sqrt(np.sum( (rec2ndarr(recData[['x','y','z']]).data - loc )**2,axis=1)) < 1e-5]
|
||||
dat1DList = []
|
||||
src1DList = []
|
||||
for freq in locrecData['freq']:
|
||||
src1DList.append(MT.SrcMT.src_polxy_1Dprimary(rx1DList,freq))
|
||||
for comp in ['r','i']:
|
||||
dat1DList.append( corr * locrecData[rxType3D+comp][locrecData['freq']== freq].data )
|
||||
|
||||
# Make the survey
|
||||
sur1D = MT.Survey(src1DList)
|
||||
|
||||
# Make the data
|
||||
dataVec = np.hstack(dat1DList)
|
||||
dat1D = MT.Data(sur1D,dataVec)
|
||||
sur1D.dobs = dataVec
|
||||
# Need to take MTdata.survey.std and split it as well.
|
||||
std=0.05
|
||||
sur1D.std = np.abs(sur1D.dobs*std) #+ 0.01*np.linalg.norm(sur1D.dobs)
|
||||
mtData1DList.append(dat1D)
|
||||
|
||||
# Return the the list of data.
|
||||
return mtData1DList
|
||||
|
||||
def resampleMTdataAtFreq(MTdata,freqs):
|
||||
"""
|
||||
Function to resample MTdata at set of frequencies
|
||||
|
||||
"""
|
||||
from SimPEG import MT
|
||||
# Make a rec array
|
||||
MTrec = MTdata.toRecArray().data
|
||||
|
||||
# Find unique locations
|
||||
uniLoc = np.unique(MTrec[['x','y','z']])
|
||||
uniFreq = MTdata.survey.freqs
|
||||
# Get the comps
|
||||
dNames = MTrec.dtype
|
||||
|
||||
# Loop over all the locations and interpolate
|
||||
for loc in uniLoc:
|
||||
# Find the index of the station
|
||||
ind = np.sqrt(np.sum((rec2ndarr(MTrec[['x','y','z']]) - rec2ndarr(loc))**2,axis=1)) < 1. # Find dist of 1 m accuracy
|
||||
# Make a temporary recArray and interpolate all the components
|
||||
tArrRec = np.concatenate((simpeg.mkvc(freqs,2),np.ones((len(freqs),1))*rec2ndarr(loc),np.nan*np.ones((len(freqs),12))),axis=1).view(dNames)
|
||||
for comp in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
|
||||
int1d = sciint.interp1d(MTrec[ind]['freq'],MTrec[ind][comp],bounds_error=False)
|
||||
tArrRec[comp] = simpeg.mkvc(int1d(freqs),2)
|
||||
|
||||
# Join together
|
||||
try:
|
||||
outRecArr = recFunc.stack_arrays((outRecArr,tArrRec))
|
||||
except NameError as e:
|
||||
outRecArr = tArrRec
|
||||
|
||||
# Make the MTdata and return
|
||||
return MT.Data.fromRecArray(outRecArr)
|
||||
@@ -0,0 +1,175 @@
|
||||
# Functions to import and export MT EDI files.
|
||||
from SimPEG import mkvc
|
||||
from scipy.constants import mu_0
|
||||
from numpy.lib import recfunctions as recFunc
|
||||
from SimPEG.MT.Utils.dataUtils import rec2ndarr
|
||||
|
||||
# Import modules
|
||||
import numpy as np
|
||||
import os, sys, re
|
||||
try:
|
||||
import osr
|
||||
except ImportError as e:
|
||||
print 'Could not import osr, missing the gdal package'
|
||||
pass
|
||||
|
||||
class EDIimporter:
|
||||
"""
|
||||
A class to import EDIfiles.
|
||||
|
||||
"""
|
||||
_impUnitEDI2SI = 4*np.pi*1e-4 # Convert Z[mV/km/nT] (as in EDI)to Z[V/A] SI unit
|
||||
_impUnitSI2EDI = 1./_impUnitEDI2SI # ConvertZ[V/A] SI unit to Z[mV/km/nT] (as in EDI)
|
||||
|
||||
# Properties
|
||||
filesList = None
|
||||
comps = None
|
||||
|
||||
# Hidden properties
|
||||
_outEPSG = None
|
||||
_2out = None
|
||||
|
||||
|
||||
def __init__(self, EDIfilesList, compList=None, outEPSG=None):
|
||||
|
||||
# Set the fileList
|
||||
self.filesList = EDIfilesList
|
||||
# Set the components to import
|
||||
if compList is None:
|
||||
self.comps = ['ZXXR','ZXYR','ZYXR','ZYYR','ZXXI','ZXYI','ZYXI','ZYYI','ZXX.VAR','ZXY.VAR','ZYX.VAR','ZYY.VAR']
|
||||
else:
|
||||
self.comps = compList
|
||||
if outEPSG is not None:
|
||||
self._outEPSG = outEPSG
|
||||
|
||||
def __call__(self,comps=None):
|
||||
|
||||
if comps is None:
|
||||
return self._data
|
||||
|
||||
return self._data[comps]
|
||||
|
||||
def importFiles(self):
|
||||
"""
|
||||
Function to import EDI files into a object.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Constants that are needed for convertion of units
|
||||
|
||||
# Temp lists
|
||||
tmpStaList = []
|
||||
|
||||
tmpCompList = ['freq','x','y','z']
|
||||
tmpCompList.extend(self.comps)
|
||||
# Make the outarray
|
||||
dtRI = [(compS.lower().replace('.',''),float) for compS in tmpCompList]
|
||||
# Loop through all the files
|
||||
for nrEDI, EDIfile in enumerate(self.filesList):
|
||||
# Read the file into a list of the lines
|
||||
with open(EDIfile,'r') as fid:
|
||||
EDIlines = fid.readlines()
|
||||
# Find the location
|
||||
latD, longD, elevM = _findLatLong(EDIlines)
|
||||
# Transfrom coordinates
|
||||
transCoord = self._transfromPoints(longD,latD)
|
||||
# Extract the name of the file (station)
|
||||
EDIname = EDIfile.split(os.sep)[-1].split('.')[0]
|
||||
# Arrange the data
|
||||
staList = [EDIname, EDIfile, transCoord[0], transCoord[1], elevM[0]]
|
||||
# Add to the station list
|
||||
tmpStaList.extend(staList)
|
||||
|
||||
# Read the frequency data
|
||||
freq = _findEDIcomp('>FREQ',EDIlines)
|
||||
# Make the temporary rec array.
|
||||
tArrRec = ( np.nan*np.ones( (len(freq),len(dtRI)) ) ).view(dtRI) #np.concatenate((freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],8))),axis=1).view(dtRI)
|
||||
# Add data to the array
|
||||
tArrRec['freq'] = mkvc(freq,2)
|
||||
tArrRec['x'] = mkvc(np.ones((len(freq),1))*transCoord[0],2)
|
||||
tArrRec['y'] = mkvc(np.ones((len(freq),1))*transCoord[1],2)
|
||||
tArrRec['z'] = mkvc(np.ones((len(freq),1))*elevM[0],2)
|
||||
for comp in self.comps:
|
||||
# Deal with converting units of the impedance tensor
|
||||
if 'Z' in comp:
|
||||
unitConvert = self._impUnitEDI2SI
|
||||
else:
|
||||
unitConvert = 1
|
||||
# Rotate the data since EDI x is *north, y *east but Simpeg uses x *east, y *north (* means internal reference frame)
|
||||
key = [comp.lower().replace('.','').replace(s,t) for s,t in [['xx','yy'],['xy','yx'],['yx','xy'],['yy','xx']] if s in comp.lower()][0]
|
||||
tArrRec[key] = mkvc(unitConvert*_findEDIcomp('>'+comp,EDIlines),2)
|
||||
# Make a masked array
|
||||
mArrRec = np.ma.MaskedArray(rec2ndarr(tArrRec),mask=np.isnan(rec2ndarr(tArrRec))).view(dtype=tArrRec.dtype)
|
||||
try:
|
||||
outTemp = recFunc.stack_arrays((outTemp,mArrRec))
|
||||
except NameError as e:
|
||||
outTemp = mArrRec
|
||||
|
||||
# Assign the data
|
||||
self._data = outTemp
|
||||
|
||||
# % Assign the data to the obj
|
||||
# nOutData=length(obj.data);
|
||||
# obj.data(nOutData+1:nOutData+length(TEMP.data),:) = TEMP.data;
|
||||
def _transfromPoints(self,longD,latD):
|
||||
# Coordinates convertor
|
||||
if self._2out is None:
|
||||
src = osr.SpatialReference()
|
||||
src.ImportFromEPSG(4326)
|
||||
out = osr.SpatialReference()
|
||||
if self._outEPSG is None:
|
||||
# Find the UTM EPSG number
|
||||
Nnr = 700 if latD < 0.0 else 600
|
||||
utmZ = int(1+(longD+180.0)/6.0)
|
||||
self._outEPSG = 32000 + Nnr + utmZ
|
||||
out.ImportFromEPSG(self._outEPSG)
|
||||
self._2out = osr.CoordinateTransformation(src,out)
|
||||
# Return the transfrom
|
||||
return self._2out.TransformPoint(longD,latD)
|
||||
|
||||
# Hidden functions
|
||||
def _findLatLong(fileLines):
|
||||
latDMS = np.array(fileLines[_findLine('LAT=',fileLines)[0]].split('=')[1].split()[0].split(':'),float)
|
||||
longDMS = np.array(fileLines[_findLine('LONG=',fileLines)[0]].split('=')[1].split()[0].split(':'),float)
|
||||
elevM = np.array([fileLines[_findLine('ELEV=',fileLines)[0]].split('=')[1].split()[0]],float)
|
||||
# Convert to D.ddddd values
|
||||
latS = np.sign(latDMS[0])
|
||||
longS = np.sign(longDMS[0])
|
||||
latD = latDMS[0] + latS*latDMS[1]/60 + latS*latDMS[2]/3600
|
||||
longD = longDMS[0] + longS*longDMS[1]/60 + longS*longDMS[2]/3600
|
||||
return latD, longD, elevM
|
||||
|
||||
def _findLine(comp,fileLines):
|
||||
""" Find a line number in the file"""
|
||||
# Line counter
|
||||
c = 0
|
||||
# List of indices for found lines
|
||||
found = []
|
||||
# Loop through all the lines
|
||||
for line in fileLines:
|
||||
if comp in line:
|
||||
# Append if found
|
||||
found.append(c)
|
||||
# Increse the counter
|
||||
c += 1
|
||||
# Return the found indices
|
||||
return found
|
||||
|
||||
def _findEDIcomp(comp,fileLines,dt=float):
|
||||
"""
|
||||
Extract the data vector.
|
||||
|
||||
Returns a list of the data.
|
||||
"""
|
||||
# Find the data
|
||||
headLine, indHead = [(st,nr) for nr,st in enumerate(fileLines) if re.search(comp,st)][0]
|
||||
# Extract the data
|
||||
nrVec = int(headLine.split()[-1])
|
||||
c = 0
|
||||
dataList = []
|
||||
while c < nrVec:
|
||||
indHead += 1
|
||||
dataList.extend(fileLines[indHead].split())
|
||||
c = len(dataList)
|
||||
return np.array(dataList,dt)
|
||||
@@ -0,0 +1,416 @@
|
||||
from matplotlib import pyplot as plt, colors, numpy as np
|
||||
|
||||
|
||||
def rec2nd(structArray):
|
||||
""" Converts a structured/record array to ndarray to do operations on."""
|
||||
return structArray.view((np.float,len(structArray.dtype.names)))
|
||||
|
||||
def plotIsoFreqNSimpedance(ax,freq,array,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=True,contour=True):
|
||||
|
||||
indUniFreq = np.where(freq==array['freq'])
|
||||
|
||||
|
||||
x, y = array['x'][indUniFreq],array['y'][indUniFreq]
|
||||
if par == 'abs':
|
||||
zPlot = np.abs(array[flag][indUniFreq])
|
||||
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||
level = np.logspace(0,-5,31)
|
||||
clevel = np.logspace(0,-4,5)
|
||||
plotNorm = colors.LogNorm()
|
||||
elif par == 'real':
|
||||
zPlot = np.real(array[flag][indUniFreq])
|
||||
cmap = plt.get_cmap('RdYlBu')
|
||||
if cLevel:
|
||||
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||
else:
|
||||
plotNorm = colors.Normalize()
|
||||
elif par == 'imag':
|
||||
zPlot = np.imag(array[flag][indUniFreq])
|
||||
cmap = plt.get_cmap('RdYlBu')
|
||||
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||
if cLevel:
|
||||
level = np.concatenate((-np.logspace(0,-10,31),np.logspace(-10,0,31)))
|
||||
clevel = np.concatenate((-np.logspace(0,-8,5),np.logspace(-8,0,5)))
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.SymLogNorm(1e-10,linscale=2)
|
||||
elif colorNorm=='Lin':
|
||||
plotNorm = colors.Normalize()
|
||||
if contour:
|
||||
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||
else:
|
||||
uniX,uniY = np.unique(x),np.unique(y)
|
||||
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||
if colorbar:
|
||||
plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||
ax.set_title(flag+' '+par,fontsize=8)
|
||||
return cs
|
||||
|
||||
def plotIsoFreqNSDiff(ax,freq,arrayList,flag,par='abs',colorbar=True,cLevel=True,mask=None,contourLine=True,useLog=False):
|
||||
|
||||
indUniFreq0 = np.where(freq==arrayList[0]['freq'])
|
||||
indUniFreq1 = np.where(freq==arrayList[1]['freq'])
|
||||
seicmap = plt.get_cmap('RdYlBu')#seismic')
|
||||
x, y = arrayList[0]['x'][indUniFreq0],arrayList[0]['y'][indUniFreq0]
|
||||
if par == 'abs':
|
||||
if useLog:
|
||||
zPlot = (np.log10(np.abs(arrayList[0][flag][indUniFreq0])) - np.log10(np.abs(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs(arrayList[1][flag][indUniFreq1]))
|
||||
else:
|
||||
zPlot = (np.abs(arrayList[0][flag][indUniFreq0]) - np.abs(arrayList[1][flag][indUniFreq1]))/np.abs(arrayList[1][flag][indUniFreq1])
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(arrayList[0][flag][indUniFreq0])< 1e-3,np.abs(arrayList[1][flag][indUniFreq1]) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
if cLevel:
|
||||
level = np.arange(-200,201,10)
|
||||
clevel = np.arange(-200,201,25)
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
elif par == 'real':
|
||||
if useLog:
|
||||
zPlot = (np.log10(np.real(arrayList[0][flag][indUniFreq0])) -np.log10(np.real(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs((np.real(arrayList[1][flag][indUniFreq1]))))
|
||||
else:
|
||||
zPlot = (np.real(arrayList[0][flag][indUniFreq0]) -np.real(arrayList[1][flag][indUniFreq1]))/np.abs((np.real(arrayList[1][flag][indUniFreq1])))
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(np.real(arrayList[0][flag][indUniFreq0])) < 1e-3,np.abs(np.real(arrayList[1][flag][indUniFreq1])) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
if cLevel:
|
||||
level = np.arange(-200,201,10)
|
||||
clevel = np.arange(-200,201,25)
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
elif par == 'imag':
|
||||
if useLog:
|
||||
zPlot = (np.log10(np.imag(arrayList[0][flag][indUniFreq0])) -np.log10(np.imag(arrayList[1][flag][indUniFreq1])))/np.log10(np.abs((np.imag(arrayList[1][flag][indUniFreq1]))))
|
||||
else:
|
||||
zPlot = (np.imag(arrayList[0][flag][indUniFreq0]) -np.imag(arrayList[1][flag][indUniFreq1]))/np.abs((np.imag(arrayList[1][flag][indUniFreq1])))
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(np.imag(arrayList[0][flag][indUniFreq0])) < 1e-3,np.abs(np.imag(arrayList[1][flag][indUniFreq1])) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
if cLevel:
|
||||
level = np.arange(-200,201,10)
|
||||
clevel = np.arange(-200,201,25)
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
cs = ax.tricontourf(x,y,zPlot*100,levels=level*100,cmap=seicmap,extend='both') #,norm=colors.SymLogNorm(1e-2,linscale=2))
|
||||
if contourLine:
|
||||
csl = ax.tricontour(x,y,zPlot*100,levels=clevel*100,colors='k')
|
||||
plt.clabel(csl, fontsize=7, inline=1,fmt='%1.1e',inline_spacing=10)
|
||||
if colorbar:
|
||||
cb = plt.colorbar(cs,cax=ax.cax,ticks=clevel*100,format='%1.1e')
|
||||
for t in cb.ax.get_yticklabels():
|
||||
t.set_rotation(60)
|
||||
t.set_fontsize(8)
|
||||
|
||||
ax.set_title(flag+' '+par,fontsize=8)
|
||||
|
||||
def plotIsoFreqNStipper(ax,freq,array,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=True,contour=True):
|
||||
|
||||
indUniFreq = np.where(freq==array['freq'])
|
||||
|
||||
x, y = array['x'][indUniFreq],array['y'][indUniFreq]
|
||||
if par == 'abs':
|
||||
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||
zPlot = np.abs(array[flag][indUniFreq])
|
||||
if cLevel:
|
||||
level = np.logspace(-4,0,33)
|
||||
clevel = np.logspace(-4,0,5)
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.LogNorm()
|
||||
else:
|
||||
plotNorm = colors.Normalize()
|
||||
elif par == 'real':
|
||||
cmap = plt.get_cmap('RdYlBu')
|
||||
zPlot = np.real(array[flag][indUniFreq])
|
||||
if cLevel:
|
||||
level = np.concatenate((-np.logspace(0,-4,33),np.logspace(-4,0,33)))
|
||||
clevel = np.concatenate((-np.logspace(0,-4,5),np.logspace(-4,0,5)))
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.SymLogNorm(1e-4,linscale=2)
|
||||
else:
|
||||
plotNorm = colors.Normalize()
|
||||
elif par == 'imag':
|
||||
cmap = plt.get_cmap('RdYlBu')
|
||||
zPlot = np.imag(array[flag][indUniFreq])
|
||||
if cLevel:
|
||||
level = np.concatenate((-np.logspace(0,-4,33),np.logspace(-4,0,33)))
|
||||
clevel = np.concatenate((-np.logspace(0,-4,5),np.logspace(-4,0,5)))
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10)
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.SymLogNorm(1e-4,linscale=2)
|
||||
else:
|
||||
plotNorm = colors.Normalize()
|
||||
if contour:
|
||||
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||
else:
|
||||
uniX,uniY = np.unique(x),np.unique(y)
|
||||
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),levels=level,cmap=cmap,norm=plotNorm,edgecolors='k', linewidths=0.5)
|
||||
if colorbar:
|
||||
plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||
ax.set_title(flag+' '+par,fontsize=8)
|
||||
|
||||
def plotIsoStaImpedance(ax,loc,array,flag,par='abs',pSym='s',pColor=None):
|
||||
|
||||
appResFact = 1/(8*np.pi**2*10**(-7))
|
||||
treshold = 1.0 # 1 meter
|
||||
indUniSta = np.sqrt(np.sum((rec2nd(array[['x','y']])-loc)**2,axis=1)) < treshold
|
||||
freq = array['freq'][indUniSta]
|
||||
|
||||
if par == 'abs':
|
||||
zPlot = np.abs(array[flag][indUniSta])
|
||||
elif par == 'real':
|
||||
zPlot = np.real(array[flag][indUniSta])
|
||||
elif par == 'imag':
|
||||
zPlot = np.imag(array[flag][indUniSta])
|
||||
elif par == 'res':
|
||||
zPlot = (appResFact/freq)*np.abs(array[flag][indUniSta])**2
|
||||
elif par == 'phs':
|
||||
zPlot = np.arctan2(array[flag][indUniSta].imag,array[flag][indUniSta].real)*(180/np.pi)
|
||||
|
||||
if not pColor:
|
||||
if 'xx' in flag:
|
||||
lab = 'XX'
|
||||
pColor = 'g'
|
||||
elif 'xy' in flag:
|
||||
lab = 'XY'
|
||||
pColor = 'r'
|
||||
elif 'yx' in flag:
|
||||
lab = 'YX'
|
||||
pColor = 'b'
|
||||
elif 'yy' in flag:
|
||||
lab = 'YY'
|
||||
pColor = 'y'
|
||||
|
||||
ax.plot(freq,zPlot,color=pColor,marker=pSym,label=flag)
|
||||
|
||||
|
||||
def plotPsudoSectNSimpedance(ax,sectDict,array,flag,par='abs',colorbar=True,colorNorm='None',cLevel=None,contour=True):
|
||||
|
||||
indSect = np.where(sectDict.values()[0]==array[sectDict.keys()[0]])
|
||||
|
||||
# Define the plot axes
|
||||
if 'x' in sectDict.keys()[0]:
|
||||
x = array['y'][indSect]
|
||||
else:
|
||||
x = array['x'][indSect]
|
||||
y = array['freq'][indSect]
|
||||
|
||||
if par == 'abs':
|
||||
zPlot = np.abs(array[flag][indSect])
|
||||
cmap = plt.get_cmap('OrRd_r')#seismic')
|
||||
if cLevel:
|
||||
level = np.logspace(0,-5,31,endpoint=True)
|
||||
clevel = np.logspace(0,-4,5,endpoint=True)
|
||||
else:
|
||||
level = np.linspace(zPlot.min(),zPlot.max(),100,endpoint=True)
|
||||
clevel = np.linspace(zPlot.min(),zPlot.max(),10,endpoint=True)
|
||||
|
||||
elif par == 'ares':
|
||||
zPlot = np.abs(array[flag][indSect])**2/(8*np.pi**2*10**(-7)*array['freq'][indSect])
|
||||
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||
if cLevel:
|
||||
zMax = np.log10(cLevel[1])
|
||||
zMin = np.log10(cLevel[0])
|
||||
else:
|
||||
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||
level = np.logspace(zMin,zMax,(zMax-zMin)*8+1,endpoint=True)
|
||||
clevel = np.logspace(zMin,zMax,(zMax-zMin)*2+1,endpoint=True)
|
||||
plotNorm = colors.LogNorm()
|
||||
|
||||
elif par == 'aphs':
|
||||
zPlot = np.arctan2(array[flag][indSect].imag,array[flag][indSect].real)*(180/np.pi)
|
||||
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||
if cLevel:
|
||||
zMax = cLevel[1]
|
||||
zMin = cLevel[0]
|
||||
else:
|
||||
zMax = (np.ceil(zPlot).max())
|
||||
zMin = (np.floor(zPlot).min())
|
||||
level = np.arange(zMin,zMax+.1,1)
|
||||
clevel = np.arange(zMin,zMax+.1,10)
|
||||
plotNorm = colors.Normalize()
|
||||
|
||||
elif par == 'real':
|
||||
zPlot = np.real(array[flag][indSect])
|
||||
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||
if cLevel:
|
||||
zMax = np.log10(cLevel[1])
|
||||
zMin = np.log10(cLevel[0])
|
||||
else:
|
||||
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||
elif par == 'imag':
|
||||
zPlot = np.imag(array[flag][indSect])
|
||||
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||
|
||||
if cLevel:
|
||||
zMax = np.log10(cLevel[1])
|
||||
zMin = np.log10(cLevel[0])
|
||||
else:
|
||||
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||
|
||||
if colorNorm=='SymLog':
|
||||
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||
elif colorNorm=='Lin':
|
||||
plotNorm = colors.Normalize()
|
||||
elif colorNorm=='Log':
|
||||
plotNorm = colors.LogNorm()
|
||||
if contour:
|
||||
cs = ax.tricontourf(x,y,zPlot,levels=level,cmap=cmap,norm=plotNorm)#,extend='both')
|
||||
else:
|
||||
uniX,uniY = np.unique(x),np.unique(y)
|
||||
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||
if colorbar:
|
||||
csB = plt.colorbar(cs,cax=ax.cax,ticks=clevel,format='%1.2e')
|
||||
# csB.on_mappable_changed(cs)
|
||||
ax.set_title(flag+' '+par,fontsize=8)
|
||||
return cs, csB
|
||||
return cs,None
|
||||
|
||||
|
||||
def plotPsudoSectNSDiff(ax,sectDict,arrayList,flag,par='abs',colorbar=True,colorNorm='SymLog',cLevel=None,contour=True,mask=None,useLog=False):
|
||||
|
||||
def sortInArr(arr):
|
||||
return np.sort(arr,order=['freq','x','y','z'])
|
||||
# Find the index for the slice
|
||||
indSect0 = np.where(sectDict.values()[0]==arrayList[0][sectDict.keys()[0]])
|
||||
indSect1 = np.where(sectDict.values()[0]==arrayList[1][sectDict.keys()[0]])
|
||||
# Extract and sort the mats
|
||||
arr0 = sortInArr(arrayList[0][indSect0])
|
||||
arr1 = sortInArr(arrayList[1][indSect1])
|
||||
|
||||
# Define the plot axes
|
||||
if 'x' in sectDict.keys()[0]:
|
||||
x0 = arr0['y']
|
||||
x1 = arr1['y']
|
||||
else:
|
||||
x0 = arr0['x']
|
||||
x1 = arr1['x']
|
||||
y0 = arr0['freq']
|
||||
y1 = arr1['freq']
|
||||
|
||||
|
||||
if par == 'abs':
|
||||
if useLog:
|
||||
zPlot = (np.log10(np.abs(arr0[flag])) - np.log10(np.abs(arr1[flag])))/np.log10(np.abs(arr1[flag]))
|
||||
else:
|
||||
zPlot = (np.abs(arr0[flag]) - np.abs(arr1[flag]))/np.abs(arr1[flag])
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
cmap = plt.get_cmap('RdYlBu')#seismic)
|
||||
elif par == 'ares':
|
||||
arF = 1/(8*np.pi**2*10**(-7))
|
||||
if useLog:
|
||||
zPlot = (np.log10((arF/arr0['freq'])*np.abs(arr0[flag])**2) - np.log10((arF/arr1['freq'])*np.abs(arr1[flag])**2))/np.log10((arF/arr1['freq'])*np.abs(arr1[flag])**2)
|
||||
else:
|
||||
zPlot = ((arF/arr0['freq'])*np.abs(arr0[flag])**2 - (arF/arr1['freq'])*np.abs(arr1[flag])**2)/((arF/arr1['freq'])*np.abs(arr1[flag])**2)
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
cmap = plt.get_cmap('Spectral')#seismic)
|
||||
|
||||
elif par == 'aphs':
|
||||
if useLog:
|
||||
zPlot = (np.log10(np.arctan2(arr0[flag].imag,arr0[flag].real)*(180/np.pi)) - np.log10(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi)) )/np.log10(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi))
|
||||
else:
|
||||
zPlot = ( np.arctan2(arr0[flag].imag,arr0[flag].real)*(180/np.pi) - np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi) )/(np.arctan2(arr1[flag].imag,arr1[flag].real)*(180/np.pi))
|
||||
if mask:
|
||||
maskInd = np.logical_or(np.abs(arr0[flag])< 1e-3,np.abs(arr1[flag]) < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
cmap = plt.get_cmap('Spectral')#seismic)
|
||||
elif par == 'real':
|
||||
if useLog:
|
||||
zPlot = (np.log10(arr0[flag].real) - np.log10(arr1[flag].real))/np.log10(arr1[flag].real)
|
||||
else:
|
||||
zPlot = (arr0[flag].real - arr1[flag].real)/arr1[flag].real
|
||||
if mask:
|
||||
maskInd = np.logical_or(arr0[flag].real< 1e-3,arr1[flag].real < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
cmap = plt.get_cmap('Spectral') #('Spectral')
|
||||
|
||||
elif par == 'imag':
|
||||
if useLog:
|
||||
zPlot = (np.log10(arr0[flag].imag) - np.log10(arr1[flag].imag))/np.log10(arr1[flag].imag)
|
||||
else:
|
||||
zPlot = (arr0[flag].imag - arr1[flag].imag)/arr1[flag].imag
|
||||
if mask:
|
||||
maskInd = np.logical_or(arr0[flag].imag< 1e-3,arr1[flag].imag < 1e-3)
|
||||
zPlot = np.ma.array(zPlot)
|
||||
zPlot[maskInd] = mask
|
||||
cmap = plt.get_cmap('Spectral') #('RdYlBu')
|
||||
|
||||
if cLevel:
|
||||
zMax = np.log10(cLevel[1])
|
||||
zMin = np.log10(cLevel[0])
|
||||
else:
|
||||
zMax = (np.ceil(np.log10(np.abs(zPlot).max())))
|
||||
zMin = (np.floor(np.log10(np.abs(zPlot).min())))
|
||||
|
||||
|
||||
if colorNorm=='SymLog':
|
||||
level = np.concatenate((-np.logspace(zMax,zMin-.125,(zMax-zMin)*8+1,endpoint=True),np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)))
|
||||
clevel = np.concatenate((-np.logspace(zMax,zMin,(zMax-zMin)*1+1,endpoint=True),np.logspace(zMin,zMax,(zMax-zMin)*1+1,endpoint=True)))
|
||||
plotNorm = colors.SymLogNorm(np.abs(level).min(),linscale=0.1)
|
||||
elif colorNorm=='Lin':
|
||||
if cLevel:
|
||||
level = np.arange(cLevel[0],cLevel[1]+.1,(cLevel[1] - cLevel[0])/50.)
|
||||
clevel = np.arange(cLevel[0],cLevel[1]+.1,(cLevel[1] - cLevel[0])/10.)
|
||||
else:
|
||||
level = np.arange(zPlot.min(),zPlot.max(),(zPlot.max() - zPlot.min())/50.)
|
||||
clevel = np.arange(zPlot.min(),zPlot.max(),(zPlot.max() - zPlot.min())/10.)
|
||||
plotNorm = colors.Normalize()
|
||||
elif colorNorm=='Log':
|
||||
level = np.logspace(zMin-.125,zMax,(zMax-zMin)*8+1,endpoint=True)
|
||||
clevel = np.logspace(zMin,zMax,(zMax-zMin)*2+1,endpoint=True)
|
||||
plotNorm = colors.LogNorm()
|
||||
if contour:
|
||||
cs = ax.tricontourf(x0,y0,zPlot*100,levels=level*100,cmap=cmap,norm=plotNorm,extend='both')#,extend='both')
|
||||
else:
|
||||
uniX,uniY = np.unique(x0),np.unique(y0)
|
||||
X,Y = np.meshgrid(np.append(uniX-25,uniX[-1]+25),np.append(uniY-25,uniY[-1]+25))
|
||||
cs = ax.pcolor(X,Y,np.reshape(zPlot,(len(uniY),len(uniX))),cmap=cmap,norm=plotNorm)
|
||||
if colorbar:
|
||||
csB = plt.colorbar(cs,cax=ax.cax,ticks=clevel*100,format='%1.2e')
|
||||
# csB.on_mappable_changed(cs)
|
||||
ax.set_title(flag+' '+par + ' diff',fontsize=8)
|
||||
return cs, csB
|
||||
return cs,None
|
||||
@@ -0,0 +1,178 @@
|
||||
import SimPEG as simpeg, numpy as np
|
||||
|
||||
def homo1DModelSource(mesh,freq,sigma_1d):
|
||||
'''
|
||||
Function that calculates and return background fields
|
||||
|
||||
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||
:param float freq: The frequency to solve at
|
||||
:param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||
:rtype: numpy.ndarray (mesh.nE,2)
|
||||
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||
|
||||
'''
|
||||
# import
|
||||
from SimPEG.MT.Utils import get1DEfields
|
||||
# Get a 1d solution for a halfspace background
|
||||
if mesh.dim == 1:
|
||||
mesh1d = mesh
|
||||
elif mesh.dim == 2:
|
||||
mesh1d = simpeg.Mesh.TensorMesh([mesh.hy],np.array([mesh.x0[1]]))
|
||||
elif mesh.dim == 3:
|
||||
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||
|
||||
# # Note: Everything is using e^iwt
|
||||
e0_1d = get1DEfields(mesh1d,sigma_1d,freq)
|
||||
if mesh.dim == 1:
|
||||
eBG_px = simpeg.mkvc(e0_1d,2)
|
||||
eBG_py = -simpeg.mkvc(e0_1d,2) # added a minus to make the results in the correct quadrents.
|
||||
elif mesh.dim == 2:
|
||||
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
for i in np.arange(mesh.vnEx[0]):
|
||||
ex_px[i,:] = -e0_1d
|
||||
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px))
|
||||
# Setup y (north) polarization (_py)
|
||||
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||
# Assign the source to ey_py
|
||||
for i in np.arange(mesh.vnEy[0]):
|
||||
ey_py[i,:] = e0_1d
|
||||
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
elif mesh.dim == 3:
|
||||
# Setup x (east) polarization (_x)
|
||||
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||
# Assign the source to ex_x
|
||||
for i in np.arange(mesh.vnEx[0]):
|
||||
for j in np.arange(mesh.vnEx[1]):
|
||||
ex_px[i,j,:] = -e0_1d
|
||||
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||
# Setup y (north) polarization (_py)
|
||||
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||
# Assign the source to ey_py
|
||||
for i in np.arange(mesh.vnEy[0]):
|
||||
for j in np.arange(mesh.vnEy[1]):
|
||||
ey_py[i,j,:] = e0_1d
|
||||
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
|
||||
# Return the electric fields
|
||||
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||
return eBG_bp
|
||||
|
||||
def analytic1DModelSource(mesh,freq,sigma_1d):
|
||||
'''
|
||||
Function that calculates and return background fields
|
||||
|
||||
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||
:param float freq: The frequency to solve at
|
||||
:param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||
:rtype: numpy.ndarray (mesh.nE,2)
|
||||
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||
|
||||
'''
|
||||
# import
|
||||
from SimPEG.MT.Utils import getEHfields
|
||||
# Get a 1d solution for a halfspace background
|
||||
if mesh.dim == 1:
|
||||
mesh1d = mesh
|
||||
elif mesh.dim == 2:
|
||||
mesh1d = simpeg.Mesh.TensorMesh([mesh.hy],np.array([mesh.x0[1]]))
|
||||
elif mesh.dim == 3:
|
||||
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||
|
||||
# # Note: Everything is using e^iwt
|
||||
Eu, Ed, _, _ = getEHfields(mesh1d,sigma_1d,freq,mesh.vectorNz)
|
||||
# Make the fields into a dictionary of location and the fields
|
||||
e0_1d = Eu+Ed
|
||||
E1dFieldDict = dict(zip(mesh.vectorNz,e0_1d))
|
||||
if mesh.dim == 1:
|
||||
eBG_px = simpeg.mkvc(e0_1d,2)
|
||||
eBG_py = -simpeg.mkvc(e0_1d,2) # added a minus to make the results in the correct quadrents.
|
||||
elif mesh.dim == 2:
|
||||
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
for i in np.arange(mesh.vnEx[0]):
|
||||
ex_px[i,:] = -e0_1d
|
||||
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px))
|
||||
# Setup y (north) polarization (_py)
|
||||
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||
# Assign the source to ey_py
|
||||
for i in np.arange(mesh.vnEy[0]):
|
||||
ey_py[i,:] = e0_1d
|
||||
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
elif mesh.dim == 3:
|
||||
# Setup x (east) polarization (_x)
|
||||
ex_px = -np.array([E1dFieldDict[i] for i in mesh.gridEx[:,2]]).reshape(-1,1)
|
||||
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||
# Construct the full fields
|
||||
eBG_px = np.vstack((ex_px,ey_px,ez_px))
|
||||
# Setup y (north) polarization (_py)
|
||||
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
ey_py = np.array([E1dFieldDict[i] for i in mesh.gridEy[:,2]]).reshape(-1,1)
|
||||
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||
# Construct the full fields
|
||||
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
|
||||
# Return the electric fields
|
||||
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||
return eBG_bp
|
||||
|
||||
# def homo3DModelSource(mesh,model,freq):
|
||||
# '''
|
||||
# Function that estimates 1D analytic background fields from a 3D model.
|
||||
|
||||
# :param Simpeg mesh object mesh: Holds information on the discretization
|
||||
# :param float freq: The frequency to solve at
|
||||
# :param np.array sigma_1d: Background model of conductivity to base the calculations on, 1d model.
|
||||
# :rtype: numpy.ndarray (mesh.nE,2)
|
||||
# :return: eBG_bp, E fields for the background model at both polarizations.
|
||||
|
||||
# '''
|
||||
|
||||
# if mesh.dim < 3:
|
||||
# raise IOError('Input mesh has to have 3 dimensions.')
|
||||
|
||||
|
||||
# # Get the locations
|
||||
# a = mesh.gridCC[:,0:2].copy()
|
||||
# unixy = np.unique(a.view(a.dtype.descr * a.shape[1])).view(float).reshape(-1,2)
|
||||
# uniz = np.unique(mesh.gridCC[:,2])
|
||||
# # # Note: Everything is using e^iwt
|
||||
# # Need to loop thourgh the xy locations, assess the model and calculate the fields at the phusdo cell centers.
|
||||
# # Then interpolate the cc fields to the edges.
|
||||
|
||||
# e0_1d = get1DEfields(mesh1d,sigma_1d,freq)
|
||||
|
||||
# elif mesh.dim == 3:
|
||||
# # Setup x (east) polarization (_x)
|
||||
# ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||
# ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
# ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||
# # Assign the source to ex_x
|
||||
# for i in np.arange(mesh.vnEx[0]):
|
||||
# for j in np.arange(mesh.vnEx[1]):
|
||||
# ex_px[i,j,:] = -e0_1d
|
||||
# eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||
# # Setup y (north) polarization (_py)
|
||||
# ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
# ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||
# ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||
# # Assign the source to ey_py
|
||||
# for i in np.arange(mesh.vnEy[0]):
|
||||
# for j in np.arange(mesh.vnEy[1]):
|
||||
# ey_py[i,j,:] = e0_1d
|
||||
# # ey_py[1:-1,1:-1,1:-1] = 0
|
||||
# eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
|
||||
# # Return the electric fields
|
||||
# eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||
# return eBG_bp
|
||||
@@ -0,0 +1,46 @@
|
||||
import SimPEG as simpeg, numpy as np
|
||||
|
||||
def homo1DModelSource(mesh,freq,m_back):
|
||||
'''
|
||||
Function that calculates and return background fields for a 3D mesh and model.
|
||||
The calculuations use 1D field solution for a vertical slice throught model (south-western most column),
|
||||
which is assigned at the fields everywhere for the respective polarizations.2
|
||||
|
||||
:param Simpeg mesh object mesh: Holds information on the discretization
|
||||
:param float freq: The frequency to solve at
|
||||
:param np.array m_back: Background model of conductivity to base the calculations on.
|
||||
:rtype: numpy.ndarray (mesh.nE,2)
|
||||
:return: eBG_bp, E fields for the background model at both polarizations.
|
||||
|
||||
'''
|
||||
|
||||
# import
|
||||
from SimPEG.MT.Utils import get1DEfields
|
||||
# Get a 1d solution for a halfspace background
|
||||
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
|
||||
# Note: Everything is using e^iwt
|
||||
e0_1d = get1DEfields(mesh1d,mesh.r(m_back,'CC','CC','M')[0,0,:],freq)
|
||||
# Setup x (east) polarization (_x)
|
||||
ex_px = np.zeros(mesh.vnEx,dtype=complex)
|
||||
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
|
||||
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
|
||||
# Assign the source to ex_x
|
||||
for i in np.arange(mesh.vnEx[0]):
|
||||
for j in np.arange(mesh.vnEx[1]):
|
||||
ex_px[i,j,:] = -e0_1d
|
||||
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
|
||||
# Setup y (north) polarization (_py)
|
||||
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
|
||||
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
|
||||
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
|
||||
# Assign the source to ey_py
|
||||
|
||||
for i in np.arange(mesh.vnEy[0]):
|
||||
for j in np.arange(mesh.vnEy[1]):
|
||||
ey_py[i,j,:] = e0_1d
|
||||
# ey_py[1:-1,1:-1,1:-1] = 0
|
||||
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
|
||||
|
||||
# Return the electric fields
|
||||
eBG_bp = np.hstack((eBG_px,eBG_py))
|
||||
return eBG_bp
|
||||
@@ -0,0 +1,5 @@
|
||||
import Utils
|
||||
from SurveyMT import Rx, Survey, Data
|
||||
from FieldsMT import Fields1D_e, Fields3D_e
|
||||
import Problem1D, Problem2D, Problem3D
|
||||
import SrcMT
|
||||
+95
-53
@@ -4,27 +4,32 @@ from Tests import checkDerivative
|
||||
from PropMaps import PropMap, Property
|
||||
from numpy.polynomial import polynomial
|
||||
from scipy.interpolate import UnivariateSpline
|
||||
import warnings
|
||||
|
||||
class IdentityMap(object):
|
||||
"""
|
||||
SimPEG Map
|
||||
|
||||
"""
|
||||
|
||||
__metaclass__ = Utils.SimPEGMetaClass
|
||||
|
||||
mesh = None #: A SimPEG Mesh
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
def __init__(self, mesh=None, nP=None, **kwargs):
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
if nP is not None:
|
||||
assert type(nP) in [int, long], ' Number of parameters must be an integer.'
|
||||
|
||||
self.mesh = mesh
|
||||
self._nP = nP
|
||||
|
||||
@property
|
||||
def nP(self):
|
||||
"""
|
||||
:rtype: int
|
||||
:return: number of parameters in the model
|
||||
:return: number of parameters that the mapping accepts
|
||||
"""
|
||||
if self._nP is not None:
|
||||
return self._nP
|
||||
if self.mesh is None:
|
||||
return '*'
|
||||
return self.mesh.nC
|
||||
@@ -32,11 +37,15 @@ class IdentityMap(object):
|
||||
@property
|
||||
def shape(self):
|
||||
"""
|
||||
The default shape is (mesh.nC, nP).
|
||||
The default shape is (mesh.nC, nP) if the mesh is defined.
|
||||
If this is a meshless mapping (i.e. nP is defined independently)
|
||||
the shape will be the the shape (nP,nP).
|
||||
|
||||
:rtype: (int,int)
|
||||
:return: shape of the operator as a tuple
|
||||
"""
|
||||
if self._nP is not None:
|
||||
return (self.nP, self.nP)
|
||||
if self.mesh is None:
|
||||
return ('*', self.nP)
|
||||
return (self.mesh.nC, self.nP)
|
||||
@@ -118,6 +127,7 @@ class IdentityMap(object):
|
||||
def __str__(self):
|
||||
return "%s(%s,%s)" % (self.__class__.__name__, self.shape[0], self.shape[1])
|
||||
|
||||
|
||||
class ComboMap(IdentityMap):
|
||||
"""Combination of various maps."""
|
||||
|
||||
@@ -287,11 +297,11 @@ class LogMap(IdentityMap):
|
||||
def inverse(self, m):
|
||||
return np.exp(Utils.mkvc(m))
|
||||
|
||||
class FullMap(IdentityMap):
|
||||
class SurjectFull(IdentityMap):
|
||||
"""
|
||||
FullMap
|
||||
SurjectFull
|
||||
|
||||
Given a scalar, the FullMap maps the value to the
|
||||
Given a scalar, the SurjectFull maps the value to the
|
||||
full model space.
|
||||
"""
|
||||
|
||||
@@ -318,9 +328,15 @@ class FullMap(IdentityMap):
|
||||
"""
|
||||
return np.ones([self.mesh.nC,1])
|
||||
|
||||
class FullMap(SurjectFull):
|
||||
def __init__(self,mesh,**kwargs):
|
||||
warnings.warn(
|
||||
"`FullMap` is deprecated and will be removed in future versions. Use `SurjectFull` instead",
|
||||
FutureWarning)
|
||||
SurjectFull.__init__(self,mesh,**kwargs)
|
||||
|
||||
class Vertical1DMap(IdentityMap):
|
||||
"""Vertical1DMap
|
||||
class SurjectVertical1D(IdentityMap):
|
||||
"""SurjectVertical1DMap
|
||||
|
||||
Given a 1D vector through the last dimension
|
||||
of the mesh, this will extend to the full
|
||||
@@ -360,8 +376,14 @@ class Vertical1DMap(IdentityMap):
|
||||
), shape=(repNum, 1))
|
||||
return sp.kron(sp.identity(self.nP), repVec)
|
||||
|
||||
class Vertical1DMap(SurjectVertical1D):
|
||||
def __init__(self,mesh,**kwargs):
|
||||
warnings.warn(
|
||||
"`Vertical1DMap` is deprecated and will be removed in future versions. Use `SurjectVertical1D` instead",
|
||||
FutureWarning)
|
||||
SurjectVertical1D.__init__(self,mesh,**kwargs)
|
||||
|
||||
class Map2Dto3D(IdentityMap):
|
||||
class Surject2Dto3D(IdentityMap):
|
||||
"""Map2Dto3D
|
||||
|
||||
Given a 2D vector, this will extend to the full
|
||||
@@ -416,6 +438,13 @@ class Map2Dto3D(IdentityMap):
|
||||
), shape=(nC, nP))
|
||||
return P
|
||||
|
||||
class Map2Dto3D(Surject2Dto3D):
|
||||
def __init__(self,mesh,**kwargs):
|
||||
warnings.warn(
|
||||
"`Map2Dto3D` is deprecated and will be removed in future versions. Use `Surject2Dto3D` instead",
|
||||
FutureWarning)
|
||||
Surject2Dto3D.__init__(self,mesh,**kwargs)
|
||||
|
||||
class Mesh2Mesh(IdentityMap):
|
||||
"""
|
||||
Takes a model on one mesh are translates it to another mesh.
|
||||
@@ -449,7 +478,7 @@ class Mesh2Mesh(IdentityMap):
|
||||
return self.P
|
||||
|
||||
|
||||
class ActiveCells(IdentityMap):
|
||||
class InjectActiveCells(IdentityMap):
|
||||
"""
|
||||
Active model parameters.
|
||||
|
||||
@@ -475,7 +504,7 @@ class ActiveCells(IdentityMap):
|
||||
else:
|
||||
self.valInactive = valInactive.copy()
|
||||
self.valInactive[self.indActive] = 0
|
||||
|
||||
|
||||
inds = np.nonzero(self.indActive)[0]
|
||||
self.P = sp.csr_matrix((np.ones(inds.size),(inds, range(inds.size))), shape=(self.nC, self.nP))
|
||||
|
||||
@@ -497,7 +526,14 @@ class ActiveCells(IdentityMap):
|
||||
def deriv(self, m):
|
||||
return self.P
|
||||
|
||||
class ActiveCellsTopo(IdentityMap):
|
||||
class ActiveCells(InjectActiveCells):
|
||||
def __init__(self, mesh, indActive, valInactive, nC=None):
|
||||
warnings.warn(
|
||||
"`ActiveCells` is deprecated and will be removed in future versions. Use `InjectActiveCells` instead",
|
||||
FutureWarning)
|
||||
InjectActiveCells.__init__(self, mesh, indActive, valInactive, nC)
|
||||
|
||||
class InjectActiveCellsTopo(IdentityMap):
|
||||
"""
|
||||
Active model parameters. Extend for cells on topography to air cell (only works for tensor mesh)
|
||||
|
||||
@@ -568,6 +604,12 @@ class ActiveCellsTopo(IdentityMap):
|
||||
def deriv(self, m):
|
||||
return self.P
|
||||
|
||||
class ActiveCellsTopo(InjectActiveCellsTopo):
|
||||
def __init__(self, mesh, indActive, valInactive, nC=None):
|
||||
warnings.warn(
|
||||
"`ActiveCellsTopo` is deprecated and will be removed in future versions. Use `InjectActiveCellsTopo` instead",
|
||||
FutureWarning)
|
||||
InjectActiveCellsTopo.__init__(self, mesh, indActive, valInactive, nC)
|
||||
|
||||
class Weighting(IdentityMap):
|
||||
"""
|
||||
@@ -708,7 +750,7 @@ class PolyMap(IdentityMap):
|
||||
Parameterize the model space using a polynomials in a wholespace.
|
||||
|
||||
..math::
|
||||
|
||||
|
||||
y = \mathbf{V} c
|
||||
|
||||
Define the model as:
|
||||
@@ -752,10 +794,10 @@ class PolyMap(IdentityMap):
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
#3D
|
||||
elif self.mesh.dim == 3:
|
||||
elif self.mesh.dim == 3:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
if self.normal =='X':
|
||||
f = polynomial.polyval2d(Y, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - X
|
||||
elif self.normal =='Y':
|
||||
@@ -766,43 +808,43 @@ class PolyMap(IdentityMap):
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
else:
|
||||
raise(Exception("Only supports 2D"))
|
||||
|
||||
|
||||
|
||||
return sig1+(sig2-sig1)*(np.arctan(alpha*f)/np.pi+0.5)
|
||||
|
||||
|
||||
def deriv(self, m):
|
||||
alpha = self.slope
|
||||
sig1,sig2, c = m[0],m[1],m[2:]
|
||||
if self.logSigma:
|
||||
sig1, sig2 = np.exp(sig1), np.exp(sig2)
|
||||
#2D
|
||||
if self.mesh.dim == 2:
|
||||
if self.mesh.dim == 2:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
|
||||
if self.normal =='X':
|
||||
f = polynomial.polyval(Y, c) - X
|
||||
V = polynomial.polyvander(Y, len(c)-1)
|
||||
V = polynomial.polyvander(Y, len(c)-1)
|
||||
elif self.normal =='Y':
|
||||
f = polynomial.polyval(X, c) - Y
|
||||
V = polynomial.polyvander(X, len(c)-1)
|
||||
V = polynomial.polyvander(X, len(c)-1)
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
#3D
|
||||
elif self.mesh.dim == 3:
|
||||
elif self.mesh.dim == 3:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
|
||||
if self.normal =='X':
|
||||
f = polynomial.polyval2d(Y, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - X
|
||||
V = polynomial.polyvander2d(Y, Z, self.order)
|
||||
V = polynomial.polyvander2d(Y, Z, self.order)
|
||||
elif self.normal =='Y':
|
||||
f = polynomial.polyval2d(X, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - Y
|
||||
V = polynomial.polyvander2d(X, Z, self.order)
|
||||
V = polynomial.polyvander2d(X, Z, self.order)
|
||||
elif self.normal =='Z':
|
||||
f = polynomial.polyval2d(X, Y, c.reshape((self.order[0]+1,self.order[1]+1))) - Z
|
||||
V = polynomial.polyvander2d(X, Y, self.order)
|
||||
V = polynomial.polyvander2d(X, Y, self.order)
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
|
||||
@@ -815,16 +857,16 @@ class PolyMap(IdentityMap):
|
||||
|
||||
g3 = Utils.sdiag(alpha*(sig2-sig1)/(1.+(alpha*f)**2)/np.pi)*V
|
||||
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3])
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3])
|
||||
|
||||
class SplineMap(IdentityMap):
|
||||
|
||||
"""SplineMap
|
||||
|
||||
Parameterize the boundary of two geological units using a spline interpolation
|
||||
Parameterize the boundary of two geological units using a spline interpolation
|
||||
|
||||
..math::
|
||||
|
||||
|
||||
g = f(x)-y
|
||||
|
||||
Define the model as:
|
||||
@@ -849,7 +891,7 @@ class SplineMap(IdentityMap):
|
||||
def nP(self):
|
||||
if self.mesh.dim == 2:
|
||||
return np.size(self.pts)+2
|
||||
elif self.mesh.dim == 3:
|
||||
elif self.mesh.dim == 3:
|
||||
return np.size(self.pts)*2+2
|
||||
else:
|
||||
raise(Exception("Only supports 2D and 3D"))
|
||||
@@ -866,28 +908,28 @@ class SplineMap(IdentityMap):
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
self.spl = UnivariateSpline(self.pts, c, k=self.order, s=0)
|
||||
if self.normal =='X':
|
||||
if self.normal =='X':
|
||||
f = self.spl(Y) - X
|
||||
elif self.normal =='Y':
|
||||
f = self.spl(X) - Y
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
|
||||
# 3D:
|
||||
# Comments:
|
||||
# 3D:
|
||||
# Comments:
|
||||
# Make two spline functions and link them using linear interpolation.
|
||||
# This is not quite direct extension of 2D to 3D case
|
||||
# Using 2D interpolation is possible
|
||||
|
||||
elif self.mesh.dim == 3:
|
||||
elif self.mesh.dim == 3:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
|
||||
npts = np.size(self.pts)
|
||||
npts = np.size(self.pts)
|
||||
if np.mod(c.size, 2):
|
||||
raise(Exception("Put even points!"))
|
||||
|
||||
|
||||
self.spl = {"splb":UnivariateSpline(self.pts, c[:npts], k=self.order, s=0),
|
||||
"splt":UnivariateSpline(self.pts, c[npts:], k=self.order, s=0)}
|
||||
|
||||
@@ -902,7 +944,7 @@ class SplineMap(IdentityMap):
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
else:
|
||||
raise(Exception("Only supports 2D and 3D"))
|
||||
|
||||
|
||||
|
||||
return sig1+(sig2-sig1)*(np.arctan(alpha*f)/np.pi+0.5)
|
||||
|
||||
@@ -912,7 +954,7 @@ class SplineMap(IdentityMap):
|
||||
if self.logSigma:
|
||||
sig1, sig2 = np.exp(sig1), np.exp(sig2)
|
||||
#2D
|
||||
if self.mesh.dim == 2:
|
||||
if self.mesh.dim == 2:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
|
||||
@@ -921,9 +963,9 @@ class SplineMap(IdentityMap):
|
||||
elif self.normal =='Y':
|
||||
f = self.spl(X) - Y
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
#3D
|
||||
elif self.mesh.dim == 3:
|
||||
elif self.mesh.dim == 3:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
@@ -931,7 +973,7 @@ class SplineMap(IdentityMap):
|
||||
zb = self.ptsv[0]
|
||||
zt = self.ptsv[1]
|
||||
flines = (self.spl["splt"](Y)-self.spl["splb"](Y))*(Z-zb)/(zt-zb) + self.spl["splb"](Y)
|
||||
f = flines - X
|
||||
f = flines - X
|
||||
# elif self.normal =='Y':
|
||||
# elif self.normal =='Z':
|
||||
else:
|
||||
@@ -944,7 +986,7 @@ class SplineMap(IdentityMap):
|
||||
g1 = -(np.arctan(alpha*f)/np.pi + 0.5) + 1.0
|
||||
g2 = (np.arctan(alpha*f)/np.pi + 0.5)
|
||||
|
||||
|
||||
|
||||
if self.mesh.dim ==2:
|
||||
g3 = np.zeros((self.mesh.nC, self.npts))
|
||||
if self.normal =='Y':
|
||||
@@ -958,7 +1000,7 @@ class SplineMap(IdentityMap):
|
||||
cb = c.copy()
|
||||
dy = self.mesh.hy[ind]*1.5
|
||||
ca[i] = ctemp+dy
|
||||
cb[i] = ctemp-dy
|
||||
cb[i] = ctemp-dy
|
||||
spla = UnivariateSpline(self.pts, ca, k=self.order, s=0)
|
||||
splb = UnivariateSpline(self.pts, cb, k=self.order, s=0)
|
||||
fderiv = (spla(X)-splb(X))/(2*dy)
|
||||
@@ -968,7 +1010,7 @@ class SplineMap(IdentityMap):
|
||||
g3 = np.zeros((self.mesh.nC, self.npts*2))
|
||||
if self.normal =='X':
|
||||
# Here we use perturbation to compute sensitivity
|
||||
for i in range(self.npts*2):
|
||||
for i in range(self.npts*2):
|
||||
ctemp = c[i]
|
||||
ind = np.argmin(abs(self.mesh.vectorCCy-ctemp))
|
||||
ca = c.copy()
|
||||
@@ -982,20 +1024,20 @@ class SplineMap(IdentityMap):
|
||||
splbb = UnivariateSpline(self.pts, cb[:self.npts], k=self.order, s=0)
|
||||
flinesa = (self.spl["splt"](Y)-splba(Y))*(Z-zb)/(zt-zb) + splba(Y) - X
|
||||
flinesb = (self.spl["splt"](Y)-splbb(Y))*(Z-zb)/(zt-zb) + splbb(Y) - X
|
||||
#treat top boundary
|
||||
#treat top boundary
|
||||
else:
|
||||
splta = UnivariateSpline(self.pts, ca[self.npts:], k=self.order, s=0)
|
||||
spltb = UnivariateSpline(self.pts, ca[self.npts:], k=self.order, s=0)
|
||||
flinesa = (self.spl["splt"](Y)-splta(Y))*(Z-zb)/(zt-zb) + splta(Y) - X
|
||||
flinesb = (self.spl["splt"](Y)-spltb(Y))*(Z-zb)/(zt-zb) + spltb(Y) - X
|
||||
fderiv = (flinesa-flinesb)/(2*dy)
|
||||
flinesb = (self.spl["splt"](Y)-spltb(Y))*(Z-zb)/(zt-zb) + spltb(Y) - X
|
||||
fderiv = (flinesa-flinesb)/(2*dy)
|
||||
g3[:,i] = Utils.sdiag(alpha*(sig2-sig1)/(1.+(alpha*f)**2)/np.pi)*fderiv
|
||||
else :
|
||||
raise(Exception("Not Implemented for Y and Z, your turn :)"))
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3])
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -746,4 +746,3 @@ class DiffOperators(object):
|
||||
kron3(av(n[2]), speye(n[1]+1), av(n[0])),
|
||||
kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr")
|
||||
return self._aveN2F
|
||||
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
import numpy as np, os
|
||||
from SimPEG import Utils
|
||||
|
||||
class TensorMeshIO(object):
|
||||
|
||||
@classmethod
|
||||
def readUBC(TensorMesh, fileName):
|
||||
"""
|
||||
Read UBC GIF 3DTensor mesh and generate 3D Tensor mesh in simpegTD
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF mesh file
|
||||
|
||||
Output:
|
||||
:param SimPEG TensorMesh object
|
||||
"""
|
||||
|
||||
# Interal function to read cell size lines for the UBC mesh files.
|
||||
def readCellLine(line):
|
||||
for seg in line.split():
|
||||
if '*' in seg:
|
||||
st = seg
|
||||
sp = seg.split('*')
|
||||
re = np.array(sp[0],dtype=int)*(' ' + sp[1])
|
||||
line = line.replace(st,re.strip())
|
||||
return np.array(line.split(),dtype=float)
|
||||
|
||||
# Read the file as line strings, remove lines with comment = !
|
||||
msh = np.genfromtxt(fileName,delimiter='\n',dtype=np.str,comments='!')
|
||||
|
||||
# Fist line is the size of the model
|
||||
sizeM = np.array(msh[0].split(),dtype=float)
|
||||
# Second line is the South-West-Top corner coordinates.
|
||||
x0 = np.array(msh[1].split(),dtype=float)
|
||||
# Read the cell sizes
|
||||
h1 = readCellLine(msh[2])
|
||||
h2 = readCellLine(msh[3])
|
||||
h3temp = readCellLine(msh[4])
|
||||
h3 = h3temp[::-1] # Invert the indexing of the vector to start from the bottom.
|
||||
# Adjust the reference point to the bottom south west corner
|
||||
x0[2] = x0[2] - np.sum(h3)
|
||||
# Make the mesh
|
||||
tensMsh = TensorMesh([h1,h2,h3],x0)
|
||||
return tensMsh
|
||||
|
||||
@classmethod
|
||||
def readVTK(TensorMesh, fileName):
|
||||
"""
|
||||
Read VTK Rectilinear (vtr xml file) and return SimPEG Tensor mesh and model
|
||||
|
||||
Input:
|
||||
:param vtrFileName, path to the vtr model file to write to
|
||||
|
||||
Output:
|
||||
:return SimPEG TensorMesh object
|
||||
:return SimPEG model dictionary
|
||||
|
||||
"""
|
||||
# Import
|
||||
from vtk import vtkXMLRectilinearGridReader as vtrFileReader
|
||||
from vtk.util.numpy_support import vtk_to_numpy
|
||||
|
||||
# Read the file
|
||||
vtrReader = vtrFileReader()
|
||||
vtrReader.SetFileName(fileName)
|
||||
vtrReader.Update()
|
||||
vtrGrid = vtrReader.GetOutput()
|
||||
# Sort information
|
||||
hx = np.abs(np.diff(vtk_to_numpy(vtrGrid.GetXCoordinates())))
|
||||
xR = vtk_to_numpy(vtrGrid.GetXCoordinates())[0]
|
||||
hy = np.abs(np.diff(vtk_to_numpy(vtrGrid.GetYCoordinates())))
|
||||
yR = vtk_to_numpy(vtrGrid.GetYCoordinates())[0]
|
||||
zD = np.diff(vtk_to_numpy(vtrGrid.GetZCoordinates()))
|
||||
# Check the direction of hz
|
||||
if np.all(zD < 0):
|
||||
hz = np.abs(zD[::-1])
|
||||
zR = vtk_to_numpy(vtrGrid.GetZCoordinates())[-1]
|
||||
else:
|
||||
hz = np.abs(zD)
|
||||
zR = vtk_to_numpy(vtrGrid.GetZCoordinates())[0]
|
||||
x0 = np.array([xR,yR,zR])
|
||||
|
||||
# Make the SimPEG object
|
||||
tensMsh = TensorMesh([hx,hy,hz],x0)
|
||||
|
||||
# Grap the models
|
||||
models = {}
|
||||
for i in np.arange(vtrGrid.GetCellData().GetNumberOfArrays()):
|
||||
modelName = vtrGrid.GetCellData().GetArrayName(i)
|
||||
if np.all(zD < 0):
|
||||
modFlip = vtk_to_numpy(vtrGrid.GetCellData().GetArray(i))
|
||||
tM = tensMsh.r(modFlip,'CC','CC','M')
|
||||
modArr = tensMsh.r(tM[:,:,::-1],'CC','CC','V')
|
||||
else:
|
||||
modArr = vtk_to_numpy(vtrGrid.GetCellData().GetArray(i))
|
||||
models[modelName] = modArr
|
||||
|
||||
# Return the data
|
||||
return tensMsh, models
|
||||
|
||||
def writeVTK(mesh, fileName, models=None):
|
||||
"""
|
||||
Makes and saves a VTK rectilinear file (vtr) for a simpeg Tensor mesh and model.
|
||||
|
||||
Input:
|
||||
:param str, path to the output vtk file
|
||||
:param mesh, SimPEG TensorMesh object - mesh to be transfer to VTK
|
||||
:param models, dictionary of numpy.array - Name('s) and array('s). Match number of cells
|
||||
|
||||
"""
|
||||
# Import
|
||||
from vtk import vtkRectilinearGrid as rectGrid, vtkXMLRectilinearGridWriter as rectWriter, VTK_VERSION
|
||||
from vtk.util.numpy_support import numpy_to_vtk
|
||||
|
||||
# Deal with dimensionalities
|
||||
if mesh.dim >= 1:
|
||||
vX = mesh.vectorNx
|
||||
xD = mesh.nNx
|
||||
yD,zD = 1,1
|
||||
vY, vZ = np.array([0,0])
|
||||
if mesh.dim >= 2:
|
||||
vY = mesh.vectorNy
|
||||
yD = mesh.nNy
|
||||
if mesh.dim == 3:
|
||||
vZ = mesh.vectorNz
|
||||
zD = mesh.nNz
|
||||
# Use rectilinear VTK grid.
|
||||
# Assign the spatial information.
|
||||
vtkObj = rectGrid()
|
||||
vtkObj.SetDimensions(xD,yD,zD)
|
||||
vtkObj.SetXCoordinates(numpy_to_vtk(vX,deep=1))
|
||||
vtkObj.SetYCoordinates(numpy_to_vtk(vY,deep=1))
|
||||
vtkObj.SetZCoordinates(numpy_to_vtk(vZ,deep=1))
|
||||
|
||||
# Assign the model('s) to the object
|
||||
if models is not None:
|
||||
for item in models.iteritems():
|
||||
# Convert numpy array
|
||||
vtkDoubleArr = numpy_to_vtk(item[1],deep=1)
|
||||
vtkDoubleArr.SetName(item[0])
|
||||
vtkObj.GetCellData().AddArray(vtkDoubleArr)
|
||||
# Set the active scalar
|
||||
vtkObj.GetCellData().SetActiveScalars(models.keys()[0])
|
||||
# vtkObj.Update()
|
||||
|
||||
# Check the extension of the fileName
|
||||
ext = os.path.splitext(fileName)[1]
|
||||
if ext is '':
|
||||
fileName = fileName + '.vtr'
|
||||
elif ext not in '.vtr':
|
||||
raise IOError('{:s} is an incorrect extension, has to be .vtr')
|
||||
# Write the file.
|
||||
vtrWriteFilter = rectWriter()
|
||||
if float(VTK_VERSION.split('.')[0]) >=6:
|
||||
vtrWriteFilter.SetInputData(vtkObj)
|
||||
else:
|
||||
vtuWriteFilter.SetInput(vtuObj)
|
||||
vtrWriteFilter.SetFileName(fileName)
|
||||
vtrWriteFilter.Update()
|
||||
|
||||
|
||||
def readModelUBC(mesh, fileName):
|
||||
"""
|
||||
Read UBC 3DTensor mesh model and generate 3D Tensor mesh model in simpeg
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF mesh file to read
|
||||
:param mesh, TensorMesh object, mesh that coresponds to the model
|
||||
|
||||
Output:
|
||||
:return numpy array, model with TensorMesh ordered
|
||||
"""
|
||||
f = open(fileName, 'r')
|
||||
model = np.array(map(float, f.readlines()))
|
||||
f.close()
|
||||
model = np.reshape(model, (mesh.nCz, mesh.nCx, mesh.nCy), order = 'F')
|
||||
model = model[::-1,:,:]
|
||||
model = np.transpose(model, (1, 2, 0))
|
||||
model = Utils.mkvc(model)
|
||||
return model
|
||||
|
||||
def writeModelUBC(mesh, fileName, model):
|
||||
"""
|
||||
Writes a model associated with a SimPEG TensorMesh
|
||||
to a UBC-GIF format model file.
|
||||
|
||||
:param str fileName: File to write to
|
||||
:param simpeg.Mesh.TensorMesh mesh: The mesh
|
||||
:param numpy.ndarray model: The model
|
||||
"""
|
||||
|
||||
# Reshape model to a matrix
|
||||
modelMat = mesh.r(model,'CC','CC','M')
|
||||
# Transpose the axes
|
||||
modelMatT = modelMat.transpose((2,0,1))
|
||||
# Flip z to positive down
|
||||
modelMatTR = Utils.mkvc(modelMatT[::-1,:,:])
|
||||
|
||||
np.savetxt(fileName, modelMatTR.ravel())
|
||||
|
||||
def writeUBC(mesh, fileName, models=None):
|
||||
"""
|
||||
Writes a SimPEG TensorMesh to a UBC-GIF format mesh file.
|
||||
|
||||
:param str fileName: File to write to
|
||||
:param simpeg.Mesh.TensorMesh mesh: The mesh
|
||||
|
||||
"""
|
||||
assert mesh.dim == 3
|
||||
s = ''
|
||||
s += '%i %i %i\n' %tuple(mesh.vnC)
|
||||
origin = mesh.x0 + np.array([0,0,mesh.hz.sum()]) # Have to it in the same operation or use mesh.x0.copy(), otherwise the mesh.x0 is updated.
|
||||
origin.dtype = float
|
||||
|
||||
s += '%.2f %.2f %.2f\n' %tuple(origin)
|
||||
s += ('%.2f '*mesh.nCx+'\n')%tuple(mesh.hx)
|
||||
s += ('%.2f '*mesh.nCy+'\n')%tuple(mesh.hy)
|
||||
s += ('%.2f '*mesh.nCz+'\n')%tuple(mesh.hz[::-1])
|
||||
f = open(fileName, 'w')
|
||||
f.write(s)
|
||||
f.close()
|
||||
|
||||
if models is None: return
|
||||
assert type(models) is dict, 'models must be a dict'
|
||||
for key in models:
|
||||
assert type(key) is str, 'The dict key is a file name'
|
||||
mesh.writeModelUBC(key, models[key])
|
||||
|
||||
class TreeMeshIO(object):
|
||||
|
||||
def writeUBC(mesh, fileName, models=None):
|
||||
"""
|
||||
Write UBC ocTree mesh and model files from a simpeg ocTree mesh and model.
|
||||
|
||||
:param str fileName: File to write to
|
||||
:param simpeg.Mesh.TreeMesh mesh: The mesh
|
||||
:param dictionary models: The models in a dictionary, where the keys is the name of the of the model file
|
||||
"""
|
||||
|
||||
# Calculate information to write in the file.
|
||||
# Number of cells in the underlying mesh
|
||||
nCunderMesh = np.array([h.size for h in mesh.h],dtype=np.int64)
|
||||
# The top-south-west most corner of the mesh
|
||||
tswCorn = mesh.x0 + np.array([0,0,np.sum(mesh.h[2])])
|
||||
# Smallest cell size
|
||||
smallCell = np.array([h.min() for h in mesh.h])
|
||||
# Number of cells
|
||||
nrCells = mesh.nC
|
||||
|
||||
## Extract iformation about the cells.
|
||||
# cell pointers
|
||||
cellPointers = np.array([c._pointer for c in mesh])
|
||||
# cell with
|
||||
cellW = np.array([ mesh._levelWidth(i) for i in cellPointers[:,-1] ])
|
||||
# Need to shift the pointers to work with UBC indexing
|
||||
# UBC Octree indexes always the top-left-close (top-south-west) corner first and orders the cells in z(top-down),x,y vs x,y,z(bottom-up).
|
||||
# Shift index up by 1
|
||||
ubcCellPt = cellPointers[:,0:-1].copy() + np.array([1.,1.,1.])
|
||||
# Need reindex the z index to be from the top-left-close corner and to be from the global top.
|
||||
ubcCellPt[:,2] = ( nCunderMesh[-1] + 2) - (ubcCellPt[:,2] + cellW)
|
||||
|
||||
# Reorder the ubcCellPt
|
||||
ubcReorder = np.argsort(ubcCellPt.view(','.join(3*['float'])),axis=0,order=['f2','f1','f0'])[:,0]
|
||||
# Make a array with the pointers and the withs, that are order in the ubc ordering
|
||||
indArr = np.concatenate((ubcCellPt[ubcReorder,:],cellW[ubcReorder].reshape((-1,1)) ),axis=1)
|
||||
|
||||
## Write the UBC octree mesh file
|
||||
with open(fileName,'w') as mshOut:
|
||||
mshOut.write('{:.0f} {:.0f} {:.0f}\n'.format(nCunderMesh[0],nCunderMesh[1],nCunderMesh[2]))
|
||||
mshOut.write('{:.4f} {:.4f} {:.4f}\n'.format(tswCorn[0],tswCorn[1],tswCorn[2]))
|
||||
mshOut.write('{:.3f} {:.3f} {:.3f}\n'.format(smallCell[0],smallCell[1],smallCell[2]))
|
||||
mshOut.write('{:.0f} \n'.format(nrCells))
|
||||
np.savetxt(mshOut,indArr,fmt='%i')
|
||||
|
||||
## Print the models
|
||||
# Assign the model('s) to the object
|
||||
if models is not None:
|
||||
# indUBCvector = np.argsort(cX0[np.argsort(np.concatenate((cX0[:,0:2],cX0[:,2:3].max() - cX0[:,2:3]),axis=1).view(','.join(3*['float'])),axis=0,order=('f2','f1','f0'))[:,0]].view(','.join(3*['float'])),axis=0,order=('f2','f1','f0'))[:,0]
|
||||
for item in models.iteritems():
|
||||
# Save the data
|
||||
np.savetxt(item[0],item[1][ubcReorder],fmt='%3.5e')
|
||||
|
||||
@classmethod
|
||||
def readUBC(TreeMesh, meshFile):
|
||||
"""
|
||||
Read UBC 3D OcTree mesh and/or modelFiles
|
||||
|
||||
Input:
|
||||
:param str meshFile: path to the UBC GIF OcTree mesh file to read
|
||||
|
||||
Output:
|
||||
:return SimPEG.Mesh.TreeMesh mesh: The octree mesh
|
||||
:return list of ndarray's: models as a list of numpy array's
|
||||
"""
|
||||
|
||||
## Read the file lines
|
||||
fileLines = np.genfromtxt(meshFile,dtype=str,delimiter='\n')
|
||||
# Extract the data
|
||||
nCunderMesh = np.array(fileLines[0].split(),dtype=float)
|
||||
# I think this is the case?
|
||||
if np.unique(nCunderMesh).size >1:
|
||||
raise Exception('SimPEG TreeMeshes have the same number of cell in all directions')
|
||||
tswCorn = np.array(fileLines[1].split(),dtype=float)
|
||||
smallCell = np.array(fileLines[2].split(),dtype=float)
|
||||
nrCells = np.array(fileLines[3].split(),dtype=float)
|
||||
# Read the index array
|
||||
indArr = np.genfromtxt(fileLines[4::],dtype=np.int)
|
||||
|
||||
## Calculate simpeg parameters
|
||||
h1,h2,h3 = [np.ones(nr)*sz for nr,sz in zip(nCunderMesh,smallCell)]
|
||||
x0 = tswCorn - np.array([0,0,np.sum(h3)])
|
||||
# Need to convert the index array to a points list that complies with SimPEG TreeMesh.
|
||||
# Shift to start at 0
|
||||
simpegCellPt = indArr[:,0:-1].copy()
|
||||
simpegCellPt[:,2] = ( nCunderMesh[-1] + 2) - (simpegCellPt[:,2] + indArr[:,3])
|
||||
# Need reindex the z index to be from the bottom-left-close corner and to be from the global bottom.
|
||||
simpegCellPt = simpegCellPt - np.array([1.,1.,1.])
|
||||
|
||||
# Calculate the cell level
|
||||
simpegLevel = np.log2(np.min(nCunderMesh)) - np.log2(indArr[:,3])
|
||||
# Make a pointer matrix
|
||||
simpegPointers = np.concatenate((simpegCellPt,simpegLevel.reshape((-1,1))),axis=1)
|
||||
|
||||
## Make the tree mesh
|
||||
mesh = TreeMesh([h1,h2,h3],x0)
|
||||
mesh._cells = set([mesh._index(p) for p in simpegPointers.tolist()])
|
||||
|
||||
# Figure out the reordering
|
||||
mesh._simpegReorderUBC = np.argsort(np.array([mesh._index(i) for i in simpegPointers.tolist()]))
|
||||
# mesh._simpegReorderUBC = np.argsort((np.array([[1,1,1,-1]])*simpegPointers).view(','.join(4*['float'])),axis=0,order=['f3','f2','f1','f0'])[:,0]
|
||||
|
||||
return mesh
|
||||
|
||||
|
||||
def readModelUBC(mesh, fileName):
|
||||
"""
|
||||
Read UBC OcTree model and get vector
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF model file to read
|
||||
|
||||
Output:
|
||||
:return numpy array, OcTree model
|
||||
"""
|
||||
|
||||
if type(fileName) is list:
|
||||
out = {}
|
||||
for f in fileName:
|
||||
out[f] = mesh.readModelUBC(f)
|
||||
return out
|
||||
|
||||
assert hasattr(mesh, '_simpegReorderUBC'), 'The file must have been loaded from a UBC format.'
|
||||
assert mesh.dim == 3
|
||||
|
||||
modList = []
|
||||
modArr = np.loadtxt(fileName)
|
||||
if len(modArr.shape) == 1:
|
||||
modList.append(modArr[mesh._simpegReorderUBC])
|
||||
else:
|
||||
modList.append(modArr[mesh._simpegReorderUBC,:])
|
||||
return modList
|
||||
|
||||
def writeVTK(mesh, fileName, models=None):
|
||||
"""
|
||||
Function to write a VTU file from a SimPEG TreeMesh and model.
|
||||
"""
|
||||
import vtk
|
||||
from vtk import vtkXMLUnstructuredGridWriter as Writer, VTK_VERSION
|
||||
from vtk.util.numpy_support import numpy_to_vtk, numpy_to_vtkIdTypeArray
|
||||
|
||||
if str(type(mesh)).split()[-1][1:-2] not in 'SimPEG.Mesh.TreeMesh.TreeMesh':
|
||||
raise IOError('mesh is not a SimPEG TreeMesh.')
|
||||
|
||||
# Make the data parts for the vtu object
|
||||
# Points
|
||||
mesh.number()
|
||||
ptsMat = mesh._gridN + mesh.x0
|
||||
|
||||
vtkPts = vtk.vtkPoints()
|
||||
vtkPts.SetData(numpy_to_vtk(ptsMat,deep=True))
|
||||
# Cells
|
||||
cellConn = np.array([c.nodes for c in mesh],dtype=np.int64)
|
||||
|
||||
cellsMat = np.concatenate((np.ones((cellConn.shape[0],1),dtype=np.int64)*cellConn.shape[1],cellConn),axis=1).ravel()
|
||||
cellsArr = vtk.vtkCellArray()
|
||||
cellsArr.SetNumberOfCells(cellConn.shape[0])
|
||||
cellsArr.SetCells(cellConn.shape[0],numpy_to_vtkIdTypeArray(cellsMat,deep=True))
|
||||
|
||||
# Make the object
|
||||
vtuObj = vtk.vtkUnstructuredGrid()
|
||||
vtuObj.SetPoints(vtkPts)
|
||||
vtuObj.SetCells(vtk.VTK_VOXEL,cellsArr)
|
||||
# Add the level of refinement as a cell array
|
||||
cellSides = np.array([np.array(vtuObj.GetCell(i).GetBounds()).reshape((3,2)).dot(np.array([-1, 1])) for i in np.arange(vtuObj.GetNumberOfCells())])
|
||||
uniqueLevel, indLevel = np.unique(np.prod(cellSides,axis=1),return_inverse=True)
|
||||
refineLevelArr = numpy_to_vtk(indLevel.max() - indLevel,deep=1)
|
||||
refineLevelArr.SetName('octreeLevel')
|
||||
vtuObj.GetCellData().AddArray(refineLevelArr)
|
||||
# Assign the model('s) to the object
|
||||
if models is not None:
|
||||
for item in models.iteritems():
|
||||
# Convert numpy array
|
||||
vtkDoubleArr = numpy_to_vtk(item[1],deep=1)
|
||||
vtkDoubleArr.SetName(item[0])
|
||||
vtuObj.GetCellData().AddArray(vtkDoubleArr)
|
||||
|
||||
# Make the writer
|
||||
vtuWriteFilter = Writer()
|
||||
if float(VTK_VERSION.split('.')[0]) >=6:
|
||||
vtuWriteFilter.SetInputData(vtuObj)
|
||||
else:
|
||||
vtuWriteFilter.SetInput(vtuObj)
|
||||
vtuWriteFilter.SetFileName(fileName)
|
||||
# Write the file
|
||||
vtuWriteFilter.Update()
|
||||
|
||||
+572
-558
File diff suppressed because it is too large
Load Diff
+59
-123
@@ -100,11 +100,12 @@ except Exception, e:
|
||||
|
||||
from InnerProducts import InnerProducts
|
||||
from TensorMesh import TensorMesh, BaseTensorMesh
|
||||
from MeshIO import TreeMeshIO
|
||||
import time
|
||||
|
||||
MAX_BITS = 20
|
||||
|
||||
class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
|
||||
|
||||
_meshType = 'TREE'
|
||||
|
||||
@@ -564,15 +565,18 @@ class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
return [p - (p % mod) for p in pointer[:-1]] + [pointer[-1]-1]
|
||||
|
||||
def _cellN(self, p):
|
||||
"""Node location [x,y(,z)] of a single cell, closest to origin, given a pointer."""
|
||||
p = self._asPointer(p)
|
||||
return [hi[:p[ii]].sum() for ii, hi in enumerate(self.h)]
|
||||
|
||||
def _cellH(self, p):
|
||||
"""Widths of a single cell given a pointer."""
|
||||
p = self._asPointer(p)
|
||||
w = self._levelWidth(p[-1])
|
||||
return [hi[p[ii]:p[ii]+w].sum() for ii, hi in enumerate(self.h)]
|
||||
|
||||
def _cellC(self, p):
|
||||
"""Cell center of a single cell (without origin correction), given a pointer."""
|
||||
return (np.array(self._cellH(p))/2.0 + self._cellN(p)).tolist()
|
||||
|
||||
def _levelWidth(self, level):
|
||||
@@ -827,8 +831,10 @@ class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
def _numberCells(self, force=False):
|
||||
if not self.__dirtyCells__ and not force: return
|
||||
self._cc2i = dict()
|
||||
self._i2cc = dict()
|
||||
for ii, c in enumerate(sorted(self._cells)):
|
||||
self._cc2i[c] = ii
|
||||
self._i2cc[ii] = c
|
||||
self.__dirtyCells__ = False
|
||||
|
||||
def _numberNodes(self, force=False):
|
||||
@@ -1704,9 +1710,9 @@ class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
"Construct the averaging operator on cell faces to cell centers."
|
||||
if getattr(self, '_aveF2CC', None) is None:
|
||||
if self.dim == 2:
|
||||
self._aveF2CC = 1./self.dim*sp.hstack([self.aveFx2CC, self.aveFy2CC])
|
||||
self._aveF2CC = 1./self.dim*sp.hstack([self.aveFx2CC, self.aveFy2CC]).tocsr()
|
||||
elif self.dim == 3:
|
||||
self._aveF2CC = 1./self.dim*sp.hstack([self.aveFx2CC, self.aveFy2CC, self.aveFz2CC])
|
||||
self._aveF2CC = 1./self.dim*sp.hstack([self.aveFx2CC, self.aveFy2CC, self.aveFz2CC]).tocsr()
|
||||
return self._aveF2CC
|
||||
|
||||
@property
|
||||
@@ -1714,9 +1720,9 @@ class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
"Construct the averaging operator on cell faces to cell centers."
|
||||
if getattr(self, '_aveF2CCV', None) is None:
|
||||
if self.dim == 2:
|
||||
self._aveF2CCV = sp.block_diag([self.aveFx2CC, self.aveFy2CC])
|
||||
self._aveF2CCV = sp.block_diag([self.aveFx2CC, self.aveFy2CC]).tocsr()
|
||||
elif self.dim == 3:
|
||||
self._aveF2CCV = sp.block_diag([self.aveFx2CC, self.aveFy2CC, self.aveFz2CC])
|
||||
self._aveF2CCV = sp.block_diag([self.aveFx2CC, self.aveFy2CC, self.aveFz2CC]).tocsr()
|
||||
return self._aveF2CCV
|
||||
|
||||
@property
|
||||
@@ -2218,6 +2224,25 @@ class TreeMesh(BaseTensorMesh, InnerProducts):
|
||||
if showIt: plt.show()
|
||||
return tuple(out)
|
||||
|
||||
def __len__(self): return self.nC
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance( key, slice ) :
|
||||
#Get the start, stop, and step from the slice
|
||||
return [self[ii] for ii in xrange(*key.indices(len(self)))]
|
||||
elif isinstance( key, int ) :
|
||||
if key < 0 : #Handle negative indices
|
||||
key += len( self )
|
||||
if key >= len( self ) :
|
||||
raise IndexError, "The index (%d) is out of range."%key
|
||||
|
||||
self._numberCells() # no-op if numbered
|
||||
index = self._i2cc[key]
|
||||
pointer = self._asPointer(index)
|
||||
return Cell(self, index, pointer)
|
||||
else:
|
||||
raise TypeError, "Invalid argument type."
|
||||
|
||||
|
||||
class Cell(object):
|
||||
def __init__(self, mesh, index, pointer):
|
||||
@@ -2225,6 +2250,35 @@ class Cell(object):
|
||||
self._index = index
|
||||
self._pointer = pointer
|
||||
|
||||
@property
|
||||
def nodes(self):
|
||||
"""The node index in _gridN (this may include hanging nodes)."""
|
||||
M = self.mesh
|
||||
M._numberNodes()
|
||||
p = self._pointer
|
||||
i = self._index
|
||||
w = M._levelWidth(p[-1])
|
||||
|
||||
if M.dim == 2:
|
||||
n = [
|
||||
i,
|
||||
M._index([ p[0] + w, p[1] , p[2]]),
|
||||
M._index([ p[0] , p[1]+ w, p[2]]),
|
||||
M._index([ p[0] + w, p[1]+ w, p[2]]),
|
||||
]
|
||||
elif self.dim == 3:
|
||||
n = [
|
||||
i,
|
||||
M._index([ p[0] + w, p[1] , p[2] ,p[3]]),
|
||||
M._index([ p[0] , p[1] + w, p[2] ,p[3]]),
|
||||
M._index([ p[0] + w, p[1] + w, p[2] ,p[3]]),
|
||||
M._index([ p[0] , p[1] , p[2] + w,p[3]]),
|
||||
M._index([ p[0] + w, p[1] , p[2] + w,p[3]]),
|
||||
M._index([ p[0] , p[1] + w, p[2] + w,p[3]]),
|
||||
M._index([ p[0] + w, p[1] + w, p[2] + w,p[3]]),
|
||||
]
|
||||
return [M._n2i[_] for _ in n]
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
if getattr(self, '_center', None) is None:
|
||||
@@ -2282,121 +2336,3 @@ class NotBalancedException(TreeException):
|
||||
pass
|
||||
class CellLookUpException(TreeException):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
import matplotlib.colors as colors
|
||||
import matplotlib.cm as cmx
|
||||
|
||||
def topo(x):
|
||||
return np.sin(x*(2.*np.pi))*0.3 + 0.5
|
||||
|
||||
def function(cell):
|
||||
r = cell.center - np.array([0.5]*len(cell.center))
|
||||
dist = np.sqrt(r.dot(r))
|
||||
# dist2 = np.abs(cell.center[-1] - topo(cell.center[0]))
|
||||
|
||||
# dist = min([dist1,dist2])
|
||||
# if dist < 0.05:
|
||||
# return 5
|
||||
if dist < 0.1:
|
||||
return 5
|
||||
if dist < 0.2:
|
||||
return 4
|
||||
if dist < 0.4:
|
||||
return 3
|
||||
return 2
|
||||
|
||||
# T = TreeMesh([[(1,128)],[(1,128)],[(1,128)]],levels=7)
|
||||
# T = TreeMesh([128,128,128])
|
||||
# T = TreeMesh([64,64],levels=6)
|
||||
T = TreeMesh([4,4,4])
|
||||
# T = TreeMesh([[(1,128)],[(1,128)]],levels=7)
|
||||
# T.refine(lambda xc:2, balance=False)
|
||||
# T._index([0,0,0])
|
||||
# T._pointer(0)
|
||||
|
||||
|
||||
# tic = time.time()
|
||||
T.refine(function)#, balance=False)
|
||||
# print time.time() - tic
|
||||
# print T.nC
|
||||
T.plotSlice(np.log(T.vol))#np.random.rand(T.nC))
|
||||
|
||||
plt.show()
|
||||
blah
|
||||
|
||||
# T.plotImage(np.arange(len(T.vol)),showIt=True)
|
||||
|
||||
# print T.getFaceInnerProduct()
|
||||
# print T.gridFz
|
||||
|
||||
|
||||
# T._refineCell([8,0,1])
|
||||
# T._refineCell([8,0,2])
|
||||
# T._refineCell([12,0,2])
|
||||
# T._refineCell([8,4,2])
|
||||
# T._refineCell([6,0,3])
|
||||
# T._refineCell([8,8,1])
|
||||
# T._refineCell([0,0,0,1])
|
||||
# T.__dirty__ = True
|
||||
|
||||
|
||||
# print T.gridFx.shape[0], T.nFx
|
||||
|
||||
|
||||
|
||||
ax = plt.subplot(211)
|
||||
ax.spy(T.edgeCurl)
|
||||
|
||||
# print Mesh.TensorMesh([2,2,2]).edgeCurl.todense()
|
||||
# print T.edgeCurl.todense()
|
||||
# print Mesh.TensorMesh([2,2,2]).edgeCurl.todense() - T.edgeCurl.todense()
|
||||
# print T.gridEy - Mesh.TensorMesh([2,2,2]).gridEy
|
||||
|
||||
# print T.edge
|
||||
# T.plotGrid(ax=ax)
|
||||
|
||||
# R = deflationMatrix(T._facesX, T._hangingFx, T._fx2i)
|
||||
# print R
|
||||
|
||||
ax = plt.subplot(212)#, projection='3d')
|
||||
ax.spy(Mesh.TensorMesh([2,2,2]).edgeCurl)
|
||||
|
||||
# ax = plt.subplot(313)
|
||||
# ax.spy(T.faceDiv[:,:T.nFx] * R)
|
||||
|
||||
|
||||
# T.balance()
|
||||
# T.plotGrid(ax=ax)
|
||||
|
||||
# cx = T._getNextCell([0,0,1],direction=0,positive=True)
|
||||
# print cx
|
||||
# # print [T._asPointer(_) for _ in cx]
|
||||
# cx = T._getNextCell([8,0,3],direction=0,positive=False)
|
||||
# print T._asPointer(cx)
|
||||
# cx = T._getNextCell([8,8,1],direction=1,positive=False)
|
||||
# print cx, #[T._asPointer(_) for _ in cx]
|
||||
# cm = T._getNextCell([64,80,4],direction=0,positive=False)
|
||||
# cy = T._getNextCell([64,80,4],direction=1,positive=True)
|
||||
# cp = T._getNextCell([64,80,4],direction=1,positive=False)
|
||||
|
||||
# ax.plot( T._cellN([4,0,1])[0],T._cellN([4,0,1])[1], 'yd')
|
||||
# ax.plot( T._cellN(cx)[0],T._cellN(cx)[1], 'ys')
|
||||
# ax.plot( T._cellN(cm)[0],T._cellN(cm)[1], 'ys')
|
||||
# ax.plot( T._cellN(cy)[0],T._cellN(cy)[1], 'ys')
|
||||
# ax.plot( T._cellN(cp[0])[0],T._cellN(cp[0])[1], 'ys')
|
||||
# ax.plot( T._cellN(cp[1])[0],T._cellN(cp[1])[1], 'ys')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# print T.nN
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
+17
-2
@@ -32,8 +32,8 @@ class BaseProblem(object):
|
||||
val._assertMatchesPair(self.mapPair)
|
||||
self._mapping = val
|
||||
else:
|
||||
self._mapping = self.PropMap(val)
|
||||
|
||||
self._mapping = self.PropMap(val)
|
||||
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
assert isinstance(mesh, Mesh.BaseMesh), "mesh must be a SimPEG.Mesh object."
|
||||
@@ -213,5 +213,20 @@ class BaseTimeProblem(BaseProblem):
|
||||
if hasattr(self, '_timeMesh'):
|
||||
del self._timeMesh
|
||||
|
||||
class LinearProblem(BaseProblem):
|
||||
|
||||
surveyPair = Survey.LinearSurvey
|
||||
|
||||
def __init__(self, mesh, G, **kwargs):
|
||||
BaseProblem.__init__(self, mesh, **kwargs)
|
||||
self.G = G
|
||||
|
||||
def fields(self, m):
|
||||
return self.G.dot(m)
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
return self.G.dot(v)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
return self.G.T.dot(v)
|
||||
|
||||
|
||||
+287
-87
@@ -20,12 +20,13 @@ class BaseRegularization(object):
|
||||
mesh = None #: A SimPEG.Mesh instance.
|
||||
mref = None #: Reference model.
|
||||
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
def __init__(self, mesh, mapping=None, indActive=None, **kwargs):
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
self.mesh = mesh
|
||||
assert isinstance(mesh, Mesh.BaseMesh), "mesh must be a SimPEG.Mesh object."
|
||||
self.mapping = mapping or Maps.IdentityMap(mesh)
|
||||
self.mapping = mapping or self.mapPair(mesh)
|
||||
self.mapping._assertMatchesPair(self.mapPair)
|
||||
self.indActive = indActive
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
@@ -112,89 +113,8 @@ class BaseRegularization(object):
|
||||
return mD.T * ( self.W.T * ( self.W * ( mD * v) ) )
|
||||
|
||||
|
||||
|
||||
|
||||
class Tikhonov(BaseRegularization):
|
||||
"""**Tikhonov Regularization**
|
||||
|
||||
Here we will define regularization of a model, m, in general however, this should be thought of as (m-m_ref) but otherwise it is exactly the same:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) = \int_\Omega \\frac{\\alpha_x}{2}\left(\\frac{\partial m}{\partial x}\\right)^2 + \\frac{\\alpha_y}{2}\left(\\frac{\partial m}{\partial y}\\right)^2 \partial v
|
||||
|
||||
Our discrete gradient operator works on cell centers and gives the derivative on the cell faces, which is not where we want to be evaluating this integral. We need to average the values back to the cell-centers before we integrate. To avoid null spaces, we square first and then average. In 2D with ij notation it looks like this:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \\approx \sum_{ij} \left[\\frac{\\alpha_x}{2}\left[\left(\\frac{m_{i+1,j} - m_{i,j}}{h}\\right)^2 + \left(\\frac{m_{i,j} - m_{i-1,j}}{h}\\right)^2\\right]
|
||||
+ \\frac{\\alpha_y}{2}\left[\left(\\frac{m_{i,j+1} - m_{i,j}}{h}\\right)^2 + \left(\\frac{m_{i,j} - m_{i,j-1}}{h}\\right)^2\\right]
|
||||
\\right]h^2
|
||||
|
||||
If we let D_1 be the derivative matrix in the x direction
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{D}_1 = \mathbf{I}_2\otimes\mathbf{d}_1
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{D}_2 = \mathbf{d}_2\otimes\mathbf{I}_1
|
||||
|
||||
Where d_1 is the one dimensional derivative:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{d}_1 = \\frac{1}{h} \left[ \\begin{array}{cccc}
|
||||
-1 & 1 & & \\\\
|
||||
& \ddots & \ddots&\\\\
|
||||
& & -1 & 1\end{array} \\right]
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \\approx \mathbf{v}^\\top \left[\\frac{\\alpha_x}{2}\mathbf{A}_1 (\mathbf{D}_1 m) \odot (\mathbf{D}_1 m) + \\frac{\\alpha_y}{2}\mathbf{A}_2 (\mathbf{D}_2 m) \odot (\mathbf{D}_2 m) \\right]
|
||||
|
||||
Recall that this is really a just point wise multiplication, or a diagonal matrix times a vector. When we multiply by something in a diagonal we can interchange and it gives the same results (i.e. it is point wise)
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{a\odot b} = \\text{diag}(\mathbf{a})\mathbf{b} = \\text{diag}(\mathbf{b})\mathbf{a} = \mathbf{b\odot a}
|
||||
|
||||
and the transpose also is true (but the sizes have to make sense...):
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{a}^\\top\\text{diag}(\mathbf{b}) = \mathbf{b}^\\top\\text{diag}(\mathbf{a})
|
||||
|
||||
So R(m) can simplify to:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \\approx \mathbf{m}^\\top \left[\\frac{\\alpha_x}{2}\mathbf{D}_1^\\top \\text{diag}(\mathbf{A}_1^\\top\mathbf{v}) \mathbf{D}_1 + \\frac{\\alpha_y}{2}\mathbf{D}_2^\\top \\text{diag}(\mathbf{A}_2^\\top \mathbf{v}) \mathbf{D}_2 \\right] \mathbf{m}
|
||||
|
||||
We will define W_x as:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{W}_x = \sqrt{\\alpha_x}\\text{diag}\left(\sqrt{\mathbf{A}_1^\\top\mathbf{v}}\\right) \mathbf{D}_1
|
||||
|
||||
|
||||
And then W as a tall matrix of all of the different regularization terms:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{W} = \left[ \\begin{array}{c}
|
||||
\mathbf{W}_s\\\\
|
||||
\mathbf{W}_x\\\\
|
||||
\mathbf{W}_y\end{array} \\right]
|
||||
|
||||
Then we can write
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \\approx \\frac{1}{2}\mathbf{m^\\top W^\\top W m}
|
||||
|
||||
|
||||
"""
|
||||
"""
|
||||
smoothModel = True #: SMOOTH and SMOOTH_MOD_DIF options
|
||||
alpha_s = Utils.dependentProperty('_alpha_s', 1e-6, ['_W', '_Ws'], "Smallness weight")
|
||||
@@ -205,14 +125,18 @@ class Tikhonov(BaseRegularization):
|
||||
alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction")
|
||||
alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction")
|
||||
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
def __init__(self, mesh, mapping=None, indActive = None, **kwargs):
|
||||
BaseRegularization.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
self.indActive = indActive
|
||||
|
||||
@property
|
||||
def Ws(self):
|
||||
"""Regularization matrix Ws"""
|
||||
if getattr(self,'_Ws', None) is None:
|
||||
self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5)
|
||||
self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5)
|
||||
if self.indActive is not None:
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
self._Ws = Pac.T * self._Ws * Pac
|
||||
return self._Ws
|
||||
|
||||
@property
|
||||
@@ -221,6 +145,13 @@ class Tikhonov(BaseRegularization):
|
||||
if getattr(self, '_Wx', None) is None:
|
||||
Ave_x_vol = self.mesh.aveF2CC[:,:self.mesh.nFx].T*self.mesh.vol
|
||||
self._Wx = Utils.sdiag((Ave_x_vol*self.alpha_x)**0.5)*self.mesh.cellGradx
|
||||
|
||||
if self.indActive is not None:
|
||||
indActive_Fx = (self.mesh.aveFx2CC.T * self.indActive) == 1
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
Pafx = Utils.speye(self.mesh.nFx)[:,indActive_Fx]
|
||||
self._Wx = Pafx.T*self._Wx*Pac
|
||||
|
||||
return self._Wx
|
||||
|
||||
@property
|
||||
@@ -229,6 +160,13 @@ class Tikhonov(BaseRegularization):
|
||||
if getattr(self, '_Wy', None) is None:
|
||||
Ave_y_vol = self.mesh.aveF2CC[:,self.mesh.nFx:np.sum(self.mesh.vnF[:2])].T*self.mesh.vol
|
||||
self._Wy = Utils.sdiag((Ave_y_vol*self.alpha_y)**0.5)*self.mesh.cellGrady
|
||||
|
||||
if self.indActive is not None:
|
||||
indActive_Fy = (self.mesh.aveFy2CC.T * self.indActive) == 1
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
Pafy = Utils.speye(self.mesh.nFy)[:,indActive_Fy]
|
||||
self._Wy = Pafy.T*self._Wy*Pac
|
||||
|
||||
return self._Wy
|
||||
|
||||
@property
|
||||
@@ -237,6 +175,13 @@ class Tikhonov(BaseRegularization):
|
||||
if getattr(self, '_Wz', None) is None:
|
||||
Ave_z_vol = self.mesh.aveF2CC[:,np.sum(self.mesh.vnF[:2]):].T*self.mesh.vol
|
||||
self._Wz = Utils.sdiag((Ave_z_vol*self.alpha_z)**0.5)*self.mesh.cellGradz
|
||||
|
||||
if self.indActive is not None:
|
||||
indActive_Fz = (self.mesh.aveFz2CC.T * self.indActive) == 1
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
Pafz = Utils.speye(self.mesh.nFz)[:,indActive_Fz]
|
||||
self._Wz = Pafz.T*self._Wz*Pac
|
||||
|
||||
return self._Wz
|
||||
|
||||
@property
|
||||
@@ -244,6 +189,11 @@ class Tikhonov(BaseRegularization):
|
||||
"""Regularization matrix Wxx"""
|
||||
if getattr(self, '_Wxx', None) is None:
|
||||
self._Wxx = Utils.sdiag((self.mesh.vol*self.alpha_xx)**0.5)*self.mesh.faceDivx*self.mesh.cellGradx
|
||||
|
||||
if self.indActive is not None:
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
self._Wxx = Pac.T*self._Wxx*Pac
|
||||
|
||||
return self._Wxx
|
||||
|
||||
@property
|
||||
@@ -251,6 +201,11 @@ class Tikhonov(BaseRegularization):
|
||||
"""Regularization matrix Wyy"""
|
||||
if getattr(self, '_Wyy', None) is None:
|
||||
self._Wyy = Utils.sdiag((self.mesh.vol*self.alpha_yy)**0.5)*self.mesh.faceDivy*self.mesh.cellGrady
|
||||
|
||||
if self.indActive is not None:
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
self._Wyy = Pac.T*self._Wyy*Pac
|
||||
|
||||
return self._Wyy
|
||||
|
||||
@property
|
||||
@@ -258,6 +213,11 @@ class Tikhonov(BaseRegularization):
|
||||
"""Regularization matrix Wzz"""
|
||||
if getattr(self, '_Wzz', None) is None:
|
||||
self._Wzz = Utils.sdiag((self.mesh.vol*self.alpha_zz)**0.5)*self.mesh.faceDivz*self.mesh.cellGradz
|
||||
|
||||
if self.indActive is not None:
|
||||
Pac = Utils.speye(self.mesh.nC)[:,self.indActive]
|
||||
self._Wzz = Pac.T*self._Wzz*Pac
|
||||
|
||||
return self._Wzz
|
||||
|
||||
@property
|
||||
@@ -311,7 +271,7 @@ class Tikhonov(BaseRegularization):
|
||||
if self.smoothModel == True:
|
||||
mD1 = self.mapping.deriv(m)
|
||||
mD2 = self.mapping.deriv(m - self.mref)
|
||||
r1 = self.Wsmooth * ( self.mapping * (m))
|
||||
r1 = self.Wsmooth * ( self.mapping * (m))
|
||||
r2 = self.Ws * ( self.mapping * (m - self.mref) )
|
||||
out1 = mD1.T * ( self.Wsmooth.T * r1 )
|
||||
out2 = mD2.T * ( self.Ws.T * r2 )
|
||||
@@ -322,3 +282,243 @@ class Tikhonov(BaseRegularization):
|
||||
out = mD.T * ( self.W.T * r )
|
||||
return out
|
||||
|
||||
# <<<<<<< HEAD
|
||||
|
||||
# class Simple(BaseRegularization):
|
||||
# """
|
||||
# Only for tensor mesh
|
||||
# """
|
||||
|
||||
# smoothModel = True #: SMOOTH and SMOOTH_MOD_DIF options
|
||||
# alpha_s = Utils.dependentProperty('_alpha_s', 1.0, ['_W', '_Ws'], "Smallness weight")
|
||||
# alpha_x = Utils.dependentProperty('_alpha_x', 1.0, ['_W', '_Wx'], "Weight for the first derivative in the x direction")
|
||||
# alpha_y = Utils.dependentProperty('_alpha_y', 1.0, ['_W', '_Wy'], "Weight for the first derivative in the y direction")
|
||||
# alpha_z = Utils.dependentProperty('_alpha_z', 1.0, ['_W', '_Wz'], "Weight for the first derivative in the z direction")
|
||||
# alpha_xx = Utils.dependentProperty('_alpha_xx', 0.0, ['_W', '_Wxx'], "Weight for the second derivative in the x direction")
|
||||
# alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction")
|
||||
# alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction")
|
||||
|
||||
# def __init__(self, mesh, mapping=None, **kwargs):
|
||||
# BaseRegularization.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
|
||||
|
||||
# @property
|
||||
# def Ws(self):
|
||||
# """Regularization matrix Ws"""
|
||||
# if getattr(self,'_Ws', None) is None:
|
||||
# self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s)**0.5)
|
||||
# return self._Ws
|
||||
|
||||
# @property
|
||||
# def Wx(self):
|
||||
# """Regularization matrix Wx"""
|
||||
# if getattr(self, '_Wx', None) is None:
|
||||
# self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x)**0.5)*self.mesh.unitCellGradx
|
||||
# return self._Wx
|
||||
|
||||
# @property
|
||||
# def Wy(self):
|
||||
# """Regularization matrix Wy"""
|
||||
# if getattr(self, '_Wy', None) is None:
|
||||
# self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y)**0.5)*self.mesh.unitCellGrady
|
||||
# return self._Wy
|
||||
|
||||
# @property
|
||||
# def Wz(self):
|
||||
# """Regularization matrix Wz"""
|
||||
# if getattr(self, '_Wz', None) is None:
|
||||
# self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z)**0.5)*self.mesh.unitCellGradz
|
||||
# return self._Wz
|
||||
|
||||
# @property
|
||||
# def Wxx(self):
|
||||
# """Regularization matrix Wxx"""
|
||||
# if getattr(self, '_Wxx', None) is None:
|
||||
# self._Wxx = Utils.sdiag((self.mesh.vol*self.alpha_xx)**0.5)*self.mesh.faceDivx*self.mesh.cellGradx
|
||||
# return self._Wxx
|
||||
|
||||
# @property
|
||||
# def Wyy(self):
|
||||
# """Regularization matrix Wyy"""
|
||||
# if getattr(self, '_Wyy', None) is None:
|
||||
# self._Wyy = Utils.sdiag((self.mesh.vol*self.alpha_yy)**0.5)*self.mesh.faceDivy*self.mesh.cellGrady
|
||||
# return self._Wyy
|
||||
|
||||
# @property
|
||||
# def Wzz(self):
|
||||
# """Regularization matrix Wzz"""
|
||||
# if getattr(self, '_Wzz', None) is None:
|
||||
# self._Wzz = Utils.sdiag((self.mesh.vol*self.alpha_zz)**0.5)*self.mesh.faceDivz*self.mesh.cellGradz
|
||||
# return self._Wzz
|
||||
|
||||
# @property
|
||||
# def Wsmooth(self):
|
||||
# """Full smoothness regularization matrix W"""
|
||||
# if getattr(self, '_Wsmooth', None) is None:
|
||||
# wlist = (self.Wx, self.Wxx)
|
||||
# if self.mesh.dim > 1:
|
||||
# wlist += (self.Wy, self.Wyy)
|
||||
# if self.mesh.dim > 2:
|
||||
# wlist += (self.Wz, self.Wzz)
|
||||
# self._Wsmooth = sp.vstack(wlist)
|
||||
# return self._Wsmooth
|
||||
|
||||
# @property
|
||||
# def W(self):
|
||||
# """Full regularization matrix W"""
|
||||
# if getattr(self, '_W', None) is None:
|
||||
# wlist = (self.Ws, self.Wsmooth)
|
||||
# self._W = sp.vstack(wlist)
|
||||
# return self._W
|
||||
|
||||
# @Utils.timeIt
|
||||
# def eval(self, m):
|
||||
# if self.smoothModel == True:
|
||||
# r1 = self.Wsmooth * ( self.mapping * (m) )
|
||||
# r2 = self.Ws * ( self.mapping * (m - self.mref) )
|
||||
# return 0.5*(r1.dot(r1)+r2.dot(r2))
|
||||
# elif self.smoothModel == False:
|
||||
# r = self.W * ( self.mapping * (m - self.mref) )
|
||||
# return 0.5*r.dot(r)
|
||||
|
||||
|
||||
# @Utils.timeIt
|
||||
# def evalDeriv(self, m):
|
||||
# """
|
||||
|
||||
# The regularization is:
|
||||
|
||||
# .. math::
|
||||
|
||||
# R(m) = \\frac{1}{2}\mathbf{(m-m_\\text{ref})^\\top W^\\top W(m-m_\\text{ref})}
|
||||
|
||||
# So the derivative is straight forward:
|
||||
|
||||
# .. math::
|
||||
|
||||
# R(m) = \mathbf{W^\\top W (m-m_\\text{ref})}
|
||||
|
||||
# """
|
||||
# if self.smoothModel == True:
|
||||
# mD1 = self.mapping.deriv(m)
|
||||
# mD2 = self.mapping.deriv(m - self.mref)
|
||||
# r1 = self.Wsmooth * ( self.mapping * (m))
|
||||
# r2 = self.Ws * ( self.mapping * (m - self.mref) )
|
||||
# out1 = mD1.T * ( self.Wsmooth.T * r1 )
|
||||
# out2 = mD2.T * ( self.Ws.T * r2 )
|
||||
# out = out1+out2
|
||||
# elif self.smoothModel == False:
|
||||
# mD = self.mapping.deriv(m - self.mref)
|
||||
# r = self.W * ( self.mapping * (m - self.mref) )
|
||||
# out = mD.T * ( self.W.T * r )
|
||||
# return out
|
||||
|
||||
# class SparseRegularization(Simple):
|
||||
|
||||
# eps = 1e-1
|
||||
|
||||
# m = None
|
||||
# gamma = 1.
|
||||
# p = 0.
|
||||
# qx = 2.
|
||||
# qy = 2.
|
||||
# qz = 2.
|
||||
|
||||
# def __init__(self, mesh, mapping=None, **kwargs):
|
||||
# Simple.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
|
||||
# @property
|
||||
# def Wsmooth(self):
|
||||
# """Full smoothness regularization matrix W"""
|
||||
# if getattr(self, '_Wsmooth', None) is None:
|
||||
# wlist = (self.Wx, self.Wxx)
|
||||
# if self.mesh.dim > 1:
|
||||
# wlist += (self.Wy, self.Wyy)
|
||||
# if self.mesh.dim > 2:
|
||||
# wlist += (self.Wz, self.Wzz)
|
||||
# self._Wsmooth = sp.vstack(wlist)
|
||||
# return self._Wsmooth
|
||||
|
||||
# @property
|
||||
# def W(self):
|
||||
# """Full regularization matrix W"""
|
||||
# if getattr(self, '_W', None) is None:
|
||||
# wlist = (self.Ws, self.Wsmooth)
|
||||
# self._W = sp.vstack(wlist)
|
||||
# return self._W
|
||||
|
||||
# @property
|
||||
# def Ws(self):
|
||||
# """Regularization matrix Ws"""
|
||||
# if getattr(self, 'm', None) is None:
|
||||
# self.Rs = Utils.speye(self.mesh.nC)
|
||||
|
||||
# else:
|
||||
# f_m = self.m
|
||||
# self.rs = self.R(f_m , self.p, self.eps)
|
||||
# #print "Min rs: " + str(np.max(self.rs)) + "Max rs: " + str(np.min(self.rs))
|
||||
# self.Rs = Utils.sdiag( self.rs )
|
||||
|
||||
# self._Ws = Utils.sdiag((self.mesh.vol*self.alpha_s*self.gamma)**0.5)*self.Rs
|
||||
|
||||
# return self._Ws
|
||||
|
||||
# @property
|
||||
# def Wx(self):
|
||||
# """Regularization matrix Wx"""
|
||||
|
||||
# if getattr(self, 'm', None) is None:
|
||||
# self.Rx = Utils.speye(self.mesh.unitCellGradx.shape[0])
|
||||
|
||||
# else:
|
||||
# f_m = self.mesh.unitCellGradx * self.m
|
||||
# self.rx = self.R( f_m , self.qx, self.eps)
|
||||
# self.Rx = Utils.sdiag( self.rx )
|
||||
|
||||
# if getattr(self, '_Wx', None) is None:
|
||||
# self._Wx = Utils.sdiag((self.mesh.vol*self.alpha_x*self.gamma)**0.5)*self.Rx*self.mesh.unitCellGradx
|
||||
# return self._Wx
|
||||
|
||||
# @property
|
||||
# def Wy(self):
|
||||
# """Regularization matrix Wy"""
|
||||
|
||||
# if getattr(self, 'm', None) is None:
|
||||
# self.Ry = Utils.speye(self.mesh.unitCellGrady.shape[0])
|
||||
|
||||
# else:
|
||||
# f_m = self.mesh.unitCellGrady * self.m
|
||||
# self.ry = self.R( f_m , self.qy, self.eps)
|
||||
# self.Ry = Utils.sdiag( self.ry )
|
||||
|
||||
# if getattr(self, '_Wy', None) is None:
|
||||
# self._Wy = Utils.sdiag((self.mesh.vol*self.alpha_y*self.gamma)**0.5)*self.Ry*self.mesh.unitCellGrady
|
||||
# return self._Wy
|
||||
|
||||
# @property
|
||||
# def Wz(self):
|
||||
# """Regularization matrix Wz"""
|
||||
|
||||
# if getattr(self, 'm', None) is None:
|
||||
# self.Rz = Utils.speye(self.mesh.unitCellGradz.shape[0])
|
||||
|
||||
# else:
|
||||
# f_m = self.mesh.unitCellGradz * self.m
|
||||
# self.rz = self.R( f_m , self.qz, self.eps)
|
||||
# self.Rz = Utils.sdiag( self.rz )
|
||||
|
||||
# if getattr(self, '_Wz', None) is None:
|
||||
# self._Wz = Utils.sdiag((self.mesh.vol*self.alpha_z*self.gamma)**0.5)*self.Rz*self.mesh.unitCellGradz
|
||||
# return self._Wz
|
||||
|
||||
|
||||
# def R(self, f_m , p, dec):
|
||||
|
||||
# eta = (self.eps**(1-p/2.))**0.5
|
||||
# r = eta / (f_m**2.+self.eps**2.)**((1-p/2.)/2.)
|
||||
|
||||
# return r
|
||||
# =======
|
||||
# >>>>>>> 834de582844e8e1eac95819fbe03eed55dbeb001
|
||||
|
||||
+21
-10
@@ -1,6 +1,5 @@
|
||||
import Utils, numpy as np, scipy.sparse as sp, uuid
|
||||
|
||||
|
||||
class BaseRx(object):
|
||||
"""SimPEG Receiver Object"""
|
||||
|
||||
@@ -35,7 +34,7 @@ class BaseRx(object):
|
||||
"""Number of data in the receiver."""
|
||||
return self.locs.shape[0]
|
||||
|
||||
def getP(self, mesh):
|
||||
def getP(self, mesh, projGLoc=None):
|
||||
"""
|
||||
Returns the projection matrices as a
|
||||
list for all components collected by
|
||||
@@ -48,7 +47,10 @@ class BaseRx(object):
|
||||
if mesh in self._Ps:
|
||||
return self._Ps[mesh]
|
||||
|
||||
P = mesh.getInterpolationMat(self.locs, self.projGLoc)
|
||||
if projGLoc is None:
|
||||
projGLoc = self.projGLoc
|
||||
|
||||
P = mesh.getInterpolationMat(self.locs, projGLoc)
|
||||
if self.storeProjections:
|
||||
self._Ps[mesh] = P
|
||||
return P
|
||||
@@ -205,6 +207,7 @@ class BaseSurvey(object):
|
||||
__metaclass__ = Utils.SimPEGMetaClass
|
||||
|
||||
std = None #: Estimated Standard Deviations
|
||||
eps = None #: Estimated Noise Floor
|
||||
dobs = None #: Observed data
|
||||
dtrue = None #: True data, if data is synthetic
|
||||
mtrue = None #: True model, if data is synthetic
|
||||
@@ -306,12 +309,12 @@ class BaseSurvey(object):
|
||||
Where P is a projection of the fields onto the data space.
|
||||
"""
|
||||
if u is None: u = self.prob.fields(m)
|
||||
return Utils.mkvc(self.projectFields(u))
|
||||
return Utils.mkvc(self.eval(u))
|
||||
|
||||
|
||||
@Utils.count
|
||||
def projectFields(self, u):
|
||||
"""projectFields(u)
|
||||
def eval(self, u):
|
||||
"""eval(u)
|
||||
|
||||
This function projects the fields onto the data space.
|
||||
|
||||
@@ -319,11 +322,11 @@ class BaseSurvey(object):
|
||||
|
||||
d_\\text{pred} = \mathbf{P} u(m)
|
||||
"""
|
||||
raise NotImplemented('projectFields is not yet implemented.')
|
||||
raise NotImplemented('eval is not yet implemented.')
|
||||
|
||||
@Utils.count
|
||||
def projectFieldsDeriv(self, u):
|
||||
"""projectFieldsDeriv(u)
|
||||
def evalDeriv(self, u):
|
||||
"""evalDeriv(u)
|
||||
|
||||
This function s the derivative of projects the fields onto the data space.
|
||||
|
||||
@@ -331,7 +334,7 @@ class BaseSurvey(object):
|
||||
|
||||
\\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P}
|
||||
"""
|
||||
raise NotImplemented('projectFields is not yet implemented.')
|
||||
raise NotImplemented('eval is not yet implemented.')
|
||||
|
||||
@Utils.count
|
||||
def residual(self, m, u=None):
|
||||
@@ -374,3 +377,11 @@ class BaseSurvey(object):
|
||||
self.dobs = self.dtrue+noise
|
||||
self.std = self.dobs*0 + std
|
||||
return self.dobs
|
||||
|
||||
class LinearSurvey(BaseSurvey):
|
||||
def eval(self, u):
|
||||
return u
|
||||
|
||||
@property
|
||||
def nD(self):
|
||||
return self.prob.G.shape[0]
|
||||
|
||||
@@ -118,6 +118,44 @@ def defineElipse(ccMesh, center=[0,0,0], anisotropy=[1,1,1], slope=10., theta=0.
|
||||
D = np.sqrt(np.sum(G**2,axis=1))
|
||||
return -np.arctan((D-1)*slope)*(2./np.pi)/2.+0.5
|
||||
|
||||
def getIndicesSphere(center,radius,ccMesh):
|
||||
"""
|
||||
Creates a vector containing the sphere indices in the cell centers mesh.
|
||||
Returns a tuple
|
||||
|
||||
The sphere is defined by the points
|
||||
|
||||
p0, describe the position of the center of the cell
|
||||
|
||||
r, describe the radius of the sphere.
|
||||
|
||||
ccMesh represents the cell-centered mesh
|
||||
|
||||
The points p0 must live in the the same dimensional space as the mesh.
|
||||
|
||||
"""
|
||||
|
||||
# Validation: mesh and point (p0) live in the same dimensional space
|
||||
dimMesh = np.size(ccMesh[0,:])
|
||||
assert len(center) == dimMesh, "Dimension mismatch. len(p0) != dimMesh"
|
||||
|
||||
if dimMesh == 1:
|
||||
# Define the reference points
|
||||
|
||||
ind = np.abs(center[0] - ccMesh[:,0]) < radius
|
||||
|
||||
elif dimMesh == 2:
|
||||
# Define the reference points
|
||||
|
||||
ind = np.sqrt( ( center[0] - ccMesh[:,0] )**2 + ( center[1] - ccMesh[:,1] )**2 ) < radius
|
||||
|
||||
elif dimMesh == 3:
|
||||
# Define the points
|
||||
ind = np.sqrt( ( center[0] - ccMesh[:,0] )**2 + ( center[1] - ccMesh[:,1] )**2 + ( center[2] - ccMesh[:,2] )**2 ) < radius
|
||||
|
||||
# Return a tuple
|
||||
return ind
|
||||
|
||||
def defineTwoLayers(ccMesh,depth,vals=[0,1]):
|
||||
"""
|
||||
Define a two layered model. Depth of the first layer must be specified.
|
||||
|
||||
@@ -26,7 +26,14 @@ def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6):
|
||||
|
||||
def __init__(self, A, **kwargs):
|
||||
self.A = A.tocsc()
|
||||
|
||||
self.checkAccuracy = kwargs.get("checkAccuracy", checkAccuracy)
|
||||
if kwargs.has_key("checkAccuracy"): del kwargs["checkAccuracy"]
|
||||
self.accuracyTol = kwargs.get("accuracyTol", accuracyTol)
|
||||
if kwargs.has_key("accuracyTol"): del kwargs["accuracyTol"]
|
||||
|
||||
self.kwargs = kwargs
|
||||
|
||||
if factorize:
|
||||
self.solver = fun(self.A, **kwargs)
|
||||
|
||||
@@ -57,8 +64,8 @@ def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6):
|
||||
else:
|
||||
X[:,i] = fun(self.A, b[:,i], **self.kwargs)
|
||||
|
||||
if checkAccuracy:
|
||||
_checkAccuracy(self.A, b, X, accuracyTol)
|
||||
if self.checkAccuracy:
|
||||
_checkAccuracy(self.A, b, X, self.accuracyTol)
|
||||
return X
|
||||
|
||||
def clean(self):
|
||||
@@ -81,6 +88,12 @@ def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5):
|
||||
|
||||
def __init__(self, A, **kwargs):
|
||||
self.A = A
|
||||
|
||||
self.checkAccuracy = kwargs.get("checkAccuracy", checkAccuracy)
|
||||
if kwargs.has_key("checkAccuracy"): del kwargs["checkAccuracy"]
|
||||
self.accuracyTol = kwargs.get("accuracyTol", accuracyTol)
|
||||
if kwargs.has_key("accuracyTol"): del kwargs["accuracyTol"]
|
||||
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __mul__(self, b):
|
||||
@@ -108,8 +121,8 @@ def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5):
|
||||
else:
|
||||
X[:,i] = out
|
||||
|
||||
if checkAccuracy:
|
||||
_checkAccuracy(self.A, b, X, accuracyTol)
|
||||
if self.checkAccuracy:
|
||||
_checkAccuracy(self.A, b, X, self.accuracyTol)
|
||||
return X
|
||||
|
||||
def clean(self):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from matutils import *
|
||||
from codeutils import *
|
||||
from meshutils import exampleLrmGrid, meshTensor, closestPoints, readUBCTensorMesh, writeUBCTensorMesh, writeUBCTensorModel, readVTRFile, writeVTRFile
|
||||
from meshutils import *
|
||||
from curvutils import volTetra, faceInfo, indexCube
|
||||
from interputils import interpmat
|
||||
from CounterUtils import *
|
||||
|
||||
@@ -17,7 +17,7 @@ def memProfileWrapper(towrap, *funNames):
|
||||
|
||||
For example::
|
||||
|
||||
foo_mem = memProfile(foo,'my_func')
|
||||
foo_mem = memProfileWrapper(foo,['my_func'])
|
||||
fooi = foo_mem()
|
||||
for i in range(5):
|
||||
fooi.my_func()
|
||||
|
||||
@@ -2,7 +2,6 @@ import numpy as np
|
||||
import scipy.sparse as sp
|
||||
from codeutils import isScalar
|
||||
|
||||
|
||||
def mkvc(x, numDims=1):
|
||||
"""Creates a vector with the number of dimension specified
|
||||
|
||||
@@ -26,6 +25,9 @@ def mkvc(x, numDims=1):
|
||||
if hasattr(x, 'tovec'):
|
||||
x = x.tovec()
|
||||
|
||||
if isinstance(x, Zero):
|
||||
return x
|
||||
|
||||
assert isinstance(x, np.ndarray), "Vector must be a numpy array"
|
||||
|
||||
if numDims == 1:
|
||||
@@ -37,6 +39,9 @@ def mkvc(x, numDims=1):
|
||||
|
||||
def sdiag(h):
|
||||
"""Sparse diagonal matrix"""
|
||||
if isinstance(h, Zero):
|
||||
return Zero()
|
||||
|
||||
return sp.spdiags(mkvc(h), 0, h.size, h.size, format="csr")
|
||||
|
||||
def sdInv(M):
|
||||
@@ -417,6 +422,12 @@ class Zero(object):
|
||||
def __ge__(self, v):return 0 >= v
|
||||
def __gt__(self, v):return 0 > v
|
||||
|
||||
@property
|
||||
def transpose(self): return Zero()
|
||||
|
||||
@property
|
||||
def T(self): return Zero()
|
||||
|
||||
class Identity(object):
|
||||
_positive = True
|
||||
def __init__(self, positive=True):
|
||||
|
||||
@@ -102,223 +102,6 @@ def closestPoints(mesh, pts, gridLoc='CC'):
|
||||
|
||||
return nodeInds
|
||||
|
||||
def readUBCTensorMesh(fileName):
|
||||
"""
|
||||
Read UBC GIF 3DTensor mesh and generate 3D Tensor mesh in simpegTD
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF mesh file
|
||||
|
||||
Output:
|
||||
:param SimPEG TensorMesh object
|
||||
:return
|
||||
"""
|
||||
|
||||
# Interal function to read cell size lines for the UBC mesh files.
|
||||
def readCellLine(line):
|
||||
for seg in line.split():
|
||||
if '*' in seg:
|
||||
st = seg
|
||||
sp = seg.split('*')
|
||||
re = np.array(sp[0],dtype=int)*(' ' + sp[1])
|
||||
line = line.replace(st,re.strip())
|
||||
return np.array(line.split(),dtype=float)
|
||||
|
||||
# Read the file as line strings, remove lines with comment = !
|
||||
msh = np.genfromtxt(fileName,delimiter='\n',dtype=np.str,comments='!')
|
||||
|
||||
# Fist line is the size of the model
|
||||
sizeM = np.array(msh[0].split(),dtype=float)
|
||||
# Second line is the South-West-Top corner coordinates.
|
||||
x0 = np.array(msh[1].split(),dtype=float)
|
||||
# Read the cell sizes
|
||||
h1 = readCellLine(msh[2])
|
||||
h2 = readCellLine(msh[3])
|
||||
h3temp = readCellLine(msh[4])
|
||||
h3 = h3temp[::-1] # Invert the indexing of the vector to start from the bottom.
|
||||
# Adjust the reference point to the bottom south west corner
|
||||
x0[2] = x0[2] - np.sum(h3)
|
||||
# Make the mesh
|
||||
from SimPEG import Mesh
|
||||
tensMsh = Mesh.TensorMesh([h1,h2,h3],x0)
|
||||
return tensMsh
|
||||
|
||||
def readUBCTensorModel(fileName, mesh):
|
||||
"""
|
||||
Read UBC 3DTensor mesh model and generate 3D Tensor mesh model in simpeg
|
||||
|
||||
Input:
|
||||
:param fileName, path to the UBC GIF mesh file to read
|
||||
:param mesh, TensorMesh object, mesh that coresponds to the model
|
||||
|
||||
Output:
|
||||
:return numpy array, model with TensorMesh ordered
|
||||
"""
|
||||
f = open(fileName, 'r')
|
||||
model = np.array(map(float, f.readlines()))
|
||||
f.close()
|
||||
model = np.reshape(model, (mesh.nCz, mesh.nCx, mesh.nCy), order = 'F')
|
||||
model = model[::-1,:,:]
|
||||
model = np.transpose(model, (1, 2, 0))
|
||||
model = mkvc(model)
|
||||
|
||||
return model
|
||||
|
||||
def writeUBCTensorMesh(fileName, mesh):
|
||||
"""
|
||||
Writes a SimPEG TensorMesh to a UBC-GIF format mesh file.
|
||||
|
||||
:param str fileName: File to write to
|
||||
:param simpeg.Mesh.TensorMesh mesh: The mesh
|
||||
|
||||
"""
|
||||
assert mesh.dim == 3
|
||||
s = ''
|
||||
s += '%i %i %i\n' %tuple(mesh.vnC)
|
||||
origin = mesh.x0 + np.array([0,0,mesh.hz.sum()]) # Have to it in the same operation or use mesh.x0.copy(), otherwise the mesh.x0 is updated.
|
||||
origin.dtype = float
|
||||
|
||||
s += '%.2f %.2f %.2f\n' %tuple(origin)
|
||||
s += ('%.2f '*mesh.nCx+'\n')%tuple(mesh.hx)
|
||||
s += ('%.2f '*mesh.nCy+'\n')%tuple(mesh.hy)
|
||||
s += ('%.2f '*mesh.nCz+'\n')%tuple(mesh.hz[::-1])
|
||||
f = open(fileName, 'w')
|
||||
f.write(s)
|
||||
f.close()
|
||||
|
||||
def writeUBCTensorModel(fileName, mesh, model):
|
||||
"""
|
||||
Writes a model associated with a SimPEG TensorMesh
|
||||
to a UBC-GIF format model file.
|
||||
|
||||
:param str fileName: File to write to
|
||||
:param simpeg.Mesh.TensorMesh mesh: The mesh
|
||||
:param numpy.ndarray model: The model
|
||||
"""
|
||||
|
||||
# Reshape model to a matrix
|
||||
modelMat = mesh.r(model,'CC','CC','M')
|
||||
# Transpose the axes
|
||||
modelMatT = modelMat.transpose((2,0,1))
|
||||
# Flip z to positive down
|
||||
modelMatTR = mkvc(modelMatT[::-1,:,:])
|
||||
|
||||
np.savetxt(fileName, modelMatTR.ravel())
|
||||
|
||||
|
||||
def readVTRFile(fileName):
|
||||
"""
|
||||
Read VTK Rectilinear (vtr xml file) and return SimPEG Tensor mesh and model
|
||||
|
||||
Input:
|
||||
:param vtrFileName, path to the vtr model file to write to
|
||||
|
||||
Output:
|
||||
:return SimPEG TensorMesh object
|
||||
:return SimPEG model dictionary
|
||||
|
||||
"""
|
||||
# Import
|
||||
from vtk import vtkXMLRectilinearGridReader as vtrFileReader
|
||||
from vtk.util.numpy_support import vtk_to_numpy
|
||||
|
||||
# Read the file
|
||||
vtrReader = vtrFileReader()
|
||||
vtrReader.SetFileName(fileName)
|
||||
vtrReader.Update()
|
||||
vtrGrid = vtrReader.GetOutput()
|
||||
# Sort information
|
||||
hx = np.abs(np.diff(vtk_to_numpy(vtrGrid.GetXCoordinates())))
|
||||
xR = vtk_to_numpy(vtrGrid.GetXCoordinates())[0]
|
||||
hy = np.abs(np.diff(vtk_to_numpy(vtrGrid.GetYCoordinates())))
|
||||
yR = vtk_to_numpy(vtrGrid.GetYCoordinates())[0]
|
||||
zD = np.diff(vtk_to_numpy(vtrGrid.GetZCoordinates()))
|
||||
# Check the direction of hz
|
||||
if np.all(zD < 0):
|
||||
hz = np.abs(zD[::-1])
|
||||
zR = vtk_to_numpy(vtrGrid.GetZCoordinates())[-1]
|
||||
else:
|
||||
hz = np.abs(zD)
|
||||
zR = vtk_to_numpy(vtrGrid.GetZCoordinates())[0]
|
||||
x0 = np.array([xR,yR,zR])
|
||||
|
||||
# Make the SimPEG object
|
||||
from SimPEG import Mesh
|
||||
tensMsh = Mesh.TensorMesh([hx,hy,hz],x0)
|
||||
|
||||
# Grap the models
|
||||
modelDict = {}
|
||||
for i in np.arange(vtrGrid.GetCellData().GetNumberOfArrays()):
|
||||
modelName = vtrGrid.GetCellData().GetArrayName(i)
|
||||
if np.all(zD < 0):
|
||||
modFlip = vtk_to_numpy(vtrGrid.GetCellData().GetArray(i))
|
||||
tM = tensMsh.r(modFlip,'CC','CC','M')
|
||||
modArr = tensMsh.r(tM[:,:,::-1],'CC','CC','V')
|
||||
else:
|
||||
modArr = vtk_to_numpy(vtrGrid.GetCellData().GetArray(i))
|
||||
modelDict[modelName] = modArr
|
||||
|
||||
# Return the data
|
||||
return tensMsh, modelDict
|
||||
|
||||
def writeVTRFile(fileName,mesh,model=None):
|
||||
"""
|
||||
Makes and saves a VTK rectilinear file (vtr) for a simpeg Tensor mesh and model.
|
||||
|
||||
Input:
|
||||
:param str, path to the output vtk file
|
||||
:param mesh, SimPEG TensorMesh object - mesh to be transfer to VTK
|
||||
:param model, dictionary of numpy.array - Name('s) and array('s). Match number of cells
|
||||
|
||||
"""
|
||||
# Import
|
||||
from vtk import vtkRectilinearGrid as rectGrid, vtkXMLRectilinearGridWriter as rectWriter
|
||||
from vtk.util.numpy_support import numpy_to_vtk
|
||||
|
||||
# Deal with dimensionalities
|
||||
if mesh.dim >= 1:
|
||||
vX = mesh.vectorNx
|
||||
xD = mesh.nNx
|
||||
yD,zD = 1,1
|
||||
vY, vZ = np.array([0,0])
|
||||
if mesh.dim >= 2:
|
||||
vY = mesh.vectorNy
|
||||
yD = mesh.nNy
|
||||
if mesh.dim == 3:
|
||||
vZ = mesh.vectorNz
|
||||
zD = mesh.nNz
|
||||
# Use rectilinear VTK grid.
|
||||
# Assign the spatial information.
|
||||
vtkObj = rectGrid()
|
||||
vtkObj.SetDimensions(xD,yD,zD)
|
||||
vtkObj.SetXCoordinates(numpy_to_vtk(vX,deep=1))
|
||||
vtkObj.SetYCoordinates(numpy_to_vtk(vY,deep=1))
|
||||
vtkObj.SetZCoordinates(numpy_to_vtk(vZ,deep=1))
|
||||
|
||||
# Assign the model('s) to the object
|
||||
for item in model.iteritems():
|
||||
# Convert numpy array
|
||||
vtkDoubleArr = numpy_to_vtk(item[1],deep=1)
|
||||
vtkDoubleArr.SetName(item[0])
|
||||
vtkObj.GetCellData().AddArray(vtkDoubleArr)
|
||||
# Set the active scalar
|
||||
vtkObj.GetCellData().SetActiveScalars(model.keys()[0])
|
||||
vtkObj.Update()
|
||||
|
||||
|
||||
# Check the extension of the fileName
|
||||
ext = os.path.splitext(fileName)[1]
|
||||
if ext is '':
|
||||
fileName = fileName + '.vtr'
|
||||
elif ext not in '.vtr':
|
||||
raise IOError('{:s} is an incorrect extension, has to be .vtr')
|
||||
# Write the file.
|
||||
vtrWriteFilter = rectWriter()
|
||||
vtrWriteFilter.SetInput(vtkObj)
|
||||
vtrWriteFilter.SetFileName(fileName)
|
||||
vtrWriteFilter.Update()
|
||||
|
||||
|
||||
def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
|
||||
"""
|
||||
Extracts Core Mesh from Global mesh
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import Directives
|
||||
import Inversion
|
||||
import Tests
|
||||
|
||||
__version__ = '0.1.9'
|
||||
__version__ = '0.1.10'
|
||||
__author__ = 'Rowan Cockett'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2014 Rowan Cockett'
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
+150
@@ -0,0 +1,150 @@
|
||||
.. _api_DC:
|
||||
|
||||
.. math::
|
||||
|
||||
\renewcommand{\div}{\nabla\cdot\,}
|
||||
\newcommand{\grad}{\vec \nabla}
|
||||
\newcommand{\curl}{{\vec \nabla}\times\,}
|
||||
\newcommand{\dcurl}{{\mathbf C}}
|
||||
\newcommand{\dgrad}{{\mathbf G}}
|
||||
\newcommand{\Acf}{{\mathbf A_c^f}}
|
||||
\newcommand{\Ace}{{\mathbf A_c^e}}
|
||||
\renewcommand{\S}{{\mathbf \Sigma}}
|
||||
\renewcommand{\Div}{{\mathbf {Div}}}
|
||||
\renewcommand{\Grad}{{\mathbf {Grad}}}
|
||||
\newcommand{\St}{{\mathbf \Sigma_\tau}}
|
||||
\newcommand{\diag}{\mathbf{diag}}
|
||||
\newcommand{\M}{{\mathbf M}}
|
||||
\newcommand{\Me}{{\M^e}}
|
||||
\newcommand{\Mes}[1]{{\M^e_{#1}}}
|
||||
\newcommand{\be}{\mathbf{e}}
|
||||
\newcommand{\bj}{\mathbf{j}}
|
||||
\newcommand{\bphi}{\mathbf{\phi}}
|
||||
\newcommand{\bq}{\mathbf{q}}
|
||||
\newcommand{\bJ}{\mathbf{J}}
|
||||
\newcommand{\bG}{\mathbf{G}}
|
||||
\newcommand{\bP}{\mathbf{P}}
|
||||
\newcommand{\bA}{\mathbf{A}}
|
||||
\newcommand{\bm}{\mathbf{m}}
|
||||
\newcommand{\B}{\vec{B}}
|
||||
\newcommand{\D}{\vec{D}}
|
||||
\renewcommand{\H}{\vec{H}}
|
||||
\renewcommand {\j} { {\vec j} }
|
||||
\newcommand {\h} { {\vec h} }
|
||||
\renewcommand {\b} { {\vec b} }
|
||||
\newcommand {\e} { {\vec e} }
|
||||
\newcommand {\c} { {\vec c} }
|
||||
\renewcommand {\d} { {\vec d} }
|
||||
\renewcommand {\u} { {\vec u} }
|
||||
\newcommand{\I}{\vec{I}}
|
||||
|
||||
DC resistivity survey
|
||||
*********************
|
||||
|
||||
Electrical resistivity of subsurface materials is measured by causing an electrical current to flow in the earth between one pair of electrodes while the voltage across a second pair of electrodes is measured. The result is an "apparent" resistivity which is a value representing the weighted average resistivity over a volume of the earth. Variations in this measurement are caused by variations in the soil, rock, and pore fluid electrical resistivity. Surveys require contact with the ground, so they can be labour intensive. Results are sometimes interpreted directly, but more commonly, 1D, 2D or 3D models are estimated using inversion procedures (`GPG <http://www.eos.ubc.ca/courses/eosc350/content/>`_).
|
||||
|
||||
|
||||
Background
|
||||
==========
|
||||
|
||||
As direct current (DC) implies, in DC resistivity survey, we assume steady-state. We consider Maxwell's equations in steady state as
|
||||
|
||||
.. math::
|
||||
|
||||
\curl \frac{1}{\mu} \vec{b} - \j = \j_s \\
|
||||
|
||||
\curl \e = 0
|
||||
|
||||
Then by taking \\(\\curl\\) for the first equation, we have
|
||||
|
||||
.. math::
|
||||
|
||||
- \div\j = q \\
|
||||
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\div \j_s = q = I(\delta(\vec{r}-\vec{r}_{s+})-\delta(\vec{r}-\vec{r}_{s-}))
|
||||
|
||||
Since \\(\\curl \\e = 0\\), we have
|
||||
|
||||
.. math::
|
||||
|
||||
\e = \grad \phi
|
||||
|
||||
And by Ohm's law, we have
|
||||
|
||||
.. math::
|
||||
|
||||
\j = \sigma \grad \phi
|
||||
|
||||
Finally, we can compute the solution of the system:
|
||||
|
||||
.. math::
|
||||
|
||||
- \div\j = q
|
||||
|
||||
\j = \sigma \grad \phi
|
||||
|
||||
\frac{\partial \phi}{\partial r}\Big|_{\partial \Omega_{BC}} = 0
|
||||
|
||||
|
||||
Discretization
|
||||
==============
|
||||
|
||||
By using finite volume method (FVM), we discretize our system as
|
||||
|
||||
.. math::
|
||||
|
||||
-\Div \bj = \bq
|
||||
|
||||
\diag(\Acf^{T}\sigma^{-1}) \bj = \Grad \bphi
|
||||
|
||||
Here boundary condtions are embedded in the discrete differential operators. With some linear algebra we have
|
||||
|
||||
.. math::
|
||||
|
||||
\bA\bphi = -\bq
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\bA = \Div (\diag(\Acf^{T}\sigma^{-1}))^{-1} \Grad
|
||||
|
||||
By solving this linear equation, we can compute the solution of \\(\\phi\\). Based on this discretization, we derive sensitivity in discretized space. Sensitivity matrix can be in general can be written as
|
||||
|
||||
.. math ::
|
||||
|
||||
\bJ = -\bP\bA^{-1}\bG
|
||||
|
||||
where
|
||||
|
||||
.. math ::
|
||||
|
||||
\bP: \text{Projection}
|
||||
|
||||
\bJ = \bP\frac{\partial \phi}{\partial \bm}
|
||||
|
||||
Here \\(\\bm\\) indicates model parameters in discretized space.
|
||||
|
||||
Verification
|
||||
============
|
||||
|
||||
Comparing to the analytic function:
|
||||
|
||||
.. plot::
|
||||
|
||||
import simpegDC as DC
|
||||
DC.Examples.Verification.run(plotIt=True)
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. automodule:: simpegDC.BaseDC
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
@@ -0,0 +1,19 @@
|
||||
.. _api_FiniteVolume:
|
||||
|
||||
Finite Volume
|
||||
*************
|
||||
|
||||
Any numerical implementation requires the discretization of continuous functions into discrete approximations. These approximations are typically organized in a mesh, which defines boundaries, locations, and connectivity. Of specific interest to geophysical simulations, we require that averaging, interpolation and differential operators be defined for any mesh. In SimPEG, we have implemented a staggered mimetic finite volume approach (`Hyman and Shashkov, 1999 <http://math.lanl.gov/~mac/papers/numerics/HS99B.pdf>`_). This approach requires the definitions of variables at either cell-centers, nodes, faces, or edges as seen in the figure below.
|
||||
|
||||
.. image:: images/finitevolrealestate.png
|
||||
:width: 400 px
|
||||
:alt: FiniteVolume
|
||||
:align: center
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_Mesh
|
||||
api_DiffOps
|
||||
api_InnerProducts
|
||||
@@ -61,11 +61,6 @@ If the forward problem is invertible, then we can rearrange for \\(\\frac{\\part
|
||||
This can often be computed given a vector (i.e. \\(J(v)\\)) rather than stored, as \\(J\\) is a large dense matrix.
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
u(m)
|
||||
|
||||
|
||||
|
||||
The API
|
||||
=======
|
||||
@@ -78,7 +73,7 @@ Problem
|
||||
|
||||
Survey
|
||||
------
|
||||
|
||||
.. automodule:: SimPEG.Survey
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
.. _api_Inverse:
|
||||
|
||||
|
||||
Regularization
|
||||
**************
|
||||
InvProblem
|
||||
**********
|
||||
|
||||
.. automodule:: SimPEG.Regularization
|
||||
.. automodule:: SimPEG.InvProblem
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
Optimize
|
||||
********
|
||||
Inversion
|
||||
*********
|
||||
|
||||
.. automodule:: SimPEG.Optimization
|
||||
.. automodule:: SimPEG.Inversion
|
||||
:show-inheritance:
|
||||
:private-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
@@ -27,12 +25,3 @@ Directives
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
Inversion
|
||||
*********
|
||||
|
||||
.. automodule:: SimPEG.Inversion
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Inversion Components
|
||||
********************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
api_DataMisfit
|
||||
api_Regularization
|
||||
api_Optimization
|
||||
api_Inversion
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@ the implementations.
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.Mesh_ThreeMeshes.run()
|
||||
|
||||
Examples.Mesh_Basic_Types.run()
|
||||
|
||||
|
||||
Variable Locations and Terminology
|
||||
|
||||
+10
-10
@@ -9,6 +9,15 @@ Tensor Mesh
|
||||
:undoc-members:
|
||||
|
||||
|
||||
Cylindrical Mesh
|
||||
================
|
||||
|
||||
.. automodule:: SimPEG.Mesh.CylMesh
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
Tree Mesh
|
||||
=========
|
||||
|
||||
@@ -21,16 +30,7 @@ Tree Mesh
|
||||
Curvilinear Mesh
|
||||
================
|
||||
|
||||
.. automodule:: SimPEG.Mesh.Curvilinear
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
Cylindrical Mesh
|
||||
================
|
||||
|
||||
.. automodule:: SimPEG.Mesh.CylMesh
|
||||
.. automodule:: SimPEG.Mesh.CurvilinearMesh
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
Optimize
|
||||
********
|
||||
|
||||
.. automodule:: SimPEG.Optimization
|
||||
:show-inheritance:
|
||||
:private-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
Regularization
|
||||
**************
|
||||
|
||||
If there is one model that has a misfit that equals the desired tolerance, then there are infinitely many other models which can fit to the same degree. The challenge is to find that model which has the desired characteristics and is compatible with a priori information. A single model can be selected from an infinite ensemble by measuring the length, or norm, of each model. Then a smallest, or sometimes largest, member can be isolated. Our goal is to design a norm that embodies our prior knowledge and, when minimized, yields a realistic candidate for the solution of our problem. The norm can penalize variation from a reference model, spatial derivatives of the model, or some combination of these.
|
||||
|
||||
Tikhonov Regularization
|
||||
=======================
|
||||
|
||||
Here we will define regularization of a model, m, in general however, this should be thought of as (m-m_ref) but otherwise it is exactly the same:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) = \int_\Omega \frac{\alpha_x}{2}\left(\frac{\partial m}{\partial x}\right)^2 + \frac{\alpha_y}{2}\left(\frac{\partial m}{\partial y}\right)^2 \partial v
|
||||
|
||||
Our discrete gradient operator works on cell centers and gives the derivative on the cell faces, which is not where we want to be evaluating this integral. We need to average the values back to the cell-centers before we integrate. To avoid null spaces, we square first and then average. In 2D with ij notation it looks like this:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \approx \sum_{ij} \left[\frac{\alpha_x}{2}\left[\left(\frac{m_{i+1,j} - m_{i,j}}{h}\right)^2 + \left(\frac{m_{i,j} - m_{i-1,j}}{h}\right)^2\right] \\
|
||||
+ \frac{\alpha_y}{2}\left[\left(\frac{m_{i,j+1} - m_{i,j}}{h}\right)^2 + \left(\frac{m_{i,j} - m_{i,j-1}}{h}\right)^2\right]
|
||||
\right]h^2
|
||||
|
||||
If we let D_1 be the derivative matrix in the x direction
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{D}_1 = \mathbf{I}_2\otimes\mathbf{d}_1
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{D}_2 = \mathbf{d}_2\otimes\mathbf{I}_1
|
||||
|
||||
Where d_1 is the one dimensional derivative:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{d}_1 = \frac{1}{h} \left[ \begin{array}{cccc}
|
||||
-1 & 1 & & \\
|
||||
& \ddots & \ddots&\\
|
||||
& & -1 & 1\end{array} \right]
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \approx \mathbf{v}^\top \left[\frac{\alpha_x}{2}\mathbf{A}_1 (\mathbf{D}_1 m) \odot (\mathbf{D}_1 m) + \frac{\alpha_y}{2}\mathbf{A}_2 (\mathbf{D}_2 m) \odot (\mathbf{D}_2 m) \right]
|
||||
|
||||
Recall that this is really a just point wise multiplication, or a diagonal matrix times a vector. When we multiply by something in a diagonal we can interchange and it gives the same results (i.e. it is point wise)
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{a\odot b} = \text{diag}(\mathbf{a})\mathbf{b} = \text{diag}(\mathbf{b})\mathbf{a} = \mathbf{b\odot a}
|
||||
|
||||
and the transpose also is true (but the sizes have to make sense...):
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{a}^\top\text{diag}(\mathbf{b}) = \mathbf{b}^\top\text{diag}(\mathbf{a})
|
||||
|
||||
So R(m) can simplify to:
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \approx \mathbf{m}^\top \left[\frac{\alpha_x}{2}\mathbf{D}_1^\top \text{diag}(\mathbf{A}_1^\top\mathbf{v}) \mathbf{D}_1 + \frac{\alpha_y}{2}\mathbf{D}_2^\top \text{diag}(\mathbf{A}_2^\top \mathbf{v}) \mathbf{D}_2 \right] \mathbf{m}
|
||||
|
||||
We will define W_x as:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{W}_x = \sqrt{\alpha_x}\text{diag}\left(\sqrt{\mathbf{A}_1^\top\mathbf{v}}\right) \mathbf{D}_1
|
||||
|
||||
|
||||
And then W as a tall matrix of all of the different regularization terms:
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{W} = \left[ \begin{array}{c}
|
||||
\mathbf{W}_s\\
|
||||
\mathbf{W}_x\\
|
||||
\mathbf{W}_y\end{array} \right]
|
||||
|
||||
Then we can write
|
||||
|
||||
.. math::
|
||||
|
||||
R(m) \approx \frac{1}{2}\mathbf{m^\top W^\top W m}
|
||||
|
||||
The API
|
||||
-------
|
||||
|
||||
.. autoclass:: SimPEG.Regularization.BaseRegularization
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
.. autoclass:: SimPEG.Regularization.Tikhonov
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Utilities
|
||||
*********
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_Solver
|
||||
api_Maps
|
||||
api_Utils
|
||||
api_Tests
|
||||
+3
-6
@@ -1,8 +1,5 @@
|
||||
.. _api_Utils:
|
||||
|
||||
|
||||
Utilities
|
||||
*********
|
||||
Utils
|
||||
*****
|
||||
|
||||
.. automodule:: SimPEG.Utils
|
||||
:members:
|
||||
@@ -52,7 +49,7 @@ Interpolation Utilities
|
||||
:undoc-members:
|
||||
|
||||
Counter Utilities
|
||||
=======================
|
||||
=================
|
||||
|
||||
::
|
||||
class MyClass(object):
|
||||
|
||||
+58
-6
@@ -1,17 +1,69 @@
|
||||
.. _api_license:
|
||||
|
||||
Why SimPEG?
|
||||
***********
|
||||
===========
|
||||
|
||||
Our essential functions as researchers are the pursuit and dissemination of knowledge through research and education. As scientists we
|
||||
seek to find models that reproduce the observations that we make in the world. In geophysics, we use inverse theory to mathematically
|
||||
create models of the earth from measured data. It is a difficult problem with many moving pieces: physics, discretization, simulation,
|
||||
regularization, optimization, computer science, linear algebra, geology. Exploring each of these disciplines can take a career, if you
|
||||
are so inclined, but as geophysicists we care about the combination: how to pull these disciplines together to answer our questions.
|
||||
This is the first problem we hope to help solve: to create a toolbox for the geophysicist that allows you to work at a high level and
|
||||
keep your geophysical question in focus. However, a toolbox is not enough. The research questions that we are interested in surround
|
||||
the integration of information to make better decisions.
|
||||
|
||||
We believe that the feedback loops in the geosciences could use some serious work. For example, collect multiple data-sets from the
|
||||
same field area (geology, seismic, electromagnetics, hydrogeology), process the data separately, and then reconvene with your
|
||||
multidisciplinary team. You may be rather surprised (or not) that the everyone has a (completely!?) different model. Dissonant at best,
|
||||
but often conflicting in the details. Therein lies the second problem: how do we integrate these geoscience fields? Not by force or
|
||||
even by default, but at least to have the option of quantitative communication and built in feedback loops. What we require is an
|
||||
implementation that is inherently and unequivocally modular, with all pieces available to manipulation. Black-box software, where the
|
||||
implementations are hidden, obfuscated, or difficult to manipulate, do not promote experimentation and investigation. We are working on
|
||||
a framework that exposes the details of the implementation to the geophysicist in a manner that promotes productivity and question
|
||||
based interrogation. This framework can be easily extended to encompass many geophysical problems and is built with the inverse problem
|
||||
as the fundamental goal.
|
||||
|
||||
The future we see is a mix of tools that span our disciplines, and a framework that allows us to integrate many different types of
|
||||
geophysical data so that we can communicate effectively and experiment efficiently. A toolbox combined with a framework that allows you
|
||||
to solve your own problems, and creates opportunities for us to work together to better image and understand the subsurface. What we
|
||||
are building is called SimPEG, simulation and parameter estimation in geophysics. We are building it in the open. We are testing it.
|
||||
Breaking it. Building it. Fixing it. Using it. If you believe, like we do, that geophysics can be more innovative and informative in
|
||||
the open and that these tools are necessary and invaluable in education as well as research, then you should get in touch. There is a
|
||||
lot of work to do!
|
||||
|
||||
The Big Picture
|
||||
===============
|
||||
---------------
|
||||
|
||||
Defining a well-posed inverse problem and solving it is a complex task that requires many components that must interact. It is helpful
|
||||
to view this task as a workflow in which various elements are explicitly identified and integrated. The figure below outlines the inversion components that consists of inputs, implementation, and evaluation. The inputs are composed of the geophysical data, the equations which are a mathematical description of the governing physics, and prior knowledge or assumptions about the setting. The implementation consists of two broad categories: the forward simulation and the inversion. The **forward simulation** is the means by which we solve the governing equations given a model and the **inversion components** evaluate and update this model. We are considering a gradient based approach, which updates the model through an optimization routine. The output of this implementation is a model, which, prior to interpretation, must be evaluated. This requires considering, and often re-assessing, the choices and assumptions made in both the input and implementation stages.
|
||||
|
||||
.. image:: InversionWorkflow-PreSimPEG.png
|
||||
:width: 400 px
|
||||
:alt: Components
|
||||
:align: center
|
||||
|
||||
|
||||
A Comprehensive Framework
|
||||
-------------------------
|
||||
|
||||
There are an overwhelming amount of choices to be made as one works through the forward modeling and inversion process (see figure above). As a result, software implementations of this workflow often become complex and highly interdependent, making it difficult to interact with and to ask other scientists to pick up and change. Our approach to handling this complexity is to propose a framework, (see below), that compartmentalizes the implementation of inversions into various units. We present it in this specific modular style, as each unit contains a targeted subset of choices crucial to the inversion process.
|
||||
|
||||
.. image:: InversionWorkflow.png
|
||||
:width: 400 px
|
||||
:alt: Framework
|
||||
:align: center
|
||||
|
||||
The process of obtaining an acceptable model from an inversion generally requires the geophysicist to perform several iterations of the inversion workflow, rethinking and redesigning each piece of the framework to ensure it is appropriate in the current context. Inversions are experimental and empirical by nature and our software package is designed to facilitate this iterative process. To accomplish this, we have divided the inversion methodology into eight major components (See figure above). The (:class:`SimPEG.Mesh.BaseMesh`) class handles the discretization of the earth and also provides numerical operators. The forward simulation is split into two classes, the (:class:`SimPEG.Survey.BaseSurvey`) and the (:class:`SimPEG.Problem.BaseProblem`). The (:class:`SimPEG.Survey.BaseSurvey`) class handles the geometry of a geophysical problem as well as sources. The (:class:`SimPEG.Problem.BaseProblem`) class handles the simulation of the physics for the geophysical problem of interest. Although created independently, these two classes must be paired to form all of the components necessary for a geophysical forward simulation and calculation of the sensitivity. The (:class:`SimPEG.Problem.BaseProblem`) creates geophysical fields given a source from the (:class:`SimPEG.Survey.BaseSurvey`). The (:class:`SimPEG.Survey.BaseSurvey`) interpolates these fields to the receiver locations and converts them to the appropriate data type, for example, by selecting only the measured components of the field. Each of these operations may have associated derivatives with respect to the model and the computed field; these are included in the calculation of the sensitivity. For the inversion, a (:class:`SimPEG.DataMisfit.BaseDataMisfit`) is chosen to capture the goodness of fit of the predicted data and a (:class:`SimPEG.Regularization.BaseRegularization`) is chosen to handle the non-uniqueness. These inversion elements and an Optimization routine are combined into an inverse problem class (:class:`SimPEG.InvProblem.BaseInvProblem`). (:class:`SimPEG.InvProblem.BaseInvProblem`) is the mathematical statement that will be numerically solved by running an Inversion. The (:class:`SimPEG.Inversion.BaseInversion`) class handles organization and dispatch of directives between all of the various pieces of the framework.
|
||||
|
||||
Explaining The Big Picture
|
||||
==========================
|
||||
The arrows in the figure above indicate what each class takes as a primary argument. For example, both the (:class:`SimPEG.Problem.BaseProblem`) and (:class:`SimPEG.Regularization.BaseRegularization`) classes take a (:class:`SimPEG.Mesh.BaseMesh`) class as an argument. The diagram does not show class inheritance, as each of the base classes outlined have many subtypes that can be interchanged. The (:class:`SimPEG.Mesh.BaseMesh`) class, for example, could be a regular Cartesian mesh (:class:`SimPEG.Mesh.TensorMesh`) or a cylindrical coordinate mesh (:class:`SimPEG.Mesh.CylMesh`), which have many properties in common. These common features, such as both meshes being created from tensor products, can be exploited through inheritance of base classes, and differences can be expressed through subtype polymorphism. Please look at the documentation here for more in-depth information.
|
||||
|
||||
|
||||
.. include:: ../CITATION.rst
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
.. include:: ../AUTHORS.rst
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
.. include:: ../LICENSE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.. _api_installing:
|
||||
|
||||
Installation
|
||||
************
|
||||
Getting Started with SimPEG
|
||||
***************************
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
.. _api_license:
|
||||
|
||||
License
|
||||
*******
|
||||
|
||||
.. include:: ../LICENSE
|
||||
|
||||
Authors
|
||||
*******
|
||||
|
||||
.. include:: ../AUTHORS.rst
|
||||
|
||||
|
||||
Projects Using SimPEG
|
||||
*********************
|
||||
|
||||
.. include:: ../PROJECTS.rst
|
||||
+2
-2
@@ -51,9 +51,9 @@ copyright = u'2013, SimPEG Developers'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1.9'
|
||||
version = '0.1.10'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1.9'
|
||||
release = '0.1.10'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+62
-42
@@ -19,14 +19,14 @@ Electromagnetic phenomena are governed by Maxwell's equations. They describe the
|
||||
|
||||
Fourier Transform Convention
|
||||
----------------------------
|
||||
In order to examine Maxwell's equations in the frequency domain, we must first define our choice of harmonic time-dependence by choosing a Fourier transform convention. We use the \\(e^{i \\omega t} \\) convention, so we define our Fourier Transform pair as
|
||||
In order to examine Maxwell's equations in the frequency domain, we must first define our choice of harmonic time-dependence by choosing a Fourier transform convention. We use the :math:`e^{i \omega t}` convention, so we define our Fourier Transform pair as
|
||||
|
||||
.. math ::
|
||||
F(\omega) = \int_{-\infty}^{\infty} f(t) e^{- i \omega t} dt \\
|
||||
F(\omega) = \int_{-\infty}^{\infty} f(t) e^{- i \omega t} dt \\
|
||||
|
||||
f(t) = \frac{1}{2\pi}\int_{-\infty}^{\infty} F(\omega) e^{i \omega t} d \omega
|
||||
f(t) = \frac{1}{2\pi}\int_{-\infty}^{\infty} F(\omega) e^{i \omega t} d \omega
|
||||
|
||||
where \\(\\omega\\) is angular frequency, \\(t\\) is time, \\(F(\\omega)\\) is the function defined in the frequency domain and \\(f(t)\\) is the function defined in the time domain.
|
||||
where :math:`\omega` is angular frequency, :math:`t` is time, :math:`F(\omega)` is the function defined in the frequency domain and :math:`f(t)` is the function defined in the time domain.
|
||||
|
||||
|
||||
Maxwell's Equations
|
||||
@@ -34,44 +34,46 @@ Maxwell's Equations
|
||||
In the frequency domain, Maxwell's equations are given by
|
||||
|
||||
.. math ::
|
||||
\curl \vec{E} = - i \omega \vec{B} \\
|
||||
\curl \vec{E} + i \omega \vec{B} = \vec{S_m}\\
|
||||
|
||||
\curl \vec{H} = \vec{J} + i \omega \vec{D} + \vec{S} \\
|
||||
\curl \vec{H} - \vec{J} - i \omega \vec{D} = \vec{S_e} \\
|
||||
|
||||
\div \vec{B} = 0 \\
|
||||
\div \vec{B} = 0 \\
|
||||
|
||||
\div \vec{D} = \rho_f
|
||||
\div \vec{D} = \rho_f
|
||||
|
||||
where:
|
||||
|
||||
- \\(\\vec{E}\\) : electric field (\\(V/m\\))
|
||||
- \\(\\vec{H}\\) : magnetic field (\\(A/m\\))
|
||||
- \\(\\vec{B}\\) : magnetic flux density (\\(Wb/m^2\\))
|
||||
- \\(\\vec{D}\\) : electric displacement / electric flux density (\\(C/m^2\\))
|
||||
- \\(\\vec{J}\\) : electric current density (\\(A/m^2\\))
|
||||
- \\(\\rho_f\\) : free charge density
|
||||
- :math:`\vec{E}` : electric field (:math:`V/m` )
|
||||
- :math:`\vec{H}` : magnetic field (:math:`A/m` )
|
||||
- :math:`\vec{B}` : magnetic flux density (:math:`Wb/m^2` )
|
||||
- :math:`\vec{D}` : electric displacement / electric flux density (:math:`C/m^2` )
|
||||
- :math:`\vec{J}` : electric current density (:math:`A/m^2` )
|
||||
- :math:`\vec{S_m}` : magnetic source term (:math:`V/m^2` )
|
||||
- :math:`\vec{S_e}` : electric source term (:math:`A/m^2` )
|
||||
- :math:`\rho_f` : free charge density (:math:`\Omega m` )
|
||||
|
||||
The source term is \\(\\vec{S}\\)
|
||||
|
||||
|
||||
Constitutive Relations
|
||||
----------------------
|
||||
|
||||
The fields and fluxes are related through the constitutive relations. At each frequency, they are given by
|
||||
|
||||
.. math ::
|
||||
\vec{J} = \sigma \vec{E} \\
|
||||
\vec{J} = \sigma \vec{E} \\
|
||||
|
||||
\vec{B} = \mu \vec{H} \\
|
||||
\vec{B} = \mu \vec{H} \\
|
||||
|
||||
\vec{D} = \varepsilon \vec{E}
|
||||
\vec{D} = \varepsilon \vec{E}
|
||||
|
||||
where:
|
||||
|
||||
- \\(\\sigma\\) : electrical conductivity \\(S/m\\)
|
||||
- \\(\\mu\\) : magnetic permeability \\(H/m\\)
|
||||
- \\(\\varepsilon\\) : dielectric permittivity \\(F/m\\)
|
||||
- :math:`\sigma` : electrical conductivity (:math:`S/m`)
|
||||
- :math:`\mu` : magnetic permeability (:math:`H/m`)
|
||||
- :math:`\varepsilon` : dielectric permittivity (:math:`F/m`)
|
||||
|
||||
\\(\\sigma\\), \\(\\mu\\), \\(\\varepsilon\\) are physical properties which depend on the material. \\(\\sigma\\) describes how easily electric current passes through a material, \\(\\mu\\) describes how easily a material is magnetized, and \\(\\varepsilon\\) describes how easily a material is electrically polarized. In most geophysical applications of EM, \\(\\sigma\\) is the the primary physical property of interest, and \\(\\mu\\), \\(\\varepsilon\\) are assumed to have their free-space values \\(\\mu_0 = 4\\pi \\times 10^{-7} H/m \\), \\(\\varepsilon_0 = 8.85 \\times 10^{-12} F/m\\)
|
||||
:math:`\sigma`, :math:`\mu`, :math:`\varepsilon` are physical properties which depend on the material. :math:`\sigma` describes how easily electric current passes through a material, :math:`\mu` describes how easily a material is magnetized, and :math:`\varepsilon` describes how easily a material is electrically polarized. In most geophysical applications of EM, :math:`\sigma` is the the primary physical property of interest, and :math:`\mu`, :math:`\varepsilon` are assumed to have their free-space values :math:`\mu_0 = 4\pi \times 10^{-7} H/m` , :math:`\varepsilon_0 = 8.85 \times 10^{-12} F/m`
|
||||
|
||||
|
||||
Quasi-static Approximation
|
||||
@@ -80,8 +82,8 @@ Quasi-static Approximation
|
||||
For the frequency range typical of most geophysical surveys, the contribution of the electric displacement is negligible compared to the electric current density. In this case, we use the Quasi-static approximation and assume that this term can be neglected, giving
|
||||
|
||||
.. math ::
|
||||
\nabla \times \vec{E} = -i \omega \vec{B} \\
|
||||
\nabla \times \vec{H} = \vec{J} + \vec{S}
|
||||
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S_m} \\
|
||||
\nabla \times \vec{H} - \vec{J} = \vec{S_e}
|
||||
|
||||
|
||||
Implementation in SimPEG.EM
|
||||
@@ -90,14 +92,14 @@ Implementation in SimPEG.EM
|
||||
We consider two formulations in SimPEG.EM, both first-order and both in terms of one field and one flux. We allow for the definition of magnetic and electric sources (see for example: Ward and Hohmann, starting on page 144). The E-B formulation is in terms of the electric field and the magnetic flux:
|
||||
|
||||
.. math ::
|
||||
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S}_m \\
|
||||
\nabla \times \mu^{-1} \vec{B} - \sigma \vec{E} = \vec{S}_e
|
||||
\nabla \times \vec{E} + i \omega \vec{B} = \vec{S}_m \\
|
||||
\nabla \times \mu^{-1} \vec{B} - \sigma \vec{E} = \vec{S}_e
|
||||
|
||||
The H-J formulation is in terms of the current density and the magnetic field:
|
||||
|
||||
.. math ::
|
||||
\nabla \times \sigma^{-1} \vec{J} + i \omega \mu \vec{H} = \vec{S}_m \\
|
||||
\nabla \times \vec{H} - \vec{J} = \vec{S}_e
|
||||
\nabla \times \sigma^{-1} \vec{J} + i \omega \mu \vec{H} = \vec{S}_m \\
|
||||
\nabla \times \vec{H} - \vec{J} = \vec{S}_e
|
||||
|
||||
|
||||
Discretizing
|
||||
@@ -106,34 +108,34 @@ For both formulations, we use a finite volume discretization
|
||||
and discretize fields on cell edges, fluxes on cell faces and
|
||||
physical properties in cell centers. This is particularly
|
||||
important when using symmetry to reduce the dimensionality of a problem
|
||||
(for instance on a 2D CylMesh, there are \\(r\\), \\(z\\) faces and \\(\\theta\\) edges)
|
||||
(for instance on a 2D CylMesh, there are :math:`r`, :math:`z` faces and :math:`\theta` edges)
|
||||
|
||||
.. figure:: ../images/finitevolrealestate.png
|
||||
:align: center
|
||||
:scale: 60 %
|
||||
:align: center
|
||||
:scale: 60 %
|
||||
|
||||
For the two formulations, the discretization of the physical properties, fields and fluxes are summarized below.
|
||||
|
||||
.. figure:: ../images/ebjhdiscretizations.png
|
||||
:align: center
|
||||
:scale: 60 %
|
||||
:align: center
|
||||
:scale: 60 %
|
||||
|
||||
Note that resistivity is the inverse of conductivity, \\(\\rho = \\sigma^{-1}\\).
|
||||
Note that resistivity is the inverse of conductivity, :math:`\rho = \sigma^{-1}`.
|
||||
|
||||
|
||||
E-B Formulation:
|
||||
****************
|
||||
E-B Formulation
|
||||
---------------
|
||||
|
||||
.. math ::
|
||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\
|
||||
\mathbf{C^T} \mathbf{M^f_{\mu^{-1}}} \mathbf{b} - \mathbf{M^e_\sigma} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}
|
||||
\mathbf{C} \mathbf{e} + i \omega \mathbf{b} = \mathbf{s_m} \\
|
||||
\mathbf{C^T} \mathbf{M^f_{\mu^{-1}}} \mathbf{b} - \mathbf{M^e_\sigma} \mathbf{e} = \mathbf{M^e} \mathbf{s_e}
|
||||
|
||||
H-J Formulation:
|
||||
****************
|
||||
H-J Formulation
|
||||
---------------
|
||||
|
||||
.. math ::
|
||||
\mathbf{C^T} \mathbf{M^f_\rho} \mathbf{j} + i \omega \mathbf{M^e_\mu} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\
|
||||
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||
\mathbf{C^T} \mathbf{M^f_\rho} \mathbf{j} + i \omega \mathbf{M^e_\mu} \mathbf{h} = \mathbf{M^e} \mathbf{s_m} \\
|
||||
\mathbf{C} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||
|
||||
|
||||
.. Forward Problem
|
||||
@@ -144,6 +146,10 @@ H-J Formulation:
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
FDEM Problem
|
||||
------------
|
||||
|
||||
.. automodule:: SimPEG.EM.FDEM.FDEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
@@ -157,3 +163,17 @@ FDEM Survey
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. automodule:: SimPEG.EM.FDEM.SrcFDEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
FDEM Fields
|
||||
-----------
|
||||
|
||||
.. automodule:: SimPEG.EM.FDEM.FieldsFDEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
@@ -48,6 +48,305 @@
|
||||
\newcommand{\I}{\vec{I}}
|
||||
|
||||
|
||||
Time Domain Electromagnetics
|
||||
****************************
|
||||
|
||||
.. _api_TDEM_derivation:
|
||||
|
||||
Time-Domain EM Derivation
|
||||
=========================
|
||||
|
||||
The following shows the derivation for the TDEM problem. We use the b-formulation below.
|
||||
(More to come soon..!)
|
||||
|
||||
|
||||
Sensitivity Calculation
|
||||
-----------------------
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\dcurl \e^{(t+1)} + \frac{\b^{(t+1)} - \b^{(t)}}{\delta t} = 0 \\
|
||||
\dcurl^\top \MfMui \b^{(t+1)} - \MeSig \e^{(t+1)} = \Me \j_s^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
Using Gauss-Newton to solve the inverse problem requires the ability to calculate the product of the
|
||||
Jacobian and a vector, as well as the transpose of the Jacobian times a vector.
|
||||
The above system can be rewritten as:
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{A} \u^{(t+1)} + \mathbf{B} \u^{(t)}= \s^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{A} =
|
||||
\left[
|
||||
\begin{array}{cc}
|
||||
\frac{1}{\delta t} \MfMui & \MfMui\dcurl \\
|
||||
\dcurl^\top \MfMui & -\MeSig
|
||||
\end{array}
|
||||
\right] \\
|
||||
\mathbf{B} =
|
||||
\left[
|
||||
\begin{array}{cc}
|
||||
-\frac{1}{\delta t} \MfMui & 0 \\
|
||||
0 & 0
|
||||
\end{array}
|
||||
\right] \\
|
||||
\u^{(k)} = \left[
|
||||
\begin{array}{c}
|
||||
\b^{(k)}\\
|
||||
\e^{(k)}
|
||||
\end{array}
|
||||
\right] \\
|
||||
\s^{(k)} = \left[
|
||||
\begin{array}{c}
|
||||
0\\
|
||||
\Me \j^{(k)}_s
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
.. note::
|
||||
|
||||
Here we have multiplied through by \\(\\MfMui\\) to make A and B symmetric!
|
||||
|
||||
The entire time dependent system can be written in a single matrix expression
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\hat{\mathbf{A}} \hat{u} = \hat{s}
|
||||
\end{align}
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\begin{array}{cccc}
|
||||
A & 0 & & \\
|
||||
B & A & & \\
|
||||
& \ddots & \ddots & \\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\right] \\
|
||||
\hat{u} = \left[
|
||||
\begin{array}{c}
|
||||
\u^{(1)} \\
|
||||
\u^{(2)} \\
|
||||
\vdots \\
|
||||
\u^{(N)}
|
||||
\end{array} \right]\\
|
||||
\hat{s} = \left[
|
||||
\begin{array}{c}
|
||||
\s^{(1)} - \mathbf{B} \u^{(0)} \\
|
||||
\s^{(2)} \\
|
||||
\vdots \\
|
||||
\s^{(N)}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
For the fields \\(\\u\\), the measured data is given by
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{d} = \mathbf{Q} \u
|
||||
\end{align}
|
||||
|
||||
The sensitivity matrix **J** is then defined as
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{J} = \mathbf{Q} \frac{\partial \u}{\partial \sigma}
|
||||
\end{align}
|
||||
|
||||
|
||||
Defining the function \\(\\c(m,\\u)\\) to be
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{c}(m,\u) = \hat{\mathbf{A}} \vec{u} - \vec{q} = \vec{0}
|
||||
\end{align}
|
||||
|
||||
then
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial m} \partial m
|
||||
+ \frac{\partial \vec{c}}{\partial \u} \partial \vec{u} = 0
|
||||
\end{align}
|
||||
|
||||
or
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{u}}{\partial m} = -\left(\frac{\partial \vec{c}}{\partial \u} \right)^{-1} \frac{\partial \vec{c}}{\partial m}
|
||||
\end{align}
|
||||
|
||||
|
||||
Differentiating, we find that
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial \hat{u}} = \hat{\mathbf{A}}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial \sigma} = \mathbf{G}_\sigma =
|
||||
\left[
|
||||
\begin{array}{c}
|
||||
g_\sigma^{(1)}\\
|
||||
g_\sigma^{(2)}\\
|
||||
\vdots \\
|
||||
g_\sigma^{(N)}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
with
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
g_\sigma^{(n)} =
|
||||
\left[
|
||||
\begin{array}{c}
|
||||
\mathbf{0} \\
|
||||
- \diag{\e^{(n)}} \Ace \diag{\vec{V}}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
|
||||
Implementing **J** times a vector
|
||||
---------------------------------
|
||||
|
||||
Multiplying **J** onto a vector can be broken into three steps
|
||||
|
||||
|
||||
* Compute \\(\\vec{p} = \\mathbf{G}m\\)
|
||||
* Solve \\(\\hat{\\mathbf{A}} \\vec{y} = \\vec{p}\\)
|
||||
* Compute \\(\\vec{w} = -\\mathbf{Q} \\vec{y}\\)
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{p}^{(n)} = \left[
|
||||
\begin{array}{c}
|
||||
\vec{p}_b^{(n)} \\
|
||||
\vec{p}_e^{(n)}
|
||||
\end{array}
|
||||
\right] \\
|
||||
\vec{p}_b^{(n)} = 0 \\
|
||||
\vec{p}_e^{(n)} = - \diag{\e^{(n)}} \Ace \diag{V} m
|
||||
\end{align}
|
||||
|
||||
|
||||
For all time steps:
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t+1)} + \MfMui\dcurl \vec{y}_{e}^{(t+1)}
|
||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t)}
|
||||
= \vec{p}_b^{(t+1)} \\
|
||||
\dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig \vec{y}_e^{(t+1)} = \vec{p}_e^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t+1)} =
|
||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t)}
|
||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t+1)} + \vec{p}_b^{(t+1)} \\
|
||||
\vec{y}_e^{(t+1)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig^{-1} \vec{p}_e^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
.. note::
|
||||
|
||||
For the first time step, \\\(t=0\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(0)}\\\) is zero.
|
||||
|
||||
|
||||
|
||||
|
||||
Implementing **J** transpose times a vector
|
||||
-------------------------------------------
|
||||
|
||||
Multiplying \\(\\mathbf{J}^\\top\\) onto a vector can be broken into three steps
|
||||
|
||||
|
||||
* Compute \\(\\vec{p} = \\mathbf{Q}^\\top \\vec{v}\\)
|
||||
* Solve \\(\\hat{\\mathbf{A}}^\\top \\vec{y} = \\vec{p}\\)
|
||||
* Compute \\(\\vec{w} = -\\mathbf{G}^\\top y\\)
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{\hat{A}}^\top = \left[
|
||||
\begin{array}{cccc}
|
||||
A & B & & \\
|
||||
& \ddots & \ddots & \\
|
||||
& & A & B \\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\right]
|
||||
|
||||
For the all time-steps (going backwards in time):
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
A \vec{y}^{(t)} + B \vec{y}^{(t+1)} = \vec{p}^{(t)}
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t)} + \MfMui\dcurl \vec{y}_{e}^{(t)}
|
||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t+1)}
|
||||
= \vec{p}_b^{(t)} \\
|
||||
\dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig \vec{y}_e^{(t)} = \vec{p}_e^{(t)}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t)} =
|
||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t+1)}
|
||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t)} + \vec{p}_b^{(t)} \\
|
||||
\vec{y}_e^{(t)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig^{-1} \vec{p}_e^{(t)}
|
||||
\end{align}
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
For the last time step, \\\(t=N\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(N+1)}\\\) is zero.
|
||||
|
||||
|
||||
|
||||
TDEM - B formulation
|
||||
====================
|
||||
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
.. _api_TDEM_derivation:
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\renewcommand{\div}{\nabla\cdot\,}
|
||||
\newcommand{\grad}{\vec \nabla}
|
||||
\newcommand{\curl}{{\vec \nabla}\times\,}
|
||||
\newcommand {\J}{{\vec J}}
|
||||
\renewcommand{\H}{{\vec H}}
|
||||
\newcommand {\E}{{\vec E}}
|
||||
\newcommand{\dcurl}{{\mathbf C}}
|
||||
\newcommand{\dgrad}{{\mathbf G}}
|
||||
\newcommand{\Acf}{{\mathbf A_c^f}}
|
||||
\newcommand{\Ace}{{\mathbf A_c^e}}
|
||||
\renewcommand{\S}{{\mathbf \Sigma}}
|
||||
\newcommand{\St}{{\mathbf \Sigma_\tau}}
|
||||
\newcommand{\T}{{\mathbf T}}
|
||||
\newcommand{\Tt}{{\mathbf T_\tau}}
|
||||
\newcommand{\diag}[1]{\,{\sf diag}\left( #1 \right)}
|
||||
\newcommand{\M}{{\mathbf M}}
|
||||
\newcommand{\MfMui}{{\M^f_{\mu^{-1}}}}
|
||||
\newcommand{\MeSig}{{\M^e_\sigma}}
|
||||
\newcommand{\MeSigInf}{{\M^e_{\sigma_\infty}}}
|
||||
\newcommand{\MeSigO}{{\M^e_{\sigma_0}}}
|
||||
\newcommand{\Me}{{\M^e}}
|
||||
\newcommand{\Mes}[1]{{\M^e_{#1}}}
|
||||
\newcommand{\Mee}{{\M^e_e}}
|
||||
\newcommand{\Mej}{{\M^e_j}}
|
||||
\newcommand{\BigO}[1]{\mathcal{O}\bigl(#1\bigr)}
|
||||
\newcommand{\bE}{\mathbf{E}}
|
||||
\newcommand{\bH}{\mathbf{H}}
|
||||
\newcommand{\B}{\vec{B}}
|
||||
\newcommand{\D}{\vec{D}}
|
||||
\renewcommand{\H}{\vec{H}}
|
||||
\newcommand{\s}{\vec{s}}
|
||||
\newcommand{\bfJ}{\bf{J}}
|
||||
\newcommand{\vecm}{\vec m}
|
||||
\renewcommand{\Re}{\mathsf{Re}}
|
||||
\renewcommand{\Im}{\mathsf{Im}}
|
||||
\renewcommand {\j} { {\vec j} }
|
||||
\newcommand {\h} { {\vec h} }
|
||||
\renewcommand {\b} { {\vec b} }
|
||||
\newcommand {\e} { {\vec e} }
|
||||
\newcommand {\c} { {\vec c} }
|
||||
\renewcommand {\d} { {\vec d} }
|
||||
\renewcommand {\u} { {\vec u} }
|
||||
\newcommand{\I}{\vec{I}}
|
||||
|
||||
|
||||
Time-Domain EM Derivation
|
||||
*************************
|
||||
|
||||
The following shows the derivation for the TDEM problem. We use the b-formulation below.
|
||||
(More to come soon..!)
|
||||
|
||||
|
||||
Sensitivity Calculation
|
||||
=======================
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\dcurl \e^{(t+1)} + \frac{\b^{(t+1)} - \b^{(t)}}{\delta t} = 0 \\
|
||||
\dcurl^\top \MfMui \b^{(t+1)} - \MeSig \e^{(t+1)} = \Me \j_s^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
Using Gauss-Newton to solve the inverse problem requires the ability to calculate the product of the
|
||||
Jacobian and a vector, as well as the transpose of the Jacobian times a vector.
|
||||
The above system can be rewritten as:
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{A} \u^{(t+1)} + \mathbf{B} \u^{(t)}= \s^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{A} =
|
||||
\left[
|
||||
\begin{array}{cc}
|
||||
\frac{1}{\delta t} \MfMui & \MfMui\dcurl \\
|
||||
\dcurl^\top \MfMui & -\MeSig
|
||||
\end{array}
|
||||
\right] \\
|
||||
\mathbf{B} =
|
||||
\left[
|
||||
\begin{array}{cc}
|
||||
-\frac{1}{\delta t} \MfMui & 0 \\
|
||||
0 & 0
|
||||
\end{array}
|
||||
\right] \\
|
||||
\u^{(k)} = \left[
|
||||
\begin{array}{c}
|
||||
\b^{(k)}\\
|
||||
\e^{(k)}
|
||||
\end{array}
|
||||
\right] \\
|
||||
\s^{(k)} = \left[
|
||||
\begin{array}{c}
|
||||
0\\
|
||||
\Me \j^{(k)}_s
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
.. note::
|
||||
|
||||
Here we have multiplied through by \\(\\MfMui\\) to make A and B symmetric!
|
||||
|
||||
The entire time dependent system can be written in a single matrix expression
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\hat{\mathbf{A}} \hat{u} = \hat{s}
|
||||
\end{align}
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\begin{array}{cccc}
|
||||
A & 0 & & \\
|
||||
B & A & & \\
|
||||
& \ddots & \ddots & \\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\right] \\
|
||||
\hat{u} = \left[
|
||||
\begin{array}{c}
|
||||
\u^{(1)} \\
|
||||
\u^{(2)} \\
|
||||
\vdots \\
|
||||
\u^{(N)}
|
||||
\end{array} \right]\\
|
||||
\hat{s} = \left[
|
||||
\begin{array}{c}
|
||||
\s^{(1)} - \mathbf{B} \u^{(0)} \\
|
||||
\s^{(2)} \\
|
||||
\vdots \\
|
||||
\s^{(N)}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
For the fields \\(\\u\\), the measured data is given by
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{d} = \mathbf{Q} \u
|
||||
\end{align}
|
||||
|
||||
The sensitivity matrix **J** is then defined as
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\mathbf{J} = \mathbf{Q} \frac{\partial \u}{\partial \sigma}
|
||||
\end{align}
|
||||
|
||||
|
||||
Defining the function \\(\\c(m,\\u)\\) to be
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{c}(m,\u) = \hat{\mathbf{A}} \vec{u} - \vec{q} = \vec{0}
|
||||
\end{align}
|
||||
|
||||
then
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial m} \partial m
|
||||
+ \frac{\partial \vec{c}}{\partial \u} \partial \vec{u} = 0
|
||||
\end{align}
|
||||
|
||||
or
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{u}}{\partial m} = -\left(\frac{\partial \vec{c}}{\partial \u} \right)^{-1} \frac{\partial \vec{c}}{\partial m}
|
||||
\end{align}
|
||||
|
||||
|
||||
Differentiating, we find that
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial \hat{u}} = \hat{\mathbf{A}}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{\partial \vec{c}}{\partial \sigma} = \mathbf{G}_\sigma =
|
||||
\left[
|
||||
\begin{array}{c}
|
||||
g_\sigma^{(1)}\\
|
||||
g_\sigma^{(2)}\\
|
||||
\vdots \\
|
||||
g_\sigma^{(N)}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
with
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
g_\sigma^{(n)} =
|
||||
\left[
|
||||
\begin{array}{c}
|
||||
\mathbf{0} \\
|
||||
- \diag{\e^{(n)}} \Ace \diag{\vec{V}}
|
||||
\end{array}
|
||||
\right]
|
||||
\end{align}
|
||||
|
||||
|
||||
Implementing **J** times a vector
|
||||
=================================
|
||||
|
||||
Multiplying **J** onto a vector can be broken into three steps
|
||||
|
||||
|
||||
* Compute \\(\\vec{p} = \\mathbf{G}m\\)
|
||||
* Solve \\(\\hat{\\mathbf{A}} \\vec{y} = \\vec{p}\\)
|
||||
* Compute \\(\\vec{w} = -\\mathbf{Q} \\vec{y}\\)
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\vec{p}^{(n)} = \left[
|
||||
\begin{array}{c}
|
||||
\vec{p}_b^{(n)} \\
|
||||
\vec{p}_e^{(n)}
|
||||
\end{array}
|
||||
\right] \\
|
||||
\vec{p}_b^{(n)} = 0 \\
|
||||
\vec{p}_e^{(n)} = - \diag{\e^{(n)}} \Ace \diag{V} m
|
||||
\end{align}
|
||||
|
||||
|
||||
For all time steps:
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t+1)} + \MfMui\dcurl \vec{y}_{e}^{(t+1)}
|
||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t)}
|
||||
= \vec{p}_b^{(t+1)} \\
|
||||
\dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig \vec{y}_e^{(t+1)} = \vec{p}_e^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t+1)} =
|
||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t)}
|
||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t+1)} + \vec{p}_b^{(t+1)} \\
|
||||
\vec{y}_e^{(t+1)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t+1)} - \MeSig^{-1} \vec{p}_e^{(t+1)}
|
||||
\end{align}
|
||||
|
||||
.. note::
|
||||
|
||||
For the first time step, \\\(t=0\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(0)}\\\) is zero.
|
||||
|
||||
|
||||
|
||||
|
||||
Implementing **J** transpose times a vector
|
||||
===========================================
|
||||
|
||||
Multiplying \\(\\mathbf{J}^\\top\\) onto a vector can be broken into three steps
|
||||
|
||||
|
||||
* Compute \\(\\vec{p} = \\mathbf{Q}^\\top \\vec{v}\\)
|
||||
* Solve \\(\\hat{\\mathbf{A}}^\\top \\vec{y} = \\vec{p}\\)
|
||||
* Compute \\(\\vec{w} = -\\mathbf{G}^\\top y\\)
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\mathbf{\hat{A}}^\top = \left[
|
||||
\begin{array}{cccc}
|
||||
A & B & & \\
|
||||
& \ddots & \ddots & \\
|
||||
& & A & B \\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\right]
|
||||
|
||||
For the all time-steps (going backwards in time):
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
A \vec{y}^{(t)} + B \vec{y}^{(t+1)} = \vec{p}^{(t)}
|
||||
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\frac{1}{\delta t} \MfMui\vec{y}_{b}^{(t)} + \MfMui\dcurl \vec{y}_{e}^{(t)}
|
||||
- \frac{1}{\delta t} \MfMui \vec{y}_{b}^{(t+1)}
|
||||
= \vec{p}_b^{(t)} \\
|
||||
\dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig \vec{y}_e^{(t)} = \vec{p}_e^{(t)}
|
||||
\end{align}
|
||||
|
||||
and
|
||||
|
||||
.. math::
|
||||
|
||||
\begin{align}
|
||||
\left( \MfMui \dcurl \MeSig^{-1} \dcurl^\top \MfMui + \frac{1}{\delta t} \MfMui \right) \vec{y}_{b}^{(t)} =
|
||||
\frac{1}{\delta t} \MfMui \vec{y}_b^{(t+1)}
|
||||
+ \MfMui \dcurl \MeSig^{-1} \vec{p}_e^{(t)} + \vec{p}_b^{(t)} \\
|
||||
\vec{y}_e^{(t)} = \MeSig^{-1} \dcurl^\top \MfMui \vec{y}_b^{(t)} - \MeSig^{-1} \vec{p}_e^{(t)}
|
||||
\end{align}
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
For the last time step, \\\(t=N\\\), the term: \\\(\\frac{1}{\\delta t} \\MfMui \\vec{y}_b^{(N+1)}\\\) is zero.
|
||||
+12
-12
@@ -4,10 +4,20 @@ simpegEM Utilities
|
||||
SimPEG for EM provides a few EM specific utility codes,
|
||||
sources, and analytic functions.
|
||||
|
||||
Utilities for Electromagnetics
|
||||
==============================
|
||||
|
||||
.. automodule:: SimPEG.EM.Utils
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Analytic Functions - Time
|
||||
=========================
|
||||
|
||||
.. automodule:: SimPEG.EM.Utils.Ana.TEM
|
||||
.. automodule:: SimPEG.EM.Analytics.TDEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
@@ -17,17 +27,7 @@ Analytic Functions - Time
|
||||
Analytic Functions - Frequency
|
||||
==============================
|
||||
|
||||
.. automodule:: SimPEG.EM.Utils.Ana.FEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Sources
|
||||
=======
|
||||
|
||||
.. automodule:: SimPEG.EM.Utils.Sources.magneticDipole
|
||||
.. automodule:: SimPEG.EM.Analytics.FDEM
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
+12
-31
@@ -3,43 +3,24 @@ Electromagnetics
|
||||
================
|
||||
|
||||
`SimPEG.EM` uses SimPEG as the framework for the forward and inverse
|
||||
electromagnetics geophysical problems.
|
||||
electromagnetics geophysical problems.
|
||||
|
||||
To solve for predicted data, we follow the framework shown below. The model is
|
||||
what we invert for. This is mapped to a physical property on the simulation
|
||||
mesh. A source which is used to excite the system is specified. Having a model
|
||||
and a source, we can solve Maxwell's equations for fields. We sample these
|
||||
fields with recievers to give us predicted data.
|
||||
|
||||
|
||||
.. image:: ../images/simpegEM_noMath.png
|
||||
:scale: 50%
|
||||
|
||||
Time Domian Electromagnetics
|
||||
----------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_TDEM_derivation
|
||||
|
||||
|
||||
Code for Time Domian Electromagnetics
|
||||
-------------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_TDEM
|
||||
|
||||
Frequency Domian Electromagnetics
|
||||
---------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_ForwardProblem
|
||||
api_FDEM
|
||||
|
||||
|
||||
Utility Codes
|
||||
-------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_TDEM
|
||||
api_Utils
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
.. _examples_DC_Analytic_Dipole:
|
||||
|
||||
.. --------------------------------- ..
|
||||
.. ..
|
||||
.. THIS FILE IS AUTO GENEREATED ..
|
||||
.. ..
|
||||
.. SimPEG/Examples/__init__.py ..
|
||||
.. ..
|
||||
.. --------------------------------- ..
|
||||
|
||||
DC Analytic Dipole
|
||||
==================
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.DC_Analytic_Dipole.run()
|
||||
|
||||
.. literalinclude:: ../../SimPEG/Examples/DC_Analytic_Dipole.py
|
||||
:language: python
|
||||
:linenos:
|
||||
@@ -0,0 +1,28 @@
|
||||
.. _examples_DC_Forward_PseudoSection:
|
||||
|
||||
.. --------------------------------- ..
|
||||
.. ..
|
||||
.. THIS FILE IS AUTO GENEREATED ..
|
||||
.. ..
|
||||
.. SimPEG/Examples/__init__.py ..
|
||||
.. ..
|
||||
.. --------------------------------- ..
|
||||
|
||||
|
||||
DC Forward Simulation
|
||||
=====================
|
||||
|
||||
Forward model conductive spheres in a half-space and plot a pseudo-section
|
||||
|
||||
Created by @fourndo on Mon Feb 01 19:28:06 2016
|
||||
|
||||
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.DC_Forward_PseudoSection.run()
|
||||
|
||||
.. literalinclude:: ../../SimPEG/Examples/DC_Forward_PseudoSection.py
|
||||
:language: python
|
||||
:linenos:
|
||||
@@ -0,0 +1,26 @@
|
||||
.. _examples_EM_FDEM_Analytic_MagDipoleWholespace:
|
||||
|
||||
.. --------------------------------- ..
|
||||
.. ..
|
||||
.. THIS FILE IS AUTO GENEREATED ..
|
||||
.. ..
|
||||
.. SimPEG/Examples/__init__.py ..
|
||||
.. ..
|
||||
.. --------------------------------- ..
|
||||
|
||||
|
||||
EM: Magnetic Dipole in a Whole-Space
|
||||
====================================
|
||||
|
||||
Here we plot the magnetic flux density from a harmonic dipole in a wholespace.
|
||||
|
||||
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.EM_FDEM_Analytic_MagDipoleWholespace.run()
|
||||
|
||||
.. literalinclude:: ../../SimPEG/Examples/EM_FDEM_Analytic_MagDipoleWholespace.py
|
||||
:language: python
|
||||
:linenos:
|
||||
@@ -0,0 +1,26 @@
|
||||
.. _examples_EM_TDEM_1D_Inversion:
|
||||
|
||||
.. --------------------------------- ..
|
||||
.. ..
|
||||
.. THIS FILE IS AUTO GENEREATED ..
|
||||
.. ..
|
||||
.. SimPEG/Examples/__init__.py ..
|
||||
.. ..
|
||||
.. --------------------------------- ..
|
||||
|
||||
|
||||
EM: TDEM: 1D: Inversion
|
||||
=======================
|
||||
|
||||
Here we will create and run a TDEM 1D inversion.
|
||||
|
||||
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.EM_TDEM_1D_Inversion.run()
|
||||
|
||||
.. literalinclude:: ../../SimPEG/Examples/EM_TDEM_1D_Inversion.py
|
||||
:language: python
|
||||
:linenos:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user