├── LICENSE ├── LICENSE.txt ├── Manifest.toml ├── Project.toml ├── README.md ├── doc └── documentation.pdf ├── examples ├── 01MT │ ├── mt1data.dat │ ├── paraMCMCScript.jl │ ├── plotMCMCResults.m │ ├── plotModel2dPPD.m │ ├── runMCMCScript.jl │ ├── runPTMCMCScript.jl │ ├── startupfile │ └── synmt1data.jl ├── 02TEM │ ├── paraMCMCScript.jl │ ├── plotMCMCResults.m │ ├── runMCMCScript.jl │ ├── runPTMCMCScript.jl │ ├── startupfile │ ├── syntem1data.jl │ └── tem1data.dat └── 03MCSEM │ ├── csem1data.dat │ ├── paraMCMCScript.jl │ ├── plotMCMCResults.m │ ├── runMCMCScript.jl │ ├── runPTMCMCScript.jl │ ├── startupfile │ └── syncsem1data.jl └── src ├── TBAnalysis └── TBAnalysis.jl ├── TBChain ├── TBChain.jl ├── acceptanceRatio.jl ├── parallelTempering.jl └── runMCMC.jl ├── TBFileIO ├── TBFileIO.jl ├── readEMData.jl ├── readstartupFile.jl └── writeEMData.jl ├── TBFwdSolver ├── TBFwdSolver.jl ├── compCSEMRespData.jl ├── compMTRespData.jl ├── compTEMRespData.jl └── deps │ ├── build.jl │ ├── lib │ ├── em1dmod.dll │ └── em1dmod.so │ └── src │ ├── Dipole1D.f90 │ ├── FilterModules.f90 │ ├── callDipole1D.f90 │ ├── comptem1d.f90 │ ├── mt1dmod.f90 │ └── tem1dmod.f90 ├── TBStruct └── TBStruct.jl ├── TBUtility └── TBUtility.jl └── TransdEM.jl /Manifest.toml: -------------------------------------------------------------------------------- 1 | [[Base64]] 2 | uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" 3 | 4 | [[BenchmarkTools]] 5 | deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"] 6 | git-tree-sha1 = "c31ebabde28d102b602bada60ce8922c266d205b" 7 | uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" 8 | version = "1.1.1" 9 | 10 | [[BinDeps]] 11 | deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] 12 | git-tree-sha1 = "1289b57e8cf019aede076edab0587eb9644175bd" 13 | uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" 14 | version = "1.0.2" 15 | 16 | [[CodeTracking]] 17 | deps = ["InteractiveUtils", "UUIDs"] 18 | git-tree-sha1 = "9aa8a5ebb6b5bf469a7e0e2b5202cf6f8c291104" 19 | uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" 20 | version = "1.0.6" 21 | 22 | [[Dates]] 23 | deps = ["Printf"] 24 | uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" 25 | 26 | [[Distributed]] 27 | deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] 28 | uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" 29 | 30 | [[DistributedArrays]] 31 | deps = ["Distributed", "LinearAlgebra", "Primes", "Random", "Serialization", "SparseArrays", "Statistics"] 32 | git-tree-sha1 = "ab2f313a5ef4db9d127b8fae4d922e268db1552f" 33 | uuid = "aaf54ef3-cdf8-58ed-94cc-d582ad619b94" 34 | version = "0.6.5" 35 | 36 | [[FileWatching]] 37 | uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" 38 | 39 | [[InteractiveUtils]] 40 | deps = ["LinearAlgebra", "Markdown"] 41 | uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" 42 | 43 | [[JSON]] 44 | deps = ["Dates", "Mmap", "Parsers", "Unicode"] 45 | git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37" 46 | uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" 47 | version = "0.21.2" 48 | 49 | [[JuliaInterpreter]] 50 | deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] 51 | git-tree-sha1 = "76112882a210564b616dbdf371fc0fbbae28d31f" 52 | uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" 53 | version = "0.8.19" 54 | 55 | [[LibGit2]] 56 | uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" 57 | 58 | [[Libdl]] 59 | uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" 60 | 61 | [[LinearAlgebra]] 62 | deps = ["Libdl"] 63 | uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 64 | 65 | [[Logging]] 66 | uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" 67 | 68 | [[LoweredCodeUtils]] 69 | deps = ["JuliaInterpreter"] 70 | git-tree-sha1 = "4bfb8b57df913f3b28a6bd3bdbebe9a50538e689" 71 | uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" 72 | version = "2.1.0" 73 | 74 | [[Markdown]] 75 | deps = ["Base64"] 76 | uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" 77 | 78 | [[Mmap]] 79 | uuid = "a63ad114-7e13-5084-954f-fe012c677804" 80 | 81 | [[OrderedCollections]] 82 | git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" 83 | uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" 84 | version = "1.4.1" 85 | 86 | [[Parsers]] 87 | deps = ["Dates"] 88 | git-tree-sha1 = "477bf42b4d1496b454c10cce46645bb5b8a0cf2c" 89 | uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" 90 | version = "2.0.2" 91 | 92 | [[Pkg]] 93 | deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] 94 | uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" 95 | 96 | [[Primes]] 97 | deps = ["Test"] 98 | git-tree-sha1 = "ff1a2323cb468ec5f201838fcbe3c232266b1f95" 99 | uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" 100 | version = "0.4.0" 101 | 102 | [[Printf]] 103 | deps = ["Unicode"] 104 | uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" 105 | 106 | [[REPL]] 107 | deps = ["InteractiveUtils", "Markdown", "Sockets"] 108 | uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" 109 | 110 | [[Random]] 111 | deps = ["Serialization"] 112 | uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 113 | 114 | [[Requires]] 115 | deps = ["UUIDs"] 116 | git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" 117 | uuid = "ae029012-a4dd-5104-9daa-d747884805df" 118 | version = "1.1.3" 119 | 120 | [[Revise]] 121 | deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"] 122 | git-tree-sha1 = "cc560140bd7d171f351a148dc81a634a32c48c6b" 123 | uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" 124 | version = "3.1.18" 125 | 126 | [[SHA]] 127 | uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" 128 | 129 | [[Serialization]] 130 | uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" 131 | 132 | [[Sockets]] 133 | uuid = "6462fe0b-24de-5631-8697-dd941f90decc" 134 | 135 | [[SparseArrays]] 136 | deps = ["LinearAlgebra", "Random"] 137 | uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" 138 | 139 | [[Statistics]] 140 | deps = ["LinearAlgebra", "SparseArrays"] 141 | uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 142 | 143 | [[Test]] 144 | deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] 145 | uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 146 | 147 | [[URIParser]] 148 | deps = ["Unicode"] 149 | git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d" 150 | uuid = "30578b45-9adc-5946-b283-645ec420af67" 151 | version = "0.4.1" 152 | 153 | [[UUIDs]] 154 | deps = ["Random"] 155 | uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" 156 | 157 | [[Unicode]] 158 | uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" 159 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" 3 | BinDeps = "9e28174c-4ba2-5203-b857-d8d62c4213ee" 4 | DistributedArrays = "aaf54ef3-cdf8-58ed-94cc-d582ad619b94" 5 | Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" 6 | Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TransdEM 2 | 3 | **TransdEM** is a package for transdimensional Bayesian inversion of electromagnetic data in layered media written in the [Julia language](http://julialang.org). 4 | 5 | For the details regarding the algorithm and implementation, please refer to: 6 | > Peng, R., B. Han, Y. Liu and X. Hu, 2022, A Julia software package for transdimensional Bayesian inversion of 7 | electromagnetic data over horizontally stratified media: Geophysics, 87(5); DOI: 8 | > [10.1190/GEO2021-0534.1](https://library.seg.org/doi/10.1190/geo2021-0534.1). 9 | 10 | * Authors: [Ronghua Peng](https://github.com/prhjiajie), [Bo Han](https://github.com/hanbo1735), [Yajun Liu](https://github.com/liuyajun7112) and Xiangyun Hu (China University of Geosciences (Wuhan)). 11 | 12 | 13 | ## License 14 | 15 | The **TransdEM** package is licensed under the [GNU General Public License](http://www.gnu.org/licenses/). 16 | 17 | 18 | ## File structure 19 | * **./doc** : an instruction of file format. 20 | 21 | * **./examples** : contains subdirectories corresponding to different types of synthetic examples, including all those presented in the manuscript. 22 | 23 | * **./src** : source code. 24 | 25 | ## Installation of Julia 26 | TransdEM is compatible with Julia v0.7 and later versions. We recommend to install v1.0.5, the long-term support (LTS) release. 27 | 28 | ### Windows systems 29 | Go to the [Julia download page](https://julialang.org/downloads/) to download the Windows command line version (.exe) and install it. 30 | 31 | ### Linux systems 32 | Although Julia is a cross-platform language, we strongly recommend to run **TransdEM** under Linux rather than Windows. This is because some of the third-party packages utilized by TransdEM such as **Dipole1D** are more straightforward to complie under Linux. 33 | 34 | There are three ways to install Julia on Linux: 35 | 36 | * **Using precompiled binaries (recommended)**. Go to the [Julia download page](https://julialang.org/downloads/) to download the generic Linux binaries (.tar.gz file). 37 | Then make sure that the Julia executable is visible for your system. To do this, first extract the .tar.gz file to a folder on your computer. 38 | Then you can either add Julia’s bin folder to your system PATH environment variable, or create a symbolic link to julia inside a folder which 39 | is on your system PATH, for example, by using the following command: 40 | 41 | `sudo ln -s /bin/julia /usr/local/bin/julia` 42 | 43 | 44 | * **Compiling from source**. Assume that Git has been installed already, then we can grab the Julia sources from GitHub by using the following command: 45 | 46 | `git clone git://github.com/JuliaLang/julia.git` 47 | 48 | This will download the Julia source code into a julia directory in the current folder. The Julia building process needs the GNU compilation tools g++, gfortran, and m4, so make sure that you have installed them. Now go to the Julia folder and start the compilation process as follows: 49 | 50 | `cd julia` 51 | 52 | `make` 53 | 54 | 55 | * **Using PPA for Ubuntu Linux**. Particularly, for Ubuntu systems (Version 12.04 or later), there is a Personal Package Archive (PPA) for Julia 56 | that makes the installation painless. All you need to do to get the stable version is to issue the following commands in a terminal session: 57 | 58 | `sudo add-apt-repository ppa:staticfloat/juliareleases` 59 | 60 | `sudo add-apt-repository ppa:staticfloat/julia-deps` 61 | 62 | `sudo apt-get update` 63 | 64 | `sudo apt-get install julia` 65 | 66 | After a successful installation, Julia can be started by double-clicking the Julia executable (on Windows) or typing `julia` from the command line (on Linux). Following is the Julia's command line environment (the so-called REPL): 67 | 68 | 69 | ```jl 70 | _ _ _(_)_ | 71 | (_) | (_) (_) | Documentation: https://docs.julialang.org 72 | _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help. 73 | | | | | | | |/ _` | | 74 | | | |_| | | | (_| | | Version 1.0.5 (2019-09-09) 75 | _/ |\__'_|_|_|\__'_| | Official http://julialang.org/ release 76 | |__/ | 77 | 78 | julia> 79 | ``` 80 | 81 | ## Installation of the TransdEM package 82 | ### Setting up the package environment 83 | **TransdEM** depends on several external packages (the so-called dependencies) which are not shipped with the package .zip file. These dependencies can be automatically resolved by activating and instantiating the package environment through [Julia's package manager (Pkg)](https://julialang.github.io/Pkg.jl/v1/). Go to the package directory, for example: 84 | `cd /home/username/code/TransdEM` 85 | 86 | , and then enter the Julia REPL. Then press `]` from the Julia REPL you will enter the Pkg REPL which looks like 87 | ```jl 88 | (v1.0) pkg> 89 | ``` 90 | 91 | , indicating that you are currently in the environment named v1.0, Julia 1.0's default environment. To switch to the package environment, just `activate` the current directory: 92 | ```jl 93 | (v1.0) pkg> activate . 94 | ``` 95 | you will get: 96 | ```jl 97 | (TransdEM) pkg> 98 | ``` 99 | indicating that you are in the environment TransdEM. The environment will not be well-configured until you `instantiate` it: 100 | ```jl 101 | (TransdEM) pkg> instantiate 102 | ``` 103 | . By doing so the dependencies listed in `Project.toml` and `Manifest.toml` can be automatically downloaded and installed. 104 | 105 | To get back to Julia REPL from Pkg REPL, press `backspace` or `^C`. 106 | 107 | ### Building the em1dmod library 108 | **TransdEM** contains several Fortran codes that are used to compute 1D MT/CSEM/TEM responses, and they must be compiled to generate a shared library (.so, on Linux) or dynamic link library (.dll, on Windows) called `em1dmod` prior to running **TransdEM**. Suppose that you have already installed the GNU fortran compiler (gfortran) in your computer system, all you need to do is going to the subdirectory of the `TBFwdSolver` module and "including" the script *build.jl*, the Julia REPL commands of which are like: 109 | ```jl 110 | julia> cd("/home/username/code/TransdEM/src/TBFwdSolver/deps") 111 | julia> include("build.jl") 112 | ``` 113 | If no error is reported during the building process, the library file should be generated and placed at the directory like “D:\code\TransdEM\src\TBFwdSolver\deps\lib”. 114 | 115 | For installing gfortran, on Windows systems we recommend to install [MinGW](https://en.wikipedia.org/wiki/MinGW/), particularly [Mingw-w64](https://www.mingw-w64.org/) for 64 bits systems. The windows installer (.exe) can be downloaded from https://sourceforge.net/projects/mingw-w64. After installing, you need to edit the `PATH` variable. You can access the System Control Center by pressing **Windows Key + Pause**. In the System window, click **Advanced System Settings $\rightarrow$ Advanced (tab) $\rightarrow$ Environment Variables**. For Windows 10, a quick access is to enter "Edit the system environment variables" in the Start Search of Windows and click the button "Environment Variables". Change the `PATH` variable (double-click on it or Select and **Edit**), and add the path where your MinGW-w64 has been installed to e.g., `C:\mingw\mingw64\bin`. This folder should contain a number of .exe-files that you can see in your explorer. To check that your Mingw-w64-gfortran is correctly installed and available, enter the shell mode by pressing ";" from the Julia REPL and type `gfortran --version`, the expected output looks like 116 | ```jl 117 | shell> gfortran --version 118 | GNU Fortran (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0 119 | Copyright (C) 2018 Free Software Foundation, Inc. 120 | This is free software; see the source for copying conditions. There is NO 121 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 122 | ``` 123 | ## Running the code 124 | ### Running single MCMC sampling 125 | * First, you need to let the **TransdEM** package to be "loaded" by the current Julia environment. This is done by adding the parent directory of the package directory to `LOAD_PATH`, a global environment variable of Julia. For example, the TransdEM package is placed at `home/username/code` on Linux or at `D:\\code` on Windows, then type the following command from the Julia REPL: 126 | 127 | ```jl 128 | julia> push!(LOAD_PATH,"/home/username/code") 129 | ``` 130 | 131 | on Linux, or 132 | 133 | ```jl 134 | julia> push!(LOAD_PATH,"D:\\code") 135 | ``` 136 | 137 | on Windows. 138 | 139 | 140 | * Then go to the directory where the running script loaded, and run the script by typing the following command (for example) from the Julia REPL: 141 | 142 | ```jl 143 | julia> include("runMCMCScript.jl") 144 | ``` 145 | 146 | ### Running parallel MCMC sampling 147 | To perform parallel MCMC sampling, call the parallel MCMC sampling function **parallelMCMCsampling** instead of the single MCMC sampling **runMCMC** (please refer to the `paraMCMCScript.jl` scripts within the `examples` directory). 148 | 149 | * first you need to launch multiple worker processes by either starting Julia like 150 | 151 | `shell> julia -p 12` 152 | 153 | or adding processes within Julia (recommended) like 154 | 155 | `julia> addprocs(12)` 156 | 157 | * Then you need to get the **TransdEM** package to be "loaded" on all processes by following command from the Julia REPL: 158 | 159 | ```jl 160 | julia> @everywhere push!(LOAD_PATH,"/home/username/code") 161 | ```` 162 | 163 | * Finally, go to the directory where the running script loaded, and run the script by typing the following command (for example) from the Julia REPL: 164 | 165 | ```jl 166 | julia> include("paraMCMCScript.jl") 167 | ``` 168 | 169 | ### Running MCMC sampling with parallel tempering 170 | In order to accelerate convergence of the Markov chains, a powerful technique known as parallel tempering can be used. To perform MCMC sampling with parallel tempering, call the parallel tempered MCMC sampling function **runTemperedMCMC** instead of the parallel MCMC sampling function **parallelMCMCsampling** (please refer to the `runPTMCMCScript.jl` scripts within the `examples` directory). 171 | * first you need to launch multiple worker processes by either starting Julia like 172 | 173 | `shell> julia -p 6` 174 | 175 | or adding processes within Julia (recommended) like 176 | 177 | `julia> addprocs(6)` 178 | Note that the number of processes invoked should be equal or larger than the number of parallel tempered Markov chains. 179 | * Then you need to get the **TransdEM** package to be "loaded" on all processes by following command from the Julia REPL: 180 | 181 | ```jl 182 | julia> @everywhere push!(LOAD_PATH,"/home/username/code") 183 | ```` 184 | 185 | * Finally, go to the directory where the running script loaded, and run the script by typing the following command (for example) from the Julia REPL: 186 | 187 | ```jl 188 | julia> include("runPTMCMCScript.jl") 189 | ``` 190 | 191 | ### Writing a running script 192 | For each numerical example contained in the directory `./examples`, one or more running scripts named `runMCMCScript.jl`, `paraMCMCScript.jl` or `runPTMCMCScript.jl` have been provided. These scripts are well documented. A user can modify them to get his/her own. 193 | -------------------------------------------------------------------------------- /doc/documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CUG-EMI/TransdEM/4932e7e178b48f4a46f27e75fb2cd904e8a7b5ed/doc/documentation.pdf -------------------------------------------------------------------------------- /examples/01MT/mt1data.dat: -------------------------------------------------------------------------------- 1 | Format: MTData_1.0 2 | # file generated in Wed Dec 29 16:18:39 2021 3 | Frequencies (Hz): 40 4 | 3.2000e+02 5 | 2.4667e+02 6 | 1.9015e+02 7 | 1.4657e+02 8 | 1.1299e+02 9 | 8.7095e+01 10 | 6.7137e+01 11 | 5.1752e+01 12 | 3.9893e+01 13 | 3.0751e+01 14 | 2.3705e+01 15 | 1.8273e+01 16 | 1.4085e+01 17 | 1.0858e+01 18 | 8.3696e+00 19 | 6.4517e+00 20 | 4.9733e+00 21 | 3.8336e+00 22 | 2.9551e+00 23 | 2.2780e+00 24 | 1.7560e+00 25 | 1.3536e+00 26 | 1.0434e+00 27 | 8.0430e-01 28 | 6.1999e-01 29 | 4.7792e-01 30 | 3.6840e-01 31 | 2.8398e-01 32 | 2.1891e-01 33 | 1.6874e-01 34 | 1.3008e-01 35 | 1.0027e-01 36 | 7.7292e-02 37 | 5.9580e-02 38 | 4.5927e-02 39 | 3.5403e-02 40 | 2.7290e-02 41 | 2.1037e-02 42 | 1.6216e-02 43 | 1.2500e-02 44 | DataType: 2 45 | realZ 46 | imagZ 47 | Data Block: 80 48 | # FreqNo. DataType Value Error 49 | 1 1 5.867024e-01 2.901863e-02 50 | 1 2 5.747248e-01 3.082017e-02 51 | 2 1 4.947074e-01 2.458978e-02 52 | 2 2 5.501747e-01 2.818401e-02 53 | 3 1 3.797326e-01 2.031061e-02 54 | 3 2 4.846002e-01 2.553323e-02 55 | 4 1 3.379323e-01 1.638001e-02 56 | 4 2 4.173906e-01 2.276581e-02 57 | 5 1 2.430892e-01 1.296878e-02 58 | 5 2 3.884621e-01 1.992046e-02 59 | 6 1 2.100409e-01 1.015856e-02 60 | 6 2 3.134945e-01 1.711580e-02 61 | 7 1 1.519949e-01 7.936836e-03 62 | 7 2 2.949539e-01 1.447906e-02 63 | 8 1 1.160280e-01 6.229429e-03 64 | 8 2 2.438651e-01 1.210163e-02 65 | 9 1 9.727230e-02 4.938348e-03 66 | 9 2 1.922845e-01 1.002872e-02 67 | 10 1 8.645463e-02 3.965987e-03 68 | 10 2 1.713901e-01 8.267599e-03 69 | 11 1 6.042424e-02 3.226793e-03 70 | 11 2 1.358067e-01 6.798643e-03 71 | 12 1 5.002634e-02 2.653626e-03 72 | 12 2 1.048465e-01 5.585200e-03 73 | 13 1 4.312227e-02 2.200346e-03 74 | 13 2 8.905752e-02 4.584240e-03 75 | 14 1 3.581122e-02 1.838752e-03 76 | 14 2 7.162927e-02 3.756190e-03 77 | 15 1 3.232366e-02 1.551504e-03 78 | 15 2 6.067317e-02 3.069819e-03 79 | 16 1 2.808390e-02 1.326096e-03 80 | 16 2 5.143939e-02 2.501690e-03 81 | 17 1 2.133641e-02 1.152119e-03 82 | 17 2 4.209893e-02 2.033965e-03 83 | 18 1 2.118898e-02 1.020107e-03 84 | 18 2 3.191625e-02 1.652859e-03 85 | 19 1 1.688520e-02 9.207131e-04 86 | 19 2 2.524247e-02 1.347275e-03 87 | 20 1 1.675926e-02 8.445674e-04 88 | 20 2 2.170781e-02 1.107341e-03 89 | 21 1 1.691971e-02 7.828112e-04 90 | 21 2 1.877861e-02 9.234194e-04 91 | 22 1 1.477623e-02 7.277717e-04 92 | 22 2 1.527141e-02 7.857689e-04 93 | 23 1 1.312223e-02 6.734417e-04 94 | 23 2 1.498300e-02 6.846174e-04 95 | 24 1 1.260314e-02 6.157472e-04 96 | 24 2 1.253117e-02 6.103849e-04 97 | 25 1 1.115360e-02 5.527009e-04 98 | 25 2 1.126701e-02 5.539542e-04 99 | 26 1 9.793420e-03 4.845515e-04 100 | 26 2 1.053072e-02 5.070546e-04 101 | 27 1 8.582167e-03 4.138131e-04 102 | 27 2 9.192596e-03 4.629707e-04 103 | 28 1 6.986966e-03 3.447061e-04 104 | 28 2 8.980410e-03 4.175247e-04 105 | 29 1 5.578386e-03 2.817561e-04 106 | 29 2 7.122615e-03 3.696445e-04 107 | 30 1 4.789219e-03 2.281775e-04 108 | 30 2 5.359435e-03 3.208113e-04 109 | 31 1 4.111773e-03 1.850603e-04 110 | 31 2 5.580578e-03 2.736126e-04 111 | 32 1 3.175557e-03 1.516633e-04 112 | 32 2 4.479938e-03 2.303580e-04 113 | 33 1 2.449806e-03 1.262691e-04 114 | 33 2 3.581587e-03 1.924053e-04 115 | 34 1 2.276189e-03 1.069598e-04 116 | 34 2 3.049508e-03 1.601507e-04 117 | 35 1 1.839517e-03 9.205469e-05 118 | 35 2 2.781402e-03 1.333228e-04 119 | 36 1 1.566850e-03 8.025712e-05 120 | 36 2 2.209317e-03 1.113006e-04 121 | 37 1 1.454002e-03 7.063953e-05 122 | 37 2 1.752213e-03 9.334477e-05 123 | 38 1 1.210000e-03 6.256955e-05 124 | 38 2 1.561775e-03 7.873095e-05 125 | 39 1 1.129803e-03 5.562909e-05 126 | 39 2 1.336022e-03 6.681430e-05 127 | 40 1 9.943438e-04 4.954670e-05 128 | 40 2 1.158041e-03 5.705114e-05 129 | -------------------------------------------------------------------------------- /examples/01MT/paraMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling in parallel 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | 16 | #------------------------------------------------------------------------------ 17 | @everywhere begin 18 | printstyled("read datafile and startupfile ...\n", color=:blue) 19 | startup = "startupfile" 20 | (emData,mclimits) = readstartupFile(startup) 21 | end 22 | 23 | # 24 | printstyled("perform MCMC sampling ...\n", color=:blue) 25 | pids = workers() 26 | (results, status) = parallelMCMCsampling(mclimits, emData, pids) 27 | 28 | # 29 | printstyled("Output ensemble results ...\n", color=:blue) 30 | nData = length(emData.obsData) 31 | multipleStatistics(results, mclimits, "mt") 32 | 33 | # 34 | printstyled("extract model parameters from the ensemble ...\n", color=:blue) 35 | nLayer = 5 36 | extractModelParam(results, mclimits, nLayer) 37 | 38 | println("===============================") 39 | # 40 | -------------------------------------------------------------------------------- /examples/01MT/plotMCMCResults.m: -------------------------------------------------------------------------------- 1 | % function plotMCMCResults 2 | % 3 | %-------------------------------------------------------------------------- 4 | surveytype = 'mt'; 5 | resultfile = ['posteriorModel-',surveytype,'.dat']; 6 | fid = fopen(resultfile, 'r'); 7 | tmp = textscan(fid,'%f %f %f %f %f %f %f','HeaderLines',1); 8 | fclose(fid); 9 | results.mean = 10 .^ tmp{2}; 10 | results.median = 10 .^ tmp{3}; 11 | results.mode = 10 .^ tmp{4}; 12 | results.credmin = 10 .^ tmp{5}; 13 | results.credmax = 10 .^ tmp{6}; 14 | results.std = 10 .^ tmp{7}; 15 | fclose(fid); 16 | 17 | %% 18 | m2km = 0.001; 19 | depthBins = 10 .^ load(['depthBins-',surveytype,'.dat']); 20 | rhoBins = 10 .^ load(['rhoBins-',surveytype,'.dat']); 21 | 22 | % histogram of number of layers 23 | tmp = load(['nlayerHistogram-',surveytype,'.dat']); 24 | nlayerHist = tmp(:,2); 25 | 26 | % histogram of interface depths 27 | depthHist = load(['depthHistogram-',surveytype,'.dat']); 28 | 29 | % PPD of resistivity with depth 30 | depthrhoHist = load(['depthrhoHistogram-',surveytype,'.dat']); 31 | 32 | %% set plot range 33 | depthplotlim = [0 2e4]*m2km; 34 | rhoplotlim = [0.1 1e4]; 35 | depthBins = depthBins * m2km; 36 | 37 | %% plot figures 38 | figure(1); 39 | fontsize = 11; 40 | set(gcf,'Units','centimeters'); 41 | set(gcf,'Position',[10 2 30 14]); 42 | 43 | %% PPD of the resistivity 44 | subplot(1,4,1:2); 45 | nsample = sum(depthrhoHist(:,1)); 46 | depthrhoHist = depthrhoHist / nsample; 47 | pch = pcolor(rhoBins, depthBins, log10(depthrhoHist')); 48 | set(pch, 'EdgeColor', 'none') 49 | hold on 50 | cm = colormap('jet'); 51 | cm(1:15,:) = []; 52 | 53 | colormap(cm); 54 | c = colorbar; 55 | c.Location = 'westoutside'; 56 | c.Label.String = 'log_{10}Probability'; 57 | c.Label.FontSize = 10; 58 | caxis([-3 0]); 59 | 60 | linewidth = 1.2; 61 | semilogx(results.mean, depthBins, 'b-', 'LineWidth', linewidth); 62 | hold on 63 | semilogx(results.median, depthBins, 'r-', 'LineWidth', linewidth); 64 | semilogx(results.mode, depthBins, 'k-', 'LineWidth', linewidth); 65 | semilogx(results.credmin, depthBins, 'm-', 'LineWidth', linewidth); 66 | semilogx(results.credmax, depthBins, 'm-', 'LineWidth', linewidth); 67 | 68 | set(gca,'ydir','reverse','xscale','log','layer','top','box','on'); 69 | xlim(rhoplotlim); 70 | ylim(depthplotlim); 71 | xlabel('Resistivity [\Omegam]','Fontsize',fontsize); 72 | ylabel('Depth [km]','Fontsize',fontsize); 73 | 74 | 75 | %% histgram for the interface depths 76 | subplot(1,4,3); 77 | totalayer = sum(depthHist); 78 | dtmp = depthHist / totalayer; 79 | fill([0;dtmp;0],[0;depthBins;50],'b'); 80 | box on; grid on; 81 | xlim( [0 0.1]); 82 | ylim(depthplotlim); 83 | set(gca,'ydir','reverse'); 84 | xlabel('Interface Probability','Fontsize',fontsize); 85 | % 86 | a = [0;dtmp;0]; 87 | b = [0;depthBins;50]; 88 | interfaceHist = [a b]; 89 | 90 | %% histogram for the number of layers 91 | % 92 | subplot(1,4,4); 93 | bar(nlayerHist/nsample); 94 | box on; 95 | set(gca,'layer','top'); 96 | ylim([0 0.4]); 97 | xlabel('Number of Layers','Fontsize',fontsize); 98 | ylabel('Probability','Fontsize',fontsize); 99 | -------------------------------------------------------------------------------- /examples/01MT/plotModel2dPPD.m: -------------------------------------------------------------------------------- 1 | % function plotModel2dPPD 2 | % 3 | %% load data 4 | modparam = load('modelParam_5layer.dat'); 5 | 6 | %% compute thickness of each layer 7 | nsample = size(modparam, 1); 8 | depth1d = 10.^modparam(:,6:end); 9 | thick1d = [10.^modparam(:,6) diff(depth1d,1,2)]; 10 | thick1d = log10(thick1d); 11 | 12 | %% plot 13 | hf = figure(1); 14 | set(gcf,'Units','centimeters'); 15 | set(gcf,'Position',[8 2 24 16]); 16 | nrow = 2; 17 | ncol = 2; 18 | mksize = 10; 19 | linew = 2; 20 | capfs = 11; 21 | axefs = 11; 22 | % 23 | k = 1; 24 | subaxis(nrow,ncol,k,'ML',0.07,'MT',0.03,'MB',0.2,'MR',0.03); 25 | rhotmp = modparam(:,k); 26 | deptmp = modparam(:,k+5); 27 | thktmp = thick1d(:,k); 28 | h = histogram2(rhotmp,thktmp,'DisplayStyle','tile','ShowEmptyBins','off'); 29 | rhoRange = 2.0:0.02:3; 30 | depRange = 2.4:0.02:3; 31 | h.XBinEdges = rhoRange; 32 | h.YBinEdges = depRange; 33 | h.Normalization = 'probability'; 34 | set(h, 'EdgeColor', 'none') 35 | ch = colorbar; 36 | % ch.Label.String = 'Probability'; 37 | ch.Label.FontSize = 11; 38 | ch.FontSize = 11; 39 | 40 | caxis([0 0.05]) 41 | % xlabel('log_{10}Resistivity [\Omegam]','Fontsize',capfs); 42 | ylabel('log_{10}Thickness [m]','Fontsize',capfs); 43 | rho01 = log10(250); 44 | dep01 = log10(600); 45 | hold on 46 | plot(rho01,dep01,'rx','MarkerSize',mksize,'LineWidth',linew); 47 | set(gca,'layer','top','Fontsize',axefs); 48 | 49 | text(2.03,2.96,'(a) first layer','Fontsize',13); 50 | 51 | % 52 | k = 2; 53 | subaxis(nrow,ncol,k,'ML',0.06); 54 | rhotmp = modparam(:,k); 55 | deptmp = modparam(:,k+5); 56 | thktmp = thick1d(:,k); 57 | h = histogram2(rhotmp,thktmp,'DisplayStyle','tile','ShowEmptyBins','off'); 58 | rhoRange = 1.1:0.01:1.7; 59 | depRange = 2.8:0.02:3.4; 60 | h.XBinEdges = rhoRange; 61 | h.YBinEdges = depRange; 62 | h.Normalization = 'probability'; 63 | set(h, 'EdgeColor', 'none'); 64 | ch = colorbar; 65 | % ch.Label.String = 'Probability'; 66 | ch.Label.FontSize = 11; 67 | ch.FontSize = 11; 68 | 69 | caxis([0 0.05]) 70 | % xlabel('log_{10}Resistivity [\Omegam]','Fontsize',capfs); 71 | % ylabel('log_{10}Depth [m]','Fontsize',capfs); 72 | rho02 = log10(25); 73 | dep02 = log10(1400); 74 | hold on 75 | plot(rho02,dep02,'rx','MarkerSize',mksize,'LineWidth',linew); 76 | set(gca,'layer','top','Fontsize',axefs); 77 | 78 | text(1.12,3.36,'(b) second layer','Fontsize',13); 79 | 80 | % 81 | k = 3; 82 | subaxis(nrow,ncol,k,'ML',0.07,'MT',0.02,'MB',0.1,'MR',0.03); 83 | rhotmp = modparam(:,k); 84 | deptmp = modparam(:,k+5); 85 | thktmp = thick1d(:,k); 86 | h = histogram2(rhotmp,thktmp,'DisplayStyle','tile','ShowEmptyBins','off'); 87 | rhoRange = 1.0:0.04:4.0; 88 | depRange = 3.2:0.02:4.2; 89 | h.XBinEdges = rhoRange; 90 | h.YBinEdges = depRange; 91 | h.Normalization = 'probability'; 92 | set(h, 'EdgeColor', 'none') 93 | ch = colorbar; 94 | % ch.Label.String = 'Probability'; 95 | ch.Label.FontSize = 11; 96 | ch.FontSize = 11; 97 | 98 | caxis([0 0.004]) 99 | xlabel('log_{10}Resistivity [\Omegam]','Fontsize',capfs); 100 | ylabel('log_{10}Thickness [m]','Fontsize',capfs); 101 | rho03 = log10(100); 102 | dep03 = log10(4000); 103 | hold on 104 | plot(rho03,dep03,'rx','MarkerSize',mksize,'LineWidth',linew); 105 | set(gca,'layer','top','Fontsize',axefs); 106 | 107 | text(1.1,4.15,'(c) third layer','Fontsize',13); 108 | 109 | % 110 | k = 4; 111 | subaxis(nrow,ncol,k,'ML',0.06); 112 | rhotmp = modparam(:,k); 113 | deptmp = modparam(:,k+5); 114 | thktmp = thick1d(:,k); 115 | h = histogram2(rhotmp,thktmp,'DisplayStyle','tile','ShowEmptyBins','off'); 116 | rhoRange = 0.2:0.02:1.4; 117 | depRange = 3.0:0.02:4.4; 118 | h.XBinEdges = rhoRange; 119 | h.YBinEdges = depRange; 120 | h.Normalization = 'probability'; 121 | set(h, 'EdgeColor', 'none') 122 | ch = colorbar; 123 | % ch.Label.String = 'Probability'; 124 | ch.Label.FontSize = 11; 125 | ch.FontSize = 11; 126 | 127 | caxis([0 0.004]) 128 | xlabel('log_{10}Resistivity [\Omegam]','Fontsize',capfs); 129 | % ylabel('log_{10}Depth [m]','Fontsize',capfs); 130 | rho04 = log10(10); 131 | dep04 = log10(4000); 132 | hold on 133 | plot(rho04,dep04,'rx','MarkerSize',mksize,'LineWidth',linew); 134 | set(gca,'layer','top','Fontsize',axefs); 135 | 136 | text(0.23,4.32,'(d) fourth layer','Fontsize',13); 137 | 138 | %% save figure 139 | filename = 'layer2dPPD'; 140 | set(hf,'Units','Inches'); 141 | pos = get(hf,'Position'); 142 | set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)]) 143 | print(hf,filename,'-dpdf','-r300') 144 | print(hf,filename,'-dtiff','-r300') -------------------------------------------------------------------------------- /examples/01MT/runMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling 3 | # 4 | #------------------------------------------------------------------------------ 5 | # push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | using TransdEM.TBUtility 8 | using TransdEM.TBStruct 9 | using TransdEM.TBFileIO 10 | using TransdEM.TBChain 11 | using TransdEM.TBFwdSolver 12 | using TransdEM.TBAnalysis 13 | 14 | #------------------------------------------------------------------------------ 15 | printstyled("read datafile and startupfile ...\n", color=:blue) 16 | startup = "startupfile" 17 | (emData,mclimits) = readstartupFile(startup) 18 | 19 | # 20 | printstyled("perform MCMC sampling ...\n", color=:blue) 21 | @time (mcArray,mcstatus) = runMCMC(mclimits, emData) 22 | 23 | # 24 | printstyled("Output Markov chain ...\n", color=:blue) 25 | @time outputMCMCsamples(mcArray) 26 | 27 | printstyled("Extract statistical quantities ...\n", color=:blue) 28 | filestr = "mt" 29 | @time sampleStatistics(mcArray, mclimits, filestr) 30 | 31 | # 32 | printstyled("extract model parameters from the ensemble ...\n", color=:blue) 33 | nLayer = 5 34 | extractModelParam(mcArray, mclimits, nLayer) 35 | 36 | println("===============================") 37 | # 38 | -------------------------------------------------------------------------------- /examples/01MT/runPTMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling with parallel tempering 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | # set up temperature ladder for parallel tempering MCMC 16 | nTarget = 3 # number of chaints at target temperature T = 1.0 17 | nLadder = 3 # number of chains at abnormal temperature T > 1.0 18 | maxTemp = 2.0 # maximum temperature 19 | tempTarget = ones(nTarget-1) 20 | tempLadder = 10.0 .^ range(0.0, stop=log10(maxTemp), length=nLadder+1) 21 | tempLadder = vcat(tempTarget, tempLadder) 22 | nT = nTarget + nLadder 23 | 24 | # check number of workers 25 | pids = workers() 26 | np = length(pids) 27 | if nT < np 28 | rmprocs(collect(nT+2:np+1)) 29 | elseif nT > np 30 | error("The number of workers should be equal or larger than the number of tempered chains!") 31 | end 32 | pids = workers() 33 | 34 | #------------------------------------------------------------------------------- 35 | # run MCMC sampler 36 | @everywhere begin 37 | startup = "startupfile" 38 | printstyled("Read datafile and startupfile ...\n", color=:cyan) 39 | (emData, mclimits) = readstartupFile(startup) 40 | end 41 | nsample = mclimits.totalsamples 42 | swapchain = zeros(Int, 3, nT, nsample) 43 | tempData = TBTemperature(nT, tempLadder, swapchain) 44 | 45 | # 46 | printstyled("Perform transdimensional Bayesian inversion with parallel tempering...\n", color=:cyan) 47 | @time (mcRef,stRef,dfRef,paramRef) = runTemperedMCMC(emData,mclimits,tempData,pids) 48 | 49 | # 50 | printstyled("Output ensemble results ...\n", color=:cyan) 51 | filestr = "mt" 52 | nEnsemble = samplePTStatistics(mcRef, tempData, mclimits, filestr) 53 | outputPTchains(mcRef, tempData, filestr) 54 | 55 | # 56 | printstyled("Release all darrays ...\n", color=:cyan) 57 | clearPTrefence(mcRef,stRef,dfRef,paramRef) 58 | 59 | println("===============================") 60 | -------------------------------------------------------------------------------- /examples/01MT/startupfile: -------------------------------------------------------------------------------- 1 | surveytype: mt 2 | datafile: mt1data.dat 3 | burninsamples: 100000 4 | totalsamples: 500000 5 | numberoflayer: 2 30 6 | minthickness(m): 100 7 | zcoordinate(m): 1.0 50000.0 0.05 8 | resistivity: 0.1 1e4 0.05 9 | numberofbins: 300 400 10 | credinterval: 0.9 11 | -------------------------------------------------------------------------------- /examples/01MT/synmt1data.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # synthetic data for MT case 3 | # 4 | #------------------------------------------------------------------------------ 5 | using TransdEM.TBUtility 6 | using TransdEM.TBStruct 7 | using TransdEM.TBFileIO 8 | using TransdEM.TBFwdSolver 9 | 10 | #------------------------------------------------------------------------------ 11 | # 1D synthetic model adapted from Guo et al., 2011. 12 | # 13 | sigma = [0.004, 0.04, 0.01, 0.1, 0.04] 14 | hLayer = [600, 1400, 4e3, 4e3, 1e4] 15 | depth1D = vcat(0., cumsum(hLayer)) 16 | 17 | # simulated data 18 | period = 10 .^ LinRange(log10(1/320), log10(80), 40) 19 | freqs = 1 ./ period 20 | zimp = compMT1DImpedance(freqs, sigma, depth1D) 21 | 22 | # add 5% noise 23 | nfreq = length(freqs) 24 | predData = zeros(2*nfreq) 25 | predData[1:2:end] = real.(zimp) 26 | predData[2:2:end] = imag.(zimp) 27 | errlvl = 0.05 28 | dataErr = abs.(predData) * errlvl 29 | obsData = predData + dataErr .* randn(2*nfreq) 30 | 31 | # output 32 | dataType = ["realZxy", "imagZxy"] 33 | dataID = ones(Bool,2,nfreq) 34 | dataID = vec(dataID) 35 | mtData = MTData(freqs,dataType,dataID,obsData,dataErr) 36 | datafile = "mt1data.dat" 37 | writeMTData(datafile,mtData,obsData,dataErr) 38 | -------------------------------------------------------------------------------- /examples/02TEM/paraMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling in parallel 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | 16 | #------------------------------------------------------------------------------ 17 | @everywhere begin 18 | printstyled("read datafile and startupfile ...\n", color=:blue) 19 | startup = "startupfile" 20 | (emData,mclimits) = readstartupFile(startup) 21 | end 22 | 23 | # 24 | printstyled("perform MCMC sampling ...\n", color=:blue) 25 | pids = workers() 26 | (results, status) = parallelMCMCsampling(mclimits, emData, pids) 27 | 28 | # 29 | printstyled("Output ensemble results ...\n", color=:blue) 30 | nData = length(emData.obsData) 31 | filestr = "tem" 32 | multipleStatistics(results, mclimits, filestr) 33 | 34 | println("===============================") 35 | # 36 | -------------------------------------------------------------------------------- /examples/02TEM/plotMCMCResults.m: -------------------------------------------------------------------------------- 1 | % function plotMCMCResults 2 | % 3 | %-------------------------------------------------------------------------- 4 | surveytype = 'tem'; 5 | resultfile = ['posteriorModel-',surveytype,'.dat']; 6 | fid = fopen(resultfile, 'r'); 7 | tmp = textscan(fid,'%f %f %f %f %f %f %f','HeaderLines',1); 8 | fclose(fid); 9 | results.mean = 10 .^ tmp{2}; 10 | results.median = 10 .^ tmp{3}; 11 | results.mode = 10 .^ tmp{4}; 12 | results.credmin = 10 .^ tmp{5}; 13 | results.credmax = 10 .^ tmp{6}; 14 | results.std = 10 .^ tmp{7}; 15 | fclose(fid); 16 | 17 | %% 18 | m2km = 1; 19 | depthBins = 10 .^ load(['depthBins-',surveytype,'.dat']); 20 | rhoBins = 10 .^ load(['rhoBins-',surveytype,'.dat']); 21 | 22 | % histogram of number of layers 23 | tmp = load(['nlayerHistogram-',surveytype,'.dat']); 24 | nlayerHist = tmp(:,2); 25 | 26 | % histogram of interface depths 27 | depthHist = load(['depthHistogram-',surveytype,'.dat']); 28 | 29 | % PPD of resistivity with depth 30 | depthrhoHist = load(['depthrhoHistogram-',surveytype,'.dat']); 31 | 32 | %% set plot range 33 | depthplotlim = [0 80] * m2km; 34 | rhoplotlim = [0.1 1e5]; 35 | depthBins = depthBins * m2km; 36 | 37 | %% plot figures 38 | figure(1); 39 | fontsize = 11; 40 | set(gcf,'Units','centimeters'); 41 | set(gcf,'Position',[10 2 30 14]); 42 | 43 | %% PPD of the resistivity 44 | subplot(1,4,1:2); 45 | nsample = sum(depthrhoHist(:,1)); 46 | depthrhoHist = depthrhoHist / nsample; 47 | pch = pcolor(rhoBins, depthBins, log10(depthrhoHist')); 48 | set(pch, 'EdgeColor', 'none') 49 | hold on 50 | cm = colormap('jet'); 51 | cm(1:15,:) = []; 52 | 53 | colormap(cm); 54 | c = colorbar; 55 | c.Location = 'westoutside'; 56 | c.Label.String = 'log_{10}Probability'; 57 | c.Label.FontSize = 10; 58 | caxis([-3 log10(0.4)]); 59 | 60 | linewidth = 1.2; 61 | semilogx(results.mean, depthBins, 'b-', 'LineWidth', linewidth); 62 | hold on 63 | semilogx(results.median, depthBins, 'r-', 'LineWidth', linewidth); 64 | semilogx(results.mode, depthBins, 'k-', 'LineWidth', linewidth); 65 | semilogx(results.credmin, depthBins, 'm-', 'LineWidth', linewidth); 66 | semilogx(results.credmax, depthBins, 'm-', 'LineWidth', linewidth); 67 | 68 | set(gca,'ydir','reverse','xscale','log','layer','top','box','on'); 69 | xlim(rhoplotlim); 70 | ylim(depthplotlim); 71 | xlabel('Resistivity [\Omegam]','Fontsize',fontsize); 72 | ylabel('Depth [m]','Fontsize',fontsize); 73 | 74 | 75 | %% histgram for the interface depths 76 | subplot(1,4,3); 77 | totalayer = sum(depthHist); 78 | dtmp = depthHist / totalayer; 79 | fill([0;dtmp;0],[0;depthBins;100],'b'); 80 | box on; grid on; 81 | xlim( [0 0.1]); 82 | ylim(depthplotlim); 83 | set(gca,'ydir','reverse'); 84 | xlabel('Interface Probability','Fontsize',fontsize); 85 | 86 | %% histogram for the number of layers 87 | % 88 | subplot(1,4,4); 89 | bar(nlayerHist/nsample); 90 | box on; 91 | set(gca,'layer','top'); 92 | xlim([0 20]); 93 | ylim([0 0.25]); 94 | xlabel('Number of Layers','Fontsize',fontsize); 95 | ylabel('Probability','Fontsize',fontsize); 96 | -------------------------------------------------------------------------------- /examples/02TEM/runMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling 3 | # 4 | #------------------------------------------------------------------------------ 5 | # push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | using TransdEM.TBUtility 8 | using TransdEM.TBStruct 9 | using TransdEM.TBFileIO 10 | using TransdEM.TBChain 11 | using TransdEM.TBFwdSolver 12 | using TransdEM.TBAnalysis 13 | 14 | #------------------------------------------------------------------------------ 15 | printstyled("read datafile and startupfile ...\n", color=:blue) 16 | startup = "startupfile" 17 | (emData,mclimits) = readstartupFile(startup) 18 | 19 | # 20 | printstyled("perform MCMC sampling ...\n", color=:blue) 21 | @time (mcArray,mcstatus) = runMCMC(mclimits, emData) 22 | 23 | # 24 | printstyled("Output Markov chain ...\n", color=:blue) 25 | @time outputMCMCsamples(mcArray) 26 | 27 | printstyled("Extract statistical quantities ...\n", color=:blue) 28 | filestr = "tem" 29 | @time sampleStatistics(mcArray, mclimits, filestr) 30 | 31 | println("===============================") 32 | # 33 | -------------------------------------------------------------------------------- /examples/02TEM/runPTMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling with parallel tempering 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | # set up temperature ladder for parallel tempering MCMC 16 | nTarget = 3 # number of chaints at target temperature T = 1.0 17 | nLadder = 3 # number of chains at abnormal temperature T > 1.0 18 | maxTemp = 2.0 # maximum temperature 19 | tempTarget = ones(nTarget-1) 20 | tempLadder = 10.0 .^ range(0.0, stop=log10(maxTemp), length=nLadder+1) 21 | tempLadder = vcat(tempTarget, tempLadder) 22 | nT = nTarget + nLadder 23 | 24 | # check number of workers 25 | pids = workers() 26 | np = length(pids) 27 | if nT < np 28 | rmprocs(collect(nT+2:np+1)) 29 | elseif nT > np 30 | error("The number of workers should be equal or larger than the number of tempered chains!") 31 | end 32 | pids = workers() 33 | 34 | #------------------------------------------------------------------------------- 35 | # run MCMC sampler 36 | @everywhere begin 37 | startup = "startupfile" 38 | printstyled("Read datafile and startupfile ...\n", color=:cyan) 39 | (emData, mclimits) = readstartupFile(startup) 40 | end 41 | nsample = mclimits.totalsamples 42 | swapchain = zeros(Int, 3, nT, nsample) 43 | tempData = TBTemperature(nT, tempLadder, swapchain) 44 | 45 | # 46 | printstyled("Perform transdimensional Bayesian inversion with parallel tempering...\n", color=:cyan) 47 | @time (mcRef,stRef,dfRef,paramRef) = runTemperedMCMC(emData,mclimits,tempData,pids) 48 | 49 | # 50 | printstyled("Output ensemble results ...\n", color=:cyan) 51 | filestr = "tem" 52 | nEnsemble = samplePTStatistics(mcRef, tempData, mclimits, filestr) 53 | outputPTchains(mcRef, tempData, filestr) 54 | 55 | # 56 | printstyled("Release all darrays ...\n", color=:cyan) 57 | clearPTrefence(mcRef,stRef,dfRef,paramRef) 58 | 59 | println("===============================") 60 | -------------------------------------------------------------------------------- /examples/02TEM/startupfile: -------------------------------------------------------------------------------- 1 | surveytype: tem 2 | datafile: tem1data.dat 3 | burninsamples: 100000 4 | totalsamples: 500000 5 | numberoflayer: 2 20 6 | zcoordinate(m): 1.0 1000.0 0.05 7 | minthickness(m): 2.0 8 | resistivity: 1.0 1e5 0.05 9 | numberofbins: 400 400 10 | credinterval: 0.9 11 | -------------------------------------------------------------------------------- /examples/02TEM/syntem1data.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # synthetic data for TEM case 3 | # 4 | #------------------------------------------------------------------------------ 5 | # push!(LOAD_PATH, "/yourpath/TransdEM") 6 | # 7 | using TransdEM.TBUtility 8 | using TransdEM.TBStruct 9 | using TransdEM.TBFileIO 10 | using TransdEM.TBFwdSolver 11 | 12 | #------------------------------------------------------------------------------ 13 | # 1D synthetic model 14 | # 15 | rho = [20., 1e4, 10., 100.0] 16 | hLayer = [10, 10, 10, 1e3] 17 | depth1D = vcat(0., cumsum(hLayer)) 18 | 19 | # simulated data 20 | loopxLen = 20.0 21 | loopyLen = 20.0 22 | 23 | strtime = -6 24 | endtime = -3 25 | ntime = (endtime - strtime)*10 + 1 26 | timePoint = 10 .^ LinRange(strtime, endtime, ntime) 27 | 28 | # 29 | @time predData = compTEM1dResponse(loopxLen,loopyLen,timePoint,rho,depth1D) 30 | errlvl = 0.05 31 | errFloor = 1e-9 32 | dataErr = predData * errlvl 33 | obsData = predData + dataErr .* randn(ntime) .+ errFloor 34 | dataErr = dataErr .+ errFloor 35 | 36 | # output 37 | temData = TEMData(loopxLen,loopyLen,timePoint,obsData,dataErr) 38 | datafile = "tem1data.dat" 39 | writeTEMData(datafile,temData,obsData,dataErr) 40 | -------------------------------------------------------------------------------- /examples/02TEM/tem1data.dat: -------------------------------------------------------------------------------- 1 | Format: TEMData_1.0 2 | # file generated in Tue Mar 23 15:00:14 2021 3 | LoopWidth(m): 20 20 4 | Data Block: 31 5 | # timechannel value error 6 | 1.00000000e-06 1.827007e-02 9.400605e-04 7 | 1.25892541e-06 1.360854e-02 6.848484e-04 8 | 1.58489319e-06 9.528627e-03 4.816284e-04 9 | 1.99526231e-06 6.274962e-03 3.270102e-04 10 | 2.51188643e-06 3.990224e-03 2.143433e-04 11 | 3.16227766e-06 2.829019e-03 1.350343e-04 12 | 3.98107171e-06 1.666839e-03 8.162640e-05 13 | 5.01187234e-06 9.326573e-04 4.721596e-05 14 | 6.30957344e-06 5.027587e-04 2.626104e-05 15 | 7.94328235e-06 2.988385e-04 1.429364e-05 16 | 1.00000000e-05 1.631493e-04 7.511354e-06 17 | 1.25892541e-05 7.302680e-05 4.070098e-06 18 | 1.58489319e-05 4.398854e-05 2.198754e-06 19 | 1.99526231e-05 2.634014e-05 1.243519e-06 20 | 2.51188643e-05 1.561611e-05 7.368410e-07 21 | 3.16227766e-05 8.528627e-06 4.259532e-07 22 | 3.98107171e-05 4.894981e-06 2.556143e-07 23 | 5.01187234e-05 3.028697e-06 1.465473e-07 24 | 6.30957344e-05 1.687939e-06 8.323844e-08 25 | 7.94328235e-05 9.334699e-07 4.626360e-08 26 | 1.00000000e-04 5.074552e-07 2.528129e-08 27 | 1.25892541e-04 2.435087e-07 1.381614e-08 28 | 1.58489319e-04 1.331908e-07 7.613786e-09 29 | 1.99526231e-04 6.580034e-08 4.365968e-09 30 | 2.51188643e-04 3.283595e-08 2.725833e-09 31 | 3.16227766e-04 1.750989e-08 1.842474e-09 32 | 3.98107171e-04 9.895377e-09 1.432851e-09 33 | 5.01187234e-04 5.219196e-09 1.211774e-09 34 | 6.30957344e-04 3.117160e-09 1.106266e-09 35 | 7.94328235e-04 2.079405e-09 1.053918e-09 36 | 1.00000000e-03 1.554934e-09 1.026795e-09 37 | -------------------------------------------------------------------------------- /examples/03MCSEM/csem1data.dat: -------------------------------------------------------------------------------- 1 | Format: CSEMData_1.0 2 | # file generated in Thu Apr 15 16:25:50 2021 3 | SeaLayer: 1000.0 3.2 4 | Source Location (m): 1 5 | # X Y Z Azimuth Dip 6 | 0.0 0.0 950.0 0.0 0.0 7 | Receiver Location (m): 20 8 | # X Y Z 9 | 500.0 0.0 1000.0 10 | 1000.0 0.0 1000.0 11 | 1500.0 0.0 1000.0 12 | 2000.0 0.0 1000.0 13 | 2500.0 0.0 1000.0 14 | 3000.0 0.0 1000.0 15 | 3500.0 0.0 1000.0 16 | 4000.0 0.0 1000.0 17 | 4500.0 0.0 1000.0 18 | 5000.0 0.0 1000.0 19 | 5500.0 0.0 1000.0 20 | 6000.0 0.0 1000.0 21 | 6500.0 0.0 1000.0 22 | 7000.0 0.0 1000.0 23 | 7500.0 0.0 1000.0 24 | 8000.0 0.0 1000.0 25 | 8500.0 0.0 1000.0 26 | 9000.0 0.0 1000.0 27 | 9500.0 0.0 1000.0 28 | 10000.0 0.0 1000.0 29 | Frequencies (Hz): 2 30 | 5.0000e-01 31 | 2.5000e-01 32 | DataType: 2 33 | ampEx 34 | phsEx 35 | Data Block: 80 36 | #FreqNo TxNo RxNo DataType Value Error 37 | 1 1 1 1 4.638014e-10 2.337142e-11 38 | 1 1 1 2 -2.717600e+01 2.864789e+00 39 | 1 1 2 1 3.641894e-11 1.689530e-12 40 | 1 1 2 2 -6.338186e+01 2.864789e+00 41 | 1 1 3 1 5.775789e-12 2.916382e-13 42 | 1 1 3 2 -8.657125e+01 2.864789e+00 43 | 1 1 4 1 1.944910e-12 9.861620e-14 44 | 1 1 4 2 -1.080143e+02 2.864789e+00 45 | 1 1 5 1 9.615486e-13 4.880910e-14 46 | 1 1 5 2 -1.245993e+02 2.864789e+00 47 | 1 1 6 1 5.561244e-13 2.757747e-14 48 | 1 1 6 2 -1.456958e+02 2.864789e+00 49 | 1 1 7 1 3.436246e-13 1.661351e-14 50 | 1 1 7 2 -1.452975e+02 2.864789e+00 51 | 1 1 8 1 2.141683e-13 1.057581e-14 52 | 1 1 8 2 -1.633828e+02 2.864789e+00 53 | 1 1 9 1 1.499608e-13 7.045800e-15 54 | 1 1 9 2 -1.654327e+02 2.864789e+00 55 | 1 1 10 1 9.274993e-14 4.838822e-15 56 | 1 1 10 2 -1.691100e+02 2.864789e+00 57 | 1 1 11 1 6.977980e-14 3.383178e-15 58 | 1 1 11 2 1.838589e+02 2.864789e+00 59 | 1 1 12 1 4.856190e-14 2.391440e-15 60 | 1 1 12 2 1.719476e+02 2.864789e+00 61 | 1 1 13 1 3.494461e-14 1.703782e-15 62 | 1 1 13 2 1.617092e+02 2.864789e+00 63 | 1 1 14 1 2.234203e-14 1.222118e-15 64 | 1 1 14 2 1.544075e+02 2.864789e+00 65 | 1 1 15 1 1.734349e-14 8.823119e-16 66 | 1 1 15 2 1.474488e+02 2.864789e+00 67 | 1 1 16 1 1.234519e-14 6.410956e-16 68 | 1 1 16 2 1.436503e+02 2.864789e+00 69 | 1 1 17 1 1.013584e-14 4.688470e-16 70 | 1 1 17 2 1.314134e+02 2.864789e+00 71 | 1 1 18 1 7.132243e-15 3.451275e-16 72 | 1 1 18 2 1.262255e+02 2.864789e+00 73 | 1 1 19 1 5.305134e-15 2.557496e-16 74 | 1 1 19 2 1.145415e+02 2.864789e+00 75 | 1 1 20 1 4.211007e-15 1.907961e-16 76 | 1 1 20 2 1.054953e+02 2.864789e+00 77 | 2 1 1 1 5.391654e-10 2.627766e-11 78 | 2 1 1 2 -1.736766e+01 2.864789e+00 79 | 2 1 2 1 4.690085e-11 2.388684e-12 80 | 2 1 2 2 -4.439277e+01 2.864789e+00 81 | 2 1 3 1 8.914881e-12 4.714071e-13 82 | 2 1 3 2 -6.971790e+01 2.864789e+00 83 | 2 1 4 1 2.848808e-12 1.469277e-13 84 | 2 1 4 2 -8.564307e+01 2.864789e+00 85 | 2 1 5 1 1.248457e-12 6.609186e-14 86 | 2 1 5 2 -8.780185e+01 2.864789e+00 87 | 2 1 6 1 7.069622e-13 3.829739e-14 88 | 2 1 6 2 -9.302050e+01 2.864789e+00 89 | 2 1 7 1 5.527993e-13 2.546564e-14 90 | 2 1 7 2 -9.386476e+01 2.864789e+00 91 | 2 1 8 1 3.860902e-13 1.803573e-14 92 | 2 1 8 2 -9.341574e+01 2.864789e+00 93 | 2 1 9 1 2.564913e-13 1.313730e-14 94 | 2 1 9 2 -1.071288e+02 2.864789e+00 95 | 2 1 10 1 2.131992e-13 9.703730e-15 96 | 2 1 10 2 -1.091199e+02 2.864789e+00 97 | 2 1 11 1 1.328623e-13 7.229226e-15 98 | 2 1 11 2 -1.114794e+02 2.864789e+00 99 | 2 1 12 1 1.166220e-13 5.421469e-15 100 | 2 1 12 2 -1.259424e+02 2.864789e+00 101 | 2 1 13 1 8.014358e-14 4.090038e-15 102 | 2 1 13 2 -1.252951e+02 2.864789e+00 103 | 2 1 14 1 6.019659e-14 3.103317e-15 104 | 2 1 14 2 -1.350108e+02 2.864789e+00 105 | 2 1 15 1 4.442435e-14 2.367939e-15 106 | 2 1 15 2 -1.402322e+02 2.864789e+00 107 | 2 1 16 1 3.469473e-14 1.816866e-15 108 | 2 1 16 2 -1.459255e+02 2.864789e+00 109 | 2 1 17 1 3.075828e-14 1.401634e-15 110 | 2 1 17 2 -1.527837e+02 2.864789e+00 111 | 2 1 18 1 2.114099e-14 1.087037e-15 112 | 2 1 18 2 -1.571847e+02 2.864789e+00 113 | 2 1 19 1 1.771998e-14 8.473874e-16 114 | 2 1 19 2 -1.625693e+02 2.864789e+00 115 | 2 1 20 1 1.315654e-14 6.638414e-16 116 | 2 1 20 2 -1.741810e+02 2.864789e+00 117 | -------------------------------------------------------------------------------- /examples/03MCSEM/paraMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling in parallel 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | 16 | #------------------------------------------------------------------------------ 17 | @everywhere begin 18 | printstyled("read datafile and startupfile ...\n", color=:blue) 19 | startup = "startupfile" 20 | (emData,mclimits) = readstartupFile(startup) 21 | end 22 | 23 | # 24 | printstyled("perform MCMC sampling ...\n", color=:blue) 25 | pids = workers() 26 | (results, status) = parallelMCMCsampling(mclimits, emData, pids) 27 | 28 | # 29 | printstyled("Output ensemble results ...\n", color=:blue) 30 | nData = length(emData.obsData) 31 | filestr = "csem" 32 | multipleStatistics(results, mclimits, filestr) 33 | 34 | println("===============================") 35 | # 36 | -------------------------------------------------------------------------------- /examples/03MCSEM/plotMCMCResults.m: -------------------------------------------------------------------------------- 1 | % function plotMCMCResults 2 | % 3 | %-------------------------------------------------------------------------- 4 | surveytype = 'csem'; 5 | resultfile = ['posteriorModel-',surveytype,'.dat']; 6 | fid = fopen(resultfile, 'r'); 7 | tmp = textscan(fid,'%f %f %f %f %f %f %f','HeaderLines',1); 8 | fclose(fid); 9 | results.mean = 10 .^ tmp{2}; 10 | results.median = 10 .^ tmp{3}; 11 | results.mode = 10 .^ tmp{4}; 12 | results.credmin = 10 .^ tmp{5}; 13 | results.credmax = 10 .^ tmp{6}; 14 | results.std = 10 .^ tmp{7}; 15 | fclose(fid); 16 | 17 | %% 18 | m2km = 0.001; 19 | depthBins = 10 .^ load(['depthBins-',surveytype,'.dat']); 20 | rhoBins = 10 .^ load(['rhoBins-',surveytype,'.dat']); 21 | 22 | % histogram of number of layers 23 | tmp = load(['nlayerHistogram-',surveytype,'.dat']); 24 | nlayerHist = tmp(:,2); 25 | 26 | % histogram of interface depths 27 | depthHist = load(['depthHistogram-',surveytype,'.dat']); 28 | 29 | % PPD of resistivity with depth 30 | depthrhoHist = load(['depthrhoHistogram-',surveytype,'.dat']); 31 | 32 | %% set plot range 33 | seaDepth = 1000; 34 | depthplotlim = [seaDepth 3000]*m2km; 35 | rhoplotlim = [0.1 1000]; 36 | depthBins = (depthBins + seaDepth) * m2km; 37 | 38 | %% plot figures 39 | figure(1); 40 | fontsize = 11; 41 | set(gcf,'Units','centimeters'); 42 | set(gcf,'Position',[10 2 30 14]); 43 | 44 | %% PPD of the resistivity 45 | subplot(1,4,1:2); 46 | nsample = sum(depthrhoHist(:,1)); 47 | depthrhoHist = depthrhoHist / nsample; 48 | pch = pcolor(rhoBins, depthBins, log10(depthrhoHist')); 49 | set(pch, 'EdgeColor', 'none') 50 | hold on 51 | cm = colormap('jet'); 52 | cm(1:15,:) = []; 53 | 54 | colormap(cm); 55 | c = colorbar; 56 | c.Location = 'westoutside'; 57 | c.Label.String = 'log_{10}Probability'; 58 | c.Label.FontSize = 10; 59 | caxis([-3 log10(0.4)]); 60 | 61 | linewidth = 1.2; 62 | semilogx(results.mean, depthBins, 'b-', 'LineWidth', linewidth); 63 | hold on 64 | semilogx(results.median, depthBins, 'r-', 'LineWidth', linewidth); 65 | semilogx(results.mode, depthBins, 'k-', 'LineWidth', linewidth); 66 | semilogx(results.credmin, depthBins, 'm-', 'LineWidth', linewidth); 67 | semilogx(results.credmax, depthBins, 'm-', 'LineWidth', linewidth); 68 | 69 | set(gca,'ydir','reverse','xscale','log','layer','top','box','on'); 70 | xlim(rhoplotlim); 71 | ylim(depthplotlim); 72 | xlabel('Resistivity [\Omegam]','Fontsize',fontsize); 73 | ylabel('Depth [km]','Fontsize',fontsize); 74 | 75 | %% histgram for the interface depths 76 | subplot(1,4,3); 77 | totalayer = sum(depthHist); 78 | dtmp = depthHist / totalayer; 79 | fill([0;dtmp;0],[0;depthBins;5],'b'); 80 | box on; grid on; 81 | xlim( [0 0.1]); 82 | ylim(depthplotlim); 83 | set(gca,'ydir','reverse'); 84 | xlabel('Interface Probability','Fontsize',fontsize); 85 | 86 | %% histogram for the number of layers 87 | % 88 | subplot(1,4,4); 89 | bar(nlayerHist/nsample); 90 | box on; 91 | set(gca,'layer','top'); 92 | xlim([0 20]); 93 | ylim([0 0.25]); 94 | xlabel('Number of Layers','Fontsize',fontsize); 95 | ylabel('Probability','Fontsize',fontsize); 96 | -------------------------------------------------------------------------------- /examples/03MCSEM/runMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling 3 | # 4 | #------------------------------------------------------------------------------ 5 | # push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | using TransdEM.TBUtility 8 | using TransdEM.TBStruct 9 | using TransdEM.TBFileIO 10 | using TransdEM.TBChain 11 | using TransdEM.TBFwdSolver 12 | using TransdEM.TBAnalysis 13 | 14 | #------------------------------------------------------------------------------ 15 | printstyled("read datafile and startupfile ...\n", color=:blue) 16 | startup = "startupfile" 17 | (emData,mclimits) = readstartupFile(startup) 18 | 19 | # 20 | printstyled("perform MCMC sampling ...\n", color=:blue) 21 | @time (mcArray,mcstatus) = runMCMC(mclimits, emData) 22 | 23 | # 24 | printstyled("Output Markov chain ...\n", color=:blue) 25 | @time outputMCMCsamples(mcArray) 26 | 27 | printstyled("Extract statistical quantities ...\n", color=:blue) 28 | filestr = "csem" 29 | @time sampleStatistics(mcArray, mclimits, filestr) 30 | 31 | println("===============================") 32 | # 33 | -------------------------------------------------------------------------------- /examples/03MCSEM/runPTMCMCScript.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # script to perform MCMC sampling with parallel tempering 3 | # 4 | #------------------------------------------------------------------------------ 5 | # @everywhere push!(LOAD_PATH, "/yourpath/code") 6 | # 7 | @everywhere begin 8 | using TransdEM.TBUtility 9 | using TransdEM.TBStruct 10 | using TransdEM.TBFileIO 11 | using TransdEM.TBChain 12 | using TransdEM.TBFwdSolver 13 | using TransdEM.TBAnalysis 14 | end 15 | # set up temperature ladder for parallel tempering MCMC 16 | nTarget = 2 # number of chaints at target temperature T = 1.0 17 | nLadder = 4 # number of chains at abnormal temperature T > 1.0 18 | maxTemp = 2.5 # maximum temperature 19 | tempTarget = ones(nTarget-1) 20 | tempLadder = 10.0 .^ range(0.0, stop=log10(maxTemp), length=nLadder+1) 21 | tempLadder = vcat(tempTarget, tempLadder) 22 | nT = nTarget + nLadder 23 | 24 | # check number of workers 25 | pids = workers() 26 | np = length(pids) 27 | if nT < np 28 | rmprocs(collect(nT+2:np+1)) 29 | elseif nT > np 30 | error("The number of workers should be equal or larger than the number of tempered chains!") 31 | end 32 | pids = workers() 33 | 34 | #------------------------------------------------------------------------------- 35 | # run MCMC sampler 36 | @everywhere begin 37 | startup = "startupfile" 38 | printstyled("Read datafile and startupfile ...\n", color=:cyan) 39 | (emData, mclimits) = readstartupFile(startup) 40 | end 41 | nsample = mclimits.totalsamples 42 | swapchain = zeros(Int, 3, nT, nsample) 43 | tempData = TBTemperature(nT, tempLadder, swapchain) 44 | 45 | # 46 | printstyled("Perform transdimensional Bayesian inversion with parallel tempering...\n", color=:cyan) 47 | @time (mcRef,stRef,dfRef,paramRef) = runTemperedMCMC(emData,mclimits,tempData,pids) 48 | 49 | # 50 | printstyled("Output ensemble results ...\n", color=:cyan) 51 | filestr = "csem" 52 | nEnsemble = samplePTStatistics(mcRef, tempData, mclimits, filestr) 53 | outputPTchains(mcRef, tempData, filestr) 54 | 55 | # 56 | printstyled("Release all darrays ...\n", color=:cyan) 57 | clearPTrefence(mcRef,stRef,dfRef,paramRef) 58 | 59 | println("===============================") 60 | -------------------------------------------------------------------------------- /examples/03MCSEM/startupfile: -------------------------------------------------------------------------------- 1 | surveytype: csem 2 | datafile: csem1data.dat 3 | burninsamples: 400000 4 | totalsamples: 1000000 5 | numberoflayer: 2 30 6 | zcoordinate(m): 1.0 4000.0 0.05 7 | resistivity: 0.1 1e3 0.05 8 | numberofbins: 300 400 9 | credinterval: 0.9 10 | -------------------------------------------------------------------------------- /examples/03MCSEM/syncsem1data.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # synthetic data for CSEM case 3 | # 4 | #------------------------------------------------------------------------------ 5 | # push!(LOAD_PATH, "/yourpath/TransdEM") 6 | # 7 | using TransdEM.TBUtility 8 | using TransdEM.TBStruct 9 | using TransdEM.TBFileIO 10 | using TransdEM.TBFwdSolver 11 | 12 | #------------------------------------------------------------------------------ 13 | # 1D synthetic model from Constable et al., 2006. 14 | # 15 | rho = [1.0, 100.0, 1.0] 16 | hLayer = [1000.0, 100.0] 17 | sigAir = 1e-12 18 | airLayer = -1000000. 19 | sigSea = 3.2 20 | seaLayer = 1000. 21 | # 22 | sigma = vcat(sigAir,sigSea,1 ./rho) 23 | seaDepth = cumsum(vcat(0., hLayer)) 24 | depth1D = vcat(airLayer, 0., seaDepth .+ seaLayer) 25 | 26 | # simulated data 27 | dpLen = 0. 28 | txLoc = [0. 0. 950. 0. 0.] 29 | nTx = size(txLoc, 1) 30 | nRx = 20 31 | rxLoc = zeros(nRx, 3) 32 | rxLoc[:, 1] = collect(500:500:10000) 33 | rxLoc[:, 3] .= 1000.0 34 | freqs = [0.25, 0.5, 0.75] 35 | nFreq = length(freqs) 36 | dataType = ["ampEx", "phsEx"] 37 | nDt = length(dataType) 38 | 39 | # 40 | tvec = zeros(0) 41 | dataID = ones(Bool,nDt,nRx,nTx,nFreq) 42 | dataID = vec(dataID) 43 | seawater = [seaLayer,sigSea] 44 | emData = CSEMData(seawater,txLoc,rxLoc,0.0,freqs,dataType,dataID,tvec,tvec) 45 | 46 | @time predData = compCSEM1dResp(emData, sigma, depth1D) 47 | 48 | # add noise 49 | errlvl = 0.05 50 | nData = length(predData) 51 | dataErr = zeros(nData) 52 | ampErr = abs.(predData[1:2:end]) * errlvl 53 | # 54 | errFloor = 1e-15 55 | idx = findall(ampErr .< errFloor) 56 | ampErr[idx] .= errFloor 57 | dataErr[1:2:end] = ampErr 58 | dataErr[2:2:end] .= errlvl * 180/pi 59 | obsData = predData + dataErr .* randn(nData) 60 | 61 | # output 62 | datafile = "csem1data.dat" 63 | writeCSEMData(datafile, emData, obsData, dataErr) 64 | -------------------------------------------------------------------------------- /src/TBAnalysis/TBAnalysis.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBAnalysis` defines routines to perform posterior analysis of model 4 | # parameters. 5 | # 6 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 7 | # 8 | #------------------------------------------------------------------------------- 9 | module TBAnalysis 10 | 11 | using TransdEM.TBUtility 12 | using TransdEM.TBStruct 13 | using TransdEM.TBChain 14 | 15 | using LinearAlgebra, Printf, Statistics 16 | using Distributed, DistributedArrays 17 | 18 | export sampleStatistics 19 | export multipleStatistics 20 | export updatesampleModel! 21 | export outputMCMCsamples 22 | export outputEnsembleModel 23 | export getCredibleIntervalModel 24 | export mksampleArray 25 | export extractModelParam 26 | 27 | export outputPTDataMisfit 28 | export outputPTchainHeader 29 | export outputPTchains 30 | export samplePTStatistics 31 | export clearPTrefence 32 | 33 | #------------------------------------------------------------------------------- 34 | """ 35 | `outputMCMCsamples(mcArray)` 36 | 37 | """ 38 | function outputMCMCsamples(mcArray::MChainArray, ichain::Int=1) 39 | 40 | nsample = length(mcArray.chainMisfit) 41 | chainnlayer = mcArray.chainnlayer 42 | 43 | # chain values 44 | valuefile = "chainsamples_id$(ichain).dat" 45 | valID = open(valuefile, "w") 46 | chainstep = mcArray.chainstep 47 | chainindice = mcArray.chainindice 48 | chainvalue = mcArray.chainvalue 49 | 50 | # sart model 51 | @printf(valID,"! starting model \n") 52 | startModel = mcArray.startModel 53 | nlayer = startModel.nLayer 54 | zNode = startModel.zNode 55 | rho = startModel.rho 56 | @printf(valID, "zcoordinate: %5d \n", nlayer) 57 | for k = 1:nlayer 58 | @printf(valID, "%8g ", zNode[k]) 59 | end 60 | @printf(valID, "\n") 61 | @printf(valID, "resistivity: %5d \n", nlayer) 62 | for k = 1:nlayer 63 | @printf(valID, "%8g ", rho[k]) 64 | end 65 | @printf(valID, "\n") 66 | 67 | @printf(valID,"! #sample step accept layeridx zcoord(lg10) rho(lg10) nLayer dataMisfit\n") 68 | @printf(valID, "samplelist: %8d\n", nsample) 69 | for i = 1:nsample 70 | @printf(valID, "%8d %6d %6d %6d ", i, chainstep[i,1], chainstep[i,2], chainindice[i]) 71 | @printf(valID, "%8g %8g ", chainvalue[i, 1], chainvalue[i, 2]) 72 | @printf(valID, "%8d %12g \n", chainnlayer[i], mcArray.chainMisfit[i]) 73 | end 74 | close(valID) 75 | 76 | end 77 | 78 | 79 | #------------------------------------------------------------------------------- 80 | """ 81 | `sampleStatistics(mcArray, mclimits)` 82 | 83 | """ 84 | function sampleStatistics(mcArray::MChainArray, mclimits::MCPrior, 85 | filestr::String="mc") 86 | 87 | # 88 | burnin = mclimits.burninsamples 89 | nsamples = mclimits.totalsamples 90 | nlayermin = mclimits.nlayermin 91 | nlayermax = mclimits.nlayermax 92 | zmin = mclimits.zmin 93 | zmax = mclimits.zmax 94 | rhomin = mclimits.rhomin 95 | rhomax = mclimits.rhomax 96 | 97 | # start model 98 | startModel = mcArray.startModel 99 | zNode = copy(startModel.zNode) 100 | rho = copy(startModel.rho) 101 | 102 | # intervals 103 | nzBins = mclimits.nzBins 104 | npBins = mclimits.npBins 105 | (zLocBins, zspace) = mksampleArray(zmin, zmax, nzBins) 106 | (rhoBins,rhospace) = mksampleArray(rhomin, rhomax, npBins) 107 | 108 | for k = 1:burnin 109 | updatesampleModel!(zNode, rho, k, mcArray) 110 | end 111 | 112 | # calculate posteriors 113 | rhoModel = zeros(nzBins) 114 | nlayerPosterior = zeros(Int, nlayermax) 115 | depthPosterior = zeros(Int, nzBins) 116 | valuePosterior = zeros(Int, nzBins, npBins) 117 | 118 | # statistical moment 119 | sm01 = zeros(nzBins) 120 | sm02 = zeros(nzBins) 121 | 122 | # collecte mcmc samples after the burnin period 123 | samplestep = 100 124 | # number of samples in ensemble 125 | nEnsemble = 0 126 | for i in burnin+1:nsamples 127 | updatesampleModel!(zNode, rho, i, mcArray) 128 | 129 | # 130 | if mod(i, samplestep) > 0 131 | continue 132 | end 133 | 134 | nEnsemble += 1 135 | # posterior layer number 136 | nlayer = mcArray.chainnlayer[i] 137 | nlayerPosterior[nlayer] += 1 138 | 139 | # get depth posterior 140 | for j = 1:nlayer 141 | zLoc = zNode[j] 142 | cidx = findLocation1D(zLoc, zLocBins) 143 | depthPosterior[cidx] += 1 144 | end 145 | 146 | # get values at sampling location 147 | for j = 1:nzBins 148 | zLoc = zLocBins[j] 149 | cidx = findNearest1D(zLoc, zNode[1:nlayer]) 150 | rhoModel[j] = rho[cidx] 151 | # 152 | idx = findLocation1D(rho[cidx], rhoBins) 153 | valuePosterior[j, idx] += 1 154 | end 155 | 156 | # statistical moments 157 | sm01 += rhoModel 158 | sm02 += rhoModel .^ 2 159 | 160 | end 161 | 162 | # evaluate statistical variables 163 | sm01 = sm01 / nEnsemble 164 | sm02 = sm02 / nEnsemble 165 | 166 | # arithmetic mean 167 | valueMean = sm01 168 | 169 | # standard deviation 170 | std = sm02 - sm01 .^ 2 171 | std[std .< 0] .= eps(1.0) 172 | valueStd = sqrt.(std) 173 | 174 | # mode = maximum a posterior 175 | valueMode = zeros(nzBins) 176 | for i = 1:nzBins 177 | (val,idx) = findmax(valuePosterior[i, :]) 178 | valueMode[i] = rhoBins[idx] 179 | end 180 | 181 | # median 182 | valueMedian = zeros(nzBins) 183 | for i = 1:nzBins 184 | count = 0 185 | for j = 1:npBins 186 | count += valuePosterior[i, j] 187 | if count > nEnsemble / 2 188 | valueMedian[i] = rhoBins[j] 189 | break 190 | end 191 | end 192 | end 193 | 194 | # output results 195 | outputPosterior(zLocBins, rhoBins, nlayerPosterior, depthPosterior, 196 | valuePosterior, filestr) 197 | 198 | # credible min and max model 199 | ci = mclimits.credInterval 200 | (valueMin,valueMax) = getCredibleIntervalModel(valuePosterior, rhoBins, 201 | nEnsemble, ci) 202 | # 203 | filename = "posteriorModel-"*filestr*".dat" 204 | results = hcat(zLocBins,valueMean,valueMedian,valueMode,valueMin,valueMax,valueStd) 205 | outputEnsembleModel(filename, results) 206 | 207 | end # sampleStatistics 208 | 209 | 210 | #------------------------------------------------------------------------------- 211 | """ 212 | `updatesampleModel!(zNode, rho, k, mcArray)` 213 | 214 | """ 215 | function updatesampleModel!(zNode::Vector{T}, rho::Vector{T}, k::Int, 216 | mcArray::MChainArray) where{T<:Float64} 217 | 218 | mcstep = mcArray.chainstep[k, 1] 219 | accept = mcArray.chainstep[k, 2] 220 | nlayer = mcArray.chainnlayer[k] 221 | cidx = mcArray.chainindice[k] 222 | nlayermax = length(zNode) 223 | 224 | # birth step 225 | if mcstep==1 && accept>0 226 | zNode[nlayer] = mcArray.chainvalue[k, 1] 227 | rho[nlayer] = mcArray.chainvalue[k, 2] 228 | zNode[nlayer+1:nlayermax] .= 0. 229 | rho[nlayer+1:nlayermax] .= 0. 230 | 231 | # death step 232 | elseif mcstep==2 && accept>0 233 | zNode[cidx] = zNode[nlayer+1] 234 | rho[cidx] = rho[nlayer+1] 235 | zNode[nlayer+1:nlayermax] .= 0. 236 | rho[nlayer+1:nlayermax] .= 0. 237 | 238 | # move step 239 | elseif mcstep==3 && accept>0 240 | zNode[cidx] = mcArray.chainvalue[k, 1] 241 | 242 | # perturb step 243 | elseif mcstep==4 && accept>0 244 | rho[cidx] = mcArray.chainvalue[k, 2] 245 | 246 | end 247 | 248 | end # updatesampleModel 249 | 250 | 251 | #------------------------------------------------------------------------------- 252 | """ 253 | `outputEnsembleModel(filename, coords, results)` 254 | 255 | """ 256 | function outputEnsembleModel(filename::String, coords::Vector, results::Vector) 257 | 258 | fileID = open(filename, "w") 259 | 260 | for k = 1:length(coords) 261 | @printf(fileID, "%12g %12g \n", coords[k], results[k]) 262 | end 263 | 264 | close(fileID) 265 | 266 | end 267 | 268 | 269 | #------------------------------------------------------------------------------- 270 | """ 271 | `outputEnsembleModel(filename, results, comments)` 272 | 273 | """ 274 | function outputEnsembleModel(filename::String, results::Array{Float64}, comments::String="") 275 | 276 | fileID = open(filename, "w") 277 | if !isempty(comments) 278 | @printf(fileID, "%s\n", comments) 279 | end 280 | 281 | (nBins, nm) = size(results) 282 | for i = 1:nBins 283 | for j = 1:nm 284 | @printf(fileID, "%10g ", results[i,j]) 285 | end 286 | @printf(fileID, "\n") 287 | end 288 | 289 | close(fileID) 290 | 291 | end 292 | 293 | 294 | #------------------------------------------------------------------------------- 295 | """ 296 | `outputPosterior(coordBins, rhoBins, nlayerPosterior, depthPosterior, 297 | valuePosterior)` 298 | 299 | """ 300 | function outputPosterior(coordBins, rhoBins, nlayerPosterior, 301 | depthPosterior, valuePosterior, filestr) 302 | 303 | filename ="depthBins-"*filestr*".dat" 304 | fid = open(filename, "w") 305 | for k = 1:length(coordBins) 306 | @printf(fid, "%12g\n", coordBins[k]) 307 | end 308 | close(fid) 309 | 310 | filename ="rhoBins-"*filestr*".dat" 311 | fid = open(filename, "w") 312 | for k = 1:length(rhoBins) 313 | @printf(fid, "%12g\n", rhoBins[k]) 314 | end 315 | close(fid) 316 | 317 | filename ="nlayerHistogram-"*filestr*".dat" 318 | fid = open(filename, "w") 319 | for k = 1:length(nlayerPosterior) 320 | @printf(fid, "%4d %8d\n", k, nlayerPosterior[k]) 321 | end 322 | close(fid) 323 | 324 | filename = "depthHistogram-"*filestr*".dat" 325 | fid = open(filename, "w") 326 | for k = 1:length(depthPosterior) 327 | @printf(fid, "%8d \n", depthPosterior[k]) 328 | end 329 | close(fid) 330 | 331 | filename = "depthrhoHistogram-"*filestr*".dat" 332 | fid = open(filename, "w") 333 | for i = 1:length(rhoBins) 334 | for j = 1:length(coordBins) 335 | @printf(fid, "%8d ", valuePosterior[j, i]) 336 | end 337 | @printf(fid, "\n") 338 | end 339 | close(fid) 340 | 341 | end 342 | 343 | 344 | #------------------------------------------------------------------------------- 345 | """ 346 | `getCredibleIntervalModel(valuePosterior, rhoBins, nEnsemble, credInterval)` 347 | 348 | get the minimum and maximum credible model. 349 | 350 | """ 351 | function getCredibleIntervalModel(valuePosterior::Array{Ti}, 352 | rhoBins::Vector{Tv}, 353 | nEnsemble::Ti, 354 | credInterval::Tv) where {Ti<:Int,Tv<:Float64} 355 | 356 | # 357 | (nzBins,npBins) = size(valuePosterior) 358 | valueMin = zeros(nzBins) 359 | valueMax = zeros(nzBins) 360 | credsample = nEnsemble * (1 - credInterval) / 2 361 | 362 | # minimum credible model 363 | for i = 1:nzBins 364 | count = 0 365 | for j = 1:npBins 366 | count += valuePosterior[i, j] 367 | if count > credsample 368 | valueMin[i] = rhoBins[j] 369 | break 370 | end 371 | end 372 | end 373 | 374 | # maximum credible model 375 | for i = 1:nzBins 376 | count = 0 377 | for j = npBins:-1:1 378 | count += valuePosterior[i, j] 379 | if count > credsample 380 | valueMax[i] = rhoBins[j] 381 | break 382 | end 383 | end 384 | end 385 | 386 | return valueMin, valueMax 387 | 388 | end 389 | 390 | 391 | #------------------------------------------------------------------------------- 392 | """ 393 | `mksampleArray(vmin, vmax, nsample)` 394 | 395 | """ 396 | function mksampleArray(vmin::T, vmax::T, nsample::Int) where{T<:Number} 397 | 398 | dx = (vmax-vmin) / nsample 399 | x1 = vmin + 0.5 * dx 400 | x2 = vmax - 0.5 * dx 401 | val = LinRange(x1, x2, nsample) 402 | 403 | return collect(val), dx 404 | 405 | end 406 | 407 | 408 | #------------------------------------------------------------------------------- 409 | """ 410 | `multipleStatistics(mcArray, mclimits, filestr)` 411 | 412 | """ 413 | function multipleStatistics(results::Array, mclimits::MCPrior, 414 | filestr::String="mc", rscale::Float64=1.3) 415 | 416 | # 417 | burnin = mclimits.burninsamples 418 | nsamples = mclimits.totalsamples 419 | nlayermin = mclimits.nlayermin 420 | nlayermax = mclimits.nlayermax 421 | zmin = mclimits.zmin 422 | zmax = mclimits.zmax 423 | rhomin = mclimits.rhomin 424 | rhomax = mclimits.rhomax 425 | 426 | # multiple chains 427 | nchain = length(results) 428 | 429 | # calculate posteriors 430 | nzBins = mclimits.nzBins 431 | npBins = mclimits.npBins 432 | (zLocBins, zspace) = mksampleArray(zmin, zmax, nzBins) 433 | (rhoBins,rhospace) = mksampleArray(rhomin, rhomax, npBins) 434 | 435 | # calculate posteriors 436 | rhoModel = zeros(nzBins) 437 | nlayerPosterior = zeros(Int, nlayermax) 438 | depthPosterior = zeros(Int, nzBins) 439 | valuePosterior = zeros(Int, nzBins, npBins) 440 | 441 | # statistical moment 442 | sm01 = zeros(nzBins) 443 | sm02 = zeros(nzBins) 444 | 445 | # collecte mcmc samples after the burnin period 446 | samplestep = 100 447 | 448 | # number of samples in ensemble 449 | nEnsemble = 0 450 | 451 | # compute average data misfit 452 | aveMisfit = zeros(nchain) 453 | for ic = 1:nchain 454 | aveMisfit[ic] = mean(results[ic].chainMisfit[burnin:nsamples]) 455 | end 456 | aveMisfit = median(aveMisfit) 457 | 458 | for ic = 1:nchain 459 | 460 | mcArray = results[ic] 461 | # exclude chains that are not convergent 462 | mdatafit = mean(mcArray.chainMisfit[burnin:nsamples]) 463 | if mdatafit > rscale * aveMisfit 464 | println("chain No.$(ic) has been excluded!") 465 | continue 466 | end 467 | # 468 | println("Output sampling results for chain No.$(ic) ...") 469 | outputMCMCsamples(mcArray, ic) 470 | 471 | # start model 472 | startModel = mcArray.startModel 473 | zNode = copy(startModel.zNode) 474 | rho = copy(startModel.rho) 475 | 476 | # update model parameters 477 | for k = 1:burnin 478 | updatesampleModel!(zNode, rho, k, mcArray) 479 | end 480 | 481 | for i in burnin+1:nsamples 482 | updatesampleModel!(zNode, rho, i, mcArray) 483 | # 484 | if mod(i, samplestep) > 0; continue; end 485 | nEnsemble += 1 486 | 487 | # posterior cell number 488 | nlayer = mcArray.chainnlayer[i] 489 | nlayerPosterior[nlayer] += 1 490 | 491 | # get depth posterior 492 | for j = 1:nlayer 493 | zLoc = zNode[j] 494 | cidx = findLocation1D(zLoc, zLocBins) 495 | depthPosterior[cidx] += 1 496 | end 497 | 498 | # get values at sampling location 499 | for j = 1:nzBins 500 | zLoc = zLocBins[j] 501 | cidx = findNearest1D(zLoc, zNode[1:nlayer]) 502 | rhoModel[j] = rho[cidx] 503 | idx = findLocation1D(rho[cidx], rhoBins) 504 | valuePosterior[j, idx] += 1 505 | 506 | end 507 | 508 | # statistical moments 509 | sm01 += rhoModel 510 | sm02 += rhoModel .^ 2 511 | 512 | end 513 | 514 | end # nchain 515 | 516 | # evaluate statistical variables 517 | sm01 = sm01 / nEnsemble 518 | sm02 = sm02 / nEnsemble 519 | 520 | # arithmetic mean 521 | valueMean = sm01 522 | 523 | # standard deviation 524 | std = sm02 - sm01 .^ 2 525 | std[std .< 0] .= eps(1.0) 526 | valueStd = sqrt.(std) 527 | 528 | # mode = maximum a posterior 529 | valueMode = zeros(nzBins) 530 | for i = 1:nzBins 531 | (val,idx) = findmax(valuePosterior[i,:]) 532 | valueMode[i] = rhoBins[idx] 533 | end 534 | 535 | # meadian 536 | valueMedian = zeros(nzBins) 537 | for i = 1:nzBins 538 | count = 0 539 | for j = 1:npBins 540 | count += valuePosterior[i, j] 541 | if count > nEnsemble / 2 542 | valueMedian[i] = rhoBins[j] 543 | break 544 | end 545 | end 546 | end 547 | 548 | # output results 549 | outputPosterior(zLocBins, rhoBins, nlayerPosterior, depthPosterior, 550 | valuePosterior, filestr) 551 | 552 | # credible min and max model 553 | ci = mclimits.credInterval 554 | (valueMin,valueMax) = getCredibleIntervalModel(valuePosterior, rhoBins, 555 | nEnsemble, ci) 556 | 557 | # 558 | filename = "posteriorModel-"*filestr*".dat" 559 | results = hcat(zLocBins,valueMean,valueMedian,valueMode,valueMin,valueMax,valueStd) 560 | outputEnsembleModel(filename, results) 561 | 562 | end # multipleStatistics 563 | 564 | 565 | #------------------------------------------------------------------------------- 566 | """ 567 | `extractModelParam(mcArray, mclimits, nLayer)` 568 | 569 | """ 570 | function extractModelParam(mcArray::MChainArray, mclimits::MCPrior, nLayer::Int) 571 | 572 | # 573 | burnin = mclimits.burninsamples 574 | nsamples = mclimits.totalsamples 575 | 576 | # start model 577 | startModel = mcArray.startModel 578 | zNode = copy(startModel.zNode) 579 | rho = copy(startModel.rho) 580 | 581 | for k = 1:burnin 582 | updatesampleModel!(zNode, rho, k, mcArray) 583 | end 584 | 585 | # collecte mcmc samples after the burnin period 586 | samplestep = 100 587 | 588 | # 589 | nidx = findall(mcArray.chainnlayer[burnin:samplestep:nsamples] .== nLayer) 590 | num = length(nidx) 591 | nrow = 2*nLayer - 1 592 | km = 1 593 | modparam = zeros(nrow, num) 594 | for i in burnin+1:nsamples 595 | 596 | updatesampleModel!(zNode, rho, i, mcArray) 597 | if mod(i, samplestep) > 0; continue; end 598 | 599 | # posterior layer number 600 | pnlayer = mcArray.chainnlayer[i] 601 | if pnlayer == nLayer 602 | idx = sortperm(zNode[1:nLayer]) 603 | dep1D = zNode[idx] 604 | modparam[1:nLayer, km] = copy(rho[idx]) 605 | modparam[nLayer+1:nrow,km] = copy(dep1D[1:nLayer-1]) 606 | km = km + 1 607 | end 608 | 609 | end 610 | 611 | return modparam 612 | 613 | end 614 | 615 | 616 | #------------------------------------------------------------------------------- 617 | """ 618 | `extractModelParam(mcArray, mclimits, nLayer)` 619 | 620 | """ 621 | function extractModelParam(results::Array, mclimits::MCPrior, nLayer::Int) 622 | 623 | # number of chains 624 | nchain = length(results) 625 | 626 | filename = "modelParam_$(nLayer)layer.dat" 627 | fileID = open(filename, "w") 628 | for ic = 1:nchain 629 | modparam = extractModelParam(results[ic], mclimits, nLayer) 630 | (nr,nc) = size(modparam) 631 | for j = 1:nc 632 | for k = 1:nr 633 | @printf(fileID, "%8g ", modparam[k,j]) 634 | end 635 | @printf(fileID, "\n") 636 | end 637 | end 638 | close(fileID) 639 | 640 | end 641 | 642 | 643 | #------------------------------------------------------------------------------- 644 | """ 645 | `outputPTDataMisfit(mcArrayRef, tempData, mclimits, filestr)` 646 | 647 | output data misfit of the samples in each Markov chain for convergence analysis. 648 | 649 | """ 650 | function outputPTDataMisfit(mcArrayRef::DArray, tempData::TBTemperature, 651 | mclimits::MCPrior, filestr::String="mc") 652 | 653 | # 654 | nsamples = mclimits.totalsamples 655 | nchain = length(mcArrayRef) 656 | 657 | # 658 | dataMisfit = zeros(nsamples, nchain) 659 | swapchain = tempData.swapchain 660 | 661 | for ic = 1:nchain 662 | dataMisfit[:, ic] = mcArrayRef[ic].chainMisfit 663 | end 664 | 665 | # 666 | for k = 1:nsamples 667 | for ic = 1:nchain 668 | if swapchain[3,ic,k] > 0 669 | idx01 = [swapchain[1,ic,k],swapchain[2,ic,k]] 670 | idx02 = [swapchain[2,ic,k],swapchain[1,ic,k]] 671 | chainidx[idx01] = chainidx[idx02] 672 | end 673 | end 674 | ind = indexin(sortchain, chainidx) 675 | dataMisfit[k, :] = dataMisfit[k, ind] 676 | end 677 | 678 | # output 679 | filename = "chainMisfit-"*filestr*".dat" 680 | fileID = open(filename, "w") 681 | for k = 1:nsamples 682 | for ic = 1:nchain 683 | @printf(fileID, "%8g ", dataMisfit[k,ic]) 684 | end 685 | @printf(fileID, "\n") 686 | end 687 | 688 | close(fileID) 689 | 690 | end 691 | 692 | 693 | #------------------------------------------------------------------------------- 694 | """ 695 | `outputPTchainHeader(mcArray, chainidx)` 696 | 697 | """ 698 | function outputPTchainHeader(mcArray::Array{MChainArray}, chainidx::Vector, filestr::String="mc") 699 | 700 | # 701 | nchain = length(chainidx) 702 | valID = Array{IOStream}(undef, nchain) 703 | for ic = 1:nchain 704 | 705 | id = chainidx[ic] 706 | valuefile = filestr*"_chainsamples_id$(ic).dat" 707 | valID[ic] = open(valuefile, "w") 708 | chainstep = mcArray[id].chainstep 709 | chainindice = mcArray[id].chainindice 710 | chainvalue = mcArray[id].chainvalue 711 | nsamples = length(mcArray[id].chainMisfit) 712 | # sart model 713 | @printf(valID[ic],"! starting model \n") 714 | startModel = mcArray[id].startModel 715 | nlayer = startModel.nLayer 716 | zNode = startModel.zNode 717 | rho = startModel.rho 718 | @printf(valID[ic], "zcoordinate: %5d \n", nlayer) 719 | for k = 1:nlayer 720 | @printf(valID[ic], "%8g ", zNode[k]) 721 | end 722 | @printf(valID[ic], "\n") 723 | @printf(valID[ic], "resistivity: %5d \n", nlayer) 724 | for k = 1:nlayer 725 | @printf(valID[ic], "%8g ", rho[k]) 726 | end 727 | @printf(valID[ic], "\n") 728 | 729 | # 730 | @printf(valID[ic],"! #sample step accept layeridx zcoord(lg10) rho(lg10) nLayer dataMisfit\n") 731 | @printf(valID[ic], "samplelist: %8d\n", nsamples) 732 | k = 1 733 | @printf(valID[ic], "%8d %6d %6d %6d ", k, mcArray[id].chainstep[k, 1], 734 | mcArray[ic].chainstep[k, 2], mcArray[id].chainindice[k]) 735 | @printf(valID[ic], "%8g %8g ", mcArray[id].chainvalue[k,1], 736 | mcArray[id].chainvalue[k, 2]) 737 | @printf(valID[ic], "%8d %12g \n", mcArray[id].chainnlayer[k], 738 | mcArray[id].chainMisfit[k]) 739 | 740 | end 741 | 742 | return valID 743 | 744 | end 745 | 746 | 747 | #------------------------------------------------------------------------------- 748 | """ 749 | `outputPTchains(mcArrayRef, tempData)` 750 | 751 | write out Markov chains in parallel tempering into file. 752 | 753 | """ 754 | function outputPTchains(mcArrayRef::DArray, tempData::TBTemperature, filestr::String="mc") 755 | 756 | # 757 | nchain = length(mcArrayRef) 758 | swapchain = tempData.swapchain 759 | chainidx = collect(1:nchain) 760 | sortchain = collect(1:nchain) 761 | mcArray = Array{MChainArray}(undef, nchain) 762 | valID = Array{IOStream}(undef, nchain) 763 | 764 | # 765 | for ic = 1:nchain 766 | mcArray[ic] = mcArrayRef[ic] 767 | end 768 | nsamples = length(mcArray[1].chainMisfit) 769 | for k = 1:nsamples 770 | 771 | for ic = 1:nchain 772 | if swapchain[3,ic,k] > 0 773 | idx01 = [ swapchain[1,ic,k], swapchain[2,ic,k] ] 774 | idx02 = [ swapchain[2,ic,k], swapchain[1,ic,k] ] 775 | chainidx[idx01] = chainidx[idx02] 776 | end 777 | end 778 | ind = indexin(sortchain, chainidx) 779 | 780 | # output samples 781 | if k < 2 782 | valID = outputPTchainHeader(mcArray, ind, filestr) 783 | else 784 | for ic = 1:nchain 785 | id = ind[ic] 786 | @printf(valID[ic], "%8d %6d %6d %6d ", k, mcArray[id].chainstep[k, 1], 787 | mcArray[ic].chainstep[k, 2], mcArray[id].chainindice[k]) 788 | @printf(valID[ic], "%8g %8g ", mcArray[id].chainvalue[k,1], 789 | mcArray[id].chainvalue[k, 2]) 790 | @printf(valID[ic], "%8d %12g \n", mcArray[id].chainnlayer[k], 791 | mcArray[id].chainMisfit[k]) 792 | end 793 | end 794 | end 795 | 796 | # 797 | for ic = 1:nchain 798 | close(valID[ic]) 799 | end 800 | 801 | end 802 | 803 | 804 | #------------------------------------------------------------------------------- 805 | """ 806 | `samplePTStatistics(mcArrayRef, tempData, mclimits, filestr)` 807 | 808 | calculate statistical models from parallel tempering MCMC results. 809 | 810 | """ 811 | function samplePTStatistics(mcArrayRef::DArray, tempData::TBTemperature, 812 | mclimits::MCPrior, filestr::String="mc") 813 | 814 | # 815 | burnin = mclimits.burninsamples 816 | nsamples = mclimits.totalsamples 817 | nlayermin = mclimits.nlayermin 818 | nlayermax = mclimits.nlayermax 819 | zmin = mclimits.zmin 820 | zmax = mclimits.zmax 821 | rhomin = mclimits.rhomin 822 | rhomax = mclimits.rhomax 823 | 824 | # intervals 825 | nzBins = mclimits.nzBins 826 | npBins = mclimits.npBins 827 | (zLocBins, zspace) = mksampleArray(zmin, zmax, nzBins) 828 | (rhoBins,rhospace) = mksampleArray(rhomin, rhomax, npBins) 829 | 830 | # multiple chains 831 | nchain = length(mcArrayRef) 832 | 833 | # calculate posteriors 834 | rhoModel = zeros(nzBins) 835 | nlayerPosterior = zeros(Int, nlayermax) 836 | depthPosterior = zeros(Int, nzBins) 837 | valuePosterior = zeros(Int, nzBins, npBins) 838 | 839 | # statistical moment 840 | sm01 = zeros(nzBins) 841 | sm02 = zeros(nzBins) 842 | 843 | # collecte mcmc samples after the burnin period 844 | samplestep = 100 845 | 846 | # number of samples in ensemble 847 | nEnsemble = 0 848 | tempLadder = copy(tempData.tempLadder) 849 | chainidx = collect(1:nchain) 850 | sortchain = collect(1:nchain) 851 | 852 | # 853 | mcArray = Array{MChainArray}(undef, nchain) 854 | dataMisfit = zeros(nsamples, nchain) 855 | swapchain = tempData.swapchain 856 | zNodeArray = Array{Any}(undef, nchain) 857 | rhoArray = Array{Any}(undef, nchain) 858 | for ic = 1:nchain 859 | mcArray[ic] = mcArrayRef[ic] 860 | dataMisfit[:, ic] = mcArray[ic].chainMisfit 861 | zNodeArray[ic] = copy(mcArray[ic].startModel.zNode) 862 | rhoArray[ic] = copy(mcArray[ic].startModel.rho) 863 | end 864 | 865 | # 866 | for k = 1:nsamples 867 | 868 | for ic = 1:nchain 869 | # update model parameters 870 | updatesampleModel!(zNodeArray[ic], rhoArray[ic], k, mcArray[ic]) 871 | if swapchain[3,ic,k] > 0 872 | idx01 = [ swapchain[1,ic,k], swapchain[2,ic,k] ] 873 | idx02 = [ swapchain[2,ic,k], swapchain[1,ic,k] ] 874 | chainidx[idx01] = chainidx[idx02] 875 | 876 | # update chain temperature 877 | tempLadder[idx01] = tempLadder[idx02] 878 | end 879 | end 880 | ind = indexin(sortchain, chainidx) 881 | dataMisfit[k, :] = dataMisfit[k, ind] 882 | 883 | # collect samples after burnin period 884 | if k <= burnin 885 | continue 886 | else 887 | if mod(k, samplestep) > 0; continue; end 888 | 889 | for ic = 1:nchain 890 | if tempLadder[ic] > 1.0; continue; end 891 | nEnsemble += 1 892 | 893 | # posterior cell number 894 | nlayer = mcArray[ic].chainnlayer[k] 895 | nlayerPosterior[nlayer] += 1 896 | 897 | # get depth posterior 898 | for j = 1:nlayer 899 | zLoc = zNodeArray[ic][j] 900 | cidx = findLocation1D(zLoc, zLocBins) 901 | depthPosterior[cidx] += 1 902 | end 903 | 904 | # get values at sampling location 905 | for j = 1:nzBins 906 | zLoc = zLocBins[j] 907 | cidx = findNearest1D(zLoc, zNodeArray[ic][1:nlayer]) 908 | rhoModel[j] = rhoArray[ic][cidx] 909 | idx = findLocation1D(rhoArray[ic][cidx], rhoBins) 910 | valuePosterior[j, idx] += 1 911 | 912 | end 913 | 914 | # statistical moments 915 | sm01 += rhoModel 916 | sm02 += rhoModel .^ 2 917 | 918 | end # ic 919 | 920 | end # if 921 | 922 | end # k 923 | 924 | # evaluate statistical variables 925 | sm01 = sm01 / nEnsemble 926 | sm02 = sm02 / nEnsemble 927 | 928 | # arithmetic mean 929 | valueMean = sm01 930 | 931 | # standard deviation 932 | std = sm02 - sm01 .^ 2 933 | std[std .< 0] .= eps(1.0) 934 | valueStd = sqrt.(std) 935 | 936 | # mode = maximum a posterior 937 | valueMode = zeros(nzBins) 938 | for i = 1:nzBins 939 | (val,idx) = findmax(valuePosterior[i,:]) 940 | valueMode[i] = rhoBins[idx] 941 | end 942 | 943 | # meadian 944 | valueMedian = zeros(nzBins) 945 | for i = 1:nzBins 946 | count = 0 947 | for j = 1:npBins 948 | count += valuePosterior[i, j] 949 | if count > nEnsemble / 2 950 | valueMedian[i] = rhoBins[j] 951 | break 952 | end 953 | end 954 | end 955 | 956 | # output results 957 | outputPosterior(zLocBins, rhoBins, nlayerPosterior, depthPosterior, 958 | valuePosterior, filestr) 959 | 960 | # credible min and max model 961 | ci = mclimits.credInterval 962 | (valueMin,valueMax) = getCredibleIntervalModel(valuePosterior, rhoBins, 963 | nEnsemble, ci) 964 | 965 | # output results 966 | filename = "posteriorModel-"*filestr*".dat" 967 | results = hcat(zLocBins,valueMean,valueMedian,valueMode,valueMin,valueMax,valueStd) 968 | comments = "#z(lg10) mean(lg10) median(lg10) mode(lg10) credmin(lg10) credmax(lg10) std" 969 | outputEnsembleModel(filename, results, comments) 970 | 971 | # data misfit 972 | filename = "chainMisfit-"*filestr*".dat" 973 | outputEnsembleModel(filename, dataMisfit) 974 | 975 | return nEnsemble 976 | 977 | end 978 | 979 | #------------------------------------------------------------------------------ 980 | """ 981 | `clearPTrefence()` 982 | 983 | release all darrays created during the inversion explicitly. 984 | 985 | """ 986 | function clearPTrefence(mcRef::DArray, stRef::DArray, dfRef::DArray, paramRef::DArray) 987 | 988 | # 989 | close(mcRef) 990 | close(stRef) 991 | close(dfRef) 992 | close(paramRef) 993 | mcRef = [] 994 | stRef = [] 995 | dfRef = [] 996 | paramRef = [] 997 | d_closeall() 998 | 999 | end 1000 | 1001 | #------------------------------------------------------------------------------- 1002 | 1003 | end # TBAnalysis 1004 | -------------------------------------------------------------------------------- /src/TBChain/TBChain.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBChain` defines routines to perform transdimensional MCMC sampling. 4 | # 5 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 6 | # 7 | #------------------------------------------------------------------------------- 8 | module TBChain 9 | 10 | using TransdEM.TBUtility 11 | using TransdEM.TBStruct 12 | using TransdEM.TBFileIO 13 | using TransdEM.TBFwdSolver 14 | 15 | # 16 | using LinearAlgebra, Printf, Random 17 | using Distributed, DistributedArrays 18 | 19 | export mcBirth!, mcDeath!, mcMoveLocation!, mcPerturbProperty! 20 | export checkPropertyBounds 21 | export duplicateMCParameter, updateMCParameter! 22 | export selectChainStep! 23 | 24 | #------------------------------------------------------------------------------- 25 | """ 26 | `duplicateMCParameter(mcParam)` 27 | 28 | """ 29 | function duplicateMCParameter(mcParam::MCParameter) 30 | 31 | # 32 | nLayer = mcParam.nLayer 33 | layeridx = mcParam.layeridx 34 | zNode = copy(mcParam.zNode) 35 | rho = copy(mcParam.rho) 36 | inBound = mcParam.inBound 37 | mcParamNew = MCParameter(nLayer, layeridx, zNode, rho, inBound) 38 | 39 | return mcParamNew 40 | 41 | end 42 | 43 | 44 | #------------------------------------------------------------------------------- 45 | """ 46 | `updateMCParameter(mcParam, mcParamNew)` 47 | 48 | """ 49 | function updateMCParameter!(mcParam::MCParameter, mcParamNew::MCParameter) 50 | 51 | # 52 | mcParamNew.nLayer = mcParam.nLayer 53 | mcParamNew.layeridx = mcParam.layeridx 54 | mcParamNew.zNode = copy(mcParam.zNode) 55 | mcParamNew.rho = copy(mcParam.rho) 56 | mcParamNew.inBound = mcParam.inBound 57 | 58 | return mcParamNew 59 | 60 | end 61 | 62 | 63 | #------------------------------------------------------------------------------- 64 | """ 65 | `mcBirth!(mcParam, mcParamProposed, mclimits)` 66 | 67 | add a new layer to current model. 68 | 69 | """ 70 | function mcBirth!(mcParam::MCParameter, 71 | mcParamProposed::MCParameter, 72 | mclimits::MCPrior) 73 | 74 | # check if current layer number is within the prior bound 75 | currLayerNumber = mcParam.nLayer 76 | if currLayerNumber == mclimits.nlayermax 77 | mcParamProposed.inBound = false 78 | return 79 | end 80 | 81 | # 82 | propLayerNumber = currLayerNumber + 1 83 | mcParamProposed.nLayer = propLayerNumber 84 | 85 | # get random location for the new layer interface 86 | zLoc = 0.0 87 | iter = 0 88 | while iter < 1000 89 | iter += 1 90 | zLoc = unirandDouble(mclimits.zmin, mclimits.zmax) 91 | mcParamProposed.zNode[propLayerNumber] = zLoc 92 | 93 | # check if meet the mimimum layer thickness constraint 94 | if mclimits.hmin > 0. 95 | hmin = mclimits.hmin 96 | zNode = copy(mcParamProposed.zNode[1:propLayerNumber]) 97 | sort!(zNode) 98 | zNode = 10 .^ zNode 99 | if minimum(diff(zNode)) > hmin 100 | break 101 | end 102 | else 103 | break 104 | end 105 | end 106 | 107 | # get resistivity for the new layer by first finding the location nearest to 108 | # the new layer in the previous model, then perturb its resistivity 109 | layeridx = findLocation1D(zLoc, mcParam.zNode[1:currLayerNumber]) 110 | mcParam.layeridx = layeridx 111 | mcParamProposed.layeridx = layeridx 112 | 113 | # assign resistivity of the new layer 114 | drho = mclimits.rhostd * randn() 115 | mcParamProposed.rho[propLayerNumber] = mcParam.rho[layeridx] + drho 116 | 117 | # check if the new resistivity value in prior bounds 118 | mcParamProposed.inBound = checkPropertyBounds(mcParamProposed, mclimits) 119 | 120 | if iter == 1000 121 | mcParamProposed.inBound = false 122 | end 123 | 124 | return mcParamProposed 125 | 126 | end 127 | 128 | 129 | #------------------------------------------------------------------------------- 130 | """ 131 | `mcDeath!(mcParam, mcParamProposed, mclimits)` 132 | 133 | delete an exsiting layer from the current model. 134 | 135 | """ 136 | function mcDeath!(mcParam::MCParameter, 137 | mcParamProposed::MCParameter, 138 | mclimits::MCPrior) 139 | 140 | # 141 | currLayerNumber = mcParam.nLayer 142 | if currLayerNumber == mclimits.nlayermin 143 | mcParamProposed.inBound = false 144 | return 145 | end 146 | 147 | # 148 | propLayerNumber = currLayerNumber - 1 149 | mcParamProposed.nLayer = propLayerNumber 150 | 151 | # randomly choose an old layer to delete 152 | layeridx = unirandInteger(1, currLayerNumber) 153 | mcParam.layeridx = layeridx 154 | mcParamProposed.layeridx = layeridx 155 | 156 | # replace the deleted layer with the last layer 157 | zLoc = mcParam.zNode[currLayerNumber] 158 | mcParamProposed.zNode[layeridx] = zLoc 159 | mcParamProposed.rho[layeridx] = mcParam.rho[currLayerNumber] 160 | 161 | # delete the chosen layer 162 | mcParamProposed.zNode[currLayerNumber:mclimits.nlayermax] .= 0. 163 | mcParamProposed.rho[currLayerNumber:mclimits.nlayermax] .= 0. 164 | 165 | return mcParamProposed 166 | end 167 | 168 | 169 | #------------------------------------------------------------------------------- 170 | """ 171 | `mcMoveLocation!(mcParam, mcParamProposed)` 172 | 173 | move the location of one layer interface in the current model. 174 | 175 | """ 176 | function mcMoveLocation!(mcParam::MCParameter, 177 | mcParamProposed::MCParameter, 178 | mclimits::MCPrior) 179 | 180 | # randomly choose a layer to perturb 181 | layeridx = unirandInteger(1, mcParam.nLayer) 182 | mcParam.layeridx = layeridx 183 | mcParamProposed.layeridx = layeridx 184 | 185 | # perturb the location of the chosen layer interface 186 | mcParamProposed.zNode[layeridx] = mcParam.zNode[layeridx] + mclimits.zstd * randn() 187 | 188 | # check if the new location in prior bounds 189 | mcParamProposed.inBound = checkPropertyBounds(mcParamProposed, mclimits) 190 | 191 | return mcParamProposed 192 | end 193 | 194 | 195 | #------------------------------------------------------------------------------- 196 | """ 197 | `mcPerturbProperty!(mcParam, mcParamProposed)` 198 | 199 | perturb the resistivity of one layer in the current model 200 | 201 | """ 202 | function mcPerturbProperty!(mcParam::MCParameter, 203 | mcParamProposed::MCParameter, 204 | mclimits::MCPrior) 205 | 206 | # randomly choose a layer to perturb 207 | layeridx = unirandInteger(1, mcParam.nLayer) 208 | mcParam.layeridx = layeridx 209 | mcParamProposed.layeridx = layeridx 210 | 211 | # perturb the resistivity of the chosen layer 212 | mcParamProposed.rho[layeridx] = mcParam.rho[layeridx] + mclimits.mrhostd * randn() 213 | 214 | # check if the new location in prior bounds 215 | mcParamProposed.inBound = checkPropertyBounds(mcParamProposed, mclimits) 216 | 217 | return mcParamProposed 218 | 219 | end 220 | 221 | 222 | #------------------------------------------------------------------------------- 223 | """ 224 | `checkPropertyBounds(mcParam, mclimits)` 225 | 226 | check if the value of corresponding variables within specified bounds. 227 | 228 | """ 229 | function checkPropertyBounds(mcParam::MCParameter, mclimits::MCPrior) 230 | 231 | # 232 | inBound = true 233 | nLayer = mcParam.nLayer 234 | 235 | # check location of layer interface 236 | if !all(x->(mclimits.zmin<=x<=mclimits.zmax), mcParam.zNode[1:nLayer]) 237 | inBound = false 238 | end 239 | 240 | # check layer resistivity 241 | if !all(x->(mclimits.rhomin<=x<=mclimits.rhomax), mcParam.rho[1:nLayer]) 242 | inBound = false 243 | end 244 | 245 | return inBound 246 | 247 | end 248 | 249 | 250 | #------------------------------------------------------------------------------ 251 | """ 252 | `selectChainStep(iterNo)` 253 | 254 | randomly select a type of model perturbation for model proposal. 255 | 256 | """ 257 | function selectChainStep(iterNo::Int) 258 | 259 | # 260 | num = unirandInteger(1, 16) 261 | if num < 4 262 | step = 1 263 | elseif 3 < num < 7 264 | step = 2 265 | elseif 6 < num < 12 266 | step = 3 267 | else 268 | step = 4 269 | end 270 | 271 | return step 272 | 273 | end # 274 | 275 | 276 | function selectChainStep!(chainstep::MChainStep, iterNo::Int=1) 277 | 278 | # 279 | chainstep.isBirth = false 280 | chainstep.isDeath = false 281 | chainstep.isMove = false 282 | chainstep.isPerturb = false 283 | 284 | # 285 | step = selectChainStep(iterNo) 286 | 287 | if step == 1 288 | chainstep.isBirth = true 289 | elseif step == 2 290 | chainstep.isDeath = true 291 | elseif step == 3 292 | chainstep.isMove = true 293 | elseif step == 4 294 | chainstep.isPerturb = true 295 | else 296 | error("step $(step) is not supported") 297 | end 298 | 299 | return chainstep 300 | 301 | end 302 | 303 | 304 | #------------------------------------------------------------------------------- 305 | include("acceptanceRatio.jl") 306 | include("runMCMC.jl") 307 | include("parallelTempering.jl") 308 | 309 | end # module RJChains 310 | -------------------------------------------------------------------------------- /src/TBChain/acceptanceRatio.jl: -------------------------------------------------------------------------------- 1 | export compDataMisfit 2 | export compAcceptanceRatio 3 | export compBirthAlpha, compDeathAlpha, compPerturbAlpha 4 | export checkMHCriterion! 5 | #------------------------------------------------------------------------------- 6 | """ 7 | `compDataMisfit(obsData, predData, dataErr)` 8 | 9 | compute likelihood and data misfit. 10 | 11 | """ 12 | function compDataMisfit(obsData::Array{T}, predData::Array{T}, 13 | dataErr::Array{T}) where{T<:Float64} 14 | 15 | # 16 | dataMisfit = (obsData .- predData) ./ dataErr 17 | likelihood = 0.5 * dot(dataMisfit, dataMisfit) 18 | dataMisfit = likelihood * 2 19 | 20 | return likelihood, dataMisfit 21 | 22 | end 23 | 24 | 25 | #------------------------------------------------------------------------------- 26 | """ 27 | `compDataMisfit(emData, predData)` 28 | 29 | compute likelihood and data misfit. 30 | 31 | """ 32 | function compDataMisfit(emData::EMData, predData::Array{Float64}) 33 | 34 | obsData = emData.obsData 35 | dataErr = emData.dataErr 36 | (likelihood,dataMisfit) = compDataMisfit(obsData, predData, dataErr) 37 | 38 | return likelihood, dataMisfit 39 | 40 | end 41 | 42 | 43 | #------------------------------------------------------------------------------- 44 | """ 45 | `compAcceptanceRatio(mcParam, mcParamProposed, mcDatafit, mclimits, mcstep)` 46 | 47 | compute the acceptance ratio of current model proposal. 48 | 49 | """ 50 | function compAcceptanceRatio(mcParam::MCParameter, 51 | mcParamProposed::MCParameter, 52 | mcDatafit::MCDataFit, 53 | mclimits::MCPrior, 54 | mcstep::MChainStep, 55 | temperature::Float64=1.0) 56 | 57 | # 58 | currLikelihood = mcDatafit.currLikelihood 59 | propLikelihood = mcDatafit.propLikelihood 60 | 61 | if mcstep.isBirth 62 | alpha = compBirthAlpha(mcParam, mcParamProposed, currLikelihood, 63 | propLikelihood, mclimits, temperature) 64 | 65 | elseif mcstep.isDeath 66 | alpha = compDeathAlpha(mcParam, mcParamProposed, currLikelihood, 67 | propLikelihood, mclimits, temperature) 68 | 69 | elseif mcstep.isMove | mcstep.isPerturb 70 | alpha = compPerturbAlpha(currLikelihood, propLikelihood, temperature) 71 | 72 | end 73 | 74 | return alpha 75 | 76 | end 77 | 78 | 79 | #------------------------------------------------------------------------------- 80 | """ 81 | `compBirthAlpha(propAlpha, currLikelihood, propLikelihood)` 82 | 83 | compute the acceptance probability for a birth step. 84 | 85 | """ 86 | function compBirthAlpha(mcParam::MCParameter, 87 | mcParamProposed::MCParameter, 88 | currLikelihood::T, 89 | propLikelihood::T, 90 | mclimits::MCPrior, 91 | temperature::T=1.0) where{T<:Float64} 92 | 93 | # 94 | vmin = mclimits.rhomin 95 | vmax = mclimits.rhomax 96 | cidx = mcParam.layeridx 97 | pidx = mcParamProposed.nLayer 98 | 99 | # p(m')/p(m) and q(m|m')/q(m'|m) 100 | alpha01 = mclimits.rhostd * sqrt(2*pi) / (vmax - vmin) 101 | alpha01 = log(alpha01) 102 | 103 | alpha02 = (mcParamProposed.rho[pidx] - mcParam.rho[cidx])^2 / (2*mclimits.rhostd^2) 104 | 105 | # p(d|m')/p(d|m) 106 | alpha03 = (-propLikelihood + currLikelihood) / temperature 107 | 108 | # acceptance ratio for birth move 109 | # alpha = propAlpha - log(vmax-vmin) - propLikelihood + currLikelihood 110 | alpha = alpha01 + alpha02 + alpha03 111 | alpha = min(0.0, alpha) 112 | 113 | return alpha 114 | 115 | end 116 | 117 | 118 | #------------------------------------------------------------------------------- 119 | """ 120 | `compDeathAlpha(propAlpha, currLikelihood, propLikelihood)` 121 | 122 | compute the acceptance probability for a death step. 123 | 124 | """ 125 | function compDeathAlpha(mcParam::MCParameter, 126 | mcParamProposed::MCParameter, 127 | currLikelihood::T, 128 | propLikelihood::T, 129 | mclimits::MCPrior, 130 | temperature::T=1.0) where{T<:Float64} 131 | 132 | # 133 | vmin = mclimits.rhomin 134 | vmax = mclimits.rhomax 135 | cidx = mcParam.layeridx 136 | pidx = mcParamProposed.nLayer 137 | 138 | # find the nearest cell in new mesh to the one that was deleted 139 | idx = findLocation1D(mcParam.zNode[cidx], mcParamProposed.zNode[1:pidx]) 140 | 141 | # p(m')/p(m) and q(m|m')/q(m'|m) 142 | alpha01 = (vmax - vmin) / (mclimits.rhostd * sqrt(2*pi) ) 143 | alpha01 = log(alpha01) 144 | 145 | alpha02 = -(mcParamProposed.rho[idx] - mcParam.rho[cidx])^2 / (2*mclimits.rhostd^2) 146 | 147 | # p(d|m')/p(d|m) 148 | alpha03 = (-propLikelihood + currLikelihood) / temperature 149 | 150 | # acceptance ratio for death move 151 | # alpha = propAlpha + log(vmax-vmin) - propLikelihood + currLikelihood 152 | alpha = alpha01 + alpha02 + alpha03 153 | alpha = min(0.0, alpha) 154 | 155 | return alpha 156 | 157 | end 158 | 159 | 160 | #------------------------------------------------------------------------------- 161 | """ 162 | `compPerturbAlpha(currLikelihood, propLikelihood)` 163 | 164 | compute the acceptance probability for a move step. 165 | 166 | """ 167 | function compPerturbAlpha(currLikelihood::T, propLikelihood::T, 168 | temperature::T=1.0) where{T<:Float64} 169 | 170 | # 171 | alpha = (-propLikelihood + currLikelihood) / temperature 172 | alpha = min(0.0, alpha) 173 | 174 | return alpha 175 | 176 | end 177 | 178 | 179 | #------------------------------------------------------------------------------- 180 | """ 181 | `cheackMHCriterion(acceptanceRatio, mcstatus, mcstep)` 182 | 183 | """ 184 | function checkMHCriterion!(acceptanceRatio::Float64, 185 | mcstatus::MCStatus, 186 | mcstep::MChainStep) 187 | 188 | # 189 | mhNumber = rand() 190 | mhNumber = log(mhNumber) 191 | if mhNumber <= acceptanceRatio 192 | mcstatus.accepted = true 193 | if mcstep.isBirth 194 | mcstatus.acceptstats[1] += 1 195 | elseif mcstep.isDeath 196 | mcstatus.acceptstats[2] += 1 197 | elseif mcstep.isMove 198 | mcstatus.acceptstats[3] += 1 199 | elseif mcstep.isPerturb 200 | mcstatus.acceptstats[4] += 1 201 | end 202 | 203 | else 204 | mcstatus.accepted = false 205 | if mcstep.isBirth 206 | mcstatus.rejectstats[1] += 1 207 | elseif mcstep.isDeath 208 | mcstatus.rejectstats[2] += 1 209 | elseif mcstep.isMove 210 | mcstatus.rejectstats[3] += 1 211 | elseif mcstep.isPerturb 212 | mcstatus.rejectstats[4] += 1 213 | end 214 | 215 | end 216 | 217 | return mcstatus 218 | 219 | end 220 | -------------------------------------------------------------------------------- /src/TBChain/parallelTempering.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # define routines to perform parallel tempering of MCMC. 4 | # 5 | #------------------------------------------------------------------------------- 6 | export parallelMCMCsampling 7 | export parallelTemperedMCMC 8 | export selectSwapChains 9 | export compSwapRate 10 | export distTemperingParameter, sendData 11 | export runTemperedMCMC 12 | export TBTemperature 13 | #------------------------------------------------------------------------------- 14 | """ 15 | struct `TBTemperature` encapsalates data related to parallel tempering 16 | 17 | """ 18 | mutable struct TBTemperature{Ti<:Int, Tv<:Float64} 19 | 20 | nT::Ti # total number of temperature values 21 | tempLadder::Vector{Tv} # current temperature ladder 22 | 23 | # record swap history 24 | swapchain::Array{Ti} # dimension=[3xnTxnsample] 25 | 26 | end 27 | 28 | 29 | #------------------------------------------------------------------------------ 30 | """ 31 | `parallelMCMCsampling(mclimits, emData, pids)` 32 | 33 | run multiple Markov chains in parallel without parallel tempering. 34 | 35 | """ 36 | function parallelMCMCsampling(mclimits::MCPrior, emData::EMData, pids::Vector) 37 | 38 | # 39 | np = length(pids) 40 | result = Array{Any}(undef, np) 41 | status = Array{Any}(undef, np) 42 | 43 | # run multiple chains in parallel 44 | i = 1 45 | nextidx() = (idx = i; i+=1; idx) 46 | @sync begin 47 | 48 | for p = pids 49 | @async begin 50 | while true 51 | idx = nextidx() 52 | if idx > np 53 | break 54 | end 55 | @time (result[idx],status[idx]) = remotecall_fetch(runMCMC, p, 56 | mclimits, emData) 57 | end 58 | end # @async 59 | end # p 60 | 61 | end # @sync 62 | 63 | return result, status 64 | 65 | end 66 | 67 | 68 | #------------------------------------------------------------------------------- 69 | """ 70 | `runTemperedMCMC(emData, mclimits, tempData, pids)` 71 | 72 | """ 73 | function runTemperedMCMC(emData::EMData, mclimits::MCPrior, 74 | tempData::TBTemperature, pids::Vector{Int}) 75 | 76 | # check 77 | if tempData.nT != length(pids) 78 | error("The number of workers should be the same with the number of chains") 79 | end 80 | 81 | # initialize and distribute rjmcmc paramters for parallel tempering 82 | (mcRef,stRef,dfRef,paramRef) = distTemperingParameter(emData, mclimits, pids) 83 | 84 | # perform mcmc sampling 85 | iterNo = 0 86 | totalsamples = mclimits.totalsamples 87 | tempLadder = copy(tempData.tempLadder) 88 | while (iterNo < totalsamples) 89 | 90 | iterNo = iterNo + 1 91 | lkhdRef = parallelTemperedMCMC(emData, mclimits, tempLadder, mcRef, 92 | paramRef, stRef, dfRef, pids) 93 | 94 | # do parallel tempering 95 | for k = 1:tempData.nT 96 | # select a pair of chains 97 | (tchain01,tchain02) = selectSwapChains(tempLadder) 98 | tvalue01 = tempLadder[tchain01] 99 | tvalue02 = tempLadder[tchain02] 100 | 101 | # fetch likelihood associated with selected chains 102 | lkhood01 = lkhdRef[tchain01] 103 | lkhood02 = lkhdRef[tchain02] 104 | 105 | # calculate swap rate 106 | doswap = compSwapRate(tchain01,tchain02,lkhood01,lkhood02,tempLadder) 107 | tempData.swapchain[1,k,iterNo] = tchain01 108 | tempData.swapchain[2,k,iterNo] = tchain02 109 | if doswap 110 | tempLadder[tchain01] = tvalue02 111 | tempLadder[tchain02] = tvalue01 112 | tempData.swapchain[3,k,iterNo] = 1 113 | end 114 | # 115 | end 116 | # update temperature ladder 117 | sendData(pids, tempLadder=tempLadder) 118 | 119 | end 120 | 121 | # 122 | return mcRef,stRef,dfRef,paramRef 123 | 124 | end 125 | 126 | 127 | #------------------------------------------------------------------------------- 128 | """ 129 | `parallelTemperedMCMC(emData, mclimits, tempLadder, mcArrayRef, currParamRef, 130 | mcstatusRef, mcDatafitRef, pids)` 131 | 132 | perform parallel tempered MCMC sampling. 133 | 134 | """ 135 | function parallelTemperedMCMC(emData::EMData, 136 | mclimits::MCPrior, 137 | tempLadder::Vector{Float64}, 138 | mcArrayRef::DArray, 139 | currParamRef::DArray, 140 | mcstatusRef::DArray, 141 | mcDatafitRef::DArray, 142 | pids::Vector{Int}) 143 | 144 | # 145 | np = length(pids) 146 | lkhd = zeros(np) 147 | 148 | # run multiple chains in parallel 149 | @sync begin 150 | for (idx,ip) in enumerate(pids) 151 | @async lkhd[idx] = remotecall_fetch(parallelTemperedMCMC,ip,emData, 152 | mclimits,tempLadder,mcArrayRef,currParamRef,mcstatusRef,mcDatafitRef) 153 | end 154 | 155 | end # @sync 156 | 157 | return lkhd 158 | 159 | end 160 | 161 | 162 | #------------------------------------------------------------------------------- 163 | """ 164 | `parallelTemperedMCMC(emData, mclimits, tempLadder, mcArrayRef, currParamRef, 165 | mcstatusRef, mcDatafitRef)` 166 | 167 | perform parallel tempered MCMC sampling. 168 | 169 | """ 170 | function parallelTemperedMCMC(emData::EMData, 171 | mclimits::MCPrior, 172 | tempLadder::Vector{Float64}, 173 | mcArrayRef::DArray, 174 | currParamRef::DArray, 175 | mcstatusRef::DArray, 176 | mcDatafitRef::DArray) 177 | 178 | # fetch data from worker 179 | pid = myid() 180 | idx = pid - 1 181 | temperature = tempLadder[idx] 182 | 183 | # fetch data from worker 184 | mcArray = localpart(mcArrayRef)[1] 185 | mcstatus = localpart(mcstatusRef)[1] 186 | mcDatafit = localpart(mcDatafitRef)[1] 187 | mcParamCurrent = localpart(currParamRef)[1] 188 | 189 | # reset proposed and delayed models 190 | mcParamProposed = duplicateMCParameter(mcParamCurrent) 191 | mcDatafit.propPredData = copy(mcDatafit.currPredData) 192 | mcDatafit.propLikelihood = mcDatafit.currLikelihood 193 | mcDatafit.propMisfit = mcDatafit.currMisfit 194 | 195 | # randomly select a McMC chain step 196 | rjstep = MChainStep(false, false, false, false) 197 | rjstep = selectChainStep!(rjstep) 198 | 199 | # which step do we have 200 | if rjstep.isBirth # birth step 201 | mcBirth!(mcParamCurrent, mcParamProposed, mclimits) 202 | 203 | elseif rjstep.isDeath # death step 204 | mcDeath!(mcParamCurrent, mcParamProposed, mclimits) 205 | 206 | elseif rjstep.isMove # move step 207 | mcMoveLocation!(mcParamCurrent, mcParamProposed, mclimits) 208 | 209 | elseif rjstep.isPerturb # perturb step 210 | mcPerturbProperty!(mcParamCurrent, mcParamProposed, mclimits) 211 | 212 | end 213 | 214 | # check if proposed model is within the prior bounds 215 | if mcParamProposed.inBound 216 | 217 | # get the predicted data for the proposed model 218 | mcDatafit.propPredData = getPredData(emData, mcParamProposed) 219 | 220 | # get model likelihood and data misfit 221 | (mcDatafit.propLikelihood,mcDatafit.propMisfit) = compDataMisfit(emData, 222 | mcDatafit.propPredData) 223 | 224 | # calculate acceptance ratio of rjMcMC chain 225 | propAlpha = compAcceptanceRatio(mcParamCurrent, mcParamProposed, 226 | mcDatafit, mclimits, rjstep, temperature) 227 | 228 | # check if Metropolis-Hasting acceptance criterion is meeted 229 | checkMHCriterion!(propAlpha, mcstatus, rjstep) 230 | 231 | end # 232 | 233 | # update markov chain 234 | if mcParamProposed.inBound & mcstatus.accepted 235 | updateChainModel!(mcParamCurrent, mcParamProposed, mcDatafit) 236 | # data residuals 237 | mcDatafit.dataResiduals = emData.obsData - mcDatafit.propPredData 238 | end 239 | 240 | # record in chainarray 241 | updateChainArray!(mcArray, mcParamCurrent, rjstep, mcstatus, mcDatafit) 242 | 243 | # print iteration info 244 | cycleNumber = 1000 245 | iterNo = mcArray.nsample 246 | if mod(iterNo, cycleNumber) == 0 247 | println("iterNo=$(iterNo), dataMisfit=$(mcDatafit.currMisfit)") 248 | end 249 | 250 | # update associated stuff in the worker 251 | localpart(mcArrayRef)[1] = mcArray 252 | localpart(mcstatusRef)[1] = mcstatus 253 | localpart(mcDatafitRef)[1] = mcDatafit 254 | localpart(currParamRef)[1] = mcParamCurrent 255 | 256 | # return reference to current likelihood 257 | currLikelihood = mcDatafit.currLikelihood 258 | 259 | return currLikelihood 260 | 261 | end 262 | 263 | 264 | #------------------------------------------------------------------------------- 265 | """ 266 | `selectSwapChains(tempLadder)` 267 | 268 | select a pair of chains to swap. 269 | 270 | """ 271 | function selectSwapChains(tempLadder::Vector) 272 | 273 | # first select one chain randomly 274 | nchain = length(tempLadder) 275 | tchain = randperm(nchain)[1:2] 276 | tchain01 = tchain[1] 277 | tchain02 = tchain[2] 278 | tvalue01 = tempLadder[tchain01] 279 | tvalue02 = tempLadder[tchain02] 280 | while tvalue01 == tvalue02 281 | tchain02 = unirandInteger(1, nchain) 282 | tvalue02 = tempLadder[tchain02] 283 | end 284 | 285 | return tchain01, tchain02 286 | 287 | end 288 | 289 | 290 | #------------------------------------------------------------------------------- 291 | """ 292 | `compSwapRate(tchain01, tchain02, lkhood01, lkhood02, tempLadder)` 293 | 294 | calculate the swap rate and determine whether to swap the two chain selected. 295 | 296 | """ 297 | function compSwapRate(tchain01::Ti, tchain02::Ti, lkhood01::Tv, 298 | lkhood02::Tv, tempLadder::Vector{Tv}) where {Ti<:Int, Tv<:Float64} 299 | 300 | # 301 | doswap = false 302 | tvalue01 = tempLadder[tchain01] 303 | tvalue02 = tempLadder[tchain02] 304 | 305 | # calculate swap rate 306 | srate01 = -lkhood01/tvalue02 + lkhood02/tvalue02 307 | srate02 = -lkhood02/tvalue01 + lkhood01/tvalue01 308 | srate = srate01 + srate02 309 | srate = min(0.0, srate) 310 | rvalue = log(rand()) 311 | if rvalue <= srate 312 | doswap = true 313 | end 314 | return doswap 315 | 316 | end 317 | 318 | 319 | #------------------------------------------------------------------------------- 320 | """ 321 | `distTemperingParameter(mcParamCurrent, mcstatus, mcArray, mcstatus, mcDatafit 322 | pids)` 323 | 324 | initialize parameters for parallel tempering. 325 | 326 | """ 327 | function distTemperingParameter(emData::EMData, mclimits::MCPrior, pids::Vector, 328 | bdsteps=zeros(0), rhosteps=zeros(0), zsteps=zeros(0)) 329 | 330 | # 331 | np = length(pids) 332 | # 333 | mcArrayRef = Array{MChainArray}(undef, np) 334 | mcstatusRef = Array{MCStatus}( undef, np) 335 | mcDatafitRef = Array{MCDataFit}( undef, np) 336 | currParamRef = Array{MCParameter}(undef, np) 337 | 338 | for i = 1:np 339 | 340 | if !isempty(bdsteps) 341 | mclimits.rhostd = bdsteps[i] 342 | end 343 | if !isempty(rhosteps) 344 | mclimits.mrhostd = rhosteps[i] 345 | end 346 | if !isempty(zsteps) 347 | mclimits.zstd = zsteps[i] 348 | end 349 | 350 | # initialize model parameter 351 | mcstatus = MCStatus(false, zeros(Int,4), zeros(Int,4)) 352 | mcDatafit = initMCDataFit() 353 | mcArray = initMChainArray(mclimits) 354 | mcParamCurrent = initModelParameter(mclimits) 355 | 356 | # compute predicted data, likelihood and data misfit 357 | currPredData = getPredData(emData, mcParamCurrent) 358 | (currLikelihood, currMisfit) = compDataMisfit(emData, currPredData) 359 | mcDatafit.currPredData = currPredData 360 | mcDatafit.currLikelihood = currLikelihood 361 | mcDatafit.currMisfit = currMisfit 362 | println("starting data misfit = $(mcDatafit.currMisfit)") 363 | 364 | # record starting model 365 | mcArray.startModel = duplicateMCParameter(mcParamCurrent) 366 | mcArrayRef[i] = mcArray 367 | mcstatusRef[i] = mcstatus 368 | mcDatafitRef[i] = mcDatafit 369 | currParamRef[i] = mcParamCurrent 370 | 371 | end 372 | 373 | # distributed array 374 | mcArrayDA = distribute(mcArrayRef, procs=pids, dist=[np]) 375 | mcstatusDA = distribute(mcstatusRef, procs=pids, dist=[np]) 376 | mcDatafitDA = distribute(mcDatafitRef, procs=pids, dist=[np]) 377 | currParamDA = distribute(currParamRef, procs=pids, dist=[np]) 378 | 379 | return mcArrayDA, mcstatusDA, mcDatafitDA, currParamDA 380 | 381 | end # 382 | 383 | 384 | #------------------------------------------------------------------------------ 385 | """ 386 | `sendData()` sends an arbitrary number of variables to specified processors 387 | 388 | """ 389 | function sendData(p::Int; args...) 390 | for (nm, val) in args 391 | @spawnat(p, Core.eval(Main, Expr(:(=), nm, val))) 392 | end 393 | end 394 | 395 | #------------------------------------------------------------------------------ 396 | """ 397 | `sendData()` sends an arbitrary number of variables to specified processors 398 | 399 | """ 400 | function sendData(p::Vector{Int}; args...) 401 | for pid in p 402 | sendData(pid; args...) 403 | end 404 | end 405 | 406 | #------------------------------------------------------------------------------- 407 | -------------------------------------------------------------------------------- /src/TBChain/runMCMC.jl: -------------------------------------------------------------------------------- 1 | export runMCMC 2 | export MCMCsampling! 3 | export resetChainModel! 4 | export updateChainModel! 5 | export updateChainArray! 6 | export continueMCMCsampling 7 | export readsamplefile 8 | 9 | #------------------------------------------------------------------------------ 10 | function runMCMC(mclimits::MCPrior, emData::EMData) 11 | 12 | # initialize model parameter 13 | mcParamCurrent = initModelParameter(mclimits) 14 | mcDatafit = initMCDataFit() 15 | mcstep = MChainStep(false, false, false, false) 16 | mcstatus = MCStatus(false,zeros(Int,4),zeros(Int,4)) 17 | 18 | # compute predicted data 19 | currPredData = getPredData(emData, mcParamCurrent) 20 | residuals = emData.obsData - currPredData 21 | 22 | # calculate likelihood and data misfit 23 | (currLikelihood,currMisfit) = compDataMisfit(emData, currPredData) 24 | 25 | mcDatafit.currPredData = currPredData 26 | mcDatafit.currLikelihood = currLikelihood 27 | mcDatafit.currMisfit = currMisfit 28 | 29 | # initialize proposed model 30 | mcParamProposed = duplicateMCParameter(mcParamCurrent) 31 | 32 | # start MCMC sampling 33 | mcArray = MCMCsampling!(mcParamCurrent, mcParamProposed, mcstep, 34 | mclimits, mcstatus, mcDatafit, emData) 35 | 36 | return mcArray, mcstatus 37 | 38 | end 39 | 40 | 41 | #------------------------------------------------------------------------------ 42 | """ 43 | `MCMCsampling(mcParamCurrent,mcParamProposed,mcstep,mclimits,mcstatus, 44 | mcDatafit,emData)` 45 | 46 | produces an ensemble of samples with rjMCMC algorithm. 47 | 48 | """ 49 | function MCMCsampling!(mcParamCurrent::T, 50 | mcParamProposed::T, 51 | mcstep::MChainStep, 52 | mclimits::MCPrior, 53 | mcstatus::MCStatus, 54 | mcDatafit::MCDataFit, 55 | emData::EMData) where {T<:MCParameter} 56 | 57 | # mcmc information 58 | mcArray = initMChainArray(mclimits) 59 | 60 | # record starting model 61 | mcArray.startModel = duplicateMCParameter(mcParamCurrent) 62 | 63 | iterNo = 0 64 | cycleNumber = 1000 65 | totalsamples = mclimits.totalsamples 66 | while (iterNo < totalsamples) 67 | 68 | iterNo = iterNo + 1 69 | # reset proposed models 70 | resetChainModel!(mcParamCurrent, mcParamProposed, mcDatafit) 71 | 72 | # randomly select a type of model perturbation 73 | mcstep = selectChainStep!(mcstep, iterNo) 74 | 75 | # which step do we have 76 | if mcstep.isBirth # birth step 77 | mcBirth!(mcParamCurrent, mcParamProposed, mclimits) 78 | 79 | elseif mcstep.isDeath # death step 80 | mcDeath!(mcParamCurrent, mcParamProposed, mclimits) 81 | 82 | elseif mcstep.isMove # move step 83 | mcMoveLocation!(mcParamCurrent, mcParamProposed, mclimits) 84 | 85 | elseif mcstep.isPerturb # perturb step 86 | mcPerturbProperty!(mcParamCurrent, mcParamProposed, mclimits) 87 | 88 | end 89 | 90 | # check if proposed model is within the prior bounds 91 | if mcParamProposed.inBound 92 | 93 | if mod(iterNo, cycleNumber) == 0 94 | println("iterNo=$(iterNo), dataMisfit=$(mcDatafit.currMisfit)") 95 | end 96 | 97 | # get the predicted data for the proposed model 98 | mcDatafit.propPredData = getPredData(emData, mcParamProposed) 99 | 100 | # get model likelihood and data misfit 101 | (mcDatafit.propLikelihood,mcDatafit.propMisfit) = compDataMisfit(emData, 102 | mcDatafit.propPredData) 103 | 104 | # calculate acceptance ratio of rjMcMC chain 105 | propAlpha = compAcceptanceRatio(mcParamCurrent, mcParamProposed, 106 | mcDatafit, mclimits, mcstep) 107 | 108 | # check if Metropolis-Hasting acceptance criterion is meeted 109 | checkMHCriterion!(propAlpha, mcstatus, mcstep) 110 | 111 | end # 112 | 113 | # update markov chain 114 | if mcParamProposed.inBound & mcstatus.accepted 115 | updateChainModel!(mcParamCurrent, mcParamProposed, mcDatafit) 116 | 117 | # data residuals 118 | mcDatafit.dataResiduals = emData.obsData - mcDatafit.propPredData 119 | end 120 | 121 | # record in chainarray 122 | updateChainArray!(mcArray, mcParamCurrent, mcstep, mcstatus, mcDatafit) 123 | 124 | end 125 | 126 | return mcArray 127 | 128 | end 129 | 130 | 131 | #------------------------------------------------------------------------------ 132 | """ 133 | `resetChainModel(mcParam, mcParamProposed, mcDatafit)` 134 | 135 | reset proposed chain model. 136 | 137 | """ 138 | function resetChainModel!(mcParam::T, 139 | mcParamProposed::T, 140 | mcDatafit::MCDataFit) where{T<:MCParameter} 141 | 142 | # reset proposed model 143 | updateMCParameter!(mcParam, mcParamProposed) 144 | 145 | # update predicted data for proposed model 146 | mcDatafit.propPredData = copy(mcDatafit.currPredData) 147 | mcDatafit.propLikelihood = mcDatafit.currLikelihood 148 | mcDatafit.propMisfit = mcDatafit.currMisfit 149 | 150 | return mcParamProposed, mcDatafit 151 | 152 | end 153 | 154 | 155 | #------------------------------------------------------------------------------- 156 | """ 157 | `updateChainModel!(mcParamCurrent, rjdatafit)` 158 | 159 | """ 160 | function updateChainModel!(mcParamCurrent::MCParameter, 161 | mcParamProposed::MCParameter, 162 | rjdatafit::MCDataFit) 163 | 164 | # model update 165 | mcParamCurrent = updateMCParameter!(mcParamProposed, mcParamCurrent) 166 | 167 | # 168 | rjdatafit.currPredData = copy(rjdatafit.propPredData) 169 | rjdatafit.currLikelihood = copy(rjdatafit.propLikelihood) 170 | rjdatafit.currMisfit = copy(rjdatafit.propMisfit) 171 | 172 | return mcParamCurrent, rjdatafit 173 | 174 | end 175 | 176 | 177 | #------------------------------------------------------------------------------- 178 | """ 179 | `updateChainArray(mcArray, iterNo, mcParamCurrent, mcstep, mcstatus, mcDatafit)` 180 | 181 | """ 182 | function updateChainArray!(mcArray::MChainArray, 183 | mcParamCurrent::MCParameter, 184 | mcstep::MChainStep, 185 | mcstatus::MCStatus, 186 | mcDatafit::MCDataFit) 187 | 188 | # 189 | mcArray.nsample += 1 190 | iterNo = mcArray.nsample 191 | nLayer = mcParamCurrent.nLayer 192 | layeridx = mcParamCurrent.layeridx 193 | mcArray.chainnlayer[iterNo] = nLayer 194 | mcArray.chainMisfit[iterNo] = mcDatafit.currMisfit 195 | 196 | if mcstep.isBirth 197 | mcArray.chainstep[iterNo, 1] = 1 198 | if mcstatus.accepted 199 | mcArray.chainstep[iterNo, 2] = 1 200 | mcArray.chainindice[iterNo] = 0 201 | mcArray.chainvalue[iterNo, 1] = mcParamCurrent.zNode[nLayer] 202 | mcArray.chainvalue[iterNo, 2] = mcParamCurrent.rho[nLayer] 203 | end 204 | 205 | elseif mcstep.isDeath 206 | mcArray.chainstep[iterNo, 1] = 2 207 | if mcstatus.accepted 208 | mcArray.chainstep[iterNo, 2] = 1 209 | mcArray.chainindice[iterNo] = layeridx 210 | mcArray.chainvalue[iterNo, 1] = 0. 211 | mcArray.chainvalue[iterNo, 2] = 0. 212 | end 213 | 214 | elseif mcstep.isMove 215 | mcArray.chainstep[iterNo, 1] = 3 216 | if mcstatus.accepted 217 | mcArray.chainstep[iterNo, 2] = 1 218 | mcArray.chainindice[iterNo] = layeridx 219 | mcArray.chainvalue[iterNo, 1] = mcParamCurrent.zNode[layeridx] 220 | mcArray.chainvalue[iterNo, 2] = 0. 221 | end 222 | 223 | elseif mcstep.isPerturb 224 | mcArray.chainstep[iterNo, 1] = 4 225 | if mcstatus.accepted 226 | mcArray.chainstep[iterNo, 2] = 1 227 | mcArray.chainindice[iterNo] = layeridx 228 | mcArray.chainvalue[iterNo, 1] = 0. 229 | mcArray.chainvalue[iterNo, 2] = mcParamCurrent.rho[layeridx] 230 | end 231 | 232 | end 233 | 234 | # data residuals 235 | # if mcstatus.accepted 236 | # mcArray.residuals[:, iterNo] = mcDatafit.dataResiduals 237 | # end 238 | 239 | # reset status 240 | mcstatus.accepted = false 241 | 242 | end 243 | 244 | 245 | #------------------------------------------------------------------------------ 246 | """ 247 | `continueMCMCsampling(samplefile, mclimits, emData)` 248 | 249 | continue a MCMC sampling from previous Markov chain. 250 | 251 | """ 252 | function continueMCMCsampling(samplefile::String, mclimits::MCPrior, emData::EMData) 253 | 254 | # initialize model parameter 255 | mcDatafit = initMCDataFit() 256 | mcstep = MChainStep(false, false, false, false) 257 | mcstatus = MCStatus(false,zeros(Int,4),zeros(Int,4)) 258 | 259 | # get the last sample of previous Markov chain 260 | (mcParamCurrent, mcArrayPrev) = readsamplefile(samplefile) 261 | 262 | # compute predicted data 263 | currPredData = getPredData(emData, mcParamCurrent) 264 | residuals = emData.obsData - currPredData 265 | 266 | # calculate likelihood and data misfit 267 | (currLikelihood,currMisfit) = compDataMisfit(emData, currPredData) 268 | 269 | mcDatafit.currPredData = currPredData 270 | mcDatafit.currLikelihood = currLikelihood 271 | mcDatafit.currMisfit = currMisfit 272 | 273 | # initialize proposed model 274 | mcParamProposed = duplicateMCParameter(mcParamCurrent) 275 | 276 | # start MCMC sampling 277 | mcArray = MCMCsampling!(mcParamCurrent, mcParamProposed, mcstep, 278 | mclimits, mcstatus, mcDatafit, emData) 279 | 280 | return mcArray, mcstatus 281 | 282 | 283 | end 284 | 285 | 286 | #------------------------------------------------------------------------------ 287 | """ 288 | `readsamplefile(samplefile, mclimits)` 289 | 290 | get the last sample of the Markov chain, which will be used as the current sample 291 | for a new Markov chain. 292 | 293 | """ 294 | function readsamplefile(samplefile::String, mclimits::MCPrior) 295 | 296 | # 297 | if isfile(samplefile) 298 | fid = open(samplefile, "r") 299 | else 300 | error("$(samplefile) does not exist, please try again.") 301 | end 302 | 303 | # mcmc information 304 | mcArray = initMChainArray(mclimits) 305 | zNode = [] 306 | rho = [] 307 | nsample = 0 308 | while !eof(fid) 309 | cline = strip(readline(fid)) 310 | 311 | # ignore all comments: empty line, or line preceded with ! 312 | while cline[1] == '!' || isempty(cline) 313 | cline = strip(readline(fid)) 314 | end 315 | cline = lowercase(cline) 316 | 317 | if occursin("zcoordinate", cline) 318 | tmp = split(cline) 319 | nz = parse(Int, tmp[end]) 320 | zNode = zeros(Float64, nz) 321 | cline = strip(readline(fid)) 322 | cline = split(cline) 323 | for k = 1:nz 324 | zNode[k] = parse(Float64, cline[k]) 325 | end 326 | 327 | elseif occursin("resistivity", cline) 328 | tmp = split(cline) 329 | nz = parse(Int, tmp[end]) 330 | rho = zeros(Float64, nz) 331 | cline = strip(readline(fid)) 332 | cline = split(cline) 333 | for k = 1:nz 334 | rho[k] = parse(Float64, cline[k]) 335 | end 336 | 337 | elseif occursin("samplelist", cline) 338 | tmp = split(cline) 339 | nsample = parse(Int, tmp[end]) 340 | for k = 1:nsample 341 | cline = strip(readline(fid)) 342 | cline = split(cline) 343 | mcArray.chainstep[k, :] = parse.(Float64, cline[2:3]) 344 | mcArray.chainvalue[k,:] = parse.(Float64, cline[4:5]) 345 | mcArray.chainindice[k] = parse(Float64, cline[6]) 346 | mcArray.chainncell[k] = parse(Float64, cline[7]) 347 | mcArray.chainMisfit[k] = parse(Float64, cline[8]) 348 | end 349 | 350 | end # 351 | 352 | end # while 353 | 354 | close(fid) 355 | 356 | # update model parameters 357 | for k = 1:nsample 358 | updatesampleModel!(zNode, rho, k, mcArray) 359 | end 360 | 361 | mcParam = initModelParameter(mclimits) 362 | mcParam.zNode = copy(zNode) 363 | mcParam.rho = copy(rho) 364 | 365 | return mcParam, mcArray 366 | 367 | end 368 | -------------------------------------------------------------------------------- /src/TBFileIO/TBFileIO.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBFileIO` defines routines for prior information and EM data IO. 4 | # 5 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 6 | # 7 | #------------------------------------------------------------------------------- 8 | module TBFileIO 9 | 10 | using TransdEM.TBStruct 11 | using Printf, SparseArrays 12 | 13 | # 14 | export TEMData, CSEMData, MTData 15 | export EMData 16 | abstract type EMData end 17 | 18 | #------------------------------------------------------------------------------- 19 | """ 20 | struct `TEMData` encapsalates TEM data structure. 21 | 22 | """ 23 | mutable struct TEMData{T<:Real} <: EMData 24 | # 25 | loopxLen::T 26 | loopyLen::T 27 | timePoint::Vector{T} 28 | obsData::Vector{T} 29 | dataErr::Vector{T} 30 | 31 | end 32 | 33 | 34 | #------------------------------------------------------------------------------- 35 | """ 36 | struct `CSEMData` encapsalates CSEM data structure. 37 | 38 | """ 39 | mutable struct CSEMData{T<:Real} <: EMData 40 | 41 | # 42 | seawater::Vector{T} # [thickness,sigma] of seawater 43 | txLoc::Array{T} # source location 44 | rxLoc::Array{T} # receiver location 45 | dpLen::T # dipole length 46 | freqArray::Vector{T} # frequency array 47 | dataType::Vector{String} # data type 48 | dataID::Vector{Bool} # data index 49 | # 50 | obsData::Vector{T} 51 | dataErr::Vector{T} 52 | 53 | end 54 | 55 | 56 | #------------------------------------------------------------------------------- 57 | """ 58 | struct `MTData` encapsalates MT data structure. 59 | 60 | """ 61 | mutable struct MTData{T<:Real} <: EMData 62 | 63 | # 64 | freqArray::Vector{T} # frequency array 65 | dataType::Vector{String} # data type 66 | dataID::Vector{Bool} # data index 67 | # 68 | obsData::Vector{T} 69 | dataErr::Vector{T} 70 | 71 | end 72 | 73 | #------------------------------------------------------------------------------- 74 | include("readEMData.jl") 75 | include("writeEMData.jl") 76 | include("readstartupFile.jl") 77 | 78 | end # module 79 | -------------------------------------------------------------------------------- /src/TBFileIO/readEMData.jl: -------------------------------------------------------------------------------- 1 | export readTEMData, readMTData, readCSEMData 2 | export readMTEDIFile, readEDISection 3 | #------------------------------------------------------------------------------- 4 | """ 5 | `readTEMData(datafile)` 6 | 7 | """ 8 | function readTEMData(datafile::String) 9 | 10 | if isfile(datafile) 11 | fid = open(datafile, "r") 12 | else 13 | error("$(datafile) does not exist, please try again.") 14 | end 15 | 16 | loopxLen = []; loopyLen = [] 17 | timePoint = []; obsData = []; dataErr = [] 18 | while !eof(fid) 19 | 20 | cline = strip(readline(fid)) 21 | 22 | # ignore all comments: empty line, or line preceded with # 23 | while cline[1] == '#' || isempty(cline) 24 | cline = strip(readline(fid)) 25 | end 26 | 27 | # 28 | if occursin("LoopWidth(m)", cline) 29 | tmp = split(cline) 30 | loopxLen = parse(Float64, tmp[end-1]) 31 | loopyLen = parse(Float64, tmp[end-1]) 32 | 33 | elseif occursin("Data Block:", cline) 34 | tmp = split(cline) 35 | nData = parse(Int, tmp[end]) 36 | nd = 0 37 | timePoint = zeros(Float64, nData) 38 | obsData = zeros(Float64, nData) 39 | dataErr = zeros(nData) 40 | while nd < nData 41 | cline = strip(readline(fid)) 42 | while cline[1] == '#' || isempty(cline) 43 | cline = strip(readline(fid)) 44 | end 45 | cline = split(cline) 46 | nd = nd + 1 47 | timePoint[nd] = parse(Float64, cline[1]) 48 | obsData[nd] = parse(Float64, cline[2]) 49 | dataErr[nd] = parse(Float64, cline[3]) 50 | 51 | end 52 | 53 | end 54 | 55 | end 56 | close(fid) 57 | 58 | temData = TEMData(loopxLen,loopyLen,timePoint,obsData,dataErr) 59 | return temData 60 | 61 | end 62 | 63 | 64 | #------------------------------------------------------------------------------- 65 | """ 66 | readMTData(datafile) 67 | 68 | reads MT data file for MT problem. 69 | """ 70 | function readMTData(datafile::String) 71 | 72 | if isfile(datafile) 73 | fid = open(datafile, "r") 74 | else 75 | error("$(datafile) does not exist, please try again.") 76 | end 77 | 78 | rxLoc = []; freqID = [] 79 | dtID = []; obsData = [] 80 | dataErr = [] 81 | dataType = [] 82 | freqArray = [] 83 | nf = 0 84 | nDt = 0 85 | while !eof(fid) 86 | 87 | cline = strip(readline(fid)) 88 | 89 | # ignore all comments: empty line, or line preceded with # 90 | while cline[1] == '#' || isempty(cline) 91 | cline = strip(readline(fid)) 92 | end 93 | 94 | # data format 95 | if occursin("Format", cline) 96 | tmp = split(cline) 97 | format = tmp[2] 98 | 99 | # receiver location 100 | elseif occursin("Receiver Location", cline) 101 | tmp = split(cline) 102 | nr = parse(Int, tmp[end]) 103 | nd = 0 104 | rxLoc = zeros(nr, 3) 105 | while nd < nr 106 | cline = strip(readline(fid)) 107 | while cline[1] == '#' || isempty(cline) 108 | cline = strip(readline(fid)) 109 | end 110 | cline = split(cline) 111 | nd = nd + 1 112 | for j = 1:3 113 | rxLoc[nd, j] = parse(Float64, cline[j]) 114 | end 115 | end 116 | 117 | # frequencies 118 | elseif occursin("Frequencies", cline) 119 | tmp = split(cline) 120 | nf = parse(Int, tmp[end]) 121 | nd = 0 122 | freqArray = zeros(nf) 123 | while nd < nf 124 | cline = strip(readline(fid)) 125 | while cline[1] == '#' || isempty(cline) 126 | cline = strip(readline(fid)) 127 | end 128 | #cline = split(cline) 129 | nd = nd + 1 130 | freqArray[nd] = parse(Float64, cline) 131 | end 132 | 133 | # data type 134 | elseif occursin("DataType", cline) 135 | tmp = split(cline) 136 | nDt = parse(Int, tmp[end]) 137 | dataType = Array{String}(undef, nDt) 138 | nd = 0 139 | while nd < nDt 140 | cline = strip(readline(fid)) 141 | nd = nd + 1 142 | dataType[nd] = cline 143 | end 144 | 145 | # data block 146 | elseif occursin("Data Block", cline) 147 | tmp = split(cline) 148 | nData = parse(Int, tmp[end]) 149 | nd = 0 150 | rxID = zeros(Int, nData) 151 | dtID = zeros(Int, nData) 152 | freqID = zeros(Int, nData) 153 | obsData = zeros(Float64, nData) 154 | dataErr = zeros(nData) 155 | 156 | while nd < nData 157 | cline = strip(readline(fid)) 158 | while cline[1] == '#' || isempty(cline) 159 | cline = strip(readline(fid)) 160 | end 161 | cline = split(cline) 162 | nd = nd + 1 163 | freqID[nd] = parse(Int, cline[1]) 164 | dtID[nd] = parse(Int, cline[2]) 165 | obsData[nd] = parse(Float64, cline[3]) 166 | dataErr[nd] = parse(Float64, cline[4]) 167 | 168 | end 169 | 170 | end 171 | 172 | end 173 | close(fid) 174 | 175 | # define dataID 176 | dataID = zeros(Bool,nDt,nf) 177 | for k = 1:length(obsData) 178 | fidx = freqID[k] 179 | didx = dtID[k] 180 | dataID[didx,fidx] = true 181 | end 182 | dataID = vec(dataID) 183 | mtData = MTData(freqArray,dataType,dataID,obsData,dataErr) 184 | 185 | return mtData 186 | 187 | end 188 | 189 | 190 | #------------------------------------------------------------------------------- 191 | """ 192 | readCSEMData(datafile) 193 | 194 | read CSEM data information from file. 195 | 196 | """ 197 | function readCSEMData(datafile::String) 198 | 199 | if isfile(datafile) 200 | fid = open(datafile, "r") 201 | else 202 | error("$(datafile) does not exist, please try again.") 203 | end 204 | 205 | txLoc = []; rxLoc = []; 206 | txID = []; rxID = []; freqID = [] 207 | dtID = []; obsData = []; dataErr = [] 208 | dataType = [] 209 | freqArray = [] 210 | phaseCon = [] 211 | dpLen = 0.0 212 | seawater = zeros(0) 213 | ns = 0; nr = 0; nf = 0; nDt = 0 214 | while !eof(fid) 215 | 216 | cline = strip(readline(fid)) 217 | 218 | # ignore all comments: empty line, or line preceded with # 219 | while cline[1] == '#' || isempty(cline) 220 | cline = strip(readline(fid)) 221 | end 222 | 223 | # data format 224 | if occursin("Format", cline) 225 | tmp = split(cline) 226 | format = tmp[2] 227 | 228 | # source type 229 | elseif occursin("SeaLayer", cline) 230 | tmp = split(cline) 231 | seawater = zeros(2) 232 | seawater[1] = parse(Float64, tmp[end-1]) 233 | seawater[2] = parse(Float64, tmp[end]) 234 | 235 | # dipole length, optional 236 | elseif occursin("Dipole Length", cline) 237 | tmp = split(cline) 238 | dpLen = parse(Float64, tmp[end]) 239 | 240 | # phase convention 241 | elseif occursin("Phase Convention", cline) 242 | tmp = split(cline) 243 | phaseCon = string(tmp[end]) 244 | 245 | # source location 246 | elseif occursin("Source Location", cline) 247 | tmp = split(cline) 248 | ns = parse(Int, tmp[end]) 249 | nd = 0 250 | txLoc = zeros(ns, 5) 251 | 252 | while nd < ns 253 | cline = strip(readline(fid)) 254 | while cline[1] == '#' || isempty(cline) 255 | cline = strip(readline(fid)) 256 | end 257 | cline = split(cline) 258 | nd = nd + 1 259 | for j = 1:5 260 | txLoc[nd,j] = parse(Float64, cline[j]) 261 | end 262 | end 263 | 264 | # receiver location 265 | elseif occursin("Receiver Location", cline) 266 | tmp = split(cline) 267 | nr = parse(Int, tmp[end]) 268 | nd = 0 269 | rxLoc = zeros(nr, 3) 270 | while nd < nr 271 | cline = strip(readline(fid)) 272 | while cline[1] == '#' || isempty(cline) 273 | cline = strip(readline(fid)) 274 | end 275 | cline = split(cline) 276 | nd = nd + 1 277 | for j = 1:3 278 | rxLoc[nd,j] = parse(Float64, cline[j]) 279 | end 280 | end 281 | 282 | # frequencies 283 | elseif occursin("Frequencies", cline) 284 | tmp = split(cline) 285 | nf = parse(Int, tmp[end]) 286 | nd = 0 287 | freqArray = zeros(nf) 288 | while nd < nf 289 | cline = strip(readline(fid)) 290 | while cline[1] == '#' || isempty(cline) 291 | cline = strip(readline(fid)) 292 | end 293 | #cline = split(cline) 294 | nd = nd + 1 295 | freqArray[nd] = parse(Float64, cline) 296 | end 297 | 298 | # data type 299 | elseif occursin("DataType", cline) 300 | tmp = split(cline) 301 | nDt = parse(Int, tmp[end]) 302 | dataType = Array{String}(undef, nDt) 303 | nd = 0 304 | while nd < nDt 305 | cline = strip(readline(fid)) 306 | nd = nd + 1 307 | dataType[nd] = cline 308 | end 309 | 310 | # data block 311 | elseif occursin("Data Block", cline) 312 | tmp = split(cline) 313 | nData = parse(Int, tmp[end]) 314 | nd = 0 315 | txID = zeros(Int, nData) 316 | rxID = zeros(Int, nData) 317 | dtID = zeros(Int, nData) 318 | freqID = zeros(Int, nData) 319 | obsData = zeros(Float64, nData) 320 | dataErr = zeros(nData) 321 | 322 | while nd < nData 323 | cline = strip(readline(fid)) 324 | while cline[1] == '#' || isempty(cline) 325 | cline = strip(readline(fid)) 326 | end 327 | cline = split(cline) 328 | nd = nd + 1 329 | freqID[nd] = parse(Int, cline[1]) 330 | txID[nd] = parse(Int, cline[2]) 331 | rxID[nd] = parse(Int, cline[3]) 332 | dtID[nd] = parse(Int, cline[4]) 333 | obsData[nd] = parse(Float64, cline[5]) 334 | dataErr[nd] = parse(Float64, cline[6]) 335 | end 336 | 337 | end 338 | 339 | end 340 | close(fid) 341 | 342 | # define dataID 343 | dataID = zeros(Bool,nDt,nr,ns,nf) 344 | for k = 1:length(obsData) 345 | tidx = txID[k] 346 | ridx = rxID[k] 347 | fidx = freqID[k] 348 | didx = dtID[k] 349 | dataID[didx,ridx,tidx,fidx] = true 350 | end 351 | dataID = vec(dataID) 352 | csemData = CSEMData(seawater, txLoc, rxLoc, dpLen, freqArray, dataType, 353 | dataID, obsData, dataErr) 354 | 355 | return csemData 356 | 357 | end 358 | 359 | 360 | #------------------------------------------------------------------------------- 361 | """ 362 | `readMTEDIFile(edifile, dataType)` 363 | 364 | read MT impedance from edi file 365 | 366 | """ 367 | function readMTEDIFile(edifile::String, dataType::Int=1, errLvl=0.05) 368 | 369 | # 370 | if isfile(edifile) 371 | fid = open(edifile, "r") 372 | else 373 | error("$(edifile) does not exist, please try again.") 374 | end 375 | 376 | # 377 | freqs = readEDISection(fid, ">FREQ") 378 | if dataType == 1 379 | zxyr = readEDISection(fid, ">ZXYR") 380 | zxyi = readEDISection(fid, ">ZXYI") 381 | zxyE = readEDISection(fid, ">ZXY.VAR") 382 | obsData = hcat(zxyr, zxyi) 383 | obsData = vec(copy(transpose(obsData))) 384 | 385 | elseif dataType == 2 386 | zyxr = readEDISection(fid, ">ZYXR") 387 | zyxi = readEDISection(fid, ">ZYXI") 388 | zyxE = readEDISection(fid, ">ZYX.VAR") 389 | obsData = hcat(zyxr, zyxi) 390 | obsData = vec(copy(transpose(obsData))) 391 | 392 | elseif dataType == 3 393 | zxxr = readEDISection(fid, ">ZXXR") 394 | zxxi = readEDISection(fid, ">ZXXI") 395 | zxxE = readEDISection(fid, ">ZXX.VAR") 396 | zxx = zxxr + 1im * zxxi 397 | 398 | zxyr = readEDISection(fid, ">ZXYR") 399 | zxyi = readEDISection(fid, ">ZXYI") 400 | zxyE = readEDISection(fid, ">ZXY.VAR") 401 | zxy = zxyr + 1im * zxyi 402 | 403 | zyxr = readEDISection(fid, ">ZYXR") 404 | zyxi = readEDISection(fid, ">ZYXI") 405 | zyxE = readEDISection(fid, ">ZYX.VAR") 406 | zyx = zyxr + 1im * zyxi 407 | 408 | zyyr = readEDISection(fid, ">ZYYR") 409 | zyyi = readEDISection(fid, ">ZYYI") 410 | zyyE = readEDISection(fid, ">ZYY.VAR") 411 | zyy = zyyr + 1im * zyyi 412 | zdet = sqrt.(zxx .* zyy - zxy .* zyx) 413 | zdetr = real.(zdet) 414 | zdeti = imag.(zdet) 415 | obsData = hcat(zdetr, zdeti) 416 | obsData = vec(copy(transpose(obsData))) 417 | 418 | end 419 | 420 | # convert impedance data from mv/km/nT to Ω 421 | factor = 796 422 | obsData /= factor 423 | dataErr = abs.(obsData) * errLvl 424 | datastr = ["realZ", "imagZ"] 425 | nf = length(freqs) 426 | nDt = length(datastr) 427 | dataID = ones(Bool,nDt*nf) 428 | mtData = MTData(freqs,datastr,dataID,obsData,dataErr) 429 | 430 | end 431 | 432 | 433 | #------------------------------------------------------------------------------- 434 | function readEDISection(fid::IOStream, token::String) 435 | 436 | data = [] 437 | while !eof(fid) 438 | cline = strip(readline(fid)) 439 | if occursin(token, cline) 440 | tmp = split(cline, "//") 441 | nd = parse(Int, tmp[end]) 442 | data = zeros(nd) 443 | k = 0 444 | while k < nd 445 | cline = strip(readline(fid)) 446 | cline = split(cline) 447 | num = length(cline) 448 | for i = 1:num 449 | k = k + 1 450 | data[k] = parse(Float64, cline[i]) 451 | end 452 | end 453 | break 454 | end 455 | end 456 | 457 | return data 458 | 459 | end 460 | -------------------------------------------------------------------------------- /src/TBFileIO/readstartupFile.jl: -------------------------------------------------------------------------------- 1 | export readstartupFile 2 | 3 | #------------------------------------------------------------------------------- 4 | function readstartupFile(startupfile::String) 5 | 6 | # 7 | if isfile(startupfile) 8 | fid = open(startupfile, "r") 9 | else 10 | error("$(startupfile) does not exist, please try again.") 11 | end 12 | 13 | # 14 | surveyType = [] 15 | datafile = [] 16 | mclimits = initMCPrior() 17 | while !eof(fid) 18 | cline = strip(readline(fid)) 19 | 20 | # ignore all comments: empty line, or line preceded with # 21 | while cline[1] == '#' || isempty(cline) 22 | cline = strip(readline(fid)) 23 | end 24 | # cline = lowercase(cline) 25 | 26 | # data filename 27 | if occursin("datafile:", cline) 28 | tmp = split(cline) 29 | datafile = string(tmp[end]) 30 | 31 | elseif occursin("surveytype:", cline) 32 | tmp = split(cline) 33 | surveyType = string(tmp[end]) 34 | 35 | # markov chain parameter 36 | elseif occursin("burninsamples:", cline) 37 | tmp = split(cline) 38 | mclimits.burninsamples = parse(Int, tmp[end]) 39 | 40 | elseif occursin("totalsamples:", cline) 41 | tmp = split(cline) 42 | mclimits.totalsamples = parse(Int, tmp[end]) 43 | 44 | # prior parameter 45 | elseif occursin("numberoflayer:", cline) 46 | tmp = split(cline) 47 | mclimits.nlayermin = parse(Int, tmp[end-1]) 48 | mclimits.nlayermax = parse(Int, tmp[end]) 49 | 50 | elseif occursin("zcoordinate(m):", cline) 51 | tmp = split(cline) 52 | zmin = parse(Float64, tmp[end-2]) 53 | zmax = parse(Float64, tmp[end-1]) 54 | zstd = parse(Float64, tmp[end]) 55 | # 56 | mclimits.zmin = log10(zmin) 57 | mclimits.zmax = log10(zmax) 58 | mclimits.zstd = zstd * (mclimits.zmax - mclimits.zmin) 59 | 60 | elseif occursin("minthickness(m):",cline) 61 | tmp = split(cline) 62 | mclimits.hmin = parse(Float64, tmp[end]) 63 | 64 | # proposal parameter 65 | elseif occursin("resistivity:", cline) 66 | tmp = split(cline) 67 | rhomin = parse(Float64, tmp[end-2]) 68 | rhomax = parse(Float64, tmp[end-1]) 69 | rhostd = parse(Float64, tmp[end]) 70 | # 71 | mclimits.rhomin = log10(rhomin) 72 | mclimits.rhomax = log10(rhomax) 73 | mclimits.rhostd = rhostd * (mclimits.rhomax - mclimits.rhomin) 74 | mclimits.mrhostd = rhostd * (mclimits.rhomax - mclimits.rhomin) 75 | 76 | # parameters for post analysis 77 | elseif occursin("numberofbins:", cline) 78 | tmp = split(cline) 79 | mclimits.nzBins = parse(Float64, tmp[end-1]) 80 | mclimits.npBins = parse(Float64, tmp[end]) 81 | 82 | elseif occursin("credinterval:", cline) 83 | tmp = split(cline) 84 | mclimits.credInterval = parse(Float64, tmp[end]) 85 | 86 | else 87 | @warn("$(cline) is not supported!") 88 | 89 | end 90 | 91 | end # while 92 | close(fid) 93 | 94 | # read observed data 95 | if surveyType == "tem" 96 | println("TEM data is loading ...") 97 | emData = readTEMData(datafile) 98 | elseif surveyType == "mt" 99 | println("MT data is loading ...") 100 | if occursin(".edi", datafile) 101 | emData = readMTEDIFile(datafile, 3, 0.05) 102 | else 103 | emData = readMTData(datafile) 104 | end 105 | elseif surveyType == "csem" 106 | println("CSEM data is loading ...") 107 | emData = readCSEMData(datafile) 108 | end 109 | 110 | return emData, mclimits 111 | 112 | end 113 | -------------------------------------------------------------------------------- /src/TBFileIO/writeEMData.jl: -------------------------------------------------------------------------------- 1 | export writeTEMData, writeMTData, writeCSEMData 2 | 3 | #------------------------------------------------------------------------------- 4 | """ 5 | writeTEMData(datafile, temData, predData, dataErr) 6 | 7 | output predicated EM data. 8 | 9 | """ 10 | function writeTEMData(datafile::String, temData::TEMData, 11 | predData::Array, dataErr=zeros(0)) 12 | 13 | datID = open(datafile, "w") 14 | @printf(datID, "# %s\n", "file generated in $(Libc.strftime(time()))") 15 | 16 | xLen = temData.loopxLen 17 | yLen = temData.loopyLen 18 | @printf(datID, "%-16s %4g %4g\n","LoopWidth(m):", xLen, yLen) 19 | timePoint = temData.timePoint 20 | 21 | ntemData = length(timePoint) 22 | # data block 23 | if isempty(dataErr) 24 | dataErr = abs.(predData[1:ntemData]) * 0.05 25 | elseif length(dataErr) .== 1 26 | dataErr = abs.(predData[1:ntemData]) * dataErr[1] 27 | end 28 | 29 | @printf(datID,"%-15s %d\n","Data Block:", ntemData) 30 | for i = 1:ntemData 31 | @printf(datID,"%13.8e %15.6e %15.6e\n", timePoint[i], predData[i], dataErr[i]) 32 | end 33 | 34 | close(datID) 35 | 36 | end 37 | 38 | 39 | #------------------------------------------------------------------------------- 40 | """ 41 | writeMTData(datafile, mtData, predData, dataErr) 42 | 43 | output predicated MT data. 44 | 45 | """ 46 | function writeMTData(datafile::String, mtData::MTData, 47 | predData::Array, dataErr=zeros(0)) 48 | 49 | datID = open(datafile, "w") 50 | 51 | # data format 52 | @printf(datID, "%-20s%s\n", "Format:", "MTData_1.0") 53 | @printf(datID, "# %s\n", "file generated in $(Libc.strftime(time()))") 54 | 55 | # frequencies 56 | freqs = mtData.freqArray 57 | nF = length(freqs) 58 | @printf(datID,"%-20s %3d\n","Frequencies (Hz):",nF) 59 | for i = 1:nF 60 | @printf(datID, "%8.4e\n",freqs[i]) 61 | end 62 | 63 | # data type 64 | dataType = mtData.dataType 65 | nDT = length(dataType) 66 | @printf(datID, "%-12s %d\n", "DataType:", nDT) 67 | for i = 1:nDT 68 | @printf(datID, "%4s\n", dataType[i]) 69 | end 70 | 71 | # data block 72 | if isempty(dataErr) 73 | dataErr = abs.(predData) * 0.05 74 | elseif length(dataErr) .== 1 75 | dataErr = abs.(predData) * dataErr[1] 76 | end 77 | 78 | dataID = reshape(mtData.dataID,nDT,nF) 79 | nData = size(predData, 1) 80 | @printf(datID,"%-15s %d\n","Data Block:", nData) 81 | 82 | # freqID recID DataTpye Data DataError 83 | @printf(datID,"# %6s %10s %10s %12s\n","FreqNo.","DataType","Value","Error") 84 | i = 1 85 | for k = 1:nF 86 | for j = 1:nDT 87 | if dataID[j,k] 88 | @printf(datID,"%5d %8d %15.6e %15.6e\n",k,j,predData[i],dataErr[i]) 89 | i += 1 90 | end 91 | end 92 | end 93 | close(datID) 94 | 95 | end 96 | 97 | 98 | #------------------------------------------------------------------------------- 99 | """ 100 | writeCSEMData(datafile, emData, predData, dataErr) 101 | 102 | output predicated CSEM data. 103 | 104 | """ 105 | function writeCSEMData(datafile::String, emData::CSEMData, 106 | predData::Array, dataErr=zeros(0)) 107 | 108 | # 109 | datID = open(datafile, "w") 110 | 111 | # data format 112 | @printf(datID, "%-15s%s\n", "Format:", "CSEMData_1.0") 113 | @printf(datID, "# %s\n", "file generated in $(Libc.strftime(time()))") 114 | 115 | # seawater layer 116 | if !isempty(emData.seawater) 117 | seawater = emData.seawater 118 | @printf(datID,"%-15s %8.1f %8g\n", "SeaLayer:", seawater[1], seawater[2]) 119 | end 120 | 121 | # dipole length 122 | if emData.dpLen > 1e+2 123 | @printf(datID,"%-20s %6.1f\n", "Dipole Length:", emData.dpLen) 124 | end 125 | 126 | # source location 127 | txLoc = emData.txLoc 128 | ns = size(txLoc, 1) 129 | @printf(datID,"%-25s %4d\n", "Source Location (m):", ns) 130 | @printf(datID,"# %5s %5s %5s %5s %5s\n", "X", "Y", "Z", "Azimuth", "Dip") 131 | for i = 1:ns 132 | for j = 1:5 133 | @printf(datID,"%10.1f ",txLoc[i, j]) 134 | end 135 | @printf(datID,"\n") 136 | end 137 | 138 | # receiver location 139 | rxLoc = emData.rxLoc 140 | nr = size(rxLoc, 1) 141 | @printf(datID,"%-25s %4d\n", "Receiver Location (m):", nr) 142 | @printf(datID,"# %5s %5s %5s\n", "X", "Y", "Z") 143 | for i = 1:nr 144 | @printf(datID,"%10.1f %10.1f %10.1f\n", rxLoc[i,1], rxLoc[i,2], rxLoc[i,3]) 145 | end 146 | 147 | # frequencies 148 | freqArray = emData.freqArray 149 | nF = length(freqArray) 150 | @printf(datID,"%-20s %3d\n", "Frequencies (Hz):", nF) 151 | for i = 1:nF 152 | @printf(datID,"%8.4e\n", freqArray[i]) 153 | end 154 | 155 | # data type 156 | dataType = emData.dataType 157 | nDT = length(dataType) 158 | @printf(datID,"%-15s %d\n", "DataType:", nDT) 159 | for i = 1:nDT 160 | @printf(datID,"%4s\n",dataType[i]) 161 | end 162 | 163 | if isempty(dataErr) 164 | dataErr = abs.(predData) * 0.05 165 | elseif length(dataErr) .== 1 166 | dataErr = abs.(predData) * dataErr[1] 167 | end 168 | 169 | dataID = reshape(emData.dataID,nDT,nr,ns,nF) 170 | nData = size(predData, 1) 171 | @printf(datID,"%-15s %d\n","Data Block:", nData) 172 | 173 | # freqID scrID recID DataTpye RealPart ImagPart DataError 174 | @printf(datID,"#%6s %6s %6s %10s %8s %10s\n","FreqNo", "TxNo", "RxNo", 175 | "DataType", "Value", "Error") 176 | i = 1 177 | for k = 1:nF 178 | for itx = 1:ns 179 | for irx = 1:nr 180 | for idt = 1:nDT 181 | if dataID[idt,irx,itx,k] 182 | @printf(datID,"%5d %5d %6d %6d %15.6e %15.6e\n", 183 | k, itx, irx, idt, predData[i], dataErr[i]) 184 | i += 1 185 | end 186 | end 187 | end 188 | end 189 | end 190 | close(datID) 191 | 192 | end 193 | -------------------------------------------------------------------------------- /src/TBFwdSolver/TBFwdSolver.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBFwdSolver` defines routines to perform forward modeling. 4 | # 5 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 6 | # 7 | #------------------------------------------------------------------------------- 8 | module TBFwdSolver 9 | 10 | using TransdEM.TBStruct 11 | using TransdEM.TBFileIO 12 | 13 | export getModelParameter 14 | export getPredData 15 | 16 | #------------------------------------------------------------------------------ 17 | if Sys.isunix() 18 | libName = "em1dmod.so" 19 | elseif Sys.iswindows() 20 | libName = "em1dmod.dll" 21 | end 22 | 23 | const em1dLibpath = abspath(joinpath(splitdir(Base.source_path())[1], ".", 24 | "deps", "lib", libName)) 25 | 26 | #------------------------------------------------------------------------------- 27 | function getPredData(emData::MTData, mcParam::MCParameter) 28 | 29 | # get model parameter 30 | dep1D, rho1D = getModelParameter(mcParam) 31 | sig1D = 1 ./ rho1D 32 | 33 | predData = compMT1dResp(emData, sig1D, dep1D) 34 | 35 | return predData 36 | 37 | end 38 | 39 | #------------------------------------------------------------------------------- 40 | function getPredData(emData::CSEMData, mcParam::MCParameter) 41 | 42 | # get model parameter 43 | zNode, rho1D = getModelParameter(mcParam) 44 | sig1D = 1 ./ rho1D 45 | 46 | sigAir = 1e-12 47 | airLayer = -1000000. 48 | if isempty(emData.seawater) 49 | sig1D = vcat(sigAir, sig1D) 50 | dep1D = vcat(airLayer, zNode[1:end-1]) 51 | else 52 | seaDep = emData.seawater[1] 53 | sigSea = emData.seawater[2] 54 | sig1D = vcat(sigAir, sigSea, sig1D) 55 | dep1D = vcat(airLayer, 0.0, zNode[1:end-1] .+ seaDep) 56 | end 57 | predData = compCSEM1dResp(emData, sig1D, dep1D) 58 | 59 | return predData 60 | 61 | end 62 | 63 | #------------------------------------------------------------------------------- 64 | function getPredData(emData::TEMData, mcParam::MCParameter) 65 | 66 | # get model parameter 67 | dep1D, rho1D = getModelParameter(mcParam) 68 | 69 | xLen = emData.loopxLen 70 | yLen = emData.loopyLen 71 | timePoint = emData.timePoint 72 | predData = compTEM1dResponse(xLen, yLen, timePoint, rho1D, dep1D) 73 | 74 | return predData 75 | 76 | end 77 | 78 | #------------------------------------------------------------------------------- 79 | function getModelParameter(mcParam::MCParameter) 80 | 81 | # convert location from logarithmic scale to linear scale 82 | nLayer = mcParam.nLayer 83 | zNode = mcParam.zNode[1:nLayer] 84 | 85 | # location 86 | idx = sortperm(zNode) 87 | zNode = 10 .^ zNode[idx] 88 | dep1D = vcat(0., zNode) 89 | 90 | # convert resistivity from logarithmic scale to linear scale 91 | rho = mcParam.rho[1:nLayer] 92 | rho1D = 10 .^ rho[idx] 93 | 94 | return dep1D, rho1D 95 | 96 | end 97 | 98 | #------------------------------------------------------------------------------- 99 | include("compTEMRespData.jl") 100 | include("compCSEMRespData.jl") 101 | include("compMTRespData.jl") 102 | 103 | end # TBFwdSolver 104 | -------------------------------------------------------------------------------- /src/TBFwdSolver/compCSEMRespData.jl: -------------------------------------------------------------------------------- 1 | export comp1DCSEM, compWEM1dResp, compCSEM1dResp 2 | export unwrap, unwrap! 3 | #------------------------------------------------------------------------------- 4 | """ 5 | `comp1DCSEM(txLoc, rxLoc, freqArray, sigma, depth1D, compFlag)` 6 | 7 | Calculate specfic component of EM field due to electric dipole. 8 | 9 | # Inputs: 10 | - `txLoc` =::Array[nsx5], transmitter location 11 | - `rxLoc` =::Array[nrx3], receiver location 12 | - `freqArray` =::Array, transmission frequencies 13 | - `sigma` =::Vector, conductivities of layered model 14 | - `depth1D` =::Vector, depths of top of layered model 15 | - `compFlag` =::Integer, component identifier 16 | 17 | # Outputs: 18 | - `e1d` =::Array, computed field 19 | 20 | """ 21 | function comp1DCSEM(txLoc::Array{T,2}, rxLoc::Array{T,2}, 22 | freqArray::Array{T}, sigma::Vector{T}, 23 | depth1D::Vector{T}, compFlag::Integer) where{T<:Real} 24 | 25 | # transmitter and receivers 26 | nTx = size(txLoc, 1) 27 | nRx = size(rxLoc, 1) 28 | nFreq = length(freqArray) 29 | nLayer = length(sigma) 30 | 31 | if length(sigma) != length(depth1D) 32 | error("comp1DCSEM: Conductivity and depth must be the same length!") 33 | end 34 | 35 | # predicted field component 36 | e1d = zeros(ComplexF64, nRx, nTx, nFreq) 37 | 38 | # 39 | ccall((:calldipole1deb_, em1dLibpath), Nothing, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, 40 | Ptr{Float64}, Ptr{Float64}, Ptr{Int64}, Ptr{Int64}, Ptr{Int64}, 41 | Ptr{Int64}, Ptr{Int64}, Ptr{ComplexF64}), txLoc, rxLoc, freqArray, 42 | sigma, depth1D, Ref(nTx), Ref(nRx), Ref(nFreq), Ref(nLayer), Ref(compFlag), e1d) 43 | 44 | return e1d 45 | 46 | end # comp1DCSEM 47 | 48 | 49 | #------------------------------------------------------------------------------- 50 | """ 51 | `comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freqArray, sigma, depth1D, compFlag)` 52 | 53 | Calculate specific component of EM field due to finite wire source. 54 | 55 | # Inputs: 56 | - `txLoc` =::Array[nsx5], transmitter location 57 | - `rxLoc` =::Array[nrx3], receiver location 58 | - `dipLen` =::Float64, length of finite dipole source 59 | - `nIntPts` =::Integer, Number of points to use for Gauss quadrature 60 | integration for finite dipole 61 | - `freqArray` =::Array, transmission frequencies 62 | - `sigma` =::Array, conductivities of layered model 63 | - `depth1D` =::Array, depth of top of layered model 64 | - `compFlag` =::Integer, component identifier 65 | 66 | # Outputs: 67 | - `e1d` =::Array, computed EM field 68 | 69 | """ 70 | function comp1DCSEM(txLoc::Array{T}, rxLoc::Array{T}, dipLen::T, 71 | nIntPts::Integer, freqArray::Array{T}, 72 | sigma::Vector{T}, depth1D::Vector{T}, 73 | compFlag::Integer) where{T<:Real} 74 | 75 | # transmitter and receivers 76 | nTx = size(txLoc, 1) 77 | nRx = size(rxLoc, 1) 78 | nFreq = length(freqArray) 79 | nLayer = length(sigma) 80 | 81 | if length(sigma) != length(depth1D) 82 | error("comp1DCSEM: Conductivity and depth must be the same length!") 83 | end 84 | 85 | # predicted field component 86 | e1d = zeros(ComplexF64, nRx, nTx, nFreq) 87 | 88 | # 89 | ccall((:calldipole1dfinite_, em1dLibpath), Nothing, (Ptr{Float64}, Ptr{Float64}, 90 | Ptr{Float64}, Ptr{Int64}, Ptr{Float64}, Ptr{Float64}, 91 | Ptr{Float64}, Ptr{Int64}, Ptr{Int64}, Ptr{Int64}, 92 | Ptr{Int64}, Ptr{Int64}, Ptr{ComplexF64}), txLoc, rxLoc, 93 | Ref(dipLen), Ref(nIntPts), freqArray, sigma, depth1D, Ref(nTx), Ref(nRx), 94 | Ref(nFreq), Ref(nLayer), Ref(compFlag), e1d) 95 | 96 | return e1d 97 | 98 | end # comp1DCSEM 99 | 100 | 101 | #------------------------------------------------------------------------------- 102 | """ 103 | compCSEM1dResp(emData, sigma1D, depth1D) 104 | 105 | calculates primary electromagnetic fields at receivers. 106 | 107 | """ 108 | function compCSEM1dResp(emData::CSEMData, sigma1D::Array, depth1D::Array) 109 | 110 | # 111 | txLoc = copy(emData.txLoc) 112 | rxLoc = emData.rxLoc 113 | freq = emData.freqArray 114 | 115 | # data index 116 | dataType = emData.dataType 117 | dataID = emData.dataID 118 | 119 | ns = size(emData.txLoc, 1) 120 | nr = size(emData.rxLoc, 1) 121 | nDT = length(dataType) 122 | nFreq = length(freq) 123 | if nFreq == 1; freq = collect(freq); end 124 | 125 | # 126 | exp = zeros(ComplexF64, 0) 127 | eyp = zeros(ComplexF64, 0) 128 | ezp = zeros(ComplexF64, 0) 129 | bxp = zeros(ComplexF64, 0) 130 | byp = zeros(ComplexF64, 0) 131 | bzp = zeros(ComplexF64, 0) 132 | dipLen = emData.dpLen 133 | predData = zeros(nDT, nr, ns, nFreq) 134 | if dipLen < 100.0 135 | for i = 1:nDT 136 | dt = dataType[i] 137 | if occursin("ampEx", dt) 138 | if isempty(exp) 139 | exp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 1) 140 | end 141 | predData[i,:,:,:] = abs.(exp) 142 | 143 | elseif occursin("phsEx", dt) 144 | if isempty(exp) 145 | exp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 1) 146 | end 147 | predData[i,:,:,:] = atand.(imag(exp), real(exp)) 148 | 149 | elseif occursin("ampEy", dt) 150 | if isempty(eyp) 151 | eyp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 2) 152 | end 153 | predData[i,:,:,:] = abs.(eyp) 154 | 155 | elseif occursin("phsEy", dt) 156 | if isempty(eyp) 157 | eyp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 2) 158 | end 159 | predData[i,:,:,:] = atand.(imag(eyp), real(eyp)) 160 | 161 | elseif occursin("ampBx", dt) 162 | if isempty(bxp) 163 | bxp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 4) 164 | end 165 | predData[i,:,:,:] = abs.(bxp) 166 | 167 | elseif occursin("phsBx", dt) 168 | if isempty(bxp) 169 | bxp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 4) 170 | end 171 | predData[i,:,:,:] = atand.(imag(bxp), real(bxp)) 172 | 173 | elseif occursin("ampBy", dt) 174 | if isempty(byp) 175 | byp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 5) 176 | end 177 | predData[i,:,:,:] = abs.(byp) 178 | 179 | elseif occursin("phsBy", dt) 180 | if isempty(byp) 181 | byp = comp1DCSEM(txLoc, rxLoc, freq, sigma1D, depth1D, 5) 182 | end 183 | predData[i,:,:,:] = atand.(imag(byp), real(byp)) 184 | 185 | end 186 | end 187 | else 188 | printstyled("Finite length source is used!\n", color=:cyan) 189 | nIntPts = 15 190 | for i = 1:nDT 191 | dt = dataType[i] 192 | if occursin("ampEx", dt) 193 | if isempty(exp) 194 | exp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 1) 195 | end 196 | predData[i,:,:,:] = abs.(exp) 197 | 198 | elseif occursin("phsEx", dt) 199 | if isempty(exp) 200 | exp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 1) 201 | end 202 | predData[i,:,:,:] = atand.(imag(exp), real(exp)) 203 | 204 | elseif occursin("ampEy", dt) 205 | if isempty(eyp) 206 | eyp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 2) 207 | end 208 | predData[i,:,:,:] = abs.(eyp) 209 | 210 | elseif occursin("phsEy", dt) 211 | if isempty(eyp) 212 | eyp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 2) 213 | end 214 | predData[i,:,:,:] = atand.(imag(eyp), real(eyp)) 215 | 216 | elseif occursin("ampBx", dt) 217 | if isempty(bxp) 218 | bxp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 4) 219 | end 220 | predData[i,:,:,:] = abs.(bxp) 221 | 222 | elseif occursin("phsBx", dt) 223 | if isempty(bxp) 224 | bxp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 4) 225 | end 226 | predData[i,:,:,:] = atand.(imag(bxp), real(bxp)) 227 | 228 | elseif occursin("ampBy", dt) 229 | if isempty(byp) 230 | byp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 5) 231 | end 232 | predData[i,:,:,:] = abs.(byp) 233 | 234 | elseif occursin("phsBy", dt) 235 | if isempty(byp) 236 | byp = comp1DCSEM(txLoc, rxLoc, dipLen, nIntPts, freq, sigma1D, depth1D, 5) 237 | end 238 | predData[i,:,:,:] = atand.(imag(byp), real(byp)) 239 | 240 | end 241 | end 242 | end 243 | 244 | predData = vec(predData) 245 | predData = predData[dataID] 246 | 247 | return predData 248 | 249 | end # getPrimaryRespField 250 | 251 | 252 | #------------------------------------------------------------------------------- 253 | # Julia implementation of a phase unwrap function 254 | # by Spencer Russell (https://gist.github.com/ssfrr/7995008) 255 | # 256 | #------------------------------------------------------------------------------- 257 | function unwrap(v, inplace=false) 258 | # currently assuming an array 259 | unwrapped = inplace ? v : copy(v) 260 | for i in 2:length(v) 261 | while unwrapped[i] - unwrapped[i-1] >= pi 262 | unwrapped[i] -= 2pi 263 | end 264 | while unwrapped[i] - unwrapped[i-1] <= -pi 265 | unwrapped[i] += 2pi 266 | end 267 | end 268 | return unwrapped 269 | end 270 | 271 | unwrap!(v) = unwrap(v, true) 272 | -------------------------------------------------------------------------------- /src/TBFwdSolver/compMTRespData.jl: -------------------------------------------------------------------------------- 1 | export compMT1dResp, compMT1DImpedance 2 | #------------------------------------------------------------------------------- 3 | function compMT1dResp(emData::MTData, sig1D::Array, depth1D::Array) 4 | 5 | # frequencies 6 | freqs = emData.freqArray 7 | zimp = compMT1DImpedance(freqs, sig1D, depth1D) 8 | 9 | nfreq = length(freqs) 10 | predData = zeros(2*nfreq) 11 | 12 | predData[1:2:end] = real.(zimp) 13 | predData[2:2:end] = imag.(zimp) 14 | 15 | dataID = emData.dataID 16 | predData = predData[dataID] 17 | 18 | return predData 19 | 20 | end 21 | 22 | 23 | #------------------------------------------------------------------------------- 24 | """ 25 | compMT1DImpedance(freqArray, sigma, depth1D) 26 | 27 | calculates the impedance at the earth's surface for MT 1D layered model. 28 | Note: length(sigma) = length(depth1D)-1 29 | 30 | #Arguments 31 | - Input: 32 | * `freqArray` =::Array, frequencies 33 | * `sigma` =::Array, conductivity model 34 | * `depth1D` =::Array, location of top of each layer 35 | 36 | - Output: 37 | * `z1d` =::Array, impedance at surface 38 | 39 | """ 40 | function compMT1DImpedance(freqArray::Array{T}, sigma::Array{T}, 41 | depth1D::Array{T}) where {T<:Float64} 42 | 43 | if length(sigma) != length(depth1D)-1 44 | error("layer's conductivity is not the same size with its depth.") 45 | end 46 | 47 | # physical constant 48 | mu0 = 4 * pi * 1e-7 49 | eps0 = 8.85 * 1e-12 50 | nFreq = length(freqArray) 51 | zLen = diff(depth1D) 52 | nLayer = length(depth1D) 53 | 54 | # impedances at surface 55 | z1d = zeros(ComplexF64, nFreq) 56 | for i = 1:nFreq 57 | 58 | omega = 2 * pi * freqArray[i] 59 | k = sqrt(mu0 * eps0 * omega^2 - mu0 * sigma[end] * omega *1im) 60 | 61 | # calculate the impedance at the bottom layer 62 | ztmp = omega * mu0 / k 63 | 64 | for j = 2:nLayer 65 | ind = nLayer - j + 1 66 | k = sqrt(mu0 * eps0 * omega^2 - mu0 * sigma[ind] * omega * 1im) 67 | zp = omega * mu0 / k 68 | ztmp = zp * (ztmp + zp * tanh(k*zLen[ind]*1im)) / (zp + ztmp * tanh(k*zLen[ind]*1im)) 69 | end 70 | z1d[i] = ztmp 71 | 72 | end 73 | 74 | return z1d 75 | 76 | end 77 | -------------------------------------------------------------------------------- /src/TBFwdSolver/compTEMRespData.jl: -------------------------------------------------------------------------------- 1 | export compTEM1dResponse 2 | #------------------------------------------------------------------------------- 3 | """ 4 | `compTEM1dResponse(xLen, yLen, timePoint, rho, depth1D)` 5 | 6 | """ 7 | function compTEM1dResponse(loopxLen::T, loopyLen::T, timePoint::Vector{T}, 8 | rho::Vector{T}, depth1D::Vector{T}) where{T<:Float64} 9 | 10 | # 11 | if length(rho) != length(depth1D) - 1 12 | error("compTEM1d: resistivity and thickness must be the same length!") 13 | end 14 | 15 | nt = length(timePoint) 16 | nlayer = length(rho) 17 | thickness = diff(depth1D) 18 | thickness[nlayer] = 10000.0 19 | 20 | # predicted tem response 21 | timeResp = zeros(nt) 22 | 23 | # 24 | ccall((:comptem1dresponsenew_, em1dLibpath), Nothing, (Ptr{Float64},Ptr{Float64}, 25 | Ptr{Int64},Ptr{Float64},Ptr{Float64},Ptr{Int64},Ptr{Float64},Ptr{Float64}), 26 | Ref(loopxLen), Ref(loopyLen), Ref(nlayer), rho, thickness, Ref(nt), timePoint, timeResp) 27 | 28 | return timeResp 29 | 30 | end 31 | -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/build.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # 3 | # script to build em1dmod shared library 4 | # 5 | # 6 | #------------------------------------------------------------------------------ 7 | using BinDeps 8 | 9 | @BinDeps.setup 10 | 11 | # setup filepath 12 | depsDir = splitdir(Base.source_path())[1] 13 | srcDir = joinpath(depsDir, "src") 14 | libDir = joinpath(depsDir, "lib") 15 | 16 | printstyled("=== Building EM1DMOD shared library === \n", color=:blue) 17 | println("depsDir = $(depsDir)") 18 | println("srcDir = $(srcDir)") 19 | println("libDir = $(libDir)") 20 | 21 | if !isdir(libDir) 22 | printstyled("=== Creating library directory === \n", color=:blue) 23 | mkdir(libDir) 24 | if !isdir(libDir) 25 | error("Creating library directory failed.") 26 | end 27 | end 28 | 29 | 30 | if Sys.isunix() 31 | libName = "em1dmod.so" 32 | elseif Sys.iswindows() 33 | libName = "em1dmod.dll" 34 | end 35 | 36 | src01 = joinpath(srcDir, "tem1dmod.f90") 37 | src02 = joinpath(srcDir, "comptem1d.f90") 38 | # 39 | src03 = joinpath(srcDir, "FilterModules.f90") 40 | src04 = joinpath(srcDir, "Dipole1D.f90") 41 | src05 = joinpath(srcDir, "callDipole1D.f90") 42 | # 43 | src06 = joinpath(srcDir, "mt1dmod.f90") 44 | dirfile = joinpath(libDir, libName) 45 | 46 | #@build_steps begin 47 | printstyled("=== Fortran compilier Info ===\n", color=:blue) 48 | run(`gfortran --version`) 49 | run(`gfortran -O3 -march=native -fPIC -shared $(src01) $(src02) $(src03) $(src04) $(src05) $(src06) -o $(dirfile)`) 50 | 51 | #end 52 | -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/lib/em1dmod.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CUG-EMI/TransdEM/4932e7e178b48f4a46f27e75fb2cd904e8a7b5ed/src/TBFwdSolver/deps/lib/em1dmod.dll -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/lib/em1dmod.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CUG-EMI/TransdEM/4932e7e178b48f4a46f27e75fb2cd904e8a7b5ed/src/TBFwdSolver/deps/lib/em1dmod.so -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/src/callDipole1D.f90: -------------------------------------------------------------------------------- 1 | !====================================================================== 2 | ! `callDipole1D` calculates all electrical components excited by dipole 3 | ! source. 4 | ! 5 | ! 6 | !====================================================================== 7 | subroutine callDipole1D(txLoc, rxLoc, freqArray, sigma1D, depth1D, & 8 | nTx, nRx, nFreq, nZ, predEx1D, predEy1D, predEz1D) 9 | 10 | use dipole1d 11 | implicit none 12 | 13 | integer, intent(in) :: nTx, nRx, nFreq, nZ 14 | real(kind = 8), dimension(nTx, 5), intent(in) :: txLoc 15 | real(kind = 8), dimension(nRx, 3), intent(in) :: rxLoc 16 | real(kind = 8), dimension(nFreq), intent(in) :: freqArray 17 | ! conductivity structure, depth of top of layer 18 | real(kind = 8), dimension(nZ), intent(in) :: sigma1D, depth1D 19 | complex(kind = 8), dimension(nRx,nTx,nFreq), intent(inout) :: predEx1D, predEy1D, predEz1D 20 | 21 | ! local variables 22 | integer :: i, j, iTx, iFreq 23 | real(8), dimension(:), allocatable :: sigsite 24 | 25 | ! number of transmitters, receivers, frequencies 26 | !nTx = size(txLoc, 1) 27 | !nFreq = size(freqArray, 1) 28 | 29 | ! 30 | ! Initialize the default values 31 | call init_defaults_Dipole1D 32 | 33 | ! Specify some parameters required by Dipole1D: 34 | HTmethod1D = 'kk_ht_201' 35 | outputdomain1D = 'spatial' 36 | phaseConvention = 'lead' 37 | lbcomp = .false. 38 | sdm1D = 1.0 ! (Am), dipole moment 39 | lUseSpline1D = .false. 40 | linversion = .false. ! Compute Derivatives with respect to sigma(layers) 41 | 42 | ! compute all electric field 43 | emFlag = 111 44 | 45 | !! allocate variables 46 | ! 1D model parameter 47 | nlay1D = size(depth1D, 1) 48 | allocate(sig1D(nlay1D), zlay1D(nlay1D)) 49 | do i = 1, nlay1D 50 | sig1D(i) = sigma1D(i) 51 | zlay1D(i) = depth1D(i) 52 | enddo 53 | 54 | ! receiver 55 | n1D = size(rxLoc, 1) 56 | allocate(x1D(n1D), y1D(n1D), z1D(n1D), sigsite(n1D) ) 57 | allocate ( ex1D(n1D), ey1D(n1D), jz1D(n1D), bx1D(n1D), by1D(n1D), bz1D(n1D) ) 58 | do i = 1,n1D 59 | x1D(i) = rxLoc(i, 1) 60 | y1D(i) = rxLoc(i, 2) 61 | z1D(i) = rxLoc(i, 3) 62 | enddo 63 | 64 | ! 65 | ! store the conductivity at the site location for later use. 66 | ! The assumption is that receivers on a boundary are actually in the top layer. 67 | ! So a sea-floor site would use the sea layer conductivity to convert to vertical 68 | ! current to electric field. 69 | do i = 1, n1D 70 | sigsite(i) = sig1D(1) 71 | do j = 2,nlay1D 72 | if (zlay1D(j) .lt. z1D(i)) then 73 | sigsite(i) = sig1D(j) 74 | endif 75 | enddo 76 | enddo 77 | 78 | ! do computation 79 | ! inner loop over frequencies 80 | do iFreq = 1, nFreq 81 | 82 | ftx1D = freqArray(iFreq) 83 | 84 | ! loop over each transmitter 85 | do iTx = 1, nTx 86 | 87 | ! assign Tx parameters: 88 | xTx1D = txLoc(iTx, 1) 89 | yTx1D = txLoc(iTx, 2) 90 | zTx1D = txLoc(iTx, 3) 91 | azimuthTx1D = txLoc(iTx, 4) 92 | dipTx1D = txLoc(iTx, 5) 93 | 94 | ! compute CSEM fields 95 | call comp_dipole1D 96 | 97 | ! 98 | ! Output the response for the current transmitter, note that jz is converted to ez here 99 | ! The assumption is that receivers on a boundary are actually in the top layer. 100 | ! So a seafloor site would use the sea layer conductivity to convert to vertical 101 | ! current to electric field. 102 | do i = 1,n1D 103 | jz1D(i) = jz1D(i) / sigsite(i) 104 | predEx1D(i, iTx, iFreq) = ex1D(i) 105 | predEy1D(i, iTx, iFreq) = ey1D(i) 106 | predEz1D(i, iTx, iFreq) = jz1D(i) 107 | 108 | enddo 109 | 110 | enddo 111 | 112 | enddo 113 | 114 | ! 115 | ! Deallocate arrays 116 | ! 117 | deallocate ( x1D,y1D,z1D,sigsite ) 118 | deallocate ( ex1D,ey1D,jz1D,bx1D,by1D,bz1D ) 119 | deallocate ( sig1D, zlay1D ) 120 | 121 | return 122 | 123 | end subroutine callDipole1D 124 | 125 | 126 | !====================================================================== 127 | ! `callDipoleEB` computes specific component of electromagnetic fields 128 | ! excited by electric dipole source. 129 | ! 130 | ! -compFlag indicates user-defined component 131 | ! 132 | !====================================================================== 133 | subroutine callDipole1DEB(txLoc, rxLoc, freqArray, sigma1D, depth1D, & 134 | nTx, nRx, nFreq, nZ, compFlag, pred1D) 135 | 136 | use dipole1d 137 | implicit none 138 | 139 | integer, intent(in) :: nTx, nRx, nFreq, nZ, compFlag 140 | real(kind = 8), dimension(nTx, 5), intent(in) :: txLoc 141 | real(kind = 8), dimension(nRx, 3), intent(in) :: rxLoc 142 | real(kind = 8), dimension(nFreq), intent(in) :: freqArray 143 | real(kind = 8), dimension(nZ), intent(in) :: sigma1D, depth1D 144 | complex(kind = 8), dimension(nRx,nTx,nFreq), intent(inout) :: pred1D 145 | 146 | ! local variables 147 | integer :: i, j, iTx, iFreq 148 | real(8), dimension(:), allocatable :: sigsite 149 | 150 | ! Initialize the default values 151 | call init_defaults_Dipole1D 152 | 153 | ! Specify some parameters required by Dipole1D: 154 | HTmethod1D = 'kk_ht_201' 155 | outputdomain1D = 'spatial' 156 | phaseConvention = 'lead' 157 | lbcomp = .false. 158 | sdm1D = 1.0 ! (Am), dipole moment 159 | lUseSpline1D = .true. 160 | linversion = .false. ! Compute Derivatives with respect to sigma(layers) 161 | emFlag = compFlag 162 | 163 | !! allocate variables 164 | ! 1D model parameter 165 | nlay1D = size(depth1D, 1) 166 | allocate(sig1D(nlay1D), zlay1D(nlay1D)) 167 | do i = 1, nlay1D 168 | sig1D(i) = sigma1D(i) 169 | zlay1D(i) = depth1D(i) 170 | enddo 171 | 172 | ! receiver 173 | n1D = size(rxLoc, 1) 174 | allocate(x1D(n1D), y1D(n1D), z1D(n1D), sigsite(n1D) ) 175 | !allocate ( ex1D(n1D), ey1D(n1D), jz1D(n1D), bx1D(n1D), by1D(n1D), bz1D(n1D) ) 176 | select case (compFlag) 177 | case(1) 178 | allocate(ex1D(n1D)) 179 | case(2) 180 | allocate(ey1D(n1D)) 181 | case(3) 182 | allocate(jz1D(n1D)) 183 | case(4) 184 | allocate(bx1D(n1D)) 185 | lbcomp = .true. 186 | case(5) 187 | allocate(by1D(n1D)) 188 | lbcomp = .true. 189 | case(6) 190 | allocate(bz1D(n1D)) 191 | lbcomp = .true. 192 | end select 193 | 194 | do i = 1,n1D 195 | x1D(i) = rxLoc(i, 1) 196 | y1D(i) = rxLoc(i, 2) 197 | z1D(i) = rxLoc(i, 3) 198 | enddo 199 | 200 | ! 201 | ! store the conductivity at the site location for later use. 202 | ! The assumption is that receivers on a boundary are actually in the top layer. 203 | ! So a sea-floor site would use the sea layer conductivity to convert to vertical 204 | ! current to electric field. 205 | do i = 1, n1D 206 | sigsite(i) = sig1D(1) 207 | do j = 2,nlay1D 208 | if (zlay1D(j) .lt. z1D(i)) then 209 | sigsite(i) = sig1D(j) 210 | endif 211 | enddo 212 | enddo 213 | 214 | ! do computation 215 | ! inner loop over frequencies 216 | do iFreq = 1, nFreq 217 | 218 | ftx1D = freqArray(iFreq) 219 | 220 | ! loop over each transmitter 221 | do iTx = 1, nTx 222 | 223 | ! assign Tx parameters: 224 | xTx1D = txLoc(iTx, 1) 225 | yTx1D = txLoc(iTx, 2) 226 | zTx1D = txLoc(iTx, 3) 227 | azimuthTx1D = txLoc(iTx, 4) 228 | dipTx1D = txLoc(iTx, 5) 229 | 230 | ! compute CSEM fields 231 | call comp_dipole1D 232 | 233 | ! 234 | ! Output the response for the current transmitter, note that jz is converted to ez here 235 | ! The assumption is that receivers on a boundary are actually in the top layer. 236 | ! So a seafloor site would use the sea layer conductivity to convert to vertical 237 | ! current to electric field. 238 | do i = 1,n1D 239 | 240 | if (compFlag .eq. 1) then 241 | pred1D(i, iTx, iFreq) = ex1D(i) 242 | elseif (compFlag .eq. 2) then 243 | pred1D(i, iTx, iFreq) = ey1D(i) 244 | elseif (compFlag .eq. 3) then 245 | jz1D(i) = jz1D(i) / sigsite(i) 246 | pred1D(i, iTx, iFreq) = jz1D(i) 247 | elseif (compFlag .eq. 4) then 248 | pred1D(i, iTx, iFreq) = bx1D(i) 249 | elseif (compFlag .eq. 5) then 250 | pred1D(i, iTx, iFreq) = by1D(i) 251 | elseif (compFlag .eq. 6) then 252 | pred1D(i, iTx, iFreq) = bz1D(i) 253 | endif 254 | 255 | enddo 256 | 257 | enddo 258 | 259 | enddo 260 | 261 | ! 262 | ! Deallocate arrays 263 | ! 264 | deallocate ( x1D,y1D,z1D,sigsite ) 265 | deallocate ( sig1D, zlay1D ) 266 | select case(compFlag) 267 | case(1) 268 | deallocate ( ex1D) 269 | case(2) 270 | deallocate(ey1D) 271 | case(3) 272 | deallocate(jz1D) 273 | case(4) 274 | deallocate(bx1D) 275 | case(5) 276 | deallocate(by1D) 277 | case(6) 278 | deallocate(bz1D) 279 | end select 280 | 281 | return 282 | 283 | end subroutine callDipole1DEB 284 | 285 | 286 | !====================================================================== 287 | ! `callDipole1DFinite` computes specific component of electromagnetic 288 | ! fields excited by long-wire source. 289 | ! 290 | !====================================================================== 291 | subroutine callDipole1DFinite(txLoc, rxLoc, dipLen, nP, freqArray, sigma1D, & 292 | depth1D, nTx, nRx, nFreq, nZ, compFlag, pred1D) 293 | 294 | use dipole1d 295 | implicit none 296 | 297 | integer, intent(in) :: nP, nTx, nRx, nFreq, nZ, compFlag 298 | real(kind = 8), intent(in) :: dipLen 299 | real(kind = 8), dimension(nTx, 5), intent(in) :: txLoc 300 | real(kind = 8), dimension(nRx, 3), intent(in) :: rxLoc 301 | real(kind = 8), dimension(nFreq), intent(in) :: freqArray 302 | real(kind = 8), dimension(nZ), intent(in) :: sigma1D, depth1D 303 | complex(kind = 8), dimension(nRx,nTx,nFreq), intent(inout) :: pred1D 304 | 305 | ! local variables 306 | integer :: i, j, iTx, iFreq 307 | real(8), dimension(:), allocatable :: sigsite 308 | 309 | ! Initialize the default values 310 | call init_defaults_Dipole1D 311 | 312 | ! Specify some parameters required by Dipole1D: 313 | HTmethod1D = 'kk_ht_201' 314 | outputdomain1D = 'spatial' 315 | phaseConvention = 'lead' 316 | lbcomp = .false. 317 | sdm1D = 1.0 ! (Am), dipole moment 318 | lUseSpline1D = .true. 319 | linversion = .false. ! Compute Derivatives with respect to sigma(layers) 320 | 321 | ! initialize relevent parameters 322 | emFlag = compFlag 323 | lenTx1D = dipLen 324 | numIntegPts = nP 325 | 326 | !! allocate variables 327 | ! 1D model parameter 328 | nlay1D = size(depth1D, 1) 329 | allocate(sig1D(nlay1D), zlay1D(nlay1D)) 330 | do i = 1, nlay1D 331 | sig1D(i) = sigma1D(i) 332 | zlay1D(i) = depth1D(i) 333 | enddo 334 | 335 | ! receiver 336 | n1D = size(rxLoc, 1) 337 | allocate(x1D(n1D), y1D(n1D), z1D(n1D), sigsite(n1D) ) 338 | !allocate ( ex1D(n1D), ey1D(n1D), jz1D(n1D), bx1D(n1D), by1D(n1D), bz1D(n1D) ) 339 | select case (compFlag) 340 | case(1) 341 | allocate(ex1D(n1D)) 342 | case(2) 343 | allocate(ey1D(n1D)) 344 | case(3) 345 | allocate(jz1D(n1D)) 346 | case(4) 347 | allocate(bx1D(n1D)) 348 | lbcomp = .true. 349 | case(5) 350 | allocate(by1D(n1D)) 351 | lbcomp = .true. 352 | case(6) 353 | allocate(bz1D(n1D)) 354 | lbcomp = .true. 355 | end select 356 | 357 | do i = 1,n1D 358 | x1D(i) = rxLoc(i, 1) 359 | y1D(i) = rxLoc(i, 2) 360 | z1D(i) = rxLoc(i, 3) 361 | enddo 362 | 363 | ! 364 | ! store the conductivity at the site location for later use. 365 | ! The assumption is that receivers on a boundary are actually in the top layer. 366 | ! So a sea-floor site would use the sea layer conductivity to convert to vertical 367 | ! current to electric field. 368 | do i = 1, n1D 369 | sigsite(i) = sig1D(1) 370 | do j = 2,nlay1D 371 | if (zlay1D(j) .lt. z1D(i)) then 372 | sigsite(i) = sig1D(j) 373 | endif 374 | enddo 375 | enddo 376 | 377 | ! do computation 378 | ! inner loop over frequencies 379 | do iFreq = 1, nFreq 380 | 381 | ftx1D = freqArray(iFreq) 382 | 383 | ! loop over each transmitter 384 | do iTx = 1, nTx 385 | 386 | ! assign Tx parameters: 387 | xTx1D = txLoc(iTx, 1) 388 | yTx1D = txLoc(iTx, 2) 389 | zTx1D = txLoc(iTx, 3) 390 | azimuthTx1D = txLoc(iTx, 4) 391 | dipTx1D = txLoc(iTx, 5) 392 | 393 | ! compute CSEM fields 394 | call comp_dipole1D 395 | 396 | ! 397 | ! Output the response for the current transmitter, note that jz is converted to ez here 398 | ! The assumption is that receivers on a boundary are actually in the top layer. 399 | ! So a seafloor site would use the sea layer conductivity to convert to vertical 400 | ! current to electric field. 401 | do i = 1,n1D 402 | 403 | if (compFlag .eq. 1) then 404 | pred1D(i, iTx, iFreq) = ex1D(i) 405 | elseif (compFlag .eq. 2) then 406 | pred1D(i, iTx, iFreq) = ey1D(i) 407 | elseif (compFlag .eq. 3) then 408 | jz1D(i) = jz1D(i) / sigsite(i) 409 | pred1D(i, iTx, iFreq) = jz1D(i) 410 | elseif (compFlag .eq. 4) then 411 | pred1D(i, iTx, iFreq) = bx1D(i) 412 | elseif (compFlag .eq. 5) then 413 | pred1D(i, iTx, iFreq) = by1D(i) 414 | elseif (compFlag .eq. 6) then 415 | pred1D(i, iTx, iFreq) = bz1D(i) 416 | endif 417 | 418 | enddo 419 | 420 | enddo 421 | 422 | enddo 423 | 424 | ! 425 | ! Deallocate arrays 426 | ! 427 | deallocate ( x1D,y1D,z1D,sigsite ) 428 | deallocate ( sig1D, zlay1D ) 429 | select case(compFlag) 430 | case(1) 431 | deallocate ( ex1D) 432 | case(2) 433 | deallocate(ey1D) 434 | case(3) 435 | deallocate(jz1D) 436 | case(4) 437 | deallocate(bx1D) 438 | case(5) 439 | deallocate(by1D) 440 | case(6) 441 | deallocate(bz1D) 442 | end select 443 | 444 | return 445 | 446 | end subroutine callDipole1DFinite 447 | -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/src/comptem1d.f90: -------------------------------------------------------------------------------- 1 | !=============================================================================== 2 | ! compTEM1d get TEM response excited by a rectange loop 3 | ! 4 | !=============================================================================== 5 | subroutine compTEM1d(xLen, yLen, nlayer, rho, thickness, nTimePoint, timePoint, timeResp) 6 | 7 | use tem1dmod 8 | implicit none 9 | 10 | integer,intent(in):: nTimePoint, nlayer 11 | real(kind=8),intent(in):: xLen, yLen 12 | real(kind=8),dimension(nlayer),intent(in) :: rho, thickness 13 | real(kind=8),dimension(nTimePoint),intent(in):: timePoint 14 | real(kind=8),dimension(nTimePoint),intent(inout):: timeResp 15 | 16 | ! initialize stuff 17 | call initTEMModel(xLen, yLen) 18 | 19 | ! calculate frequency response 20 | call compFreqResponse(nlayer, rho, thickness) 21 | 22 | ! transform response from frequency domain to time domain 23 | call temsineTransform(ntemFreq,temFreq,temfreqResp,nTimePoint,timePoint,timeResp) 24 | 25 | ! deallocate arrays 26 | call deallocateTEMFwd 27 | 28 | end subroutine 29 | 30 | 31 | !=============================================================================== 32 | ! compTEM1d get TEM response excited by a rectange loop 33 | ! 34 | !=============================================================================== 35 | subroutine compTEM1dResponse(xLen, yLen, nlayer, rho, thickness, nTimePoint, & 36 | timePoint, timeResp) 37 | 38 | use tem1dmod 39 | implicit none 40 | 41 | integer,intent(in):: nTimePoint, nlayer 42 | real(kind=8),intent(in):: xLen, yLen 43 | real(kind=8),dimension(nTimePoint),intent(in):: timePoint 44 | real(kind=8),dimension(nlayer),intent(in):: rho, thickness 45 | real(kind=8),dimension(nTimePoint),intent(inout):: timeResp 46 | 47 | ! initialize stuff 48 | call initTEMModel(xLen, yLen) 49 | 50 | ! calculate frequency response 51 | call compCentralFreqResponse(nlayer, rho, thickness) 52 | 53 | ! transform response from frequency domain to time domain 54 | call temsineTransform(ntemFreq,temFreq,temfreqResp,nTimePoint,timePoint,timeResp) 55 | 56 | ! deallocate arrays 57 | call deallocateTEMFwd 58 | 59 | end subroutine 60 | 61 | 62 | subroutine compTEM1dResponseNew(xLen, yLen, nlayer, rho, thickness, nTimePoint, & 63 | timePoint, timeResp) 64 | 65 | use tem1dmod 66 | implicit none 67 | 68 | integer,intent(in):: nTimePoint, nlayer 69 | real(kind=8),intent(in):: xLen, yLen 70 | real(kind=8),dimension(nTimePoint),intent(in):: timePoint 71 | real(kind=8),dimension(nlayer),intent(in):: rho, thickness 72 | real(kind=8),dimension(nTimePoint),intent(inout):: timeResp 73 | 74 | ! initialize stuff 75 | call initTEMModel(xLen, yLen) 76 | 77 | ! calculate frequency response 78 | call compFreqResponseDipole(nLayer, rho, thickness) 79 | 80 | ! transform response from frequency domain to time domain 81 | call temsineTransform(ntemFreq,temFreq,temfreqResp,nTimePoint,timePoint,timeResp) 82 | 83 | ! deallocate arrays 84 | call deallocateTEMFwd 85 | 86 | end subroutine 87 | -------------------------------------------------------------------------------- /src/TBFwdSolver/deps/src/mt1dmod.f90: -------------------------------------------------------------------------------- 1 | !=============================================================================== 2 | ! `mt1dfwd` calculates MT response over 1-D layered earth. 3 | ! 4 | !=============================================================================== 5 | subroutine mt1dfwd(nlayer, resistivity, thickness, nfreq, mtFreq, mtPredData) 6 | 7 | implicit none 8 | 9 | integer :: nlayer, nfreq 10 | real(8),dimension(nlayer) :: resistivity, thickness 11 | real(8),dimension(nfreq):: mtFreq 12 | real(8),dimension(2*nfreq):: mtPredData 13 | 14 | ! local variables 15 | integer :: i, j, k 16 | real(8) :: muomega, zMTr, zMTi 17 | complex(8) :: omi, dj, wj, rj, re 18 | complex(8), dimension(nlayer) :: layerimpedance 19 | real(8),dimension(nfreq) :: appRes, appPhs 20 | real(8) :: pi = 3.14159265358979323846264d0 21 | real(8) :: mu0 22 | complex(8) :: ii = (0d0,1d0) 23 | 24 | ! 25 | k = 1 26 | mu0 = 4d-7 * pi 27 | do i = 1, nfreq 28 | muomega = 2.0 * pi * mtFreq(i) * mu0 29 | omi = muomega * ii 30 | j = nlayer 31 | layerimpedance(j) = sqrt(omi * resistivity(j)) 32 | 33 | do j = nlayer-1, 1, -1 34 | dj = sqrt(omi / resistivity(j) ) 35 | wj = dj * resistivity(j) 36 | rj = (wj - layerimpedance(j+1)) / (wj + layerimpedance(j+1) ) 37 | re = rj * exp( - 2.0 * thickness(j) * dj ) 38 | layerimpedance(j) = wj * ((1.0 - re) / (1.0 + re)) 39 | end do 40 | 41 | ! get impedance at surface 42 | zMTr = real(layerimpedance(1)) 43 | zMTi = aimag(layerimpedance(1)) 44 | 45 | ! get apparent resistivity and phase 46 | !appRes(i) = abs(layerimpedance(1)) ** 2 / muomega 47 | !appPhs(i) = atan2(zMTi, zMTr) * 180.0 / pi 48 | mtPredData(k) = zMTr 49 | mtPredData(k+1) = zMTi 50 | k = k + 2 51 | end do 52 | 53 | end subroutine 54 | -------------------------------------------------------------------------------- /src/TBStruct/TBStruct.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBStruct` defines data structure and routines for transdimensional 4 | # MCMC sampling. 5 | # 6 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 7 | # 8 | #------------------------------------------------------------------------------- 9 | module TBStruct 10 | 11 | using TransdEM.TBUtility 12 | export MCParameter, MCPrior 13 | export MCStatus, MChainStep 14 | export MCDataFit 15 | export MChainArray 16 | export initMCPrior, initMCParameter 17 | export initMCDataFit, initMChainArray 18 | export initModelParameter 19 | 20 | #------------------------------------------------------------------------------- 21 | """ 22 | Struct `MCParameter` defines parameter structure for MCMC chains. 23 | 24 | """ 25 | mutable struct MCParameter{Ti<:Int, Tv<:Float64} 26 | 27 | # 28 | nLayer::Ti # current layer number 29 | layeridx::Ti # selected layer index to perturb 30 | zNode::Vector{Tv} # interface depth 31 | rho::Vector{Tv} # layer resistivity 32 | inBound::Bool # indicator for inside or outside prior bounds 33 | 34 | end # MCParameter 35 | 36 | 37 | #------------------------------------------------------------------------------- 38 | """ 39 | Struct `MCPrior` defines prior bounds of variables. 40 | 41 | """ 42 | mutable struct MCPrior{Ti<:Int, Tv<:Float64} 43 | 44 | # 45 | burninsamples::Ti 46 | totalsamples::Ti 47 | 48 | # min/max number of layers 49 | nlayermin::Ti 50 | nlayermax::Ti 51 | 52 | # lower/upper bounds for interface depth 53 | zmin::Tv 54 | zmax::Tv 55 | 56 | # minimum layer thickness 57 | hmin::Tv 58 | 59 | # lower/upper bounds for layer resistivity 60 | rhomin::Tv 61 | rhomax::Tv 62 | 63 | # standard derivation associated with variables 64 | zstd::Tv 65 | rhostd::Tv 66 | mrhostd::Tv 67 | 68 | # parameters for post analysis 69 | nzBins::Ti 70 | npBins::Ti 71 | credInterval::Tv 72 | 73 | end # RJPrior 74 | 75 | 76 | #------------------------------------------------------------------------------- 77 | """ 78 | Struct `MCStatus` stores proposal statistics of MCMC chain. 79 | 80 | """ 81 | mutable struct MCStatus{T<:Int} 82 | 83 | # 84 | accepted::Bool 85 | acceptstats::Vector{T} # acceptance statistics 86 | rejectstats::Vector{T} # rejection statistics 87 | 88 | end # RJStatus 89 | 90 | 91 | #------------------------------------------------------------------------------- 92 | """ 93 | Struct `MChainStep` records the movement of MCMC chain at every step. 94 | 95 | """ 96 | mutable struct MChainStep{T<:Bool} 97 | 98 | isBirth::T 99 | isDeath::T 100 | isMove::T 101 | isPerturb::T 102 | 103 | end 104 | 105 | 106 | #------------------------------------------------------------------------------- 107 | """ 108 | Struct `MCDataFit` stores data from current and proposed models of MCMC 109 | chain at every step. 110 | 111 | """ 112 | mutable struct MCDataFit{T<:Real} 113 | 114 | # current model 115 | currPredData::Vector{T} 116 | currLikelihood::T 117 | currMisfit::T 118 | 119 | # proposed model 120 | propPredData::Vector{T} 121 | propLikelihood::T 122 | propMisfit::T 123 | 124 | # data fitting 125 | dataResiduals::Vector{T} 126 | 127 | end 128 | 129 | 130 | #------------------------------------------------------------------------------- 131 | """ 132 | Struct `MChainArray` stores status of MCMC chain at every step. 133 | 134 | """ 135 | mutable struct MChainArray{Ti<:Int, Tv<:Real} 136 | 137 | # current sample number 138 | nsample::Ti 139 | 140 | # starting model 141 | startModel::MCParameter 142 | chainstep::Array{Ti, 2} 143 | chainvalue::Array{Tv, 2} 144 | chainindice::Vector{Ti} 145 | chainnlayer::Vector{Ti} 146 | chainMisfit::Array{Tv} 147 | residuals::Array{Tv} 148 | 149 | end 150 | 151 | 152 | #------------------------------------------------------------------------------- 153 | """ 154 | `initMCParameter()` initializes parameter struct for MCMC chain. 155 | 156 | """ 157 | function initMCParameter() 158 | 159 | # 160 | itmp = zero(0) 161 | pvec = zeros(0) 162 | mcParam = MCParameter(itmp, itmp, pvec, pvec, false) 163 | return mcParam 164 | 165 | end # initMCParameter 166 | 167 | 168 | #------------------------------------------------------------------------------- 169 | """ 170 | `initMCDataFit()` initializes datafit struct for MCMC chain. 171 | 172 | """ 173 | function initMCDataFit() 174 | 175 | # 176 | it = 0.0 177 | iv = zeros(0) 178 | mcDatafit = MCDataFit(iv, it, it, iv, it, it, iv) 179 | 180 | return mcDatafit 181 | 182 | end # initMCDataFit 183 | 184 | #------------------------------------------------------------------------------ 185 | """ 186 | `initMCPrior()` 187 | 188 | """ 189 | function initMCPrior() 190 | 191 | # 192 | dtmp = 0.05 193 | mclimits = MCPrior(100, 1000, 2, 10, 0., 5.0, 0., 1.0, 5.0, dtmp, dtmp,dtmp, 194 | 400, 400, 0.9) 195 | 196 | return mclimits 197 | 198 | end # initMCPrior 199 | 200 | 201 | #------------------------------------------------------------------------------- 202 | """ 203 | `initMChainArray(tblimits::MCPrior)` initializes struct `MChainArray`. 204 | 205 | """ 206 | function initMChainArray(mclimits::MCPrior) 207 | 208 | # 209 | currsample = 0 210 | startModel = initMCParameter() 211 | # 212 | nsample = mclimits.totalsamples 213 | chainstep = zeros(Int, nsample, 2) 214 | chainvalue = zeros(Float64, nsample, 2) 215 | chainindice = zeros(Int, nsample) 216 | chainncell = zeros(Int, nsample) 217 | chainMisfit = zeros(Float64, nsample) 218 | residuals = zeros(Float64, 0) 219 | 220 | mcArray = MChainArray(currsample, startModel, chainstep, chainvalue, 221 | chainindice, chainncell, chainMisfit, residuals) 222 | 223 | return mcArray 224 | 225 | end # initMChainArray 226 | 227 | 228 | #------------------------------------------------------------------------------ 229 | """ 230 | `initModelParameter(mclimits)` 231 | 232 | initialize model parameter. 233 | 234 | """ 235 | function initModelParameter(mclimits::MCPrior) 236 | 237 | # 238 | mcParam = initMCParameter() 239 | 240 | # set number of layer 241 | currLayerNumber = 3#unirandInteger(mclimits.nlayermin, mclimits.nlayermax) 242 | mcParam.nLayer = currLayerNumber 243 | 244 | # set the locations of interface depth randomly 245 | mcParam.zNode = zeros(Float64, mclimits.nlayermax) 246 | mcParam.rho = zeros(Float64, mclimits.nlayermax) 247 | for i = 1:currLayerNumber 248 | mcParam.zNode[i] = unirandDouble(mclimits.zmin, mclimits.zmax) 249 | 250 | # set the layer resistivity randomly 251 | mcParam.rho[i] = unirandDouble(mclimits.rhomin, mclimits.rhomax) 252 | 253 | end 254 | 255 | return mcParam 256 | 257 | end 258 | 259 | 260 | end # TBStruct 261 | -------------------------------------------------------------------------------- /src/TBUtility/TBUtility.jl: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # 3 | # module `TBUtility` defines utility routines for transdimensional Bayesian 4 | # inversion. 5 | # 6 | # (c) Ronghua Peng and Bo Han, China University of Geosciences, Wuhan, 2020-2022. 7 | # 8 | #------------------------------------------------------------------------------- 9 | module TBUtility 10 | 11 | using Random 12 | 13 | export unirandInteger 14 | export unirandDouble 15 | export findLocation1D, findNearest1D 16 | export getGaussianProbability 17 | 18 | #------------------------------------------------------------------------------- 19 | """ 20 | `unirandInteger(vmin, vmax)` 21 | 22 | returns a random integer between `vmin` and `vmax` 23 | 24 | """ 25 | function unirandInteger(vmin::Int, vmax::Int) 26 | 27 | rval = rand(vmin:vmax) 28 | return rval 29 | 30 | end 31 | 32 | 33 | #------------------------------------------------------------------------------- 34 | """ 35 | `randDouble(vmin, vmax)` 36 | 37 | returns a random float number between `vmin` and `vmax` 38 | 39 | """ 40 | function unirandDouble(vmin::Float64, vmax::Float64) 41 | 42 | rval = vmin + (vmax - vmin) * rand() 43 | return rval 44 | 45 | end 46 | 47 | 48 | #------------------------------------------------------------------------------- 49 | """ 50 | `findLocation1D(point, x)` 51 | 52 | """ 53 | function findLocation1D(point::T, x::Vector{T}) where {T} 54 | 55 | (val, idx) = findmin(abs.(x .- point)) 56 | 57 | return idx 58 | 59 | end 60 | 61 | 62 | #------------------------------------------------------------------------------- 63 | """ 64 | `findNearest1D(point, x)` 65 | 66 | """ 67 | function findNearest1D(point::T, x::Vector{T}) where {T} 68 | 69 | # first sort x 70 | idxrank = sortperm(x) 71 | y = x[idxrank] 72 | (val, idx) = findmin(abs.(y .- point)) 73 | val = y[idx] 74 | if (val