diff --git a/environments/py-3.12-linux-64-dev.conda.lock.yml b/environments/py-3.12-linux-64-dev.conda.lock.yml index da5e7a30..8c8a1aa6 100644 --- a/environments/py-3.12-linux-64-dev.conda.lock.yml +++ b/environments/py-3.12-linux-64-dev.conda.lock.yml @@ -301,7 +301,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.12-linux-64.conda.lock.yml b/environments/py-3.12-linux-64.conda.lock.yml index 6a6e3292..e91545be 100644 --- a/environments/py-3.12-linux-64.conda.lock.yml +++ b/environments/py-3.12-linux-64.conda.lock.yml @@ -169,7 +169,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.12-win-64-dev.conda.lock.yml b/environments/py-3.12-win-64-dev.conda.lock.yml index aecc6570..fe68dab7 100644 --- a/environments/py-3.12-win-64-dev.conda.lock.yml +++ b/environments/py-3.12-win-64-dev.conda.lock.yml @@ -286,7 +286,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.12-win-64.conda.lock.yml b/environments/py-3.12-win-64.conda.lock.yml index 970b06b0..bf126458 100644 --- a/environments/py-3.12-win-64.conda.lock.yml +++ b/environments/py-3.12-win-64.conda.lock.yml @@ -156,7 +156,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.13-linux-64-dev.conda.lock.yml b/environments/py-3.13-linux-64-dev.conda.lock.yml index 71fdbec1..81134acf 100644 --- a/environments/py-3.13-linux-64-dev.conda.lock.yml +++ b/environments/py-3.13-linux-64-dev.conda.lock.yml @@ -298,7 +298,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.13-linux-64.conda.lock.yml b/environments/py-3.13-linux-64.conda.lock.yml index 1837eced..d6d455e0 100644 --- a/environments/py-3.13-linux-64.conda.lock.yml +++ b/environments/py-3.13-linux-64.conda.lock.yml @@ -166,7 +166,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.13-win-64-dev.conda.lock.yml b/environments/py-3.13-win-64-dev.conda.lock.yml index 762af53d..c367a277 100644 --- a/environments/py-3.13-win-64-dev.conda.lock.yml +++ b/environments/py-3.13-win-64-dev.conda.lock.yml @@ -285,7 +285,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.13-win-64.conda.lock.yml b/environments/py-3.13-win-64.conda.lock.yml index f81a0f6b..cbbb4e81 100644 --- a/environments/py-3.13-win-64.conda.lock.yml +++ b/environments/py-3.13-win-64.conda.lock.yml @@ -155,7 +155,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.14-linux-64-dev.conda.lock.yml b/environments/py-3.14-linux-64-dev.conda.lock.yml index 334541f0..0ffffdc1 100644 --- a/environments/py-3.14-linux-64-dev.conda.lock.yml +++ b/environments/py-3.14-linux-64-dev.conda.lock.yml @@ -299,7 +299,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.14-linux-64.conda.lock.yml b/environments/py-3.14-linux-64.conda.lock.yml index c35354e7..7ac8a05c 100644 --- a/environments/py-3.14-linux-64.conda.lock.yml +++ b/environments/py-3.14-linux-64.conda.lock.yml @@ -167,7 +167,7 @@ dependencies: - zlib-ng=2.3.3=hceb46e0_1 - zstd=1.5.7=hb78ec9c_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.14-win-64-dev.conda.lock.yml b/environments/py-3.14-win-64-dev.conda.lock.yml index cb0134a2..9d524d83 100644 --- a/environments/py-3.14-win-64-dev.conda.lock.yml +++ b/environments/py-3.14-win-64-dev.conda.lock.yml @@ -286,7 +286,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/environments/py-3.14-win-64.conda.lock.yml b/environments/py-3.14-win-64.conda.lock.yml index baac63c8..133ea6c7 100644 --- a/environments/py-3.14-win-64.conda.lock.yml +++ b/environments/py-3.14-win-64.conda.lock.yml @@ -156,7 +156,7 @@ dependencies: - zlib-ng=2.3.3=h0261ad2_1 - zstd=1.5.7=h534d264_6 - pip: - - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + - geoapps-utils @ git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 - geoh5py @ git+https://github.com/MiraGeoscience/geoh5py.git@6ad559b09341b80c22aac363cbd0087089bd1a8a - grid-apps @ git+https://github.com/MiraGeoscience/grid-apps.git@57806e78469cb881a8d5dceae645336e77310bf4 - mira-simpeg @ git+https://github.com/MiraGeoscience/simpeg.git@fa25746b50102620bd8bcaf91aff8a3f745d8c4b diff --git a/py-3.12.conda-lock.yml b/py-3.12.conda-lock.yml index e32a1b59..c13d64ec 100644 --- a/py-3.12.conda-lock.yml +++ b/py-3.12.conda-lock.yml @@ -8361,7 +8361,7 @@ package: category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: linux-64 dependencies: @@ -8370,16 +8370,16 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: win-64 dependencies: @@ -8388,12 +8388,12 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoh5py @@ -8436,7 +8436,7 @@ package: platform: linux-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' @@ -8455,7 +8455,7 @@ package: platform: win-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' diff --git a/py-3.13.conda-lock.yml b/py-3.13.conda-lock.yml index a3044c06..6172af6f 100644 --- a/py-3.13.conda-lock.yml +++ b/py-3.13.conda-lock.yml @@ -8302,7 +8302,7 @@ package: category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: linux-64 dependencies: @@ -8311,16 +8311,16 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: win-64 dependencies: @@ -8329,12 +8329,12 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoh5py @@ -8377,7 +8377,7 @@ package: platform: linux-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' @@ -8396,7 +8396,7 @@ package: platform: win-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' diff --git a/py-3.14.conda-lock.yml b/py-3.14.conda-lock.yml index 7b1b06a5..08f19fd6 100644 --- a/py-3.14.conda-lock.yml +++ b/py-3.14.conda-lock.yml @@ -8321,7 +8321,7 @@ package: category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: linux-64 dependencies: @@ -8330,16 +8330,16 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoapps-utils - version: 0.7.0a4.dev14+6b26f39 + version: 0.7.0a4.dev21+9baaece manager: pip platform: win-64 dependencies: @@ -8348,12 +8348,12 @@ package: numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' scipy: '>=1.17.0,<1.18.0' - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 hash: - sha256: 6b26f39967277d6384c6fc28d8868c1104dfddeb + sha256: 9baaece0133496c23519ff2708f89e679e900fd0 source: type: url - url: git+https://github.com/MiraGeoscience/geoapps-utils.git@6b26f39967277d6384c6fc28d8868c1104dfddeb + url: git+https://github.com/MiraGeoscience/geoapps-utils.git@9baaece0133496c23519ff2708f89e679e900fd0 category: main optional: false - name: geoh5py @@ -8396,7 +8396,7 @@ package: platform: linux-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' @@ -8415,7 +8415,7 @@ package: platform: win-64 dependencies: discretize: '>=0.12.0,<0.13.0' - geoapps-utils: 0.7.0a4.dev14+6b26f39 + geoapps-utils: 0.7.0a4.dev21+9baaece geoh5py: 0.13.0a4.dev7+6ad559b0 numpy: '>=2.4.2,<2.5.0' pydantic: '>=2.12.0,<2.13.0' diff --git a/simpeg_drivers/driver.py b/simpeg_drivers/driver.py index 80b4dbd8..43586dbd 100644 --- a/simpeg_drivers/driver.py +++ b/simpeg_drivers/driver.py @@ -611,7 +611,9 @@ def get_modified_regularization( continue weight = mapping * getattr(self.models, weight_name) - norm = mapping * getattr(self.models, f"{comp}_norm") + norm = getattr(self.models, f"{comp}_norm") + if norm is not None: + norm = mapping * norm if not isinstance(fun, SparseSmoothness): fun.set_weights(**{comp: weight}) @@ -633,7 +635,9 @@ def get_modified_regularization( f"aveCC2F{fun.orientation}", ) fun.set_weights(**{comp: average_op @ weight}) - fun.norm = np.round(average_op @ norm, decimals=3) + + if norm is not None: + fun.norm = np.round(average_op @ norm, decimals=3) functions.append(fun) if is_rotated: @@ -656,7 +660,9 @@ def get_modified_regularization( f"aveCC2F{fun.orientation}", ) backward_fun.set_weights(**{comp: average_op @ weight}) - backward_fun.norm = np.round(average_op @ norm, decimals=3) + + if norm is not None: + backward_fun.norm = np.round(average_op @ norm, decimals=3) functions.append(backward_fun) return functions diff --git a/simpeg_drivers/plate_simulation/match/driver.py b/simpeg_drivers/plate_simulation/match/driver.py index 1b36ebdc..6dc5ea1c 100644 --- a/simpeg_drivers/plate_simulation/match/driver.py +++ b/simpeg_drivers/plate_simulation/match/driver.py @@ -11,28 +11,30 @@ from __future__ import annotations import sys +from io import BytesIO from pathlib import Path from typing import Self +import matplotlib.pyplot as plt import numpy as np from dask.distributed import Client, Future, progress from geoapps_utils.base import Driver from geoapps_utils.utils.importing import GeoAppsError from geoapps_utils.utils.locations import topo_drape_elevation -from geoapps_utils.utils.logger import get_logger +from geoapps_utils.utils.logger import get_logger, suppress_logging from geoapps_utils.utils.numerical import inverse_weighted_operator from geoapps_utils.utils.plotting import symlog -from geoapps_utils.utils.transformations import cartesian_to_polar +from geoapps_utils.utils.transformations import cartesian_to_polar, rotate_xyz from geoh5py import Workspace from geoh5py.groups import PropertyGroup, SimPEGGroup from geoh5py.objects import AirborneTEMReceivers, MaxwellPlate, Surface from geoh5py.objects.maxwell_plate import PlateGeometry from geoh5py.ui_json import InputFile -from scipy import signal +from scipy import ndimage, signal from scipy.sparse import csr_matrix from scipy.spatial import cKDTree -from simpeg_drivers.driver import BaseDriver, validate_client, validate_workers +from simpeg_drivers.driver import validate_client, validate_workers from simpeg_drivers.electromagnetics.time_domain.options import CONVERSION from simpeg_drivers.plate_simulation.match.options import PlateMatchOptions from simpeg_drivers.plate_simulation.options import ModelOptions, PlateSimulationOptions @@ -218,7 +220,7 @@ def _create_plate_from_parameters( } ) plate = MaxwellPlate.create( - self.params.geoh5, geometry=plate_geometry, parent=self.params.out_group + self.params.geoh5, geometry=plate_geometry, parent=self.out_group ) plate.metadata = model_options.model_dump() @@ -241,6 +243,48 @@ def _get_drape_heights(self) -> np.ndarray: ) return topo_drape_z[:, 2] + @staticmethod + def plot_figure( + locations, survey, observed, time_projection, spatial_projection, center: int + ) -> BytesIO: + """ + Generate a figure showing the observed data and best matching simulated plate responses. + + :param locations: Array of locations. + :param survey: Survey object. + :param observed: Array of observed data. + :param time_projection: Array performing the time interpolation. + :param spatial_projection: Array performing the spatial interpolation. + :param center: Index of the center point in the survey vertices. + + :return: BytesIO object containing the figure. + """ + distances = np.linalg.norm(locations[0, :] - locations, axis=1) + horizontal_shift = (distances - np.mean(distances))[center] + + in_early_val = np.min(np.abs(observed[0, :])) + data = normalized_data(observed, threshold=in_early_val) + preds = get_normalized_predicted( + survey, spatial_projection, time_projection, in_early_val + ) + + preds *= data.max() / preds.max() + + fig, ax = plt.figure(figsize=(12, 10)), plt.subplot() + for obs, pred in zip(data, preds, strict=True): + ax.plot(distances, obs, c="0.75", lw=2) + ax.plot(distances + horizontal_shift, pred, c="k", ls="--", lw=2) + + ax.set_xlabel("Distance (m)") + ax.set_ylabel("Log Normalized Amplitude") + ax.legend(["Observed", "Simulated"]) + + buf = BytesIO() + fig.savefig(buf, format="png") + plt.close(fig) + buf.seek(0) + return buf + def spatial_interpolation( self, indices: np.ndarray, @@ -255,36 +299,49 @@ def spatial_interpolation( :return: Spatial interpolation matrix. """ # Compute local coordinates for the current line segment - local_polar = cartesian_to_polar( - self.params.survey.vertices[indices], - origin=np.r_[self.params.survey.vertices[indices, :2].mean(axis=0), 0], + delta = ( + self.params.survey.vertices[indices] + - self.params.survey.vertices[indices[0], :] + ) + azimuths = np.mean(np.rad2deg(np.arctan2(delta[:, 1], delta[:, 0]))[1:]) + azimuths -= np.abs(strike_angle) if strike_angle else 0.0 + + # Assume simulations are West to East + arg_center = int(np.median(indices)) + local_xyz = ( + self.params.survey.vertices[indices] + - self.params.survey.vertices[arg_center, :] + ) + local_xyz[:, 2] = ( + self.params.survey.vertices[indices, 2] - self._drape_heights[indices] ) - local_polar[local_polar[:, 1] >= 180, 0] *= -1 # Wrap azimuths - - # Flip the line segment if the azimuth angle suggests the opposite direction - start_line = len(indices) // 2 - if np.median(local_polar[:start_line, 1]) < 180: - local_polar = local_polar[::-1, :] - - local_polar[:, 1] = ( - 0.0 if strike_angle is None else strike_angle - ) # Align azimuths to zero - - # Convert to polar coordinates (distance, azimuth, height) - query_polar = cartesian_to_polar(self._template.vertices) - query_polar[query_polar[:, 1] >= 180, 0] *= -1 - query_polar[:, 1] = query_polar[:, 1] % 180 # Wrap azimuths - - # Get the 8 nearest neighbors in the simulation to each observation point - sim_tree = cKDTree(query_polar) - rad, inds = sim_tree.query(local_polar, k=16) - inds = np.minimum(query_polar.shape[0] - 1, inds) + local_xyz = rotate_xyz(local_xyz, [0, 0, 0], -azimuths) + + # Get polar coordinates + local_polar = cartesian_to_polar(local_xyz) + local_polar[local_polar[:, 1] > 180, 0] *= -1 + local_polar[local_polar[:, 1] > 180, 1] -= 180 + # Transform azimuth to arc-lengths + local_polar[:, 1] = np.abs( + local_polar[:, 0] * np.deg2rad(90 - local_polar[:, 1]) + ) + + # Get template polar coordinates + sim_polar = cartesian_to_polar(self._template.vertices) + sim_polar[sim_polar[:, 1] > 180, 0] *= -1 + sim_polar[sim_polar[:, 1] > 180, 1] -= 180 + sim_polar[:, 1] = np.abs(sim_polar[:, 0] * np.deg2rad(90 - sim_polar[:, 1])) + + sim_tree = cKDTree(sim_polar) + rad, inds = sim_tree.query(local_polar, k=14) + inds = np.minimum(self._template.vertices.shape[0] - 1, inds) + return inverse_weighted_operator( rad.flatten(), inds.flatten(), - (local_polar.shape[0], self._template.vertices.shape[0]), - 2.0, - 1e-0, + (local_xyz.shape[0], self._template.vertices.shape[0]), + 1.0, + 1e-1, ) def run(self): @@ -318,22 +375,36 @@ def run(self): ) with Workspace(self.params.simulation_files[best], mode="r") as ws: survey = fetch_survey(ws) + ui_json = survey.parent.parent.options + ui_json["geoh5"] = ws ifile = InputFile(ui_json=ui_json) - options = PlateSimulationOptions.build(ifile) - dir_correction = strike_angle[ii] + 180 if flip else strike_angle[ii] + # Avoid getting pydantic deprecation warnings from old PlateSimulations stored + with suppress_logging(): + options = PlateSimulationOptions.build(ifile) + dir_correction = strike_angle[ii] + 180 if flip else strike_angle[ii] + ind_center = int(centers[best]) plate = self._create_plate_from_parameters( - int(indices[int(centers[best])]), options.model, dir_correction + int(indices[ind_center]), options.model, dir_correction ) plate.name = f"Query [{ii}]" + figure = self.plot_figure( + self.params.survey.vertices[indices, :2], + survey, + observed[:, indices], + self._time_projection, + spatial_projection, + ind_center, + ) + plate.add_file(figure.getvalue(), name=f"profile_{plate.name}.png") names.append(self.params.simulation_files[best].name) results.append(scores[best]) - out = self.params.queries.copy(parent=self.params.out_group) + out = self.params.queries.copy(parent=self.out_group) out.add_data( { "file": { @@ -465,6 +536,37 @@ def fetch_survey(workspace: Workspace) -> AirborneTEMReceivers | None: return None +def get_normalized_predicted( + survey: AirborneTEMReceivers, spatial_projection, time_projection, threshold +) -> np.ndarray: + """ + Retrieve, interpolate and normalize predicted data stored on survey. + interpolate and normalize the data + + :param survey: AirborneTEMReceivers entity + :param spatial_projection: Spatial interpolation matrix for the current query. + :param time_projection: Time interpolation matrix for the current query. + :param threshold: Percentile threshold for symlog normalization. + + :return: Normalized predicted data + """ + data_entity = survey.get_entity("Iteration_0_vertical")[0] + + if data_entity is None: + data_entity = survey.get_entity("Iteration_0_z")[0] + + simulated = get_data_array(data_entity) + + pred = time_projection @ (spatial_projection @ simulated.T).T + scale = threshold / np.min(np.abs(pred[0, :])) + pred = normalized_data(pred, scale=scale, threshold=threshold) + + # Smooth out the spatial interpolation + pred = ndimage.convolve1d(pred, np.ones(4) / 4, axis=1) + + return pred + + def batch_files_score( files: Path | list[Path], spatial_projection, time_projection, observed ) -> list[tuple[float, int]]: @@ -486,8 +588,8 @@ def batch_files_score( if isinstance(files, Path): files = [files] - max_late_val = np.max(np.abs(observed[-1, :])) - data = normalized_data(observed, threshold=max_late_val) + in_early_val = np.minimum(np.abs(observed[0, :]), 1e-20) + data = normalized_data(observed, threshold=in_early_val) for sim_file in files: with Workspace(sim_file, mode="r") as ws: @@ -497,22 +599,13 @@ def batch_files_score( logger.warning("No survey found in %s, skipping.", sim_file) continue - data_entity = survey.get_entity("Iteration_0_vertical")[0] - - if data_entity is None: - data_entity = survey.get_entity("Iteration_0_z")[0] - - simulated = get_data_array(data_entity) - - pred = time_projection @ (spatial_projection @ simulated.T).T - scale = max_late_val / np.max(np.abs(pred[-1, :])) - pred = normalized_data(pred, scale=scale, threshold=max_late_val) - + pred = get_normalized_predicted( + survey, spatial_projection, time_projection, in_early_val + ) score = 0.0 indices = [] # Metric: normalized cross-correlation for obs, pre in zip(data, pred, strict=True): - # Scale pre on obs # Full cross-correlation corr = signal.correlate(obs, pre, mode="same") # Normalize by energy to get correlation coefficient in [-1, 1] @@ -522,7 +615,7 @@ def batch_files_score( else: corr_norm = corr / denom - score += np.linalg.norm(obs - pre) + score += np.linalg.norm(obs - pre) / np.linalg.norm(obs) indices.append(np.argmax(corr_norm)) scores.append((score, np.median(indices))) diff --git a/simpeg_drivers/plate_simulation/match/options.py b/simpeg_drivers/plate_simulation/match/options.py index 90a655ff..e589a7fe 100644 --- a/simpeg_drivers/plate_simulation/match/options.py +++ b/simpeg_drivers/plate_simulation/match/options.py @@ -8,12 +8,11 @@ # ' # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -import itertools from pathlib import Path from typing import ClassVar -import numpy as np from geoapps_utils.base import Options +from geoapps_utils.utils.importing import GeoAppsError from geoh5py.data import FloatData from geoh5py.groups import PropertyGroup, SimPEGGroup from geoh5py.objects import Grid2D, Points @@ -57,6 +56,10 @@ def simulation_files(self) -> list[Path]: """Path to simulation files directory.""" sim_dir = self.geoh5.h5file.parent / self.simulations simulation_files = [] + + if not sim_dir.exists(): + raise GeoAppsError("Simulation directory not found. Please revise.") + for file in sim_dir.iterdir(): if Path(file).resolve().suffix == ".geoh5": simulation_files.append(Path(file)) diff --git a/tests/plate_simulation/runtest/match_test.py b/tests/plate_simulation/runtest/match_test.py index 956c492d..3841b267 100644 --- a/tests/plate_simulation/runtest/match_test.py +++ b/tests/plate_simulation/runtest/match_test.py @@ -7,6 +7,7 @@ # (see LICENSE file at the root of this source code package). ' # ' # ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +import logging import shutil from pathlib import Path @@ -15,6 +16,7 @@ from geoapps_utils.utils.importing import GeoAppsError from geoapps_utils.utils.transformations import rotate_xyz from geoh5py import Workspace +from geoh5py.data import FilenameData from geoh5py.groups import PropertyGroup, SimPEGGroup from geoh5py.objects import Points from geoh5py.ui_json import InputFile @@ -26,7 +28,11 @@ TDEMForwardOptions, ) from simpeg_drivers.plate_simulation.driver import PlateSimulationDriver -from simpeg_drivers.plate_simulation.match.driver import PlateMatchDriver, fetch_survey +from simpeg_drivers.plate_simulation.match.driver import ( + PlateMatchDriver, + fetch_survey, + suppress_logging, +) from simpeg_drivers.plate_simulation.match.options import PlateMatchOptions from simpeg_drivers.plate_simulation.options import PlateSimulationOptions from simpeg_drivers.utils.synthetics.driver import ( @@ -52,7 +58,7 @@ def generate_example(geoh5: Workspace, n_grid_points: int, refinement: tuple[int topography=lambda x, y: np.zeros(x.shape), ), mesh=MeshOptions(refinement=refinement), - model=ModelOptions(background=0.001), + model=ModelOptions(background=0.0001, anomaly=10), ) components = SyntheticsComponents(geoh5, options=opts) vals = components.survey.add_data( @@ -83,15 +89,20 @@ def test_file_parsing(tmp_path: Path): with get_workspace(tmp_path / f"{__name__}.geoh5") as geoh5: components = generate_example(geoh5, n_grid_points=3, refinement=(2,)) + options = PlateMatchOptions( geoh5=geoh5, survey=components.survey, data=components.property_group, queries=components.queries, topography_object=components.topography, - simulations=tmp_path, + simulations=tmp_path / "non_existing", ) + with pytest.raises(GeoAppsError, match="Simulation directory not found"): + _ = options.simulation_files + + options.simulations = tmp_path sim_files = options.simulation_files assert len(sim_files) == 1 assert sim_files[0].name == f"{__name__}.geoh5" @@ -142,8 +153,13 @@ def test_matching_driver(tmp_path: Path): ifile.data["simulation"] = fwr_driver.out_group plate_options = PlateSimulationOptions.build(ifile.data) - plate_options.model.overburden_options.thickness = 40.0 + plate_options.model.overburden_options.thickness = 25.0 + plate_options.model.overburden_options.overburden_property = 10000 plate_options.model.plate_options.geometry.dip_length = 300.0 + plate_options.model.plate_options.geometry.width = 50.0 + plate_options.model.plate_options.geometry.elevation = 50 + plate_options.model.plate_options.plate_property = 10 + plate_options.model.background = 10000 driver = PlateSimulationDriver(plate_options) driver.run() @@ -212,6 +228,20 @@ def test_matching_driver(tmp_path: Path): assert isinstance(results, Points) names = results.get_data("file")[0] - assert names.values[0] == file.stem + f"_[{4}].geoh5" + assert names.values[0] == file.stem + f"_[{1}].geoh5" + + plate = geoh5.get_entity("Query [0]")[0] + assert plate.geometry.dip_direction == 45.0 + assert isinstance(plate.get_entity("profile_Query [0].png")[0], FilenameData) + + +def test_suppress_logging_restores_disable_level(): + original_disable_level = logging.root.manager.disable + logging.disable(logging.ERROR) + try: + with suppress_logging(level=logging.WARNING): + assert logging.root.manager.disable == logging.WARNING - assert geoh5.get_entity("Query [0]")[0].geometry.dip_direction == 45.0 + assert logging.root.manager.disable == logging.ERROR + finally: + logging.disable(original_disable_level)