mirror of
https://github.com/wassname/simpeg.git
synced 2026-09-13 13:03:14 +08:00
Compare commits
711
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35fbc6c695 | ||
|
|
05ce38013e | ||
|
|
abc464ab46 | ||
|
|
2adc7d4eb9 | ||
|
|
92b5435149 | ||
|
|
28a8a2ea80 | ||
|
|
1457e51fc1 | ||
|
|
5dab7ac1a8 | ||
|
|
967dc83fa8 | ||
|
|
3431fb46eb | ||
|
|
0704c6dc25 | ||
|
|
37d37369d8 | ||
|
|
b013508270 | ||
|
|
20d5582c0f | ||
|
|
caba2b6752 | ||
|
|
43c49d5f15 | ||
|
|
1bcb572c45 | ||
|
|
67d3cb4d9b | ||
|
|
860bd5638a | ||
|
|
4bcc9e0850 | ||
|
|
09161ff68e | ||
|
|
c24416ea12 | ||
|
|
f5333f35a2 | ||
|
|
d50232b385 | ||
|
|
d7be0ada31 | ||
|
|
e8a6a028c2 | ||
|
|
06c82ba16d | ||
|
|
a8539cc234 | ||
|
|
814bd00346 | ||
|
|
d363a125b6 | ||
|
|
37ec0835c2 | ||
|
|
84400650fa | ||
|
|
91e37c5fa7 | ||
|
|
98f209d1f0 | ||
|
|
7107cd9d94 | ||
|
|
e254c04297 | ||
|
|
8da4d78bde | ||
|
|
0b971f4a50 | ||
|
|
7a4aa4ddc3 | ||
|
|
28b8a69d7b | ||
|
|
570dfb7aba | ||
|
|
c6e90230d4 | ||
|
|
e15913cf84 | ||
|
|
01b1122fcf | ||
|
|
1700f4f9c0 | ||
|
|
32f3ef301a | ||
|
|
17348e14e4 | ||
|
|
cdf0ebf8d0 | ||
|
|
dedabcc15f | ||
|
|
ff0c81b96e | ||
|
|
2c5b19b7a0 | ||
|
|
8a61259cab | ||
|
|
f482f9d877 | ||
|
|
1ba96ba8d0 | ||
|
|
985d5b6469 | ||
|
|
8da717521c | ||
|
|
d93a23306c | ||
|
|
d38424a10f | ||
|
|
00a29d27aa | ||
|
|
9ebd0fcedc | ||
|
|
6747882e52 | ||
|
|
dd5f5df69e | ||
|
|
d64fd4ae35 | ||
|
|
a18d48348d | ||
|
|
f0f3f6c06a | ||
|
|
4342450360 | ||
|
|
79f7ca7a1e | ||
|
|
e42727610a | ||
|
|
84eb69f626 | ||
|
|
69d109524e | ||
|
|
ba2ac74740 | ||
|
|
4b7f7c3c14 | ||
|
|
d6585dcfcd | ||
|
|
e678affe41 | ||
|
|
1a40e35c26 | ||
|
|
8b53fd5965 | ||
|
|
c298ebe8d8 | ||
|
|
3972178069 | ||
|
|
25ad1488f5 | ||
|
|
a8551f3e04 | ||
|
|
589cd655af | ||
|
|
7da637e883 | ||
|
|
a7ab0dc1e2 | ||
|
|
e30a7bcafc | ||
|
|
c4d34c4e0d | ||
|
|
cfc921b667 | ||
|
|
83cb5ce46a | ||
|
|
fe94bd3edf | ||
|
|
528253a8cb | ||
|
|
b2aab4163b | ||
|
|
be3667b4ab | ||
|
|
06c66f6392 | ||
|
|
bcfe904015 | ||
|
|
7c0be4ec50 | ||
|
|
56d5019b94 | ||
|
|
109340c645 | ||
|
|
83e29b74c7 | ||
|
|
c173ccd6b2 | ||
|
|
ca3b44bacc | ||
|
|
9fd25b7104 | ||
|
|
37f56f25f3 | ||
|
|
5bd9209d6a | ||
|
|
71a5c8b2c1 | ||
|
|
9b847c0e32 | ||
|
|
f3fb1e6481 | ||
|
|
815311bfec | ||
|
|
6a83af7334 | ||
|
|
af540478c5 | ||
|
|
1a43600781 | ||
|
|
c775917bb3 | ||
|
|
ccc3e4fe8f | ||
|
|
f928ab8019 | ||
|
|
dcb9b8787d | ||
|
|
ca05d0599e | ||
|
|
5678d90ef1 | ||
|
|
81a36684ca | ||
|
|
134c163c01 | ||
|
|
312acd217a | ||
|
|
033a64d53f | ||
|
|
c1b5f45ac7 | ||
|
|
92cd1fa15a | ||
|
|
622152c0c8 | ||
|
|
699f6452b2 | ||
|
|
7023e999bc | ||
|
|
2fbe0af108 | ||
|
|
fe2e29031c | ||
|
|
1718e3506d | ||
|
|
5afaa54ad9 | ||
|
|
61460261f0 | ||
|
|
c041ad3de6 | ||
|
|
e0684a0820 | ||
|
|
eafa2bfbe8 | ||
|
|
87eb02b76b | ||
|
|
2c00459846 | ||
|
|
4ac85e81d6 | ||
|
|
5e20960335 | ||
|
|
d7bcc0c074 | ||
|
|
9d92f62cfa | ||
|
|
52375ecd06 | ||
|
|
2da82b7d19 | ||
|
|
356a5b103d | ||
|
|
aed5f5ad52 | ||
|
|
19bcdbfbf5 | ||
|
|
3b62957d08 | ||
|
|
25e21608df | ||
|
|
4fe9475ffc | ||
|
|
d130b50f7f | ||
|
|
6a30ea1d83 | ||
|
|
821932ed82 | ||
|
|
86eb244011 | ||
|
|
8fc1a86742 | ||
|
|
fd55aa6d67 | ||
|
|
a53372a859 | ||
|
|
7fd07f8213 | ||
|
|
d1ef2fc330 | ||
|
|
29579c35e0 | ||
|
|
601079dab3 | ||
|
|
277318240c | ||
|
|
13c8843fd1 | ||
|
|
d860456105 | ||
|
|
314b50c435 | ||
|
|
37a68dd5b7 | ||
|
|
f6c5b011e8 | ||
|
|
4dafecaef7 | ||
|
|
b870abbdce | ||
|
|
b7be3861a0 | ||
|
|
ff68400320 | ||
|
|
da7fbbb461 | ||
|
|
94a79298bd | ||
|
|
2ce694e9d5 | ||
|
|
5b85bcaeaf | ||
|
|
6b413c8b43 | ||
|
|
4a5a0a3c8c | ||
|
|
f63847ddae | ||
|
|
554f507ba7 | ||
|
|
915a2f8446 | ||
|
|
297ea916b4 | ||
|
|
639a6e7a06 | ||
|
|
91284e2286 | ||
|
|
fb18d8a5d8 | ||
|
|
855c92dc77 | ||
|
|
406addd332 | ||
|
|
b1058eb07d | ||
|
|
b2381796f7 | ||
|
|
ebb57f6218 | ||
|
|
0aafb8931b | ||
|
|
e8ae516030 | ||
|
|
109a5f0f86 | ||
|
|
ac688254ac | ||
|
|
e55dc2e78d | ||
|
|
337383f8ab | ||
|
|
0af1f59891 | ||
|
|
4553d6db51 | ||
|
|
d102ec9c00 | ||
|
|
7b0bea4e1d | ||
|
|
96b855d71d | ||
|
|
41970a221a | ||
|
|
14f7cf4fff | ||
|
|
a4a2c400a7 | ||
|
|
6669c3d3aa | ||
|
|
8ce9699b8a | ||
|
|
ac9b50358f | ||
|
|
63a2ace3db | ||
|
|
10bd87ac02 | ||
|
|
4b87b4b45f | ||
|
|
025b2db70e | ||
|
|
e2e2fcec03 | ||
|
|
ff5885cde0 | ||
|
|
c688bfd5ae | ||
|
|
0da5888e37 | ||
|
|
d2baf15b54 | ||
|
|
21a3353aa7 | ||
|
|
44d11542c7 | ||
|
|
6fb93ee6e4 | ||
|
|
7c897d0f21 | ||
|
|
4db756b060 | ||
|
|
e622dfb5cc | ||
|
|
1aade8155d | ||
|
|
cd01f07467 | ||
|
|
465087beba | ||
|
|
536b9abb86 | ||
|
|
f9b77a9128 | ||
|
|
fc3590ec9d | ||
|
|
7106b2b57f | ||
|
|
b8fe0cfdbf | ||
|
|
0885b72577 | ||
|
|
b00488a6d0 | ||
|
|
de2891ccdf | ||
|
|
e85da0d15b | ||
|
|
24f2bae3c2 | ||
|
|
e4f67ef649 | ||
|
|
059d318ab6 | ||
|
|
670511eb61 | ||
|
|
ed463c736f | ||
|
|
d148a5342c | ||
|
|
1cb06297ec | ||
|
|
5d844ac2c0 | ||
|
|
70453b6720 | ||
|
|
b927a1daf2 | ||
|
|
8a358506f1 | ||
|
|
d3d453ac48 | ||
|
|
075cea488f | ||
|
|
97f27c76d1 | ||
|
|
bac28ba133 | ||
|
|
0d88ec3652 | ||
|
|
ed6d091705 | ||
|
|
c29f2792ff | ||
|
|
ff2ce3f1c8 | ||
|
|
502e76c33a | ||
|
|
8566316a12 | ||
|
|
2b17ad45ac | ||
|
|
23a3768b52 | ||
|
|
309e7f84cf | ||
|
|
2c6a2ae35c | ||
|
|
575ac2ea7b | ||
|
|
c7c1126e9b | ||
|
|
168ad8b0ff | ||
|
|
7020201976 | ||
|
|
efbd0da0b3 | ||
|
|
f726a27269 | ||
|
|
7f74f7ee82 | ||
|
|
c886817773 | ||
|
|
41e9d175f2 | ||
|
|
d634575137 | ||
|
|
7cd4ba7d61 | ||
|
|
e4ed2265ba | ||
|
|
5cb3babafe | ||
|
|
cb73c38e8e | ||
|
|
59eb52dce8 | ||
|
|
037155e3f3 | ||
|
|
a84bc5cbc1 | ||
|
|
b5336c1ca1 | ||
|
|
4848542632 | ||
|
|
2ef8beddb6 | ||
|
|
d518f1e685 | ||
|
|
039e3430ab | ||
|
|
b5b8b5fae6 | ||
|
|
7c63bfb6ac | ||
|
|
9900885984 | ||
|
|
0b5453dc98 | ||
|
|
9e4d8e1884 | ||
|
|
001bcbce27 | ||
|
|
94ef2f1eb6 | ||
|
|
f9f23dfd4b | ||
|
|
4fa5e9426d | ||
|
|
954cb2d7bc | ||
|
|
265a1189e8 | ||
|
|
2dcb356682 | ||
|
|
37368199f1 | ||
|
|
0b8f80f41e | ||
|
|
f96cf0418a | ||
|
|
5ec86300f6 | ||
|
|
198dd165fc | ||
|
|
5aea1ee4d5 | ||
|
|
1fbf40568d | ||
|
|
d9e8996336 | ||
|
|
ac3e7765fe | ||
|
|
36f8eca25c | ||
|
|
5732e85a2f | ||
|
|
7c01c84514 | ||
|
|
527133c709 | ||
|
|
32f83322ca | ||
|
|
7239d9cbe6 | ||
|
|
58e8ce4988 | ||
|
|
ab1f55cd9c | ||
|
|
05ca7bb78b | ||
|
|
39b5b5eb3f | ||
|
|
69977ad0b2 | ||
|
|
9a732ae5b5 | ||
|
|
611e930925 | ||
|
|
4113a7438b | ||
|
|
6d3d8d78b6 | ||
|
|
26a84c7bfc | ||
|
|
06ba32f07d | ||
|
|
08c9013fd1 | ||
|
|
678d8611d8 | ||
|
|
1240d65eaf | ||
|
|
da9b541d81 | ||
|
|
bf08fe83da | ||
|
|
686598cc8f | ||
|
|
9ec2fa5e79 | ||
|
|
838ee6e09b | ||
|
|
1b7ad56e94 | ||
|
|
d545d9e393 | ||
|
|
cd94fea61d | ||
|
|
54468c7751 | ||
|
|
ca6c485889 | ||
|
|
693ae256c1 | ||
|
|
c28e2c10de | ||
|
|
66b26a1b5e | ||
|
|
1424d071d4 | ||
|
|
e53b124724 | ||
|
|
29476eca77 | ||
|
|
6c2baf0744 | ||
|
|
6b4dede7a4 | ||
|
|
165afb958f | ||
|
|
80a7dfb51b | ||
|
|
e0387978f2 | ||
|
|
5caf237121 | ||
|
|
b7a3b4e5e6 | ||
|
|
4df383ccec | ||
|
|
f90637509e | ||
|
|
4fa4ef643d | ||
|
|
658d481dd6 | ||
|
|
d07bb6722b | ||
|
|
5b45fc628e | ||
|
|
4df00148a3 | ||
|
|
27c8da341d | ||
|
|
3df2140a88 | ||
|
|
760f24ea33 | ||
|
|
13a5760398 | ||
|
|
3f4f71bf3c | ||
|
|
592d169f9d | ||
|
|
2c48a69fb2 | ||
|
|
37fc9981f7 | ||
|
|
8475eadcce | ||
|
|
fbda6ab53b | ||
|
|
3810b392dd | ||
|
|
a953a52ccc | ||
|
|
5e7194b078 | ||
|
|
401336f412 | ||
|
|
8c4a01a665 | ||
|
|
44f5cf960f | ||
|
|
2827e85330 | ||
|
|
e634b00af0 | ||
|
|
de27c4e4ec | ||
|
|
59fcd3925f | ||
|
|
116f7620a6 | ||
|
|
7e171ede05 | ||
|
|
14ee13fadb | ||
|
|
eea51c9923 | ||
|
|
b942d214d4 | ||
|
|
fbf022ff76 | ||
|
|
8d97f322c9 | ||
|
|
ec7ed8a585 | ||
|
|
f080c61f74 | ||
|
|
dc3e641524 | ||
|
|
5b44b16bc4 | ||
|
|
59c141a5f1 | ||
|
|
369694335a | ||
|
|
0c432e1d82 | ||
|
|
0a4b7d14ac | ||
|
|
37dacec8ff | ||
|
|
4a1772bbb1 | ||
|
|
a87b37e6a7 | ||
|
|
682528d555 | ||
|
|
40aae0b610 | ||
|
|
b8118464b8 | ||
|
|
618f803ffa | ||
|
|
e8647f3902 | ||
|
|
24c754cea2 | ||
|
|
9eede4e840 | ||
|
|
35d56655f5 | ||
|
|
f8324186d4 | ||
|
|
569d22bf6a | ||
|
|
6f487e17d0 | ||
|
|
1f58f5ef37 | ||
|
|
428e34f631 | ||
|
|
0869777284 | ||
|
|
df4819d02f | ||
|
|
1e52c365bc | ||
|
|
96b0d6a38c | ||
|
|
deaa159f3f | ||
|
|
47fca47b33 | ||
|
|
c844f7b36a | ||
|
|
77346af8ee | ||
|
|
d8c82da1d4 | ||
|
|
4d75c9d6e5 | ||
|
|
5fc6ff39eb | ||
|
|
54a0580a8a | ||
|
|
3d2128f333 | ||
|
|
f86d3d7bdc | ||
|
|
a990849256 | ||
|
|
51342e7cc8 | ||
|
|
dc7cc1c716 | ||
|
|
08d90bbb67 | ||
|
|
2864a976c6 | ||
|
|
93778d13f1 | ||
|
|
e980477031 | ||
|
|
50a853a3b6 | ||
|
|
1964a002f1 | ||
|
|
f5a0465f1e | ||
|
|
dbf3175c39 | ||
|
|
903a418a11 | ||
|
|
3dba2d9a04 | ||
|
|
fcc0650713 | ||
|
|
228f3f7f38 | ||
|
|
e61679bdc6 | ||
|
|
e93295e91f | ||
|
|
a9b1f89e7f | ||
|
|
223380484b | ||
|
|
6e4aac140b | ||
|
|
b9818a600b | ||
|
|
78c98e5ad6 | ||
|
|
224a5311d6 | ||
|
|
a6e82ecc2a | ||
|
|
127b90c88d | ||
|
|
1a760cedc5 | ||
|
|
65842379b5 | ||
|
|
6ffeb5cc1a | ||
|
|
c480279ae5 | ||
|
|
ed61178b45 | ||
|
|
a697833b9f | ||
|
|
fa0fd7f23f | ||
|
|
35678c587d | ||
|
|
62b4edcf6d | ||
|
|
84fedcf2a7 | ||
|
|
2780a4cea7 | ||
|
|
5c19da60cc | ||
|
|
564fa14826 | ||
|
|
94d12b257a | ||
|
|
0e0033eb0a | ||
|
|
d5eef78b57 | ||
|
|
5ac746f31f | ||
|
|
5284b91f33 | ||
|
|
5d74fc2d1b | ||
|
|
8d4e001301 | ||
|
|
5e323591b9 | ||
|
|
5bb5e8f6b3 | ||
|
|
3201d1fc59 | ||
|
|
31c697eafa | ||
|
|
57380ea2d8 | ||
|
|
2a6f0ab440 | ||
|
|
4e69ee1f1f | ||
|
|
ab0049180d | ||
|
|
e99d6dc208 | ||
|
|
629bb56185 | ||
|
|
2a53b78ccd | ||
|
|
0a98c5f1c3 | ||
|
|
71881c7ce8 | ||
|
|
ed1457df86 | ||
|
|
0220ee57f2 | ||
|
|
5cdab15aa1 | ||
|
|
3369096126 | ||
|
|
99ada822e8 | ||
|
|
5afea4e2b6 | ||
|
|
d65a3540a4 | ||
|
|
a4f3db398f | ||
|
|
5c85f13cb6 | ||
|
|
72c8dfb3ef | ||
|
|
1c40cbb972 | ||
|
|
e0303b8dff | ||
|
|
156b85319c | ||
|
|
006dcf393d | ||
|
|
9a8f4d60f7 | ||
|
|
5e5a3221e8 | ||
|
|
310f327b5a | ||
|
|
a7a6671ad4 | ||
|
|
aa05cdc653 | ||
|
|
5c675a7992 | ||
|
|
b2d57e8892 | ||
|
|
bc7fb2f1d7 | ||
|
|
1edca3b9e3 | ||
|
|
e83b76a70c | ||
|
|
9067590c65 | ||
|
|
bfb5e045fc | ||
|
|
7d14489c4e | ||
|
|
3846a6303b | ||
|
|
682a8e3877 | ||
|
|
c229e26f74 | ||
|
|
ed54a230da | ||
|
|
cf2a9688c3 | ||
|
|
6166c2b14b | ||
|
|
dd766a3ce3 | ||
|
|
2e0aadf113 | ||
|
|
c503173317 | ||
|
|
10cf6817a3 | ||
|
|
896bb68bac | ||
|
|
076005a7e9 | ||
|
|
f747d724c9 | ||
|
|
23141c682f | ||
|
|
2e3f98800c | ||
|
|
e5650dfc92 | ||
|
|
57f16fc408 | ||
|
|
932f1dbd05 | ||
|
|
1bcf58154f | ||
|
|
3e35c6068b | ||
|
|
0838d21bce | ||
|
|
b34b36bfb9 | ||
|
|
0718c2c50a | ||
|
|
75fb7a8719 | ||
|
|
49e07477b2 | ||
|
|
26079333bb | ||
|
|
bbf2a2a7f3 | ||
|
|
f4a38e1165 | ||
|
|
6aaacc382a | ||
|
|
06e8059e3c | ||
|
|
7a15797fbe | ||
|
|
6e7d88de77 | ||
|
|
1530096d01 | ||
|
|
9f7527d8bf | ||
|
|
e11b8f3fe3 | ||
|
|
3da5b6ebc0 | ||
|
|
bb8dbd8c86 | ||
|
|
6aada6d61d | ||
|
|
9400bdd9e0 | ||
|
|
c4f795ea79 | ||
|
|
897c0e4a13 | ||
|
|
c28c358930 | ||
|
|
711c66a2ab | ||
|
|
5a1856c1cf | ||
|
|
48ad667c1f | ||
|
|
95a837b1fc | ||
|
|
4398092023 | ||
|
|
4e104ced08 | ||
|
|
220e244cdf | ||
|
|
cfcf741a0b | ||
|
|
4899346f6b | ||
|
|
205d2e6b5e | ||
|
|
d58cb21123 | ||
|
|
06b34d8f7b | ||
|
|
464264b902 | ||
|
|
cb4f699be5 | ||
|
|
5cf74864cc | ||
|
|
88e16ac1e0 | ||
|
|
1dd9109f22 | ||
|
|
53c1fe220d | ||
|
|
25267d1d32 | ||
|
|
cb6781a138 | ||
|
|
f0e0cade97 | ||
|
|
8b30a1fe6a | ||
|
|
a834b69744 | ||
|
|
ce2ab57669 | ||
|
|
c77a0279c8 | ||
|
|
6e21d32230 | ||
|
|
b750faa3e0 | ||
|
|
6f2d57857d | ||
|
|
8e5c93accb | ||
|
|
4c15267c0c | ||
|
|
3514ad56d3 | ||
|
|
df7676f14c | ||
|
|
d1f23081d6 | ||
|
|
45884ba865 | ||
|
|
9f82ffff7b | ||
|
|
9ebbe7613d | ||
|
|
78b3a281f4 | ||
|
|
05bb79f710 | ||
|
|
9a801a87d5 | ||
|
|
f3f571edbd | ||
|
|
06de6a346f | ||
|
|
7254933695 | ||
|
|
200a1680ca | ||
|
|
d9e91a5a1d | ||
|
|
96e129aae2 | ||
|
|
f8f681df25 | ||
|
|
2831b66ea9 | ||
|
|
44ec85cfaa | ||
|
|
7b8cbabbb3 | ||
|
|
030e3bc521 | ||
|
|
f4e210a61d | ||
|
|
8f108d488d | ||
|
|
2d90799415 | ||
|
|
eab87c892b | ||
|
|
113c97ca87 | ||
|
|
3f7f95de8c | ||
|
|
4b569924f4 | ||
|
|
9252ed7980 | ||
|
|
20d1ad27c8 | ||
|
|
8326c50d56 | ||
|
|
beb2fa1a58 | ||
|
|
e26aa607a3 | ||
|
|
cd871dbaf4 | ||
|
|
b7bff90c47 | ||
|
|
9779457448 | ||
|
|
fa657eb7d3 | ||
|
|
b1613dba65 | ||
|
|
9acaae66db | ||
|
|
b7d47bcf38 | ||
|
|
aacbc03cf6 | ||
|
|
a8f09ee869 | ||
|
|
837a114ad7 | ||
|
|
879f13df70 | ||
|
|
0a5a7aa5e3 | ||
|
|
3197689fb7 | ||
|
|
67aa72f494 | ||
|
|
0efdffa3d6 | ||
|
|
4709545b0d | ||
|
|
357633c39b | ||
|
|
0f7e4a88ff | ||
|
|
dd671e1277 | ||
|
|
49eddb4b83 | ||
|
|
d177960abb | ||
|
|
3fd840201d | ||
|
|
95e5a09523 | ||
|
|
5c4ec4a1cd | ||
|
|
31da380804 | ||
|
|
e1010f9eca | ||
|
|
f10c029161 | ||
|
|
98385e5f28 | ||
|
|
a14d771515 | ||
|
|
e074cca07c | ||
|
|
994bb2159e | ||
|
|
880780fce0 | ||
|
|
752696a5d5 | ||
|
|
8919861e91 | ||
|
|
4b7323b8f5 | ||
|
|
ce9111efa1 | ||
|
|
d8128c2976 | ||
|
|
0e9b1371e3 | ||
|
|
e5105065a5 | ||
|
|
9fb83ad727 | ||
|
|
7a5fde8a3e | ||
|
|
297b16bf43 | ||
|
|
8ed0ee29e2 | ||
|
|
3a3949d9bb | ||
|
|
1016def784 | ||
|
|
0084a278b5 | ||
|
|
715bed21ce | ||
|
|
beb87d1f36 | ||
|
|
12b0755dfc | ||
|
|
d3789eebe2 | ||
|
|
e897165edb | ||
|
|
e696170ce8 | ||
|
|
81e013857d | ||
|
|
edc241eec2 | ||
|
|
4cd25fde82 | ||
|
|
7195adc6c5 | ||
|
|
bf2021d8e8 | ||
|
|
915667ad4a | ||
|
|
7bb70103aa | ||
|
|
1128fbe39b | ||
|
|
d27e7d3aba | ||
|
|
2070bca0a6 | ||
|
|
58b65e0acc | ||
|
|
847070f1a2 | ||
|
|
ea81e40ef7 | ||
|
|
cc3f2d867c | ||
|
|
cd62bfd17b | ||
|
|
a2e9b3f780 | ||
|
|
47c2927ffc | ||
|
|
b0a99d4116 | ||
|
|
9370e24643 | ||
|
|
5239e57c69 | ||
|
|
b42f523db0 | ||
|
|
434a4232d5 | ||
|
|
4e08f12de4 | ||
|
|
c01f1a4a5a | ||
|
|
732ff882e0 | ||
|
|
99157a89c8 | ||
|
|
80fff7722e | ||
|
|
ffb87aafa7 | ||
|
|
d4a316b764 | ||
|
|
cf620acf22 | ||
|
|
6e6d90d362 | ||
|
|
88fb57bd9f | ||
|
|
4a92b6b5eb | ||
|
|
d7566f42e5 | ||
|
|
66ccdb4042 | ||
|
|
68abb7a7cf | ||
|
|
08f2020ae9 | ||
|
|
d65705fd0b | ||
|
|
92788ad5f1 | ||
|
|
710032e636 | ||
|
|
bb3e05fbfa | ||
|
|
8efea97bb2 | ||
|
|
2c29d07fe7 | ||
|
|
2ec86ea09a | ||
|
|
f1f261243a | ||
|
|
50c2ec650e | ||
|
|
d9df40b5be | ||
|
|
efc28e7251 | ||
|
|
7378d5f109 | ||
|
|
45c25365d9 | ||
|
|
0532d99317 | ||
|
|
81c77ad1ba | ||
|
|
566291955c | ||
|
|
76d971485e | ||
|
|
5c509b3538 | ||
|
|
2404f3e08a | ||
|
|
45567fd422 | ||
|
|
3ee18c8946 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
[bumpversion]
|
||||
current_version = 0.1.2
|
||||
current_version = 0.1.9
|
||||
files = setup.py SimPEG/__init__.py docs/conf.py
|
||||
|
||||
|
||||
@@ -38,5 +38,4 @@ nosetests.xml
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
docs/_build/
|
||||
*_cython.c
|
||||
Makefile
|
||||
|
||||
+28
-6
@@ -2,6 +2,27 @@ language: python
|
||||
python:
|
||||
- 2.7
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc
|
||||
- gfortran
|
||||
- libopenmpi-dev
|
||||
- libmumps-seq-dev
|
||||
- libblas-dev
|
||||
- liblapack-dev
|
||||
|
||||
env:
|
||||
- TEST_DIR="tests/mesh tests/base tests/utils"
|
||||
- TEST_DIR=tests/em/fdem/inverse/derivs
|
||||
- TEST_DIR=tests/em/tdem
|
||||
- TEST_DIR=tests/flow
|
||||
- TEST_DIR=tests/examples
|
||||
- TEST_DIR=tests/em/fdem/inverse/adjoint
|
||||
- TEST_DIR=tests/em/fdem/forward
|
||||
|
||||
# Setup anaconda
|
||||
before_install:
|
||||
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.8.3-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.8.3-Linux-x86_64.sh -O miniconda.sh; fi
|
||||
@@ -9,20 +30,21 @@ before_install:
|
||||
- ./miniconda.sh -b
|
||||
- export PATH=/home/travis/anaconda/bin:/home/travis/miniconda/bin:$PATH
|
||||
- conda update --yes conda
|
||||
# The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda
|
||||
- sudo rm -rf /dev/shm
|
||||
- sudo ln -s /run/shm /dev/shm
|
||||
|
||||
# Install packages
|
||||
install:
|
||||
- conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython
|
||||
- conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython ipython nose vtk
|
||||
- pip install nose-cov python-coveralls
|
||||
# - pip install -r requirements.txt
|
||||
|
||||
- git clone https://github.com/rowanc1/pymatsolver.git
|
||||
- cd pymatsolver; python setup.py install; cd ..
|
||||
|
||||
- python setup.py install
|
||||
- python setup.py build_ext --inplace
|
||||
|
||||
# Run test
|
||||
script:
|
||||
- nosetests --with-cov --cov SimPEG --cov-config .coveragerc -v -s
|
||||
- nosetests $TEST_DIR --with-cov --cov SimPEG --cov-config .coveragerc -v -s
|
||||
|
||||
# Calculate coverage
|
||||
after_success:
|
||||
|
||||
+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/>`_)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Citing 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::
|
||||
|
||||
@article{cockett2015simpeg,
|
||||
title={SimPEG: An open source framework for simulation and gradient based parameter estimation in geophysical applications},
|
||||
author={Cockett, Rowan and Kang, Seogi and Heagy, Lindsey J and Pidlisecky, Adam and Oldenburg, Douglas W},
|
||||
journal={Computers \& Geosciences},
|
||||
year={2015},
|
||||
publisher={Elsevier}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
+24
-2
@@ -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
|
||||
|
||||
@@ -36,6 +36,28 @@ The vision is to create a package for finite volume simulation with applications
|
||||
* designed for large-scale inversions
|
||||
|
||||
|
||||
Citing SimPEG:
|
||||
--------------
|
||||
|
||||
There is a paper about SimPEG!
|
||||
|
||||
|
||||
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::
|
||||
|
||||
@article{cockett2015simpeg,
|
||||
title={SimPEG: An open source framework for simulation and gradient based parameter estimation in geophysical applications},
|
||||
author={Cockett, Rowan and Kang, Seogi and Heagy, Lindsey J and Pidlisecky, Adam and Oldenburg, Douglas W},
|
||||
journal={Computers \& Geosciences},
|
||||
year={2015},
|
||||
publisher={Elsevier}
|
||||
}
|
||||
|
||||
|
||||
Website:
|
||||
http://simpeg.xyz
|
||||
|
||||
@@ -57,4 +79,4 @@ https://github.com/simpeg/simpeg/issues
|
||||
|
||||
|
||||
Code Snippets & Tutorials:
|
||||
http://www.row1.ca/simpeg
|
||||
http://simpeg.xyz/Journal
|
||||
|
||||
+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
|
||||
|
||||
+89
-13
@@ -144,23 +144,99 @@ class BetaSchedule(InversionDirective):
|
||||
if self.debug: print 'BetaSchedule is cooling Beta. Iteration: %d' % self.opt.iter
|
||||
self.invProb.beta /= self.coolingFactor
|
||||
|
||||
|
||||
|
||||
class SaveModelEveryIteration(InversionDirective):
|
||||
"""SaveModelEveryIteration"""
|
||||
class TargetMisfit(InversionDirective):
|
||||
|
||||
@property
|
||||
def modelName(self):
|
||||
if getattr(self, '_modelName', None) is None:
|
||||
from datetime import datetime
|
||||
self._modelName = 'inversionModel-%s'%datetime.now().strftime('%Y-%m-%d')
|
||||
return self._modelName
|
||||
@modelName.setter
|
||||
def modelName(self, value):
|
||||
self._modelName = value
|
||||
def target(self):
|
||||
if getattr(self, '_target', None) is None:
|
||||
self._target = self.survey.nD*0.5
|
||||
return self._target
|
||||
@target.setter
|
||||
def target(self, val):
|
||||
self._target = val
|
||||
|
||||
def endIter(self):
|
||||
np.save('%03d-%s' % (self.opt.iter, self.modelName), self.opt.xc)
|
||||
if self.invProb.phi_d < self.target:
|
||||
self.opt.stopNextIteration = True
|
||||
|
||||
|
||||
|
||||
class _SaveEveryIteration(InversionDirective):
|
||||
@property
|
||||
def name(self):
|
||||
if getattr(self, '_name', None) is None:
|
||||
self._name = 'InversionModel'
|
||||
return self._name
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self._name = value
|
||||
|
||||
@property
|
||||
def fileName(self):
|
||||
if getattr(self, '_fileName', None) is None:
|
||||
from datetime import datetime
|
||||
self._fileName = '%s-%s'%(self.name, datetime.now().strftime('%Y-%m-%d-%H-%M'))
|
||||
return self._fileName
|
||||
@fileName.setter
|
||||
def fileName(self, value):
|
||||
self._fileName = value
|
||||
|
||||
|
||||
class SaveModelEveryIteration(_SaveEveryIteration):
|
||||
"""SaveModelEveryIteration"""
|
||||
|
||||
def initialize(self):
|
||||
print "SimPEG.SaveModelEveryIteration will save your models as: '###-%s.npy'"%self.fileName
|
||||
|
||||
def endIter(self):
|
||||
np.save('%03d-%s' % (self.opt.iter, self.fileName), self.opt.xc)
|
||||
|
||||
|
||||
class SaveOutputEveryIteration(_SaveEveryIteration):
|
||||
"""SaveModelEveryIteration"""
|
||||
|
||||
def initialize(self):
|
||||
print "SimPEG.SaveOutputEveryIteration will save your inversion progress as: '###-%s.txt'"%self.fileName
|
||||
f = open(self.fileName+'.txt', 'w')
|
||||
f.write(" # beta phi_d phi_m f\n")
|
||||
f.close()
|
||||
|
||||
def endIter(self):
|
||||
f = open(self.fileName+'.txt', 'a')
|
||||
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 UpdateReferenceModel(Parameter):
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
from __future__ import division
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, pi
|
||||
from scipy.special import erf
|
||||
from SimPEG import Utils
|
||||
|
||||
|
||||
def hzAnalyticDipoleF(r, freq, sigma, secondary=True, mu=mu_0):
|
||||
"""
|
||||
4.56 in Ward and Hohmann
|
||||
|
||||
.. plot::
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from SimPEG import EM
|
||||
freq = np.logspace(-1, 6, 61)
|
||||
test = EM.Analytics.FDEM.hzAnalyticDipoleF(100, freq, 0.001, secondary=False)
|
||||
plt.loglog(freq, abs(test.real))
|
||||
plt.loglog(freq, abs(test.imag))
|
||||
plt.title('Response at $r$=100m')
|
||||
plt.xlabel('Frequency')
|
||||
plt.ylabel('Response')
|
||||
plt.legend(('real','imag'))
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
r = np.abs(r)
|
||||
k = np.sqrt(-1j*2.*np.pi*freq*mu*sigma)
|
||||
|
||||
m = 1
|
||||
front = m / (2. * np.pi * (k**2) * (r**5) )
|
||||
back = 9 - ( 9 + 9j * k * r - 4 * (k**2) * (r**2) - 1j * (k**3) * (r**3)) * np.exp(-1j*k*r)
|
||||
hz = front*back
|
||||
|
||||
if secondary:
|
||||
hp =-1/(4*np.pi*r**3)
|
||||
hz = hz-hp
|
||||
|
||||
if hz.ndim == 1:
|
||||
hz = Utils.mkvc(hz,2)
|
||||
|
||||
return hz
|
||||
|
||||
def MagneticDipoleWholeSpace(XYZ, srcLoc, sig, f, moment=1., orientation='X', mu = mu_0):
|
||||
"""
|
||||
Analytical solution for a dipole in a whole-space.
|
||||
|
||||
Equation 2.57 of Ward and Hohmann
|
||||
|
||||
TODOs:
|
||||
- set it up to instead take a mesh & survey
|
||||
- add E-fields
|
||||
- handle multiple frequencies
|
||||
- add divide by zero safety
|
||||
|
||||
|
||||
.. plot::
|
||||
|
||||
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.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'))
|
||||
plt.show()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
|
||||
|
||||
dx = XYZ[:,0]-srcLoc[0]
|
||||
dy = XYZ[:,1]-srcLoc[1]
|
||||
dz = XYZ[:,2]-srcLoc[2]
|
||||
|
||||
r = np.sqrt( dx**2. + dy**2. + dz**2.)
|
||||
k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
|
||||
kr = k*r
|
||||
|
||||
front = moment / (4.*pi * r**3.) * np.exp(-1j*kr)
|
||||
mid = -kr**2. + 3.*1j*kr + 3.
|
||||
|
||||
if orientation.upper() == 'X':
|
||||
Hx = front*( (dx/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
Hy = front*( (dx*dy/r**2.) * mid )
|
||||
Hz = front*( (dx*dz/r**2.) * mid )
|
||||
|
||||
elif orientation.upper() == 'Y':
|
||||
Hx = front*( (dy*dx/r**2.) * mid )
|
||||
Hy = front*( (dy/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
Hz = front*( (dy*dz/r**2.) * mid )
|
||||
|
||||
elif orientation.upper() == 'Z':
|
||||
Hx = front*( (dx*dz/r**2.) * mid )
|
||||
Hy = front*( (dy*dz/r**2.) * mid )
|
||||
Hz = front*( (dz/r)**2. * mid + (kr**2. - 1j*kr - 1.) )
|
||||
|
||||
Bx = mu*Hx
|
||||
By = mu*Hy
|
||||
Bz = mu*Hz
|
||||
|
||||
if Bx.ndim is 1:
|
||||
Bx = Utils.mkvc(Bx,2)
|
||||
|
||||
if By.ndim is 1:
|
||||
By = Utils.mkvc(By,2)
|
||||
|
||||
if Bz.ndim is 1:
|
||||
Bz = Utils.mkvc(Bz,2)
|
||||
|
||||
return Bx, By, Bz
|
||||
|
||||
|
||||
def ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', mu=mu_0):
|
||||
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
|
||||
|
||||
dx = XYZ[:,0]-srcLoc[0]
|
||||
dy = XYZ[:,1]-srcLoc[1]
|
||||
dz = XYZ[:,2]-srcLoc[2]
|
||||
|
||||
r = np.sqrt( dx**2. + dy**2. + dz**2.)
|
||||
k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
|
||||
kr = k*r
|
||||
|
||||
front = current * length / (4. * np.pi * sig * r**3) * np.exp(-1j*k*r)
|
||||
mid = -k**2 * r**2 + 3*1j*k*r + 3
|
||||
|
||||
# Ex = front*((dx**2 / r**2)*mid + (k**2 * r**2 -1j*k*r))
|
||||
# Ey = front*(dx*dy / r**2)*mid
|
||||
# Ez = front*(dx*dz / r**2)*mid
|
||||
|
||||
if orientation.upper() == 'X':
|
||||
Ex = front*((dx**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ey = front*(dx*dy / r**2)*mid
|
||||
Ez = front*(dx*dz / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
|
||||
elif orientation.upper() == 'Y':
|
||||
# x--> y, y--> z, z-->x
|
||||
Ey = front*((dy**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ez = front*(dy*dz / r**2)*mid
|
||||
Ex = front*(dy*dx / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
|
||||
elif orientation.upper() == 'Z':
|
||||
# x --> z, y --> x, z --> y
|
||||
Ez = front*((dz**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
|
||||
Ex = front*(dz*dx / r**2)*mid
|
||||
Ey = front*(dz*dy / r**2)*mid
|
||||
return Ex, Ey, Ez
|
||||
# return Ey, Ez, Ex
|
||||
@@ -0,0 +1,98 @@
|
||||
from SimPEG import Utils, np
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
from SimPEG.EM.Utils.EMUtils import k
|
||||
|
||||
def getKc(freq,sigma,a,b,mu=mu_0,eps=epsilon_0):
|
||||
a = float(a)
|
||||
b = float(b)
|
||||
# return 1./(2*np.pi) * np.sqrt(b / a) * np.exp(-1j*k(freq,sigma,mu,eps)*(b-a))
|
||||
return np.sqrt(b / a) * np.exp(-1j*k(freq,sigma,mu,eps)*(b-a))
|
||||
|
||||
def _r2(xyz):
|
||||
return np.sum(xyz**2,1)
|
||||
|
||||
def _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
Kc1 = getKc(freq,sigma[1],a,b,mu[1],eps)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
sqrtr2z2 = np.sqrt(r2 + dxyz[:,2]**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return Kc1 * moment / (4.*np.pi) *np.exp(-1j*k2*sqrtr2z2) / sqrtr2z2
|
||||
|
||||
|
||||
def _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
sqrtr2z2 = np.sqrt(r2 + dxyz[:,2]**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return -HertzZ * np.sqrt(r2) / sqrtr2z2 * (1j*k2 + 1./ sqrtr2z2)
|
||||
|
||||
|
||||
def _getCasingHertzMagDipoleDeriv_z(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2z2 = _r2(dxyz)
|
||||
sqrtr2z2 = np.sqrt(r2z2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return -HertzZ*dxyz[:,2] /sqrtr2z2 * (1j*k2 + 1./sqrtr2z2)
|
||||
|
||||
def _getCasingHertzMagDipole2Deriv_z_r(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
dHertzZdr = _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
r = np.sqrt(r2)
|
||||
z = dxyz[:,2]
|
||||
sqrtr2z2 = np.sqrt(r2 + z**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return dHertzZdr*(-z/sqrtr2z2)*(1j*k2+1./sqrtr2z2) + HertzZ*(z*r/sqrtr2z2**3)*(1j*k2 + 2./sqrtr2z2)
|
||||
|
||||
def _getCasingHertzMagDipole2Deriv_z_z(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
dHertzZdz = _getCasingHertzMagDipoleDeriv_z(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
nobs = obsloc.shape[0]
|
||||
dxyz = obsloc - np.c_[np.ones(nobs)]*np.r_[srcloc]
|
||||
|
||||
r2 = _r2(dxyz[:,:2])
|
||||
r = np.sqrt(r2)
|
||||
z = dxyz[:,2]
|
||||
sqrtr2z2 = np.sqrt(r2 + z**2)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
|
||||
return (dHertzZdz*z + HertzZ)/sqrtr2z2*(-1j*k2 - 1./sqrtr2z2) + HertzZ*z/sqrtr2z2**3*(1j*k2*z + 2.*z/sqrtr2z2)
|
||||
|
||||
def getCasingEphiMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return 1j * omega(freq) * mu * _getCasingHertzMagDipoleDeriv_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingHrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return _getCasingHertzMagDipole2Deriv_z_r(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingHzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
d2HertzZdz2 = _getCasingHertzMagDipole2Deriv_z_z(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
k2 = k(freq,sigma[2],mu[2],eps)
|
||||
HertzZ = _getCasingHertzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
return d2HertzZdz2 + k2**2 * HertzZ
|
||||
|
||||
def getCasingBrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return mu_0 * getCasingHrMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
|
||||
def getCasingBzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu=mu_0*np.ones(3),eps=epsilon_0,moment=1.):
|
||||
return mu_0 * getCasingHzMagDipole(srcloc,obsloc,freq,sigma,a,b,mu,eps,moment)
|
||||
@@ -0,0 +1,12 @@
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, pi
|
||||
from scipy.special import erf
|
||||
|
||||
def hzAnalyticDipoleT(r, t, sigma):
|
||||
theta = np.sqrt((sigma*mu_0)/(4*t))
|
||||
tr = theta*r
|
||||
etr = erf(tr)
|
||||
t1 = (9/(2*tr**2) - 1)*etr
|
||||
t2 = (1/np.sqrt(pi))*(9/tr + 4*tr)*np.exp(-tr**2)
|
||||
hz = (t1 - t2)/(4*pi*r**3)
|
||||
return hz
|
||||
@@ -0,0 +1,3 @@
|
||||
from TDEM import hzAnalyticDipoleT
|
||||
from FDEM import hzAnalyticDipoleF
|
||||
from FDEMcasing import *
|
||||
@@ -0,0 +1,186 @@
|
||||
from SimPEG import Survey, Problem, Utils, Models, Maps, PropMaps, np, sp, Solver as SimpegSolver
|
||||
from scipy.constants import mu_0
|
||||
|
||||
class EMPropMap(Maps.PropMap):
|
||||
"""
|
||||
Property Map for EM Problems. The electrical conductivity (\\(\\sigma\\)) is the default inversion property, and the default value of the magnetic permeability is that of free space (\\(\\mu = 4\\pi\\times 10^{-7} \\) H/m)
|
||||
"""
|
||||
|
||||
sigma = Maps.Property("Electrical Conductivity", defaultInvProp = True, propertyLink=('rho',Maps.ReciprocalMap))
|
||||
mu = Maps.Property("Inverse Magnetic Permeability", defaultVal = mu_0, propertyLink=('mui',Maps.ReciprocalMap))
|
||||
|
||||
rho = Maps.Property("Electrical Resistivity", propertyLink=('sigma', Maps.ReciprocalMap))
|
||||
mui = Maps.Property("Inverse Magnetic Permeability", defaultVal = 1./mu_0, propertyLink=('mu', Maps.ReciprocalMap))
|
||||
|
||||
|
||||
class BaseEMProblem(Problem.BaseProblem):
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
Problem.BaseProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
|
||||
surveyPair = Survey.BaseSurvey
|
||||
dataPair = Survey.Data
|
||||
|
||||
PropMap = EMPropMap
|
||||
|
||||
Solver = SimpegSolver
|
||||
solverOpts = {}
|
||||
|
||||
verbose = False
|
||||
|
||||
####################################################
|
||||
# Make A Symmetric
|
||||
####################################################
|
||||
@property
|
||||
def _makeASymmetric(self):
|
||||
if getattr(self, '__makeASymmetric', None) is None:
|
||||
self.__makeASymmetric = True
|
||||
return self.__makeASymmetric
|
||||
|
||||
|
||||
####################################################
|
||||
# Mass Matrices
|
||||
####################################################
|
||||
|
||||
@property
|
||||
def deleteTheseOnModelUpdate(self):
|
||||
toDelete = []
|
||||
if self.mapping.sigmaMap is not None or self.mapping.rhoMap is not None:
|
||||
toDelete += ['_MeSigma', '_MeSigmaI','_MfRho','_MfRhoI']
|
||||
if self.mapping.muMap is not None or self.mapping.muiMap is not None:
|
||||
toDelete += ['_MeMu', '_MeMuI','_MfMui','_MfMuiI']
|
||||
return toDelete
|
||||
|
||||
@property
|
||||
def Me(self):
|
||||
"""
|
||||
Edge inner product matrix
|
||||
"""
|
||||
if getattr(self, '_Me', None) is None:
|
||||
self._Me = self.mesh.getEdgeInnerProduct()
|
||||
return self._Me
|
||||
|
||||
@property
|
||||
def Mf(self):
|
||||
"""
|
||||
Face inner product matrix
|
||||
"""
|
||||
if getattr(self, '_Mf', None) is None:
|
||||
self._Mf = self.mesh.getFaceInnerProduct()
|
||||
return self._Mf
|
||||
|
||||
|
||||
# ----- Magnetic Permeability ----- #
|
||||
@property
|
||||
def MfMui(self):
|
||||
"""
|
||||
Face inner product matrix for \\(\\mu^{-1}\\). Used in the E-B formulation
|
||||
"""
|
||||
if getattr(self, '_MfMui', None) is None:
|
||||
self._MfMui = self.mesh.getFaceInnerProduct(self.curModel.mui)
|
||||
return self._MfMui
|
||||
|
||||
@property
|
||||
def MfMuiI(self):
|
||||
"""
|
||||
Inverse of :code:`MfMui`.
|
||||
"""
|
||||
if getattr(self, '_MfMuiI', None) is None:
|
||||
self._MfMuiI = self.mesh.getFaceInnerProduct(self.curModel.mui, invMat=True)
|
||||
return self._MfMuiI
|
||||
|
||||
@property
|
||||
def MeMu(self):
|
||||
"""
|
||||
Edge inner product matrix for \\(\\mu\\). Used in the H-J formulation
|
||||
"""
|
||||
if getattr(self, '_MeMu', None) is None:
|
||||
self._MeMu = self.mesh.getEdgeInnerProduct(self.curModel.mu)
|
||||
return self._MeMu
|
||||
|
||||
@property
|
||||
def MeMuI(self):
|
||||
"""
|
||||
Inverse of :code:`MeMu`
|
||||
"""
|
||||
if getattr(self, '_MeMuI', None) is None:
|
||||
self._MeMuI = self.mesh.getEdgeInnerProduct(self.curModel.mu, invMat=True)
|
||||
return self._MeMuI
|
||||
|
||||
|
||||
# ----- Electrical Conductivity ----- #
|
||||
#TODO: hardcoded to sigma as the model
|
||||
@property
|
||||
def MeSigma(self):
|
||||
"""
|
||||
Edge inner product matrix for \\(\\sigma\\). Used in the E-B formulation
|
||||
"""
|
||||
if getattr(self, '_MeSigma', None) is None:
|
||||
self._MeSigma = self.mesh.getEdgeInnerProduct(self.curModel.sigma)
|
||||
return self._MeSigma
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MeSigmaDeriv(self, u):
|
||||
"""
|
||||
Derivative of MeSigma with respect to the model
|
||||
"""
|
||||
return self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma)(u) * self.curModel.sigmaDeriv
|
||||
|
||||
|
||||
@property
|
||||
def MeSigmaI(self):
|
||||
"""
|
||||
Inverse of the edge inner product matrix for \\(\\sigma\\).
|
||||
"""
|
||||
if getattr(self, '_MeSigmaI', None) is None:
|
||||
self._MeSigmaI = self.mesh.getEdgeInnerProduct(self.curModel.sigma, invMat=True)
|
||||
return self._MeSigmaI
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MeSigmaIDeriv(self, u):
|
||||
"""
|
||||
Derivative of :code:`MeSigma` with respect to the model
|
||||
"""
|
||||
# TODO: only works for diagonal tensors. getEdgeInnerProductDeriv, invMat=True should be implemented in SimPEG
|
||||
|
||||
dMeSigmaI_dI = -self.MeSigmaI**2
|
||||
dMe_dsig = self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma)(u)
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
return dMeSigmaI_dI * ( dMe_dsig * ( dsig_dm))
|
||||
# return self.mesh.getEdgeInnerProductDeriv(self.curModel.sigma, invMat=True)(u)
|
||||
|
||||
|
||||
@property
|
||||
def MfRho(self):
|
||||
"""
|
||||
Face inner product matrix for \\(\\rho\\). Used in the H-J formulation
|
||||
"""
|
||||
if getattr(self, '_MfRho', None) is None:
|
||||
self._MfRho = self.mesh.getFaceInnerProduct(self.curModel.rho)
|
||||
return self._MfRho
|
||||
|
||||
# TODO: This should take a vector
|
||||
def MfRhoDeriv(self,u):
|
||||
"""
|
||||
Derivative of :code:`MfRho` with respect to the model.
|
||||
"""
|
||||
return self.mesh.getFaceInnerProductDeriv(self.curModel.rho)(u) * (-Utils.sdiag(self.curModel.rho**2) * self.curModel.sigmaDeriv)
|
||||
# self.curModel.rhoDeriv
|
||||
|
||||
@property
|
||||
def MfRhoI(self):
|
||||
"""
|
||||
Inverse of :code:`MfRho`
|
||||
"""
|
||||
if getattr(self, '_MfRhoI', None) is None:
|
||||
self._MfRhoI = self.mesh.getFaceInnerProduct(self.curModel.rho, invMat=True)
|
||||
return self._MfRhoI
|
||||
|
||||
# TODO: This isn't going to work yet
|
||||
# TODO: This should take a vector
|
||||
def MfRhoIDeriv(self,u):
|
||||
"""
|
||||
Derivative of :code:`MfRhoI` with respect to the model.
|
||||
"""
|
||||
return self.mesh.getFaceInnerProductDeriv(self.curModel.rho, invMat=True)(u) * self.curModel.rhoDeriv
|
||||
@@ -0,0 +1,573 @@
|
||||
from SimPEG import Problem, Utils, np, sp, Solver as SimpegSolver
|
||||
from scipy.constants import mu_0
|
||||
from SurveyFDEM import Survey as SurveyFDEM
|
||||
from FieldsFDEM import Fields, Fields_e, Fields_b, Fields_h, Fields_j
|
||||
from SimPEG.EM.Base import BaseEMProblem
|
||||
from SimPEG.EM.Utils import omega
|
||||
|
||||
|
||||
class BaseFDEMProblem(BaseEMProblem):
|
||||
"""
|
||||
We start by looking at Maxwell's equations in the electric
|
||||
field \\\(\\\mathbf{e}\\\) and the magnetic flux
|
||||
density \\\(\\\mathbf{b}\\\)
|
||||
|
||||
.. 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}}
|
||||
|
||||
if using the E-B formulation (:code:`Problem_e`
|
||||
or :code:`Problem_b`) or the magnetic field
|
||||
\\\(\\\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} \mathbf{h} - \mathbf{j} = \mathbf{s_e}
|
||||
|
||||
if using the H-J formulation (:code:`Problem_j` or :code:`Problem_h`).
|
||||
|
||||
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
|
||||
"""
|
||||
|
||||
surveyPair = SurveyFDEM
|
||||
fieldsPair = Fields
|
||||
|
||||
def fields(self, m=None):
|
||||
"""
|
||||
Solve the forward problem for the fields.
|
||||
"""
|
||||
|
||||
self.curModel = m
|
||||
F = self.fieldsPair(self.mesh, self.survey)
|
||||
|
||||
for freq in self.survey.freqs:
|
||||
A = self.getA(freq)
|
||||
rhs = self.getRHS(freq)
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
sol = Ainv * rhs
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
ftype = self._fieldType + 'Solution'
|
||||
F[Srcs, ftype] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
Sensitivity times a vector
|
||||
"""
|
||||
|
||||
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) #
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
|
||||
for src in self.survey.getSrcByFreq(freq):
|
||||
ftype = self._fieldType + 'Solution'
|
||||
u_src = u[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 )
|
||||
|
||||
for rx in src.rxList:
|
||||
df_duFun = getattr(u, '_%sDeriv_u'%rx.projField, None)
|
||||
df_dudu_dm = df_duFun(src, du_dm, adjoint=False)
|
||||
|
||||
df_dmFun = getattr(u, '_%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, u, v) # wrt u, also have wrt m
|
||||
|
||||
Jv[src, rx] = P(Df_Dm)
|
||||
|
||||
Ainv.clean()
|
||||
return Utils.mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
"""
|
||||
Sensitivity transpose times a vector
|
||||
"""
|
||||
|
||||
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, ftype]
|
||||
|
||||
for rx in src.rxList:
|
||||
PTv = rx.projectFieldsDeriv(src, self.mesh, u, v[src, rx], adjoint=True) # wrt u, need possibility wrt m
|
||||
|
||||
df_duTFun = getattr(u, '_%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)
|
||||
du_dmT = -dA_dmT + dRHS_dmT
|
||||
|
||||
df_dmFun = getattr(u, '_%sDeriv_m'%rx.projField, None)
|
||||
dfT_dm = df_dmFun(src, PTv, adjoint=True)
|
||||
|
||||
du_dmT += dfT_dm
|
||||
|
||||
real_or_imag = rx.projComp
|
||||
if real_or_imag is 'real':
|
||||
Jtv += np.array(du_dmT,dtype=complex).real
|
||||
elif real_or_imag is 'imag':
|
||||
Jtv += - np.array(du_dmT,dtype=complex).real
|
||||
else:
|
||||
raise Exception('Must be real or imag')
|
||||
|
||||
ATinv.clean()
|
||||
|
||||
return Utils.mkvc(Jtv)
|
||||
|
||||
def getSourceTerm(self, freq):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
Srcs = self.survey.getSrcByFreq(freq)
|
||||
if self._eqLocs is 'FE':
|
||||
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':
|
||||
S_m = np.zeros((self.mesh.nE,len(Srcs)), dtype=complex)
|
||||
S_e = np.zeros((self.mesh.nF,len(Srcs)), dtype=complex)
|
||||
|
||||
for i, src in enumerate(Srcs):
|
||||
smi, sei = src.eval(self)
|
||||
S_m[:,i] = S_m[:,i] + smi
|
||||
S_e[:,i] = S_e[:,i] + sei
|
||||
|
||||
return S_m, S_e
|
||||
|
||||
|
||||
##########################################################################################
|
||||
################################ E-B Formulation #########################################
|
||||
##########################################################################################
|
||||
|
||||
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:
|
||||
|
||||
.. 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}
|
||||
|
||||
which we solve for \\\(\\\mathbf{e}\\\).
|
||||
"""
|
||||
|
||||
_fieldType = 'e'
|
||||
_eqLocs = 'FE'
|
||||
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}}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
MfMui = self.MfMui
|
||||
MeSigma = self.MeSigma
|
||||
C = self.mesh.edgeCurl
|
||||
|
||||
return C.T*MfMui*C + 1j*omega(freq)*MeSigma
|
||||
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
dsig_dm = self.curModel.sigmaDeriv
|
||||
dMe_dsig = self.MeSigmaDeriv(u)
|
||||
|
||||
if adjoint:
|
||||
return 1j * omega(freq) * ( dMe_dsig.T * v )
|
||||
|
||||
return 1j * omega(freq) * ( dMe_dsig * v )
|
||||
|
||||
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}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
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 RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
MfMui = self.MfMui
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
if adjoint:
|
||||
dRHS = MfMui * (C * v)
|
||||
return S_mDeriv(dRHS) - 1j * omega(freq) * S_eDeriv(v)
|
||||
|
||||
else:
|
||||
return C.T * (MfMui * S_mDeriv(v)) -1j * omega(freq) * S_eDeriv(v)
|
||||
|
||||
|
||||
class Problem_b(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{e}\\\) using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{e} = \mathbf{M^e_{\sigma}}^{-1} \\left(\mathbf{C}^T \mathbf{M_{\mu^{-1}}^f} \mathbf{b} - \mathbf{s_e}\\right)
|
||||
|
||||
and solve for \\\(\\\mathbf{b}\\\) using:
|
||||
|
||||
.. 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}
|
||||
|
||||
.. note ::
|
||||
The inverse problem will not work with full anisotropy
|
||||
"""
|
||||
|
||||
_fieldType = 'b'
|
||||
_eqLocs = 'FE'
|
||||
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
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MfMui = self.MfMui
|
||||
MeSigmaI = self.MeSigmaI
|
||||
C = self.mesh.edgeCurl
|
||||
iomega = 1j * omega(freq) * sp.eye(self.mesh.nF)
|
||||
|
||||
A = C * (MeSigmaI * (C.T * MfMui)) + iomega
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfMui.T*A
|
||||
return A
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
|
||||
MfMui = self.MfMui
|
||||
C = self.mesh.edgeCurl
|
||||
MeSigmaIDeriv = self.MeSigmaIDeriv
|
||||
vec = C.T * (MfMui * u)
|
||||
|
||||
MeSigmaIDeriv = MeSigmaIDeriv(vec)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric is True:
|
||||
v = MfMui * v
|
||||
return MeSigmaIDeriv.T * (C.T * v)
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfMui.T * ( C * ( MeSigmaIDeriv * v ) )
|
||||
return C * ( MeSigmaIDeriv * v )
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
\mathbf{RHS} = \mathbf{s_m} + \mathbf{M^e_{\sigma}}^{-1}\mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MeSigmaI = self.MeSigmaI
|
||||
|
||||
RHS = S_m + C * ( MeSigmaI * S_e )
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
MfMui = self.MfMui
|
||||
return MfMui.T * RHS
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
S_m, S_e = src.eval(self)
|
||||
MfMui = self.MfMui
|
||||
|
||||
if self._makeASymmetric and adjoint:
|
||||
v = self.MfMui * v
|
||||
|
||||
MeSigmaIDeriv = self.MeSigmaIDeriv(S_e)
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
if not adjoint:
|
||||
RHSderiv = C * (MeSigmaIDeriv * v)
|
||||
SrcDeriv = S_mDeriv(v) + C * (self.MeSigmaI * S_eDeriv(v))
|
||||
elif adjoint:
|
||||
RHSderiv = MeSigmaIDeriv.T * (C.T * v)
|
||||
SrcDeriv = S_mDeriv(v) + self.MeSigmaI.T * (C.T * S_eDeriv(v))
|
||||
|
||||
if self._makeASymmetric is True and not adjoint:
|
||||
return MfMui.T * (SrcDeriv + RHSderiv)
|
||||
|
||||
return RHSderiv + SrcDeriv
|
||||
|
||||
|
||||
|
||||
##########################################################################################
|
||||
################################ H-J Formulation #########################################
|
||||
##########################################################################################
|
||||
|
||||
|
||||
class Problem_j(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. 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)
|
||||
|
||||
and solve for \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. 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}
|
||||
|
||||
.. note::
|
||||
This implementation does not yet work with full anisotropy!!
|
||||
|
||||
"""
|
||||
|
||||
_fieldType = 'j'
|
||||
_eqLocs = 'EF'
|
||||
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
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
iomega = 1j * omega(freq) * sp.eye(self.mesh.nF)
|
||||
|
||||
A = C * MeMuI * C.T * MfRho + iomega
|
||||
|
||||
if self._makeASymmetric is True:
|
||||
return MfRho.T*A
|
||||
return A
|
||||
|
||||
|
||||
def getADeriv_m(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
|
||||
|
||||
.. math ::
|
||||
|
||||
\\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}}
|
||||
"""
|
||||
|
||||
MeMuI = self.MeMuI
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(u)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric is True:
|
||||
v = MfRho * v
|
||||
return MfRhoDeriv_m.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)))
|
||||
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. 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)
|
||||
C = self.mesh.edgeCurl
|
||||
MeMuI = self.MeMuI
|
||||
|
||||
RHS = C * (MeMuI * S_m) - 1j * omega(freq) * S_e
|
||||
if self._makeASymmetric is True:
|
||||
MfRho = self.MfRho
|
||||
return MfRho.T*RHS
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
C = self.mesh.edgeCurl
|
||||
MeMuI = self.MeMuI
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
if adjoint:
|
||||
if self._makeASymmetric:
|
||||
MfRho = self.MfRho
|
||||
v = MfRho*v
|
||||
return S_mDeriv(MeMuI.T * (C.T * v)) - 1j * omega(freq) * S_eDeriv(v)
|
||||
|
||||
else:
|
||||
RHSDeriv = C * (MeMuI * S_mDeriv(v)) - 1j * omega(freq) * S_eDeriv(v)
|
||||
|
||||
if self._makeASymmetric:
|
||||
MfRho = self.MfRho
|
||||
return MfRho.T * RHSDeriv
|
||||
return RHSDeriv
|
||||
|
||||
|
||||
|
||||
|
||||
class Problem_h(BaseFDEMProblem):
|
||||
"""
|
||||
We eliminate \\\(\\\mathbf{j}\\\) using
|
||||
|
||||
.. math ::
|
||||
|
||||
\mathbf{j} = \mathbf{C} \mathbf{h} - \mathbf{s_e}
|
||||
|
||||
and solve for \\\(\\\mathbf{h}\\\) using
|
||||
|
||||
.. 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}
|
||||
|
||||
"""
|
||||
|
||||
_fieldType = 'h'
|
||||
_eqLocs = 'EF'
|
||||
fieldsPair = Fields_h
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
BaseFDEMProblem.__init__(self, mesh, **kwargs)
|
||||
|
||||
def getA(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
|
||||
\mathbf{A} = \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
|
||||
MeMu = self.MeMu
|
||||
MfRho = self.MfRho
|
||||
C = self.mesh.edgeCurl
|
||||
|
||||
return C.T * (MfRho * C) + 1j*omega(freq)*MeMu
|
||||
|
||||
def getADeriv_m(self, freq, u, v, adjoint=False):
|
||||
|
||||
MeMu = self.MeMu
|
||||
C = self.mesh.edgeCurl
|
||||
MfRhoDeriv_m = self.MfRhoDeriv(C*u)
|
||||
|
||||
if adjoint:
|
||||
return MfRhoDeriv_m.T * (C * v)
|
||||
return C.T * (MfRhoDeriv_m * v)
|
||||
|
||||
def getRHS(self, freq):
|
||||
"""
|
||||
.. math ::
|
||||
|
||||
\mathbf{RHS} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^T \mathbf{M_{\\rho}^f} \mathbf{s_e}
|
||||
|
||||
:param float freq: Frequency
|
||||
:rtype: numpy.ndarray (nE, nSrc)
|
||||
:return: RHS
|
||||
"""
|
||||
|
||||
S_m, S_e = self.getSourceTerm(freq)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
|
||||
RHS = S_m + C.T * ( MfRho * S_e )
|
||||
|
||||
return RHS
|
||||
|
||||
def getRHSDeriv_m(self, freq, src, v, adjoint=False):
|
||||
_, S_e = src.eval(self)
|
||||
C = self.mesh.edgeCurl
|
||||
MfRho = self.MfRho
|
||||
|
||||
MfRhoDeriv = self.MfRhoDeriv(S_e)
|
||||
if not adjoint:
|
||||
RHSDeriv = C.T * (MfRhoDeriv * v)
|
||||
elif adjoint:
|
||||
RHSDeriv = MfRhoDeriv.T * (C * v)
|
||||
|
||||
S_mDeriv, S_eDeriv = src.evalDeriv(self, adjoint)
|
||||
|
||||
return RHSDeriv + S_mDeriv(v) + C.T * (MfRho * S_eDeriv(v))
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
import numpy as np
|
||||
import scipy.sparse as sp
|
||||
import SimPEG
|
||||
from SimPEG import Utils
|
||||
from SimPEG.EM.Utils import omega
|
||||
from SimPEG.Utils import Zero, Identity
|
||||
|
||||
|
||||
class Fields(SimPEG.Problem.Fields):
|
||||
"""Fancy Field Storage for a FDEM survey."""
|
||||
knownFields = {}
|
||||
dtype = complex
|
||||
|
||||
class Fields_e(Fields):
|
||||
knownFields = {'eSolution':'E'}
|
||||
aliasFields = {
|
||||
'e' : ['eSolution','E','_e'],
|
||||
'ePrimary' : ['eSolution','E','_ePrimary'],
|
||||
'eSecondary' : ['eSolution','E','_eSecondary'],
|
||||
'b' : ['eSolution','F','_b'],
|
||||
'bPrimary' : ['eSolution','F','_bPrimary'],
|
||||
'bSecondary' : ['eSolution','F','_bSecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
Fields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
|
||||
def _ePrimary(self, eSolution, srcList):
|
||||
ePrimary = np.zeros_like(eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.prob)
|
||||
ePrimary[:,i] = ePrimary[:,i] + ep
|
||||
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 Identity()*v
|
||||
|
||||
def _eDeriv_m(self, src, v, adjoint = False):
|
||||
# assuming primary does not depend on the model
|
||||
return Zero()
|
||||
|
||||
def _bPrimary(self, eSolution, srcList):
|
||||
bPrimary = np.zeros([self._edgeCurl.shape[0],eSolution.shape[1]],dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.prob)
|
||||
bPrimary[:,i] = bPrimary[:,i] + bp
|
||||
return bPrimary
|
||||
|
||||
def _bSecondary(self, eSolution, srcList):
|
||||
C = self._edgeCurl
|
||||
b = (C * eSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
b[:,i] *= - 1./(1j*omega(src.freq))
|
||||
S_m, _ = src.eval(self.prob)
|
||||
b[:,i] = b[:,i]+ 1./(1j*omega(src.freq)) * S_m
|
||||
return b
|
||||
|
||||
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
|
||||
C = self._edgeCurl
|
||||
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):
|
||||
S_mDeriv, _ = src.evalDeriv(self.prob, adjoint)
|
||||
S_mDeriv = S_mDeriv(v)
|
||||
return 1./(1j * omega(src.freq)) * S_mDeriv
|
||||
|
||||
def _b(self, eSolution, srcList):
|
||||
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class Fields_b(Fields):
|
||||
knownFields = {'bSolution':'F'}
|
||||
aliasFields = {
|
||||
'b' : ['bSolution','F','_b'],
|
||||
'bPrimary' : ['bSolution','F','_bPrimary'],
|
||||
'bSecondary' : ['bSolution','F','_bSecondary'],
|
||||
'e' : ['bSolution','E','_e'],
|
||||
'ePrimary' : ['bSolution','E','_ePrimary'],
|
||||
'eSecondary' : ['bSolution','E','_eSecondary'],
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
Fields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self._MfMui = self.survey.prob.MfMui
|
||||
self._MeSigmaIDeriv = self.survey.prob.MeSigmaIDeriv
|
||||
self._Me = self.survey.prob.Me
|
||||
|
||||
def _bPrimary(self, bSolution, srcList):
|
||||
bPrimary = np.zeros_like(bSolution)
|
||||
for i, src in enumerate(srcList):
|
||||
bp = src.bPrimary(self.prob)
|
||||
bPrimary[:,i] = bPrimary[:,i] + bp
|
||||
return bPrimary
|
||||
|
||||
def _bSecondary(self, bSolution, srcList):
|
||||
return bSolution
|
||||
|
||||
def _b(self, bSolution, srcList):
|
||||
return self._bPrimary(bSolution, srcList) + self._bSecondary(bSolution, srcList)
|
||||
|
||||
def _bDeriv_u(self, src, v, adjoint=False):
|
||||
return Identity()*v
|
||||
|
||||
def _bDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return Zero()
|
||||
|
||||
def _ePrimary(self, bSolution, srcList):
|
||||
ePrimary = np.zeros([self._edgeCurl.shape[1],bSolution.shape[1]],dtype = complex)
|
||||
for i,src in enumerate(srcList):
|
||||
ep = src.ePrimary(self.prob)
|
||||
ePrimary[:,i] = ePrimary[:,i] + ep
|
||||
return ePrimary
|
||||
|
||||
def _eSecondary(self, bSolution, srcList):
|
||||
e = self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * bSolution))
|
||||
for i,src in enumerate(srcList):
|
||||
_,S_e = src.eval(self.prob)
|
||||
e[:,i] = e[:,i]+ -self._MeSigmaI * S_e
|
||||
return e
|
||||
|
||||
def _eSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return self._MeSigmaI * ( self._edgeCurl.T * ( self._MfMui * v) )
|
||||
else:
|
||||
return self._MfMui.T * (self._edgeCurl * (self._MeSigmaI.T * v))
|
||||
|
||||
def _eSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
bSolution = self[[src],'bSolution']
|
||||
_,S_e = src.eval(self.prob)
|
||||
Me = self._Me
|
||||
|
||||
if adjoint:
|
||||
Me = Me.T
|
||||
|
||||
w = self._edgeCurl.T * (self._MfMui * bSolution)
|
||||
w = w - Utils.mkvc(Me * S_e,2)
|
||||
|
||||
if not adjoint:
|
||||
de_dm = self._MeSigmaIDeriv(w) * v
|
||||
elif adjoint:
|
||||
de_dm = self._MeSigmaIDeriv(w).T * v
|
||||
|
||||
_, S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
||||
Se_Deriv = S_eDeriv(v)
|
||||
|
||||
de_dm = de_dm - self._MeSigmaI * Se_Deriv
|
||||
|
||||
return de_dm
|
||||
|
||||
def _e(self, bSolution, srcList):
|
||||
return self._ePrimary(bSolution, srcList) + self._eSecondary(bSolution, srcList)
|
||||
|
||||
def _eDeriv_u(self, src, v, adjoint=False):
|
||||
return self._eSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _eDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary doesn't depend on model
|
||||
return self._eSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
|
||||
class Fields_j(Fields):
|
||||
knownFields = {'jSolution':'F'}
|
||||
aliasFields = {
|
||||
'j' : ['jSolution','F','_j'],
|
||||
'jPrimary' : ['jSolution','F','_jPrimary'],
|
||||
'jSecondary' : ['jSolution','F','_jSecondary'],
|
||||
'h' : ['jSolution','E','_h'],
|
||||
'hPrimary' : ['jSolution','E','_hPrimary'],
|
||||
'hSecondary' : ['jSolution','E','_hSecondary'],
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
Fields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeMuI = self.survey.prob.MeMuI
|
||||
self._MfRho = self.survey.prob.MfRho
|
||||
self._MfRhoDeriv = self.survey.prob.MfRhoDeriv
|
||||
self._Me = self.survey.prob.Me
|
||||
|
||||
def _jPrimary(self, jSolution, srcList):
|
||||
jPrimary = np.zeros_like(jSolution,dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
jp = src.jPrimary(self.prob)
|
||||
jPrimary[:,i] = jPrimary[:,i] + jp
|
||||
return jPrimary
|
||||
|
||||
def _jSecondary(self, jSolution, srcList):
|
||||
return jSolution
|
||||
|
||||
def _j(self, jSolution, srcList):
|
||||
return self._jPrimary(jSolution, srcList) + self._jSecondary(jSolution, srcList)
|
||||
|
||||
def _jDeriv_u(self, src, v, adjoint=False):
|
||||
return Identity()*v
|
||||
|
||||
def _jDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return Zero()
|
||||
|
||||
def _hPrimary(self, jSolution, srcList):
|
||||
hPrimary = np.zeros([self._edgeCurl.shape[1],jSolution.shape[1]],dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
hp = src.hPrimary(self.prob)
|
||||
hPrimary[:,i] = hPrimary[:,i] + hp
|
||||
return hPrimary
|
||||
|
||||
def _hSecondary(self, jSolution, srcList):
|
||||
h = self._MeMuI * (self._edgeCurl.T * (self._MfRho * jSolution) )
|
||||
for i, src in enumerate(srcList):
|
||||
h[:,i] *= -1./(1j*omega(src.freq))
|
||||
S_m,_ = src.eval(self.prob)
|
||||
h[:,i] = h[:,i]+ 1./(1j*omega(src.freq)) * self._MeMuI * (S_m)
|
||||
return h
|
||||
|
||||
def _hSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return -1./(1j*omega(src.freq)) * self._MeMuI * (self._edgeCurl.T * (self._MfRho * v) )
|
||||
elif adjoint:
|
||||
return -1./(1j*omega(src.freq)) * self._MfRho.T * (self._edgeCurl * ( self._MeMuI.T * v))
|
||||
|
||||
def _hSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
jSolution = self[[src],'jSolution']
|
||||
MeMuI = self._MeMuI
|
||||
C = self._edgeCurl
|
||||
MfRho = self._MfRho
|
||||
MfRhoDeriv = self._MfRhoDeriv
|
||||
Me = self._Me
|
||||
|
||||
if not adjoint:
|
||||
hDeriv_m = -1./(1j*omega(src.freq)) * MeMuI * (C.T * (MfRhoDeriv(jSolution)*v ) )
|
||||
elif adjoint:
|
||||
hDeriv_m = -1./(1j*omega(src.freq)) * MfRhoDeriv(jSolution).T * ( C * (MeMuI.T * v ) )
|
||||
|
||||
S_mDeriv,_ = src.evalDeriv(self.prob, adjoint)
|
||||
|
||||
if not adjoint:
|
||||
S_mDeriv = S_mDeriv(v)
|
||||
hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * MeMuI * (Me * S_mDeriv)
|
||||
elif adjoint:
|
||||
S_mDeriv = S_mDeriv(Me.T * (MeMuI.T * v))
|
||||
hDeriv_m = hDeriv_m + 1./(1j*omega(src.freq)) * S_mDeriv
|
||||
return hDeriv_m
|
||||
|
||||
|
||||
def _h(self, jSolution, srcList):
|
||||
return self._hPrimary(jSolution, srcList) + self._hSecondary(jSolution, srcList)
|
||||
|
||||
def _hDeriv_u(self, src, v, adjoint=False):
|
||||
return self._hSecondaryDeriv_u(src, v, adjoint)
|
||||
|
||||
def _hDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming the primary doesn't depend on the model
|
||||
return self._hSecondaryDeriv_m(src, v, adjoint)
|
||||
|
||||
|
||||
class Fields_h(Fields):
|
||||
knownFields = {'hSolution':'E'}
|
||||
aliasFields = {
|
||||
'h' : ['hSolution','E','_h'],
|
||||
'hPrimary' : ['hSolution','E','_hPrimary'],
|
||||
'hSecondary' : ['hSolution','E','_hSecondary'],
|
||||
'j' : ['hSolution','F','_j'],
|
||||
'jPrimary' : ['hSolution','F','_jPrimary'],
|
||||
'jSecondary' : ['hSolution','F','_jSecondary']
|
||||
}
|
||||
|
||||
def __init__(self,mesh,survey,**kwargs):
|
||||
Fields.__init__(self,mesh,survey,**kwargs)
|
||||
|
||||
def startup(self):
|
||||
self.prob = self.survey.prob
|
||||
self._edgeCurl = self.survey.prob.mesh.edgeCurl
|
||||
self._MeMuI = self.survey.prob.MeMuI
|
||||
self._MfRho = self.survey.prob.MfRho
|
||||
|
||||
def _hPrimary(self, hSolution, srcList):
|
||||
hPrimary = np.zeros_like(hSolution,dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
hp = src.hPrimary(self.prob)
|
||||
hPrimary[:,i] = hPrimary[:,i] + hp
|
||||
return hPrimary
|
||||
|
||||
def _hSecondary(self, hSolution, srcList):
|
||||
return hSolution
|
||||
|
||||
def _h(self, hSolution, srcList):
|
||||
return self._hPrimary(hSolution, srcList) + self._hSecondary(hSolution, srcList)
|
||||
|
||||
def _hDeriv_u(self, src, v, adjoint=False):
|
||||
return Identity()*v
|
||||
|
||||
def _hDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming primary does not depend on the model
|
||||
return Zero()
|
||||
|
||||
def _jPrimary(self, hSolution, srcList):
|
||||
jPrimary = np.zeros([self._edgeCurl.shape[0], hSolution.shape[1]], dtype = complex)
|
||||
for i, src in enumerate(srcList):
|
||||
jp = src.jPrimary(self.prob)
|
||||
jPrimary[:,i] = jPrimary[:,i] + jp
|
||||
return jPrimary
|
||||
|
||||
def _jSecondary(self, hSolution, srcList):
|
||||
j = self._edgeCurl*hSolution
|
||||
for i, src in enumerate(srcList):
|
||||
_,S_e = src.eval(self.prob)
|
||||
j[:,i] = j[:,i]+ -S_e
|
||||
return j
|
||||
|
||||
def _jSecondaryDeriv_u(self, src, v, adjoint=False):
|
||||
if not adjoint:
|
||||
return self._edgeCurl*v
|
||||
elif adjoint:
|
||||
return self._edgeCurl.T*v
|
||||
|
||||
def _jSecondaryDeriv_m(self, src, v, adjoint=False):
|
||||
_,S_eDeriv = src.evalDeriv(self.prob, adjoint)
|
||||
S_eDeriv = S_eDeriv(v)
|
||||
return -S_eDeriv
|
||||
|
||||
def _j(self, hSolution, srcList):
|
||||
return self._jPrimary(hSolution, srcList) + self._jSecondary(hSolution, srcList)
|
||||
|
||||
def _jDeriv_u(self, src, v, adjoint=False):
|
||||
return self._jSecondaryDeriv_u(src,v,adjoint)
|
||||
|
||||
def _jDeriv_m(self, src, v, adjoint=False):
|
||||
# assuming the primary does not depend on the model
|
||||
return self._jSecondaryDeriv_m(src,v,adjoint)
|
||||
@@ -0,0 +1,316 @@
|
||||
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):
|
||||
freq = None
|
||||
# rxPair = Rx
|
||||
integrate = True
|
||||
|
||||
def eval(self, prob):
|
||||
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 bPrimary(self, prob):
|
||||
return Zero()
|
||||
|
||||
def hPrimary(self, prob):
|
||||
return Zero()
|
||||
|
||||
def ePrimary(self, prob):
|
||||
return Zero()
|
||||
|
||||
def jPrimary(self, prob):
|
||||
return Zero()
|
||||
|
||||
def S_m(self, prob):
|
||||
return Zero()
|
||||
|
||||
def S_e(self, prob):
|
||||
return Zero()
|
||||
|
||||
def S_mDeriv(self, prob, v, adjoint = False):
|
||||
return Zero()
|
||||
|
||||
def S_eDeriv(self, prob, v, adjoint = False):
|
||||
return Zero()
|
||||
|
||||
|
||||
class RawVec_e(BaseSrc):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
def __init__(self, rxList, freq, S_e): #, ePrimary=None, bPrimary=None, hPrimary=None, jPrimary=None):
|
||||
self._S_e = np.array(S_e,dtype=complex)
|
||||
self.freq = float(freq)
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def S_e(self, prob):
|
||||
return self._S_e
|
||||
|
||||
|
||||
class RawVec_m(BaseSrc):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
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):
|
||||
return self._S_m
|
||||
|
||||
|
||||
class RawVec(BaseSrc):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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:
|
||||
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:
|
||||
return prob.Me * self._S_e
|
||||
return self._S_e
|
||||
|
||||
|
||||
class MagDipole(BaseSrc):
|
||||
|
||||
#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):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
self.orientation = orientation
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
self.integrate = False
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
a = MagneticDipoleVectorPotential(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
|
||||
else:
|
||||
srcfct = MagneticDipoleVectorPotential
|
||||
ax = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
ay = srcfct(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
az = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
a = np.concatenate((ax, ay, az))
|
||||
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob,b)
|
||||
|
||||
def S_m(self, prob):
|
||||
b_p = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b_p
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
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
|
||||
def __init__(self, rxList, freq, loc, orientation='Z', moment=1., mu = mu_0):
|
||||
self.freq = float(freq)
|
||||
self.loc = loc
|
||||
self.orientation = orientation
|
||||
self.moment = moment
|
||||
self.mu = mu
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
srcfct = MagneticDipoleFields
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
bx = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
bz = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
b = np.concatenate((bx,bz))
|
||||
else:
|
||||
bx = srcfct(self.loc, gridX, 'x', mu=self.mu, moment=self.moment)
|
||||
by = srcfct(self.loc, gridY, 'y', mu=self.mu, moment=self.moment)
|
||||
bz = srcfct(self.loc, gridZ, 'z', mu=self.mu, moment=self.moment)
|
||||
b = np.concatenate((bx,by,bz))
|
||||
|
||||
return b
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return h_from_b(prob, b)
|
||||
|
||||
def S_m(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
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):
|
||||
|
||||
#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):
|
||||
self.freq = float(freq)
|
||||
self.orientation = orientation
|
||||
self.radius = radius
|
||||
self.mu = mu
|
||||
self.loc = loc
|
||||
self.integrate = False
|
||||
BaseSrc.__init__(self, rxList)
|
||||
|
||||
def bPrimary(self, prob):
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
gridX = prob.mesh.gridEx
|
||||
gridY = prob.mesh.gridEy
|
||||
gridZ = prob.mesh.gridEz
|
||||
C = prob.mesh.edgeCurl
|
||||
|
||||
elif eqLocs is 'EF':
|
||||
gridX = prob.mesh.gridFx
|
||||
gridY = prob.mesh.gridFy
|
||||
gridZ = prob.mesh.gridFz
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
if prob.mesh._meshType is 'CYL':
|
||||
if not prob.mesh.isSymmetric:
|
||||
# TODO ?
|
||||
raise NotImplementedError('Non-symmetric cyl mesh not implemented yet!')
|
||||
a = MagneticDipoleVectorPotential(self.loc, gridY, 'y', moment=self.radius, mu=self.mu)
|
||||
|
||||
else:
|
||||
srcfct = MagneticDipoleVectorPotential
|
||||
ax = srcfct(self.loc, gridX, 'x', self.radius, mu=self.mu)
|
||||
ay = srcfct(self.loc, gridY, 'y', self.radius, mu=self.mu)
|
||||
az = srcfct(self.loc, gridZ, 'z', self.radius, mu=self.mu)
|
||||
a = np.concatenate((ax, ay, az))
|
||||
|
||||
return C*a
|
||||
|
||||
def hPrimary(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return 1./self.mu*b
|
||||
|
||||
def S_m(self, prob):
|
||||
b = self.bPrimary(prob)
|
||||
return -1j*omega(self.freq)*b
|
||||
|
||||
def S_e(self, prob):
|
||||
if all(np.r_[self.mu] == np.r_[prob.curModel.mu]):
|
||||
return Zero()
|
||||
else:
|
||||
eqLocs = prob._eqLocs
|
||||
|
||||
if eqLocs is 'FE':
|
||||
mui_s = prob.curModel.mui - 1./self.mu
|
||||
MMui_s = prob.mesh.getFaceInnerProduct(mui_s)
|
||||
C = prob.mesh.edgeCurl
|
||||
elif eqLocs is 'EF':
|
||||
mu_s = prob.curModel.mu - self.mu
|
||||
MMui_s = prob.mesh.getEdgeInnerProduct(mu_s,invMat=True)
|
||||
C = prob.mesh.edgeCurl.T
|
||||
|
||||
return -C.T * (MMui_s * self.bPrimary(prob))
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import SimPEG
|
||||
from SimPEG.EM.Utils import *
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.Utils import Zero, Identity
|
||||
import SrcFDEM as Src
|
||||
|
||||
|
||||
####################################################
|
||||
# Receivers
|
||||
####################################################
|
||||
|
||||
class Rx(SimPEG.Survey.BaseRx):
|
||||
|
||||
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'],
|
||||
|
||||
'bxr':['b', 'Fx', 'real'],
|
||||
'byr':['b', 'Fy', 'real'],
|
||||
'bzr':['b', 'Fz', 'real'],
|
||||
'bxi':['b', 'Fx', 'imag'],
|
||||
'byi':['b', 'Fy', 'imag'],
|
||||
'bzi':['b', 'Fz', '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'],
|
||||
|
||||
'hxr':['h', 'Ex', 'real'],
|
||||
'hyr':['h', 'Ey', 'real'],
|
||||
'hzr':['h', 'Ez', 'real'],
|
||||
'hxi':['h', 'Ex', 'imag'],
|
||||
'hyi':['h', 'Ey', 'imag'],
|
||||
'hzi':['h', 'Ez', 'imag'],
|
||||
}
|
||||
radius = None
|
||||
|
||||
def __init__(self, locs, rxType):
|
||||
SimPEG.Survey.BaseRx.__init__(self, locs, rxType)
|
||||
|
||||
@property
|
||||
def projField(self):
|
||||
"""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)
|
||||
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)
|
||||
|
||||
if not adjoint:
|
||||
Pv_complex = P * v
|
||||
real_or_imag = self.projComp
|
||||
Pv = getattr(Pv_complex, real_or_imag)
|
||||
elif adjoint:
|
||||
Pv_real = P.T * v
|
||||
|
||||
real_or_imag = self.projComp
|
||||
if real_or_imag == 'imag':
|
||||
Pv = 1j*Pv_real
|
||||
elif real_or_imag == 'real':
|
||||
Pv = Pv_real.astype(complex)
|
||||
else:
|
||||
raise NotImplementedError('must be real or imag')
|
||||
|
||||
return Pv
|
||||
|
||||
|
||||
####################################################
|
||||
# Survey
|
||||
####################################################
|
||||
|
||||
class Survey(SimPEG.Survey.BaseSurvey):
|
||||
"""
|
||||
docstring for SurveyFDEM
|
||||
"""
|
||||
|
||||
srcPair = Src.BaseSrc
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
# Sort these by frequency
|
||||
self.srcList = srcList
|
||||
SimPEG.Survey.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)
|
||||
|
||||
@property
|
||||
def nSrcByFreq(self):
|
||||
if getattr(self, '_nSrcByFreq', None) is None:
|
||||
self._nSrcByFreq = {}
|
||||
for freq in self.freqs:
|
||||
self._nSrcByFreq[freq] = len(self.getSrcByFreq(freq))
|
||||
return self._nSrcByFreq
|
||||
|
||||
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 projectFields(self, u):
|
||||
data = SimPEG.Survey.Data(self)
|
||||
for src in self.srcList:
|
||||
for rx in src.rxList:
|
||||
data[src, rx] = rx.projectFields(src, self.mesh, u)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(self, u):
|
||||
raise Exception('Use Sources to project fields deriv.')
|
||||
@@ -0,0 +1,3 @@
|
||||
from SurveyFDEM import Rx, Src, Survey
|
||||
from FDEM import BaseFDEMProblem, Problem_e, Problem_b, Problem_j, Problem_h
|
||||
from FieldsFDEM import *
|
||||
@@ -0,0 +1,163 @@
|
||||
from SimPEG import Solver, Problem
|
||||
from SimPEG.Problem import BaseTimeProblem
|
||||
from SimPEG.EM.Utils import *
|
||||
from scipy.constants import mu_0
|
||||
from SimPEG.Utils import sdiag, mkvc
|
||||
from SimPEG import Utils, Mesh
|
||||
from SimPEG.EM.Base import BaseEMProblem
|
||||
import numpy as np
|
||||
|
||||
|
||||
class FieldsTDEM(Problem.TimeFields):
|
||||
"""Fancy Field Storage for a TDEM survey."""
|
||||
knownFields = {'b': 'F', 'e': 'E'}
|
||||
|
||||
def tovec(self):
|
||||
nSrc, nF, nE = self.survey.nSrc, self.mesh.nF, self.mesh.nE
|
||||
u = np.empty((0,nSrc)) #((0,1) if nSrc == 1 else (0, nSrc))
|
||||
|
||||
for i in range(self.survey.prob.nT):
|
||||
if 'b' in self:
|
||||
b = self[:,'b',i+1]
|
||||
else:
|
||||
b = np.zeros((nF,nSrc)) # if nSrc == 1 else (nF, nSrc))
|
||||
|
||||
if 'e' in self:
|
||||
e = self[:,'e',i+1]
|
||||
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 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
|
||||
# Create a fields storage object
|
||||
F = self._FieldsForward_pair(self.mesh, self.survey)
|
||||
for src in self.survey.srcList:
|
||||
# Set the initial conditions
|
||||
F[src,:,0] = src.getInitialFields(self.mesh)
|
||||
F = self.forward(m, self.getRHS, F=F)
|
||||
if self.verbose: print '%s\nDone calculating fields(m)\n%s'%('*'*50,'*'*50)
|
||||
return F
|
||||
|
||||
def forward(self, m, RHS, F=None):
|
||||
self.curModel = m
|
||||
F = F or FieldsTDEM(self.mesh, self.survey)
|
||||
|
||||
dtFact = None
|
||||
Ainv = None
|
||||
for tInd, dt in enumerate(self.timeSteps):
|
||||
if dt != dtFact:
|
||||
dtFact = dt
|
||||
if Ainv is not None:
|
||||
Ainv.clean()
|
||||
A = self.getA(tInd)
|
||||
if self.verbose: print 'Factoring... (dt = %e)'%dt
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
if self.verbose: print 'Done'
|
||||
rhs = RHS(tInd, F)
|
||||
if self.verbose: print ' Solving... (tInd = %d)'%tInd
|
||||
sol = Ainv * rhs
|
||||
if self.verbose: print ' Done...'
|
||||
if sol.ndim == 1:
|
||||
sol.shape = (sol.size,1)
|
||||
F[:,self.solType,tInd+1] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def adjoint(self, m, RHS, F=None):
|
||||
self.curModel = m
|
||||
F = F or FieldsTDEM(self.mesh, self.survey)
|
||||
|
||||
dtFact = None
|
||||
Ainv = None
|
||||
for tInd, dt in reversed(list(enumerate(self.timeSteps))):
|
||||
if dt != dtFact:
|
||||
dtFact = dt
|
||||
if Ainv is not None:
|
||||
Ainv.clean()
|
||||
A = self.getA(tInd)
|
||||
if self.verbose: print 'Factoring (Adjoint)... (dt = %e)'%dt
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
if self.verbose: print 'Done'
|
||||
rhs = RHS(tInd, F)
|
||||
if self.verbose: print ' Solving (Adjoint)... (tInd = %d)'%tInd
|
||||
sol = Ainv * rhs
|
||||
if self.verbose: print ' Done...'
|
||||
if sol.ndim == 1:
|
||||
sol.shape = (sol.size,1)
|
||||
F[:,self.solType,tInd+1] = sol
|
||||
Ainv.clean()
|
||||
return F
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.ndarray v: vector (model object)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: numpy.ndarray
|
||||
:return: w (data object)
|
||||
|
||||
Multiplying \\\(\\\mathbf{J}\\\) onto a vector can be broken into three steps
|
||||
|
||||
* Compute \\\(\\\\vec{p} = \\\mathbf{G}v\\\)
|
||||
* Solve \\\(\\\hat{\\\mathbf{A}} \\\\vec{y} = \\\\vec{p}\\\)
|
||||
* Compute \\\(\\\\vec{w} = -\\\mathbf{Q} \\\\vec{y}\\\)
|
||||
|
||||
"""
|
||||
if self.verbose: print '%s\nCalculating J(v)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
p = self.Gvec(m, v, u)
|
||||
y = self.solveAh(m, p)
|
||||
Jv = self.survey.projectFieldsDeriv(u, v=y)
|
||||
if self.verbose: print '%s\nDone calculating J(v)\n%s'%('*'*50,'*'*50)
|
||||
return - mkvc(Jv)
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.ndarray,SimPEG.Survey.Data v: vector (data object)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: numpy.ndarray
|
||||
:return: w (model object)
|
||||
|
||||
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\\\)
|
||||
|
||||
"""
|
||||
if self.verbose: print '%s\nCalculating J^T(v)\n%s'%('*'*50,'*'*50)
|
||||
self.curModel = m
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
if not isinstance(v, self.dataPair):
|
||||
v = self.dataPair(self.survey, v)
|
||||
|
||||
p = self.survey.projectFieldsDeriv(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)
|
||||
return - mkvc(w)
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
from SimPEG import Utils, Survey, np
|
||||
from SimPEG.Survey import BaseSurvey
|
||||
from SimPEG.EM.Utils import *
|
||||
from BaseTDEM import FieldsTDEM
|
||||
|
||||
|
||||
class RxTDEM(Survey.BaseTimeRx):
|
||||
|
||||
knownRxTypes = {
|
||||
'ex':['e', 'Ex', 'N'],
|
||||
'ey':['e', 'Ey', 'N'],
|
||||
'ez':['e', 'Ez', 'N'],
|
||||
|
||||
'bx':['b', 'Fx', 'N'],
|
||||
'by':['b', 'Fy', 'N'],
|
||||
'bz':['b', 'Fz', 'N'],
|
||||
|
||||
'dbxdt':['b', 'Fx', 'CC'],
|
||||
'dbydt':['b', 'Fy', 'CC'],
|
||||
'dbzdt':['b', 'Fz', 'CC'],
|
||||
}
|
||||
|
||||
def __init__(self, locs, times, rxType):
|
||||
Survey.BaseTimeRx.__init__(self, locs, times, rxType)
|
||||
|
||||
@property
|
||||
def projField(self):
|
||||
"""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 projTLoc(self):
|
||||
"""Time Location projection (e.g. CC N)"""
|
||||
return self.knownRxTypes[self.rxType][2]
|
||||
|
||||
def getTimeP(self, timeMesh):
|
||||
"""
|
||||
Returns the time projection matrix.
|
||||
|
||||
.. note::
|
||||
|
||||
This is not stored in memory, but is created on demand.
|
||||
"""
|
||||
if self.rxType in ['dbxdt','dbydt','dbzdt']:
|
||||
return timeMesh.getInterpolationMat(self.times, self.projTLoc)*timeMesh.faceDiv
|
||||
else:
|
||||
return timeMesh.getInterpolationMat(self.times, self.projTLoc)
|
||||
|
||||
def projectFields(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):
|
||||
P = self.getP(mesh, timeMesh)
|
||||
|
||||
if not adjoint:
|
||||
return P * Utils.mkvc(v[src, self.projField, :])
|
||||
elif adjoint:
|
||||
return P.T * v[src, self]
|
||||
|
||||
|
||||
class SrcTDEM(Survey.BaseSrc):
|
||||
rxPair = RxTDEM
|
||||
radius = None
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
F0 = getattr(self, '_getInitialFields_' + self.srcType)(mesh)
|
||||
return F0
|
||||
|
||||
def getJs(self, mesh, time):
|
||||
return None
|
||||
|
||||
|
||||
class SrcTDEM_VMD_MVP(SrcTDEM):
|
||||
|
||||
def __init__(self,rxList,loc):
|
||||
self.loc = loc
|
||||
SrcTDEM.__init__(self,rxList)
|
||||
|
||||
def getInitialFields(self, mesh):
|
||||
"""Vertical magnetic dipole, magnetic vector potential"""
|
||||
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}
|
||||
|
||||
|
||||
class SrcTDEM_CircularLoop_MVP(SrcTDEM):
|
||||
def __init__(self,rxList,loc,radius,waveformType):
|
||||
self.loc = loc
|
||||
self.radius = radius
|
||||
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)
|
||||
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 mesh.edgeCurl.T*MfMui*mesh.edgeCurl*MVP
|
||||
|
||||
|
||||
class SurveyTDEM(Survey.BaseSurvey):
|
||||
"""
|
||||
docstring for SurveyTDEM
|
||||
"""
|
||||
srcPair = SrcTDEM
|
||||
|
||||
def __init__(self, srcList, **kwargs):
|
||||
# Sort these by frequency
|
||||
self.srcList = srcList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
|
||||
def projectFields(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)
|
||||
return data
|
||||
|
||||
def projectFieldsDeriv(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)
|
||||
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 = 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
|
||||
else: # there are already fields, so let's add to them!
|
||||
f[src, rx.projField, :] += Ptv
|
||||
return f
|
||||
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
from BaseTDEM import BaseTDEMProblem, FieldsTDEM
|
||||
from SimPEG.Utils import mkvc, sdiag
|
||||
import numpy as np
|
||||
from SurveyTDEM import SurveyTDEM
|
||||
|
||||
|
||||
class FieldsTDEM_e_from_b(FieldsTDEM):
|
||||
"""Fancy Field Storage for a TDEM survey."""
|
||||
knownFields = {'b': 'F'}
|
||||
aliasFields = {'e': ['b','E','e_from_b']}
|
||||
|
||||
def startup(self):
|
||||
self.MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self.edgeCurlT = self.survey.prob.mesh.edgeCurl.T
|
||||
self.MfMui = self.survey.prob.MfMui
|
||||
|
||||
def e_from_b(self, b, srcInd, timeInd):
|
||||
# TODO: implement non-zero js
|
||||
return self.MeSigmaI*(self.edgeCurlT*(self.MfMui*b))
|
||||
|
||||
class FieldsTDEM_e_from_b_Ah(FieldsTDEM):
|
||||
"""Fancy Field Storage for a TDEM survey.
|
||||
|
||||
This is used when solving Ahat and AhatT
|
||||
"""
|
||||
knownFields = {'b': 'F'}
|
||||
aliasFields = {'e': ['b','E','e_from_b']}
|
||||
p = None
|
||||
|
||||
def startup(self):
|
||||
self.MeSigmaI = self.survey.prob.MeSigmaI
|
||||
self.edgeCurlT = self.survey.prob.mesh.edgeCurl.T
|
||||
self.MfMui = self.survey.prob.MfMui
|
||||
|
||||
def e_from_b(self, y_b, srcInd, tInd):
|
||||
y_e = self.MeSigmaI*(self.edgeCurlT*(self.MfMui*y_b))
|
||||
if 'e' in self.p:
|
||||
y_e = y_e - self.MeSigmaI*self.p[srcInd,'e',tInd]
|
||||
return y_e
|
||||
|
||||
class ProblemTDEM_b(BaseTDEMProblem):
|
||||
"""
|
||||
Time-Domain EM problem - B-formulation
|
||||
|
||||
TDEM_b treats the following discretization of Maxwell's equations
|
||||
|
||||
.. math::
|
||||
\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)}
|
||||
|
||||
with \\\(\\b\\\) defined on cell faces and \\\(\e\\\) defined on edges.
|
||||
"""
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
BaseTDEMProblem.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
solType = 'b' #: Type of the solution, in this case the 'b' field
|
||||
|
||||
surveyPair = SurveyTDEM
|
||||
_FieldsForward_pair = FieldsTDEM_e_from_b #: used for the forward calculation only
|
||||
|
||||
####################################################
|
||||
# Internal Methods
|
||||
####################################################
|
||||
|
||||
def getA(self, tInd):
|
||||
"""
|
||||
:param int tInd: Time index
|
||||
:rtype: scipy.sparse.csr_matrix
|
||||
:return: A
|
||||
"""
|
||||
dt = self.timeSteps[tInd]
|
||||
return self.MfMui*self.mesh.edgeCurl*self.MeSigmaI*self.mesh.edgeCurl.T*self.MfMui + (1.0/dt)*self.MfMui
|
||||
|
||||
def getRHS(self, tInd, F):
|
||||
dt = self.timeSteps[tInd]
|
||||
B_n = np.c_[[F[src,'b',tInd] for src in self.survey.srcList]].T
|
||||
if B_n.shape[0] is not 1:
|
||||
raise NotImplementedError('getRHS not implemented for this shape of B_n')
|
||||
RHS = (1.0/dt)*self.MfMui*B_n[0,:,:] #TODO: This is a hack
|
||||
return RHS
|
||||
|
||||
####################################################
|
||||
# Derivatives
|
||||
####################################################
|
||||
|
||||
def Gvec(self, m, vec, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.array vec: vector (like a model)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply G by a vector
|
||||
"""
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
self.curModel = m
|
||||
|
||||
# Note: Fields has shape (nF/E, nSrc, nT+1)
|
||||
# However, p will only really fill (:,:,1:nT+1)
|
||||
# meaning the 'initial fields' are zero (:,:,0)
|
||||
p = FieldsTDEM(self.mesh, self.survey)
|
||||
# 'b' at all times is zero.
|
||||
# However, to save memory we will **not** do:
|
||||
#
|
||||
# p[:, 'b', :] = 0.0
|
||||
|
||||
# fake initial 'e' fields
|
||||
p[:, 'e', 0] = 0.0
|
||||
dMdsig = self.MeSigmaDeriv
|
||||
# self.mesh.getEdgeInnerProductDeriv(self.curModel.transform)
|
||||
# dsigdm_x_v = self.curModel.sigmaDeriv*vec
|
||||
# dsigdm_x_v = self.curModel.transformDeriv*vec
|
||||
for i in range(1,self.nT+1):
|
||||
# TODO: G[1] may be dependent on the model
|
||||
# for a galvanic source (deriv of the dc problem)
|
||||
#
|
||||
# Do multiplication for all src in self.survey.srcList
|
||||
for src in self.survey.srcList:
|
||||
p[src, 'e', i] = - dMdsig(u[src,'e',i]) * vec
|
||||
return p
|
||||
|
||||
def Gtvec(self, m, vec, u=None):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param numpy.array vec: vector (like a fields)
|
||||
:param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
|
||||
:rtype: np.ndarray (like a model)
|
||||
:return: p
|
||||
|
||||
Multiply G.T by a vector
|
||||
"""
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
self.curModel = m
|
||||
# dMdsig = self.mesh.getEdgeInnerProductDeriv(self.curModel.transform)
|
||||
# dsigdm = self.curModel.transformDeriv
|
||||
MeSigmaDeriv = self.MeSigmaDeriv
|
||||
|
||||
nSrc = self.survey.nSrc
|
||||
VUs = None
|
||||
# Here we can do internal multiplications of Gt*v and then multiply by MsigDeriv.T in one go.
|
||||
for i in range(1,self.nT+1):
|
||||
vu = None
|
||||
for src in self.survey.srcList:
|
||||
vusrc = MeSigmaDeriv(u[src,'e',i]).T * vec[src,'e',i]
|
||||
vu = vusrc if vu is None else vu + vusrc
|
||||
VUs = vu if VUs is None else VUs + vu
|
||||
# p = -dsigdm.T*VUs
|
||||
return -VUs
|
||||
|
||||
def solveAh(self, m, p):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM p: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: y
|
||||
|
||||
Solve the block-matrix system \\\(\\\hat{A} \\\hat{y} = \\\hat{p}\\\):
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & 0 & & \\\\
|
||||
B & A & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
def AhRHS(tInd, y):
|
||||
rhs = self.MfMui*(self.mesh.edgeCurl*(self.MeSigmaI*p[:,'e',tInd+1]))
|
||||
if 'b' in p:
|
||||
rhs = rhs + p[:,'b',tInd+1]
|
||||
if tInd == 0:
|
||||
return rhs
|
||||
dt = self.timeSteps[tInd]
|
||||
return rhs + 1.0/dt*self.MfMui*y[:,'b',tInd]
|
||||
|
||||
F = FieldsTDEM_e_from_b_Ah(self.mesh, self.survey, p=p)
|
||||
|
||||
return self.forward(m, AhRHS, F)
|
||||
|
||||
def solveAht(self, m, p):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM p: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: y
|
||||
|
||||
Solve the block-matrix system \\\(\\\hat{A}^\\\\top \\\hat{y} = \\\hat{p}\\\):
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}}^\\top = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & B & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & A & B \\\\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
# Mini Example:
|
||||
#
|
||||
# nT = 3, len(times) == 4, fields stored in F[:,:,1:4]
|
||||
#
|
||||
# 0 is held for initial conditions (this shifts the storage by +1)
|
||||
# ^
|
||||
# fLoc 0 1 2 3
|
||||
# |-----|-----|-----|
|
||||
# tInd 0 1 2
|
||||
# / ___/
|
||||
# 2 (tInd=2 uses fields 3 and would use 4 but it doesn't exist)
|
||||
# / ___/
|
||||
# 1 (tInd=1 uses fields 2 and 3)
|
||||
|
||||
def AhtRHS(tInd, y):
|
||||
nSrc, nF = self.survey.nSrc, self.mesh.nF
|
||||
rhs = np.zeros((nF,1) if nSrc == 1 else (nF, nSrc))
|
||||
|
||||
if 'e' in p:
|
||||
rhs += self.MfMui*(self.mesh.edgeCurl*(self.MeSigmaI*p[:,'e',tInd+1]))
|
||||
if 'b' in p:
|
||||
rhs += p[:,'b',tInd+1]
|
||||
|
||||
if tInd == self.nT-1:
|
||||
return rhs
|
||||
dt = self.timeSteps[tInd+1]
|
||||
return rhs + 1.0/dt*self.MfMui*y[:,'b',tInd+2]
|
||||
|
||||
F = FieldsTDEM_e_from_b_Ah(self.mesh, self.survey, p=p)
|
||||
|
||||
return self.adjoint(m, AhtRHS, F)
|
||||
|
||||
####################################################
|
||||
# Functions for tests
|
||||
####################################################
|
||||
|
||||
def _AhVec(self, m, vec):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM vec: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}} = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & 0 & & \\\\
|
||||
B & A & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & B & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
|
||||
self.curModel = m
|
||||
f = FieldsTDEM(self.mesh, self.survey)
|
||||
for i in range(1,self.nT+1):
|
||||
dt = self.timeSteps[i-1]
|
||||
b = 1.0/dt*self.MfMui*vec[:,'b',i] + self.MfMui*(self.mesh.edgeCurl*vec[:,'e',i])
|
||||
if i > 1:
|
||||
b = b - 1.0/dt*self.MfMui*vec[:,'b',i-1]
|
||||
f[:,'b',i] = b
|
||||
f[:,'e',i] = self.mesh.edgeCurl.T*(self.MfMui*vec[:,'b',i]) - self.MeSigma*vec[:,'e',i]
|
||||
return f
|
||||
|
||||
def _AhtVec(self, m, vec):
|
||||
"""
|
||||
:param numpy.array m: Conductivity model
|
||||
:param simpegEM.TDEM.FieldsTDEM vec: Fields object
|
||||
:rtype: simpegEM.TDEM.FieldsTDEM
|
||||
:return: f
|
||||
|
||||
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
|
||||
|
||||
.. math::
|
||||
\mathbf{\hat{A}}^\\top = \left[
|
||||
\\begin{array}{cccc}
|
||||
A & B & & \\\\
|
||||
& \ddots & \ddots & \\\\
|
||||
& & A & B \\\\
|
||||
& & 0 & A
|
||||
\end{array}
|
||||
\\right] \\\\
|
||||
\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] \\\\
|
||||
"""
|
||||
self.curModel = m
|
||||
f = FieldsTDEM(self.mesh, self.survey)
|
||||
for i in range(self.nT):
|
||||
b = 1.0/self.timeSteps[i]*self.MfMui*vec[:,'b',i+1] + self.MfMui*(self.mesh.edgeCurl*vec[:,'e',i+1])
|
||||
if i < self.nT-1:
|
||||
b = b - 1.0/self.timeSteps[i+1]*self.MfMui*vec[:,'b',i+2]
|
||||
f[:,'b', i+1] = b
|
||||
f[:,'e', i+1] = self.mesh.edgeCurl.T*(self.MfMui*vec[:,'b',i+1]) - self.MeSigma*vec[:,'e',i+1]
|
||||
return f
|
||||
@@ -0,0 +1,3 @@
|
||||
from SurveyTDEM import * #SurveyTDEM, RxTDEM, SrcTDEM
|
||||
from BaseTDEM import BaseTDEMProblem, FieldsTDEM
|
||||
from TDEM_b import ProblemTDEM_b
|
||||
@@ -0,0 +1,203 @@
|
||||
from SimPEG import *
|
||||
from scipy.special import ellipk, ellipe
|
||||
from scipy.constants import mu_0, pi
|
||||
|
||||
def MagneticDipoleVectorPotential(srcLoc, obsLoc, component, moment=1., dipoleMoment=(0., 0., 1.), mu = mu_0):
|
||||
"""
|
||||
Calculate the vector potential of a set of magnetic dipoles
|
||||
at given locations 'ref. <http://en.wikipedia.org/wiki/Dipole#Magnetic_vector_potential>'
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray,SimPEG.Mesh obsLoc: Where the potentials will be calculated (x, y, z) or a SimPEG Mesh
|
||||
:param str,list component: The component to calculate - 'x', 'y', or 'z' if an array, or grid type if mesh, can be a list
|
||||
:param numpy.ndarray dipoleMoment: The vector dipole moment
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
#TODO: break this out!
|
||||
|
||||
if type(component) in [list, tuple]:
|
||||
out = range(len(component))
|
||||
for i, comp in enumerate(component):
|
||||
out[i] = MagneticDipoleVectorPotential(srcLoc, obsLoc, comp, dipoleMoment=dipoleMoment)
|
||||
return np.concatenate(out)
|
||||
|
||||
if isinstance(obsLoc, Mesh.BaseMesh):
|
||||
mesh = obsLoc
|
||||
assert component in ['Ex','Ey','Ez','Fx','Fy','Fz'], "Components must be in: ['Ex','Ey','Ez','Fx','Fy','Fz']"
|
||||
return MagneticDipoleVectorPotential(srcLoc, getattr(mesh,'grid'+component), component[1], dipoleMoment=dipoleMoment)
|
||||
|
||||
if component == 'x':
|
||||
dimInd = 0
|
||||
elif component == 'y':
|
||||
dimInd = 1
|
||||
elif component == 'z':
|
||||
dimInd = 2
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
dipoleMoment = np.atleast_2d(dipoleMoment)
|
||||
|
||||
nEdges = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
m = np.array(dipoleMoment).repeat(nEdges, axis=0)
|
||||
A = np.empty((nEdges, nSrc))
|
||||
for i in range(nSrc):
|
||||
dR = obsLoc - srcLoc[i, np.newaxis].repeat(nEdges, axis=0)
|
||||
mCr = np.cross(m, dR)
|
||||
r = np.sqrt((dR**2).sum(axis=1))
|
||||
A[:, i] = +(mu/(4*pi)) * mCr[:,dimInd]/(r**3)
|
||||
if nSrc == 1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
|
||||
def MagneticDipoleFields(srcLoc, obsLoc, component, moment=1., mu = mu_0):
|
||||
"""
|
||||
Calculate the vector potential of a set of magnetic dipoles
|
||||
at given locations 'ref. <http://en.wikipedia.org/wiki/Dipole#Magnetic_vector_potential>'
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray obsLoc: Where the potentials will be calculated (x, y, z)
|
||||
:param str component: The component to calculate - 'x', 'y', or 'z'
|
||||
:param numpy.ndarray moment: The vector dipole moment (vertical)
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
|
||||
if component=='x':
|
||||
dimInd = 0
|
||||
elif component=='y':
|
||||
dimInd = 1
|
||||
elif component=='z':
|
||||
dimInd = 2
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
moment = np.atleast_2d(moment)
|
||||
|
||||
nFaces = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
m = np.array(moment).repeat(nFaces, axis=0)
|
||||
B = np.empty((nFaces, nSrc))
|
||||
for i in range(nSrc):
|
||||
dR = obsLoc - srcLoc[i, np.newaxis].repeat(nFaces, axis=0)
|
||||
r = np.sqrt((dR**2).sum(axis=1))
|
||||
if dimInd == 0:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,0]/r**2)
|
||||
elif dimInd == 1:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,1]/r**2)
|
||||
elif dimInd == 2:
|
||||
B[:, i] = +(mu/(4*pi)) /(r**3) * (3*dR[:,2]**2/r**2-1)
|
||||
else:
|
||||
raise Exception("Not Implemented")
|
||||
if nSrc == 1:
|
||||
return B.flatten()
|
||||
return B
|
||||
|
||||
|
||||
|
||||
def MagneticLoopVectorPotential(srcLoc, obsLoc, component, radius, mu=mu_0):
|
||||
"""
|
||||
Calculate the vector potential of horizontal circular loop
|
||||
at given locations
|
||||
|
||||
:param numpy.ndarray srcLoc: Location of the source(s) (x, y, z)
|
||||
:param numpy.ndarray,SimPEG.Mesh obsLoc: Where the potentials will be calculated (x, y, z) or a SimPEG Mesh
|
||||
:param str,list component: The component to calculate - 'x', 'y', or 'z' if an array, or grid type if mesh, can be a list
|
||||
:param numpy.ndarray I: Input current of the loop
|
||||
:param numpy.ndarray radius: radius of the loop
|
||||
:rtype: numpy.ndarray
|
||||
:return: The vector potential each dipole at each observation location
|
||||
"""
|
||||
|
||||
if type(component) in [list, tuple]:
|
||||
out = range(len(component))
|
||||
for i, comp in enumerate(component):
|
||||
out[i] = MagneticLoopVectorPotential(srcLoc, obsLoc, comp, radius, mu)
|
||||
return np.concatenate(out)
|
||||
|
||||
if isinstance(obsLoc, Mesh.BaseMesh):
|
||||
mesh = obsLoc
|
||||
assert component in ['Ex','Ey','Ez','Fx','Fy','Fz'], "Components must be in: ['Ex','Ey','Ez','Fx','Fy','Fz']"
|
||||
return MagneticLoopVectorPotential(srcLoc, getattr(mesh,'grid'+component), component[1], radius, mu)
|
||||
|
||||
srcLoc = np.atleast_2d(srcLoc)
|
||||
obsLoc = np.atleast_2d(obsLoc)
|
||||
|
||||
n = obsLoc.shape[0]
|
||||
nSrc = srcLoc.shape[0]
|
||||
|
||||
if component=='z':
|
||||
A = np.zeros((n, nSrc))
|
||||
if nSrc ==1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
else:
|
||||
|
||||
A = np.zeros((n, nSrc))
|
||||
for i in range (nSrc):
|
||||
x = obsLoc[:, 0] - srcLoc[i, 0]
|
||||
y = obsLoc[:, 1] - srcLoc[i, 1]
|
||||
z = obsLoc[:, 2] - srcLoc[i, 2]
|
||||
r = np.sqrt(x**2 + y**2)
|
||||
m = (4 * radius * r) / ((radius + r)**2 + z**2)
|
||||
m[m > 1.] = 1.
|
||||
# m might be slightly larger than 1 due to rounding errors
|
||||
# but ellipke requires 0 <= m <= 1
|
||||
K = ellipk(m)
|
||||
E = ellipe(m)
|
||||
ind = (r > 0) & (m < 1)
|
||||
# % 1/r singular at r = 0 and K(m) singular at m = 1
|
||||
Aphi = np.zeros(n)
|
||||
# % Common factor is (mu * I) / pi with I = 1 and mu = 4e-7 * pi.
|
||||
Aphi[ind] = 4e-7 / np.sqrt(m[ind]) * np.sqrt(radius / r[ind]) *((1. - m[ind] / 2.) * K[ind] - E[ind])
|
||||
if component == 'x':
|
||||
A[ind, i] = Aphi[ind] * (-y[ind] / r[ind] )
|
||||
elif component == 'y':
|
||||
A[ind, i] = Aphi[ind] * ( x[ind] / r[ind] )
|
||||
else:
|
||||
raise ValueError('Invalid component')
|
||||
|
||||
if nSrc == 1:
|
||||
return A.flatten()
|
||||
return A
|
||||
|
||||
if __name__ == '__main__':
|
||||
from SimPEG import Mesh
|
||||
import matplotlib.pyplot as plt
|
||||
cs = 20
|
||||
ncx, ncy, ncz = 41, 41, 40
|
||||
hx = np.ones(ncx)*cs
|
||||
hy = np.ones(ncy)*cs
|
||||
hz = np.ones(ncz)*cs
|
||||
mesh = Mesh.TensorMesh([hx, hy, hz], 'CCC')
|
||||
srcLoc = np.r_[0., 0., 0.]
|
||||
Ax = MagneticLoopVectorPotential(srcLoc, mesh.gridEx, 'x', 200)
|
||||
Ay = MagneticLoopVectorPotential(srcLoc, mesh.gridEy, 'y', 200)
|
||||
Az = MagneticLoopVectorPotential(srcLoc, mesh.gridEz, 'z', 200)
|
||||
A = np.r_[Ax, Ay, Az]
|
||||
B0 = mesh.edgeCurl*A
|
||||
J0 = mesh.edgeCurl.T*B0
|
||||
|
||||
# mesh.plotImage(A, vType = 'Ex')
|
||||
# mesh.plotImage(A, vType = 'Ey')
|
||||
|
||||
mesh.plotImage(B0, vType = 'Fx')
|
||||
mesh.plotImage(B0, vType = 'Fy')
|
||||
mesh.plotImage(B0, vType = 'Fz')
|
||||
|
||||
# # mesh.plotImage(J0, vType = 'Ex')
|
||||
# mesh.plotImage(J0, vType = 'Ey')
|
||||
# mesh.plotImage(J0, vType = 'Ez')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import numpy as np
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
|
||||
# useful params
|
||||
def omega(freq):
|
||||
"""Angular frequency, omega"""
|
||||
return 2.*np.pi*freq
|
||||
|
||||
def k(freq, sigma, mu=mu_0, eps=epsilon_0):
|
||||
""" Eq 1.47 - 1.49 in Ward and Hohmann """
|
||||
w = omega(freq)
|
||||
alp = w * np.sqrt( mu*eps/2 * ( np.sqrt(1. + (sigma / (eps*w))**2 ) + 1) )
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# import Sources
|
||||
# import Ana
|
||||
# import Solver
|
||||
from EMUtils import omega, e_from_j, j_from_e, b_from_h, h_from_b
|
||||
from AnalyticUtils import MagneticDipoleFields, MagneticDipoleVectorPotential, MagneticLoopVectorPotential
|
||||
@@ -0,0 +1,75 @@
|
||||
import unittest
|
||||
from SimPEG import *
|
||||
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
|
||||
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)
|
||||
|
||||
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.])
|
||||
Rx0 = EM.FDEM.Rx(XYZ, comp)
|
||||
|
||||
Src = []
|
||||
|
||||
for SrcType in SrcList:
|
||||
if SrcType is 'MagDipole':
|
||||
Src.append(EM.FDEM.Src.MagDipole([Rx0], freq=freq, loc=np.r_[0.,0.,0.]))
|
||||
elif SrcType is 'MagDipole_Bfield':
|
||||
Src.append(EM.FDEM.Src.MagDipole_Bfield([Rx0], freq=freq, loc=np.r_[0.,0.,0.]))
|
||||
elif SrcType is 'CircularLoop':
|
||||
Src.append(EM.FDEM.Src.CircularLoop([Rx0], freq=freq, loc=np.r_[0.,0.,0.]))
|
||||
elif SrcType is 'RawVec':
|
||||
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.
|
||||
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.
|
||||
Src.append(EM.FDEM.Src.RawVec([Rx0], freq, S_m, S_e))
|
||||
|
||||
if verbose:
|
||||
print ' Fetching %s problem' % (fdemType)
|
||||
|
||||
if fdemType == 'e':
|
||||
survey = EM.FDEM.Survey(Src)
|
||||
prb = EM.FDEM.Problem_e(mesh, mapping=mapping)
|
||||
|
||||
elif fdemType == 'b':
|
||||
survey = EM.FDEM.Survey(Src)
|
||||
prb = EM.FDEM.Problem_b(mesh, mapping=mapping)
|
||||
|
||||
elif fdemType == 'j':
|
||||
survey = EM.FDEM.Survey(Src)
|
||||
prb = EM.FDEM.Problem_j(mesh, mapping=mapping)
|
||||
|
||||
elif fdemType == 'h':
|
||||
survey = EM.FDEM.Survey(Src)
|
||||
prb = EM.FDEM.Problem_h(mesh, mapping=mapping)
|
||||
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
prb.pair(survey)
|
||||
|
||||
try:
|
||||
from pymatsolver import MumpsSolver
|
||||
prb.Solver = MumpsSolver
|
||||
except ImportError, e:
|
||||
pass
|
||||
|
||||
return prb
|
||||
@@ -0,0 +1,6 @@
|
||||
import TDEM
|
||||
import FDEM
|
||||
import Base
|
||||
import Analytics
|
||||
import Utils
|
||||
from scipy.constants import mu_0, epsilon_0
|
||||
@@ -1,71 +0,0 @@
|
||||
from SimPEG import Mesh, Utils, np, SolverLU
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
from matplotlib.mlab import griddata
|
||||
|
||||
## 2D DC forward modeling example with Tensor and Curvilinear Meshes
|
||||
|
||||
# Step1: Generate Tensor and Curvilinear Mesh
|
||||
sz = [40,40]
|
||||
# Tensor Mesh
|
||||
tM = Mesh.TensorMesh(sz)
|
||||
# Curvilinear Mesh
|
||||
rM = Mesh.CurvilinearMesh(Utils.meshutils.exampleLrmGrid(sz,'rotate'))
|
||||
|
||||
# Step2: Direct Current (DC) operator
|
||||
def DCfun(mesh, pts):
|
||||
D = mesh.faceDiv
|
||||
G = D.T
|
||||
sigma = 1e-2*np.ones(mesh.nC)
|
||||
Msigi = mesh.getFaceInnerProduct(1./sigma)
|
||||
MsigI = Utils.sdInv(Msigi)
|
||||
A = D*MsigI*G
|
||||
A[-1,-1] /= mesh.vol[-1] # Remove null space
|
||||
rhs = np.zeros(mesh.nC)
|
||||
txind = Utils.meshutils.closestPoints(mesh, pts)
|
||||
rhs[txind] = np.r_[1,-1]
|
||||
return A, rhs
|
||||
|
||||
pts = np.vstack((np.r_[0.25, 0.5], np.r_[0.75, 0.5]))
|
||||
|
||||
#Step3: Solve DC problem (LU solver)
|
||||
AtM, rhstM = DCfun(tM, pts)
|
||||
AinvtM = SolverLU(AtM)
|
||||
phitM = AinvtM*rhstM
|
||||
|
||||
ArM, rhsrM = DCfun(rM, pts)
|
||||
AinvrM = SolverLU(ArM)
|
||||
phirM = AinvrM*rhsrM
|
||||
|
||||
#Step4: Making Figure
|
||||
fig, axes = plt.subplots(1,2,figsize=(12*1.2,4*1.2))
|
||||
label = ["(a)", "(b)"]
|
||||
opts = {}
|
||||
vmin, vmax = phitM.min(), phitM.max()
|
||||
dat = tM.plotImage(phitM, ax=axes[0], clim=(vmin, vmax), grid=True)
|
||||
|
||||
#TODO: At the moment Curvilinear Mesh do not have plotimage
|
||||
|
||||
Xi = tM.gridCC[:,0].reshape(sz[0], sz[1], order='F')
|
||||
Yi = tM.gridCC[:,1].reshape(sz[0], sz[1], order='F')
|
||||
PHIrM = griddata(rM.gridCC[:,0], rM.gridCC[:,1], phirM, Xi, Yi, interp='linear')
|
||||
axes[1].contourf(Xi, Yi, PHIrM, 100, vmin=vmin, vmax=vmax)
|
||||
|
||||
cb = plt.colorbar(dat[0], ax=axes[0]); cb.set_label("Voltage (V)")
|
||||
cb = plt.colorbar(dat[0], ax=axes[1]); cb.set_label("Voltage (V)")
|
||||
|
||||
tM.plotGrid(ax=axes[0], **opts)
|
||||
axes[0].set_title('TensorMesh')
|
||||
rM.plotGrid(ax=axes[1], **opts)
|
||||
axes[1].set_title('CurvilinearMesh')
|
||||
for i in range(2):
|
||||
axes[i].set_xlim(0.025, 0.975)
|
||||
axes[i].set_ylim(0.025, 0.975)
|
||||
axes[i].text(0., 1.0, label[i], fontsize=20)
|
||||
if i==0:
|
||||
axes[i].set_ylabel("y")
|
||||
else:
|
||||
axes[i].set_ylabel(" ")
|
||||
axes[i].set_xlabel("x")
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,116 @@
|
||||
from SimPEG import *
|
||||
import SimPEG.EM as EM
|
||||
from SimPEG.EM import mu_0
|
||||
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
EM: FDEM: 1D: Inversion
|
||||
=======================
|
||||
|
||||
Here we will create and run a FDEM 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')
|
||||
|
||||
layerz = -100.
|
||||
|
||||
active = mesh.vectorCCz<0.
|
||||
layer = (mesh.vectorCCz<0.) & (mesh.vectorCCz>=layerz)
|
||||
actMap = Maps.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(mesh) * actMap
|
||||
sig_half = 2e-2
|
||||
sig_air = 1e-8
|
||||
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(-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=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.pair(survey)
|
||||
|
||||
std = 0.05
|
||||
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 = (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)
|
||||
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 = 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-3
|
||||
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(-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}$'],loc='best')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -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.ActiveCells(mesh, active, np.log(1e-8), nC=mesh.nCz)
|
||||
mapping = Maps.ExpMap(mesh) * Maps.Vertical1DMap(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()
|
||||
@@ -0,0 +1,86 @@
|
||||
from SimPEG import *
|
||||
from SimPEG.FLOW import Richards
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
FLOW: Richards: 1D: Celia1990
|
||||
=============================
|
||||
|
||||
There are two different forms of Richards equation that differ
|
||||
on how they deal with the non-linearity in the time-stepping term.
|
||||
|
||||
The most fundamental form, referred to as the
|
||||
'mixed'-form of Richards Equation Celia1990_
|
||||
|
||||
.. math::
|
||||
|
||||
\\frac{\partial \\theta(\psi)}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0
|
||||
\quad \psi \in \Omega
|
||||
|
||||
where \\\\(\\\\theta\\\\) is water content, and \\\\(\\\\psi\\\\) is pressure head.
|
||||
This formulation of Richards equation is called the
|
||||
'mixed'-form because the equation is parameterized in \\\\(\\\\psi\\\\)
|
||||
but the time-stepping is in terms of \\\\(\\\\theta\\\\).
|
||||
|
||||
As noted in Celia1990_ the 'head'-based form of Richards
|
||||
equation can be written in the continuous form as:
|
||||
|
||||
.. math::
|
||||
|
||||
\\frac{\partial \\theta}{\partial \psi}\\frac{\partial \psi}{\partial t} - \\nabla \cdot k(\psi) \\nabla \psi - \\frac{\partial k(\psi)}{\partial z} = 0 \quad \psi \in \Omega
|
||||
|
||||
However, it can be shown that this does not conserve mass in the discrete formulation.
|
||||
|
||||
Here we reproduce the results from Celia1990_ demonstrating the head-based formulation and the mixed-formulation.
|
||||
|
||||
.. _Celia1990: http://www.webpages.uidaho.edu/ch/papers/Celia.pdf
|
||||
"""
|
||||
M = Mesh.TensorMesh([np.ones(40)])
|
||||
M.setCellGradBC('dirichlet')
|
||||
params = Richards.Empirical.HaverkampParams().celia1990
|
||||
params['Ks'] = np.log(params['Ks'])
|
||||
E = Richards.Empirical.Haverkamp(M, **params)
|
||||
|
||||
bc = np.array([-61.5,-20.7])
|
||||
h = np.zeros(M.nC) + bc[0]
|
||||
|
||||
|
||||
def getFields(timeStep,method):
|
||||
timeSteps = np.ones(360/timeStep)*timeStep
|
||||
prob = Richards.RichardsProblem(M, mapping=E, timeSteps=timeSteps,
|
||||
boundaryConditions=bc, initialConditions=h,
|
||||
doNewton=False, method=method)
|
||||
return prob.fields(params['Ks'])
|
||||
|
||||
Hs_M10 = getFields(10., 'mixed')
|
||||
Hs_M30 = getFields(30., 'mixed')
|
||||
Hs_M120= getFields(120.,'mixed')
|
||||
Hs_H10 = getFields(10., 'head')
|
||||
Hs_H30 = getFields(30., 'head')
|
||||
Hs_H120= getFields(120.,'head')
|
||||
|
||||
if not plotIt:return
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure(figsize=(13,5))
|
||||
plt.subplot(121)
|
||||
plt.plot(40-M.gridCC, Hs_M10[-1],'b-')
|
||||
plt.plot(40-M.gridCC, Hs_M30[-1],'r-')
|
||||
plt.plot(40-M.gridCC, Hs_M120[-1],'k-')
|
||||
plt.ylim([-70,-10])
|
||||
plt.title('Mixed Method')
|
||||
plt.xlabel('Depth, cm')
|
||||
plt.ylabel('Pressure Head, cm')
|
||||
plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec'))
|
||||
plt.subplot(122)
|
||||
plt.plot(40-M.gridCC, Hs_H10[-1],'b-')
|
||||
plt.plot(40-M.gridCC, Hs_H30[-1],'r-')
|
||||
plt.plot(40-M.gridCC, Hs_H120[-1],'k-')
|
||||
plt.ylim([-70,-10])
|
||||
plt.title('Head-Based Method')
|
||||
plt.xlabel('Depth, cm')
|
||||
plt.ylabel('Pressure Head, cm')
|
||||
plt.legend(('$\Delta t$ = 10 sec','$\Delta t$ = 30 sec','$\Delta t$ = 120 sec'))
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,77 @@
|
||||
from SimPEG import Mesh, Utils, np, SolverLU
|
||||
|
||||
## 2D DC forward modeling example with Tensor and Curvilinear Meshes
|
||||
|
||||
def run(plotIt=True):
|
||||
# Step1: Generate Tensor and Curvilinear Mesh
|
||||
sz = [40,40]
|
||||
# Tensor Mesh
|
||||
tM = Mesh.TensorMesh(sz)
|
||||
# Curvilinear Mesh
|
||||
rM = Mesh.CurvilinearMesh(Utils.meshutils.exampleLrmGrid(sz,'rotate'))
|
||||
# Step2: Direct Current (DC) operator
|
||||
def DCfun(mesh, pts):
|
||||
D = mesh.faceDiv
|
||||
G = D.T
|
||||
sigma = 1e-2*np.ones(mesh.nC)
|
||||
Msigi = mesh.getFaceInnerProduct(1./sigma)
|
||||
MsigI = Utils.sdInv(Msigi)
|
||||
A = D*MsigI*G
|
||||
A[-1,-1] /= mesh.vol[-1] # Remove null space
|
||||
rhs = np.zeros(mesh.nC)
|
||||
txind = Utils.meshutils.closestPoints(mesh, pts)
|
||||
rhs[txind] = np.r_[1,-1]
|
||||
return A, rhs
|
||||
|
||||
pts = np.vstack((np.r_[0.25, 0.5], np.r_[0.75, 0.5]))
|
||||
|
||||
#Step3: Solve DC problem (LU solver)
|
||||
AtM, rhstM = DCfun(tM, pts)
|
||||
AinvtM = SolverLU(AtM)
|
||||
phitM = AinvtM*rhstM
|
||||
|
||||
ArM, rhsrM = DCfun(rM, pts)
|
||||
AinvrM = SolverLU(ArM)
|
||||
phirM = AinvrM*rhsrM
|
||||
|
||||
if not plotIt: return
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
from matplotlib.mlab import griddata
|
||||
|
||||
#Step4: Making Figure
|
||||
fig, axes = plt.subplots(1,2,figsize=(12*1.2,4*1.2))
|
||||
label = ["(a)", "(b)"]
|
||||
opts = {}
|
||||
vmin, vmax = phitM.min(), phitM.max()
|
||||
dat = tM.plotImage(phitM, ax=axes[0], clim=(vmin, vmax), grid=True)
|
||||
|
||||
#TODO: At the moment Curvilinear Mesh do not have plotimage
|
||||
|
||||
Xi = tM.gridCC[:,0].reshape(sz[0], sz[1], order='F')
|
||||
Yi = tM.gridCC[:,1].reshape(sz[0], sz[1], order='F')
|
||||
PHIrM = griddata(rM.gridCC[:,0], rM.gridCC[:,1], phirM, Xi, Yi, interp='linear')
|
||||
axes[1].contourf(Xi, Yi, PHIrM, 100, vmin=vmin, vmax=vmax)
|
||||
|
||||
cb = plt.colorbar(dat[0], ax=axes[0]); cb.set_label("Voltage (V)")
|
||||
cb = plt.colorbar(dat[0], ax=axes[1]); cb.set_label("Voltage (V)")
|
||||
|
||||
tM.plotGrid(ax=axes[0], **opts)
|
||||
axes[0].set_title('TensorMesh')
|
||||
rM.plotGrid(ax=axes[1], **opts)
|
||||
axes[1].set_title('CurvilinearMesh')
|
||||
for i in range(2):
|
||||
axes[i].set_xlim(0.025, 0.975)
|
||||
axes[i].set_ylim(0.025, 0.975)
|
||||
axes[i].text(0., 1.0, label[i], fontsize=20)
|
||||
if i==0:
|
||||
axes[i].set_ylabel("y")
|
||||
else:
|
||||
axes[i].set_ylabel(" ")
|
||||
axes[i].set_xlabel("x")
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -1,29 +1,39 @@
|
||||
from SimPEG import *
|
||||
|
||||
class LinearSurvey(Survey.BaseSurvey):
|
||||
def projectFields(self, u):
|
||||
return u
|
||||
|
||||
class LinearProblem(Problem.BaseProblem):
|
||||
"""docstring for LinearProblem"""
|
||||
def run(N=100, plotIt=True):
|
||||
"""
|
||||
Inversion: Linear Problem
|
||||
=========================
|
||||
|
||||
surveyPair = LinearSurvey
|
||||
Here we go over the basics of creating a linear problem and inversion.
|
||||
|
||||
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)
|
||||
class LinearSurvey(Survey.BaseSurvey):
|
||||
def projectFields(self, u):
|
||||
return u
|
||||
|
||||
def Jvec(self, m, v, u=None):
|
||||
return self.G.dot(v)
|
||||
class LinearProblem(Problem.BaseProblem):
|
||||
|
||||
def Jtvec(self, m, v, u=None):
|
||||
return self.G.T.dot(v)
|
||||
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)
|
||||
|
||||
|
||||
def run(N, plotIt=True):
|
||||
np.random.seed(1)
|
||||
|
||||
mesh = Mesh.TensorMesh([N])
|
||||
|
||||
nk = 20
|
||||
@@ -52,7 +62,7 @@ def run(N, plotIt=True):
|
||||
|
||||
reg = Regularization.Tikhonov(mesh)
|
||||
dmis = DataMisfit.l2_DataMisfit(survey)
|
||||
opt = Optimization.InexactGaussNewton(maxIter=20)
|
||||
opt = Optimization.InexactGaussNewton(maxIter=35)
|
||||
invProb = InvProblem.BaseInvProblem(dmis, reg, opt)
|
||||
beta = Directives.BetaSchedule()
|
||||
betaest = Directives.BetaEstimate_ByEig()
|
||||
@@ -63,16 +73,18 @@ def run(N, plotIt=True):
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure(1)
|
||||
for i in range(prob.G.shape[0]):
|
||||
plt.plot(prob.G[i,:])
|
||||
|
||||
plt.figure(2)
|
||||
plt.plot(M.vectorCCx, survey.mtrue, 'b-')
|
||||
plt.plot(M.vectorCCx, mrec, 'r-')
|
||||
fig, axes = plt.subplots(1,2,figsize=(12*1.2,4*1.2))
|
||||
for i in range(prob.G.shape[0]):
|
||||
axes[0].plot(prob.G[i,:])
|
||||
axes[0].set_title('Columns of matrix G')
|
||||
|
||||
axes[1].plot(M.vectorCCx, survey.mtrue, 'b-')
|
||||
axes[1].plot(M.vectorCCx, mrec, 'r-')
|
||||
axes[1].legend(('True Model', 'Recovered Model'))
|
||||
plt.show()
|
||||
|
||||
return prob, survey, mesh, mrec
|
||||
|
||||
if __name__ == '__main__':
|
||||
run(100)
|
||||
run()
|
||||
@@ -0,0 +1,46 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
Mesh: Basic: PlotImage
|
||||
======================
|
||||
|
||||
You can use M.PlotImage to plot images on all of the Meshes.
|
||||
|
||||
|
||||
"""
|
||||
M = Mesh.TensorMesh([32,32])
|
||||
v = Utils.ModelBuilder.randomModel(M.vnC, seed=789)
|
||||
v = Utils.mkvc(v)
|
||||
|
||||
O = Mesh.TreeMesh([32,32])
|
||||
O.refine(1)
|
||||
def function(cell):
|
||||
if (cell.center[0] < 0.75 and cell.center[0] > 0.25 and
|
||||
cell.center[1] < 0.75 and cell.center[1] > 0.25):return 5
|
||||
if (cell.center[0] < 0.9 and cell.center[0] > 0.1 and
|
||||
cell.center[1] < 0.9 and cell.center[1] > 0.1):return 4
|
||||
return 3
|
||||
O.refine(function)
|
||||
|
||||
P = M.getInterpolationMat(O.gridCC, 'CC')
|
||||
|
||||
ov = P * v
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, axes = plt.subplots(1,2,figsize=(10,5))
|
||||
|
||||
out = M.plotImage(v, grid=True, ax=axes[0])
|
||||
cb = plt.colorbar(out[0], ax=axes[0]); cb.set_label("Random Field")
|
||||
axes[0].set_title('TensorMesh')
|
||||
|
||||
out = O.plotImage(ov, grid=True, ax=axes[1], clim=[0,1])
|
||||
cb = plt.colorbar(out[0], ax=axes[1]); cb.set_label("Random Field")
|
||||
axes[1].set_title('TreeMesh')
|
||||
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,30 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
Mesh: Basic: Types
|
||||
==================
|
||||
|
||||
Here we show SimPEG used to create three different types of meshes.
|
||||
|
||||
"""
|
||||
sz = [16,16]
|
||||
tM = Mesh.TensorMesh(sz)
|
||||
qM = Mesh.TreeMesh(sz)
|
||||
qM.refine(lambda cell: 4 if np.sqrt(((np.r_[cell.center]-0.5)**2).sum()) < 0.4 else 3)
|
||||
rM = Mesh.CurvilinearMesh(Utils.meshutils.exampleLrmGrid(sz,'rotate'))
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, axes = plt.subplots(1,3,figsize=(14,5))
|
||||
opts = {}
|
||||
tM.plotGrid(ax=axes[0], **opts)
|
||||
axes[0].set_title('TensorMesh')
|
||||
qM.plotGrid(ax=axes[1], **opts)
|
||||
axes[1].set_title('TreeMesh')
|
||||
rM.plotGrid(ax=axes[2], **opts)
|
||||
axes[2].set_title('CurvilinearMesh')
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,125 @@
|
||||
|
||||
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([8,8])
|
||||
# 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))
|
||||
T.plotGrid()
|
||||
# print [c for c in T]
|
||||
c = T[0]
|
||||
plt.plot(c.center[0],c.center[1],'r.')
|
||||
nodes = c.nodes
|
||||
for n in nodes:
|
||||
_ = T._gridN[n,:]
|
||||
plt.plot(_[0],_[1],'gs')
|
||||
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()
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True, n=60):
|
||||
"""
|
||||
Mesh: Operators: Cahn Hilliard
|
||||
==============================
|
||||
|
||||
This example is based on the example in the FiPy_ library.
|
||||
Please see their documentation for more information about the Cahn-Hilliard equation.
|
||||
|
||||
The "Cahn-Hilliard" equation separates a field \\\\( \\\\phi \\\\) into 0 and 1 with smooth transitions.
|
||||
|
||||
.. math::
|
||||
|
||||
\\frac{\partial \phi}{\partial t} = \\nabla \cdot D \\nabla \left( \\frac{\partial f}{\partial \phi} - \epsilon^2 \\nabla^2 \phi \\right)
|
||||
|
||||
Where \\\\( f \\\\) is the energy function \\\\( f = ( a^2 / 2 )\\\\phi^2(1 - \\\\phi)^2 \\\\)
|
||||
which drives \\\\( \\\\phi \\\\) towards either 0 or 1, this competes with the term
|
||||
\\\\(\\\\epsilon^2 \\\\nabla^2 \\\\phi \\\\) which is a diffusion term that creates smooth changes in \\\\( \\\\phi \\\\).
|
||||
The equation can be factored:
|
||||
|
||||
.. math::
|
||||
|
||||
\\frac{\partial \phi}{\partial t} = \\nabla \cdot D \\nabla \psi \\\\
|
||||
\psi = \\frac{\partial^2 f}{\partial \phi^2} (\phi - \phi^{\\text{old}}) + \\frac{\partial f}{\partial \phi} - \epsilon^2 \\nabla^2 \phi
|
||||
|
||||
Here we will need the derivatives of \\\\( f \\\\):
|
||||
|
||||
.. math::
|
||||
|
||||
\\frac{\partial f}{\partial \phi} = (a^2/2)2\phi(1-\phi)(1-2\phi)
|
||||
\\frac{\partial^2 f}{\partial \phi^2} = (a^2/2)2[1-6\phi(1-\phi)]
|
||||
|
||||
The implementation below uses backwards Euler in time with an exponentially increasing time step.
|
||||
The initial \\\\( \\\\phi \\\\) is a normally distributed field with a standard deviation of 0.1 and mean of 0.5.
|
||||
The grid is 60x60 and takes a few seconds to solve ~130 times. The results are seen below, and you can see the
|
||||
field separating as the time increases.
|
||||
|
||||
.. _FiPy: http://www.ctcms.nist.gov/fipy/examples/cahnHilliard/generated/examples.cahnHilliard.mesh2DCoupled.html
|
||||
|
||||
"""
|
||||
|
||||
np.random.seed(5)
|
||||
|
||||
# Here we are going to rearrange the equations:
|
||||
|
||||
# (phi_ - phi)/dt = A*(d2fdphi2*(phi_ - phi) + dfdphi - L*phi_)
|
||||
# (phi_ - phi)/dt = A*(d2fdphi2*phi_ - d2fdphi2*phi + dfdphi - L*phi_)
|
||||
# (phi_ - phi)/dt = A*d2fdphi2*phi_ + A*( - d2fdphi2*phi + dfdphi - L*phi_)
|
||||
# phi_ - phi = dt*A*d2fdphi2*phi_ + dt*A*(- d2fdphi2*phi + dfdphi - L*phi_)
|
||||
# phi_ - dt*A*d2fdphi2 * phi_ = dt*A*(- d2fdphi2*phi + dfdphi - L*phi_) + phi
|
||||
# (I - dt*A*d2fdphi2) * phi_ = dt*A*(- d2fdphi2*phi + dfdphi - L*phi_) + phi
|
||||
# (I - dt*A*d2fdphi2) * phi_ = dt*A*dfdphi - dt*A*d2fdphi2*phi - dt*A*L*phi_ + phi
|
||||
# (dt*A*d2fdphi2 - I) * phi_ = dt*A*d2fdphi2*phi + dt*A*L*phi_ - phi - dt*A*dfdphi
|
||||
# (dt*A*d2fdphi2 - I - dt*A*L) * phi_ = (dt*A*d2fdphi2 - I)*phi - dt*A*dfdphi
|
||||
|
||||
h = [(0.25,n)]
|
||||
M = Mesh.TensorMesh([h,h])
|
||||
|
||||
# Constants
|
||||
D = a = epsilon = 1.
|
||||
I = Utils.speye(M.nC)
|
||||
|
||||
# Operators
|
||||
A = D * M.faceDiv * M.cellGrad
|
||||
L = epsilon**2 * M.faceDiv * M.cellGrad
|
||||
|
||||
duration = 75
|
||||
elapsed = 0.
|
||||
dexp = -5
|
||||
phi = np.random.normal(loc=0.5,scale=0.01,size=M.nC)
|
||||
ii, jj = 0, 0
|
||||
PHIS = []
|
||||
capture = np.logspace(-1,np.log10(duration),8)
|
||||
while elapsed < duration:
|
||||
dt = min(100, np.exp(dexp))
|
||||
elapsed += dt
|
||||
dexp += 0.05
|
||||
|
||||
dfdphi = a**2 * 2 * phi * (1 - phi) * (1 - 2 * phi)
|
||||
d2fdphi2 = Utils.sdiag(a**2 * 2 * (1 - 6 * phi * (1 - phi)))
|
||||
|
||||
MAT = (dt*A*d2fdphi2 - I - dt*A*L)
|
||||
rhs = (dt*A*d2fdphi2 - I)*phi - dt*A*dfdphi
|
||||
phi = Solver(MAT)*rhs
|
||||
|
||||
if elapsed > capture[jj]:
|
||||
PHIS += [(elapsed, phi.copy())]
|
||||
jj += 1
|
||||
if ii % 10 == 0: print ii, elapsed
|
||||
ii += 1
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, axes = plt.subplots(2,4,figsize=(14,6))
|
||||
axes = np.array(axes).flatten().tolist()
|
||||
for ii, ax in zip(np.linspace(0,len(PHIS)-1,len(axes)),axes):
|
||||
ii = int(ii)
|
||||
out = M.plotImage(PHIS[ii][1],ax=ax)
|
||||
ax.axis('off')
|
||||
ax.set_title('Elapsed Time: %4.1f'%PHIS[ii][0])
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,28 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
Mesh: QuadTree: Creation
|
||||
========================
|
||||
|
||||
You can give the refine method a function, which is evaluated on every cell
|
||||
of the TreeMesh.
|
||||
|
||||
Occasionally it is useful to initially refine to a constant level
|
||||
(e.g. 3 in this 32x32 mesh). This means the function is first evaluated
|
||||
on an 8x8 mesh (2^3).
|
||||
|
||||
"""
|
||||
M = Mesh.TreeMesh([32,32])
|
||||
M.refine(3)
|
||||
def function(cell):
|
||||
xyz = cell.center
|
||||
for i in range(3):
|
||||
if np.abs(np.sin(xyz[0]*np.pi*2)*0.5 + 0.5 - xyz[1]) < 0.2*i:
|
||||
return 6-i
|
||||
return 0
|
||||
M.refine(function);
|
||||
if plotIt: M.plotGrid(showIt=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,49 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True, n=60):
|
||||
"""
|
||||
Mesh: QuadTree: FaceDiv
|
||||
=======================
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
M = Mesh.TreeMesh([[(1,16)],[(1,16)]], levels=4)
|
||||
M._refineCell([0,0,0])
|
||||
M._refineCell([0,0,1])
|
||||
M._refineCell([4,4,2])
|
||||
M.__dirty__ = True
|
||||
M.number()
|
||||
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
fig, axes = plt.subplots(2,1,figsize=(10,10))
|
||||
|
||||
M.plotGrid(cells=True, nodes=False, ax=axes[0])
|
||||
axes[0].axis('off')
|
||||
axes[0].set_title('Simple QuadTree Mesh')
|
||||
axes[0].set_xlim([-1,17])
|
||||
axes[0].set_ylim([-1,17])
|
||||
|
||||
for ii, loc in zip(range(M.nC),M.gridCC):
|
||||
axes[0].text(loc[0]+0.2,loc[1],'%d'%ii, color='r')
|
||||
|
||||
axes[0].plot(M.gridFx[:,0],M.gridFx[:,1], 'g>')
|
||||
for ii, loc in zip(range(M.nFx),M.gridFx):
|
||||
axes[0].text(loc[0]+0.2,loc[1],'%d'%ii, color='g')
|
||||
|
||||
axes[0].plot(M.gridFy[:,0],M.gridFy[:,1], 'm^')
|
||||
for ii, loc in zip(range(M.nFy),M.gridFy):
|
||||
axes[0].text(loc[0]+0.2,loc[1]+0.2,'%d'%(ii+M.nFx), color='m')
|
||||
|
||||
axes[1].spy(M.faceDiv)
|
||||
axes[1].set_title('Face Divergence')
|
||||
axes[1].set_ylabel('Cell Number')
|
||||
axes[1].set_xlabel('Face Number')
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,32 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
Mesh: QuadTree: Hanging Nodes
|
||||
=============================
|
||||
|
||||
You can give the refine method a function, which is evaluated on every cell
|
||||
of the TreeMesh.
|
||||
|
||||
Occasionally it is useful to initially refine to a constant level
|
||||
(e.g. 3 in this 32x32 mesh). This means the function is first evaluated
|
||||
on an 8x8 mesh (2^3).
|
||||
|
||||
"""
|
||||
M = Mesh.TreeMesh([8,8])
|
||||
def function(cell):
|
||||
xyz = cell.center
|
||||
dist = ((xyz - [0.25,0.25])**2).sum()**0.5
|
||||
if dist < 0.25:
|
||||
return 3
|
||||
return 2
|
||||
M.refine(function);
|
||||
M.number()
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
M.plotGrid(nodes=True, cells=True, facesX=True)
|
||||
plt.legend(('Grid', 'Cell Centers', 'Nodes', 'Hanging Nodes', 'X faces', 'Hanging X faces'))
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -0,0 +1,35 @@
|
||||
from SimPEG import *
|
||||
|
||||
def run(plotIt=True):
|
||||
"""
|
||||
|
||||
Mesh: Tensor: Creation
|
||||
======================
|
||||
|
||||
For tensor meshes, there are some functions that can come
|
||||
in handy. For example, creating mesh tensors can be a bit time
|
||||
consuming, these can be created speedily by just giving numbers
|
||||
and sizes of padding. See the example below, that follows this
|
||||
notation::
|
||||
|
||||
h1 = (
|
||||
(cellSize, numPad, [, increaseFactor]),
|
||||
(cellSize, numCore),
|
||||
(cellSize, numPad, [, increaseFactor])
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
You can center your mesh by passing a 'C' for the x0[i] position.
|
||||
A 'N' will make the entire mesh negative, and a '0' (or a 0) will
|
||||
make the mesh start at zero.
|
||||
|
||||
"""
|
||||
h1 = [(10, 5, -1.3), (5, 20), (10, 3, 1.3)]
|
||||
M = Mesh.TensorMesh([h1, h1], x0='CN')
|
||||
if plotIt:
|
||||
M.plotGrid(showIt=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
||||
+106
-1
@@ -1 +1,106 @@
|
||||
import Linear
|
||||
# Run this file to add imports.
|
||||
|
||||
##### AUTOIMPORTS #####
|
||||
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
|
||||
import Mesh_Basic_PlotImage
|
||||
import Mesh_Basic_Types
|
||||
import Mesh_Operators_CahnHilliard
|
||||
import Mesh_QuadTree_Creation
|
||||
import Mesh_QuadTree_FaceDiv
|
||||
import Mesh_QuadTree_HangingNodes
|
||||
import Mesh_Tensor_Creation
|
||||
|
||||
__examples__ = ["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"]
|
||||
|
||||
##### AUTOIMPORTS #####
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""
|
||||
|
||||
Run the following to create the examples documentation and add to the imports at the top.
|
||||
|
||||
"""
|
||||
|
||||
import shutil, os
|
||||
from SimPEG import Examples
|
||||
|
||||
# Create the examples dir in the docs folder.
|
||||
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(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(fName, 'r')
|
||||
inimports = False
|
||||
out = ''
|
||||
for line in f:
|
||||
if not inimports:
|
||||
out += line
|
||||
|
||||
if line == "##### AUTOIMPORTS #####\n":
|
||||
inimports = not inimports
|
||||
if inimports:
|
||||
out += '\n'.join(["import %s"%_ for _ in exfiles])
|
||||
out += '\n\n__examples__ = ["' + '", "'.join(exfiles)+ '"]\n'
|
||||
out += '\n##### AUTOIMPORTS #####\n'
|
||||
f.close()
|
||||
|
||||
f = file(fName, 'w')
|
||||
f.write(out)
|
||||
f.close()
|
||||
|
||||
|
||||
def _makeExample(filePath, runFunction):
|
||||
"""Makes the example given a path of the file and the run function."""
|
||||
filePath = os.path.realpath(filePath)
|
||||
name = filePath.split(os.path.sep)[-1].rstrip('.pyc').rstrip('.py')
|
||||
|
||||
docstr = runFunction.__doc__
|
||||
if docstr is None:
|
||||
doc = '%s\n%s'%(name.replace('_',' '),'='*len(name))
|
||||
else:
|
||||
doc = '\n'.join([_[8:].rstrip() for _ in docstr.split('\n')])
|
||||
|
||||
out = """.. _examples_%s:
|
||||
|
||||
.. --------------------------------- ..
|
||||
.. ..
|
||||
.. THIS FILE IS AUTO GENEREATED ..
|
||||
.. ..
|
||||
.. SimPEG/Examples/__init__.py ..
|
||||
.. ..
|
||||
.. --------------------------------- ..
|
||||
|
||||
%s
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Examples
|
||||
Examples.%s.run()
|
||||
|
||||
.. literalinclude:: ../../SimPEG/Examples/%s.py
|
||||
:language: python
|
||||
:linenos:
|
||||
"""%(name,doc,name,name)
|
||||
|
||||
rst = os.path.sep.join((filePath.split(os.path.sep)[:-3] + ['docs', 'examples', name + '.rst']))
|
||||
|
||||
print 'Creating: %s.rst'%name
|
||||
f = open(rst, 'w')
|
||||
f.write(out)
|
||||
f.close()
|
||||
|
||||
for ex in dir(Examples):
|
||||
if ex.startswith('_'): continue
|
||||
E = getattr(Examples,ex)
|
||||
_makeExample(E.__file__, E.run)
|
||||
|
||||
@@ -0,0 +1,578 @@
|
||||
from SimPEG import Mesh, Maps, Utils, np
|
||||
|
||||
|
||||
class NonLinearMap(object):
|
||||
"""
|
||||
SimPEG NonLinearMap
|
||||
|
||||
"""
|
||||
|
||||
__metaclass__ = Utils.SimPEGMetaClass
|
||||
|
||||
counter = None #: A SimPEG.Utils.Counter object
|
||||
mesh = None #: A SimPEG Mesh
|
||||
|
||||
def __init__(self, mesh):
|
||||
self.mesh = mesh
|
||||
|
||||
def _transform(self, u, m):
|
||||
"""
|
||||
:param numpy.array u: fields
|
||||
:param numpy.array m: model
|
||||
:rtype: numpy.array
|
||||
:return: transformed model
|
||||
|
||||
The *transform* changes the model into the physical property.
|
||||
|
||||
"""
|
||||
return m
|
||||
|
||||
def derivU(self, u, m):
|
||||
"""
|
||||
:param numpy.array u: fields
|
||||
:param numpy.array m: model
|
||||
:rtype: scipy.csr_matrix
|
||||
:return: derivative of transformed model
|
||||
|
||||
The *transform* changes the model into the physical property.
|
||||
The *transformDerivU* provides the derivative of the *transform* with respect to the fields.
|
||||
"""
|
||||
raise NotImplementedError('The transformDerivU is not implemented.')
|
||||
|
||||
|
||||
def derivM(self, u, m):
|
||||
"""
|
||||
:param numpy.array u: fields
|
||||
:param numpy.array m: model
|
||||
:rtype: scipy.csr_matrix
|
||||
:return: derivative of transformed model
|
||||
|
||||
The *transform* changes the model into the physical property.
|
||||
The *transformDerivU* provides the derivative of the *transform* with respect to the model.
|
||||
"""
|
||||
raise NotImplementedError('The transformDerivM is not implemented.')
|
||||
|
||||
@property
|
||||
def nP(self):
|
||||
"""Number of parameters in the model."""
|
||||
return self.mesh.nC
|
||||
|
||||
def example(self):
|
||||
raise NotImplementedError('The example is not implemented.')
|
||||
|
||||
def test(self, m=None):
|
||||
raise NotImplementedError('The test is not implemented.')
|
||||
|
||||
|
||||
class RichardsMap(object):
|
||||
"""docstring for RichardsMap"""
|
||||
|
||||
mesh = None #: SimPEG mesh
|
||||
|
||||
@property
|
||||
def thetaModel(self):
|
||||
"""Model for moisture content"""
|
||||
return self._thetaModel
|
||||
|
||||
@property
|
||||
def kModel(self):
|
||||
"""Model for hydraulic conductivity"""
|
||||
return self._kModel
|
||||
|
||||
def __init__(self, mesh, thetaModel, kModel):
|
||||
self.mesh = mesh
|
||||
assert isinstance(thetaModel, NonLinearMap)
|
||||
assert isinstance(kModel, NonLinearMap)
|
||||
|
||||
self._thetaModel = thetaModel
|
||||
self._kModel = kModel
|
||||
|
||||
def theta(self, u, m):
|
||||
return self.thetaModel.transform(u, m)
|
||||
|
||||
def thetaDerivM(self, u, m):
|
||||
return self.thetaModel.transformDerivM(u, m)
|
||||
|
||||
def thetaDerivU(self, u, m):
|
||||
return self.thetaModel.transformDerivU(u, m)
|
||||
|
||||
def k(self, u, m):
|
||||
return self.kModel.transform(u, m)
|
||||
|
||||
def kDerivM(self, u, m):
|
||||
return self.kModel.transformDerivM(u, m)
|
||||
|
||||
def kDerivU(self, u, m):
|
||||
return self.kModel.transformDerivU(u, m)
|
||||
|
||||
def plot(self, m):
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
m = m[0]
|
||||
h = np.linspace(-100, 20, 1000)
|
||||
ax = plt.subplot(121)
|
||||
ax.plot(self.theta(h, m), h)
|
||||
ax = plt.subplot(122)
|
||||
ax.semilogx(self.k(h, m), h)
|
||||
|
||||
def _assertMatchesPair(self, pair):
|
||||
assert isinstance(self, pair), "Mapping object must be an instance of a %s class."%(pair.__name__)
|
||||
|
||||
|
||||
|
||||
def _ModelProperty(name, models, doc=None, default=None):
|
||||
|
||||
def fget(self):
|
||||
model = models[0]
|
||||
if getattr(self, model, None) is not None:
|
||||
MOD = getattr(self, model)
|
||||
return getattr(MOD, name, default)
|
||||
return default
|
||||
|
||||
def fset(self, value):
|
||||
for model in models:
|
||||
if getattr(self, model, None) is not None:
|
||||
MOD = getattr(self, model)
|
||||
setattr(MOD, name, value)
|
||||
|
||||
return property(fget, fset=fset, doc=doc)
|
||||
|
||||
|
||||
class HaverkampParams(object):
|
||||
"""Holds some default parameterizations for the Haverkamp model."""
|
||||
def __init__(self): pass
|
||||
@property
|
||||
def celia1990(self):
|
||||
"""
|
||||
Parameters used in:
|
||||
|
||||
Celia, Michael A., Efthimios T. Bouloutas, and Rebecca L. Zarba.
|
||||
"A general mass-conservative numerical solution for the unsaturated flow equation."
|
||||
Water Resources Research 26.7 (1990): 1483-1496.
|
||||
|
||||
"""
|
||||
return {'alpha':1.611e+06, 'beta':3.96,
|
||||
'theta_r':0.075, 'theta_s':0.287,
|
||||
'Ks':9.44e-03, 'A':1.175e+06,
|
||||
'gamma':4.74}
|
||||
|
||||
|
||||
class _haverkamp_theta(NonLinearMap):
|
||||
|
||||
theta_s = 0.430
|
||||
theta_r = 0.078
|
||||
alpha = 0.036
|
||||
beta = 3.960
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
NonLinearMap.__init__(self, mesh)
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
def setModel(self, m):
|
||||
self._currentModel = m
|
||||
|
||||
def transform(self, u, m):
|
||||
self.setModel(m)
|
||||
f = (self.alpha*(self.theta_s - self.theta_r )/
|
||||
(self.alpha + abs(u)**self.beta) + self.theta_r)
|
||||
if Utils.isScalar(self.theta_s):
|
||||
f[u >= 0] = self.theta_s
|
||||
else:
|
||||
f[u >= 0] = self.theta_s[u >= 0]
|
||||
return f
|
||||
|
||||
def transformDerivM(self, u, m):
|
||||
self.setModel(m)
|
||||
|
||||
def transformDerivU(self, u, m):
|
||||
self.setModel(m)
|
||||
g = (self.alpha*((self.theta_s - self.theta_r)/
|
||||
(self.alpha + abs(u)**self.beta)**2)
|
||||
*(-self.beta*abs(u)**(self.beta-1)*np.sign(u)))
|
||||
g[u >= 0] = 0
|
||||
g = Utils.sdiag(g)
|
||||
return g
|
||||
|
||||
|
||||
class _haverkamp_k(NonLinearMap):
|
||||
|
||||
A = 1.175e+06
|
||||
gamma = 4.74
|
||||
Ks = np.log(24.96)
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
NonLinearMap.__init__(self, mesh)
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
def setModel(self, m):
|
||||
self._currentModel = m
|
||||
#TODO: Fix me!
|
||||
self.Ks = m
|
||||
|
||||
def transform(self, u, m):
|
||||
self.setModel(m)
|
||||
f = np.exp(self.Ks)*self.A/(self.A+abs(u)**self.gamma)
|
||||
if Utils.isScalar(self.Ks):
|
||||
f[u >= 0] = np.exp(self.Ks)
|
||||
else:
|
||||
f[u >= 0] = np.exp(self.Ks[u >= 0])
|
||||
return f
|
||||
|
||||
def transformDerivM(self, u, m):
|
||||
self.setModel(m)
|
||||
#A
|
||||
# dA = np.exp(self.Ks)/(self.A+abs(u)**self.gamma) - np.exp(self.Ks)*self.A/(self.A+abs(u)**self.gamma)**2
|
||||
#gamma
|
||||
# dgamma = -(self.A*np.exp(self.Ks)*np.log(abs(u))*abs(u)**self.gamma)/(self.A + abs(u)**self.gamma)**2
|
||||
|
||||
# This assumes that the the model is Ks
|
||||
return Utils.sdiag(self.transform(u, m))
|
||||
|
||||
def transformDerivU(self, u, m):
|
||||
self.setModel(m)
|
||||
g = -(np.exp(self.Ks)*self.A*self.gamma*abs(u)**(self.gamma-1)*np.sign(u))/((self.A+abs(u)**self.gamma)**2)
|
||||
g[u >= 0] = 0
|
||||
g = Utils.sdiag(g)
|
||||
return g
|
||||
|
||||
class Haverkamp(RichardsMap):
|
||||
"""Haverkamp Model"""
|
||||
|
||||
alpha = _ModelProperty('alpha', ['thetaModel'], default=1.6110e+06)
|
||||
beta = _ModelProperty('beta', ['thetaModel'], default=3.96)
|
||||
theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075)
|
||||
theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287)
|
||||
|
||||
Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96))
|
||||
A = _ModelProperty('A', ['kModel'], default=1.1750e+06)
|
||||
gamma = _ModelProperty('gamma', ['kModel'], default=4.74)
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
RichardsMap.__init__(self, mesh,
|
||||
_haverkamp_theta(mesh),
|
||||
_haverkamp_k(mesh))
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
class _vangenuchten_theta(NonLinearMap):
|
||||
|
||||
theta_s = 0.430
|
||||
theta_r = 0.078
|
||||
alpha = 0.036
|
||||
n = 1.560
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
NonLinearMap.__init__(self, mesh)
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
def setModel(self, m):
|
||||
self._currentModel = m
|
||||
|
||||
def transform(self, u, m):
|
||||
self.setModel(m)
|
||||
m = 1 - 1.0/self.n
|
||||
f = (( self.theta_s - self.theta_r )/
|
||||
((1+abs(self.alpha*u)**self.n)**m) + self.theta_r)
|
||||
if Utils.isScalar(self.theta_s):
|
||||
f[u >= 0] = self.theta_s
|
||||
else:
|
||||
f[u >= 0] = self.theta_s[u >= 0]
|
||||
|
||||
return f
|
||||
|
||||
def transformDerivM(self, u, m):
|
||||
self.setModel(m)
|
||||
|
||||
def transformDerivU(self, u, m):
|
||||
g = -self.alpha*self.n*abs(self.alpha*u)**(self.n - 1)*np.sign(self.alpha*u)*(1./self.n - 1)*(self.theta_r - self.theta_s)*(abs(self.alpha*u)**self.n + 1)**(1./self.n - 2)
|
||||
g[u >= 0] = 0
|
||||
g = Utils.sdiag(g)
|
||||
return g
|
||||
|
||||
|
||||
class _vangenuchten_k(NonLinearMap):
|
||||
|
||||
I = 0.500
|
||||
alpha = 0.036
|
||||
n = 1.560
|
||||
Ks = np.log(24.96)
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
NonLinearMap.__init__(self, mesh)
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
def setModel(self, m):
|
||||
self._currentModel = m
|
||||
#TODO: Fix me!
|
||||
self.Ks = m
|
||||
|
||||
def transform(self, u, m):
|
||||
self.setModel(m)
|
||||
|
||||
alpha = self.alpha
|
||||
I = self.I
|
||||
n = self.n
|
||||
Ks = self.Ks
|
||||
m = 1.0 - 1.0/n
|
||||
|
||||
theta_e = 1.0/((1.0+abs(alpha*u)**n)**m)
|
||||
f = np.exp(Ks)*theta_e**I* ( ( 1.0 - ( 1.0 - theta_e**(1.0/m) )**m )**2 )
|
||||
if Utils.isScalar(self.Ks):
|
||||
f[u >= 0] = np.exp(self.Ks)
|
||||
else:
|
||||
f[u >= 0] = np.exp(self.Ks[u >= 0])
|
||||
return f
|
||||
|
||||
def transformDerivM(self, u, m):
|
||||
self.setModel(m)
|
||||
# #alpha
|
||||
# # dA = I*u*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*u)**n + 1)**(1.0/n - 2) - (2*u*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/(((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n));
|
||||
# #n
|
||||
# # dn = 2*np.exp(Ks)*((np.log(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n))/n**2 + ((1.0/n - 1)*(((np.log(abs(alpha*u)**n + 1)*(abs(alpha*u)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*u)**n*np.log(abs(alpha*u))*(1.0/n - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/((1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)) - np.log((abs(alpha*u)**n + 1)**(1.0/n - 1))/(n**2*(1.0/n - 1)**2*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))))/(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1) - I*np.exp(Ks)*((np.log(abs(alpha*u)**n + 1)*(abs(alpha*u)**n + 1)**(1.0/n - 1))/n**2 - abs(alpha*u)**n*np.log(abs(alpha*u))*(1.0/n - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2;
|
||||
# #I
|
||||
# # dI = np.exp(Ks)*np.log((abs(alpha*u)**n + 1)**(1.0/n - 1))*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2;
|
||||
return Utils.sdiag(self.transform(u, m)) # This assumes that the the model is Ks
|
||||
|
||||
def transformDerivU(self, u, m):
|
||||
self.setModel(m)
|
||||
alpha = self.alpha
|
||||
I = self.I
|
||||
n = self.n
|
||||
Ks = self.Ks
|
||||
m = 1.0 - 1.0/n
|
||||
|
||||
g = I*alpha*n*np.exp(Ks)*abs(alpha*u)**(n - 1.0)*np.sign(alpha*u)*(1.0/n - 1.0)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**(I - 1)*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)**2*(abs(alpha*u)**n + 1)**(1.0/n - 2) - (2*alpha*n*np.exp(Ks)*abs(alpha*u)**(n - 1)*np.sign(alpha*u)*(1.0/n - 1)*((abs(alpha*u)**n + 1)**(1.0/n - 1))**I*((1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1 - 1.0/n) - 1)*(abs(alpha*u)**n + 1)**(1.0/n - 2))/(((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1) + 1)*(1 - 1.0/((abs(alpha*u)**n + 1)**(1.0/n - 1))**(1.0/(1.0/n - 1)))**(1.0/n))
|
||||
g[u >= 0] = 0
|
||||
g = Utils.sdiag(g)
|
||||
return g
|
||||
|
||||
class VanGenuchten(RichardsMap):
|
||||
"""vanGenuchten Model"""
|
||||
|
||||
theta_r = _ModelProperty('theta_r', ['thetaModel'], default=0.075)
|
||||
theta_s = _ModelProperty('theta_s', ['thetaModel'], default=0.287)
|
||||
|
||||
alpha = _ModelProperty('alpha', ['thetaModel', 'kModel'], default=0.036)
|
||||
n = _ModelProperty('n', ['thetaModel', 'kModel'], default=1.560)
|
||||
|
||||
Ks = _ModelProperty('Ks', ['kModel'], default=np.log(24.96))
|
||||
I = _ModelProperty('I', ['kModel'], default=0.500)
|
||||
|
||||
def __init__(self, mesh, **kwargs):
|
||||
RichardsMap.__init__(self, mesh,
|
||||
_vangenuchten_theta(mesh),
|
||||
_vangenuchten_k(mesh))
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
|
||||
class VanGenuchtenParams(object):
|
||||
"""
|
||||
The RETC code for quantifying the hydraulic functions of unsaturated soils,
|
||||
Van Genuchten, M Th, Leij, F J, Yates, S R
|
||||
|
||||
Table 3: Average values for selected soil water retention and hydraulic
|
||||
conductivity parameters for 11 major soil textural groups
|
||||
according to Rawls et al. [1982]
|
||||
|
||||
"""
|
||||
def __init__(self): pass
|
||||
@property
|
||||
def sand(self):
|
||||
return {"theta_r": 0.020, "theta_s": 0.417, "alpha": 0.138*100., "n": 1.592, "Ks": 504.0/100./24./60./60.}
|
||||
@property
|
||||
def loamySand(self):
|
||||
return {"theta_r": 0.035, "theta_s": 0.401, "alpha": 0.115*100., "n": 1.474, "Ks": 146.6/100./24./60./60.}
|
||||
@property
|
||||
def sandyLoam(self):
|
||||
return {"theta_r": 0.041, "theta_s": 0.412, "alpha": 0.068*100., "n": 1.322, "Ks": 62.16/100./24./60./60.}
|
||||
@property
|
||||
def loam(self):
|
||||
return {"theta_r": 0.027, "theta_s": 0.434, "alpha": 0.090*100., "n": 1.220, "Ks": 16.32/100./24./60./60.}
|
||||
@property
|
||||
def siltLoam(self):
|
||||
return {"theta_r": 0.015, "theta_s": 0.486, "alpha": 0.048*100., "n": 1.211, "Ks": 31.68/100./24./60./60.}
|
||||
@property
|
||||
def sandyClayLoam(self):
|
||||
return {"theta_r": 0.068, "theta_s": 0.330, "alpha": 0.036*100., "n": 1.250, "Ks": 10.32/100./24./60./60.}
|
||||
@property
|
||||
def clayLoam(self):
|
||||
return {"theta_r": 0.075, "theta_s": 0.390, "alpha": 0.039*100., "n": 1.194, "Ks": 5.52/100./24./60./60.}
|
||||
@property
|
||||
def siltyClayLoam(self):
|
||||
return {"theta_r": 0.040, "theta_s": 0.432, "alpha": 0.031*100., "n": 1.151, "Ks": 3.60/100./24./60./60.}
|
||||
@property
|
||||
def sandyClay(self):
|
||||
return {"theta_r": 0.109, "theta_s": 0.321, "alpha": 0.034*100., "n": 1.168, "Ks": 2.88/100./24./60./60.}
|
||||
@property
|
||||
def siltyClay(self):
|
||||
return {"theta_r": 0.056, "theta_s": 0.423, "alpha": 0.029*100., "n": 1.127, "Ks": 2.16/100./24./60./60.}
|
||||
@property
|
||||
def clay(self):
|
||||
return {"theta_r": 0.090, "theta_s": 0.385, "alpha": 0.027*100., "n": 1.131, "Ks": 1.44/100./24./60./60.}
|
||||
|
||||
|
||||
# From: INDIRECT METHODS FOR ESTIMATING THE HYDRAULIC PROPERTIES OF UNSATURATED SOILS
|
||||
# @property
|
||||
# def siltLoamGE3(self):
|
||||
# """Soil Index: 3310"""
|
||||
# return {"theta_r": 0.139, "theta_s": 0.394, "alpha": 0.00414, "n": 2.15}
|
||||
# @property
|
||||
# def yoloLightClayK_WC(self):
|
||||
# """Soil Index: None"""
|
||||
# return {"theta_r": 0.205, "theta_s": 0.499, "alpha": 0.02793, "n": 1.71}
|
||||
# @property
|
||||
# def yoloLightClayK_H(self):
|
||||
# """Soil Index: None"""
|
||||
# return {"theta_r": 0.205, "theta_s": 0.499, "alpha": 0.02793, "n": 1.71}
|
||||
# @property
|
||||
# def hygieneSandstone(self):
|
||||
# """Soil Index: 4130"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.256, "alpha": 0.00562, "n": 3.27}
|
||||
# @property
|
||||
# def lambcrgClay(self):
|
||||
# """Soil Index: 1003"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.502, "alpha": 0.140, "n": 1.93}
|
||||
# @property
|
||||
# def beitNetofaClaySoil(self):
|
||||
# """Soil Index: 1006"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.447, "alpha": 0.00156, "n": 1.17}
|
||||
# @property
|
||||
# def shiohotSiltyClay(self):
|
||||
# """Soil Index: 1101"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.456, "alpha": 183, "n":1.17}
|
||||
# @property
|
||||
# def siltColumbia(self):
|
||||
# """Soil Index: 2001"""
|
||||
# return {"theta_r": 0.146, "theta_s": 0.397, "alpha": 0.0145, "n": 1.85}
|
||||
# @property
|
||||
# def siltMontCenis(self):
|
||||
# """Soil Index: 2002"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.425, "alpha": 0.0103, "n": 1.34}
|
||||
# @property
|
||||
# def slateDust(self):
|
||||
# """Soil Index: 2004"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.498, "alpha": 0.00981, "n": 6.75}
|
||||
# @property
|
||||
# def weldSiltyClayLoam(self):
|
||||
# """Soil Index: 3001"""
|
||||
# return {"theta_r": 0.159, "theta_s": 0.496, "alpha": 0.0136, "n": 5.45}
|
||||
# @property
|
||||
# def rideauClayLoam_Wetting(self):
|
||||
# """Soil Index: 3101a"""
|
||||
# return {"theta_r": 0.279, "theta_s": 0.419, "alpha": 0.0661, "n": 1.89}
|
||||
# @property
|
||||
# def rideauClayLoam_Drying(self):
|
||||
# """Soil Index: 3101b"""
|
||||
# return {"theta_r": 0.290, "theta_s": 0.419, "alpha": 0.0177, "n": 3.18}
|
||||
# @property
|
||||
# def caribouSiltLoam_Drying(self):
|
||||
# """Soil Index: 3301a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.451, "alpha": 0.00845, "n": 1.29}
|
||||
# @property
|
||||
# def caribouSiltLoam_Wetting(self):
|
||||
# """Soil Index: 3301b"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.450, "alpha": 0.140, "n": 1.09}
|
||||
# @property
|
||||
# def grenvilleSiltLoam_Wetting(self):
|
||||
# """Soil Index: 3302a"""
|
||||
# return {"theta_r": 0.013, "theta_s": 0523, "alpha": 0.0630, "n": 1.24}
|
||||
# @property
|
||||
# def grenvilleSiltLoam_Drying(self):
|
||||
# """Soil Index: 3302c"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.488, "alpha": 0.0112, "n": 1.23}
|
||||
# @property
|
||||
# def touchetSiltLoam(self):
|
||||
# """Soil Index: 3304"""
|
||||
# return {"theta_r": 0.183, "theta_s": 0.498, "alpha": 0.0104, "n": 5.78}
|
||||
# @property
|
||||
# def gilatLoam(self):
|
||||
# """Soil Index: 3402a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.454, "alpha": 0.0291, "n": 1.47}
|
||||
# @property
|
||||
# def pachapaLoam(self):
|
||||
# """Soil Index: 3403"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.472, "alpha": 0.00829, "n": 1.62}
|
||||
# @property
|
||||
# def adelantoLoam(self):
|
||||
# """Soil Index: 3404"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.444, "alpha": 0.00710, "n": 1.26}
|
||||
# @property
|
||||
# def indioLoam(self):
|
||||
# """Soil Index: 3405a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.507, "alpha": 0.00847, "n": 1.60}
|
||||
# @property
|
||||
# def guclphLoam(self):
|
||||
# """Soil Index: 3407a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.563, "alpha": 0.0275, "n": 1.27}
|
||||
# @property
|
||||
# def guclphLoam(self):
|
||||
# """Soil Index: 3407b"""
|
||||
# return {"theta_r": 0.236, "theta_s": 0.435, "alpha": 0.0271, "n": 262}
|
||||
# @property
|
||||
# def rubiconSandyLoam(self):
|
||||
# """Soil Index: 3501a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.393, "alpha": 0.00972, "n": 2.18}
|
||||
# @property
|
||||
# def rubiconSandyLoam(self):
|
||||
# """Soil Index: 350lb"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.433, "alpha": 0.147, "n": 1.28}
|
||||
# @property
|
||||
# def pachapaFmeSandyClay(self):
|
||||
# """Soil Index: 3503a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.340, "alpha": 0.0194, "n": 1.45}
|
||||
# @property
|
||||
# def gilatSandyLoam(self):
|
||||
# """Soil Index: 3504"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.432, "alpha": 0.0103, "n": 1.48}
|
||||
# @property
|
||||
# def plainfieldSand_210to250(self):
|
||||
# """Soil Index: 4101a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.351, "alpha": 0.0236, "n": 12.30}
|
||||
# @property
|
||||
# def plainfieldSand_210to250(self):
|
||||
# """Soil Index: 4101b"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.312, "alpha": 0.0387, "n": 4.48}
|
||||
# @property
|
||||
# def plainfieldSand_177to210(self):
|
||||
# """Soil Index: 4102a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.361, "alpha": 0.0207, "n": 10.0}
|
||||
# @property
|
||||
# def plainfieldSand_177to210(self):
|
||||
# """Soil Index: 4102b"""
|
||||
# return {"theta_r": 0.022, "theta_s": 0.309, "alpha": 0.0328, "n": 6.23}
|
||||
# @property
|
||||
# def plainfieldSand_149to177(self):
|
||||
# """Soil Index: 4103a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.387, "alpha": 0.0173, "n": 7.80}
|
||||
# @property
|
||||
# def plainfieldSand_149to177(self):
|
||||
# """Soil Index: 4103b"""
|
||||
# return {"theta_r": 0.025, "theta_s": 0.321, "alpha": 0.0272, "n": 6.69}
|
||||
# @property
|
||||
# def plainfieldSand_l25to149(self):
|
||||
# """Soil Index: 4104a"""
|
||||
# return {"theta_r": 0.000, "theta_s": 03770, "alpha": 0.0145, "n": 10.60}
|
||||
# @property
|
||||
# def plainfieldSand_125to149(self):
|
||||
# """Soil Index: 4104b"""
|
||||
# return {"theta_r": 0.000, "theta_s": 0.342, "alpha": 0.0230, "n": 5.18}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import matplotlib.pyplot as plt
|
||||
M = Mesh.TensorMesh([10])
|
||||
VGparams = VanGenuchtenParams()
|
||||
leg = []
|
||||
for p in dir(VGparams):
|
||||
if p[0] == '_': continue
|
||||
leg += [p]
|
||||
params = getattr(VGparams, p)
|
||||
model = VanGenuchten(M, **params)
|
||||
ks = np.log(np.r_[params['Ks']])
|
||||
model.plot(ks)
|
||||
|
||||
plt.legend(leg)
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,304 @@
|
||||
from SimPEG import *
|
||||
from Empirical import RichardsMap
|
||||
import time
|
||||
|
||||
|
||||
class RichardsRx(Survey.BaseTimeRx):
|
||||
"""Richards Receiver Object"""
|
||||
|
||||
knownRxTypes = ['saturation','pressureHead']
|
||||
|
||||
def projectFields(self, U, m, mapping, mesh, timeMesh):
|
||||
|
||||
if self.rxType == 'pressureHead':
|
||||
u = np.concatenate(U)
|
||||
elif self.rxType == 'saturation':
|
||||
u = np.concatenate([mapping.theta(ui, m) for ui in U])
|
||||
|
||||
return self.getP(mesh, timeMesh) * u
|
||||
|
||||
def projectFieldsDeriv(self, U, m, mapping, mesh, timeMesh):
|
||||
|
||||
P = self.getP(mesh, timeMesh)
|
||||
if self.rxType == 'pressureHead':
|
||||
return P
|
||||
elif self.rxType == 'saturation':
|
||||
#TODO: if m is a parameter in the theta
|
||||
# distribution, we may need to do
|
||||
# some more chain rule here.
|
||||
dT = sp.block_diag([mapping.thetaDerivU(ui, m) for ui in U])
|
||||
return P*dT
|
||||
|
||||
|
||||
class RichardsSurvey(Survey.BaseSurvey):
|
||||
"""docstring for RichardsSurvey"""
|
||||
|
||||
rxList = None
|
||||
|
||||
def __init__(self, rxList, **kwargs):
|
||||
self.rxList = rxList
|
||||
Survey.BaseSurvey.__init__(self, **kwargs)
|
||||
|
||||
@property
|
||||
def nD(self):
|
||||
return np.array([rx.nD for rx in self.rxList]).sum()
|
||||
|
||||
@Utils.count
|
||||
@Utils.requires('prob')
|
||||
def dpred(self, m, u=None):
|
||||
"""
|
||||
Create the projected data from a model.
|
||||
The field, u, (if provided) will be used for the predicted data
|
||||
instead of recalculating the fields (which may be expensive!).
|
||||
|
||||
.. math::
|
||||
d_\\text{pred} = P(u(m), m)
|
||||
|
||||
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))
|
||||
|
||||
@Utils.requires('prob')
|
||||
def projectFields(self, U, m):
|
||||
Ds = range(len(self.rxList))
|
||||
for ii, rx in enumerate(self.rxList):
|
||||
Ds[ii] = rx.projectFields(U, m,
|
||||
self.prob.mapping,
|
||||
self.prob.mesh,
|
||||
self.prob.timeMesh)
|
||||
|
||||
return np.concatenate(Ds)
|
||||
|
||||
@Utils.requires('prob')
|
||||
def projectFieldsDeriv(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,
|
||||
self.prob.mapping,
|
||||
self.prob.mesh,
|
||||
self.prob.timeMesh)
|
||||
|
||||
return sp.vstack(Ds)
|
||||
|
||||
class RichardsProblem(Problem.BaseTimeProblem):
|
||||
"""docstring for RichardsProblem"""
|
||||
|
||||
boundaryConditions = None
|
||||
initialConditions = None
|
||||
|
||||
surveyPair = RichardsSurvey
|
||||
mapPair = RichardsMap
|
||||
|
||||
debug=True
|
||||
|
||||
Solver = Solver
|
||||
solverOpts = {}
|
||||
|
||||
def __init__(self, mesh, mapping=None, **kwargs):
|
||||
Problem.BaseTimeProblem.__init__(self, mesh, mapping=mapping, **kwargs)
|
||||
|
||||
def getBoundaryConditions(self, ii, u_ii):
|
||||
if type(self.boundaryConditions) is np.ndarray:
|
||||
return self.boundaryConditions
|
||||
|
||||
time = self.timeMesh.vectorCCx[ii]
|
||||
|
||||
return self.boundaryConditions(time, u_ii)
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
"""Method must be either 'mixed' or 'head'. See notes in Celia et al., 1990."""
|
||||
return getattr(self, '_method', 'mixed')
|
||||
@method.setter
|
||||
def method(self, value):
|
||||
assert value in ['mixed','head'], "method must be 'mixed' or 'head'."
|
||||
self._method = value
|
||||
|
||||
# Setting doNewton will clear the rootFinder, which will be reinitialized when called
|
||||
doNewton = Utils.dependentProperty('_doNewton', False, ['_rootFinder'],
|
||||
"Do a Newton iteration. If False, a Picard iteration will be completed.")
|
||||
|
||||
maxIterRootFinder = Utils.dependentProperty('_maxIterRootFinder', 30, ['_rootFinder'],
|
||||
"Maximum iterations for rootFinder iteration.")
|
||||
tolRootFinder = Utils.dependentProperty('_tolRootFinder', 1e-4, ['_rootFinder'],
|
||||
"Maximum iterations for rootFinder iteration.")
|
||||
|
||||
@property
|
||||
def rootFinder(self):
|
||||
"""Root-finding Algorithm"""
|
||||
if getattr(self, '_rootFinder', None) is None:
|
||||
self._rootFinder = Optimization.NewtonRoot(doLS=self.doNewton, maxIter=self.maxIterRootFinder, tol=self.tolRootFinder, Solver=self.Solver)
|
||||
return self._rootFinder
|
||||
|
||||
@Utils.timeIt
|
||||
def fields(self, m):
|
||||
tic = time.time()
|
||||
u = range(self.nT+1)
|
||||
u[0] = self.initialConditions
|
||||
for ii, dt in enumerate(self.timeSteps):
|
||||
bc = self.getBoundaryConditions(ii, u[ii])
|
||||
u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii])
|
||||
if self.debug: print "Solving Fields (%4d/%d - %3.1f%% Done) %d Iterations, %4.2f seconds"%(ii+1, self.nT, 100.0*(ii+1)/self.nT, self.rootFinder.iter, time.time() - tic)
|
||||
return u
|
||||
|
||||
@Utils.timeIt
|
||||
def diagsJacobian(self, m, hn, hn1, dt, bc):
|
||||
|
||||
DIV = self.mesh.faceDiv
|
||||
GRAD = self.mesh.cellGrad
|
||||
BC = self.mesh.cellGradBC
|
||||
AV = self.mesh.aveF2CC.T
|
||||
if self.mesh.dim == 1:
|
||||
Dz = self.mesh.faceDivx
|
||||
elif self.mesh.dim == 2:
|
||||
Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]), self.mesh.faceDivy),format='csr')
|
||||
elif self.mesh.dim == 3:
|
||||
Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr')
|
||||
|
||||
dT = self.mapping.thetaDerivU(hn, m)
|
||||
dT1 = self.mapping.thetaDerivU(hn1, m)
|
||||
K1 = self.mapping.k(hn1, m)
|
||||
dK1 = self.mapping.kDerivU(hn1, m)
|
||||
dKm1 = self.mapping.kDerivM(hn1, m)
|
||||
|
||||
# Compute part of the derivative of:
|
||||
#
|
||||
# DIV*diag(GRAD*hn1+BC*bc)*(AV*(1.0/K))^-1
|
||||
|
||||
DdiagGh1 = DIV*Utils.sdiag(GRAD*hn1+BC*bc)
|
||||
diagAVk2_AVdiagK2 = Utils.sdiag((AV*(1./K1))**(-2)) * AV*Utils.sdiag(K1**(-2))
|
||||
|
||||
# The matrix that we are computing has the form:
|
||||
#
|
||||
# - - - - - -
|
||||
# | Adiag | | h1 | | b1 |
|
||||
# | Asub Adiag | | h2 | | b2 |
|
||||
# | Asub Adiag | | h3 | = | b3 |
|
||||
# | ... ... | | .. | | .. |
|
||||
# | Asub Adiag | | hn | | bn |
|
||||
# - - - - - -
|
||||
|
||||
Asub = (-1.0/dt)*dT
|
||||
|
||||
Adiag = (
|
||||
(1.0/dt)*dT1
|
||||
-DdiagGh1*diagAVk2_AVdiagK2*dK1
|
||||
-DIV*Utils.sdiag(1./(AV*(1./K1)))*GRAD
|
||||
-Dz*diagAVk2_AVdiagK2*dK1
|
||||
)
|
||||
|
||||
B = DdiagGh1*diagAVk2_AVdiagK2*dKm1 + Dz*diagAVk2_AVdiagK2*dKm1
|
||||
|
||||
return Asub, Adiag, B
|
||||
|
||||
@Utils.timeIt
|
||||
def getResidual(self, m, hn, h, dt, bc, return_g=True):
|
||||
"""
|
||||
Where h is the proposed value for the next time iterate (h_{n+1})
|
||||
"""
|
||||
DIV = self.mesh.faceDiv
|
||||
GRAD = self.mesh.cellGrad
|
||||
BC = self.mesh.cellGradBC
|
||||
AV = self.mesh.aveF2CC.T
|
||||
if self.mesh.dim == 1:
|
||||
Dz = self.mesh.faceDivx
|
||||
elif self.mesh.dim == 2:
|
||||
Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]), self.mesh.faceDivy),format='csr')
|
||||
elif self.mesh.dim == 3:
|
||||
Dz = sp.hstack((Utils.spzeros(self.mesh.nC,self.mesh.vnF[0]+self.mesh.vnF[1]), self.mesh.faceDivz),format='csr')
|
||||
|
||||
T = self.mapping.theta(h, m)
|
||||
dT = self.mapping.thetaDerivU(h, m)
|
||||
Tn = self.mapping.theta(hn, m)
|
||||
K = self.mapping.k(h, m)
|
||||
dK = self.mapping.kDerivU(h, m)
|
||||
|
||||
aveK = 1./(AV*(1./K))
|
||||
|
||||
RHS = DIV*Utils.sdiag(aveK)*(GRAD*h+BC*bc) + Dz*aveK
|
||||
if self.method == 'mixed':
|
||||
r = (T-Tn)/dt - RHS
|
||||
elif self.method == 'head':
|
||||
r = dT*(h - hn)/dt - RHS
|
||||
|
||||
if not return_g: return r
|
||||
|
||||
J = dT/dt - DIV*Utils.sdiag(aveK)*GRAD
|
||||
if self.doNewton:
|
||||
DDharmAve = Utils.sdiag(aveK**2)*AV*Utils.sdiag(K**(-2)) * dK
|
||||
J = J - DIV*Utils.sdiag(GRAD*h + BC*bc)*DDharmAve - Dz*DDharmAve
|
||||
|
||||
return r, J
|
||||
|
||||
@Utils.timeIt
|
||||
def Jfull(self, m, u=None):
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
nn = len(u)-1
|
||||
Asubs, Adiags, Bs = range(nn), range(nn), range(nn)
|
||||
for ii in range(nn):
|
||||
dt = self.timeSteps[ii]
|
||||
bc = self.getBoundaryConditions(ii, u[ii])
|
||||
Asubs[ii], Adiags[ii], Bs[ii] = self.diagsJacobian(m, u[ii], u[ii+1], dt, bc)
|
||||
Ad = sp.block_diag(Adiags)
|
||||
zRight = Utils.spzeros((len(Asubs)-1)*Asubs[0].shape[0],Adiags[0].shape[1])
|
||||
zTop = Utils.spzeros(Adiags[0].shape[0], len(Adiags)*Adiags[0].shape[1])
|
||||
As = sp.vstack((zTop,sp.hstack((sp.block_diag(Asubs[1:]),zRight))))
|
||||
A = As + Ad
|
||||
B = np.array(sp.vstack(Bs).todense())
|
||||
|
||||
Ainv = self.Solver(A, **self.solverOpts)
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
AinvB = Ainv * B
|
||||
z = np.zeros((self.mesh.nC, B.shape[1]))
|
||||
zAinvB = np.vstack((z, AinvB))
|
||||
J = P * zAinvB
|
||||
return J
|
||||
|
||||
@Utils.timeIt
|
||||
def Jvec(self, m, v, u=None):
|
||||
if u is None:
|
||||
u = self.fields(m)
|
||||
|
||||
JvC = range(len(u)-1) # Cell to hold each row of the long vector.
|
||||
|
||||
# This is done via forward substitution.
|
||||
bc = self.getBoundaryConditions(0, u[0])
|
||||
temp, Adiag, B = self.diagsJacobian(m, u[0], u[1], self.timeSteps[0], bc)
|
||||
Adiaginv = self.Solver(Adiag, **self.solverOpts)
|
||||
JvC[0] = Adiaginv * (B*v)
|
||||
|
||||
for ii in range(1,len(u)-1):
|
||||
bc = self.getBoundaryConditions(ii, u[ii])
|
||||
Asub, Adiag, B = self.diagsJacobian(m, u[ii], u[ii+1], self.timeSteps[ii], bc)
|
||||
Adiaginv = self.Solver(Adiag, **self.solverOpts)
|
||||
JvC[ii] = Adiaginv * (B*v - Asub*JvC[ii-1])
|
||||
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
return P * np.concatenate([np.zeros(self.mesh.nC)] + JvC)
|
||||
|
||||
@Utils.timeIt
|
||||
def Jtvec(self, m, v, u=None):
|
||||
if u is None:
|
||||
u = self.field(m)
|
||||
|
||||
P = self.survey.projectFieldsDeriv(u, m)
|
||||
PTv = P.T*v
|
||||
|
||||
# This is done via backward substitution.
|
||||
minus = 0
|
||||
BJtv = 0
|
||||
for ii in range(len(u)-1,0,-1):
|
||||
bc = self.getBoundaryConditions(ii-1, u[ii-1])
|
||||
Asub, Adiag, B = self.diagsJacobian(m, u[ii-1], u[ii], self.timeSteps[ii-1], bc)
|
||||
#select the correct part of v
|
||||
vpart = range((ii)*Adiag.shape[0], (ii+1)*Adiag.shape[0])
|
||||
AdiaginvT = self.Solver(Adiag.T, **self.solverOpts)
|
||||
JTvC = AdiaginvT * (PTv[vpart] - minus)
|
||||
minus = Asub.T*JTvC # this is now the super diagonal.
|
||||
BJtv = BJtv + B.T*JTvC
|
||||
|
||||
return BJtv
|
||||
@@ -0,0 +1,2 @@
|
||||
import Empirical
|
||||
from RichardsProblem import *
|
||||
@@ -0,0 +1 @@
|
||||
import Richards
|
||||
@@ -0,0 +1,261 @@
|
||||
import Utils, numpy as np, scipy.sparse as sp
|
||||
|
||||
class Fields(object):
|
||||
"""Fancy Field Storage
|
||||
|
||||
u[:,'phi'] = phi
|
||||
print u[src0,'phi']
|
||||
|
||||
"""
|
||||
|
||||
knownFields = None #: Known fields, a dict with locations, e.g. {"e": "E", "phi": "CC"}
|
||||
aliasFields = None #: Aliased fields, a dict with [alias, location, function], e.g. {"b":["e","F",lambda(F,e,ind)]}
|
||||
dtype = float #: dtype is the type of the storage matrix. This can be a dictionary.
|
||||
|
||||
def __init__(self, mesh, survey, **kwargs):
|
||||
self.survey = survey
|
||||
self.mesh = mesh
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
self._fields = {}
|
||||
|
||||
if self.knownFields is None:
|
||||
raise Exception('knownFields cannot be set to None')
|
||||
if self.aliasFields is None:
|
||||
self.aliasFields = {}
|
||||
|
||||
allFields = [k for k in self.knownFields] + [a for a in self.aliasFields]
|
||||
assert len(allFields) == len(set(allFields)), 'Aliased fields and Known Fields have overlapping definitions.'
|
||||
self.startup()
|
||||
|
||||
def startup(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def approxSize(self):
|
||||
"""The approximate cost to storing all of the known fields."""
|
||||
sz = 0.0
|
||||
for f in self.knownFields:
|
||||
loc =self.knownFields[f]
|
||||
sz += np.array(self._storageShape(loc)).prod()*8.0/(1024**2)
|
||||
return "%e MB"%sz
|
||||
|
||||
def _storageShape(self, loc):
|
||||
nSrc = self.survey.nSrc
|
||||
|
||||
nP = {'CC': self.mesh.nC,
|
||||
'N': self.mesh.nN,
|
||||
'F': self.mesh.nF,
|
||||
'E': self.mesh.nE}[loc]
|
||||
|
||||
return (nP, nSrc)
|
||||
|
||||
def _initStore(self, name):
|
||||
if name in self._fields:
|
||||
return self._fields[name]
|
||||
|
||||
assert name in self.knownFields, 'field name is not known.'
|
||||
|
||||
loc = self.knownFields[name]
|
||||
|
||||
if type(self.dtype) is dict:
|
||||
dtype = self.dtype[name]
|
||||
else:
|
||||
dtype = self.dtype
|
||||
field = np.zeros(self._storageShape(loc), dtype=dtype)
|
||||
|
||||
self._fields[name] = field
|
||||
|
||||
return field
|
||||
|
||||
def _srcIndex(self, srcTestList):
|
||||
if type(srcTestList) is slice:
|
||||
ind = srcTestList
|
||||
else:
|
||||
ind = self.survey.getSourceIndex(srcTestList)
|
||||
return ind
|
||||
|
||||
def _nameIndex(self, name, accessType):
|
||||
|
||||
if type(name) is slice:
|
||||
assert name == slice(None,None,None), 'Fancy field name slicing is not supported... yet.'
|
||||
name = None
|
||||
|
||||
if name is None:
|
||||
return
|
||||
if accessType=='set' and name not in self.knownFields:
|
||||
if name in self.aliasFields:
|
||||
raise KeyError("Invalid field name (%s) for setter, you can't set an aliased property"%name)
|
||||
else:
|
||||
raise KeyError('Invalid field name (%s) for setter'%name)
|
||||
|
||||
elif accessType=='get' and (name not in self.knownFields and name not in self.aliasFields):
|
||||
raise KeyError('Invalid field name (%s) for getter'%name)
|
||||
return name
|
||||
|
||||
def _indexAndNameFromKey(self, key, accessType):
|
||||
if type(key) is not tuple:
|
||||
key = (key,)
|
||||
if len(key) == 1:
|
||||
key += (None,)
|
||||
|
||||
assert len(key) == 2, 'must be [Src, fieldName]'
|
||||
|
||||
srcTestList, name = key
|
||||
name = self._nameIndex(name, accessType)
|
||||
ind = self._srcIndex(srcTestList)
|
||||
return ind, name
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
ind, name = self._indexAndNameFromKey(key, 'set')
|
||||
if name is None:
|
||||
freq = key
|
||||
assert type(value) is dict, 'New fields must be a dictionary, if field is not specified.'
|
||||
newFields = value
|
||||
elif name in self.knownFields:
|
||||
newFields = {name: value}
|
||||
else:
|
||||
raise Exception('Unknown setter')
|
||||
|
||||
for name in newFields:
|
||||
field = self._initStore(name)
|
||||
self._setField(field, newFields[name], name, ind)
|
||||
|
||||
def __getitem__(self, key):
|
||||
ind, name = self._indexAndNameFromKey(key, 'get')
|
||||
if name is None:
|
||||
out = {}
|
||||
for name in self._fields:
|
||||
out[name] = self._getField(name, ind)
|
||||
return out
|
||||
return self._getField(name, ind)
|
||||
|
||||
def _setField(self, field, val, name, ind):
|
||||
if isinstance(val, np.ndarray) and (field.shape[0] == field.size or val.ndim == 1):
|
||||
val = Utils.mkvc(val,2)
|
||||
field[:,ind] = val
|
||||
|
||||
def _getField(self, name, ind):
|
||||
if name in self._fields:
|
||||
out = self._fields[name][:,ind]
|
||||
else:
|
||||
# Aliased fields
|
||||
alias, loc, func = self.aliasFields[name]
|
||||
|
||||
srcII = np.array(self.survey.srcList)[ind]
|
||||
srcII = srcII.tolist()
|
||||
|
||||
if type(func) is str:
|
||||
assert hasattr(self, func), 'The alias field function is a string, but it does not exist in the Fields class.'
|
||||
func = getattr(self, func)
|
||||
out = func(self._fields[alias][:,ind], srcII)
|
||||
if out.shape[0] == out.size or out.ndim == 1:
|
||||
out = Utils.mkvc(out,2)
|
||||
return out
|
||||
|
||||
def __contains__(self, other):
|
||||
if other in self.aliasFields:
|
||||
other = self.aliasFields[other][0]
|
||||
return self._fields.__contains__(other)
|
||||
|
||||
|
||||
class TimeFields(Fields):
|
||||
"""Fancy Field Storage for time domain problems
|
||||
|
||||
u[:,'phi', timeInd] = phi
|
||||
print u[src0,'phi']
|
||||
|
||||
"""
|
||||
|
||||
def _storageShape(self, loc):
|
||||
nP = {'CC': self.mesh.nC,
|
||||
'N': self.mesh.nN,
|
||||
'F': self.mesh.nF,
|
||||
'E': self.mesh.nE}[loc]
|
||||
nSrc = self.survey.nSrc
|
||||
nT = self.survey.prob.nT + 1
|
||||
return (nP, nSrc, nT)
|
||||
|
||||
def _indexAndNameFromKey(self, key, accessType):
|
||||
if type(key) is not tuple:
|
||||
key = (key,)
|
||||
if len(key) == 1:
|
||||
key += (None,)
|
||||
if len(key) == 2:
|
||||
key += (slice(None,None,None),)
|
||||
|
||||
assert len(key) == 3, 'must be [Src, fieldName, times]'
|
||||
|
||||
srcTestList, name, timeInd = key
|
||||
|
||||
name = self._nameIndex(name, accessType)
|
||||
srcInd = self._srcIndex(srcTestList)
|
||||
|
||||
return (srcInd, timeInd), name
|
||||
|
||||
def _correctShape(self, name, ind, deflate=False):
|
||||
srcInd, timeInd = ind
|
||||
if name in self.knownFields:
|
||||
loc = self.knownFields[name]
|
||||
else:
|
||||
loc = self.aliasFields[name][1]
|
||||
nP, total_nSrc, total_nT = self._storageShape(loc)
|
||||
nSrc = np.ones(total_nSrc, dtype=bool)[srcInd].sum()
|
||||
nT = np.ones(total_nT, dtype=bool)[timeInd].sum()
|
||||
shape = nP, nSrc, nT
|
||||
if deflate:
|
||||
shape = tuple([s for s in shape if s > 1])
|
||||
if len(shape) == 1:
|
||||
shape = shape + (1,)
|
||||
return shape
|
||||
|
||||
def _setField(self, field, val, name, ind):
|
||||
srcInd, timeInd = ind
|
||||
shape = self._correctShape(name, ind)
|
||||
if Utils.isScalar(val):
|
||||
field[:,srcInd,timeInd] = val
|
||||
return
|
||||
if val.size != np.array(shape).prod():
|
||||
raise ValueError('Incorrect size for data.')
|
||||
correctShape = field[:,srcInd,timeInd].shape
|
||||
field[:,srcInd,timeInd] = val.reshape(correctShape, order='F')
|
||||
|
||||
def _getField(self, name, ind):
|
||||
srcInd, timeInd = ind
|
||||
|
||||
if name in self._fields:
|
||||
out = self._fields[name][:,srcInd,timeInd]
|
||||
else:
|
||||
# Aliased fields
|
||||
alias, loc, func = self.aliasFields[name]
|
||||
if type(func) is str:
|
||||
assert hasattr(self, func), 'The alias field function is a string, but it does not exist in the Fields class.'
|
||||
func = getattr(self, func)
|
||||
pointerFields = self._fields[alias][:,srcInd,timeInd]
|
||||
pointerShape = self._correctShape(alias, ind)
|
||||
pointerFields = pointerFields.reshape(pointerShape, order='F')
|
||||
|
||||
timeII = np.arange(self.survey.prob.nT + 1)[timeInd]
|
||||
srcII = np.array(self.survey.srcList)[srcInd]
|
||||
srcII = srcII.tolist()
|
||||
|
||||
if timeII.size == 1:
|
||||
pointerShapeDeflated = self._correctShape(alias, ind, deflate=True)
|
||||
pointerFields = pointerFields.reshape(pointerShapeDeflated, order='F')
|
||||
out = func(pointerFields, srcII, timeII)
|
||||
else: #loop over the time steps
|
||||
nT = pointerShape[2]
|
||||
out = range(nT)
|
||||
for i, TIND_i in enumerate(timeII):
|
||||
fieldI = pointerFields[:,:,i]
|
||||
if fieldI.shape[0] == fieldI.size:
|
||||
fieldI = Utils.mkvc(fieldI, 2)
|
||||
out[i] = func(fieldI, srcII, TIND_i)
|
||||
if out[i].ndim == 1:
|
||||
out[i] = out[i][:,np.newaxis,np.newaxis]
|
||||
elif out[i].ndim == 2:
|
||||
out[i] = out[i][:,:,np.newaxis]
|
||||
out = np.concatenate(out, axis=2)
|
||||
|
||||
shape = self._correctShape(name, ind, deflate=True)
|
||||
return out.reshape(shape, order='F')
|
||||
|
||||
@@ -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):
|
||||
|
||||
+388
-20
@@ -1,39 +1,55 @@
|
||||
import Utils, numpy as np, scipy.sparse as sp
|
||||
from scipy.sparse.linalg import LinearOperator
|
||||
from Tests import checkDerivative
|
||||
|
||||
from PropMaps import PropMap, Property
|
||||
from numpy.polynomial import polynomial
|
||||
from scipy.interpolate import UnivariateSpline
|
||||
|
||||
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
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
def _transform(self, m):
|
||||
"""
|
||||
Changes the model into the physical property.
|
||||
@@ -98,17 +114,18 @@ class IdentityMap(object):
|
||||
|
||||
def __mul__(self, val):
|
||||
if isinstance(val, IdentityMap):
|
||||
if not self.shape[1] == val.shape[0]:
|
||||
if not (self.shape[1] == '*' or val.shape[0] == '*') and not self.shape[1] == val.shape[0]:
|
||||
raise ValueError('Dimension mismatch in %s and %s.' % (str(self), str(val)))
|
||||
return ComboMap([self, val])
|
||||
elif isinstance(val, np.ndarray):
|
||||
if not self.shape[1] == val.shape[0]:
|
||||
if not self.shape[1] == '*' and not self.shape[1] == val.shape[0]:
|
||||
raise ValueError('Dimension mismatch in %s and np.ndarray%s.' % (str(self), str(val.shape)))
|
||||
return self._transform(val)
|
||||
raise Exception('Unrecognized data type to multiply. Try a map or a numpy.ndarray!')
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%d,%d)" % (self.__class__.__name__, self.shape[0], self.shape[1])
|
||||
return "%s(%s,%s)" % (self.__class__.__name__, self.shape[0], self.shape[1])
|
||||
|
||||
|
||||
class ComboMap(IdentityMap):
|
||||
"""Combination of various maps."""
|
||||
@@ -119,10 +136,10 @@ class ComboMap(IdentityMap):
|
||||
self.maps = []
|
||||
for ii, m in enumerate(maps):
|
||||
assert isinstance(m, IdentityMap), 'Unrecognized data type, inherit from an IdentityMap or ComboMap!'
|
||||
if ii > 0 and not self.shape[1] == m.shape[0]:
|
||||
if ii > 0 and not (self.shape[1] == '*' or m.shape[0] == '*') and not self.shape[1] == m.shape[0]:
|
||||
prev = self.maps[-1]
|
||||
errArgs = (prev.__name__, prev.shape[0], prev.shape[1], m.__name__, m.shape[0], m.shape[1])
|
||||
raise ValueError('Dimension mismatch in map[%s] (%i, %i) and map[%s] (%i, %i).' % errArgs)
|
||||
errArgs = (prev.__class__.__name__, prev.shape[0], prev.shape[1], m.__class__.__name__, m.shape[0], m.shape[1])
|
||||
raise ValueError('Dimension mismatch in map[%s] (%s, %s) and map[%s] (%s, %s).' % errArgs)
|
||||
|
||||
if isinstance(m, ComboMap):
|
||||
self.maps += m.maps
|
||||
@@ -155,7 +172,7 @@ class ComboMap(IdentityMap):
|
||||
return deriv
|
||||
|
||||
def __str__(self):
|
||||
return 'ComboMap[%s]%s' % (' * '.join([m.__str__() for m in self.maps]), str(self.shape))
|
||||
return 'ComboMap[%s](%s,%s)' % (' * '.join([m.__str__() for m in self.maps]), self.shape[0], self.shape[1])
|
||||
|
||||
|
||||
class ExpMap(IdentityMap):
|
||||
@@ -220,6 +237,26 @@ class ExpMap(IdentityMap):
|
||||
"""
|
||||
return Utils.sdiag(np.exp(Utils.mkvc(m)))
|
||||
|
||||
class ReciprocalMap(IdentityMap):
|
||||
"""
|
||||
Reciprocal mapping. For example, electrical resistivity and conductivity.
|
||||
|
||||
.. math::
|
||||
|
||||
\\rho = \\frac{1}{\sigma}
|
||||
|
||||
"""
|
||||
def _transform(self, m):
|
||||
return 1.0 / Utils.mkvc(m)
|
||||
|
||||
def inverse(self, D):
|
||||
return 1.0 / Utils.mkvc(m)
|
||||
|
||||
def deriv(self, m):
|
||||
# TODO: if this is a tensor, you might have a problem.
|
||||
return Utils.sdiag( - Utils.mkvc(m)**(-2) )
|
||||
|
||||
|
||||
|
||||
class LogMap(IdentityMap):
|
||||
"""
|
||||
@@ -259,6 +296,37 @@ class LogMap(IdentityMap):
|
||||
def inverse(self, m):
|
||||
return np.exp(Utils.mkvc(m))
|
||||
|
||||
class FullMap(IdentityMap):
|
||||
"""
|
||||
FullMap
|
||||
|
||||
Given a scalar, the FullMap maps the value to the
|
||||
full model space.
|
||||
"""
|
||||
|
||||
def __init__(self,mesh,**kwargs):
|
||||
IdentityMap.__init__(self, mesh,**kwargs)
|
||||
|
||||
@property
|
||||
def nP(self):
|
||||
return 1
|
||||
|
||||
def _transform(self, m):
|
||||
"""
|
||||
:param m: model (scalar)
|
||||
:rtype: numpy.array
|
||||
:return: transformed model
|
||||
"""
|
||||
return np.ones(self.mesh.nC)*m
|
||||
|
||||
def deriv(self, m):
|
||||
"""
|
||||
:param numpy.array m: model
|
||||
:rtype: numpy.array
|
||||
:return: derivative of transformed model
|
||||
"""
|
||||
return np.ones([self.mesh.nC,1])
|
||||
|
||||
|
||||
class Vertical1DMap(IdentityMap):
|
||||
"""Vertical1DMap
|
||||
@@ -412,10 +480,10 @@ class ActiveCells(IdentityMap):
|
||||
self.indActive = indActive
|
||||
self.indInactive = np.logical_not(indActive)
|
||||
if Utils.isScalar(valInactive):
|
||||
valInactive = np.ones(self.nC)*float(valInactive)
|
||||
|
||||
valInactive[self.indActive] = 0
|
||||
self.valInactive = valInactive
|
||||
self.valInactive = np.ones(self.nC)*float(valInactive)
|
||||
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))
|
||||
@@ -582,7 +650,7 @@ class ComplexMap(IdentityMap):
|
||||
return v[:nC] + v[nC:]*1j
|
||||
def adj(v):
|
||||
return np.r_[v.real,v.imag]
|
||||
return Utils.SimPEGLinearOperator(shp,fwd,adj)
|
||||
return LinearOperator(shp,matvec=fwd,rmatvec=adj)
|
||||
|
||||
inverse = deriv
|
||||
|
||||
@@ -639,4 +707,304 @@ class CircleMap(IdentityMap):
|
||||
g3 = a*(-X + x)*(-sig1 + sig2)/(np.pi*(a**2*(-r + np.sqrt((X - x)**2 + (Y - y)**2))**2 + 1)*np.sqrt((X - x)**2 + (Y - y)**2))
|
||||
g4 = a*(-Y + y)*(-sig1 + sig2)/(np.pi*(a**2*(-r + np.sqrt((X - x)**2 + (Y - y)**2))**2 + 1)*np.sqrt((X - x)**2 + (Y - y)**2))
|
||||
g5 = -a*(-sig1 + sig2)/(np.pi*(a**2*(-r + np.sqrt((X - x)**2 + (Y - y)**2))**2 + 1))
|
||||
return np.c_[g1,g2,g3,g4,g5]
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3,g4,g5])
|
||||
|
||||
|
||||
class PolyMap(IdentityMap):
|
||||
|
||||
"""PolyMap
|
||||
|
||||
Parameterize the model space using a polynomials in a wholespace.
|
||||
|
||||
..math::
|
||||
|
||||
y = \mathbf{V} c
|
||||
|
||||
Define the model as:
|
||||
|
||||
..math::
|
||||
|
||||
m = [\sigma_1, \sigma_2, c]
|
||||
|
||||
"""
|
||||
def __init__(self, mesh, order, logSigma=True, normal='X'):
|
||||
IdentityMap.__init__(self, mesh)
|
||||
self.logSigma = logSigma
|
||||
self.order = order
|
||||
self.normal = normal
|
||||
|
||||
slope = 1e4
|
||||
|
||||
@property
|
||||
def nP(self):
|
||||
if np.isscalar(self.order):
|
||||
nP = self.order+3
|
||||
else:
|
||||
nP =(self.order[0]+1)*(self.order[1]+1)+2
|
||||
return nP
|
||||
|
||||
def _transform(self, m):
|
||||
# Set model parameters
|
||||
alpha = self.slope
|
||||
sig1,sig2 = m[0],m[1]
|
||||
c = m[2:]
|
||||
if self.logSigma:
|
||||
sig1, sig2 = np.exp(sig1), np.exp(sig2)
|
||||
#2D
|
||||
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
|
||||
elif self.normal =='Y':
|
||||
f = polynomial.polyval(X, c) - Y
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
#3D
|
||||
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
|
||||
elif self.normal =='Y':
|
||||
f = polynomial.polyval2d(X, Z, c.reshape((self.order[0]+1,self.order[1]+1))) - Y
|
||||
elif self.normal =='Z':
|
||||
f = polynomial.polyval2d(X, Y, c.reshape((self.order[0]+1,self.order[1]+1))) - Z
|
||||
else:
|
||||
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:
|
||||
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)
|
||||
elif self.normal =='Y':
|
||||
f = polynomial.polyval(X, c) - Y
|
||||
V = polynomial.polyvander(X, len(c)-1)
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
#3D
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
raise(Exception("Input for normal = X or Y or Z"))
|
||||
|
||||
if self.logSigma:
|
||||
g1 = -(np.arctan(alpha*f)/np.pi + 0.5)*sig1 + sig1
|
||||
g2 = (np.arctan(alpha*f)/np.pi + 0.5)*sig2
|
||||
else:
|
||||
g1 = -(np.arctan(alpha*f)/np.pi + 0.5) + 1.0
|
||||
g2 = (np.arctan(alpha*f)/np.pi + 0.5)
|
||||
|
||||
g3 = Utils.sdiag(alpha*(sig2-sig1)/(1.+(alpha*f)**2)/np.pi)*V
|
||||
|
||||
return sp.csr_matrix(np.c_[g1,g2,g3])
|
||||
|
||||
class SplineMap(IdentityMap):
|
||||
|
||||
"""SplineMap
|
||||
|
||||
Parameterize the boundary of two geological units using a spline interpolation
|
||||
|
||||
..math::
|
||||
|
||||
g = f(x)-y
|
||||
|
||||
Define the model as:
|
||||
|
||||
..math::
|
||||
|
||||
m = [\sigma_1, \sigma_2, y]
|
||||
|
||||
"""
|
||||
def __init__(self, mesh, pts, ptsv=None,order=3, logSigma=True, normal='X'):
|
||||
IdentityMap.__init__(self, mesh)
|
||||
self.logSigma = logSigma
|
||||
self.order = order
|
||||
self.normal = normal
|
||||
self.pts= pts
|
||||
self.npts = np.size(pts)
|
||||
self.ptsv = ptsv
|
||||
self.spl = None
|
||||
|
||||
slope = 1e4
|
||||
@property
|
||||
def nP(self):
|
||||
if self.mesh.dim == 2:
|
||||
return np.size(self.pts)+2
|
||||
elif self.mesh.dim == 3:
|
||||
return np.size(self.pts)*2+2
|
||||
else:
|
||||
raise(Exception("Only supports 2D and 3D"))
|
||||
|
||||
def _transform(self, m):
|
||||
# Set model parameters
|
||||
alpha = self.slope
|
||||
sig1,sig2 = m[0],m[1]
|
||||
c = m[2:]
|
||||
if self.logSigma:
|
||||
sig1, sig2 = np.exp(sig1), np.exp(sig2)
|
||||
#2D
|
||||
if self.mesh.dim == 2:
|
||||
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':
|
||||
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:
|
||||
# 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:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
|
||||
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)}
|
||||
|
||||
if self.normal =='X':
|
||||
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
|
||||
# elif self.normal =='Y':
|
||||
# elif self.normal =='Z':
|
||||
else:
|
||||
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)
|
||||
|
||||
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:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
|
||||
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
|
||||
elif self.mesh.dim == 3:
|
||||
X = self.mesh.gridCC[:,0]
|
||||
Y = self.mesh.gridCC[:,1]
|
||||
Z = self.mesh.gridCC[:,2]
|
||||
if self.normal =='X':
|
||||
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
|
||||
# elif self.normal =='Y':
|
||||
# elif self.normal =='Z':
|
||||
else:
|
||||
raise(Exception("Not Implemented for Y and Z, your turn :)"))
|
||||
|
||||
if self.logSigma:
|
||||
g1 = -(np.arctan(alpha*f)/np.pi + 0.5)*sig1 + sig1
|
||||
g2 = (np.arctan(alpha*f)/np.pi + 0.5)*sig2
|
||||
else:
|
||||
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':
|
||||
# Here we use perturbation to compute sensitivity
|
||||
# TODO: bit more generalization of this ...
|
||||
# Modfications for X and Z directions ...
|
||||
for i in range(np.size(self.pts)):
|
||||
ctemp = c[i]
|
||||
ind = np.argmin(abs(self.mesh.vectorCCy-ctemp))
|
||||
ca = c.copy()
|
||||
cb = c.copy()
|
||||
dy = self.mesh.hy[ind]*1.5
|
||||
ca[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)
|
||||
g3[:,i] = Utils.sdiag(alpha*(sig2-sig1)/(1.+(alpha*f)**2)/np.pi)*fderiv
|
||||
|
||||
elif self.mesh.dim==3:
|
||||
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):
|
||||
ctemp = c[i]
|
||||
ind = np.argmin(abs(self.mesh.vectorCCy-ctemp))
|
||||
ca = c.copy()
|
||||
cb = c.copy()
|
||||
dy = self.mesh.hy[ind]*1.5
|
||||
ca[i] = ctemp+dy
|
||||
cb[i] = ctemp-dy
|
||||
#treat bottom boundary
|
||||
if i< self.npts:
|
||||
splba = UnivariateSpline(self.pts, ca[:self.npts], k=self.order, s=0)
|
||||
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
|
||||
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)
|
||||
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])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class BaseMesh(object):
|
||||
# Ensure x0 & n are 1D vectors
|
||||
self._n = np.array(n, dtype=int).ravel()
|
||||
self._x0 = np.array(x0, dtype=float).ravel()
|
||||
self._dim = len(self._x0)
|
||||
|
||||
@property
|
||||
def x0(self):
|
||||
@@ -46,7 +47,7 @@ class BaseMesh(object):
|
||||
:rtype: int
|
||||
:return: dim
|
||||
"""
|
||||
return len(self._n)
|
||||
return self._dim
|
||||
|
||||
@property
|
||||
def nC(self):
|
||||
|
||||
@@ -2,12 +2,12 @@ import numpy as np
|
||||
import scipy.sparse as sp
|
||||
from scipy.constants import pi
|
||||
from SimPEG.Utils import mkvc, ndgrid, sdiag, kron3, speye, spzeros, ddx, av, avExtrap
|
||||
from TensorMesh import BaseTensorMesh
|
||||
from TensorMesh import BaseTensorMesh, BaseRectangularMesh
|
||||
from InnerProducts import InnerProducts
|
||||
from View import CylView
|
||||
|
||||
|
||||
class CylMesh(BaseTensorMesh, InnerProducts, CylView):
|
||||
class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView):
|
||||
"""
|
||||
CylMesh is a mesh class for cylindrical problems
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
+559
-555
File diff suppressed because it is too large
Load Diff
+2312
-1117
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
# from __future__ import division
|
||||
# import numpy as np
|
||||
# cimport numpy as np
|
||||
# from libcpp.vector cimport vector
|
||||
|
||||
|
||||
"""
|
||||
The Z-order curve is generated by interleaving the bits of an offset.
|
||||
|
||||
See also:
|
||||
|
||||
https://github.com/cortesi/scurve
|
||||
Aldo Cortesi <aldo@corte.si>
|
||||
|
||||
"""
|
||||
|
||||
def bitrange(long x, int width, int start, int end):
|
||||
"""
|
||||
Extract a bit range as an integer.
|
||||
(start, end) is inclusive lower bound, exclusive upper bound.
|
||||
"""
|
||||
return x >> (width-end) & ((2**(end-start))-1)
|
||||
|
||||
def index(int dimension, int bits, int levelBits, list p, int level):
|
||||
cdef long idx = 0
|
||||
cdef int iwidth
|
||||
cdef int i
|
||||
cdef long b
|
||||
cdef int bitoff
|
||||
|
||||
p = [_ for _ in p]
|
||||
|
||||
p.reverse()
|
||||
iwidth = bits * dimension
|
||||
for i in range(iwidth):
|
||||
bitoff = bits-(i/dimension)-1
|
||||
poff = dimension-(i%dimension)-1
|
||||
b = bitrange(p[poff], bits, bitoff, bitoff+1) << i
|
||||
idx |= b
|
||||
|
||||
return (idx << levelBits) + level
|
||||
|
||||
def point(int dimension, int bits, int levelBits, long idx):
|
||||
cdef list p
|
||||
cdef int iwidth
|
||||
cdef int i, n
|
||||
cdef long b
|
||||
|
||||
n = idx & (2**levelBits-1)
|
||||
idx = idx >> levelBits
|
||||
|
||||
p = [0]*dimension
|
||||
iwidth = bits * dimension
|
||||
for i in range(iwidth):
|
||||
b = bitrange(idx, iwidth, i, i+1) << (iwidth-i-1)/dimension
|
||||
p[i%dimension] |= b
|
||||
p.reverse()
|
||||
return p + [n]
|
||||
|
||||
|
||||
# def _refineCell(int dimension, int bits, self, pointer):
|
||||
# self._structureChange()
|
||||
# pointer = self._asPointer(pointer)
|
||||
# ind = self._asIndex(pointer)
|
||||
# assert ind in self
|
||||
# h = self._levelWidth(pointer[-1])/2 # halfWidth
|
||||
# nL = pointer[-1] + 1 # new level
|
||||
# add = lambda p:p[0]+p[1]
|
||||
# added = []
|
||||
# def addCell(p):
|
||||
# i = self._index(p+[nL])
|
||||
# self._treeInds.add(i)
|
||||
# added.append(i)
|
||||
|
||||
# addCell(map(add, zip(pointer[:-1], [0,0,0])))
|
||||
# addCell(map(add, zip(pointer[:-1], [h,0,0])))
|
||||
# addCell(map(add, zip(pointer[:-1], [0,h,0])))
|
||||
# addCell(map(add, zip(pointer[:-1], [h,h,0])))
|
||||
# if self.dim == 3:
|
||||
# addCell(map(add, zip(pointer[:-1], [0,0,h])))
|
||||
# addCell(map(add, zip(pointer[:-1], [h,0,h])))
|
||||
# addCell(map(add, zip(pointer[:-1], [0,h,h])))
|
||||
# addCell(map(add, zip(pointer[:-1], [h,h,h])))
|
||||
# self._treeInds.remove(ind)
|
||||
# return added
|
||||
+10
-62
@@ -1,8 +1,11 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from SimPEG.Utils import mkvc, animate
|
||||
from SimPEG.Utils import mkvc
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
except ImportError, e:
|
||||
print 'Trouble importing matplotlib.'
|
||||
|
||||
|
||||
class TensorView(object):
|
||||
@@ -173,7 +176,7 @@ class TensorView(object):
|
||||
ax=None, clim=None, showIt=False,
|
||||
pcolorOpts={},
|
||||
streamOpts={'color':'k'},
|
||||
gridOpts={'color':'k'}
|
||||
gridOpts={'color':'k', 'alpha':0.5}
|
||||
):
|
||||
|
||||
"""
|
||||
@@ -216,6 +219,7 @@ class TensorView(object):
|
||||
if ind is None: ind = int(szSliceDim/2)
|
||||
assert type(ind) in [int, long], 'ind must be an integer'
|
||||
|
||||
assert not (v.dtype == complex and view == 'vec'), 'Can not plot a complex vector.'
|
||||
# The slicing and plotting code!!
|
||||
|
||||
def getIndSlice(v):
|
||||
@@ -328,6 +332,7 @@ class TensorView(object):
|
||||
v = getattr(np,view)(v) # e.g. np.real(v)
|
||||
if clim is None:
|
||||
clim = [v.min(),v.max()]
|
||||
v = np.ma.masked_where(np.isnan(v), v)
|
||||
out += (ax.pcolormesh(self.vectorNx, self.vectorNy, v.T, vmin=clim[0], vmax=clim[1], **pcolorOpts),)
|
||||
elif view in ['vec']:
|
||||
U, V = self.r(v.reshape((self.nC,-1), order='F'), 'CC', 'CC', 'M')
|
||||
@@ -477,63 +482,6 @@ class TensorView(object):
|
||||
ax.grid(True)
|
||||
if showIt: plt.show()
|
||||
|
||||
def slicer(mesh, var, imageType='CC', normal='z', index=0, ax=None, clim=None):
|
||||
assert normal in 'xyz', 'normal must be x, y, or z'
|
||||
if ax is None: ax = plt.subplot(111)
|
||||
I = mesh.r(var,'CC','CC','M')
|
||||
axes = [p for p in 'xyz' if p not in normal.lower()]
|
||||
if normal is 'x': I = I[index,:,:]
|
||||
if normal is 'y': I = I[:,index,:]
|
||||
if normal is 'z': I = I[:,:,index]
|
||||
if clim is None: clim = [I.min(),I.max()]
|
||||
p = ax.pcolormesh(getattr(mesh,'vectorN'+axes[0]),getattr(mesh,'vectorN'+axes[1]),I.T,vmin=clim[0],vmax=clim[1])
|
||||
ax.axis('tight')
|
||||
ax.set_xlabel(axes[0])
|
||||
ax.set_ylabel(axes[1])
|
||||
return p
|
||||
|
||||
def videoSlicer(mesh,var,imageType='CC',normal='z',figsize=(10,8)):
|
||||
assert mesh.dim > 2, 'This is for 3D meshes only.'
|
||||
# First set up the figure, the axis, and the plot element we want to animate
|
||||
fig = plt.figure(figsize=figsize)
|
||||
ax = plt.axes()
|
||||
clim = [var.min(),var.max()]
|
||||
plt.colorbar(mesh.slicer(var, imageType=imageType, normal=normal, index=0, ax=ax, clim=clim))
|
||||
tlt = plt.title(normal)
|
||||
|
||||
def animateFrame(i):
|
||||
mesh.slicer(var, imageType=imageType, normal=normal, index=i, ax=ax, clim=clim)
|
||||
tlt.set_text(normal.upper()+('-Slice: %d, %4.4f' % (i,getattr(mesh,'vectorCC'+normal)[i])))
|
||||
|
||||
return animate(fig, animateFrame, frames=mesh.vnC['xyz'.index(normal)])
|
||||
|
||||
def video(mesh, var, function, figsize=(10, 8), colorbar=True, skip=1):
|
||||
"""
|
||||
Call a function for a list of models to create a video.
|
||||
|
||||
::
|
||||
|
||||
def function(var, ax, clim, tlt, i):
|
||||
tlt.set_text('%d'%i)
|
||||
return mesh.plotImage(var, imageType='CC', ax=ax, clim=clim)
|
||||
|
||||
mesh.video([model1, model2, ..., modeln],function)
|
||||
"""
|
||||
# First set up the figure, the axis, and the plot element we want to animate
|
||||
fig = plt.figure(figsize=figsize)
|
||||
ax = plt.axes()
|
||||
VAR = np.concatenate(var)
|
||||
clim = [VAR.min(),VAR.max()]
|
||||
tlt = plt.title('')
|
||||
if colorbar:
|
||||
plt.colorbar(function(var[0],ax,clim,tlt,0))
|
||||
|
||||
frames = np.arange(0,len(var),skip)
|
||||
def animateFrame(j):
|
||||
i = frames[j]
|
||||
function(var[i],ax,clim,tlt,i)
|
||||
|
||||
return animate(fig, animateFrame, frames=len(frames))
|
||||
|
||||
class CylView(object):
|
||||
|
||||
|
||||
+2
-2
@@ -31,5 +31,5 @@ class Model(np.ndarray):
|
||||
@property
|
||||
def transformDeriv(self):
|
||||
if getattr(self, '_transformDeriv', None) is None:
|
||||
self.deriv = self.mapping.deriv(self.view(np.ndarray))
|
||||
return self.deriv
|
||||
self._transformDeriv = self.mapping.deriv(self.view(np.ndarray))
|
||||
return self._transformDeriv
|
||||
|
||||
@@ -97,6 +97,8 @@ class Minimize(object):
|
||||
tolG = 1e-1 #: Tolerance on gradient norm
|
||||
eps = 1e-5 #: Small value
|
||||
|
||||
stopNextIteration = False #: Stops the optimization program nicely.
|
||||
|
||||
debug = False #: Print debugging information
|
||||
debugLS = False #: Print debugging information for the line-search
|
||||
|
||||
@@ -186,6 +188,7 @@ class Minimize(object):
|
||||
xt, caught = self.modifySearchDirectionBreak(p)
|
||||
if not caught: return self.xc
|
||||
self.doEndIteration(xt)
|
||||
if self.stopNextIteration: break
|
||||
|
||||
self.printDone()
|
||||
self.finish()
|
||||
@@ -210,6 +213,7 @@ class Minimize(object):
|
||||
|
||||
self.iter = 0
|
||||
self.iterLS = 0
|
||||
self.stopNextIteration = False
|
||||
|
||||
x0 = self.projection(x0) # ensure that we start of feasible.
|
||||
self.x0 = x0
|
||||
|
||||
+19
-275
@@ -1,278 +1,7 @@
|
||||
import Utils, Survey, Models, numpy as np, scipy.sparse as sp
|
||||
Solver = Utils.SolverUtils.Solver
|
||||
import Maps, Mesh
|
||||
|
||||
|
||||
class Fields(object):
|
||||
"""Fancy Field Storage
|
||||
|
||||
u[:,'phi'] = phi
|
||||
print u[src0,'phi']
|
||||
|
||||
"""
|
||||
|
||||
knownFields = None #: Known fields, a dict with locations, e.g. {"e": "E", "phi": "CC"}
|
||||
aliasFields = None #: Aliased fields, a dict with [alias, location, function], e.g. {"b":["e","F",lambda(F,e,ind)]}
|
||||
dtype = float #: dtype is the type of the storage matrix. This can be a dictionary.
|
||||
|
||||
def __init__(self, mesh, survey, **kwargs):
|
||||
self.survey = survey
|
||||
self.mesh = mesh
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
self._fields = {}
|
||||
|
||||
if self.knownFields is None:
|
||||
raise Exception('knownFields cannot be set to None')
|
||||
if self.aliasFields is None:
|
||||
self.aliasFields = {}
|
||||
|
||||
allFields = [k for k in self.knownFields] + [a for a in self.aliasFields]
|
||||
assert len(allFields) == len(set(allFields)), 'Aliased fields and Known Fields have overlapping definitions.'
|
||||
self.startup()
|
||||
|
||||
def startup(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def approxSize(self):
|
||||
"""The approximate cost to storing all of the known fields."""
|
||||
sz = 0.0
|
||||
for f in self.knownFields:
|
||||
loc =self.knownFields[f]
|
||||
sz += np.array(self._storageShape(loc)).prod()*8.0/(1024**2)
|
||||
return "%e MB"%sz
|
||||
|
||||
def _storageShape(self, loc):
|
||||
nSrc = self.survey.nSrc
|
||||
|
||||
nP = {'CC': self.mesh.nC,
|
||||
'N': self.mesh.nN,
|
||||
'F': self.mesh.nF,
|
||||
'E': self.mesh.nE}[loc]
|
||||
|
||||
return (nP, nSrc)
|
||||
|
||||
def _initStore(self, name):
|
||||
if name in self._fields:
|
||||
return self._fields[name]
|
||||
|
||||
assert name in self.knownFields, 'field name is not known.'
|
||||
|
||||
loc = self.knownFields[name]
|
||||
|
||||
if type(self.dtype) is dict:
|
||||
dtype = self.dtype[name]
|
||||
else:
|
||||
dtype = self.dtype
|
||||
field = np.zeros(self._storageShape(loc), dtype=dtype)
|
||||
|
||||
self._fields[name] = field
|
||||
|
||||
return field
|
||||
|
||||
def _srcIndex(self, srcTestList):
|
||||
if type(srcTestList) is slice:
|
||||
ind = srcTestList
|
||||
else:
|
||||
if type(srcTestList) is not list:
|
||||
srcTestList = [srcTestList]
|
||||
for srcTest in srcTestList:
|
||||
if srcTest not in self.survey.srcList:
|
||||
raise KeyError('Invalid Source, not in survey list.')
|
||||
|
||||
ind = np.in1d(self.survey.srcList, srcTestList)
|
||||
return ind
|
||||
|
||||
def _nameIndex(self, name, accessType):
|
||||
|
||||
if type(name) is slice:
|
||||
assert name == slice(None,None,None), 'Fancy field name slicing is not supported... yet.'
|
||||
name = None
|
||||
|
||||
if name is None:
|
||||
return
|
||||
if accessType=='set' and name not in self.knownFields:
|
||||
if name in self.aliasFields:
|
||||
raise KeyError("Invalid field name (%s) for setter, you can't set an aliased property"%name)
|
||||
else:
|
||||
raise KeyError('Invalid field name (%s) for setter'%name)
|
||||
|
||||
elif accessType=='get' and (name not in self.knownFields and name not in self.aliasFields):
|
||||
raise KeyError('Invalid field name (%s) for getter'%name)
|
||||
return name
|
||||
|
||||
def _indexAndNameFromKey(self, key, accessType):
|
||||
if type(key) is not tuple:
|
||||
key = (key,)
|
||||
if len(key) == 1:
|
||||
key += (None,)
|
||||
|
||||
assert len(key) == 2, 'must be [Src, fieldName]'
|
||||
|
||||
srcTestList, name = key
|
||||
name = self._nameIndex(name, accessType)
|
||||
ind = self._srcIndex(srcTestList)
|
||||
return ind, name
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
ind, name = self._indexAndNameFromKey(key, 'set')
|
||||
if name is None:
|
||||
freq = key
|
||||
assert type(value) is dict, 'New fields must be a dictionary, if field is not specified.'
|
||||
newFields = value
|
||||
elif name in self.knownFields:
|
||||
newFields = {name: value}
|
||||
else:
|
||||
raise Exception('Unknown setter')
|
||||
|
||||
for name in newFields:
|
||||
field = self._initStore(name)
|
||||
self._setField(field, newFields[name], name, ind)
|
||||
|
||||
def __getitem__(self, key):
|
||||
ind, name = self._indexAndNameFromKey(key, 'get')
|
||||
if name is None:
|
||||
out = {}
|
||||
for name in self._fields:
|
||||
out[name] = self._getField(name, ind)
|
||||
return out
|
||||
return self._getField(name, ind)
|
||||
|
||||
def _setField(self, field, val, name, ind):
|
||||
if isinstance(val, np.ndarray) and (field.shape[1] == 1 or val.ndim == 1):
|
||||
val = Utils.mkvc(val,2)
|
||||
field[:,ind] = val
|
||||
|
||||
def _getField(self, name, ind):
|
||||
if name in self._fields:
|
||||
out = self._fields[name][:,ind]
|
||||
else:
|
||||
# Aliased fields
|
||||
alias, loc, func = self.aliasFields[name]
|
||||
|
||||
srcII = np.array(self.survey.srcList)[ind]
|
||||
if isinstance(srcII, np.ndarray):
|
||||
srcII = srcII.tolist()
|
||||
if len(srcII) == 1:
|
||||
srcII = srcII[0]
|
||||
|
||||
if type(func) is str:
|
||||
assert hasattr(self, func), 'The alias field function is a string, but it does not exist in the Fields class.'
|
||||
func = getattr(self, func)
|
||||
out = func(self._fields[alias][:,ind], srcII)
|
||||
if out.shape[0] == out.size:
|
||||
out = Utils.mkvc(out)
|
||||
return out
|
||||
|
||||
def __contains__(self, other):
|
||||
if other in self.aliasFields:
|
||||
other = self.aliasFields[other][0]
|
||||
return self._fields.__contains__(other)
|
||||
|
||||
|
||||
class TimeFields(Fields):
|
||||
"""Fancy Field Storage for time domain problems
|
||||
|
||||
u[:,'phi', timeInd] = phi
|
||||
print u[src0,'phi']
|
||||
|
||||
"""
|
||||
|
||||
def _storageShape(self, loc):
|
||||
nP = {'CC': self.mesh.nC,
|
||||
'N': self.mesh.nN,
|
||||
'F': self.mesh.nF,
|
||||
'E': self.mesh.nE}[loc]
|
||||
nSrc = self.survey.nSrc
|
||||
nT = self.survey.prob.nT + 1
|
||||
return (nP, nSrc, nT)
|
||||
|
||||
def _indexAndNameFromKey(self, key, accessType):
|
||||
if type(key) is not tuple:
|
||||
key = (key,)
|
||||
if len(key) == 1:
|
||||
key += (None,)
|
||||
if len(key) == 2:
|
||||
key += (slice(None,None,None),)
|
||||
|
||||
assert len(key) == 3, 'must be [Src, fieldName, times]'
|
||||
|
||||
srcTestList, name, timeInd = key
|
||||
|
||||
name = self._nameIndex(name, accessType)
|
||||
srcInd = self._srcIndex(srcTestList)
|
||||
|
||||
return (srcInd, timeInd), name
|
||||
|
||||
def _correctShape(self, name, ind, deflate=False):
|
||||
srcInd, timeInd = ind
|
||||
if name in self.knownFields:
|
||||
loc = self.knownFields[name]
|
||||
else:
|
||||
loc = self.aliasFields[name][1]
|
||||
nP, total_nSrc, total_nT = self._storageShape(loc)
|
||||
nSrc = np.ones(total_nSrc, dtype=bool)[srcInd].sum()
|
||||
nT = np.ones(total_nT, dtype=bool)[timeInd].sum()
|
||||
shape = nP, nSrc, nT
|
||||
if deflate:
|
||||
shape = tuple([s for s in shape if s > 1])
|
||||
return shape
|
||||
|
||||
def _setField(self, field, val, name, ind):
|
||||
srcInd, timeInd = ind
|
||||
shape = self._correctShape(name, ind)
|
||||
if Utils.isScalar(val):
|
||||
field[:,srcInd,timeInd] = val
|
||||
return
|
||||
if val.size != np.array(shape).prod():
|
||||
raise ValueError('Incorrect size for data.')
|
||||
correctShape = field[:,srcInd,timeInd].shape
|
||||
field[:,srcInd,timeInd] = val.reshape(correctShape, order='F')
|
||||
|
||||
def _getField(self, name, ind):
|
||||
srcInd, timeInd = ind
|
||||
|
||||
if name in self._fields:
|
||||
out = self._fields[name][:,srcInd,timeInd]
|
||||
else:
|
||||
# Aliased fields
|
||||
alias, loc, func = self.aliasFields[name]
|
||||
if type(func) is str:
|
||||
assert hasattr(self, func), 'The alias field function is a string, but it does not exist in the Fields class.'
|
||||
func = getattr(self, func)
|
||||
pointerFields = self._fields[alias][:,srcInd,timeInd]
|
||||
pointerShape = self._correctShape(alias, ind)
|
||||
pointerFields = pointerFields.reshape(pointerShape, order='F')
|
||||
|
||||
timeII = np.arange(self.survey.prob.nT + 1)[timeInd]
|
||||
srcII = np.array(self.survey.srcList)[srcInd]
|
||||
if isinstance(srcII, np.ndarray):
|
||||
srcII = srcII.tolist()
|
||||
if len(srcII) == 1:
|
||||
srcII = srcII[0]
|
||||
|
||||
if timeII.size == 1:
|
||||
pointerShapeDeflated = self._correctShape(alias, ind, deflate=True)
|
||||
pointerFields = pointerFields.reshape(pointerShapeDeflated, order='F')
|
||||
out = func(pointerFields, srcII, timeII)
|
||||
else: #loop over the time steps
|
||||
nT = pointerShape[2]
|
||||
out = range(nT)
|
||||
for i, TIND_i in enumerate(timeII):
|
||||
fieldI = pointerFields[:,:,i]
|
||||
if fieldI.ndim == 2 and fieldI.shape[1] == 1:
|
||||
fieldI = Utils.mkvc(fieldI)
|
||||
out[i] = func(fieldI, srcII, TIND_i)
|
||||
if out[i].ndim == 1:
|
||||
out[i] = out[i][:,np.newaxis,np.newaxis]
|
||||
elif out[i].ndim == 2:
|
||||
out[i] = out[i][:,:,np.newaxis]
|
||||
out = np.concatenate(out, axis=2)
|
||||
|
||||
shape = self._correctShape(name, ind, deflate=True)
|
||||
return out.reshape(shape, order='F')
|
||||
|
||||
|
||||
from Fields import Fields, TimeFields
|
||||
|
||||
class BaseProblem(object):
|
||||
"""
|
||||
@@ -289,15 +18,27 @@ class BaseProblem(object):
|
||||
Solver = Solver #: A SimPEG Solver class.
|
||||
solverOpts = {} #: Sovler options as a kwarg dict
|
||||
|
||||
mapping = None #: A SimPEG.Map instance.
|
||||
mesh = None #: A SimPEG.Mesh instance.
|
||||
|
||||
PropMap = None #: A SimPEG PropertyMap class.
|
||||
|
||||
@property
|
||||
def mapping(self):
|
||||
"A SimPEG.Map instance or a property map is PropMap is not None"
|
||||
return getattr(self, '_mapping', None)
|
||||
@mapping.setter
|
||||
def mapping(self, val):
|
||||
if self.PropMap is None:
|
||||
val._assertMatchesPair(self.mapPair)
|
||||
self._mapping = val
|
||||
else:
|
||||
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."
|
||||
self.mesh = mesh
|
||||
self.mapping = mapping or Maps.IdentityMap(mesh)
|
||||
self.mapping._assertMatchesPair(self.mapPair)
|
||||
|
||||
@property
|
||||
def survey(self):
|
||||
@@ -333,7 +74,10 @@ class BaseProblem(object):
|
||||
def curModel(self, value):
|
||||
if value is self.curModel:
|
||||
return # it is the same!
|
||||
self._curModel = Models.Model(value, self.mapping)
|
||||
if self.PropMap is not None:
|
||||
self._curModel = self.mapping(value)
|
||||
else:
|
||||
self._curModel = Models.Model(value, self.mapping)
|
||||
for prop in self.deleteTheseOnModelUpdate:
|
||||
if hasattr(self, prop):
|
||||
delattr(self, prop)
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
import Utils, Maps, numpy as np, scipy.sparse as sp
|
||||
|
||||
class Property(object):
|
||||
|
||||
name = ''
|
||||
doc = ''
|
||||
|
||||
defaultVal = None
|
||||
defaultInvProp = False
|
||||
|
||||
def __init__(self, doc, **kwargs):
|
||||
# Set the default after all other params are set
|
||||
self.doc = doc
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
@property
|
||||
def propertyLink(self):
|
||||
"Can be something like: ('sigma', Maps.ReciprocalMap)"
|
||||
return getattr(self, '_propertyLink', None)
|
||||
@propertyLink.setter
|
||||
def propertyLink(self, value):
|
||||
assert type(value) is tuple and len(value) == 2 and type(value[0]) is str and issubclass(value[1], Maps.IdentityMap), 'Use format: ("%s", Maps.ReciprocalMap)'%self.name
|
||||
self._propertyLink = value
|
||||
|
||||
def _getMapProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
return getattr(self, '_%sMap'%prop.name, None)
|
||||
def fset(self, val):
|
||||
if prop.propertyLink is not None:
|
||||
linkName, linkMap = prop.propertyLink
|
||||
assert getattr(self, '%sMap'%linkName, None) is None, 'Cannot set both sides of a linked property.'
|
||||
# TODO: Check if the mapping can be correct
|
||||
setattr(self, '_%sMap'%prop.name, val)
|
||||
return property(fget=fget, fset=fset, doc=prop.doc)
|
||||
|
||||
def _getIndexProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
return getattr(self, '_%sIndex'%prop.name, slice(None))
|
||||
def fset(self, val):
|
||||
setattr(self, '_%sIndex'%prop.name, val)
|
||||
return property(fget=fget, fset=fset, doc=prop.doc)
|
||||
|
||||
def _getProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
mapping = getattr(self, '%sMap'%prop.name)
|
||||
if mapping is None and prop.propertyLink is None:
|
||||
return prop.defaultVal
|
||||
|
||||
if mapping is None and prop.propertyLink is not None:
|
||||
linkName, linkMapClass = prop.propertyLink
|
||||
linkMap = linkMapClass(None)
|
||||
if getattr(self, '%sMap'%linkName, None) is None:
|
||||
return prop.defaultVal
|
||||
m = getattr(self, '%s'%linkName)
|
||||
return linkMap * m
|
||||
|
||||
m = getattr(self, '%sModel'%prop.name)
|
||||
return mapping * m
|
||||
return property(fget=fget)
|
||||
|
||||
def _getModelDerivProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
mapping = getattr(self, '%sMap'%prop.name)
|
||||
if mapping is None and prop.propertyLink is None:
|
||||
return None
|
||||
|
||||
if mapping is None and prop.propertyLink is not None:
|
||||
linkName, linkMapClass = prop.propertyLink
|
||||
linkedMap = getattr(self, '%sMap'%linkName)
|
||||
if linkedMap is None:
|
||||
return None
|
||||
linkMap = linkMapClass(None) * linkedMap
|
||||
m = getattr(self, '%s'%linkName)
|
||||
return linkMap.deriv( m )
|
||||
|
||||
m = getattr(self, '%sModel'%prop.name)
|
||||
return mapping.deriv( m )
|
||||
return property(fget=fget)
|
||||
|
||||
def _getModelProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
mapping = getattr(self, '%sMap'%prop.name)
|
||||
if mapping is None:
|
||||
return None
|
||||
index = getattr(self.propMap, '%sIndex'%prop.name)
|
||||
return self.vector[index]
|
||||
return property(fget=fget)
|
||||
|
||||
def _getModelProjProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
mapping = getattr(self, '%sMap'%prop.name)
|
||||
if mapping is None:
|
||||
return None
|
||||
inds = getattr(self.propMap, '%sIndex'%prop.name)
|
||||
if type(inds) is slice:
|
||||
inds = range(*inds.indices(self.nP))
|
||||
nI, nP = len(inds),self.nP
|
||||
return sp.csr_matrix((np.ones(nI), (range(nI), inds) ), shape=(nI, nP))
|
||||
return property(fget=fget)
|
||||
|
||||
def _getModelMapProperty(self):
|
||||
prop = self
|
||||
def fget(self):
|
||||
return getattr(self.propMap, '_%sMap'%prop.name, None)
|
||||
return property(fget=fget)
|
||||
|
||||
|
||||
|
||||
class PropModel(object):
|
||||
def __init__(self, propMap, vector):
|
||||
self.propMap = propMap
|
||||
self.vector = vector
|
||||
assert len(self.vector) == self.nP
|
||||
|
||||
@property
|
||||
def nP(self):
|
||||
inds = []
|
||||
if getattr(self, '_nP', None) is None:
|
||||
for name in self.propMap._properties:
|
||||
index = getattr(self.propMap, '%sIndex'%name, None)
|
||||
if index is not None:
|
||||
if type(index) is slice:
|
||||
inds += range(*index.indices(len(self.vector)))
|
||||
else:
|
||||
inds += list(index)
|
||||
self._nP = len(set(inds))
|
||||
return self._nP
|
||||
|
||||
def __contains__(self, val):
|
||||
return val in self.propMap
|
||||
|
||||
|
||||
|
||||
_PROPMAPCLASSREGISTRY = {}
|
||||
|
||||
class _PropMapMetaClass(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
assert name.endswith('PropMap'), 'Please use convention: ___PropMap, e.g. ElectromagneticPropMap'
|
||||
_properties = {}
|
||||
for base in bases:
|
||||
for baseProp in getattr(base, '_properties', {}):
|
||||
_properties[baseProp] = base._properties[baseProp]
|
||||
keys = [key for key in attrs]
|
||||
for attr in keys:
|
||||
if isinstance(attrs[attr], Property):
|
||||
attrs[attr].name = attr
|
||||
attrs[attr + 'Map' ] = attrs[attr]._getMapProperty()
|
||||
attrs[attr + 'Index'] = attrs[attr]._getIndexProperty()
|
||||
_properties[attr] = attrs[attr]
|
||||
attrs.pop(attr)
|
||||
|
||||
attrs['_properties'] = _properties
|
||||
|
||||
defaultInvProps = []
|
||||
for p in _properties:
|
||||
prop = _properties[p]
|
||||
if prop.defaultInvProp:
|
||||
defaultInvProps += [p]
|
||||
if prop.propertyLink is not None:
|
||||
assert prop.propertyLink[0] in _properties, "You can only link to things that exist: '%s' is trying to link to '%s'"%(prop.name, prop.propertyLink[0])
|
||||
if len(defaultInvProps) > 1:
|
||||
raise Exception('You have more than one default inversion property: %s' % defaultInvProps)
|
||||
|
||||
newClass = super(_PropMapMetaClass, cls).__new__(cls, name, bases, attrs)
|
||||
|
||||
newClass.PropModel = cls.createPropModelClass(newClass, name, _properties)
|
||||
|
||||
_PROPMAPCLASSREGISTRY[name] = newClass
|
||||
return newClass
|
||||
|
||||
def createPropModelClass(self, name, _properties):
|
||||
|
||||
attrs = dict()
|
||||
|
||||
for attr in _properties:
|
||||
prop = _properties[attr]
|
||||
|
||||
attrs[attr ] = prop._getProperty()
|
||||
attrs[attr + 'Map' ] = prop._getModelMapProperty()
|
||||
attrs[attr + 'Proj' ] = prop._getModelProjProperty()
|
||||
attrs[attr + 'Model'] = prop._getModelProperty()
|
||||
attrs[attr + 'Deriv'] = prop._getModelDerivProperty()
|
||||
|
||||
return type(name.replace('PropMap', 'PropModel'), (PropModel, ), attrs)
|
||||
|
||||
|
||||
class PropMap(object):
|
||||
__metaclass__ = _PropMapMetaClass
|
||||
|
||||
def __init__(self, mappings):
|
||||
"""
|
||||
PropMap takes a multi parameter model and maps it to the equivalent PropModel
|
||||
"""
|
||||
if type(mappings) is dict:
|
||||
assert np.all([k in ['maps', 'slices'] for k in mappings]), 'Dict must only have properties "maps" and "slices"'
|
||||
self.setup(mappings['maps'], slices=mappings['slices'])
|
||||
elif type(mappings) is list:
|
||||
self.setup(mappings)
|
||||
elif isinstance(mappings, Maps.IdentityMap):
|
||||
self.setup([(self.defaultInvProp, mappings)])
|
||||
else:
|
||||
raise Exception('mappings must be a dict, a mapping, or a list of tuples.')
|
||||
|
||||
|
||||
def setup(self, maps, slices=None):
|
||||
"""
|
||||
Sets up the maps and slices for the PropertyMap
|
||||
|
||||
|
||||
:param list maps: [('sigma', sigmaMap), ('mu', muMap), ...]
|
||||
:param list slices: [('sigma', slice(0,nP)), ('mu', [1,2,5,6]), ...]
|
||||
|
||||
"""
|
||||
assert np.all([
|
||||
type(m) is tuple and
|
||||
len(m)==2 and
|
||||
type(m[0]) is str and
|
||||
m[0] in self._properties and
|
||||
isinstance(m[1], Maps.IdentityMap)
|
||||
for m in maps]), "Use signature: [%s]" % (', '.join(["('%s', %sMap)"%(p,p) for p in self._properties]))
|
||||
if slices is None:
|
||||
slices = dict()
|
||||
else:
|
||||
assert np.all([
|
||||
s in self._properties and
|
||||
(type(slices[s]) in [slice, list] or isinstance(slices[s], np.ndarray))
|
||||
for s in slices]), 'Slices must be for each property'
|
||||
|
||||
self.clearMaps()
|
||||
|
||||
nP = 0
|
||||
for name, mapping in maps:
|
||||
setattr(self, '%sMap'%name, mapping)
|
||||
setattr(self, '%sIndex'%name, slices.get(name, slice(nP, nP + mapping.nP)))
|
||||
nP += mapping.nP
|
||||
self.nP = nP
|
||||
|
||||
@property
|
||||
def defaultInvProp(self):
|
||||
for name in self._properties:
|
||||
p = self._properties[name]
|
||||
if p.defaultInvProp:
|
||||
return p.name
|
||||
|
||||
def clearMaps(self):
|
||||
for name in self._properties:
|
||||
setattr(self, '%sMap'%name, None)
|
||||
setattr(self, '%sIndex'%name, None)
|
||||
|
||||
def __call__(self, vec):
|
||||
return self.PropModel(self, vec)
|
||||
|
||||
def __contains__(self, val):
|
||||
activeMaps = [name for name in self._properties if getattr(self, '%sMap'%name) is not None]
|
||||
return val in activeMaps
|
||||
+66
-98
@@ -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,25 +213,38 @@ 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
|
||||
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.Wx, self.Wxx)
|
||||
if self.mesh.dim > 1:
|
||||
wlist += (self.Wy, self.Wyy)
|
||||
if self.mesh.dim > 2:
|
||||
wlist += (self.Wz, self.Wzz)
|
||||
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.W * ( self.mapping * (m) )
|
||||
r2 = self.Ws * ( self.mapping * (self.mref) )
|
||||
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) )
|
||||
@@ -302,12 +270,12 @@ class Tikhonov(BaseRegularization):
|
||||
"""
|
||||
if self.smoothModel == True:
|
||||
mD1 = self.mapping.deriv(m)
|
||||
mD2 = self.mapping.deriv(self.mref)
|
||||
r1 = self.W * ( self.mapping * (m) )
|
||||
r2 = self.Ws * ( self.mapping * (self.mref) )
|
||||
out1 = mD1.T * ( self.W.T * r1 )
|
||||
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
|
||||
out = out1+out2
|
||||
elif self.smoothModel == False:
|
||||
mD = self.mapping.deriv(m - self.mref)
|
||||
r = self.W * ( self.mapping * (m - self.mref) )
|
||||
|
||||
+21
-6
@@ -1,4 +1,4 @@
|
||||
import Utils, numpy as np, scipy.sparse as sp
|
||||
import Utils, numpy as np, scipy.sparse as sp, uuid
|
||||
|
||||
|
||||
class BaseRx(object):
|
||||
@@ -13,6 +13,7 @@ class BaseRx(object):
|
||||
storeProjections = True #: Store calls to getP (organized by mesh)
|
||||
|
||||
def __init__(self, locs, rxType, **kwargs):
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.locs = locs
|
||||
self.rxType = rxType
|
||||
self._Ps = {}
|
||||
@@ -119,14 +120,12 @@ class BaseSrc(object):
|
||||
rxList = None #: SimPEG Receiver List
|
||||
rxPair = BaseRx
|
||||
|
||||
def __init__(self, loc, srcType, rxList, **kwargs):
|
||||
def __init__(self, rxList, **kwargs):
|
||||
assert type(rxList) is list, 'rxList must be a list'
|
||||
for rx in rxList:
|
||||
assert isinstance(rx, self.rxPair), 'rxList must be a %s'%self.rxPair.__name__
|
||||
assert len(set(rxList)) == len(rxList), 'The rxList must be unique'
|
||||
|
||||
self.loc = loc
|
||||
self.srcType = srcType
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.rxList = rxList
|
||||
Utils.setKwargs(self, **kwargs)
|
||||
|
||||
@@ -146,6 +145,7 @@ class Data(object):
|
||||
"""Fancy data storage by Src and Rx"""
|
||||
|
||||
def __init__(self, survey, v=None):
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.survey = survey
|
||||
self._dataDict = {}
|
||||
for src in self.survey.srcList:
|
||||
@@ -205,6 +205,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
|
||||
@@ -227,6 +228,19 @@ class BaseSurvey(object):
|
||||
assert np.all([isinstance(src, self.srcPair) for src in value]), 'All sources must be instances of %s' % self.srcPair.__name__
|
||||
assert len(set(value)) == len(value), 'The srcList must be unique'
|
||||
self._srcList = value
|
||||
self._sourceOrder = dict()
|
||||
[self._sourceOrder.setdefault(src.uid, ii) for ii, src in enumerate(self._srcList)]
|
||||
|
||||
def getSourceIndex(self, sources):
|
||||
if type(sources) is not list:
|
||||
sources = [sources]
|
||||
for src in sources:
|
||||
if getattr(src,'uid',None) is None:
|
||||
raise KeyError('Source does not have a uid: %s'%str(src))
|
||||
inds = map(lambda src: self._sourceOrder.get(src.uid, None), sources)
|
||||
if None in inds:
|
||||
raise KeyError('Some of the sources specified are not in this survey. %s'%str(inds))
|
||||
return inds
|
||||
|
||||
@property
|
||||
def prob(self):
|
||||
@@ -354,9 +368,10 @@ class BaseSurvey(object):
|
||||
|
||||
"""
|
||||
if getattr(self, 'dobs', None) is not None and not force:
|
||||
raise Exception('Survey already has dobs.')
|
||||
raise Exception('Survey already has dobs. You can use force=True to override this exception.')
|
||||
self.mtrue = m
|
||||
self.dtrue = self.dpred(m, u=u)
|
||||
noise = std*abs(self.dtrue)*np.random.randn(*self.dtrue.shape)
|
||||
self.dobs = self.dtrue+noise
|
||||
self.std = self.dobs*0 + std
|
||||
return self.dobs
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from numpy.linalg import norm
|
||||
from SimPEG.Utils import mkvc, sdiag, diagEst
|
||||
from SimPEG import Utils
|
||||
from SimPEG.Mesh import TensorMesh, CurvilinearMesh, CylMesh
|
||||
from SimPEG.Mesh.TreeMesh import TreeMesh as Tree
|
||||
import numpy as np
|
||||
import scipy.sparse as sp
|
||||
import unittest
|
||||
@@ -132,6 +132,34 @@ class OrderTest(unittest.TestCase):
|
||||
self.M = CurvilinearMesh([X, Y, Z])
|
||||
return 1./nc
|
||||
|
||||
elif 'Tree' in self._meshType:
|
||||
nc *= 2
|
||||
if 'uniform' in self._meshType or 'notatree' in self._meshType:
|
||||
h = [nc, nc, nc]
|
||||
elif 'random' in self._meshType:
|
||||
h1 = np.random.rand(nc)*nc*0.5 + nc*0.5
|
||||
h2 = np.random.rand(nc)*nc*0.5 + nc*0.5
|
||||
h3 = np.random.rand(nc)*nc*0.5 + nc*0.5
|
||||
h = [hi/np.sum(hi) for hi in [h1, h2, h3]] # normalize
|
||||
else:
|
||||
raise Exception('Unexpected meshType')
|
||||
|
||||
levels = int(np.log(nc)/np.log(2))
|
||||
self.M = Tree(h[:self.meshDimension], levels=levels)
|
||||
def function(cell):
|
||||
if 'notatree' in self._meshType:
|
||||
return levels - 1
|
||||
r = cell.center - np.array([0.5]*len(cell.center))
|
||||
dist = np.sqrt(r.dot(r))
|
||||
if dist < 0.2:
|
||||
return levels
|
||||
return levels - 1
|
||||
self.M.refine(function,balance=False)
|
||||
self.M.number(balance=False)
|
||||
# self.M.plotGrid(showIt=True)
|
||||
max_h = max([np.max(hi) for hi in self.M.h])
|
||||
return max_h
|
||||
|
||||
def getError(self):
|
||||
"""For given h, generate A[h], f and A(f) and return norm of error."""
|
||||
return 1.
|
||||
@@ -282,6 +310,7 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
|
||||
|
||||
|
||||
if plotIt:
|
||||
import matplotlib.pyplot as plt
|
||||
ax = ax or plt.subplot(111)
|
||||
ax.loglog(h, E0, 'b')
|
||||
ax.loglog(h, E1, 'g--')
|
||||
@@ -1,505 +0,0 @@
|
||||
from SimPEG.Mesh import TensorMesh
|
||||
from SimPEG.Mesh.TreeMesh import TreeMesh, TreeFace, TreeCell
|
||||
import numpy as np
|
||||
import unittest
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
TOL = 1e-10
|
||||
|
||||
class TestOcTreeObjects(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.M = TreeMesh([2,1,1])
|
||||
self.M.number()
|
||||
|
||||
self.Mr = TreeMesh([2,1,1])
|
||||
self.Mr.children[0,0,0].refine()
|
||||
self.Mr.number()
|
||||
|
||||
def q(s):
|
||||
if s[0] == 'M':
|
||||
m = self.M
|
||||
s = s[1:]
|
||||
else:
|
||||
m = self.Mr
|
||||
c = m.sortedCells[int(s[1])]
|
||||
if len(s) == 2: return c
|
||||
if s[2] == 'f' and len(s) == 5: return c.faceDict[s[2:]]
|
||||
if s[2] == 'f': return getattr(c.faceDict[s[2:5]], 'edg' +s[5:])
|
||||
if s[2] == 'e': return getattr(c,s[2:])
|
||||
if s[2] == 'n': return getattr(c,'node'+s[3:])
|
||||
|
||||
self.q = q
|
||||
|
||||
def test_counts(self):
|
||||
self.assertTrue(self.M.nC == 2)
|
||||
self.assertTrue(self.M.nFx == 3)
|
||||
self.assertTrue(self.M.nFy == 4)
|
||||
self.assertTrue(self.M.nFz == 4)
|
||||
self.assertTrue(self.M.nF == 11)
|
||||
self.assertTrue(self.M.nEx == 8)
|
||||
self.assertTrue(self.M.nEy == 6)
|
||||
self.assertTrue(self.M.nEz == 6)
|
||||
self.assertTrue(self.M.nE == 20)
|
||||
self.assertTrue(self.M.nN == 12)
|
||||
|
||||
self.assertTrue(self.Mr.nC == 9)
|
||||
self.assertTrue(self.Mr.nFx == 13)
|
||||
self.assertTrue(self.Mr.nFy == 14)
|
||||
self.assertTrue(self.Mr.nFz == 14)
|
||||
self.assertTrue(self.Mr.nF == 41)
|
||||
|
||||
|
||||
for cell in self.Mr.sortedCells:
|
||||
for e in cell.edgeDict:
|
||||
self.assertTrue(cell.edgeDict[e].edgeType==e[1].lower())
|
||||
|
||||
self.assertTrue(self.Mr.nN == 31)
|
||||
self.assertTrue(self.Mr.nEx == 22)
|
||||
self.assertTrue(self.Mr.nEy == 20)
|
||||
self.assertTrue(self.Mr.nEz == 20)
|
||||
|
||||
def test_sizes(self):
|
||||
q = self.q
|
||||
|
||||
for key in ['Mc0','Mc1']:
|
||||
self.assertTrue(q(key).vol == 0.5)
|
||||
self.assertTrue(q(key+'fXm').area == 1.)
|
||||
self.assertTrue(q(key+'fXp').area == 1.)
|
||||
self.assertTrue(q(key+'fYm').area == 0.5)
|
||||
self.assertTrue(q(key+'fYp').area == 0.5)
|
||||
self.assertTrue(q(key+'fZm').area == 0.5)
|
||||
self.assertTrue(q(key+'fZp').area == 0.5)
|
||||
|
||||
def test_pointersM(self):
|
||||
q = self.q
|
||||
|
||||
self.assertTrue(q('Mc0fXp') is q('Mc1fXm'))
|
||||
self.assertTrue(q('Mc0fXpe0') is q('Mc1fXme0'))
|
||||
self.assertTrue(q('Mc0fXpe1') is q('Mc1fXme1'))
|
||||
self.assertTrue(q('Mc0fXpe2') is q('Mc1fXme2'))
|
||||
self.assertTrue(q('Mc0fXpe3') is q('Mc1fXme3'))
|
||||
self.assertTrue(q('Mc0fYp') is not q('c1fYm'))
|
||||
self.assertTrue(q('Mc0fXm') is not q('c1fXm'))
|
||||
|
||||
# Test connectivity of shared edges
|
||||
self.assertTrue(q('Mc0fZpe3') is not q('c1fZpe0'))
|
||||
self.assertTrue(q('Mc0fZpe3') is not q('c1fZpe1'))
|
||||
self.assertTrue(q('Mc0fZpe3') is q('Mc1fZpe2'))
|
||||
self.assertTrue(q('Mc0fZpe3') is not q('c1fZpe3'))
|
||||
|
||||
self.assertTrue(q('Mc0fZme3') is not q('c1fZme0'))
|
||||
self.assertTrue(q('Mc0fZme3') is not q('c1fZme1'))
|
||||
self.assertTrue(q('Mc0fZme3') is q('Mc1fZme2'))
|
||||
self.assertTrue(q('Mc0fZme3') is not q('c1fZme3'))
|
||||
|
||||
self.assertTrue(q('Mc0fYpe3') is not q('c1fYpe0'))
|
||||
self.assertTrue(q('Mc0fYpe3') is not q('c1fYpe1'))
|
||||
self.assertTrue(q('Mc0fYpe3') is q('Mc1fYpe2'))
|
||||
self.assertTrue(q('Mc0fYpe3') is not q('c1fYpe3'))
|
||||
|
||||
self.assertTrue(q('Mc0fYme3') is not q('c1fYme0'))
|
||||
self.assertTrue(q('Mc0fYme3') is not q('c1fYme1'))
|
||||
self.assertTrue(q('Mc0fYme3') is q('Mc1fYme2'))
|
||||
self.assertTrue(q('Mc0fYme3') is not q('c1fYme3'))
|
||||
|
||||
self.assertTrue(q('Mc0fZme3') is q('Mc1fXme0'))
|
||||
self.assertTrue(q('Mc0fZpe3') is q('Mc1fXme1'))
|
||||
self.assertTrue(q('Mc0fYme3') is q('Mc1fXme2'))
|
||||
self.assertTrue(q('Mc0fYpe3') is q('Mc1fXme3'))
|
||||
|
||||
self.assertTrue(q('Mc0fZme3') is q('Mc0fXpe0'))
|
||||
self.assertTrue(q('Mc0fZpe3') is q('Mc0fXpe1'))
|
||||
self.assertTrue(q('Mc0fYme3') is q('Mc0fXpe2'))
|
||||
self.assertTrue(q('Mc0fYpe3') is q('Mc0fXpe3'))
|
||||
|
||||
self.assertTrue(q('Mc1fZme2') is q('Mc1fXme0'))
|
||||
self.assertTrue(q('Mc1fZpe2') is q('Mc1fXme1'))
|
||||
self.assertTrue(q('Mc1fYme2') is q('Mc1fXme2'))
|
||||
self.assertTrue(q('Mc1fYpe2') is q('Mc1fXme3'))
|
||||
|
||||
self.assertTrue(q('Mc1fZme2') is q('Mc0fXpe0'))
|
||||
self.assertTrue(q('Mc1fZpe2') is q('Mc0fXpe1'))
|
||||
self.assertTrue(q('Mc1fYme2') is q('Mc0fXpe2'))
|
||||
self.assertTrue(q('Mc1fYpe2') is q('Mc0fXpe3'))
|
||||
|
||||
|
||||
def test_nodePointers(self):
|
||||
q = self.q
|
||||
c0 = self.Mr.sortedCells[0]
|
||||
c0n0 = c0.node0
|
||||
self.assertTrue(c0n0 is q('c0n0'))
|
||||
self.assertTrue(np.all(q('c0n0').center == np.r_[0,0,0.]))
|
||||
self.assertTrue(q('c0n0').num == 0)
|
||||
self.assertTrue(q('c0n1').num == 1)
|
||||
self.assertTrue(q('c0n2').num == 4)
|
||||
self.assertTrue(q('c0n3').num == 5)
|
||||
self.assertTrue(q('c0n4').num == 11)
|
||||
self.assertTrue(q('c0n5').num == 12)
|
||||
self.assertTrue(q('c0n6').num == 14)
|
||||
self.assertTrue(q('c0n7').num == 15)
|
||||
|
||||
def test_pointersMr(self):
|
||||
q = self.q
|
||||
|
||||
c0 = self.Mr.sortedCells[0]
|
||||
c0fXm = c0.fXm
|
||||
c0eX0 = c0.eX0
|
||||
c0fYme0 = c0.fYm.edge0
|
||||
self.assertTrue(c0 is q('c0'))
|
||||
self.assertTrue(c0fXm is q('c0fXm'))
|
||||
self.assertTrue(c0eX0 is q('c0eX0'))
|
||||
self.assertTrue(c0fYme0 is q('c0fYme0'))
|
||||
|
||||
self.assertTrue(q('c0').depth == 1)
|
||||
self.assertTrue(q('c1').depth == 1)
|
||||
self.assertTrue(q('c2').depth == 0)
|
||||
|
||||
# Make sure we know where the center of the cells are.
|
||||
self.assertTrue(np.all(q('c0').center == np.r_[0.125,0.25,0.25]))
|
||||
self.assertTrue(np.all(q('c1').center == np.r_[0.375,0.25,0.25]))
|
||||
self.assertTrue(np.all(q('c2').center == np.r_[0.75,0.5,0.5]))
|
||||
self.assertTrue(np.all(q('c3').center == np.r_[0.125,0.75,0.25]))
|
||||
self.assertTrue(np.all(q('c4').center == np.r_[0.375,0.75,0.25]))
|
||||
self.assertTrue(np.all(q('c5').center == np.r_[0.125,0.25,0.75]))
|
||||
self.assertTrue(np.all(q('c6').center == np.r_[0.375,0.25,0.75]))
|
||||
self.assertTrue(np.all(q('c7').center == np.r_[0.125,0.75,0.75]))
|
||||
self.assertTrue(np.all(q('c8').center == np.r_[0.375,0.75,0.75]))
|
||||
|
||||
# Test X face connectivity and locations and stuff...
|
||||
self.assertTrue(np.all(q('c0fXm').center == np.r_[0,0.25,0.25]))
|
||||
self.assertTrue(np.all(q('c0fXp').center == np.r_[0.25,0.25,0.25]))
|
||||
self.assertTrue(q('c0fXp') is q('c1fXm'))
|
||||
self.assertTrue(np.all(q('c1fXp').center == np.r_[0.5,0.25,0.25]))
|
||||
self.assertTrue(np.all(q('c2fXm').center == np.r_[0.5,0.5,0.5]))
|
||||
self.assertTrue(q('c2fXm').branchdepth == 1)
|
||||
self.assertTrue(q('c2fXm').children[0,0] is q('c1fXp'))
|
||||
self.assertTrue(np.all(q('c3fXm').center == np.r_[0,0.75,0.25]))
|
||||
self.assertTrue(np.all(q('c3fXp').center == np.r_[0.25,0.75,0.25]))
|
||||
self.assertTrue(q('c4fXm') is q('c3fXp'))
|
||||
self.assertTrue(q('c2fXm').children[1,0] is q('c4fXp'))
|
||||
|
||||
#Test some internal stuff (edges held by cell should be same as inside)
|
||||
for key in ['Mc0', 'Mc1'] + ['c%d'%i for i in range(9)]:
|
||||
self.assertTrue(q(key+'eX0') is q(key+'fZme0'))
|
||||
self.assertTrue(q(key+'eX1') is q(key+'fZme1'))
|
||||
self.assertTrue(q(key+'eX2') is q(key+'fZpe0'))
|
||||
self.assertTrue(q(key+'eX3') is q(key+'fZpe1'))
|
||||
|
||||
self.assertTrue(q(key+'eX0') is q(key+'fYme0'))
|
||||
self.assertTrue(q(key+'eX1') is q(key+'fYpe0'))
|
||||
self.assertTrue(q(key+'eX2') is q(key+'fYme1'))
|
||||
self.assertTrue(q(key+'eX3') is q(key+'fYpe1'))
|
||||
|
||||
self.assertTrue(q(key+'eY0') is q(key+'fXme0'))
|
||||
self.assertTrue(q(key+'eY1') is q(key+'fXpe0'))
|
||||
self.assertTrue(q(key+'eY2') is q(key+'fXme1'))
|
||||
self.assertTrue(q(key+'eY3') is q(key+'fXpe1'))
|
||||
|
||||
self.assertTrue(q(key+'eY0') is q(key+'fZme2'))
|
||||
self.assertTrue(q(key+'eY1') is q(key+'fZme3'))
|
||||
self.assertTrue(q(key+'eY2') is q(key+'fZpe2'))
|
||||
self.assertTrue(q(key+'eY3') is q(key+'fZpe3'))
|
||||
|
||||
self.assertTrue(q(key+'eZ0') is q(key+'fXme2'))
|
||||
self.assertTrue(q(key+'eZ1') is q(key+'fXpe2'))
|
||||
self.assertTrue(q(key+'eZ2') is q(key+'fXme3'))
|
||||
self.assertTrue(q(key+'eZ3') is q(key+'fXpe3'))
|
||||
|
||||
self.assertTrue(q(key+'eZ0') is q(key+'fYme2'))
|
||||
self.assertTrue(q(key+'eZ1') is q(key+'fYme3'))
|
||||
self.assertTrue(q(key+'eZ2') is q(key+'fYpe2'))
|
||||
self.assertTrue(q(key+'eZ3') is q(key+'fYpe3'))
|
||||
|
||||
#Test some edge stuff
|
||||
self.assertTrue(np.all(q('c0eX0').center == np.r_[0.125,0,0]))
|
||||
self.assertTrue(np.all(q('c0eX1').center == np.r_[0.125,0.5,0]))
|
||||
self.assertTrue(np.all(q('c0eX2').center == np.r_[0.125,0,0.5]))
|
||||
self.assertTrue(np.all(q('c0eX3').center == np.r_[0.125,0.5,0.5]))
|
||||
|
||||
self.assertTrue(np.all(q('c5eX0').center == np.r_[0.125,0,0.5]))
|
||||
self.assertTrue(np.all(q('c5eX1').center == np.r_[0.125,0.5,0.5]))
|
||||
self.assertTrue(q('c5eX0') is q('c0eX2'))
|
||||
self.assertTrue(q('c5eX1') is q('c0eX3'))
|
||||
|
||||
self.assertTrue(np.all(q('c0eY0').center == np.r_[0,0.25,0]))
|
||||
self.assertTrue(np.all(q('c0eY1').center == np.r_[0.25,0.25,0]))
|
||||
self.assertTrue(np.all(q('c0eY2').center == np.r_[0,0.25,0.5]))
|
||||
self.assertTrue(np.all(q('c0eY3').center == np.r_[0.25,0.25,0.5]))
|
||||
|
||||
self.assertTrue(np.all(q('c1eY0').center == np.r_[0.25,0.25,0]))
|
||||
self.assertTrue(np.all(q('c1eY2').center == np.r_[0.25,0.25,0.5]))
|
||||
self.assertTrue(q('c1eY0') is q('c0eY1'))
|
||||
self.assertTrue(q('c1eY2') is q('c0eY3'))
|
||||
|
||||
|
||||
self.assertTrue(np.all(q('c0eZ0').center == np.r_[0,0,0.25]))
|
||||
self.assertTrue(np.all(q('c0eZ1').center == np.r_[0.25,0,0.25]))
|
||||
self.assertTrue(np.all(q('c0eZ2').center == np.r_[0,0.5,0.25]))
|
||||
self.assertTrue(np.all(q('c0eZ3').center == np.r_[0.25,0.5,0.25]))
|
||||
|
||||
self.assertTrue(np.all(q('c3eZ0').center == np.r_[0,0.5,0.25]))
|
||||
self.assertTrue(np.all(q('c3eZ1').center == np.r_[0.25,0.5,0.25]))
|
||||
self.assertTrue(q('c3eZ0') is q('c0eZ2'))
|
||||
self.assertTrue(q('c3eZ1') is q('c0eZ3'))
|
||||
|
||||
|
||||
self.assertTrue(q('c0fXp') is q('c1fXm'))
|
||||
self.assertTrue(q('c0fYp') is not q('c1fYm'))
|
||||
self.assertTrue(q('c0fXm') is not q('c1fXm'))
|
||||
|
||||
self.assertTrue(q('c1fXp') is q('c2fXm').children[0,0])
|
||||
|
||||
self.assertTrue(q('c1fYp') is q('c4fYm'))
|
||||
self.assertTrue(q('c1fZp') is q('c6fZm'))
|
||||
|
||||
self.assertTrue(q('c6fXp') is q('c2fXm').children[0,1])
|
||||
|
||||
self.assertTrue(q('c4fXp') is q('c2fXm').children[1,0])
|
||||
|
||||
|
||||
def test_gridCC(self):
|
||||
x = np.r_[0.25,0.75]
|
||||
y = np.r_[0.5,0.5]
|
||||
z = np.r_[0.5,0.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridCC).flatten()) == 0)
|
||||
|
||||
x = np.r_[0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.125,0.375]
|
||||
y = np.r_[0.25,0.25,0.5,0.75,0.75,0.25,0.25,0.75,0.75]
|
||||
z = np.r_[0.25,0.25,0.5,0.25,0.25,0.75,0.75,0.75,0.75]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridCC).flatten()) == 0)
|
||||
|
||||
def test_gridN(self):
|
||||
x = np.r_[0,0.5,1,0,0.5,1,0,0.5,1,0,0.5,1]
|
||||
y = np.r_[0,0,0,1,1,1,0,0,0,1,1,1.]
|
||||
z = np.r_[0,0,0,0,0,0,1,1,1,1,1,1.]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridN).flatten()) == 0)
|
||||
|
||||
x = np.r_[0,0.25,0.5,1,0,0.25,0.5,0,0.25,0.5,1,0,0.25,0.5,0,0.25,0.5,0,0.25,0.5,0,0.25,0.5,1,0,0.25,0.5,0,0.25,0.5,1]
|
||||
y = np.r_[0,0,0,0,0.5,0.5,0.5,1,1,1,1,0,0,0,0.5,0.5,0.5,1,1,1,0,0,0,0,0.5,0.5,0.5,1,1,1,1]
|
||||
z = np.r_[0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1,1,1,1,1,1,1,1,1,1,1]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridN).flatten()) == 0)
|
||||
|
||||
def test_gridFx(self):
|
||||
x = np.r_[0.0,0.5,1.0]
|
||||
y = np.r_[0.5,0.5,0.5]
|
||||
z = np.r_[0.5,0.5,0.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridFx).flatten()) == 0)
|
||||
|
||||
x = np.r_[0.0,0.25,0.5,1.0,0.0,0.25,0.5,0.0,0.25,0.5,0.0,0.25,0.5]
|
||||
y = np.r_[0.25,0.25,0.25,0.5,0.75,0.75,0.75,0.25,0.25,0.25,0.75,0.75,0.75]
|
||||
z = np.r_[0.25,0.25,0.25,0.5,0.25,0.25,0.25,0.75,0.75,0.75,0.75,0.75,0.75]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridFx).flatten()) == 0)
|
||||
|
||||
def test_gridFy(self):
|
||||
x = np.r_[0.25,0.75,0.25,0.75]
|
||||
y = np.r_[0,0,1.,1.]
|
||||
z = np.r_[0.5,0.5,0.5,0.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridFy).flatten()) == 0)
|
||||
|
||||
x = np.r_[0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.125,0.375]
|
||||
y = np.r_[0,0,0,0.5,0.5,1,1,1,0,0,0.5,0.5,1,1]
|
||||
z = np.r_[0.25,0.25,0.5,0.25,0.25,0.25,0.25,0.5,0.75,0.75,0.75,0.75,0.75,0.75]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridFy).flatten()) == 0)
|
||||
|
||||
def test_gridFz(self):
|
||||
x = np.r_[0.25,0.75,0.25,0.75]
|
||||
y = np.r_[0.5,0.5,0.5,0.5]
|
||||
z = np.r_[0,0,1.,1.]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridFz).flatten()) == 0)
|
||||
|
||||
x = np.r_[0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.125,0.375,0.125,0.375,0.75,0.125,0.375]
|
||||
y = np.r_[0.25,0.25,0.5,0.75,0.75,0.25,0.25,0.75,0.75,0.25,0.25,0.5,0.75,0.75]
|
||||
z = np.r_[0,0,0,0,0,0.5,0.5,0.5,0.5,1,1,1,1,1]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridFz).flatten()) == 0)
|
||||
|
||||
|
||||
def test_gridEx(self):
|
||||
x = np.r_[0.25,0.75,0.25,0.75,0.25,0.75,0.25,0.75]
|
||||
y = np.r_[0,0,1.,1.,0,0,1.,1.]
|
||||
z = np.r_[0,0,0,0,1.,1.,1.,1.]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridEx).flatten()) == 0)
|
||||
|
||||
x = np.r_[0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.125,0.375,0.125,0.375,0.75,0.125,0.375,0.125,0.375,0.75]
|
||||
y = np.r_[0,0,0,0.5,0.5,1,1,1,0,0,0.5,0.5,1,1,0,0,0,0.5,0.5,1,1,1]
|
||||
z = np.r_[0,0,0,0,0,0,0,0,0.5,0.5,0.5,0.5,0.5,0.5,1,1,1,1,1,1,1,1]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridEx).flatten()) == 0)
|
||||
|
||||
def test_gridEy(self):
|
||||
x = np.r_[0,0.5,1,0,0.5,1]
|
||||
y = np.r_[0.5,0.5,0.5,0.5,0.5,0.5]
|
||||
z = np.r_[0,0,0,1.,1.,1.]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridEy).flatten()) == 0)
|
||||
|
||||
x = np.r_[0,0.25,0.5,1,0,0.25,0.5,0,0.25,0.5,0,0.25,0.5,0,0.25,0.5,1,0,0.25,0.5]
|
||||
y = np.r_[0.25,0.25,0.25,0.5,0.75,0.75,0.75,0.25,0.25,0.25,0.75,0.75,0.75,0.25,0.25,0.25,0.5,0.75,0.75,0.75]
|
||||
z = np.r_[0,0,0,0,0,0,0,0.5,0.5,0.5,0.5,0.5,0.5,1,1,1,1,1,1,1]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridEy).flatten()) == 0)
|
||||
|
||||
def test_gridEz(self):
|
||||
x = np.r_[0,0.5,1,0,0.5,1]
|
||||
y = np.r_[0,0,0,1.,1.,1.]
|
||||
z = np.r_[0.5,0.5,0.5,0.5,0.5,0.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.M.gridEz).flatten()) == 0)
|
||||
|
||||
x = np.r_[0,0.25,0.5,1,0 ,0.25,0.5,0,0.25,0.5,1,0,0.25,0.5,0 ,0.25,0.5,0 ,0.25,0.5]
|
||||
y = np.r_[0,0 ,0 ,0,0.5,0.5 ,0.5,1,1 ,1 ,1,0,0 ,0 ,0.5,0.5 ,0.5,1 ,1 ,1 ]
|
||||
z = np.r_[0.25,0.25,0.25,0.5,0.25,0.25,0.25,0.25,0.25,0.25,0.5,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75,0.75]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y,z]-self.Mr.gridEz).flatten()) == 0)
|
||||
|
||||
|
||||
class TestQuadTreeObjects(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.M = TreeMesh([2,1])
|
||||
self.Mr = TreeMesh([2,1])
|
||||
self.Mr.children[0,0].refine()
|
||||
self.Mr.number()
|
||||
# self.Mr.plotGrid(showIt=True)
|
||||
|
||||
def test_pointersM(self):
|
||||
c0 = self.M.children[0,0]
|
||||
c0fXm = c0.fXm
|
||||
c0fXp = c0.fXp
|
||||
c0fYm = c0.fYm
|
||||
c0fYp = c0.fYp
|
||||
|
||||
c1 = self.M.children[1,0]
|
||||
c1fXm = c1.fXm
|
||||
c1fXp = c1.fXp
|
||||
c1fYm = c1.fYm
|
||||
c1fYp = c1.fYp
|
||||
|
||||
self.assertTrue(c0fXp is c1fXm)
|
||||
self.assertTrue(c0fYp is not c1fYm)
|
||||
self.assertTrue(c0fXm is not c1fXm)
|
||||
|
||||
self.assertTrue(c0fXm.area == 1)
|
||||
self.assertTrue(c0fYm.area == 0.5)
|
||||
|
||||
self.assertTrue(c0.node1 is c1.node0)
|
||||
self.assertTrue(c0.node3 is c1.node2)
|
||||
self.assertTrue(self.M.nN == 6)
|
||||
|
||||
|
||||
def test_pointersMr(self):
|
||||
c0 = self.Mr.sortedCells[0]
|
||||
c0fXm = c0.fXm
|
||||
c0fXp = c0.fXp
|
||||
c0fYm = c0.fYm
|
||||
c0fYp = c0.fYp
|
||||
|
||||
c1 = self.Mr.sortedCells[1]
|
||||
c1fXm = c1.fXm
|
||||
c1fXp = c1.fXp
|
||||
c1fYm = c1.fYm
|
||||
c1fYp = c1.fYp
|
||||
|
||||
c2 = self.Mr.sortedCells[2]
|
||||
c2fXm = c2.fXm
|
||||
c2fXp = c2.fXp
|
||||
c2fYm = c2.fYm
|
||||
c2fYp = c2.fYp
|
||||
|
||||
c4 = self.Mr.sortedCells[4]
|
||||
c4fXm = c4.fXm
|
||||
c4fXp = c4.fXp
|
||||
c4fYm = c4.fYm
|
||||
c4fYp = c4.fYp
|
||||
|
||||
self.assertTrue(c0fXp is c1fXm)
|
||||
self.assertTrue(c1fXp.node0 is c2fXm.node0)
|
||||
self.assertTrue(c1fXp.node0 is c2fXm.node0)
|
||||
self.assertTrue(c4fYm is c1fYp)
|
||||
self.assertTrue(c4fXp.node1 is c2fXm.node1)
|
||||
self.assertTrue(c4fXp.node0 is c1fYp.node1)
|
||||
self.assertTrue(c0fXp.node1 is c4fYm.node0)
|
||||
|
||||
self.assertTrue(self.Mr.nN == 11)
|
||||
|
||||
self.assertTrue(np.all(c1fXp.node0.x0 == np.r_[0.5,0]))
|
||||
self.assertTrue(np.all(c1fYp.node0.x0 == np.r_[0.25,0.5]))
|
||||
|
||||
|
||||
class TestQuadTreeMesh(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
M = TreeMesh([np.ones(x) for x in [3,2]])
|
||||
for ii in range(1):
|
||||
M.children[ii,ii].refine()
|
||||
self.M = M
|
||||
M.number()
|
||||
# M.plotGrid(showIt=True)
|
||||
|
||||
def test_MeshSizes(self):
|
||||
self.assertTrue(self.M.nC==9)
|
||||
self.assertTrue(self.M.nF==25)
|
||||
self.assertTrue(self.M.nFx==12)
|
||||
self.assertTrue(self.M.nFy==13)
|
||||
self.assertTrue(self.M.nE==25)
|
||||
self.assertTrue(self.M.nEx==13)
|
||||
self.assertTrue(self.M.nEy==12)
|
||||
|
||||
def test_gridCC(self):
|
||||
x = np.r_[0.25,0.75,1.5,2.5,0.25,0.75,0.5,1.5,2.5]
|
||||
y = np.r_[0.25,0.25,0.5,0.5,0.75,0.75,1.5,1.5,1.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridCC).flatten()) == 0)
|
||||
|
||||
def test_gridN(self):
|
||||
x = np.r_[0,0.5,1,2,3,0,0.5,1,0,0.5,1,2,3,0,1,2,3]
|
||||
y = np.r_[0,0,0,0,0,.5,.5,.5,1,1,1,1,1,2,2,2,2]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridN).flatten()) == 0)
|
||||
|
||||
def test_gridFx(self):
|
||||
x = np.r_[0.0,0.5,1.0,2.0,3.0,0.0,0.5,1.0,0.0,1.0,2.0,3.0]
|
||||
y = np.r_[0.25,0.25,0.25,0.5,0.5,0.75,0.75,0.75,1.5,1.5,1.5,1.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridFx).flatten()) == 0)
|
||||
|
||||
def test_gridFy(self):
|
||||
x = np.r_[0.25,0.75,1.5,2.5,0.25,0.75,0.25,0.75,1.5,2.5,0.5,1.5,2.5]
|
||||
y = np.r_[0,0,0,0,0.5,0.5,1,1,1,1,2,2,2]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridFy).flatten()) == 0)
|
||||
|
||||
def test_gridEx(self):
|
||||
x = np.r_[0.25,0.75,1.5,2.5,0.25,0.75,0.25,0.75,1.5,2.5,0.5,1.5,2.5]
|
||||
y = np.r_[0,0,0,0,0.5,0.5,1,1,1,1,2,2,2]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridEx).flatten()) == 0)
|
||||
|
||||
def test_gridEy(self):
|
||||
x = np.r_[0.0,0.5,1.0,2.0,3.0,0.0,0.5,1.0,0.0,1.0,2.0,3.0]
|
||||
y = np.r_[0.25,0.25,0.25,0.5,0.5,0.75,0.75,0.75,1.5,1.5,1.5,1.5]
|
||||
self.assertTrue(np.linalg.norm((np.c_[x,y]-self.M.gridEy).flatten()) == 0)
|
||||
|
||||
|
||||
class SimpleOctreeOperatorTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
h1 = np.random.rand(5)
|
||||
h2 = np.random.rand(7)
|
||||
h3 = np.random.rand(3)
|
||||
self.tM = TensorMesh([h1,h2,h3])
|
||||
self.oM = TreeMesh([h1,h2,h3])
|
||||
self.tM2 = TensorMesh([h1,h2])
|
||||
self.oM2 = TreeMesh([h1,h2])
|
||||
|
||||
def test_faceDiv(self):
|
||||
self.assertAlmostEqual((self.tM.faceDiv - self.oM.faceDiv).toarray().sum(), 0)
|
||||
self.assertAlmostEqual((self.tM2.faceDiv - self.oM2.faceDiv).toarray().sum(), 0)
|
||||
|
||||
def test_nodalGrad(self):
|
||||
self.assertAlmostEqual((self.tM.nodalGrad - self.oM.nodalGrad).toarray().sum(), 0)
|
||||
self.assertAlmostEqual((self.tM2.nodalGrad - self.oM2.nodalGrad).toarray().sum(), 0)
|
||||
|
||||
def test_edgeCurl(self):
|
||||
self.assertAlmostEqual((self.tM.edgeCurl - self.oM.edgeCurl).toarray().sum(), 0)
|
||||
# self.assertAlmostEqual((self.tM2.edgeCurl - self.oM2.edgeCurl).toarray().sum(), 0)
|
||||
|
||||
def test_InnerProducts(self):
|
||||
self.assertAlmostEqual((self.tM.getFaceInnerProduct() - self.oM.getFaceInnerProduct()).toarray().sum(), 0)
|
||||
self.assertAlmostEqual((self.tM2.getFaceInnerProduct() - self.oM2.getFaceInnerProduct()).toarray().sum(), 0)
|
||||
self.assertAlmostEqual((self.tM2.getEdgeInnerProduct() - self.oM2.getEdgeInnerProduct()).toarray().sum(), 0)
|
||||
self.assertAlmostEqual((self.tM.getEdgeInnerProduct() - self.oM.getEdgeInnerProduct()).toarray().sum(), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,13 +0,0 @@
|
||||
import unittest
|
||||
import sys
|
||||
from SimPEG.Examples import Linear
|
||||
import numpy as np
|
||||
|
||||
class TestLinear(unittest.TestCase):
|
||||
|
||||
def test_running(self):
|
||||
Linear.run(100, plotIt=False)
|
||||
self.assertTrue(True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,31 +0,0 @@
|
||||
import numpy as np
|
||||
import unittest
|
||||
from SimPEG import *
|
||||
from TestUtils import checkDerivative
|
||||
from scipy.sparse.linalg import dsolve
|
||||
import inspect
|
||||
|
||||
|
||||
class RegularizationTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.mesh2 = Mesh.TensorMesh([3, 2])
|
||||
|
||||
def test_regularization(self):
|
||||
for R in dir(Regularization):
|
||||
r = getattr(Regularization, R)
|
||||
if not inspect.isclass(r): continue
|
||||
if not issubclass(r, Regularization.BaseRegularization):
|
||||
continue
|
||||
# if 'Regularization' not in R: continue
|
||||
print 'Check:', R
|
||||
mapping = r.mapPair(self.mesh2)
|
||||
reg = r(self.mesh2, mapping=mapping)
|
||||
m = np.random.rand(mapping.nP)
|
||||
reg.mref = m[:]*0
|
||||
passed = checkDerivative(lambda m : [reg.eval(m), reg.evalDeriv(m)], m, plotIt=False)
|
||||
self.assertTrue(passed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -3,10 +3,27 @@ import scipy.ndimage as ndi
|
||||
import scipy.sparse as sp
|
||||
from matutils import mkvc
|
||||
|
||||
|
||||
def getIndecesBlock(p0,p1,ccMesh):
|
||||
def addBlock(gridCC, modelCC, p0, p1, blockProp):
|
||||
"""
|
||||
Creates a vector containing the block indexes in the cell centerd mesh.
|
||||
Add a block to an exsisting cell centered model, modelCC
|
||||
|
||||
:param numpy.array, gridCC: mesh.gridCC is the cell centered grid
|
||||
:param numpy.array, modelCC: cell centered model
|
||||
:param numpy.array, p0: bottom, southwest corner of block
|
||||
:param numpy.array, p1: top, northeast corner of block
|
||||
:blockProp float, blockProp: property to assign to the model
|
||||
|
||||
:return numpy.array, modelBlock: model with block
|
||||
"""
|
||||
ind = getIndicesBlock(p0, p1, gridCC)
|
||||
modelBlock = modelCC.copy()
|
||||
modelBlock[ind] = blockProp
|
||||
return modelBlock
|
||||
|
||||
|
||||
def getIndicesBlock(p0,p1,ccMesh):
|
||||
"""
|
||||
Creates a vector containing the block indices in the cell centers mesh.
|
||||
Returns a tuple
|
||||
|
||||
The block is defined by the points
|
||||
@@ -78,7 +95,7 @@ def defineBlock(ccMesh,p0,p1,vals=[0,1]):
|
||||
vals[1] conductivity of the ground
|
||||
"""
|
||||
sigma = np.zeros(ccMesh.shape[0]) + vals[1]
|
||||
ind = getIndecesBlock(p0,p1,ccMesh)
|
||||
ind = getIndicesBlock(p0,p1,ccMesh)
|
||||
|
||||
sigma[ind] = vals[0]
|
||||
|
||||
@@ -132,7 +149,7 @@ def defineTwoLayers(ccMesh,depth,vals=[0,1]):
|
||||
# The depth is always defined on the last one.
|
||||
p1[len(p1)-1] -= depth
|
||||
|
||||
ind = getIndecesBlock(p0,p1,ccMesh)
|
||||
ind = getIndicesBlock(p0,p1,ccMesh)
|
||||
|
||||
sigma[ind] = vals[0];
|
||||
|
||||
@@ -153,16 +170,58 @@ def scalarConductivity(ccMesh,pFunction):
|
||||
|
||||
return mkvc(sigma)
|
||||
|
||||
def layeredModel(ccMesh, layerTops, layerValues):
|
||||
"""
|
||||
Define a layered model from layerTops (z-positive up)
|
||||
|
||||
:param numpy.array ccMesh: cell-centered mesh
|
||||
:param numpy.array layerTops: z-locations of the tops of each layer
|
||||
:param numpy.array layerValue: values of the property to assign for each layer (starting at the top)
|
||||
:rtype: numpy.array
|
||||
:return: M, layered model on the mesh
|
||||
"""
|
||||
|
||||
descending = np.linalg.norm(sorted(layerTops, reverse=True) - layerTops) < 1e-20
|
||||
|
||||
# TODO: put an error check to make sure that there is an ordering... needs to work with inf elts
|
||||
# assert ascending or descending, "Layers must be listed in either ascending or descending order"
|
||||
|
||||
# start from bottom up
|
||||
if not descending:
|
||||
zprop = np.hstack([mkvc(layerTops,2),mkvc(layerValues,2)])
|
||||
zprop.sort(axis=0)
|
||||
layerTops, layerValues = zprop[::-1,0], zprop[::-1,1]
|
||||
|
||||
# put in vector form
|
||||
layerTops, layerValues = mkvc(layerTops), mkvc(layerValues)
|
||||
|
||||
# initialize with bottom layer
|
||||
dim = ccMesh.shape[1]
|
||||
if dim == 3:
|
||||
z = ccMesh[:,2]
|
||||
elif dim == 2:
|
||||
z = ccMesh[:,1]
|
||||
elif dim == 1:
|
||||
z = ccMesh[:,0]
|
||||
|
||||
model = np.zeros(ccMesh.shape[0])
|
||||
|
||||
for i, top in enumerate(layerTops):
|
||||
zind = z <= top
|
||||
model[zind] = layerValues[i]
|
||||
|
||||
return model
|
||||
|
||||
|
||||
|
||||
def randomModel(shape, seed=None, anisotropy=None, its=100, bounds=[0,1]):
|
||||
"""
|
||||
Create a random model by convolving a kernal with a
|
||||
Create a random model by convolving a kernel with a
|
||||
uniformly distributed model.
|
||||
|
||||
:param int,tuple shape: shape of the model.
|
||||
:param int seed: pick which model to produce, prints the seed if you don't choose.
|
||||
:param numpy.ndarray,list anisotropy: this is the (3 x n) blurring kernal that is used.
|
||||
:param numpy.ndarray,list anisotropy: this is the (3 x n) blurring kernel that is used.
|
||||
:param int its: number of smoothing iterations
|
||||
:param list bounds: bounds on the model, len(list) == 2
|
||||
:rtype: numpy.ndarray
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -37,20 +44,28 @@ def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6):
|
||||
if len(b.shape) == 1 or b.shape[1] == 1:
|
||||
b = b.flatten()
|
||||
# Just one RHS
|
||||
|
||||
if b.dtype is np.dtype('O'):
|
||||
b = b.astype(type(b[0]))
|
||||
|
||||
if factorize:
|
||||
X = self.solver.solve(b, **self.kwargs)
|
||||
else:
|
||||
X = fun(self.A, b, **self.kwargs)
|
||||
else: # Multiple RHSs
|
||||
if b.dtype is np.dtype('O'):
|
||||
b = b.astype(type(b[0,0]))
|
||||
|
||||
X = np.empty_like(b)
|
||||
|
||||
for i in range(b.shape[1]):
|
||||
if factorize:
|
||||
X[:,i] = self.solver.solve(b[:,i])
|
||||
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):
|
||||
@@ -73,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):
|
||||
@@ -100,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):
|
||||
@@ -110,9 +131,10 @@ def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5):
|
||||
return type(fun.__name__+'_Wrapped', (object,), {"__init__": __init__, "clean": clean, "__mul__": __mul__})
|
||||
|
||||
|
||||
Solver = SolverWrapD(sp.linalg.spsolve, factorize=False)
|
||||
SolverLU = SolverWrapD(sp.linalg.splu, factorize=True)
|
||||
SolverCG = SolverWrapI(sp.linalg.cg)
|
||||
from scipy.sparse import linalg
|
||||
Solver = SolverWrapD(linalg.spsolve, factorize=False)
|
||||
SolverLU = SolverWrapD(linalg.splu, factorize=True)
|
||||
SolverCG = SolverWrapI(linalg.cg)
|
||||
|
||||
|
||||
class SolverDiag(object):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
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 ipythonutils import easyAnimate as animate
|
||||
from CounterUtils import *
|
||||
import ModelBuilder
|
||||
import SolverUtils
|
||||
|
||||
from coordutils import *
|
||||
|
||||
@@ -3,10 +3,7 @@ import time
|
||||
import numpy as np
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class SimPEGMetaClass(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
return super(SimPEGMetaClass, cls).__new__(cls, name, bases, attrs)
|
||||
SimPEGMetaClass = type
|
||||
|
||||
def memProfileWrapper(towrap, *funNames):
|
||||
"""
|
||||
@@ -20,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()
|
||||
@@ -58,9 +55,11 @@ def hook(obj, method, name=None, overwrite=False, silent=False):
|
||||
print 'Method '+name+' was not overwritten.'
|
||||
|
||||
|
||||
def setKwargs(obj, **kwargs):
|
||||
def setKwargs(obj, ignore=[], **kwargs):
|
||||
"""Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist."""
|
||||
for attr in kwargs:
|
||||
if attr in ignore:
|
||||
continue
|
||||
if hasattr(obj, attr):
|
||||
setattr(obj, attr, kwargs[attr])
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import numpy as np
|
||||
from SimPEG.Utils import mkvc
|
||||
|
||||
def rotationMatrixFromNormals(v0,v1,tol=1e-20):
|
||||
"""
|
||||
Performs the minimum number of rotations to define a rotation from the direction indicated by the vector n0 to the direction indicated by n1.
|
||||
The axis of rotation is n0 x n1
|
||||
https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
|
||||
|
||||
:param numpy.array v0: vector of length 3
|
||||
:param numpy.array v1: vector of length 3
|
||||
:param tol = 1e-20: tolerance. If the norm of the cross product between the two vectors is below this, no rotation is performed
|
||||
:rtype: numpy.array, 3x3
|
||||
:return: rotation matrix which rotates the frame so that n0 is aligned with n1
|
||||
|
||||
"""
|
||||
|
||||
# ensure both n0, n1 are vectors of length 1
|
||||
assert len(v0) == 3, "Length of n0 should be 3"
|
||||
assert len(v1) == 3, "Length of n1 should be 3"
|
||||
|
||||
# ensure both are true normals
|
||||
n0 = v0*1./np.linalg.norm(v0)
|
||||
n1 = v1*1./np.linalg.norm(v1)
|
||||
|
||||
n0dotn1 = n0.dot(n1)
|
||||
|
||||
# define the rotation axis, which is the cross product of the two vectors
|
||||
rotAx = np.cross(n0,n1)
|
||||
|
||||
if np.linalg.norm(rotAx) < tol:
|
||||
return np.eye(3,dtype=float)
|
||||
|
||||
rotAx *= 1./np.linalg.norm(rotAx)
|
||||
|
||||
cosT = n0dotn1/(np.linalg.norm(n0)*np.linalg.norm(n1))
|
||||
sinT = np.sqrt(1.-n0dotn1**2)
|
||||
|
||||
ux = np.array([[0., -rotAx[2], rotAx[1]], [rotAx[2], 0., -rotAx[0]], [-rotAx[1], rotAx[0], 0.]],dtype=float)
|
||||
|
||||
return np.eye(3,dtype=float) + sinT*ux + (1.-cosT)*(ux.dot(ux))
|
||||
|
||||
|
||||
def rotatePointsFromNormals(XYZ,n0,n1,x0=np.r_[0.,0.,0.]):
|
||||
"""
|
||||
rotates a grid so that the vector n0 is aligned with the vector n1
|
||||
|
||||
:param numpy.array n0: vector of length 3, should have norm 1
|
||||
:param numpy.array n1: vector of length 3, should have norm 1
|
||||
:param numpy.array x0: vector of length 3, point about which we perform the rotation
|
||||
:rtype: numpy.array, 3x3
|
||||
:return: rotation matrix which rotates the frame so that n0 is aligned with n1
|
||||
"""
|
||||
|
||||
R = rotationMatrixFromNormals(n0, n1)
|
||||
|
||||
assert XYZ.shape[1] == 3, "Grid XYZ should be 3 wide"
|
||||
assert len(x0) == 3, "x0 should have length 3"
|
||||
|
||||
X0 = np.ones([XYZ.shape[0],1])*mkvc(x0)
|
||||
|
||||
return (XYZ - X0).dot(R.T) + X0 # equivalent to (R*(XYZ - X0)).T + X0
|
||||
@@ -124,13 +124,13 @@ if not _interpCython:
|
||||
ind_x1, ind_x2, wx1, wx2 = _interp_point_1D(x, locs[i, 0])
|
||||
ind_y1, ind_y2, wy1, wy2 = _interp_point_1D(y, locs[i, 1])
|
||||
|
||||
inds += [( ind_x1, ind_y2),
|
||||
( ind_x1, ind_y1),
|
||||
inds += [( ind_x1, ind_y1),
|
||||
( ind_x1, ind_y2),
|
||||
( ind_x2, ind_y1),
|
||||
( ind_x2, ind_y2)]
|
||||
|
||||
vals += [wx1*wy2,
|
||||
wx1*wy1,
|
||||
vals += [wx1*wy1,
|
||||
wx1*wy2,
|
||||
wx2*wy1,
|
||||
wx2*wy2]
|
||||
|
||||
@@ -152,8 +152,8 @@ if not _interpCython:
|
||||
ind_y1, ind_y2, wy1, wy2 = _interp_point_1D(y, locs[i, 1])
|
||||
ind_z1, ind_z2, wz1, wz2 = _interp_point_1D(z, locs[i, 2])
|
||||
|
||||
inds += [( ind_x1, ind_y2, ind_z1),
|
||||
( ind_x1, ind_y1, ind_z1),
|
||||
inds += [( ind_x1, ind_y1, ind_z1),
|
||||
( ind_x1, ind_y2, ind_z1),
|
||||
( ind_x2, ind_y1, ind_z1),
|
||||
( ind_x2, ind_y2, ind_z1),
|
||||
( ind_x1, ind_y1, ind_z2),
|
||||
@@ -161,8 +161,8 @@ if not _interpCython:
|
||||
( ind_x2, ind_y1, ind_z2),
|
||||
( ind_x2, ind_y2, ind_z2)]
|
||||
|
||||
vals += [wx1*wy2*wz1,
|
||||
wx1*wy1*wz1,
|
||||
vals += [wx1*wy1*wz1,
|
||||
wx1*wy2*wz1,
|
||||
wx2*wy1*wz1,
|
||||
wx2*wy2*wz1,
|
||||
wx1*wy1*wz2,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,12 +71,12 @@ def _interpmat2D(np.ndarray[np.float64_t, ndim=2] locs,
|
||||
ind_x1, ind_x2, wx1, wx2 = _interp_point_1D(x, locs[i, 0])
|
||||
ind_y1, ind_y2, wy1, wy2 = _interp_point_1D(y, locs[i, 1])
|
||||
|
||||
inds += [( ind_x1, ind_y2),
|
||||
( ind_x1, ind_y1),
|
||||
inds += [( ind_x1, ind_y1),
|
||||
( ind_x1, ind_y2),
|
||||
( ind_x2, ind_y1),
|
||||
( ind_x2, ind_y2)]
|
||||
|
||||
vals += [wx1*wy2, wx1*wy1, wx2*wy1, wx2*wy2]
|
||||
vals += [wx1*wy1, wx1*wy2, wx2*wy1, wx2*wy2]
|
||||
|
||||
return inds, vals
|
||||
|
||||
@@ -98,8 +98,8 @@ def _interpmat3D(np.ndarray[np.float64_t, ndim=2] locs,
|
||||
ind_y1, ind_y2, wy1, wy2 = _interp_point_1D(y, locs[i, 1])
|
||||
ind_z1, ind_z2, wz1, wz2 = _interp_point_1D(z, locs[i, 2])
|
||||
|
||||
inds += [( ind_x1, ind_y2, ind_z1),
|
||||
( ind_x1, ind_y1, ind_z1),
|
||||
inds += [( ind_x1, ind_y1, ind_z1),
|
||||
( ind_x1, ind_y2, ind_z1),
|
||||
( ind_x2, ind_y1, ind_z1),
|
||||
( ind_x2, ind_y2, ind_z1),
|
||||
( ind_x1, ind_y1, ind_z2),
|
||||
@@ -107,8 +107,8 @@ def _interpmat3D(np.ndarray[np.float64_t, ndim=2] locs,
|
||||
( ind_x2, ind_y1, ind_z2),
|
||||
( ind_x2, ind_y2, ind_z2)]
|
||||
|
||||
vals += [wx1*wy2*wz1,
|
||||
wx1*wy1*wz1,
|
||||
vals += [wx1*wy1*wz1,
|
||||
wx1*wy2*wz1,
|
||||
wx2*wy1*wz1,
|
||||
wx2*wy2*wz1,
|
||||
wx1*wy1*wz2,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
from tempfile import NamedTemporaryFile
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import animation
|
||||
|
||||
# http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/
|
||||
# http://www.renevolution.com/how-to-install-ffmpeg-on-mac-os-x/
|
||||
|
||||
VIDEO_TAG = """<video controls loop>
|
||||
<source src="data:video/x-m4v;base64,{0}" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>"""
|
||||
|
||||
def anim_to_html(anim):
|
||||
if not hasattr(anim, '_encoded_video'):
|
||||
with NamedTemporaryFile(suffix='.mp4') as f:
|
||||
anim.save(f.name, fps=20, extra_args=['-vcodec', 'libx264', '-pix_fmt', 'yuv420p'])
|
||||
video = open(f.name, "rb").read()
|
||||
anim._encoded_video = video.encode("base64")
|
||||
|
||||
return VIDEO_TAG.format(anim._encoded_video)
|
||||
|
||||
def display_animation(anim):
|
||||
plt.close(anim._fig)
|
||||
return anim_to_html(anim)
|
||||
|
||||
animation.Animation._repr_html_ = display_animation
|
||||
|
||||
easyAnimate = animation.FuncAnimation
|
||||
@@ -26,6 +26,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 +40,9 @@ def mkvc(x, numDims=1):
|
||||
|
||||
def sdiag(h):
|
||||
"""Sparse diagonal matrix"""
|
||||
if isinstance(h, Zero):
|
||||
return h
|
||||
|
||||
return sp.spdiags(mkvc(h), 0, h.size, h.size, format="csr")
|
||||
|
||||
def sdInv(M):
|
||||
@@ -342,10 +348,10 @@ def invPropertyTensor(M, tensor, returnMatrix=False):
|
||||
|
||||
|
||||
def diagEst(matFun, n, k=None, approach='Probing'):
|
||||
"""
|
||||
"""
|
||||
Estimate the diagonal of a matrix, A. Note that the matrix may be a function which returns A times a vector.
|
||||
|
||||
Three different approaches have been implemented,
|
||||
Three different approaches have been implemented,
|
||||
1. Probing : uses cyclic permutations of vectors with ones and zeros (default)
|
||||
2. Ones : random +/- 1 entries
|
||||
3. Random : random vectors
|
||||
@@ -362,7 +368,7 @@ def diagEst(matFun, n, k=None, approach='Probing'):
|
||||
|
||||
if type(matFun).__name__=='ndarray':
|
||||
A = matFun
|
||||
matFun = lambda v: A.dot(v)
|
||||
matFun = lambda v: A.dot(v)
|
||||
|
||||
if k is None:
|
||||
k = np.floor(n/10.)
|
||||
@@ -396,11 +402,68 @@ def diagEst(matFun, n, k=None, approach='Probing'):
|
||||
|
||||
return d
|
||||
|
||||
class Zero(object):
|
||||
def __add__(self, v):return v
|
||||
def __radd__(self, v):return v
|
||||
def __iadd__(self, v):return v
|
||||
def __sub__(self, v):return -v
|
||||
def __rsub__(self, v):return v
|
||||
def __isub__(self, v):return v
|
||||
def __mul__(self, v):return self
|
||||
def __rmul__(self, v):return self
|
||||
def __div__(self, v): return self
|
||||
def __truediv__(self, v): return self
|
||||
def __rdiv__(self, v): raise ZeroDivisionError('Cannot divide by zero.')
|
||||
def __pos__(self):return self
|
||||
def __neg__(self):return self
|
||||
def __lt__(self, v):return 0 < v
|
||||
def __le__(self, v):return 0 <= v
|
||||
def __eq__(self, v):return v == 0
|
||||
def __ne__(self, v):return not (0 == v)
|
||||
def __ge__(self, v):return 0 >= v
|
||||
def __gt__(self, v):return 0 > v
|
||||
|
||||
from scipy.sparse.linalg import LinearOperator
|
||||
|
||||
class SimPEGLinearOperator(LinearOperator):
|
||||
"""Extends scipy.sparse.linalg.LinearOperator to have a .T function."""
|
||||
@property
|
||||
def transpose(self): return Zero()
|
||||
|
||||
@property
|
||||
def T(self):
|
||||
return self.__class__((self.shape[1],self.shape[0]),self.rmatvec,rmatvec=self.matvec,matmat=self.matmat)
|
||||
def T(self): return Zero()
|
||||
|
||||
class Identity(object):
|
||||
_positive = True
|
||||
def __init__(self, positive=True):
|
||||
self._positive = positive is True
|
||||
|
||||
def __pos__(self):return self
|
||||
def __neg__(self):return Identity(not self._positive)
|
||||
|
||||
def __add__(self, v):
|
||||
if sp.issparse(v):
|
||||
return v + speye(v.shape[0]) if self._positive else v - speye(v.shape[0])
|
||||
return v + 1 if self._positive else v - 1
|
||||
def __radd__(self, v):
|
||||
return self.__add__(v)
|
||||
|
||||
def __sub__(self, v): return self+-v
|
||||
def __rsub__(self, v):return -self+v
|
||||
|
||||
def __mul__(self, v): return v if self._positive else -v
|
||||
def __rmul__(self, v):return v if self._positive else -v
|
||||
|
||||
def __div__(self, v):
|
||||
if sp.issparse(v): raise NotImplementedError('Sparse arrays not divisibile.')
|
||||
return 1/v if self._positive else -1/v
|
||||
def __truediv__(self, v):
|
||||
if sp.issparse(v): raise NotImplementedError('Sparse arrays not divisibile.')
|
||||
return 1.0/v if self._positive else -1.0/v
|
||||
def __rdiv__(self, v):
|
||||
return v if self._positive else -v
|
||||
|
||||
def __lt__(self, v):return 1 < v if self._positive else -1 < v
|
||||
def __le__(self, v):return 1 <= v if self._positive else -1 <= v
|
||||
def __eq__(self, v):return v == 1 if self._positive else v == -1
|
||||
def __ne__(self, v):return (not (1 == v))if self._positive else (not (-1 == v))
|
||||
def __ge__(self, v):return 1 >= v if self._positive else -1 >= v
|
||||
def __gt__(self, v):return 1 > v if self._positive else -1 > v
|
||||
|
||||
|
||||
|
||||
+22
-239
@@ -102,278 +102,61 @@ 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
|
||||
xyzlim: 2D array [ndim x 2]
|
||||
mesh: SimPEG mesh
|
||||
This function ouputs:
|
||||
This function ouputs:
|
||||
- actind: corresponding boolean index from global to core
|
||||
- meshcore: core SimPEG mesh
|
||||
- meshcore: core SimPEG mesh
|
||||
Warning: 1D and 2D has not been tested
|
||||
"""
|
||||
from SimPEG import Mesh
|
||||
if mesh.dim ==1:
|
||||
xyzlim = xyzlim.flatten()
|
||||
xmin, xmax = xyzlim[0], xyzlim[1]
|
||||
|
||||
xind = np.logical_and(mesh.vectorCCx>xmin, mesh.vectorCCx<xmax)
|
||||
|
||||
|
||||
xind = np.logical_and(mesh.vectorCCx>xmin, mesh.vectorCCx<xmax)
|
||||
|
||||
xc = mesh.vectorCCx[xind]
|
||||
|
||||
hx = mesh.hx[xind]
|
||||
|
||||
|
||||
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5]
|
||||
|
||||
|
||||
meshCore = Mesh.TensorMesh([hx, hy] ,x0=x0)
|
||||
|
||||
|
||||
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax)
|
||||
|
||||
|
||||
elif mesh.dim ==2:
|
||||
xmin, xmax = xyzlim[0,0], xyzlim[0,1]
|
||||
ymin, ymax = xyzlim[1,0], xyzlim[1,1]
|
||||
|
||||
yind = np.logical_and(mesh.vectorCCy>ymin, mesh.vectorCCy<ymax)
|
||||
zind = np.logical_and(mesh.vectorCCz>zmin, mesh.vectorCCz<zmax)
|
||||
zind = np.logical_and(mesh.vectorCCz>zmin, mesh.vectorCCz<zmax)
|
||||
|
||||
xc = mesh.vectorCCx[xind]
|
||||
yc = mesh.vectorCCy[yind]
|
||||
|
||||
hx = mesh.hx[xind]
|
||||
hy = mesh.hy[yind]
|
||||
|
||||
|
||||
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5]
|
||||
|
||||
|
||||
meshCore = Mesh.TensorMesh([hx, hy] ,x0=x0)
|
||||
|
||||
|
||||
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \
|
||||
& (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \
|
||||
|
||||
|
||||
elif mesh.dim==3:
|
||||
xmin, xmax = xyzlim[0,0], xyzlim[0,1]
|
||||
ymin, ymax = xyzlim[1,0], xyzlim[1,1]
|
||||
zmin, zmax = xyzlim[2,0], xyzlim[2,1]
|
||||
|
||||
|
||||
xind = np.logical_and(mesh.vectorCCx>xmin, mesh.vectorCCx<xmax)
|
||||
yind = np.logical_and(mesh.vectorCCy>ymin, mesh.vectorCCy<ymax)
|
||||
zind = np.logical_and(mesh.vectorCCz>zmin, mesh.vectorCCz<zmax)
|
||||
zind = np.logical_and(mesh.vectorCCz>zmin, mesh.vectorCCz<zmax)
|
||||
|
||||
xc = mesh.vectorCCx[xind]
|
||||
yc = mesh.vectorCCy[yind]
|
||||
@@ -382,19 +165,19 @@ def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
|
||||
hx = mesh.hx[xind]
|
||||
hy = mesh.hy[yind]
|
||||
hz = mesh.hz[zind]
|
||||
|
||||
|
||||
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5, zc[0]-hz[0]*0.5]
|
||||
|
||||
|
||||
meshCore = Mesh.TensorMesh([hx, hy, hz] ,x0=x0)
|
||||
|
||||
|
||||
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \
|
||||
& (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \
|
||||
& (mesh.gridCC[:,2]>zmin) & (mesh.gridCC[:,2]<zmax)
|
||||
|
||||
|
||||
else:
|
||||
raise(Exception("Not implemented!"))
|
||||
|
||||
|
||||
|
||||
|
||||
return actind, meshCore
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import Directives
|
||||
import Inversion
|
||||
import Tests
|
||||
|
||||
__version__ = '0.1.2'
|
||||
__version__ = '0.1.9'
|
||||
__author__ = 'Rowan Cockett'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2014 Rowan Cockett'
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
@@ -3,8 +3,15 @@
|
||||
Examples
|
||||
********
|
||||
|
||||
Forward problem
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
examples/*
|
||||
|
||||
|
||||
External Notebooks
|
||||
==================
|
||||
|
||||
* `Example 1: Direct Current <http://www.seogi.me/s/notebooks/DCEx.html>`_
|
||||
* `Example 2: Seismic-Acoustic <http://www.seogi.me/s/notebooks/SeismicEx.html>`_
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-17
@@ -23,23 +23,8 @@ the implementations.
|
||||
|
||||
.. plot::
|
||||
|
||||
from SimPEG import Mesh, Utils, np
|
||||
import matplotlib.pyplot as plt
|
||||
sz = [10,10]
|
||||
tM = Mesh.TensorMesh(sz)
|
||||
qM = Mesh.TreeMesh(sz)
|
||||
qM.refine(lambda X: 1 if np.sqrt(((X-0.5)**2).sum()) < 0.3 else 0)
|
||||
rM = Mesh.CurvilinearMesh(Utils.meshutils.exampleLrmGrid(sz,'rotate'))
|
||||
|
||||
fig, axes = plt.subplots(1,3,figsize=(14,5))
|
||||
opts = {}
|
||||
tM.plotGrid(ax=axes[0], **opts)
|
||||
axes[0].set_title('TensorMesh')
|
||||
qM.plotGrid(ax=axes[1], **opts)
|
||||
axes[1].set_title('TreeMesh')
|
||||
rM.plotGrid(ax=axes[2], **opts)
|
||||
axes[2].set_title('CurvilinearMesh')
|
||||
plt.show()
|
||||
from SimPEG import Examples
|
||||
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:
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@
|
||||
Testing SimPEG
|
||||
==============
|
||||
|
||||
.. automodule:: SimPEG.Tests.TestUtils
|
||||
.. automodule:: SimPEG.Tests
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Utilities
|
||||
*********
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api_Solver
|
||||
api_Maps
|
||||
api_Utils
|
||||
api_Tests
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user