├── model ├── __init__.py ├── model_rf_swd_vs_thk.py ├── model_rf.py └── model_surf.py ├── .vscode ├── settings.json └── c_cpp_properties.json ├── pyhmc ├── __pycache__ │ ├── hmc.cpython-37.pyc │ ├── hmc.cpython-38.pyc │ ├── hmc.cpython-312.pyc │ ├── hmcda.cpython-312.pyc │ └── stats.cpython-38.pyc ├── hmc.py └── hmcda.py ├── src ├── __pycache__ │ └── plot_results.cpython-312.pyc ├── RF │ ├── CMakeLists.txt │ ├── fftpack.f90 │ ├── rf_cal.hpp │ ├── deconit.f90 │ └── main.cpp ├── SWD │ ├── CMakeLists.txt │ ├── main.cpp │ ├── surfdisp.hpp │ ├── surfdisp.cpp │ └── slegn96.f90 ├── utils.py └── plot_results.py ├── param.yaml ├── CMakeLists.txt ├── test_forward.py ├── README.md ├── plot.py ├── main_DA.py ├── main_base.py ├── cmake └── FindFFTW.cmake └── license.md /model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "fortran.linter.modOutput": "~/code/python/RfSurfHmc/.vscode/obj" 3 | } -------------------------------------------------------------------------------- /pyhmc/__pycache__/hmc.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/pyhmc/__pycache__/hmc.cpython-37.pyc -------------------------------------------------------------------------------- /pyhmc/__pycache__/hmc.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/pyhmc/__pycache__/hmc.cpython-38.pyc -------------------------------------------------------------------------------- /pyhmc/__pycache__/hmc.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/pyhmc/__pycache__/hmc.cpython-312.pyc -------------------------------------------------------------------------------- /pyhmc/__pycache__/hmcda.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/pyhmc/__pycache__/hmcda.cpython-312.pyc -------------------------------------------------------------------------------- /pyhmc/__pycache__/stats.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/pyhmc/__pycache__/stats.cpython-38.pyc -------------------------------------------------------------------------------- /src/__pycache__/plot_results.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nqdu/RfSurfHmc/HEAD/src/__pycache__/plot_results.cpython-312.pyc -------------------------------------------------------------------------------- /src/RF/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | file(GLOB SOURCE "*.cpp" "*.f90") 3 | pybind11_add_module(librf ${SOURCE}) 4 | 5 | target_link_libraries(librf PRIVATE ${FFTW_LIBRARIES} ) 6 | install(TARGETS librf 7 | COMPONENT python 8 | LIBRARY DESTINATION ${PROJECT_SOURCE_DIR}/model/lib 9 | ) 10 | # add_library(libcps330 ${SOURCE}) -------------------------------------------------------------------------------- /src/SWD/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | file(GLOB SOURCE "*.cpp" "*.f90" "*.f") 3 | pybind11_add_module(libsurf ${SOURCE}) 4 | 5 | target_link_libraries(libsurf PRIVATE ${FFTW_LIBRARIES} ) 6 | install(TARGETS libsurf 7 | COMPONENT python 8 | LIBRARY DESTINATION ${PROJECT_SOURCE_DIR}/model/lib 9 | ) 10 | # add_library(libcps330 ${SOURCE}) -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "/home/nqdu/.conda/envs/work/include/python3.12", 8 | "/home/nqdu/.conda/envs/work/lib/python3.12/site-packages/pybind11/include" 9 | ], 10 | "defines": [], 11 | "compilerPath": "/usr/bin/gcc", 12 | "cStandard": "gnu17", 13 | "cppStandard": "gnu++14", 14 | "intelliSenseMode": "gcc-x64" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import os 4 | import os.path as op 5 | from scipy.interpolate import interp1d 6 | 7 | def next_power_of_2(x): 8 | return 1 if x == 0 else 2**(x-1).bit_length() 9 | 10 | def get_rf_inv_para(real_rf_dobs, real_rf_time, t_start, t_end): 11 | if t_start < real_rf_time[0] or t_end > real_rf_time[-1]: 12 | raise Exception("wrong t_start or t_end") 13 | 14 | assert len(real_rf_dobs) == len(real_rf_time) 15 | obs_dt = real_rf_time[1] - real_rf_time[0] 16 | 17 | t_syn_len = t_end - t_start 18 | nt = int(t_syn_len / obs_dt) 19 | nt = next_power_of_2(nt) 20 | 21 | t = np.linspace(t_start, t_end, nt) 22 | 23 | dt = t[1] - t[0] 24 | time_shift = -t_start 25 | 26 | fun1 = interp1d(real_rf_time, real_rf_dobs, kind = 'cubic') 27 | interp_dobs = fun1(t) 28 | 29 | 30 | return interp_dobs, nt, dt, time_shift -------------------------------------------------------------------------------- /src/RF/fftpack.f90: -------------------------------------------------------------------------------- 1 | subroutine rfft(inp,out,n) 2 | use, intrinsic :: iso_c_binding 3 | implicit none 4 | include 'fftw3.f03' 5 | 6 | 7 | integer(c_int),intent(in) :: n 8 | !complex(c_double_complex),INTENT(IN) :: inp(n) 9 | real(c_double),intent(in) :: inp(n) 10 | complex(c_double_complex),intent(inout) :: out(n/2+1) 11 | 12 | ! fft plan 13 | type(C_PTR) :: plan 14 | real(c_double) :: inp1(n) 15 | inp1(:) = inp(:) 16 | plan = fftw_plan_dft_r2c_1d(n,inp1,out,FFTW_ESTIMATE) 17 | 18 | call fftw_execute_dft_r2c(plan, inp1, out) 19 | call fftw_destroy_plan(plan) 20 | 21 | end subroutine rfft 22 | 23 | subroutine irfft(inp,out,n) 24 | use, intrinsic :: iso_c_binding 25 | implicit none 26 | include 'fftw3.f03' 27 | 28 | integer(c_int),intent(in) :: n 29 | complex(c_double_complex),intent(in) :: inp(n/2+1) 30 | real(c_double),intent(inout) :: out(n) 31 | 32 | ! fft plan 33 | type(C_PTR) :: plan 34 | complex(c_double_complex) inp1(n/2+1) 35 | inp1(:) = inp(:) 36 | plan = fftw_plan_dft_c2r_1d(n,inp1,out,FFTW_ESTIMATE) 37 | 38 | call fftw_execute_dft_c2r(plan, inp1, out) 39 | call fftw_destroy_plan(plan) 40 | 41 | out = out / n 42 | 43 | end subroutine irfft -------------------------------------------------------------------------------- /param.yaml: -------------------------------------------------------------------------------- 1 | rf: 2 | ray_p: 0.045 3 | dt: 0.4 4 | nt: 125 5 | gauss: 1.5 6 | time_shift: 5. 7 | water_level: 0.001 8 | type: P # P or S 9 | method: "freq" # time or freq, freq is more stable 10 | 11 | # weight factor 12 | weight: 1. 13 | 14 | swd: 15 | # period used for Rayleigh phase/group, and Love wave phase/group 16 | tRc: [ 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 17 | 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 18 | 31., 32., 33., 34., 35., 36., 37., 38., 39., 40.] 19 | tRg: [ 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 20 | 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 21 | 31., 32., 33., 34., 35., 36., 37., 38., 39., 40.] 22 | tLc: [] 23 | tLg: [] 24 | 25 | 26 | # weight factor 27 | weight: 1. 28 | 29 | true_model: 30 | thk: [6.,6,13.,5,10,30,0] 31 | vs: [3.2,2.8,3.46,3.3,3.9,4.5,4.7] 32 | 33 | 34 | ## HMC 35 | hmc: 36 | seed: 991206 37 | nsamples: 800 38 | ndraws: 200 39 | Lrange: [5,20] # only used for HMCBase 40 | dt: 0.1 41 | nbest: 10 # average the best (nbest) models to get final result 42 | 43 | # parameters for dual averaging 44 | # in this case dt will be tuned according to the following 45 | target_ratio: 0.65 46 | L0: 10 47 | 48 | # output name 49 | name: "chain_joint" 50 | OUTPUT_DIR: ./results/ 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | 3 | cmake_minimum_required(VERSION 3.21.0) 4 | 5 | ################## compiler options ################## 6 | # compiler 7 | set(CMAKE_CXX_COMPILER ${CXX}) 8 | set(CMAKE_Fortran_COMPILER ${FC}) 9 | 10 | # compiler flags 11 | set(CMAKE_CXX_FLAGS ${CXXFLAGS}) 12 | 13 | project(SWDTTI LANGUAGES CXX Fortran) 14 | 15 | # build type 16 | set(CMAKE_BUILD_TYPE RELEASE) 17 | set(CMAKE_CXX_STANDARD 14) 18 | set(CMAKE_CXX_STANDARD_REQUIRED True) 19 | set(CMAKE_Fortran_MODULE_DIRECTORY ${PROJECT_SOURCE_DIR}/build/obj) 20 | 21 | set(CMAKE_CXX_FLAGS "-g -Wall") 22 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 23 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 24 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=native") 25 | set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -march=native -ffixed-line-length-none") 26 | #set(CMAKE_CXX_FLAGS_RELEASE "-O0 -D_GLIBCXX_DEBUG") 27 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") 28 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -xHost") 29 | set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -xHost -extend-source") 30 | else () 31 | set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -march=native -ffixed-line-length-none") 32 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=native") 33 | #set(CMAKE_CXX_FLAGS_RELEASE "-O0 -D_GLIBCXX_DEBUG") 34 | endif() 35 | 36 | set(CMAKE_VERBOSE_MAKEFILE on) 37 | 38 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 39 | find_package(FFTW REQUIRED COMPONENTS DOUBLE_LIB) 40 | include_directories(${FFTW_INCLUDE_DIRS}) 41 | 42 | find_package(Python3 COMPONENTS Interpreter Development) 43 | find_package(pybind11 CONFIG) 44 | add_subdirectory(${PROJECT_SOURCE_DIR}/src/RF) 45 | add_subdirectory(${PROJECT_SOURCE_DIR}/src/SWD) -------------------------------------------------------------------------------- /test_forward.py: -------------------------------------------------------------------------------- 1 | from matplotlib.colors import BoundaryNorm 2 | import numpy as np 3 | from model.model_rf import ReceiverFunc 4 | from model.model_surf import SurfWD 5 | from model.model_rf_swd_vs_thk import Joint_RF_SWD 6 | import matplotlib.pyplot as plt 7 | import time 8 | 9 | def main(): 10 | 11 | 12 | # surf parameters 13 | tRc = np.linspace(5,40,36) 14 | tRg = tRc.copy() 15 | model_swd = SurfWD(tRc=tRc,tRg=tRg) 16 | 17 | # receiver function parameters 18 | rayp = 0.045; dt = 0.1; 19 | nt = 500; gauss = 1.0; time_shift = 5.0 20 | water = 0.001; rf_type = 'P'; rf_method = "time" 21 | model_rf = ReceiverFunc(rayp,nt,dt,gauss,time_shift, 22 | water,rf_type, rf_method) 23 | 24 | 25 | 26 | 27 | # set model parameters 28 | thk = np.array([6,6,13,5,10,30,0]) 29 | # vs = np.array([3.2,2.8,3.46,3.3,3.9,4.5,4.7]) 30 | vs = np.array([3.2,3.4,3.46,3.7,3.9,4.5,4.7]) 31 | 32 | 33 | model_swd.set_thk(thk) 34 | model_rf.set_thk(thk) 35 | 36 | # joint inversion model 37 | sigma1 = 1.0; sigma2 = 1.0 38 | model = Joint_RF_SWD(sigma1,sigma2,model_rf,model_swd) 39 | 40 | # obs data 41 | dobs = None 42 | x = None 43 | 44 | # true model 45 | x = np.hstack((vs,thk)) 46 | drsyn,dssyn,_ = model.forward(x) 47 | 48 | 49 | t_rf = np.linspace(0,(nt-1)*dt, nt) - time_shift 50 | 51 | plt.figure(1,figsize=(14,30)) 52 | plt.subplot(3,1,1) 53 | plt.plot(t_rf,drsyn) 54 | plt.title("rf_syn") 55 | plt.subplot(3,1,2) 56 | plt.plot(tRc,dssyn[:len(tRc)]) 57 | plt.title("Rc_syn") 58 | plt.subplot(3,1,3) 59 | plt.plot(tRg,dssyn[len(tRc):]) 60 | plt.title("Rg_syn") 61 | 62 | plt.savefig("./syn_test.png") 63 | 64 | 65 | if __name__ == "__main__": 66 | tic = time.time() 67 | main() 68 | toc = time.time() 69 | print("time elapse: {}".format(toc-tic)) 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RFSurfHMC 2 | Joint inversion of Receiver Function and Surface Wave Disperion by Hamiltonian 3 | Monte Carlo Method 4 | 5 | Requirements 6 | ------------------- 7 | RFSurfHMC has been built upon a few modern packages for its performance and sustainability, as listed below: 8 | 9 | name | version| 10 | |:---:|:----:| 11 | |[Pybind11](https://github.com/pybind/pybind11) | >=0.4| 12 | |[FFTW3](https://www.fftw.org/) | >=3.3 | 13 | |[GCC](https://gcc.gnu.org/)| >=7.5| 14 | |[CMAKE](https://cmake.org/)| >=3.10.0| 15 | |mpi4py| >=3.0 | 16 | 17 | Installation 18 | ------------------- 19 | + This package requires some py-package as follow: 20 | - mpi4py 21 | - pybind11 22 | - numpy 23 | - h5py 24 | - pyyaml 25 | we recommand install those packages by anaconda 26 | ```shell 27 | conda create -n hmc_inv python=3.9 28 | conda install mpi4py pybind11-global numpy h5py pyyaml 29 | ``` 30 | 31 | + Then use the following to compile the code 32 | ``` 33 | mkdir -p build 34 | cd build 35 | cmake .. 36 | make -j4 37 | make install 38 | ``` 39 | 40 | Usage 41 | -------------------- 42 | + set your parameters in `param.yaml` 43 | + For naive HMC, try: `mpiexec -n 4 python main_base.py ` for parallel running 44 | + For HMC with dual averaging ([hoffman 2014](https://jmlr.org/papers/volume15/hoffman14a/hoffman14a.pdf),algorithm 5), try `mpiexec -n 4 python main_DA.py ` 45 | + `python plot.py` for drawing figures. 46 | 47 | New Features 48 | ------------------- 49 | + Python extensions for surface wave dispersion, receiver functions and their Frechet kernels. 50 | 51 | 52 | + A more general framework for HMC. 53 | 54 | Update 55 | ------------------- 56 | + 2023-08-23 we update the forward calculation of receiver function. RF could be calculated in time domain or frequency domain either. 57 | + 2025-07-26 update pybind11 interface, and add dual averaging, which significantly reduces the need for manual hyperparameter tunings on `dt` and `L`. 58 | 59 | References 60 | ------------------- 61 | Junliu Suwen, Qi‐Fu Chen, Nanqiao Du; Joint Inversion of Receiver Function and Surface Wave Dispersion by Hamiltonian Monte Carlo Sampling. Seismological Research Letters 2022;; 94 (1): 369–384. doi: https://doi.org/10.1785/0220220044 62 | -------------------------------------------------------------------------------- /src/RF/rf_cal.hpp: -------------------------------------------------------------------------------- 1 | extern "C"{ 2 | 3 | // base on nanqiao's surfdisp.hpp 4 | // That's the cpp warpper , packing the fortran code 5 | 6 | /** 7 | * computer the rf 8 | **/ 9 | 10 | void cal_rf_par_time_(const double *thk,const double *vp,const double *vs,const double *rho, 11 | const double *qa,const double *qb, 12 | int nlayer,int nt,double dt,double ray_p, 13 | double gauss,double time_shift, 14 | int rf_type,int par_type,double *rcv_fun, 15 | double *rcv_fun_p); 16 | 17 | void cal_rf_par_time_all_(const double *thk,const double *vp,const double *vs,const double *rho, 18 | const double *qa,const double *qb, 19 | int nlayer,int nt,double dt,double ray_p, 20 | double gauss,double time_shift, 21 | int rf_type,double *rcv_fun, 22 | double *rcv_fun_p); 23 | 24 | 25 | void cal_rf_time_(const double *thk,const double *vp,const double *vs,const double *rho, 26 | const double *qa,const double *qb, 27 | int nlayer,int nt,double dt,double ray_p, 28 | double gauss,double time_shift, 29 | int rf_type,double *rcv_fun); 30 | 31 | 32 | void cal_rf_par_freq_(const double *thk,const double *vp,const double *vs,const double *rho, 33 | const double *qa,const double *qb, 34 | int nlayer,int nt,double dt,double ray_p, 35 | double gauss,double time_shift,double water, 36 | int rf_type,int par_type,double *rcv_fun, 37 | double *rcv_fun_p); 38 | 39 | 40 | void cal_rf_par_freq_all_(const double *thk,const double *vp,const double *vs,const double *rho, 41 | const double *qa,const double *qb, 42 | int nlayer,int nt,double dt,double ray_p, 43 | double gauss,double time_shift,double water, 44 | int rf_type,double *rcv_fun, 45 | double *rcv_fun_p); 46 | 47 | void cal_rf_freq_(const double *thk,const double *vp,const double *vs,const double *rho, 48 | const double *qa,const double *qb, 49 | int nlayer,int nt,double dt,double ray_p, 50 | double gauss,double time_shift,double water, 51 | int rf_type,double *rcv_fun); 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import os 4 | import yaml 5 | 6 | 7 | from src.plot_results import Obs_data 8 | from src.plot_results import plotFig 9 | 10 | # load yaml files 11 | with open("param.yaml","r") as f: 12 | param = yaml.safe_load(f) 13 | 14 | # set para 15 | plotFigure = plotFig( 16 | HMC_output_path=param['hmc']['OUTPUT_DIR'], 17 | chain_name=param['hmc']['name'], 18 | figure_output_path=param['hmc']['OUTPUT_DIR']) 19 | plotFigure.obs_data.set_t(param['swd']['tRc'],"Rc") 20 | plotFigure.obs_data.set_t(param['swd']['tRg'],"Rg") 21 | 22 | t = np.arange(param['rf']['nt']) * param['rf']['dt'] - param['rf']['time_shift'] 23 | plotFigure.obs_data.set_t(t,"rf") 24 | # set_t for setting the time series 25 | 26 | real_data = np.load(f"{param['hmc']['OUTPUT_DIR']}/real_syn.npy") 27 | plotFigure.update_real_data(real_data) 28 | 29 | 30 | thk_real = np.array(param['true_model']['thk']) 31 | vs_real = np.array(param['true_model']['vs']) 32 | #thk_real = np.array([5.,10.,16.,0.0]) 33 | #vs_real = np.array([3.1,3.64,3.87,4.5]) 34 | plotFigure.update_refmodel(np.hstack((vs_real,thk_real))) 35 | 36 | 37 | 38 | 39 | # set the search range of inversion 40 | boundaries = np.ones((len(thk_real)*2,2)) 41 | for i in range(len(thk_real)): 42 | boundaries[i,0] = vs_real[i] - vs_real[i]*0.8 43 | boundaries[i,1] = vs_real[i] + vs_real[i]*0.8 44 | if boundaries[i,0] < 1.5: 45 | boundaries[i,0] = 1.5 46 | if boundaries[i,1] > 5: 47 | boundaries[i,1] = 5 48 | 49 | boundaries[i + len(thk_real),0] = thk_real[i] - thk_real[i]*0.2 50 | boundaries[i + len(thk_real),1] = thk_real[i] + thk_real[i]*0.2 51 | boundaries[-1,:] = 0.0,2.0 52 | 53 | 54 | #plotFigure.update_initmodel(np.hstack((vs_init,thk_init))) 55 | 56 | 57 | figure = plotFigure.plot_bestmodels(np.array([0,80,200])) 58 | # np.array([0,65,200]) means the depth interpolation 59 | # from 0 to 65km, 200 points 60 | 61 | plotFigure.savefig(figure,"best_model.png") 62 | 63 | figure = plotFigure.plot_best_fit() 64 | plotFigure.savefig(figure,"best_syn_fit.png") 65 | 66 | figure = plotFigure.plot_bestmodels_hit(np.array([2,5,100]),np.array([0,80,100])) 67 | # np.array([1.8,4,100]) velocity 1.8km/s ~ 4km/s 100 points 68 | plotFigure.savefig(figure,"best_model_hist.png") 69 | 70 | figure = plotFigure.plot_chain_misfit() 71 | plotFigure.savefig(figure,"misfit.png") 72 | 73 | 74 | 75 | figure = plotFigure.plot_best_fit_hist(np.array([-0.2,0.45,100]),np.array([2.5,4,100]),np.array([2,4,100])) 76 | # np.array([-0.1,0.2,100]) amplitude of rf 77 | # np.array([2,3,100]) velocity of Rc 78 | # np.array([1.6,2.4,100]) rg 79 | plotFigure.savefig(figure,"plot_best_fit_hist.png") 80 | 81 | figure = plotFigure.plot_var_statisc(boundaries, thk_real, vs_real) 82 | plotFigure.savefig(figure,"static.png") 83 | 84 | -------------------------------------------------------------------------------- /model/model_rf_swd_vs_thk.py: -------------------------------------------------------------------------------- 1 | from model.model_rf import ReceiverFunc 2 | from model.model_surf import SurfWD 3 | import numpy as np 4 | 5 | class Joint_RF_SWD: 6 | def __init__(self,sigma1,sigma2,rfmodel:ReceiverFunc,swdmodel:SurfWD) -> None: 7 | self.sigma1 = sigma1 8 | self.sigma2 = sigma2 9 | self.rfmodel = rfmodel 10 | self.swdmodel = swdmodel 11 | 12 | self.ndata = rfmodel.nt + swdmodel.nt 13 | 14 | def set_obsdata(self,rfobs:np.ndarray, swdobs:np.ndarray): 15 | self.dobs = np.zeros((self.ndata)) 16 | self.rfobs = rfobs * 1. 17 | self.swdobs = swdobs * 1. 18 | 19 | 20 | 21 | self.rfmodel.set_obsdata(rfobs) 22 | self.swdmodel.set_obsdata(swdobs) 23 | 24 | self.dobs[:self.rfmodel.nt] = self.rfobs * 1. 25 | self.dobs[self.rfmodel.nt:] = self.swdobs * 1. 26 | 27 | def forward(self,x:np.ndarray): 28 | """ 29 | compute surface wave dispersion for a given model x 30 | Parameters: 31 | ================================================== 32 | x : np.ndarray, shape(n,4) 33 | input model, [thk,vp,vs,rho] 34 | 35 | Returns: 36 | ================================================== 37 | d : np.ndarray,shape(self.nt) 38 | output dispersion dat 39 | """ 40 | n1 = self.rfmodel.nt 41 | n2 = self.swdmodel.nt 42 | drf = np.zeros((n1)) 43 | dswd = np.zeros((n2)) 44 | 45 | drf = self.rfmodel.forward(x) 46 | #print(drf) 47 | dswd,flag = self.swdmodel.forward(x) 48 | 49 | return drf,dswd,flag 50 | 51 | def misfit(self,x:np.ndarray): 52 | drf,dswd,flag = self.forward(x) 53 | if flag: 54 | n1 = drf.size 55 | n2 = dswd.size 56 | wt = (self.sigma1 / self.sigma2)**2 * n1 / n2 57 | 58 | 59 | misfit = 0.5 * np.sum((drf - self.rfobs)**2) + \ 60 | 0.5 * np.sum((dswd - self.swdobs)**2) * wt 61 | 62 | return misfit,True 63 | else: 64 | return 0.0,flag 65 | 66 | def misfit_and_grad(self,x:np.ndarray): 67 | # compute misfit and grad for each dataset 68 | 69 | misfitr,gradr,dr = self.rfmodel.misfit_and_grad(x) 70 | misfits,grads,ds,flag = self.swdmodel.misfit_and_grad(x) 71 | 72 | # check flag 73 | if flag == False: 74 | return 0.0,np.zeros((gradr.shape)),self.dobs,flag 75 | 76 | # compute weighted misfit 77 | n1 = dr.size 78 | n2 = ds.size 79 | wt = (self.sigma1 / self.sigma2)**2 * n1 / n2 80 | misft = misfitr + wt * misfits 81 | grad = gradr + wt * grads 82 | dobs = np.zeros((n1 + n2)) 83 | dobs[:n1] = dr 84 | dobs[n1:] = ds 85 | 86 | return misft,grad,dobs,True 87 | -------------------------------------------------------------------------------- /main_DA.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyhmc.hmcda import HMCDualAveraging 3 | from mpi4py import MPI 4 | from model.model_rf import ReceiverFunc 5 | from model.model_surf import SurfWD 6 | from model.model_rf_swd_vs_thk import Joint_RF_SWD 7 | import time 8 | import yaml 9 | import os 10 | 11 | 12 | def main(): 13 | # mpi information 14 | comm = MPI.COMM_WORLD 15 | rank = comm.Get_rank() 16 | ncores = comm.Get_size() 17 | 18 | # load yaml files 19 | with open("param.yaml","r") as f: 20 | param = yaml.safe_load(f) 21 | 22 | # surf parameters 23 | sparam = param['swd'] 24 | model_swd = SurfWD.init(**sparam) 25 | 26 | # receiver function parameters 27 | rparam = param['rf'] 28 | model_rf = ReceiverFunc.init(**rparam) 29 | 30 | # set model parameters 31 | thk = np.asarray(param['true_model']['thk']) 32 | vs = np.asarray(param['true_model']['vs']) 33 | model_swd.set_thk(thk) 34 | model_rf.set_thk(thk) 35 | 36 | # joint inversion model 37 | sigma1 = 1.0; sigma2 = 1.0 38 | model = Joint_RF_SWD(sigma1,sigma2,model_rf,model_swd) 39 | 40 | # get output dir 41 | outdir = param['hmc']['OUTPUT_DIR'] 42 | os.makedirs(outdir,exist_ok=True) 43 | 44 | # obs data 45 | dobs = None 46 | x = None 47 | if rank == 0: 48 | # true model 49 | x = np.hstack((vs,thk)) 50 | drsyn,dssyn,_ = model.forward(x) 51 | dobs = np.zeros((model.ndata)) 52 | dobs[:model.rfmodel.nt] = drsyn 53 | dobs[model.rfmodel.nt:] = dssyn 54 | np.save(f"{outdir}/real_syn.npy",dobs) 55 | 56 | # bcast to other procs 57 | dobs = comm.bcast(dobs) 58 | x = comm.bcast(x) 59 | nt = model.rfmodel.nt 60 | model.set_obsdata(dobs[:nt],dobs[nt:]) 61 | 62 | # set the search range of inversion 63 | n = len(x) 64 | boundaries = np.ones((n,2)) 65 | for i in range(len(thk)): 66 | boundaries[i,0] = vs[i] - vs[i]*0.8 67 | boundaries[i,1] = vs[i] + vs[i]*0.8 68 | if boundaries[i,0] < 1.5: 69 | boundaries[i,0] = 1.5 70 | if boundaries[i,1] > 5: 71 | boundaries[i,1] = 5 72 | 73 | boundaries[i + len(thk),0] = thk[i] - thk[i]*0.2 74 | boundaries[i + len(thk),1] = thk[i] + thk[i]*0.2 75 | boundaries[-1,:] = 0.0,2.0 76 | 77 | # initialize HMC sampler 78 | hparam = param['hmc'] 79 | chain = HMCDualAveraging.init(model,boundaries,rank,**hparam) 80 | nsamples = chain.nsamples 81 | tmp = chain.sample() 82 | 83 | # gather misfits 84 | if rank == 0: 85 | misfit = np.zeros((ncores,nsamples)) 86 | else: 87 | misfit = None 88 | comm.Gather(tmp,misfit,root=0) 89 | 90 | if rank == 0: 91 | np.save(f"{outdir}/misfit.npy",misfit) 92 | 93 | if __name__ == "__main__": 94 | tic = time.time() 95 | main() 96 | toc = time.time() 97 | print("time elapse: {}".format(toc-tic)) 98 | -------------------------------------------------------------------------------- /main_base.py: -------------------------------------------------------------------------------- 1 | from matplotlib.colors import BoundaryNorm 2 | import numpy as np 3 | from pyhmc.hmc import HamitonianMC 4 | from mpi4py import MPI 5 | from model.model_rf import ReceiverFunc 6 | from model.model_surf import SurfWD 7 | from model.model_rf_swd_vs_thk import Joint_RF_SWD 8 | import matplotlib.pyplot as plt 9 | import time 10 | import yaml 11 | import os 12 | 13 | 14 | def main(): 15 | # mpi information 16 | comm = MPI.COMM_WORLD 17 | rank = comm.Get_rank() 18 | ncores = comm.Get_size() 19 | 20 | # load yaml files 21 | with open("param.yaml","r") as f: 22 | param = yaml.safe_load(f) 23 | 24 | # surf parameters 25 | sparam = param['swd'] 26 | model_swd = SurfWD.init(**sparam) 27 | 28 | # receiver function parameters 29 | rparam = param['rf'] 30 | model_rf = ReceiverFunc.init(**rparam) 31 | 32 | # set model parameters 33 | thk = np.asarray(param['true_model']['thk']) 34 | vs = np.asarray(param['true_model']['vs']) 35 | model_swd.set_thk(thk) 36 | model_rf.set_thk(thk) 37 | 38 | # joint inversion model 39 | sigma1 = 1.0; sigma2 = 1.0 40 | model = Joint_RF_SWD(sigma1,sigma2,model_rf,model_swd) 41 | 42 | # get output dir 43 | outdir = param['hmc']['OUTPUT_DIR'] 44 | os.makedirs(outdir,exist_ok=True) 45 | 46 | # obs data 47 | dobs = None 48 | x = None 49 | if rank == 0: 50 | # true model 51 | x = np.hstack((vs,thk)) 52 | drsyn,dssyn,_ = model.forward(x) 53 | dobs = np.zeros((model.ndata)) 54 | dobs[:model.rfmodel.nt] = drsyn 55 | dobs[model.rfmodel.nt:] = dssyn 56 | np.save(f"{outdir}/real_syn.npy",dobs) 57 | 58 | # bcast to other procs 59 | dobs = comm.bcast(dobs) 60 | x = comm.bcast(x) 61 | nt = model.rfmodel.nt 62 | model.set_obsdata(dobs[:nt],dobs[nt:]) 63 | 64 | # set the search range of inversion 65 | n = len(x) 66 | boundaries = np.ones((n,2)) 67 | for i in range(len(thk)): 68 | boundaries[i,0] = vs[i] - vs[i]*0.8 69 | boundaries[i,1] = vs[i] + vs[i]*0.8 70 | if boundaries[i,0] < 1.5: 71 | boundaries[i,0] = 1.5 72 | if boundaries[i,1] > 5: 73 | boundaries[i,1] = 5 74 | 75 | boundaries[i + len(thk),0] = thk[i] - thk[i]*0.2 76 | boundaries[i + len(thk),1] = thk[i] + thk[i]*0.2 77 | boundaries[-1,:] = 0.0,2.0 78 | 79 | # initialize HMC sampler 80 | hparam = param['hmc'] 81 | chain = HamitonianMC.init(model,boundaries,rank,**hparam) 82 | nsamples = chain.nsamples 83 | tmp = chain.sample() 84 | 85 | # gather misfits 86 | if rank == 0: 87 | misfit = np.zeros((ncores,nsamples)) 88 | else: 89 | misfit = None 90 | comm.Gather(tmp,misfit,root=0) 91 | 92 | if rank == 0: 93 | np.save(f"{outdir}/misfit.npy",misfit) 94 | 95 | if __name__ == "__main__": 96 | tic = time.time() 97 | main() 98 | toc = time.time() 99 | print("time elapse: {}".format(toc-tic)) 100 | -------------------------------------------------------------------------------- /src/SWD/main.cpp: -------------------------------------------------------------------------------- 1 | #include"surfdisp.hpp" 2 | #include 3 | #include 4 | namespace py = pybind11; 5 | using py::arg; 6 | 7 | const auto FCST = py::array::c_style | py::array::forcecast ; 8 | 9 | typedef py::array_t ftensor; 10 | typedef py::array_t dtensor; 11 | typedef std::tuple tupledt6; 12 | typedef std::tuple tuple2; 13 | 14 | tuple2 forward(ftensor &thk,ftensor &vp,ftensor &vs,ftensor &rho, 15 | dtensor &t,std::string wavetype, 16 | int mode=0,bool sphere=false) 17 | { 18 | // check input parameters 19 | bool flag = wavetype == "Rc" || wavetype == "Rg" || 20 | wavetype == "Lc" || wavetype == "Lg"; 21 | if(flag == false){ 22 | std::cout << "cnew = _flat2sphere(double t,double c,std::string wavetp)\n"; 23 | std::cout << "parameter wavetp should be in one of [Rc,Rg,Lc,Lg]\n "; 24 | exit(0); 25 | } 26 | 27 | // allocate space 28 | int nt = t.size(), n = thk.size(); 29 | dtensor cg(nt); 30 | bool keep_flat = false; 31 | 32 | // forward computation 33 | int ierr; 34 | if(wavetype == "Rc"){ 35 | ierr = _surfdisp(thk.mutable_data(),vp.mutable_data(),vs.mutable_data(), 36 | rho.mutable_data(),n,t.mutable_data(),cg.mutable_data(), 37 | nt,wavetype,mode,sphere,keep_flat); 38 | } 39 | else if (wavetype == "Rg"){ 40 | ierr = _RayleighGroup(thk.mutable_data(),vp.mutable_data(),vs.mutable_data(), 41 | rho.mutable_data(),n,t.mutable_data(),cg.mutable_data(),nt, 42 | mode,sphere); 43 | } 44 | else if (wavetype=="Lc"){ 45 | ierr = _surfdisp(thk.mutable_data(),vp.mutable_data(),vs.mutable_data(), 46 | rho.mutable_data(),n,t.mutable_data(),cg.mutable_data(), 47 | nt,wavetype,mode,sphere,keep_flat); 48 | } 49 | else{ 50 | ierr = _LoveGroup(thk.mutable_data(),vs.mutable_data(),rho.mutable_data(), 51 | n,t.mutable_data(),cg.mutable_data(),nt,mode,sphere); 52 | } 53 | 54 | bool return_flag = true; 55 | if(ierr == 1) return_flag = false; 56 | auto tt = std::make_tuple(cg,return_flag); 57 | 58 | return tt; 59 | } 60 | 61 | tupledt6 62 | adjoint_kernel(ftensor &thk,ftensor &vp,ftensor &vs,ftensor &rho, 63 | dtensor &t,std::string wavetype,int mode=0, 64 | bool sphere=false) 65 | { 66 | // allocate space 67 | int nt = t.size(),n = thk.size(); 68 | dtensor dcda(nt*n),dcdb(nt*n),dcdr(nt*n),dcdh(nt*n),c(nt); 69 | dcda.resize({nt,n}), dcdb.resize({nt,n}); 70 | dcdh.resize({nt,n}),dcdr.resize({nt,n}); 71 | 72 | int ierr = _SurfKernel(thk.mutable_data(),vp.mutable_data(),vs.mutable_data(), 73 | rho.mutable_data(),n,t.mutable_data(),c.mutable_data(), 74 | nt,dcda.mutable_data(),dcdb.mutable_data(),dcdr.mutable_data(), 75 | dcdh.mutable_data(),wavetype,mode,sphere); 76 | 77 | bool return_flag = true; 78 | if(ierr == 1) return_flag = false; 79 | auto tt = std::make_tuple(c,dcda,dcdb,dcdr,dcdh,return_flag); 80 | 81 | return tt; 82 | } 83 | 84 | PYBIND11_MODULE(libsurf,m){ 85 | m.doc() = "Surface wave dispersion and sensivity kernel\n"; 86 | m.def("forward",&forward,arg("thk"),arg("vp"),arg("vs"), 87 | arg("rho"),arg("period"),arg("wavetype"), 88 | arg("mode")=0,arg("sphere")=false, 89 | "Surface wave dispersion c++ wrapper"); 90 | m.def("adjoint_kernel",&adjoint_kernel,arg("thk"),arg("vp"),arg("vs"), 91 | arg("rho"),arg("period"),arg("wavetype"), 92 | arg("mode")=0,arg("sphere")=false, 93 | "Surface wave dispersion sensitivity kernel c++ wrapper"); 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/RF/deconit.f90: -------------------------------------------------------------------------------- 1 | subroutine nextpow2(n,nout) 2 | implicit none 3 | 4 | integer,intent(in) :: n 5 | integer,intent(out) :: nout 6 | 7 | nout = 1 8 | do while (nout < n) 9 | nout = nout * 2 10 | enddo 11 | 12 | return; 13 | end subroutine nextpow2 14 | 15 | subroutine gauss_filter(nt,dt,f0,gauss) 16 | implicit none 17 | integer,intent(in) :: nt 18 | real(8),intent(in) :: dt,f0 19 | real(8),intent(inout) :: gauss(nt/2+1) 20 | 21 | ! local 22 | integer :: i 23 | real(8),parameter :: pi = atan(1.0) * 4. 24 | real(8)::freq 25 | 26 | do i=1,nt/2+1 27 | freq = (i-1) /(nt * dt) 28 | gauss(i) = exp(-0.25 * (2 * pi * freq / f0)**2) 29 | enddo 30 | 31 | return; 32 | end subroutine gauss_filter 33 | 34 | subroutine apply_gaussian(mydata,dt,f0,nt) 35 | 36 | implicit none 37 | integer,intent(in) :: nt 38 | real(8),intent(in) :: dt,f0 39 | real(8),intent(inout):: mydata(nt) 40 | 41 | !local 42 | real(8) :: gauss(nt/2+1) 43 | complex(kind=8) :: data_freq(nt/2+1) 44 | 45 | call gauss_filter(nt,dt,f0,gauss) 46 | call rfft(mydata,data_freq,nt) 47 | data_freq(:) = data_freq(:) * gauss 48 | call irfft(data_freq,mydata,nt) 49 | 50 | return; 51 | 52 | end subroutine apply_gaussian 53 | 54 | subroutine shift_data(mydata,dt,tshift,n) 55 | 56 | implicit none 57 | integer,intent(in) :: n 58 | real(8),intent(in) :: dt,tshift 59 | real(8),intent(inout) :: mydata(n) 60 | 61 | ! local 62 | complex(kind=8) :: data_freq(n/2+1) 63 | integer :: i 64 | real(8),parameter :: pi = atan(1.0) * 4.0 65 | 66 | call rfft(mydata,data_freq,n) 67 | do i=1,n/2+1 68 | data_freq(i) = data_freq(i) * exp(-cmplx(0,1.0) * (i-1) / (n*dt) * pi * 2 * tshift) 69 | enddo 70 | call irfft(data_freq,mydata,n) 71 | 72 | end subroutine shift_data 73 | 74 | subroutine shift_data1(mydata,dt,tshift,n) 75 | 76 | implicit none 77 | integer,intent(in) :: n 78 | real(8),intent(in) :: dt,tshift 79 | real(8),intent(inout) :: mydata(n) 80 | 81 | ! local 82 | real(8) :: data_pad(n*2) 83 | complex(kind=8) :: data_freq(n+1) 84 | integer :: i,n2 85 | real(8),parameter :: pi = atan(1.0) * 4.0 86 | n2 = n * 2 87 | 88 | data_pad = 0. 89 | data_pad(1:n) = mydata(:) 90 | call rfft(data_pad,data_freq,n2) 91 | do i=1,n+1 92 | data_freq(i) = data_freq(i) * exp(-cmplx(0,1.0) * (i-1) / (n2*dt) * pi * 2 * tshift) 93 | enddo 94 | call irfft(data_freq,data_pad,n2) 95 | mydata = data_pad(1:n) 96 | 97 | end subroutine shift_data1 98 | 99 | subroutine mycorrelate(a,b,out,n) 100 | 101 | implicit none 102 | 103 | integer,intent(in) :: n 104 | real(8),intent(in) :: a(n),b(n) 105 | real(8),intent(inout) :: out(n) 106 | 107 | !local 108 | complex(kind=8) :: aft(n/2+1),bft(n/2+1),c(n/2+1) 109 | call rfft(a,aft,n) 110 | call rfft(b,bft,n) 111 | c = aft * conjg(bft) 112 | 113 | call irfft(c,out,n) 114 | 115 | end subroutine mycorrelate 116 | 117 | subroutine myconvolve(a,b,out,n) 118 | 119 | implicit none 120 | 121 | integer,intent(in) :: n 122 | real(8),intent(in) :: a(n),b(n) 123 | real(8),intent(inout) :: out(n) 124 | 125 | !local 126 | complex(kind=8) :: aft(n/2+1),bft(n/2+1),c(n/2+1) 127 | call rfft(a,aft,n) 128 | call rfft(b,bft,n) 129 | c = aft * bft 130 | 131 | call irfft(c,out,n) 132 | 133 | end subroutine myconvolve 134 | 135 | subroutine deconit(u, w, nt,dt, tshift, f0,out) 136 | implicit none 137 | integer,intent(in) :: nt 138 | real(8),intent(in) :: dt,tshift,f0,u(nt),w(nt) 139 | real(8),intent(inout) :: out(nt) 140 | 141 | ! local 142 | integer :: nft 143 | real(8),dimension(:),allocatable :: uflt,wflt,wcopy,p,rflt 144 | real(8),dimension(:),allocatable :: cuw,temp1,temp2 145 | real(8) :: invpw,invpu,minderr,d_error,sumsq_i,sumsq 146 | integer,parameter :: maxiter = 200 147 | integer :: it,idx(1) 148 | 149 | ! allocate space 150 | call nextpow2(nt,nft) 151 | allocate(uflt(nft),wflt(nft),wcopy(nft),p(nft),rflt(nft)) 152 | allocate(cuw(nft),temp1(nft),temp2(nft)) 153 | 154 | ! copy 155 | wflt(:) = 0.; uflt(:) = 0. 156 | wflt(1:nt) = w(:); uflt(1:nt) = u(:) 157 | wcopy = wflt 158 | 159 | ! filter input arrays 160 | call apply_gaussian(uflt,dt,f0,nft) 161 | call apply_gaussian(wflt,dt,f0,nft) 162 | 163 | ! init 164 | invpw = 1. / sum(wflt**2) / dt 165 | invpu = 1. / sum(uflt**2) / dt 166 | p(:) = 0. 167 | sumsq_i = 1.0 168 | sumsq = 50 169 | minderr = 0.001 170 | d_error = 100 * invpw + minderr 171 | rflt(:) = uflt(:) 172 | 173 | ! iterative deconvolution 174 | do it = 1,maxiter 175 | if( abs(d_error) <= minderr) exit 176 | call mycorrelate(rflt,wflt,cuw,nft) 177 | cuw(:) = cuw(:) * dt 178 | idx = maxloc(abs(cuw(1:nft/2))) 179 | P(idx(1)) = P(idx(1)) + cuw(idx(1)) * invpw / dt 180 | temp1 = P 181 | call apply_gaussian(temp1,dt,f0,nft) 182 | call myconvolve(temp1,wcopy,temp2,nft) 183 | rflt = uflt - temp2 * dt 184 | 185 | ! compute error 186 | sumsq = sum(rflt**2) * dt * invpu 187 | d_error = 100. * (sumsq_i - sumsq) 188 | sumsq_i = sumsq 189 | enddo 190 | 191 | ! get rf and time shift 192 | call apply_gaussian(p,dt,f0,nft) 193 | call shift_data(p,dt,tshift,nft) 194 | out(1:nt) = p(1:nt) 195 | 196 | deallocate(uflt,wflt,wcopy,p,rflt, cuw,temp1,temp2 ) 197 | 198 | end subroutine deconit -------------------------------------------------------------------------------- /src/SWD/surfdisp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | extern "C"{ 4 | 5 | /** 6 | * compute phase velocity sensitivity kernel, and group velocity 7 | * for Rayleigh Wave,with layer-based model. 8 | * note that if in spherical model, input cp should be cp_flat 9 | * @param nlayer no. of layers 10 | * @param thk,vp,vs,rhom model, shape(nlayer) 11 | * @param t,cp,cg period and phase/group velocity 12 | * @param iflsph =0 flat earth =1 spherical earth 13 | * @param dispu/w stressu/w eigen function for u/w direction shape(nlayer) 14 | * @param dc2da(b,h,r) sensitivity kernel for vp,vs,thick and rho shape(nlayer) 15 | * 16 | */ 17 | void sregn96_(float *thk,float *vp,float *vs,float *rhom, 18 | int nlayer,double *t,double *cp,double *cg,double *dispu, 19 | double *dispw,double *stressu,double *stressw, 20 | double *dc2da,double *dc2db,double *dc2dh, 21 | double *dc2dr,int iflsph); 22 | 23 | /** 24 | * compute phase velocity sensitivity kernel, and group velocity 25 | * for Love Wave,with layer-based model 26 | * note that if in spherical model, input cp should be cp_flat 27 | * @param nlayer no. of layers 28 | * @param thk,vs,rhom model, shape(nlayer) 29 | * @param t,cp,cg period and phase/group velocity 30 | * @param iflsph =0 flat earth =1 spherical earth 31 | * @param disp,stress eigen function for u/w direction shape(nlayer) 32 | * @param dc2db(h,r) phase v sensitivity kernel for vs,thick and rho shape(nlayer) 33 | */ 34 | void slegn96_(float *thk,float *vs,float *rhom,int nlayer, 35 | double *t,double *cp,double *cg,double *disp, 36 | double *stress,double *dc2db,double *dc2dh, 37 | double *dc2dr,int iflsph); 38 | 39 | /** 40 | * calculates the dispersion values for any layered model, any frequency, and any mode. 41 | * @param nlayer no. of layers 42 | * @param thkm,vpm,vsm,rhom model, shape(nlayer) 43 | * @param kmax no. of periods used 44 | * @param t,cp period and phase velocity, shape(kmax) 45 | * @param iflsph 0 for flat earth and 1 for spherical earth 46 | * @param iwave 1 for Love and 2 for Rayleigh 47 | * @param mode i-th mode of surface wave, 1 fundamental, 2 first higher, .... 48 | * @param igr 0 phase velocity, > 0 group velocity 49 | */ 50 | void surfdisp96_(float *thkm,float *vpm,float *vsm,float *rhom, 51 | int nlayer,int iflsph,int iwave,int mode, 52 | int igr,int kmax,double *t,double *cg,int *ierr); 53 | 54 | /** 55 | * compute phase/group velocity sensitivity kernel, and group velocity 56 | * for Love Wave,with layer-based model 57 | * note that if in spherical model, input cp should be cp_flat 58 | * @param nlayer no. of layers 59 | * @param thk,vs,rhom model, shape(nlayer) 60 | * @param t,cp,cg period and phase/group velocity 61 | * @param t1,t2,cp1,cp2 slightly different period and phasev from t 62 | * @param iflsph =0 flat earth =1 spherical earth 63 | * @param disp,stress eigen function for u/w direction shape(nlayer) 64 | * @param dc2db(h,r) phase velocity sensitivity kernel for vs,thick and rho shape(nlayer) 65 | * @param du2db(h,r) group velocity sensitivity kernel for vs,thick and rho shape(nlayer) 66 | */ 67 | void slegnpu_(float *thk,float *vs,float *rhom,int nlayer, 68 | double *t,double *cp,double *cg,double *disp, 69 | double *stress,double *t1,double *cp1,double *t2,double *cp2, 70 | double *dc2db,double *dc2dh,double *dc2dr, 71 | double *du2db,double *du2dh,double *du2dr, 72 | int iflsph); 73 | 74 | /** 75 | * compute phase velocity sensitivity kernel, and group velocity 76 | * for Rayleigh Wave,with layer-based model. 77 | * note that if in spherical model, input cp should be cp_flat 78 | * @param nlayer no. of layers 79 | * @param thk,vp,vs,rhom model, shape(nlayer) 80 | * @param t,cp,cg period and phase/group velocity 81 | * @param t1,t2,cp1,cp2 slightly different period and phasev from t 82 | * @param iflsph =0 flat earth =1 spherical earth 83 | * @param dispu/w stressu/w eigen function for u/w direction shape(nlayer) 84 | * @param dc2da(b,h,r) phase velocity sensitivity kernel for vp,vs,thick and rho shape(nlayer) 85 | * @param du2da(b,h,r) phase velocity sensitivity kernel for vp,vs,thick and rho shape(nlayer) 86 | */ 87 | void sregnpu_(float *thk,float *vp,float *vs,float *rhom, 88 | int nlayer,double *t,double *cp,double *cg,double *dispu, 89 | double *dispw,double *stressu,double *stressw, 90 | double *t1,double *cp1,double *t2,double *cp2, 91 | double *dc2da,double *dc2db,double *dc2dh,double *dc2dr, 92 | double *du2da,double *du2db,double *du2dh,double *du2dr, 93 | int iflsph); 94 | } 95 | 96 | // surface wave dispersion c++ wrapper 97 | int _surfdisp(float *thk,float *vp,float *vs,float *rho, 98 | int nlayer,double *t,double *cg,int kmax,std::string wavetype, 99 | int mode=0,bool sphere=false,bool keep_flat=true); 100 | 101 | 102 | // Love group velocity with analytical method 103 | int _LoveGroup(float *thk,float *vs,float *rho, 104 | int nlayer,double *t,double *cg,int kmax, 105 | int mode=0,bool sphere=false); 106 | 107 | // Rayleigh group velocity with analytical method 108 | int _RayleighGroup(float *thk,float *vp,float *vs,float *rho, 109 | int nlayer,double *t,double *cg,int kmax, 110 | int mode=0,bool sphere=false); 111 | 112 | int _SurfKernel(float *thk,float *vp,float *vs,float *rho, 113 | int nlayer,double *t,double *c,int nt,double *dcda, 114 | double *dcdb,double *dcdr,double *dcdh, 115 | std::string wavetp,int mode,bool sphere); -------------------------------------------------------------------------------- /model/model_rf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .lib import librf 3 | 4 | class ReceiverFunc: 5 | def __init__(self, ray_p, nt, dt, gauss, 6 | time_shift, water_level = 0.001, type_ = "p", method = "time" ): 7 | """ 8 | Initialize Rceiver Function 9 | """ 10 | self.ray_p = ray_p 11 | self.nt = nt 12 | self.dt = dt 13 | self.gauss = gauss 14 | self.time_shift = time_shift 15 | self.water_level = water_level 16 | self.rf_type = type_ 17 | self.t = np.arange(nt) * dt - time_shift 18 | self.method = method 19 | 20 | @classmethod 21 | def init(self,**kargs): 22 | return ReceiverFunc( 23 | kargs['ray_p'], 24 | kargs['nt'], 25 | kargs['dt'], 26 | kargs['gauss'], 27 | kargs['time_shift'], 28 | kargs['water_level'], 29 | kargs['type'], 30 | kargs['method']) 31 | 32 | def set_obsdata(self, dobs): 33 | """ 34 | set observation data, dobs 35 | 36 | Parameters 37 | ---------- 38 | dobs: np.ndarray 39 | obs data 40 | """ 41 | self.dobs = dobs 42 | 43 | """ 44 | if np.sum(self.t - t) > 0.1 * self.dt: 45 | print("warning! the time sequence is not match!") 46 | """ 47 | 48 | def set_thk(self,thk): 49 | self.thk = thk * 1.0 50 | 51 | 52 | def empirical_relation(self,vs,deriv=False): 53 | """ 54 | compute vp/rho and derivative from empirical relations 55 | 56 | Parameters 57 | ---------- 58 | vs : np.ndarray 59 | vs model 60 | 61 | Returns 62 | ---------- 63 | vp : np.ndarray 64 | vp 65 | rho : np.ndarray 66 | rho 67 | """ 68 | vp = 0.9409 + 2.0947*vs - 0.8206*vs**2 + \ 69 | 0.2683*vs**3 - 0.0251*vs**4 70 | rho = 1.6612*vp- 0.4721*vp**2 + \ 71 | 0.0671*vp**3 - 0.0043*vp**4 + 0.000106*vp**5 72 | if deriv: 73 | drda = 1.6612 - 0.4721*2*vp + 0.0671*3*vp**2 - 0.0043*4*vp**3 + 0.000106*5*vp**4 74 | dadb = 2.0947 - 0.8206*2*vs + 0.2683*3 * vs**2 - 0.0251*4*vs**3 75 | return vp,rho,drda,dadb 76 | else: 77 | return vp, rho 78 | 79 | def forward(self,x:np.ndarray): 80 | """ 81 | compute rf for a given model x 82 | 83 | Parameters 84 | ---------- 85 | 86 | x : np.ndarray, shape(n*2,1) 87 | n means layers 88 | input model, [vs thk] 89 | 90 | Returns 91 | ---------- 92 | d : np.ndarray,shape(self.nt) 93 | output dispersion data 94 | """ 95 | # allocate spac 96 | 97 | # prepare model 98 | layers = int(len(x) / 2) 99 | vs = x[:layers] 100 | thk = x[layers:] 101 | vp,rho = self.empirical_relation(vs,deriv=False) 102 | 103 | # dummy Qa Qb 104 | # we don't take the qb/qb into inversion process 105 | qa = thk * 0 + 9999. 106 | qb = thk * 0 + 9999. 107 | 108 | # compute rf 109 | rf = librf.forward(thk,rho,vp,vs,qa,qb,self.ray_p,self.nt, 110 | self.dt,self.gauss,self.time_shift, 111 | self.method,self.water_level, 112 | self.rf_type 113 | ) 114 | 115 | 116 | return rf 117 | 118 | def misfit(self,x): 119 | """ 120 | compute misfit function 121 | 122 | Parameters 123 | ---------- 124 | 125 | x : np.ndarray, shape(n*2,1) 126 | n means layers 127 | input model, [vs thk] 128 | 129 | Returns 130 | ---------- 131 | out : float 132 | misfit 133 | """ 134 | d = self.forward(x) 135 | return 0.5 * np.sum((d - self.dobs)**2) 136 | 137 | def misfit_and_grad(self,x): 138 | """ 139 | compute gradient for current model 140 | """ 141 | n = int(x.shape[0] / 2) 142 | nt = self.nt 143 | kernel = np.zeros((nt,n)) 144 | kernel_thk = np.zeros((nt,n)) 145 | d = np.zeros((nt)) 146 | grad = np.zeros((n)) 147 | 148 | 149 | 150 | # prepare model 151 | thk = x[int(len(x)/2):] 152 | vs = x[:int(len(x)/2)] 153 | vp,rho,drda,dadb = self.empirical_relation(vs,True) 154 | drda = drda.reshape(len(vs),1) 155 | dadb = dadb.reshape(len(vs),1) 156 | 157 | qa = thk * 0 + 9999. 158 | qb = thk * 0 + 9999. 159 | # we don't take the qb/qb into inversion process 160 | 161 | # compute gradient 162 | d,kl = librf.kernel_all(thk,rho,vp,vs,qa,qb,self.ray_p, \ 163 | self.nt,self.dt,self.gauss, \ 164 | self.time_shift,self.method, 165 | self.water_level,self.rf_type) 166 | # d,kvs = librf.kernel(thk,rho,vp,vs,qa,qb,self.ray_p, \ 167 | # self.nt,self.dt,self.gauss, \ 168 | # self.time_shift,self.method, 169 | # self.water_level,self.rf_type,'vs') 170 | # _,krho = librf.kernel(thk,rho,vp,vs,qa,qb,self.ray_p, \ 171 | # self.nt,self.dt,self.gauss, \ 172 | # self.time_shift,self.method, 173 | # self.water_level,self.rf_type,'rho') 174 | # _,kvp = librf.kernel(thk,rho,vp,vs,qa,qb,self.ray_p, \ 175 | # self.nt,self.dt,self.gauss, \ 176 | # self.time_shift,self.method, 177 | # self.water_level,self.rf_type,'vp') 178 | # _,kthk = librf.kernel(thk,rho,vp,vs,qa,qb,self.ray_p, \ 179 | # self.nt,self.dt,self.gauss, \ 180 | # self.time_shift,self.method, 181 | # self.water_level,self.rf_type,'h') 182 | krho = kl[0,...] 183 | kvp = kl[1,...] 184 | kvs = kl[2,...] 185 | kthk = kl[3,...] 186 | # compute gradient 187 | 188 | 189 | kernel = kvs + dadb * kvp + drda * dadb * krho 190 | kernel_thk = kthk 191 | 192 | #grad = kernel @ (d - self.dobs) 193 | grad = np.hstack(( kernel @ (d - self.dobs) , kernel_thk @ (d - self.dobs) )) 194 | 195 | 196 | misfit = 0.5 * np.sum((d - self.dobs)**2) 197 | 198 | return misfit,grad,d -------------------------------------------------------------------------------- /src/RF/main.cpp: -------------------------------------------------------------------------------- 1 | #include"rf_cal.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | namespace py = pybind11; 9 | using py::arg; 10 | 11 | const auto FCST = py::array::c_style | py::array::forcecast ; 12 | 13 | typedef py::array_t dmat; 14 | typedef py::array_t fmat; 15 | 16 | 17 | dmat forward(dmat &thk, dmat &rho, dmat &vp, 18 | dmat &vs,dmat &qa,dmat &qb, 19 | double ray_p, int nt, double dt, double gauss, 20 | double time_shift, const std::string &method = "time", 21 | double water = 0.001, 22 | const std::string &rf_type = "P") 23 | { 24 | int nlayer = thk.size(); 25 | int int_rf_type = 0; 26 | dmat rcv_fun(nt); 27 | 28 | if( (rf_type == "P") || (rf_type == "p") ) 29 | { 30 | int_rf_type = 1; 31 | } 32 | else if ( (rf_type == "S") || (rf_type == "s") ) 33 | { 34 | int_rf_type = 2; 35 | time_shift = - time_shift; 36 | } 37 | else 38 | { 39 | std::cout << "rf_type should be one of [P,p,S,s]" << std::endl; 40 | exit(-1); 41 | } 42 | 43 | // compute receiver functions 44 | if(method == "time") { 45 | cal_rf_time_( 46 | thk.data(),vp.data(),vs.data(), 47 | rho.data(),qa.data(),qb.data(), 48 | nlayer,nt,dt,ray_p,gauss,time_shift, 49 | int_rf_type,rcv_fun.mutable_data() 50 | ); 51 | } 52 | else { 53 | cal_rf_freq_( 54 | thk.data(),vp.data(),vs.data(), 55 | rho.data(),qa.data(),qb.data(), 56 | nlayer,nt,dt,ray_p,gauss,time_shift, 57 | water,int_rf_type,rcv_fun.mutable_data() 58 | ); 59 | } 60 | 61 | return rcv_fun; 62 | } 63 | 64 | auto kernel(dmat &thk, dmat &rho, dmat &vp, 65 | dmat &vs,dmat &qa,dmat &qb, 66 | double ray_p, int nt, double dt, double gauss, 67 | double time_shift,const std::string &method = "time", 68 | double water = 0.001,const std::string &rf_type = "P", 69 | const std::string &par_type= "vs") 70 | { 71 | int nlayer = thk.size(); 72 | int int_rf_type = 0; 73 | int int_par_type = 0; 74 | 75 | if( (rf_type == "P") || (rf_type == "p") ) 76 | { 77 | int_rf_type = 1; 78 | } 79 | else if ( (rf_type == "S") || (rf_type == "s") ) 80 | { 81 | int_rf_type = 2; 82 | time_shift = -time_shift; 83 | } 84 | else 85 | { 86 | std::cout << "rf_type should be one of [P,p,S,s]" << std::endl; 87 | exit(-1); 88 | } 89 | 90 | 91 | if( (par_type == "alpha") || (par_type == "vp") ) 92 | { 93 | int_par_type = 2; 94 | } 95 | else if( (par_type == "beta") || (par_type == "vs") ) 96 | { 97 | int_par_type = 3; 98 | } 99 | else if(par_type == "rho") 100 | { 101 | int_par_type = 1; 102 | } 103 | else if( (par_type == "thick") || (par_type == "h") ) 104 | { 105 | int_par_type = 4; 106 | } 107 | else 108 | { 109 | std::cout << "par_type should be one of [vp,vs,rho,thick]" << std::endl; 110 | exit(-1); 111 | } 112 | 113 | dmat rcv_fun(nt),rcv_fun_p({nlayer,nt}); 114 | 115 | if(method == "time") { 116 | cal_rf_par_time_( 117 | thk.data(),vp.data(),vs.data(), 118 | rho.data(),qa.data(),qb.data(), 119 | nlayer,nt,dt,ray_p,gauss,time_shift, 120 | int_rf_type,int_par_type,rcv_fun.mutable_data(), 121 | rcv_fun_p.mutable_data() 122 | ); 123 | } 124 | else { 125 | cal_rf_par_freq_( 126 | thk.data(),vp.data(),vs.data(), 127 | rho.data(),qa.data(),qb.data(), 128 | nlayer,nt,dt,ray_p,gauss,time_shift,water, 129 | int_rf_type,int_par_type,rcv_fun.mutable_data(), 130 | rcv_fun_p.mutable_data() 131 | ); 132 | } 133 | 134 | return std::make_tuple(rcv_fun,rcv_fun_p); 135 | 136 | } 137 | 138 | 139 | 140 | auto kernel_all(dmat &thk, dmat &rho, dmat &vp, 141 | dmat &vs,dmat &qa,dmat &qb, 142 | double ray_p, int nt, double dt, double gauss, 143 | double time_shift,const std::string &method = "time", 144 | double water = 0.001,const std::string &rf_type = "P" 145 | ) 146 | { 147 | int nlayer = thk.size(); 148 | int int_rf_type = 0; 149 | 150 | if( (rf_type == "P") || (rf_type == "p") ) 151 | { 152 | int_rf_type = 1; 153 | } 154 | else if ( (rf_type == "S") || (rf_type == "s") ) 155 | { 156 | int_rf_type = 2; 157 | time_shift = -time_shift; 158 | } 159 | else 160 | { 161 | std::cout << "rf_type should be one of [P,p,S,s]" << std::endl; 162 | exit(-1); 163 | } 164 | 165 | 166 | dmat rcv_fun(nt),rcv_fun_p({4,nlayer,nt}); 167 | 168 | if(method == "time") { 169 | cal_rf_par_time_all_( 170 | thk.data(),vp.data(),vs.data(), 171 | rho.data(),qa.data(),qb.data(), 172 | nlayer,nt,dt,ray_p,gauss,time_shift, 173 | int_rf_type,rcv_fun.mutable_data(), 174 | rcv_fun_p.mutable_data() 175 | ); 176 | } 177 | else { 178 | cal_rf_par_freq_all_( 179 | thk.data(),vp.data(),vs.data(), 180 | rho.data(),qa.data(),qb.data(), 181 | nlayer,nt,dt,ray_p,gauss,time_shift,water, 182 | int_rf_type,rcv_fun.mutable_data(), 183 | rcv_fun_p.mutable_data() 184 | ); 185 | } 186 | 187 | return std::make_tuple(rcv_fun,rcv_fun_p); 188 | 189 | } 190 | 191 | PYBIND11_MODULE(librf,m){ 192 | m.doc() = "Receiver function and partial derivative\n"; 193 | m.def("forward",&forward,arg("thk"), arg("rho"), arg("vp"), 194 | arg("vs"),arg("qa"),arg("qb"), arg("ray_p"), arg("nt"), 195 | arg("dt") ,arg("gauss"),arg("time_shift"), 196 | arg("method") = "time", arg("water") = 0.001, 197 | arg("rf_type") = "P", 198 | "receiver function calculating c++ wrapper"); 199 | 200 | m.def("kernel",&kernel,arg("thk"), arg("rho"), arg("vp"), 201 | arg("vs"), arg("qa"),arg("qb"),arg("ray_p"), arg("nt"), 202 | arg("dt") ,arg("gauss"),arg("time_shift"), 203 | arg("method") = "time", arg("water") = 0.001, 204 | arg("rf_type") = "P", arg("par_type") = "vs", 205 | "receiver function kernel calculating c++ wrapper"); 206 | 207 | m.def("kernel_all",&kernel_all,arg("thk"), arg("rho"), arg("vp"), 208 | arg("vs"), arg("qa"),arg("qb"),arg("ray_p"), arg("nt"), 209 | arg("dt") ,arg("gauss"),arg("time_shift"), 210 | arg("method") = "time", arg("water") = 0.001, 211 | arg("rf_type") = "P" 212 | "receiver function kernel calculating c++ wrapper"); 213 | } 214 | -------------------------------------------------------------------------------- /model/model_surf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .lib import libsurf 3 | 4 | class SurfWD: 5 | def __init__(self,mode=0,sphere=False,tRc=None,tRg=None,tLc = None,tLg=None): 6 | # init swd params 7 | self.mode = mode # mode, =0 for fundamental 8 | self.sphere = sphere # False: flat model, True: spherical mod 9 | 10 | # init period vector 11 | self.tRc,self.tRg,self.tLc,self.tLg = None,None,None,None 12 | self.nt = 0 13 | self.ntRc,self.ntRg,self.ntLc,self.ntLg = 0,0,0,0 14 | if tRc is not None and len(tRc) > 0: 15 | self.tRc = np.asarray(tRc) 16 | self.ntRc = len(tRc) 17 | self.nt += self.ntRc 18 | if tRg is not None and len(tRg) > 0: 19 | self.tRg = np.asarray(tRg) 20 | self.ntRg = len(tRg) 21 | self.nt += self.ntRg 22 | if tLc is not None and len(tLc) > 0: 23 | self.tLc = np.asarray(tLc) 24 | self.ntLc = len(tLc) 25 | self.nt += self.ntLc 26 | if tLg is not None and len(tLg) > 0: 27 | self.tLg = np.asarray(tLg) 28 | self.ntLg = len(tLg) 29 | self.nt += self.ntLg 30 | 31 | @classmethod 32 | def init(self,**kargs): 33 | return SurfWD( 34 | tRc=kargs['tRc'], 35 | tRg=kargs['tRg'], 36 | tLc=kargs['tLc'], 37 | tLg=kargs['tLg'] 38 | ) 39 | 40 | def set_obsdata(self,dobs): 41 | # obs data 42 | self.dobs = dobs 43 | 44 | def set_thk(self,thk): 45 | self.thk = thk* 1.0 46 | 47 | def empirical_relation(self,vs:np.ndarray,deriv=False): 48 | """ 49 | compute vp/rho and derivative from empirical relations 50 | Parameters: 51 | =================================================== 52 | vs : np.ndarray 53 | vs model 54 | deriv : bool 55 | if True,also return derivatives 56 | Returns: 57 | ================================================== 58 | vp : np.ndarray 59 | vp 60 | rho : np.ndarray 61 | rho 62 | 63 | """ 64 | vp = 0.9409 + 2.0947*vs - 0.8206*vs**2 + \ 65 | 0.2683*vs**3 - 0.0251*vs**4 66 | rho = 1.6612*vp- 0.4721*vp**2 + \ 67 | 0.0671*vp**3 - 0.0043*vp**4 + 0.000106*vp**5 68 | dadb = np.zeros(vp.shape) 69 | drda = np.zeros((vp.shape)) 70 | 71 | if deriv: 72 | drda = 1.6612 - 0.4721*2*vp + 0.0671*3*vp**2 - 0.0043*4*vp**3 + 0.000106*5*vp**4 73 | dadb = 2.0947 - 0.8206*2*vs + 0.2683*3 * vs**2 - 0.0251*4*vs**3 74 | 75 | # return 76 | if deriv == False: 77 | return vp,rho 78 | else: 79 | return vp,rho,dadb,drda 80 | 81 | def forward(self,x:np.ndarray): 82 | """ 83 | compute surface wave dispersion for a given model x 84 | Parameters: 85 | ================================================== 86 | x : np.ndarray, shape(n*2,1) 87 | n means layers 88 | input model, [vs thk] 89 | Returns: 90 | ================================================== 91 | d : np.ndarray,shape(self.nt) 92 | output dispersion data 93 | """ 94 | # allocate space 95 | d = np.zeros((self.nt)) 96 | 97 | # prepare model 98 | layers = int(len(x) / 2) 99 | vs = x[:layers] 100 | thk = x[layers:] 101 | vp,rho = self.empirical_relation(vs) 102 | 103 | # compute dispersion 104 | if self.ntRc >0 : 105 | k1 = 0 106 | k2 = k1 + self.ntRc 107 | d[k1:k2],flag = libsurf.forward(thk,vp,vs,rho, 108 | self.tRc,"Rc",self.mode,self.sphere) 109 | if flag == False: 110 | return d,flag 111 | if self.ntRg > 0: 112 | k1 = self.ntRc 113 | k2 = k1 + self.ntRg 114 | d[k1:k2],flag = libsurf.forward(thk,vp,vs,rho, 115 | self.tRc,"Rg",self.mode,self.sphere) 116 | if flag == False: 117 | return d,flag 118 | 119 | if self.ntLc > 0: 120 | k1 = self.ntRc + self.ntRg 121 | k2 = k1 + self.ntLc 122 | d[k1:k2],flag = libsurf.forward(thk,vp,vs,rho, 123 | self.tRc,"Lc",self.mode,self.sphere) 124 | if flag == False: 125 | return d,flag 126 | if self.ntLg > 0: 127 | k1 = self.ntRc + self.ntRg + self.ntLc 128 | k2 = k1 + self.ntLg 129 | d[k1:k2],flag = libsurf.forward(thk,vp,vs,rho, 130 | self.tRc,"Lg",self.mode,self.sphere) 131 | if flag == False: 132 | return d,flag 133 | return d,True 134 | 135 | def misfit(self,x): 136 | """ 137 | compute misfit function 138 | Parameters: 139 | ================================================== 140 | x : np.ndarray, shape(n*2,1) 141 | n means layers 142 | input model, [vs thk] 143 | Returns: 144 | ================================================== 145 | out : float 146 | misfit 147 | """ 148 | d,flag = self.forward(x) 149 | if flag: 150 | return 0.5 * np.sum((d - self.dobs)**2),True 151 | else: 152 | return 0.0,flag 153 | 154 | 155 | def misfit_and_grad(self,x): 156 | """ 157 | compute misfit and gradient for current model 158 | """ 159 | # allocate space 160 | n = int(x.shape[0] / 2) 161 | nt = self.nt 162 | kernel = np.zeros((nt,n)) 163 | kernel_thk = np.zeros((nt,n)) 164 | d = np.zeros((nt)) 165 | grad = np.zeros((n)) 166 | 167 | # prepare model 168 | thk = x[int(len(x)/2):] 169 | vs = x[:int(len(x)/2)] 170 | vp,rho,dadb,drda = self.empirical_relation(vs,True) 171 | 172 | 173 | 174 | # compute sensitivity and kernel 175 | if self.ntRc > 0: 176 | k1 = 0 177 | k2 = self.ntRc 178 | cg,dcda,dcdb,dcdr,dcdh,flag = libsurf.adjoint_kernel( 179 | thk,vp,vs,rho,self.tRc,"Rc",self.mode,self.sphere 180 | ) 181 | if flag == False: 182 | return 0.0,grad,d,flag 183 | d[k1:k2] = cg 184 | kernel[k1:k2,:] = dcdb + dcda * dadb + dcdr * drda * dadb 185 | kernel_thk[k1:k2,:] = dcdh 186 | if self.ntRg >0: 187 | k1 = self.ntRc 188 | k2 = k1 + self.ntRg 189 | cg,dcda,dcdb,dcdr,dcdh,flag = libsurf.adjoint_kernel( 190 | thk,vp,vs,rho,self.tRg,"Rg",self.mode,self.sphere 191 | ) 192 | if flag == False: 193 | return 0.0,grad,d,flag 194 | d[k1:k2] = cg 195 | kernel[k1:k2,:] = dcdb +dcda * dadb + dcdr * drda * dadb 196 | kernel_thk[k1:k2,:] = dcdh 197 | if self.ntLc > 0: 198 | k1 = self.ntRc + self.ntRg 199 | k2 = k1 + self.ntLc 200 | cg,dcda,dcdb,dcdr,dcdh,flag = libsurf.adjoint_kernel( 201 | thk,vp,vs,rho,self.tRc,"Lc",self.mode,self.sphere 202 | ) 203 | if flag == False: 204 | return 0.0,grad,d,flag 205 | d[k1:k2] = cg 206 | kernel[k1:k2,:] = dcdb +dcda * dadb + dcdr * drda * dadb 207 | kernel_thk[k1:k2,:] = dcdh 208 | if self.ntLg > 0: 209 | k1 = self.ntRc + self.ntRg + self.ntLc 210 | k2 = k1 + self.ntLg 211 | cg,dcda,dcdb,dcdr,dcdh,flag= libsurf.adjoint_kernel( 212 | thk,vp,vs,rho,self.tRc,"Lg",self.mode,self.sphere 213 | ) 214 | if flag == False: 215 | return 0.0,grad,d,flag 216 | d[k1:k2] = cg 217 | kernel[k1:k2,:] = dcdb +dcda * dadb + dcdr * drda * dadb 218 | kernel_thk[k1:k2,:] = dcdh 219 | 220 | 221 | # compute gradient 222 | #grad = (d - self.dobs) @ kernel 223 | #grad = (d - self.dobs) @ kernel_thk 224 | grad = np.hstack(( (d - self.dobs) @ kernel , (d - self.dobs) @ kernel_thk )) 225 | # compute misfit 226 | out = 0.5 * np.sum((d - self.dobs)**2) 227 | 228 | return out,grad,d,True 229 | """ 230 | TEST CODE 231 | =================================== 232 | thk = np.array([2,4,6,10,12,0]) 233 | tRc = np.linspace(4,40,19) 234 | model = SurfWD(thk,tRc=tRc,mode=0) 235 | x = np.array([2.0,2.3,2.8,3.4,3.5,4.0]) 236 | dobs = model.forward(x) 237 | dobs = dobs * (1.0+ 0.01 * np.random.randn(len(dobs))) 238 | model.set_obsdata(dobs) 239 | plt.plot(tRc,dobs) 240 | plt.show() 241 | exit() 242 | """ 243 | -------------------------------------------------------------------------------- /pyhmc/hmc.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ['OPENBLAS_NUM_THREADS'] = "1" 3 | os.environ['MKL_NUM_THREADS'] = '1' 4 | import numpy as np 5 | import sys 6 | import h5py 7 | 8 | 9 | class HamitonianMC: 10 | def __init__(self,UserDefinedModel,boundaries, 11 | dt,Lrange,nbest_model,seed, 12 | nsamples,ndraws, 13 | myrank=0,name = "mychain", 14 | outdir = "./"): 15 | """ 16 | HMC parameters 17 | 18 | Parameters 19 | ---------- 20 | UserDefinedModel: class 21 | user defined model, model.misfit_and_grad is required 22 | boundaries: np.ndarray, shape(n,2) 23 | search boundaries, (low,high) 24 | dt: float 25 | dt in Hamiltonian mechnics 26 | Lrange : List[int], shape(2) 27 | min/max steps forward 28 | seed: int 29 | random seed 30 | nsamples: int 31 | no. of samples required 32 | ndraws: int 33 | no. of warmup samples 34 | nbest_model: int 35 | how many best models to average, to get best estimation 36 | myrank: int 37 | current rank 38 | name: str 39 | output name, output as {name}.{myrank}.h5 40 | """ 41 | 42 | self.myrank = myrank 43 | self.seed = seed + myrank 44 | nt = boundaries.shape[0] 45 | self.boundaries = boundaries 46 | self.Lrange = Lrange 47 | self.dt = dt 48 | self.invert_Mass = np.eye(nt) 49 | self.model = UserDefinedModel 50 | self.nbest_model = nbest_model 51 | self.nsamples = nsamples 52 | self.ndraws = ndraws 53 | 54 | self.cache = {} 55 | self.ii = 0 56 | 57 | # open hdf5 to save results 58 | self.fio = h5py.File(f"{outdir}/{name}.{self.myrank}.h5","w") 59 | 60 | # set random seed 61 | np.random.seed(self.seed) 62 | 63 | @classmethod 64 | def init(self,UserDefinedModel,boundaries,rank,**kargs): 65 | return HamitonianMC(UserDefinedModel,boundaries, 66 | kargs['dt'],kargs['Lrange'], 67 | kargs['nbest'],kargs['seed'], 68 | kargs['nsamples'], 69 | kargs['ndraws'], 70 | rank,kargs['name'], 71 | kargs['OUTPUT_DIR'] 72 | ) 73 | 74 | def set_initial_model(self): 75 | n = self.boundaries.shape[0] 76 | xcur = np.zeros((n)) 77 | for i in range(n): 78 | bdl = self.boundaries[i,0] 79 | bdr = self.boundaries[i,1] 80 | xcur[i] = bdl + (bdr - bdl) * np.random.rand() 81 | 82 | # make the vel array ordered 83 | tmp = xcur[:int(len(xcur)/2)] 84 | idx = np.argsort(tmp) 85 | tmp_order = tmp[idx] 86 | xcur[:int(len(xcur)/2)] = tmp_order 87 | 88 | tmp = xcur[int(len(xcur)/2):] 89 | tmp_order = tmp[idx] 90 | xcur[int(len(xcur)/2):] = tmp_order 91 | 92 | #print("initial model :",xcur) 93 | return xcur 94 | 95 | def _check_init_is_in_boundary(self,xcur): 96 | for i in range(len(xcur)-1): 97 | if xcur[i] < self.boundaries[i,0] or xcur[i] > self.boundaries[i,1]: 98 | return False 99 | return True 100 | 101 | 102 | def _kinetic(self,p): 103 | """ 104 | Kinetic energy 105 | """ 106 | K = np.dot(self.invert_Mass @ p,p) * 0.5 107 | 108 | return K 109 | 110 | def _forward(self,x): 111 | return self.model.forward(x) 112 | 113 | def misfit_and_grad(self,x): 114 | """ 115 | compute misfit function and corresponding gradient 116 | """ 117 | misfit,grad,dsyn,flag = self.model.misfit_and_grad(x) 118 | 119 | return misfit,grad,dsyn,flag 120 | 121 | def _mirror(self,xcur,pcur): 122 | # check boundaries 123 | xtmp = xcur.copy() 124 | ptmp = pcur.copy() 125 | high = self.boundaries[:,1] 126 | low = self.boundaries[:,0] 127 | idx1 = xtmp > high 128 | idx2 = xtmp < low 129 | while np.sum(np.logical_or(idx1,idx2))>0: 130 | xtmp[idx1] = 2 * high[idx1] - xtmp[idx1] 131 | ptmp[idx1] = -ptmp[idx1] 132 | xtmp[idx2] = 2 * low[idx2] - xtmp[idx2] 133 | ptmp[idx2] = -ptmp[idx2] 134 | idx1 = xtmp > high 135 | idx2 = xtmp < low 136 | 137 | return xtmp,ptmp 138 | 139 | 140 | def _leapfrog(self,xcur,dt,L): 141 | """ 142 | leap frog scheme 143 | """ 144 | n = len(xcur) 145 | #np.random.seed(self.seed + self.myrank + i) 146 | pcur = np.random.randn(n) * 0.5 147 | 148 | # initialize xnew and pnew 149 | pnew = pcur * 1.0 150 | xnew = xcur * 1.0 151 | 152 | # compute current Hamiltonian 153 | K = self._kinetic(pnew) 154 | U,grad,dsyn,flag = self.misfit_and_grad(xnew) 155 | if flag == False or np.sum(np.isnan(dsyn)) > 0: 156 | return xcur,np.inf,self.model.dobs,False 157 | Hcur = K + U 158 | 159 | # save current potential and synthetics 160 | dsyn_new = dsyn.copy() 161 | Unew = U 162 | 163 | # update 164 | pnew -= dt * grad * 0.5 165 | for i in range(L): 166 | xnew += dt * pnew # update xnew 167 | 168 | # check boundaries 169 | xnew,pnew = self._mirror(xnew,pnew) 170 | 171 | # update pnew 172 | if np.sum(np.isnan(xnew)) > 0: 173 | return xcur,np.inf,self.model.dobs,False 174 | Unew,grad,dsyn_new,flag = self.misfit_and_grad(xnew) 175 | if np.sum(np.isnan(grad)) > 0: 176 | # the grad might be NaN. check it. 177 | return xcur,np.inf,self.model.dobs,False 178 | if flag == False or np.sum(np.isnan(dsyn_new)) > 0: 179 | return xcur,np.inf,self.model.dobs,False 180 | if i < L-1: 181 | pnew -= dt * grad 182 | else: 183 | pnew -= dt * grad * 0.5 184 | 185 | # end for loop 186 | 187 | # update Hamiltonian 188 | pnew = -pnew 189 | Knew = self._kinetic(pnew) 190 | Hnew = Knew + Unew 191 | 192 | # accept or not 193 | AcceptFlag = False 194 | u = np.random.rand() 195 | if u < np.exp(-(Hnew - Hcur)): 196 | xcur = xnew 197 | U = Unew 198 | dsyn = dsyn_new 199 | AcceptFlag = True 200 | 201 | return xcur,U,dsyn,AcceptFlag 202 | 203 | def save_results(self,x,dsyn,idx): 204 | 205 | # create group 206 | gname = f"{idx}" 207 | self.fio.create_group(f"{gname}") 208 | 209 | # save model 210 | self.fio.create_dataset( 211 | f"{gname}/model",dtype='f8',shape=x.shape) 212 | self.fio[f"{gname}/model"][:] = x 213 | 214 | # save synthetics 215 | self.fio.create_dataset( 216 | f"{gname}/syn",dtype='f8',shape=dsyn.shape 217 | ) 218 | 219 | self.fio[f"{gname}/syn"][:] = dsyn[:] 220 | 221 | def _save_init(self,x): 222 | # save model 223 | self.fio.create_dataset("initmodel",data= x) 224 | 225 | # save obs 226 | self.fio.create_dataset("obs",data = self.model.dobs) 227 | 228 | def sample(self): 229 | 230 | # init model 231 | x = self.set_initial_model() 232 | 233 | # check the init model 234 | while not self._check_init_is_in_boundary(x): 235 | x = self.set_initial_model() 236 | self._save_init(x) 237 | 238 | # sample posterior distributions 239 | nsamples = self.nsamples 240 | ndraws = self.ndraws 241 | misfit = np.zeros((nsamples)) 242 | x_cache = np.zeros((nsamples,len(x))) 243 | syndata = np.zeros((nsamples,self.model.dobs.shape[0])) 244 | ncount = 0 245 | i = 0 246 | while i < ndraws + nsamples: 247 | #np.random.seed(self.seed + self.myrank + i) 248 | L = np.random.randint(self.Lrange[0],self.Lrange[1]+1) 249 | x,U,dsyn,AcceptFlag = self._leapfrog(x,self.dt,L) 250 | if AcceptFlag: # accept this new sample 251 | if i>=ndraws: 252 | #self.save_results(x,dsyn,i-ndraws) 253 | misfit[i-ndraws] = U 254 | x_cache[i-ndraws,:] = x.copy() 255 | syndata[i-ndraws,:] = dsyn 256 | i += 1 257 | self.ii += 1 258 | ncount += 1 259 | if i > -1 and (i % 50 == 0 or i == nsamples - 1): 260 | msg = "chain {}: {:.2%}, misfit={:.3} -- accept ratio {:.2%}". \ 261 | format(self.myrank,i/(ndraws + nsamples),U,i/ncount) 262 | print(msg) 263 | sys.stdout.flush() 264 | # end i-loop 265 | 266 | # compute average of the best n models 267 | nbests = self.nbest_model 268 | idx = np.argsort(misfit) 269 | xmean = np.mean(x_cache[idx[:nbests],:],axis=0) 270 | _,_,dsyn,_ = self.misfit_and_grad(xmean) 271 | 272 | # save synthetics and each sample 273 | self.save_results(xmean,dsyn,"mean") 274 | for i in range(nsamples): 275 | self.save_results(x_cache[i,:],syndata[i,:],i) 276 | 277 | return misfit -------------------------------------------------------------------------------- /src/SWD/surfdisp.cpp: -------------------------------------------------------------------------------- 1 | #include"surfdisp.hpp" 2 | #include 3 | 4 | /** 5 | * convert phase/group velocity of flat earth to that of spherical earth. 6 | * Refer to Schwab, F. A., and L. Knopoff (1972). Fast surface wave and free 7 | * mode computations, in Methods in Computational Physics, Volume 11, 8 | * Love Wave Equations 44, 45 , 41 pp 112-113. 9 | * Rayleigh Wave Equations 102, 108, 109 pp 142, 144. 10 | * @param t period 11 | * @param c phase/group velo 12 | * @param wavetp wavetype one of [Rc,Rg,Lc,Lg] 13 | * 14 | * @return cnew phase/group velocity of spherical earth 15 | */ 16 | double _flat2sphere(double t,double c,std::string wavetp) 17 | { 18 | double ar = 6371.0; // earth radius 19 | double tm; 20 | double omega = 2.0 * M_PI / t; // angular frequency 21 | 22 | // check input parameters 23 | bool flag = wavetp == "Rc" || wavetp == "Rg" || 24 | wavetp == "Lc" || wavetp == "Lg"; 25 | if(flag == false){ 26 | std::cout << "cnew = _flat2sphere(double t,double c,std::string wavetp)\n"; 27 | std::cout << "parameter wavetp should be in one of [Rc,Rg,Lc,Lg]\n "; 28 | exit(0); 29 | } 30 | 31 | if(wavetp[0] == 'L'){ //love wave 32 | tm = 1. + pow(1.5 * c / (ar * omega),2); 33 | } 34 | else{ // Rayleigh wave 35 | tm = 1. + pow(0.5 * c / (ar * omega),2); 36 | } 37 | tm = sqrt(tm); 38 | 39 | // convert to spherical velocity 40 | double cnew; 41 | if(wavetp[1] == 'c'){ // phase velocity 42 | cnew = c / tm; 43 | } 44 | else{ // group velocity 45 | cnew = c * tm; 46 | } 47 | 48 | return cnew; 49 | } 50 | 51 | /** 52 | * calculates the dispersion values for any layered model, any frequency, and any mode. 53 | * @param nlayer no. of layers 54 | * @param thkm,vpm,vsm,rhom model, shape(nlayer) 55 | * @param kmax no. of periods used 56 | * @param t,cp period and phase velocity, shape(kmax) 57 | * @param sphere true for spherical earth, false for flat earth 58 | * @param wavetype one of [Rc,Rg,Lc,Lg] 59 | * @param mode i-th mode of surface wave, 0 fundamental, 1 first higher, .... 60 | * @param keep_flat keep flat earth phase/group velocity or convert it to spherical 61 | */ 62 | int _surfdisp(float *thk,float *vp,float *vs,float *rho, 63 | int nlayer,double *t,double *cg,int kmax,std::string wavetype, 64 | int mode,bool sphere,bool keep_flat) 65 | { 66 | int iwave,igr,ifsph=0; 67 | if(wavetype=="Rc"){ 68 | iwave = 2; 69 | igr = 0; 70 | } 71 | else if(wavetype == "Rg"){ 72 | iwave = 2; 73 | igr = 1; 74 | } 75 | else if(wavetype=="Lc"){ 76 | iwave = 1; 77 | igr = 0; 78 | } 79 | else if(wavetype=="Lg"){ 80 | iwave = 1; 81 | igr = 1; 82 | } 83 | else{ 84 | std::cout <<"wavetype should be one of [Rc,Rg,Lc,Lg]"< 0; 243 | //printf("rayleigh group %d\n",ierr); 244 | if(ierr == 1) return ierr; 245 | 246 | 247 | // compute kernel 248 | double ur[nlayer],uz[nlayer],tr[nlayer],tz[nlayer]; 249 | double dcdb1[nlayer*nt],dcda1[nlayer*nt], 250 | dcdr1[nlayer*nt],dcdh1[nlayer*nt]; 251 | for(int i=0;i 0.1 * nsamples 57 | if ndraws < 0.1 * nsamples: 58 | print("in dual averaging, ndraws should > nsamples * 0.1") 59 | print(f"ndraws = {ndraws}, nsamples = {nsamples}") 60 | exit(1) 61 | 62 | self.seed = seed + myrank 63 | self.myrank = myrank 64 | self.name = name 65 | 66 | self.cache = {} 67 | self.ii = 0 68 | 69 | # parameters for dual averaging 70 | self.delta = target_ratio 71 | self.dtbar = dt 72 | self._h0 = 0. 73 | self._gamma = 0.05 74 | self._t0 = 10. 75 | self._kappa = 0.75 76 | self._lambda = L0 * self.dt 77 | 78 | # open hdf5 to save results 79 | os.makedirs(outdir,exist_ok=True) 80 | self.fio = h5py.File(f"{outdir}/{name}.{self.myrank}.h5","w") 81 | 82 | # init global random seed 83 | np.random.seed(self.seed) 84 | 85 | @classmethod 86 | def init(self,UserDefinedModel,boundaries,rank,**kargs): 87 | return HMCDualAveraging( 88 | UserDefinedModel,boundaries, 89 | kargs['dt'],kargs['L0'], 90 | kargs['nbest'], 91 | kargs['target_ratio'], 92 | kargs['seed'], 93 | kargs['nsamples'], 94 | kargs['ndraws'], 95 | rank,kargs['name'], 96 | kargs['OUTPUT_DIR'] 97 | ) 98 | 99 | def sort_initial_model(self,xcur:np.ndarray): 100 | # make the vel array ordered 101 | tmp = xcur[:int(len(xcur)/2)] 102 | idx = np.argsort(tmp) 103 | tmp_order = tmp[idx] 104 | xcur[:int(len(xcur)/2)] = tmp_order 105 | 106 | tmp = xcur[int(len(xcur)/2):] 107 | tmp_order = tmp[idx] 108 | xcur[int(len(xcur)/2):] = tmp_order 109 | 110 | return xcur 111 | 112 | def set_initial_model(self): 113 | n = self.boundaries.shape[0] 114 | xcur = np.zeros((n)) 115 | for i in range(n): 116 | bdl = self.boundaries[i,0] 117 | bdr = self.boundaries[i,1] 118 | xcur[i] = bdl + (bdr - bdl) * np.random.rand() 119 | 120 | # sort velocity profile 121 | xcur = self.sort_initial_model(xcur) 122 | 123 | #print("initial model :",xcur) 124 | return xcur 125 | 126 | def _check_init_is_in_boundary(self,xcur): 127 | for i in range(len(xcur)-1): 128 | if xcur[i] < self.boundaries[i,0] or xcur[i] > self.boundaries[i,1]: 129 | return False 130 | return True 131 | 132 | 133 | def _kinetic(self,p): 134 | """ 135 | Kinetic energy 136 | """ 137 | K = np.dot(self.invert_Mass @ p,p) * 0.5 138 | 139 | return K 140 | 141 | def _forward(self,x): 142 | return self.model.forward(x) 143 | 144 | def _misfit_and_grad(self,x): 145 | """ 146 | compute misfit function and corresponding gradient 147 | """ 148 | misfit,grad,dsyn,flag = self.model.misfit_and_grad(x) 149 | 150 | return misfit,grad,dsyn,flag 151 | 152 | def _mirror(self,xcur,pcur): 153 | # check boundaries 154 | xtmp = xcur.copy() 155 | ptmp = pcur.copy() 156 | high = self.boundaries[:,1] 157 | low = self.boundaries[:,0] 158 | idx1 = xtmp > high 159 | idx2 = xtmp < low 160 | while np.sum(np.logical_or(idx1,idx2))>0: 161 | xtmp[idx1] = 2 * high[idx1] - xtmp[idx1] 162 | ptmp[idx1] = -ptmp[idx1] 163 | xtmp[idx2] = 2 * low[idx2] - xtmp[idx2] 164 | ptmp[idx2] = -ptmp[idx2] 165 | idx1 = xtmp > high 166 | idx2 = xtmp < low 167 | 168 | return xtmp,ptmp 169 | 170 | def _find_initial_dt(self,dt0,x): 171 | xcur = x.copy() 172 | n = len(xcur) 173 | dt = dt0 174 | pcur = np.random.randn(n) * 0.5 175 | 176 | # compute gradient and potential energy 177 | U,grad,_,flag = self._misfit_and_grad(xcur) 178 | K = self._kinetic(pcur) 179 | Hcur = U + K 180 | 181 | # leap frog to change dt 182 | a = 0. 183 | pcur -= 0.5 * dt * grad 184 | for it in range(20): 185 | xcur += dt * pcur 186 | 187 | # mirror 188 | xcur,pcur = self._mirror(xcur,pcur) 189 | 190 | # compute grad for current x 191 | U,grad,_,flag = self._misfit_and_grad(xcur) 192 | 193 | if not flag: 194 | print('error in chain %d!\n' %(self.myrank)) 195 | exit(1) 196 | 197 | # update pcur 198 | pcur -= 0.5 * dt * grad 199 | 200 | # compute new H 201 | K = self._kinetic(pcur) 202 | Hnew = U + K 203 | 204 | # factor to increase/decrease 205 | ediff = -(Hnew - Hcur) 206 | if it == 0: 207 | a = 2 * (ediff > np.log(0.5)) - 1 208 | 209 | if ediff < np.log(0.5): 210 | break 211 | else: 212 | # update P for another half step 213 | pcur -= 0.5 * dt * grad 214 | Hcur = Hnew * 1. 215 | dt = dt * 2**a 216 | if it == 20: 217 | print(f"dt estimation for chain {self.myrank} is not reliable!") 218 | print(f"chain {self.myrank}: change dt from {dt0} to {dt}") 219 | 220 | return dt 221 | 222 | def _leapfrog(self,xcur,dt,L): 223 | """ 224 | leap frog scheme 225 | """ 226 | n = len(xcur) 227 | #np.random.seed(self.seed + self.myrank + i) 228 | pcur = np.random.randn(n) * 0.5 229 | 230 | # initialize xnew and pnew 231 | pnew = pcur * 1.0 232 | xnew = xcur * 1.0 233 | 234 | # compute current Hamiltonian 235 | K = self._kinetic(pnew) 236 | U,grad,dsyn,flag = self._misfit_and_grad(xnew) 237 | if flag == False or np.sum(np.isnan(dsyn)) > 0: 238 | return xcur,np.inf,self.model.dobs,0. 239 | Hcur = K + U 240 | 241 | # save current potential and synthetics 242 | dsyn_new = dsyn.copy() 243 | Unew = U 244 | 245 | # leap frog scheme 246 | pnew -= 0.5 * dt * grad 247 | for it in range(L): 248 | # update x 249 | xnew += dt * pnew 250 | 251 | # check boundaries 252 | xnew,pnew = self._mirror(xnew,pnew) 253 | if np.sum(np.isnan(xnew)) > 0: 254 | return xcur,np.inf,self.model.dobs,0 255 | 256 | # comptue gradient 257 | Unew,grad,dsyn_new,flag = self._misfit_and_grad(xnew) 258 | if np.sum(np.isnan(grad)) > 0: 259 | # the grad might be NaN. check it. 260 | return xcur,np.inf,self.model.dobs,0 261 | if flag == False or np.sum(np.isnan(dsyn_new)) > 0: 262 | return xcur,np.inf,self.model.dobs,0 263 | 264 | # update pnew 265 | if it < L - 1: 266 | pnew -= dt * grad 267 | else : 268 | pnew -= dt * grad * 0.5 269 | # end for loop 270 | 271 | # update Hamiltonian 272 | pnew = -pnew 273 | Knew = self._kinetic(pnew) 274 | Hnew = Knew + Unew 275 | 276 | # ratio 277 | alpha = min(1.,np.exp(-(Hnew - Hcur))) 278 | return xnew,Unew,dsyn_new,alpha 279 | 280 | def sample(self): 281 | # init model 282 | x = self.set_initial_model() 283 | 284 | # check the init model 285 | while not self._check_init_is_in_boundary(x): 286 | x = self.set_initial_model() 287 | self._save_init(x) 288 | 289 | # sample posterior distributions 290 | nsamples = self.nsamples 291 | ndraws = self.ndraws 292 | misfit = np.zeros((nsamples)) 293 | x_cache = np.zeros((nsamples,len(x))) 294 | syndata = np.zeros((nsamples,self.model.dobs.shape[0])) 295 | 296 | # get initial dt 297 | dt = self._find_initial_dt(self.dt,x) 298 | dtbar = dt * 1. 299 | h0 = self._h0 300 | mu = np.log(10 * self.dt) 301 | 302 | # sample begin 303 | ncount = 0 304 | i = 0 305 | while i < ndraws + nsamples: 306 | #np.random.seed(self.seed + self.myrank + i) 307 | L = max(1,int(self._lambda / dt)) 308 | x1,U,dsyn,alpha = self._leapfrog(x,dt,L) 309 | 310 | # check if accept this model 311 | u = np.random.rand() 312 | AcceptFlag = False 313 | if u < alpha: 314 | x = x1.copy() 315 | AcceptFlag = True 316 | 317 | if AcceptFlag: # accept this new sample 318 | if i>=ndraws: 319 | #self.save_results(x,dsyn,i-ndraws) 320 | misfit[i-ndraws] = U 321 | x_cache[i-ndraws,:] = x.copy() 322 | syndata[i-ndraws,:] = dsyn 323 | 324 | # update i 325 | i += 1 326 | self.ii += 1 327 | 328 | # dual averaging 329 | if ncount < self.ndraws: 330 | # update h 331 | m = ncount + 1 332 | fac = 1. / (m + self._t0) 333 | h0 = (1 - fac) * h0 + fac * (self.delta - alpha) 334 | 335 | # update dt 336 | logdt = mu - np.sqrt(m) / self._gamma * h0 337 | dt = np.exp(logdt) 338 | 339 | # update eps_cur 340 | fac = m**(-self._kappa) 341 | logdtbar = fac * logdt + (1 - fac) * np.log(dtbar) 342 | dtbar = np.exp(logdtbar) 343 | else: 344 | # set dt_prev 345 | dt = dtbar * 1. 346 | 347 | # update counter 348 | ncount += 1 349 | if i > -1 and (i % 50 == 0 or i == nsamples - 1): 350 | msg = "chain {}: {:.2%}, dt = {:.3}, misfit={:.3} -- accept ratio {:.2%}". \ 351 | format(self.myrank,i/(ndraws + nsamples),dt,U,i/ncount) 352 | 353 | print(msg) 354 | #print(x) 355 | sys.stdout.flush() 356 | # end i-loop 357 | 358 | # compute average of the best n models 359 | nbests = 10 360 | idx = np.argsort(misfit) 361 | xmean = np.mean(x_cache[idx[:nbests],:],axis=0) 362 | _,_,dsyn,_ = self._misfit_and_grad(xmean) 363 | 364 | # save synthetics and each sample 365 | self.save_results(xmean,dsyn,"mean") 366 | for i in range(nsamples): 367 | self.save_results(x_cache[i,:],syndata[i,:],i) 368 | 369 | return misfit 370 | 371 | def save_results(self,x,dsyn,idx): 372 | 373 | # create group 374 | gname = f"{idx}" 375 | self.fio.create_group(f"{gname}") 376 | 377 | # save model 378 | self.fio.create_dataset( 379 | f"{gname}/model",dtype='f8',shape=x.shape) 380 | self.fio[f"{gname}/model"][:] = x 381 | 382 | # save synthetics 383 | self.fio.create_dataset( 384 | f"{gname}/syn",dtype='f8',shape=dsyn.shape 385 | ) 386 | 387 | self.fio[f"{gname}/syn"][:] = dsyn[:] 388 | 389 | def _save_init(self,x): 390 | # save model 391 | self.fio.create_dataset("initmodel",dtype='f8',shape=x.shape) 392 | self.fio['initmodel'][:] = x 393 | 394 | # save obs 395 | self.fio.create_dataset("obs",dtype='f8',shape=self.model.dobs.shape) 396 | self.fio["obs"][:] = self.model.dobs[:] 397 | 398 | def close(self): 399 | # close h5 file 400 | self.fio.close() -------------------------------------------------------------------------------- /cmake/FindFFTW.cmake: -------------------------------------------------------------------------------- 1 | # - Find the FFTW library 2 | # 3 | # Original version of this file: 4 | # Copyright (c) 2015, Wenzel Jakob 5 | # https://github.com/wjakob/layerlab/blob/master/cmake/FindFFTW.cmake, commit 4d58bfdc28891b4f9373dfe46239dda5a0b561c6 6 | # Modifications: 7 | # Copyright (c) 2017, Patrick Bos 8 | # 9 | # Usage: 10 | # find_package(FFTW [REQUIRED] [QUIET] [COMPONENTS component1 ... componentX] ) 11 | # 12 | # It sets the following variables: 13 | # FFTW_FOUND ... true if fftw is found on the system 14 | # FFTW_[component]_LIB_FOUND ... true if the component is found on the system (see components below) 15 | # FFTW_LIBRARIES ... full paths to all found fftw libraries 16 | # FFTW_[component]_LIB ... full path to one of the components (see below) 17 | # FFTW_INCLUDE_DIRS ... fftw include directory paths 18 | # 19 | # The following variables will be checked by the function 20 | # FFTW_USE_STATIC_LIBS ... if true, only static libraries are found, otherwise both static and shared. 21 | # FFTW_ROOT ... if set, the libraries are exclusively searched 22 | # under this path 23 | # 24 | # This package supports the following components: 25 | # FLOAT_LIB 26 | # DOUBLE_LIB 27 | # LONGDOUBLE_LIB 28 | # FLOAT_THREADS_LIB 29 | # DOUBLE_THREADS_LIB 30 | # LONGDOUBLE_THREADS_LIB 31 | # FLOAT_OPENMP_LIB 32 | # DOUBLE_OPENMP_LIB 33 | # LONGDOUBLE_OPENMP_LIB 34 | # 35 | 36 | # TODO (maybe): extend with ExternalProject download + build option 37 | # TODO: put on conda-forge 38 | 39 | 40 | if( NOT FFTW_ROOT AND DEFINED ENV{FFTWDIR} ) 41 | set( FFTW_ROOT $ENV{FFTWDIR} ) 42 | endif() 43 | 44 | # Check if we can use PkgConfig 45 | find_package(PkgConfig) 46 | 47 | #Determine from PKG 48 | if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT ) 49 | pkg_check_modules( PKG_FFTW QUIET "fftw3" ) 50 | endif() 51 | 52 | #Check whether to search static or dynamic libs 53 | set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} ) 54 | 55 | if( ${FFTW_USE_STATIC_LIBS} ) 56 | set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ) 57 | else() 58 | set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} ) 59 | endif() 60 | 61 | if( FFTW_ROOT ) 62 | # find libs 63 | 64 | find_library( 65 | FFTW_DOUBLE_LIB 66 | NAMES "fftw3" libfftw3-3 67 | PATHS ${FFTW_ROOT} 68 | PATH_SUFFIXES "lib" "lib64" 69 | NO_DEFAULT_PATH 70 | ) 71 | 72 | find_library( 73 | FFTW_DOUBLE_THREADS_LIB 74 | NAMES "fftw3_threads" 75 | PATHS ${FFTW_ROOT} 76 | PATH_SUFFIXES "lib" "lib64" 77 | NO_DEFAULT_PATH 78 | ) 79 | 80 | find_library( 81 | FFTW_DOUBLE_OPENMP_LIB 82 | NAMES "fftw3_omp" 83 | PATHS ${FFTW_ROOT} 84 | PATH_SUFFIXES "lib" "lib64" 85 | NO_DEFAULT_PATH 86 | ) 87 | 88 | find_library( 89 | FFTW_DOUBLE_MPI_LIB 90 | NAMES "fftw3_mpi" 91 | PATHS ${FFTW_ROOT} 92 | PATH_SUFFIXES "lib" "lib64" 93 | NO_DEFAULT_PATH 94 | ) 95 | 96 | find_library( 97 | FFTW_FLOAT_LIB 98 | NAMES "fftw3f" libfftw3f-3 99 | PATHS ${FFTW_ROOT} 100 | PATH_SUFFIXES "lib" "lib64" 101 | NO_DEFAULT_PATH 102 | ) 103 | 104 | find_library( 105 | FFTW_FLOAT_THREADS_LIB 106 | NAMES "fftw3f_threads" 107 | PATHS ${FFTW_ROOT} 108 | PATH_SUFFIXES "lib" "lib64" 109 | NO_DEFAULT_PATH 110 | ) 111 | 112 | find_library( 113 | FFTW_FLOAT_OPENMP_LIB 114 | NAMES "fftw3f_omp" 115 | PATHS ${FFTW_ROOT} 116 | PATH_SUFFIXES "lib" "lib64" 117 | NO_DEFAULT_PATH 118 | ) 119 | 120 | find_library( 121 | FFTW_FLOAT_MPI_LIB 122 | NAMES "fftw3f_mpi" 123 | PATHS ${FFTW_ROOT} 124 | PATH_SUFFIXES "lib" "lib64" 125 | NO_DEFAULT_PATH 126 | ) 127 | 128 | find_library( 129 | FFTW_LONGDOUBLE_LIB 130 | NAMES "fftw3l" libfftw3l-3 131 | PATHS ${FFTW_ROOT} 132 | PATH_SUFFIXES "lib" "lib64" 133 | NO_DEFAULT_PATH 134 | ) 135 | 136 | find_library( 137 | FFTW_LONGDOUBLE_THREADS_LIB 138 | NAMES "fftw3l_threads" 139 | PATHS ${FFTW_ROOT} 140 | PATH_SUFFIXES "lib" "lib64" 141 | NO_DEFAULT_PATH 142 | ) 143 | 144 | find_library( 145 | FFTW_LONGDOUBLE_OPENMP_LIB 146 | NAMES "fftw3l_omp" 147 | PATHS ${FFTW_ROOT} 148 | PATH_SUFFIXES "lib" "lib64" 149 | NO_DEFAULT_PATH 150 | ) 151 | 152 | find_library( 153 | FFTW_LONGDOUBLE_MPI_LIB 154 | NAMES "fftw3l_mpi" 155 | PATHS ${FFTW_ROOT} 156 | PATH_SUFFIXES "lib" "lib64" 157 | NO_DEFAULT_PATH 158 | ) 159 | 160 | #find includes 161 | find_path(FFTW_INCLUDE_DIRS 162 | NAMES "fftw3.h" 163 | PATHS ${FFTW_ROOT} 164 | PATH_SUFFIXES "include" 165 | NO_DEFAULT_PATH 166 | ) 167 | 168 | else() 169 | 170 | find_library( 171 | FFTW_DOUBLE_LIB 172 | NAMES "fftw3" 173 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 174 | ) 175 | 176 | find_library( 177 | FFTW_DOUBLE_THREADS_LIB 178 | NAMES "fftw3_threads" 179 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 180 | ) 181 | 182 | find_library( 183 | FFTW_DOUBLE_OPENMP_LIB 184 | NAMES "fftw3_omp" 185 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 186 | ) 187 | 188 | find_library( 189 | FFTW_DOUBLE_MPI_LIB 190 | NAMES "fftw3_mpi" 191 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 192 | ) 193 | 194 | find_library( 195 | FFTW_FLOAT_LIB 196 | NAMES "fftw3f" 197 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 198 | ) 199 | 200 | find_library( 201 | FFTW_FLOAT_THREADS_LIB 202 | NAMES "fftw3f_threads" 203 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 204 | ) 205 | 206 | find_library( 207 | FFTW_FLOAT_OPENMP_LIB 208 | NAMES "fftw3f_omp" 209 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 210 | ) 211 | 212 | find_library( 213 | FFTW_FLOAT_MPI_LIB 214 | NAMES "fftw3f_mpi" 215 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 216 | ) 217 | 218 | find_library( 219 | FFTW_LONGDOUBLE_LIB 220 | NAMES "fftw3l" 221 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 222 | ) 223 | 224 | find_library( 225 | FFTW_LONGDOUBLE_THREADS_LIB 226 | NAMES "fftw3l_threads" 227 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 228 | ) 229 | 230 | find_library(FFTW_LONGDOUBLE_OPENMP_LIB 231 | NAMES "fftw3l_omp" 232 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 233 | ) 234 | 235 | find_library(FFTW_LONGDOUBLE_MPI_LIB 236 | NAMES "fftw3l_mpi" 237 | PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} 238 | ) 239 | 240 | find_path(FFTW_INCLUDE_DIRS 241 | NAMES "fftw3.h" 242 | PATHS ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} 243 | ) 244 | 245 | endif( FFTW_ROOT ) 246 | 247 | #--------------------------------------- components 248 | 249 | if (FFTW_DOUBLE_LIB) 250 | set(FFTW_DOUBLE_LIB_FOUND TRUE) 251 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_DOUBLE_LIB}) 252 | add_library(FFTW::Double INTERFACE IMPORTED) 253 | set_target_properties(FFTW::Double 254 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 255 | INTERFACE_LINK_LIBRARIES "${FFTW_DOUBLE_LIB}" 256 | ) 257 | else() 258 | set(FFTW_DOUBLE_LIB_FOUND FALSE) 259 | endif() 260 | 261 | if (FFTW_FLOAT_LIB) 262 | set(FFTW_FLOAT_LIB_FOUND TRUE) 263 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_FLOAT_LIB}) 264 | add_library(FFTW::Float INTERFACE IMPORTED) 265 | set_target_properties(FFTW::Float 266 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 267 | INTERFACE_LINK_LIBRARIES "${FFTW_FLOAT_LIB}" 268 | ) 269 | else() 270 | set(FFTW_FLOAT_LIB_FOUND FALSE) 271 | endif() 272 | 273 | if (FFTW_LONGDOUBLE_LIB) 274 | set(FFTW_LONGDOUBLE_LIB_FOUND TRUE) 275 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_LONGDOUBLE_LIB}) 276 | add_library(FFTW::LongDouble INTERFACE IMPORTED) 277 | set_target_properties(FFTW::LongDouble 278 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 279 | INTERFACE_LINK_LIBRARIES "${FFTW_LONGDOUBLE_LIB}" 280 | ) 281 | else() 282 | set(FFTW_LONGDOUBLE_LIB_FOUND FALSE) 283 | endif() 284 | 285 | if (FFTW_DOUBLE_THREADS_LIB) 286 | set(FFTW_DOUBLE_THREADS_LIB_FOUND TRUE) 287 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_DOUBLE_THREADS_LIB}) 288 | add_library(FFTW::DoubleThreads INTERFACE IMPORTED) 289 | set_target_properties(FFTW::DoubleThreads 290 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 291 | INTERFACE_LINK_LIBRARIES "${FFTW_DOUBLE_THREADS_LIB}" 292 | ) 293 | else() 294 | set(FFTW_DOUBLE_THREADS_LIB_FOUND FALSE) 295 | endif() 296 | 297 | if (FFTW_FLOAT_THREADS_LIB) 298 | set(FFTW_FLOAT_THREADS_LIB_FOUND TRUE) 299 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_FLOAT_THREADS_LIB}) 300 | add_library(FFTW::FloatThreads INTERFACE IMPORTED) 301 | set_target_properties(FFTW::FloatThreads 302 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 303 | INTERFACE_LINK_LIBRARIES "${FFTW_FLOAT_THREADS_LIB}" 304 | ) 305 | else() 306 | set(FFTW_FLOAT_THREADS_LIB_FOUND FALSE) 307 | endif() 308 | 309 | if (FFTW_LONGDOUBLE_THREADS_LIB) 310 | set(FFTW_LONGDOUBLE_THREADS_LIB_FOUND TRUE) 311 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_LONGDOUBLE_THREADS_LIB}) 312 | add_library(FFTW::LongDoubleThreads INTERFACE IMPORTED) 313 | set_target_properties(FFTW::LongDoubleThreads 314 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 315 | INTERFACE_LINK_LIBRARIES "${FFTW_LONGDOUBLE_THREADS_LIB}" 316 | ) 317 | else() 318 | set(FFTW_LONGDOUBLE_THREADS_LIB_FOUND FALSE) 319 | endif() 320 | 321 | if (FFTW_DOUBLE_OPENMP_LIB) 322 | set(FFTW_DOUBLE_OPENMP_LIB_FOUND TRUE) 323 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_DOUBLE_OPENMP_LIB}) 324 | add_library(FFTW::DoubleOpenMP INTERFACE IMPORTED) 325 | set_target_properties(FFTW::DoubleOpenMP 326 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 327 | INTERFACE_LINK_LIBRARIES "${FFTW_DOUBLE_OPENMP_LIB}" 328 | ) 329 | else() 330 | set(FFTW_DOUBLE_OPENMP_LIB_FOUND FALSE) 331 | endif() 332 | 333 | if (FFTW_FLOAT_OPENMP_LIB) 334 | set(FFTW_FLOAT_OPENMP_LIB_FOUND TRUE) 335 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_FLOAT_OPENMP_LIB}) 336 | add_library(FFTW::FloatOpenMP INTERFACE IMPORTED) 337 | set_target_properties(FFTW::FloatOpenMP 338 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 339 | INTERFACE_LINK_LIBRARIES "${FFTW_FLOAT_OPENMP_LIB}" 340 | ) 341 | else() 342 | set(FFTW_FLOAT_OPENMP_LIB_FOUND FALSE) 343 | endif() 344 | 345 | if (FFTW_LONGDOUBLE_OPENMP_LIB) 346 | set(FFTW_LONGDOUBLE_OPENMP_LIB_FOUND TRUE) 347 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_LONGDOUBLE_OPENMP_LIB}) 348 | add_library(FFTW::LongDoubleOpenMP INTERFACE IMPORTED) 349 | set_target_properties(FFTW::LongDoubleOpenMP 350 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 351 | INTERFACE_LINK_LIBRARIES "${FFTW_LONGDOUBLE_OPENMP_LIB}" 352 | ) 353 | else() 354 | set(FFTW_LONGDOUBLE_OPENMP_LIB_FOUND FALSE) 355 | endif() 356 | 357 | if (FFTW_DOUBLE_MPI_LIB) 358 | set(FFTW_DOUBLE_MPI_LIB_FOUND TRUE) 359 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_DOUBLE_MPI_LIB}) 360 | add_library(FFTW::DoubleMPI INTERFACE IMPORTED) 361 | set_target_properties(FFTW::DoubleMPI 362 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 363 | INTERFACE_LINK_LIBRARIES "${FFTW_DOUBLE_MPI_LIB}" 364 | ) 365 | else() 366 | set(FFTW_DOUBLE_MPI_LIB_FOUND FALSE) 367 | endif() 368 | 369 | if (FFTW_FLOAT_MPI_LIB) 370 | set(FFTW_FLOAT_MPI_LIB_FOUND TRUE) 371 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_FLOAT_MPI_LIB}) 372 | add_library(FFTW::FloatMPI INTERFACE IMPORTED) 373 | set_target_properties(FFTW::FloatMPI 374 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 375 | INTERFACE_LINK_LIBRARIES "${FFTW_FLOAT_MPI_LIB}" 376 | ) 377 | else() 378 | set(FFTW_FLOAT_MPI_LIB_FOUND FALSE) 379 | endif() 380 | 381 | if (FFTW_LONGDOUBLE_MPI_LIB) 382 | set(FFTW_LONGDOUBLE_MPI_LIB_FOUND TRUE) 383 | set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTW_LONGDOUBLE_MPI_LIB}) 384 | add_library(FFTW::LongDoubleMPI INTERFACE IMPORTED) 385 | set_target_properties(FFTW::LongDoubleMPI 386 | PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" 387 | INTERFACE_LINK_LIBRARIES "${FFTW_LONGDOUBLE_MPI_LIB}" 388 | ) 389 | else() 390 | set(FFTW_LONGDOUBLE_MPI_LIB_FOUND FALSE) 391 | endif() 392 | 393 | #--------------------------------------- end components 394 | 395 | set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} ) 396 | 397 | include(FindPackageHandleStandardArgs) 398 | 399 | find_package_handle_standard_args(FFTW 400 | REQUIRED_VARS FFTW_INCLUDE_DIRS 401 | HANDLE_COMPONENTS 402 | ) 403 | 404 | mark_as_advanced( 405 | FFTW_INCLUDE_DIRS 406 | FFTW_LIBRARIES 407 | FFTW_FLOAT_LIB 408 | FFTW_DOUBLE_LIB 409 | FFTW_LONGDOUBLE_LIB 410 | FFTW_FLOAT_THREADS_LIB 411 | FFTW_DOUBLE_THREADS_LIB 412 | FFTW_LONGDOUBLE_THREADS_LIB 413 | FFTW_FLOAT_OPENMP_LIB 414 | FFTW_DOUBLE_OPENMP_LIB 415 | FFTW_LONGDOUBLE_OPENMP_LIB 416 | FFTW_FLOAT_MPI_LIB 417 | FFTW_DOUBLE_MPI_LIB 418 | FFTW_LONGDOUBLE_MPI_LIB 419 | ) -------------------------------------------------------------------------------- /src/SWD/slegn96.f90: -------------------------------------------------------------------------------- 1 | !c----------------------------------------------------------------------c 2 | !c c 3 | !c COMPUTER PROGRAMS IN SEISMOLOGY c 4 | !c VOLUME III c 5 | !c c 6 | !c PROGRAM: SLEGN96 c 7 | !c c 8 | !c COPYRIGHT 1996, 2010 c 9 | !c R. B. Herrmann c 10 | !c Department of Earth and Atmospheric Sciences c 11 | !c Saint Louis University c 12 | !c 221 North Grand Boulevard c 13 | !c St. Louis, Missouri 63103 c 14 | !c U. S. A. c 15 | !c c 16 | !c----------------------------------------------------------------------c 17 | !c 18 | !c 19 | !c This program calculates the group velocity and partial 20 | !c derivatives of Love waves for any plane multi-layered 21 | !c model. The propagator-matrix, instead of numerical- 22 | !c integration method is used, in which the Haskell rather 23 | !c than Harkrider formalisms are concerned. 24 | !c 25 | !c Developed by C. Y. Wang and R. B. Herrmann, St. Louis 26 | !c University, Oct. 10, 1981. Modified for use in surface 27 | !c wave inversion, with addition of spherical earth flattening 28 | !c transformation and numerical calculation of group velocity 29 | !c partial derivatives by David R. Russell, St. Louis 30 | !c University, Jan. 1984. 31 | !c 32 | !c Rewrite of theory to agree more with hspec96 wavenumber 33 | !c integration code 34 | !c 35 | !c- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 | !c Revision history: 37 | !c 07 AUG 2002 - make string lengths 120 characters from 80 38 | !c 13 OCT 2006 - verbose output of energy integrals 39 | !c 26 SEP 2008 - fixed undefined LOT in subroutine up 40 | !c 14 JUN 2009 - reformulate in general terms as per book 41 | !c 01 AUG 2010 - change code to agree with book for 42 | !c migration to TI. Also clean up code using 43 | !c implicit none 44 | !c 20 SEP 2012 - cleaned up some compiler warnings 45 | !c 25 FEB 2017 - fixed error in subroutine varsv. The 46 | !c computation of sin (nu z)/nu yielded NaN when nu = 0 47 | !c The corrected code gives lim sin(nu z) / nu = z 48 | !c nu->0 49 | !c 29 JUL 2017 - modify insert if depth is in halfspace 50 | !c 22 FEB 2018 - verbose also gives LAGR/(omega^2 I0) 51 | ! 21 Apr 2021 -- Modified by Nanqiao Du, Fortran 90 version 52 | !c- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 53 | 54 | !=================================== 55 | ! MODULE : Love wave Model 56 | !=================================== 57 | module LoveWaveModel 58 | use,intrinsic :: iso_c_binding 59 | implicit none 60 | 61 | ! global variables 62 | !============================================ 63 | ! model param 64 | integer(c_int) :: mmax 65 | real(c_double),ALLOCATABLE :: zd(:),zb(:),zrho(:),za(:),xmu(:) 66 | integer(c_int),ALLOCATABLE :: iwat(:) 67 | 68 | ! eigen function 69 | real(c_double),ALLOCATABLE :: uu(:),tt(:),dcdb(:),dcdh(:),dcdr(:) 70 | real(c_double) :: uu0(4) 71 | 72 | ! spherical earth 73 | real(c_double),ALLOCATABLE :: vtp(:),dtp(:),rtp(:) 74 | 75 | ! save 76 | real(c_double),ALLOCATABLE :: exl(:) 77 | 78 | ! love wave var 79 | real(c_double) :: cosq,sinq,yl,zl,mu 80 | 81 | ! sumi 82 | real(c_double) :: sumi0,sumi1,sumi2,flagr,ale,ugr 83 | 84 | ! openmp wrapper 85 | !$omp threadprivate(mmax,zd,zb,zrho,za,xmu,iwat) 86 | !$omp threadprivate(uu,tt,dcdb,dcdh,dcdr,uu0,vtp,dtp,rtp) 87 | !$omp threadprivate(exl,cosq,sinq,yl,zl,mu) 88 | !$omp threadprivate(sumi0,sumi1,sumi2,flagr,ale,ugr) 89 | 90 | contains 91 | subroutine Alloc_Model(nlayer) 92 | implicit none 93 | integer(c_int) ::nlayer 94 | mmax = nlayer 95 | ALLOCATE(zd(mmax),zb(mmax),za(mmax),uu(mmax),tt(mmax)) 96 | ALLOCATE(dcdb(mmax),dcdh(mmax),dcdr(mmax)) 97 | ALLOCATE(iwat(mmax)) 98 | ALLOCATE(xmu(mmax),exl(mmax),zrho(mmax)) 99 | end subroutine Alloc_Model 100 | 101 | subroutine Dealloc_Model() 102 | implicit none 103 | DEALLOCATE(zb,zd,za,uu,tt,dcdb,dcdh,dcdr,zrho) 104 | DEALLOCATE(iwat,xmu,exl) 105 | end subroutine Dealloc_Model 106 | 107 | subroutine bldsph() 108 | !c----- 109 | !c Transform spherical earth to flat earth 110 | !c 111 | !c Schwab, F. A., and L. Knopoff (1972). 112 | !c Fast surface wave and free 113 | !c mode computations, 114 | !c in Methods in Computational Physics, Volume 11, 115 | !c Seismology: Surface Waves and Earth Oscillations, 116 | !c B. A. Bolt (ed), 117 | !c Academic Press, New York 118 | !c 119 | !c Love Wave Equations 44, 45 , 41 pp 112-113 120 | !c Rayleigh Wave Equations 102, 108, 109 pp 142, 144 121 | !c 122 | !c Revised 28 DEC 2007 to use mid-point, assume linear variation in 123 | !c slowness instead of using average velocity for the layer 124 | !c Use the Biswas (1972:PAGEOPH 96, 61-74, 1972) density mapping 125 | !c 126 | !c This is for Love Waves 127 | !c 128 | use,intrinsic :: iso_c_binding 129 | implicit none 130 | integer(c_int) :: i 131 | real(c_double) :: ar, dr, r0, r1, z0, z1,tmp 132 | !c----- 133 | !c vtp is the factor used to convert 134 | !c spherical velocity to flat velocity 135 | !c rtp is the factor used to convert 136 | !c spherical density to flat density 137 | !c dtp is the factor used to convert 138 | !c spherical boundary to flat boundary 139 | !c----- 140 | !c----- 141 | !c duplicate computations of srwvds(IV) 142 | !c----- 143 | ar=6371.0d0 144 | dr=0.0d0 145 | r0=ar 146 | zd(mmax)=1.0 147 | do i=1,mmax 148 | dr= dr + zd(i) 149 | r1=ar-dr 150 | z0=ar*dlog(ar/r0) 151 | z1=ar*dlog(ar/r1) 152 | dtp(i) = ar/r0 !+ ar/r1 153 | 154 | !use layer midpoint 155 | TMP=(2.0d0 *ar)/(r0+r1) 156 | vtp(i) = tmp 157 | rtp(i) = tmp**(-5) 158 | 159 | ! earth flattening transform 160 | zb(i) = zb(i) * tmp 161 | zrho(i) = zrho(i) * rtp(i) 162 | zd(i) = z1 - z0 163 | r0=r1 164 | ENDDO 165 | zd(mmax) = 0.0 166 | return 167 | end subroutine bldsph 168 | 169 | end module LoveWaveModel 170 | 171 | !=================================== 172 | ! MODULE : Love wave Kernel 173 | !=================================== 174 | module LoveWaveKernel 175 | use,intrinsic :: iso_c_binding 176 | implicit none 177 | 178 | contains 179 | subroutine shfunc(omega,wvno) 180 | !c----- 181 | !c This routine evaluates the eigenfunctions by calling sub up. 182 | !c----- 183 | !implicit double precision (a-h,o-z) 184 | use,intrinsic :: iso_c_binding 185 | use LoveWaveModel,only : mmax,iwat,uu,tt,uu0,exl 186 | implicit none 187 | real(c_double) :: fl,omega,wvno 188 | 189 | ! local 190 | real(c_double) :: ext,umax,fact 191 | integer(c_int) :: k 192 | !c----- 193 | !c get the eigenfunctions in an extended floating point form 194 | !c by propagating from the bottom upward. Note since we move in this 195 | !c direction the propagator matrix A(d) is evaluated as A(-d) 196 | !c----- 197 | call up(omega,wvno,fl) 198 | uu0(1)=1.0 199 | !c----- 200 | !c uu0(2)=stress0 is actually the value of period equation. 201 | !c uu0(3) is used to print out the period equation value before 202 | !c the root is refined. 203 | !c----- 204 | uu0(2)=fl 205 | uu0(3)=0.0 206 | uu0(4)=0.0 207 | !c----- 208 | !c convert to actual displacement from the extended floating point 209 | !c working top to down, where the amplitude should be small 210 | !c Also normalize so that the V(0) = 1 211 | !c----- 212 | ext=0.0 213 | umax = uu(1) 214 | tt(1) = 0.0 215 | do k=2,mmax 216 | if(iwat(k).eq.0)then 217 | ext=ext+exl(k-1) 218 | fact=0.0 219 | if(ext.lt.80.0) fact=1./dexp(ext) 220 | uu(k)=uu(k)*fact 221 | tt(k)=tt(k)*fact 222 | else 223 | uu(k) = 0.0 224 | tt(k) = 0.0 225 | endif 226 | if(abs(uu(k)).gt.abs(umax))then 227 | umax = uu(k) 228 | endif 229 | enddo 230 | if(uu(1).ne.0.0)then 231 | umax = uu(1) 232 | endif 233 | if(abs(umax).gt.0.0)then 234 | do k=1,mmax 235 | if(iwat(k).eq.0)then 236 | uu(k) = uu(k) / umax 237 | tt(k) = tt(k) / umax 238 | endif 239 | enddo 240 | endif 241 | !c----- 242 | !c force boundary condition at the free surface 243 | !c free surface is stress free 244 | !c----- 245 | return 246 | end subroutine shfunc 247 | 248 | subroutine varl(m,rb,omega,wvno,xkb,dpth,eexl) 249 | !c----- 250 | !c find variables cosQ, sinQ, etc. 251 | !c----- 252 | !c To handle the hyperbolic functions correctly for large 253 | !c arguments, we use an extended precision procedure, 254 | !c keeping in mind that the maximum precision in double 255 | !c precision is on the order of 16 decimal places. 256 | !c 257 | !c So cosq = 0.5 ( exp(+q) + exp(-q)) 258 | !c = exp(q) * 0.5 * ( 1.0 + exp(-2q) ) 259 | !c becomes 260 | !c cosq = 0.5 * (1.0 + exp(-2q) ) with an exponent q 261 | !c In performing matrix multiplication, we multiply the modified 262 | !c cosp terms and add the exponents. At the last step 263 | !c when it is necessary to obtain a true amplitude, 264 | !c we then form exp(p). For normalized amplitudes at any depth, 265 | !c we carry an exponent for the numerator and the denominator, and 266 | !c scale the resulting ratio by exp(NUMexp - DENexp) 267 | !c 268 | !!!c 269 | !c HSKA cosq sinq 270 | !c 271 | !c When the extended floating point is used, we use the 272 | !c largest exponent for each, which is the following: 273 | !c 274 | !c Let sex = s exponent > 0 for evanescent waves = 0 otherwise 275 | !c 276 | !c Then the modified matrix elements are as follow: 277 | !c 278 | !c Haskell: cosq -> 0.5 ( 1 + exp(-2q) ) exponent = pex 279 | !c----- 280 | !implicit double precision (a-h,o-z) 281 | use LoveWaveModel,only : zb,zrho,cosq,sinq,& 282 | yl,zl,mu 283 | implicit none 284 | integer m 285 | real(c_double) :: rb, xkb, omega, wvno, dpth, eexl,q 286 | 287 | real(c_double) :: wvnop, wvnom , fac 288 | 289 | !c----- 290 | !c define the horizontal wavenumber for the S-wave 291 | !c----- 292 | xkb = omega/zb(m) 293 | !c----- 294 | !c define the vertical wavenumber for the given wvno 295 | !c----- 296 | wvnop = wvno + xkb 297 | wvnom = dabs(wvno - xkb) 298 | fac = wvnop*wvnom 299 | !C WrItE(*,*)'fac:',m,fac 300 | rb=dsqrt(wvnop*wvnom) 301 | q = rb*dpth 302 | mu = zrho(m)*zb(m)*zb(m) 303 | !c----- 304 | !c examine S-wave eigenfunctions 305 | !c checking whether c > vs, c = vs, c < vs 306 | !c----- 307 | eexl = 0.0 308 | if(wvno.lt.xkb)then 309 | sinq=dsin(q) 310 | yl=sinq/rb 311 | zl=-rb*sinq 312 | cosq=dcos(q) 313 | else if(wvno.eq.xkb)then 314 | cosq=1.0d+00 315 | yl=dpth 316 | zl=0.0d+00 317 | else if(wvno.gt.xkb)then 318 | eexl = q 319 | fac = 0.0d+00 320 | if(q.lt.18.0d+00)fac = dexp(-2.0d+0*q) 321 | cosq = ( 1.0d+00 + fac ) * 0.5d+00 322 | sinq = ( 1.0d+00 - fac ) * 0.5d+00 323 | yl = sinq/rb 324 | zl = rb*sinq 325 | endif 326 | 327 | return 328 | end subroutine varl 329 | 330 | subroutine hskl(hl,iwat) 331 | use LoveWaveModel, only : cosq,yl,zl,mu 332 | implicit none 333 | !c----- 334 | !c procedure arguments 335 | !c----- 336 | real(c_double) :: hl(2,2) 337 | integer(c_int) :: iwat 338 | 339 | if(iwat.eq.0)then 340 | hl(1,1) = cosq 341 | hl(1,2) = yl/mu 342 | hl(2,1) = zl*mu 343 | hl(2,2) = cosq 344 | else 345 | hl(1,1) = 1.0d+00 346 | hl(1,2) = 0.0d+00 347 | hl(2,1) = 0.0d+00 348 | hl(2,2) = 1.0d+00 349 | endif 350 | return 351 | end subroutine hskl 352 | 353 | subroutine emat(xnub,wvno,el,einvl) 354 | implicit none 355 | !c----- 356 | !c compute the E and E^-1 matrices 357 | !c----- 358 | real(c_double) :: wvno 359 | complex(c_double_complex) :: xnub, el(2,2), einvl(2,2) 360 | 361 | einvl(1,1) = 0.5/wvno 362 | einvl(1,2) = 0.5/(wvno*xnub) 363 | einvl(2,1) = 0.5/wvno 364 | einvl(2,2) = -0.5/(wvno*xnub) 365 | el(1,1) = wvno 366 | el(1,2) = wvno 367 | el(2,1) = wvno*xnub 368 | el(2,2) = -wvno*xnub 369 | return 370 | end subroutine emat 371 | 372 | subroutine up(omega,wvno,fl) 373 | use LoveWaveModel, only : mmax,zb,zd,iwat,uu,tt,xmu,exl 374 | implicit none 375 | !c----- 376 | !c This routine calculates the elements of Haskell matrix, 377 | !c and finds the eigenfunctions by analytic solution. 378 | !c----- 379 | !c History: 380 | !c 381 | !c 16 JUN 2009 - generalized the routine so that the 382 | !c nature of the propagator matrices is buried in the 383 | !c subroutine hskl 384 | !c This will permit simpler extension to TI 385 | !c----- 386 | !implicit double precision (a-h,o-z) 387 | 388 | real(c_double) :: fl,omega,wvno 389 | real(c_double) :: a11, a12, a21, a22,rb, xkb, dpth,hl(2,2) 390 | real(c_double) :: eexl,amp0,str0,rr,ss,ttlast 391 | integer(c_int) :: mmx1,k,k1 392 | 393 | !c 394 | !c----- 395 | !c apply the boundary conditions to the halfspace 396 | !c----- 397 | !c----- 398 | !c kludge for fluid core 399 | !c----- 400 | if(zb(mmax).gt.0.01)then 401 | dpth = 0.0 402 | call varl(mmax,rb,omega,wvno,xkb,dpth,eexl) 403 | if(wvno.lt.xkb)then 404 | !write(LOT,*) ' imaginary nub derivl' 405 | !write(LOT,*)'omega,wvno,b(mmax)',omega,wvno,zb(mmax) 406 | endif 407 | uu(mmax)=1.0 408 | tt(mmax)=-xmu(mmax)*rb 409 | else 410 | uu(mmax)=1.0 411 | tt(mmax)=0.0 412 | endif 413 | exl(mmax) = 0.0 414 | mmx1=mmax-1 415 | ttlast = 0.0 416 | do k=mmx1,1,-1 417 | if(iwat(k).eq.0)then 418 | dpth=zd(k) 419 | call varl(k,rb,omega,wvno,xkb,dpth,eexl) 420 | call hskl(hl,iwat(k)) 421 | k1=k+1 422 | !c We actually use A^-1 since we go from the bottom to top 423 | a11 = hl(1,1) 424 | a22 = hl(2,2) 425 | a12 = - hl(1,2) 426 | a21 = - hl(2,1) 427 | !C WRITE(6,*)'eexl:',eexl 428 | !C WRITE(6,*)'hl:',hl 429 | 430 | amp0 = a11 * uu(k1) + a12*(tt(k1)) 431 | str0 = a21 * uu(k1) + a22*(tt(k1)) 432 | 433 | !c now normalize and save the normalization 434 | rr=dabs(amp0) 435 | ss=dabs(str0) 436 | if(ss.gt.rr) rr=ss 437 | if(rr.lt.1.d-30) rr=1.d+00 438 | exl(k)=dlog(rr)+eexl 439 | uu(k)=amp0/rr 440 | tt(k)=str0/rr 441 | ttlast = tt(k) 442 | endif 443 | enddo 444 | fl=ttlast 445 | end subroutine up 446 | 447 | subroutine energy(omega,wvno,& 448 | Eut,Edut,Ed2ut,Eut0,Ett0,& 449 | lss,lrr) 450 | !!c----- 451 | !!c This routine calculates the values of integrals I0, I1, 452 | !!c and I2 using analytic solutions. It is found 453 | !!c that such a formulation is more efficient and practical. 454 | !!c 455 | !!c This is based on a suggestion by David Harkrider in 1980. 456 | !!c Given the eigenfunctions, which are properly defined, 457 | !!c define the potential coefficients at the bottom and 458 | !!c top of each layer. If the V(z) = A exp (nu z) + B exp (-nu z) 459 | !!c We want the B at the top of the layer for z=0, and the A at the 460 | !!c bottom of the layer, from the relation K=[Kup Kdn]^T 461 | !!c = [A B]^T = E^-1 U 462 | !!c----- 463 | !!c History: 464 | !!c 465 | !!c 16 JUN 2009 - generalized the routine so that the 466 | !!c nature of the propagator matrices is buried in the 467 | !!c This will permit simpler extension to TI 468 | !!c----- 469 | !implicit double precision (a-h,o-z) 470 | use LoveWaveModel, only :mmax,zd,zb,zrho,xmu,iwat,& 471 | uu,tt,dcdb,dcdh,dcdr,sumi0,& 472 | sumi1,sumi2,flagr,ale,ugr 473 | implicit none 474 | complex(c_double_complex) :: nub,xnub,exqq,f1,f2,f3 475 | real(c_double) :: omega,wvno 476 | COMPLEX(c_double_complex) :: CDEXP,kmup, km1dn 477 | complex(c_double_complex) :: einvl(2,2), el(2,2) 478 | complex(c_double_complex) :: f, g 479 | real(c_double) :: TN, TL,Eut,Edut,Ed2ut,Eut0,Ett0,upup,dupdup 480 | real(c_double) :: DCDBH, DCDBV, DCDRSH,dcr,dcb,fac,& 481 | c2,llflag,dvdz,dfac 482 | real(c_double) :: VSHH, VSHV,c,omega2,wvno2,drho,dpth,& 483 | dmu,rb,xkb,eexl 484 | integer(c_int) :: k,k1,LSS,lrr 485 | 486 | c=omega/wvno 487 | omega2=omega*omega 488 | wvno2=wvno*wvno 489 | sumi0=0.0d+00 490 | sumi1=0.0d+00 491 | sumi2=0.0d+00 492 | !c----- 493 | !c RBH beware when nub = 0 494 | !c----- 495 | do k=1,mmax 496 | if(iwat(k).eq.0)then 497 | TN = zrho(k)*zb(k)*zb(k) 498 | TL = zrho(k)*zb(k)*zb(k) 499 | VSHH = zb(k) 500 | VSHV = zb(k) 501 | k1=k+1 502 | drho=zrho(k) 503 | dpth=zd(k) 504 | call varl(k,rb,omega,wvno,xkb,dpth,eexl) 505 | dmu=xmu(k) 506 | if(rb .lt. 1.0d-10)rb = 1.0d-10 507 | 508 | !c----- 509 | !c for safety do not permit rb = 0 510 | !c----- 511 | if(k.eq.mmax) then 512 | upup =(0.5d+00/rb)*uu(mmax)*uu(mmax) 513 | dupdup=(0.5d+00*rb)*uu(mmax)*uu(mmax) 514 | else 515 | !c----- 516 | !c use Einv to get the potential coefficients 517 | !c from the U and T 518 | !c----- 519 | nub=dcmplx(rb,0.0d+00) 520 | if(wvno.lt.xkb) nub=dcmplx(0.0d+00,rb) 521 | xnub=dmu*nub 522 | call emat(xnub,wvno,el,einvl) 523 | km1dn = einvl(2,1)*uu(k) + einvl(2,2)*tt(k) 524 | kmup = einvl(1,1)*uu(k1) + einvl(1,2)*tt(k1) 525 | 526 | ! c get the f function 527 | f3=nub*dpth 528 | exqq=0.0d+00 529 | if(dreal(f3).lt.40.0) exqq=cdexp(-2.d+00*f3) 530 | f=(1.d+00-exqq)/(2.d+00*nub) 531 | 532 | !c get the g function 533 | exqq=0.0d+00 534 | if(dreal(f3).lt.75.0) exqq=cdexp(-f3) 535 | g=dpth*exqq 536 | 537 | f1 = f*(el(1,1)*el(1,1)*kmup*kmup & 538 | + el(1,2)*el(1,2)*km1dn*km1dn) 539 | f2 = g*(el(1,1)*el(1,2)+el(1,1)*el(1,2))*kmup*km1dn 540 | 541 | !c cast to a real upup 542 | upup = real(f1 + f2) 543 | 544 | !c cast to a real dupdup 545 | dupdup = real(nub*nub*(f1 - f2)) 546 | endif 547 | 548 | sumi0=sumi0+drho*upup 549 | sumi1=sumi1+TN*upup 550 | sumi2=sumi2+TL*dupdup 551 | dcr=-0.5d+00*c*c*c*upup 552 | dcb=0.5d+00*c*(upup+dupdup/wvno2) 553 | DCDBH = c*drho*VSHH*upup 554 | DCDBV = c*drho*VSHV*dupdup/wvno2 555 | dcdb(k)=DCDBH + DCDBV 556 | DCDRSH= 0.5*c*(-c*c*upup + VSHH*VSHH*upup + & 557 | VSHV*VSHV*dupdup/wvno2) 558 | dcdr(k)=DCDRSH 559 | else 560 | dcdb(k)=0.0 561 | dcdr(k)=0.0 562 | endif 563 | enddo 564 | do k=1,mmax 565 | if(iwat(k).eq.0)then 566 | dcdb(k)=dcdb(k)/sumi1 567 | dcdr(k)=dcdr(k)/sumi1 568 | else 569 | dcdb(k)=0.0 570 | dcdr(k)=0.0 571 | endif 572 | enddo 573 | flagr=omega2*sumi0-wvno2*sumi1-sumi2 574 | ugr=sumi1/(c*sumi0) 575 | !print*,ugr 576 | ale=0.5d+00/sumi1 577 | 578 | !c----- 579 | !c define partial with respect to layer thickness 580 | !c----- 581 | !c fac = 0.5d+00*c**3/(omega2*sumi1) 582 | fac = ale*c/wvno2 583 | c2 = c*c 584 | llflag = 0 585 | do k=1,mmax 586 | if(iwat(k).eq.0)then 587 | if(llflag.eq.0)then 588 | drho = zrho(k) 589 | dmu = xmu(k) 590 | dvdz = 0.0 591 | else 592 | drho = zrho(k) - zrho(k-1) 593 | dmu = xmu(k) - xmu(k-1) 594 | dvdz = tt(k)*tt(k)*(1.0/xmu(k) - 1.0/xmu(k-1)) 595 | endif 596 | dfac = fac * ( uu(k)*uu(k)* & 597 | (omega2*drho - wvno2*dmu) + dvdz) 598 | if(dabs(dfac).lt.1.0d-38)then 599 | dcdh(k) = 0.0 600 | else 601 | dcdh(k) = dfac 602 | endif 603 | llflag = llflag + 1 604 | else 605 | dcdh(k) = 0.0 606 | endif 607 | enddo 608 | !c----- 609 | !c compute the eigenfuntions and depth derivatives 610 | !c at the source depth 611 | !c----- 612 | if(iwat(lss).eq.0)then 613 | Eut = uu(lss) 614 | Edut = tt(lss)/xmu(lss) 615 | Ed2ut = ( - (omega/zb(lss))**2 + wvno2)*Eut 616 | else 617 | Eut = 0.0d+00 618 | Edut = 0.0d+00 619 | Ed2ut = 0.0d+00 620 | endif 621 | if(iwat(lrr).eq.0)then 622 | Eut0 = uu(lrr) 623 | Ett0 = tt(lrr) 624 | else 625 | Eut0 = 0.0d+00 626 | Ett0 = 0.0d+00 627 | endif 628 | return 629 | end subroutine energy 630 | 631 | subroutine splove(om,c,csph,usph,ugr) 632 | !!c----- 633 | !!c Transform spherical earth to flat earth 634 | !c and relate the corresponding flat earth dispersion to spherical 635 | !!c 636 | !!c Schwab, F. A., and L. Knopoff (1972). Fast surface wave 637 | !!c and free 638 | !!c mode computations, in 639 | !!c Methods in Computational Physics, Volume 11, 640 | !!c Seismology: Surface Waves and Earth Oscillations, 641 | !!c B. A. Bolt (ed), 642 | !!c Academic Press, New York 643 | !!c 644 | !!c Rayleigh Wave Equations 111, 114 p 144 645 | !!c 646 | !!c Partial with respect to parameter uses the relation 647 | !!c For phase velocity, for example, 648 | !!c 649 | !!c dcs/dps = dcs/dpf * dpf/dps, c is phase velocity, p is 650 | !!c parameter, and f is flat model 651 | !!c 652 | !!c om R*8 angular frequency 653 | !!c c R*8 phase velocity 654 | !!c mmax I*4 number of layers 655 | !!c----- 656 | use LoveWaveModel, only : mmax,vtp,dtp,rtp,dcdb,dcdh,dcdr 657 | real(c_double) :: om,tm,c,csph,usph 658 | real(c_double) :: ugr,a 659 | INTEGER(c_int) :: i 660 | a = 6371.0d0 661 | tm=sqrt(1.+(3.0*c/(2.*a*om))**2) 662 | do i=1,mmax 663 | dcdb(i)=dcdb(i)* vtp(i)/(tm**3) 664 | dcdh(i)=dcdh(i)* dtp(i)/ (tm**3) 665 | dcdr(i)=dcdr(i)* rtp(i)/(tm**3) 666 | enddo 667 | csph = c / tm 668 | usph = ugr * tm 669 | return 670 | end 671 | 672 | subroutine slegn96(thk,vs,rhom,nlayer,& 673 | t,cp,cg,disp,stress,dc2db,dc2dh,dc2dr,& 674 | iflsph)bind(c,name='slegn96_') 675 | use LoveWaveModel 676 | implicit none 677 | integer(c_int),value,INTENT(IN) ::nlayer,iflsph 678 | real(c_float),INTENT(IN) :: thk(nlayer),vs(nlayer),rhom(nlayer) 679 | real(c_double),INTENT(IN) :: t 680 | real(c_double),INTENT(INOUT) :: cp,cg 681 | real(c_double),INTENT(INOUT) :: disp(nlayer),stress(nlayer),& 682 | dc2db(nlayer),dc2dh(nlayer),& 683 | dc2dr(nlayer) 684 | 685 | ! local variables 686 | integer(c_int) :: i,nsph 687 | real(c_double) :: c, omega, wvno, gamma, csph, usph,twopi 688 | real(c_double) :: Eut, Edut, Eut0, Ett0, Ed2ut 689 | real(c_double),PARAMETER :: pi = 3.1415926535898 690 | 691 | ! velocity model 692 | real(c_double) :: sums 693 | !real(c_float) :: sut,sdut,sd2ut,suz,sduz,sd2uz,sale,wvnsrc,sur0 694 | 695 | ! allocate arrays 696 | 697 | ! copy model 698 | call Alloc_Model(nlayer) 699 | zb(1:mmax) = vs(1:mmax); 700 | zrho(1:mmax) = rhom(1:mmax); zd(1:mmax) = thk(1:mmax) 701 | 702 | ! check water layer 703 | do i=1,mmax 704 | if(zb(i) == 0.0)then 705 | iwat(i) = 1 706 | else 707 | iwat(i) = 0 708 | endif 709 | enddo 710 | 711 | ! earth flattening transformation 712 | nsph = iflsph 713 | if(nsph > 0) then 714 | ALLOCATE(vtp(mmax),dtp(mmax),rtp(mmax)) 715 | call bldsph() 716 | endif 717 | 718 | ! compute mu 719 | xmu(1:mmax) = zrho(1:mmax) * zb(1:mmax)**2 720 | 721 | ! main part 722 | twopi = 2.0 * pi 723 | omega = twopi/t 724 | c=cp 725 | wvno=omega/c 726 | call shfunc(omega,wvno) 727 | call energy(omega,wvno,Eut,Edut,Ed2ut,Eut0,Ett0,1,1) 728 | 729 | ! no attenuation 730 | gamma = 0.0 731 | 732 | ! earth flattening transform for kernel 733 | if(nsph > 0) then 734 | call splove(omega,c,csph,usph,ugr) 735 | wvno = omega/csph 736 | else 737 | csph = c 738 | usph = ugr 739 | endif 740 | 741 | !check for underflow 742 | if(dabs(Eut).lt. 1.0d-36)Eut = 0.0d+00 743 | if(dabs(Edut).lt. 1.0d-36)Edut = 0.0d+00 744 | if(dabs(Ed2ut).lt. 1.0d-36)Ed2ut = 0.0d+00 745 | if(dabs(Eut0).lt. 1.0d-36)Eut0 = 0.0d+00 746 | if(dabs(Ett0).lt. 1.0d-36)Ett0 = 0.0d+00 747 | if(dabs(ugr).lt.1.0d-36)ugr=0.0d+00 748 | if(dabs(sumi0).lt.1.0d-36)sumi0=0.0d+00 749 | if(dabs(sumi1).lt.1.0d-36)sumi1=0.0d+00 750 | if(dabs(sumi2).lt.1.0d-36)sumi2=0.0d+00 751 | if(dabs(ale).lt.1.0d-36)ale=0.0d+00 752 | if(dabs(flagr).lt.1.0d-36)flagr=0.0d+00 753 | if(dabs(gamma).lt.1.0d-36)gamma=0.0d+00 754 | 755 | ! get the derivatives of the eigenfunctions required 756 | ! for source excitation from the definition of stress. For 757 | ! completeness get the second derivative from the first 758 | ! derivatives and the equation of motion for the medium 759 | dc2dh(:) = dcdh(:) 760 | do i=1,mmax-1 761 | sums = sum(dc2dh(i+1:mmax)) 762 | !print*,sum(dc2dh(i+1:mmax)),-sum(dc2dh(1:i)) 763 | !sums = sums * 0.5 764 | dcdh(i) = sums 765 | enddo 766 | dcdh(mmax) = 0.0 767 | 768 | ! copy to input variables 769 | disp(:) = uu(:) 770 | stress(:) = uu(:) 771 | dc2db(:) = dcdb(:) 772 | dc2dr(:) = dcdr(:) 773 | dc2dh(:) = dcdh(:) 774 | cp = csph 775 | cg = usph 776 | 777 | call Dealloc_Model() 778 | 779 | if(nsph == 1) then 780 | DEALLOCATE(vtp,dtp,rtp) 781 | endif 782 | 783 | end subroutine slegn96 784 | 785 | subroutine slegnpu(thk,vs,rhom,nlayer,& 786 | t,cp,cg,disp,stress,& 787 | t1,cp1,t2,cp2,dc2db,dc2dh,dc2dr,& 788 | du2db,du2dh,du2dr,iflsph)bind(c,name='slegnpu_') 789 | !! Love wave group kernel 790 | use LoveWaveModel 791 | implicit none 792 | integer(c_int),value,INTENT(IN) ::nlayer,iflsph 793 | real(c_float),INTENT(IN) :: thk(nlayer),vs(nlayer),rhom(nlayer) 794 | real(c_double),INTENT(IN) :: t 795 | real(c_double),INTENT(INOUT) :: cp,cg 796 | real(c_double),INTENT(INOUT) :: disp(nlayer),stress(nlayer),& 797 | dc2db(nlayer),dc2dh(nlayer),dc2dr(nlayer) 798 | real(c_double),intent(inout) :: du2db(nlayer),du2dh(nlayer),du2dr(nlayer) 799 | real(c_double),intent(inout) :: t1,t2,cp1,cp2 ! period and phasev at slightly differentperiod 800 | 801 | !local 802 | integer(c_int) :: i 803 | integer(c_int) :: nsph 804 | real(c_double),PARAMETER :: pi = atan(1.0) * 4.0 805 | real(c_double) :: c, omega, wvno, twopi,sums 806 | real(c_double) :: cg1,cg2,uc1 807 | real(c_double),ALLOCATABLE :: dc2db1(:),dc2db2(:),& 808 | dc2dh1(:),dc2dh2(:),dc2dr1(:),dc2dr2(:) 809 | real(c_double) :: ar,tm,tm1 810 | real(c_double) :: Eut, Edut, Eut0, Ett0, Ed2ut 811 | 812 | ! copy model 813 | call Alloc_Model(nlayer) 814 | zb(1:mmax) = vs(1:mmax) 815 | zrho(1:mmax) = rhom(1:mmax); zd(1:mmax) = thk(1:mmax) 816 | ALLOCATE(dc2db1(mmax),dc2db2(mmax),dc2dh1(mmax),& 817 | dc2dh2(mmax),dc2dr1(mmax),dc2dr2(mmax)) 818 | 819 | ! check water layer 820 | do i=1,mmax 821 | if(zb(i).gt.0.0)then 822 | iwat(i) = 0 823 | else 824 | iwat(i) = 1 825 | endif 826 | enddo 827 | 828 | ! earth flattening transformation 829 | nsph = iflsph 830 | if(nsph > 0) then 831 | ALLOCATE(vtp(mmax),dtp(mmax),rtp(mmax)) 832 | call bldsph() 833 | endif 834 | 835 | ! compute mu and lambda 836 | xmu(:) = zrho(:) * zb(:)**2 837 | 838 | ! compute group velocity for t and phase kernel 839 | twopi = 2.0 * pi 840 | omega = twopi/t 841 | c=cp 842 | wvno=omega/c 843 | call shfunc(omega,wvno) 844 | call energy(omega,wvno,Eut,Edut,Ed2ut,Eut0,Ett0,1,1) 845 | cg = ugr 846 | dc2db(:) = dcdb(:); stress(:) = tt(:); 847 | dc2dr(:) = dcdr(:); disp(:) = uu(:) 848 | dc2dh(:) = dcdh(:); 849 | 850 | ! compute group velocity for t1 and phase kernel 851 | twopi = 2.0 * pi 852 | omega = twopi/t1 853 | c=cp1 854 | wvno=omega/c 855 | call shfunc(omega,wvno) 856 | call energy(omega,wvno,Eut,Edut,Ed2ut,Eut0,Ett0,1,1) 857 | cg1 = ugr 858 | dc2db1(:) = dcdb(:) 859 | dc2dr1(:) = dcdr(:) 860 | dc2dh1(:) = dcdh(:) 861 | 862 | ! compute group velocity for t2 and phase kernel 863 | twopi = 2.0 * pi 864 | omega = twopi/t2 865 | c=cp2 866 | wvno=omega/c 867 | call shfunc(omega,wvno) 868 | call energy(omega,wvno,Eut,Edut,Ed2ut,Eut0,Ett0,1,1) 869 | cg2 = ugr 870 | dc2db2(:) = dcdb(:) 871 | dc2dr2(:) = dcdr(:) 872 | dc2dh2(:) = dcdh(:) 873 | 874 | ! compute duf/dm 875 | uc1 = cg / cp 876 | du2db = uc1 * (2.0 - uc1) * dcdb - uc1**2 * t * (dc2db2 - dc2db1) / (t2-t1) 877 | du2dr = uc1 * (2.0 - uc1) * dcdr - uc1**2 * t * (dc2dr2 - dc2dr1) / (t2-t1) 878 | du2dh = uc1 * (2.0 - uc1) * dcdh - uc1**2 * t * (dc2dh2 - dc2dh1) / (t2-t1) 879 | 880 | ! earth flattening transform for kernel 881 | if(nsph >0)then 882 | ar = 6371.0 883 | omega = twopi / t 884 | tm = sqrt(1.+(3.0 * cp/(2.*ar*omega))**2) 885 | tm1 = (1.5/(ar * omega))**2 / tm 886 | 887 | ! convert duf/dm to dus/dm 888 | du2db = (tm * du2db + cg * cp * dc2db * tm1) * vtp 889 | du2dr = (tm * du2dr + cg * cp * dc2dr * tm1) * rtp 890 | du2dh = (tm * du2dh + cg * cp * dc2dh * tm1) * dtp 891 | 892 | ! convert dcf/dm to dcs/dm 893 | dc2db = dc2db / tm**3 * vtp 894 | dc2dr = dc2dr / tm**3 * rtp 895 | dc2dh = dc2dh / tm**3 * dtp 896 | 897 | ! convert cp and cg to spherical one 898 | cp = cp / tm 899 | cg = cg * tm 900 | endif 901 | 902 | ! change dc/dz,du/dz to dc/dthk,du/dthk 903 | do i=1,mmax-1 904 | sums = sum(dc2dh(i+1:mmax)) 905 | dc2dh(i) = sums 906 | sums = sum(du2dh(i+1:mmax)) 907 | du2dh(i) = sums 908 | enddo 909 | dc2dh(mmax) = 0.0 910 | du2dh(mmax) = 0.0 911 | 912 | call Dealloc_Model() 913 | DEALLOCATE(dc2db1,dc2db2,dc2dh1,dc2dh2,dc2dr1,dc2dr2) 914 | 915 | if(nsph == 1) then 916 | DEALLOCATE(vtp,dtp,rtp) 917 | endif 918 | 919 | end subroutine slegnpu 920 | 921 | end module LoveWaveKernel 922 | -------------------------------------------------------------------------------- /src/plot_results.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import os 4 | import os.path as op 5 | from matplotlib.colors import ListedColormap,LinearSegmentedColormap 6 | import h5py 7 | 8 | EPS = 1e-10 9 | 10 | 11 | class Obs_data(object): 12 | 13 | ''' 14 | These class is used for storing the observation data, including the rf/swd, and 15 | also the time series. 16 | 17 | It is a little bit awkward, and it should be done by passing 18 | the receiverFunc and SWD class 19 | ''' 20 | def __init__(self): 21 | pass 22 | 23 | 24 | def set_obs(self, data, types): 25 | if types == "Rc": 26 | self.Rc = data 27 | elif types == "Rg": 28 | self.Rg= data 29 | elif types == "rf": 30 | self.rf = data 31 | else: 32 | print("wrong types") 33 | 34 | def set_t(self, time, types): 35 | if types == "Rc": 36 | self.tRc = time 37 | self.ntRc = len(time) 38 | elif types == "Rg": 39 | self.tRg= time 40 | self.ntRg = len(time) 41 | elif types == "rf": 42 | self.trf = time 43 | self.ntrf = len(time) 44 | else: 45 | print("wrong types") 46 | 47 | 48 | 49 | 50 | class plotFig(object): 51 | ''' 52 | plot figure for hmc output 53 | ''' 54 | 55 | def __init__(self, HMC_output_path = os.getcwd(),chain_name = "chain", chain_num = 0, 56 | figure_output_path = os.getcwd(), HMC_calculate_model = None): 57 | # os.chdir(HMC_output_path) 58 | self.datapath = HMC_output_path 59 | self.figpath = figure_output_path 60 | self.chain_num = chain_num 61 | self.name = chain_name 62 | 63 | print('Current data path: %s' % self.datapath) 64 | 65 | if HMC_calculate_model is not None: 66 | #get parameter from hmc calculate model 67 | #TODO: to initialize the self.obs_data 68 | pass 69 | else: 70 | self.obs_data = Obs_data() 71 | 72 | self.refmodel = None 73 | self.initmodel = None 74 | 75 | def unpack_thk_vs(self, data): 76 | vs = data[:int(len(data)/2)] 77 | thk = data[int(len(data)/2):] 78 | thk[-1] = 0 79 | depth = np.cumsum(thk) 80 | assert len(depth) == len(vs) 81 | return depth, vs 82 | 83 | def _unpack_thk_vs(self, data): 84 | vs = data[:int(len(data)/2)] 85 | thk = data[int(len(data)/2):] 86 | depth = np.cumsum(thk) 87 | assert len(depth) == len(vs) 88 | return thk, vs 89 | 90 | def unpack_obs(self, data, ntrf, ntRc, ntRg): 91 | # TODO: it should be done for the input of different types of swd. 92 | 93 | rf = data[ : ntrf] 94 | Rc = data[ntrf : ntrf + ntRc ] 95 | Rg = data[ntrf + ntRc : ntrf + ntRc + ntRg] 96 | 97 | 98 | return rf, Rc, Rg 99 | 100 | def savefig(self, fig, filename): 101 | if fig is not None: 102 | outfile = op.join(self.figpath, filename) 103 | fig.savefig(outfile, bbox_inches="tight",dpi=300) 104 | plt.close('all') 105 | 106 | def find_info_in_chain(self, chain_num, info_type, index): 107 | ''' 108 | return the model(thk/vs) or synthetic data of specfic chain/index 109 | input: 110 | chain_num (int): the index number of chain 111 | info_type (str): model for thk/vs; synthetic for data(rf/swd) 112 | index (int): the index number of model 113 | 114 | return: 115 | self.depth / self.vs for model 116 | self.obs_data.rf / self.obs_data_Rc and Rg for syn data 117 | ''' 118 | filename = f"{self.datapath}/{self.name}." + str(chain_num) + ".h5" 119 | fio = h5py.File(filename,"r") 120 | if info_type == "model": 121 | dat_file_name = f"{index}/model" 122 | data = fio[dat_file_name][:] 123 | self.depth , self.vs = self.unpack_thk_vs(data) 124 | 125 | if info_type == "model_init": 126 | dat_file_name = "initmodel" 127 | data = fio[dat_file_name][:] 128 | self.depth , self.vs = self.unpack_thk_vs(data) 129 | 130 | 131 | if info_type == "model_mean": 132 | data = fio["mean/model"][:] 133 | self.depth , self.vs = self.unpack_thk_vs(data) 134 | 135 | if info_type == "synthetic": 136 | data = fio[f'{index}/syn'][:] 137 | #todo: why there is obs data in synthetic.dat 138 | 139 | self.obs_data.rf, self.obs_data.Rc, self.obs_data.Rg = self.unpack_obs(data, 140 | self.obs_data.ntrf, self.obs_data.ntRc, self.obs_data.ntRg) 141 | 142 | if info_type == "synthetic_mean": 143 | data = fio['mean/syn'][:] 144 | #todo: why there is obs data in synthetic.dat 145 | 146 | self.obs_data.rf, self.obs_data.Rc, self.obs_data.Rg = self.unpack_obs(data, 147 | self.obs_data.ntrf, self.obs_data.ntRc, self.obs_data.ntRg) 148 | 149 | if info_type == "para": 150 | dat_file_name = f"{index}/model" 151 | data = fio[dat_file_name][:] 152 | 153 | # data = np.loadtxt(op.join(self.datapath,chain_data_folder,dat_file_name)) 154 | self.thk , self.vs = self._unpack_thk_vs(data) 155 | 156 | fio.close() 157 | 158 | def update_refmodel(self,data): 159 | self.refmodel = data 160 | 161 | def update_initmodel(self,data): 162 | self.initmodel = data 163 | 164 | def update_real_data(self,data): 165 | self.real_data = data 166 | 167 | 168 | def plot_chain_misfit(self): 169 | ''' 170 | retrun matplotlib fig 171 | 172 | Plot misfit of different chain. 173 | 174 | ''' 175 | 176 | fig, ax = plt.subplots(figsize=(12,5)) 177 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 178 | if len(all_misfit.shape) == 1: 179 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 180 | # row for chains and col for model_index 181 | max_chain = len(all_misfit[:,0]) 182 | 183 | 184 | #plot best syn result in different chain 185 | for chain_iter in range(max_chain): 186 | misfit = np.min(all_misfit[chain_iter,:]) 187 | 188 | print("misfit: {}".format(misfit)) 189 | if misfit < EPS: 190 | print("skip chain {}".format(chain_iter)) 191 | continue 192 | 193 | model_index = np.argmin(all_misfit[chain_iter,:]) 194 | ax.plot(all_misfit[chain_iter,:] ,ls='-', lw=0.8, alpha=0.5, 195 | label = "chain %d" %(chain_iter) ) 196 | 197 | 198 | ax.set_ylabel('misfit') 199 | ax.set_title('misfit curve from %d chains' %max_chain) 200 | ax.set_xlabel('iter') 201 | ax.legend() 202 | 203 | return fig 204 | 205 | def plot_para_stats(self): 206 | ''' 207 | retrun matplotlib fig 208 | 209 | Plot statistic result of different chain. 210 | 211 | ''' 212 | 213 | 214 | 215 | def plot_refmodel(self, fig, mtype='model', **kwargs): 216 | if fig is not None and self.refmodel[mtype] is not None: 217 | if mtype == 'nlays': 218 | nlays = self.refmodel[mtype] 219 | fig.axes[0].axvline(nlays, color='red', lw=0.5, alpha=0.7) 220 | 221 | if mtype == 'model': 222 | dep, vs = self.unpack_thk_vs(self.refmodel['model']) 223 | assert len(dep) == len(vs) 224 | fig.axes[0].plot(vs, dep, **kwargs) 225 | if len(fig.axes) == 2: 226 | deps = np.unique(dep) 227 | for d in deps: 228 | fig.axes[1].axhline(d, **kwargs) 229 | return fig 230 | 231 | def plot_best_fit(self): 232 | ''' 233 | retrun matplotlib fig 234 | 235 | Plot the best fit of receiver function and surface wave dispersion. 236 | 237 | ''' 238 | 239 | fig, ax = plt.subplots(3, 1, figsize=(7,10)) 240 | 241 | 242 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 243 | if len(all_misfit.shape) == 1: 244 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 245 | max_chain = len(all_misfit[:,0]) 246 | 247 | thebestmisfit = 1e15 248 | thebestchain = 0 249 | thebestindex = 0 250 | 251 | #plot best syn result in different chain 252 | for chain_iter in range(max_chain): 253 | misfit = np.min(all_misfit[chain_iter,:]) 254 | if misfit < EPS: 255 | continue 256 | model_index = np.argmin(all_misfit[chain_iter,:]) 257 | 258 | if misfit < thebestmisfit: 259 | thebestmisfit = misfit 260 | thebestchain = chain_iter 261 | thebestindex = model_index 262 | 263 | self.find_info_in_chain(chain_iter,"synthetic",model_index) 264 | 265 | if chain_iter == 0: 266 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='k', ls='-', 267 | lw=0.8, alpha=0.5) 268 | 269 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='k', ls='-', 270 | lw=0.8, alpha=0.5) 271 | 272 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='k', ls='-', 273 | lw=0.8, alpha=0.5) 274 | else: 275 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='k', ls='-', 276 | lw=0.8, alpha=0.5) 277 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='k', ls='-', 278 | lw=0.8, alpha=0.5) 279 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='k', ls='-', 280 | lw=0.8, alpha=0.5) 281 | 282 | #plot best syn result 283 | self.find_info_in_chain(thebestchain,"synthetic",thebestindex) 284 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='b', ls='-', 285 | lw=1.5, alpha=0.5, label = 'best model') 286 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='b', ls='-', 287 | lw=1.5, alpha=0.5, label = 'best model') 288 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='b', ls='-', 289 | lw=1.5, alpha=0.5, label = 'best model') 290 | 291 | #plot real data 292 | if self.real_data is not None: 293 | self.obs_data.rf, self.obs_data.Rc, self.obs_data.Rg = self.unpack_obs( 294 | self.real_data,self.obs_data.ntrf, self.obs_data.ntRc, self.obs_data.ntRg) 295 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='r', ls='-', 296 | lw=1.5, alpha=0.5, label = 'real model') 297 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='r', ls='-', 298 | lw=1.5, alpha=0.5, label = 'real model') 299 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='r', ls='-', 300 | lw=1.5, alpha=0.5, label = 'real model') 301 | 302 | 303 | ax[0].set_ylabel('Amplitude') 304 | #ax[0].set_title('Best rf result from %d chains' %max_chain) 305 | ax[2].set_ylabel('Velocity') 306 | #ax[2].set_title('Best Rg result from %d chains' %max_chain) 307 | ax[1].set_ylabel('Velocity') 308 | #ax[1].set_title('Best Rc result from %d chains' %max_chain) 309 | 310 | for i in range(len(ax)): 311 | ax[i].set_xlabel('t(s)') 312 | ax[i].legend() 313 | return fig 314 | 315 | 316 | def plot_var_statisc(self, boundaries, thk, vs): 317 | 318 | fig_num = int(np.ceil(np.sqrt(len(boundaries)))) 319 | fig, ax = plt.subplots(fig_num, fig_num, figsize=(15,15)) 320 | 321 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 322 | if len(all_misfit.shape) == 1: 323 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 324 | thebestmisfit = 1e15 325 | thebestchain = 0 326 | thebestindex = 0 327 | 328 | 329 | max_chain = len(all_misfit[:,0]) 330 | max_model = len(all_misfit[0,:]) 331 | parameter_var = np.zeros((max_chain*max_model,len(boundaries))) 332 | for chain_iter in range(max_chain): 333 | for model_iter in range(max_model): 334 | self.find_info_in_chain(chain_iter,"para",model_iter) 335 | 336 | if all_misfit[chain_iter,model_iter] < EPS: 337 | continue 338 | 339 | if all_misfit[chain_iter,model_iter] < thebestmisfit: 340 | thebestmisfit = all_misfit[chain_iter,model_iter] 341 | thebestchain = chain_iter 342 | thebestindex = model_iter 343 | 344 | for i in range(len(self.thk)): 345 | parameter_var[chain_iter*max_model+model_iter,i] = self.vs[i] 346 | parameter_var[chain_iter*max_model+model_iter,i+len(self.thk)] = self.thk[i] 347 | 348 | self.find_info_in_chain(thebestchain,"para",thebestindex) 349 | 350 | 351 | for i in range(fig_num): 352 | for j in range(fig_num): 353 | iter_num_of_var = i*fig_num + j 354 | if (iter_num_of_var) < len(self.thk) * 2: 355 | 356 | if (iter_num_of_var) == len(self.thk) * 2 - 1 : 357 | continue 358 | 359 | ax[i][j].hist((parameter_var[:,iter_num_of_var])[parameter_var[:,iter_num_of_var] != 0]) 360 | 361 | 362 | 363 | 364 | if iter_num_of_var < len(self.vs): 365 | ax[i][j].axvline(self.vs[iter_num_of_var],color = 'blue',label = "best") 366 | ax[i][j].axvline(vs[iter_num_of_var],color = 'red',label = "real") 367 | # ax[i][j].axvline(boundaries[iter_num_of_var,0],color = 'black',label = "range") 368 | # ax[i][j].axvline(boundaries[iter_num_of_var,1],color = 'black') 369 | ax[i][j].legend() 370 | ax[i][j].set_xlabel("vs of layer {}".format(iter_num_of_var+1)) 371 | else: 372 | ax[i][j].axvline(self.thk[iter_num_of_var - len(self.thk)],color = 'blue',label = "best") 373 | ax[i][j].axvline(thk[iter_num_of_var - len(self.thk)],color = 'red',label = "real") 374 | # ax[i][j].axvline(boundaries[iter_num_of_var,0],color = 'black',label = "range") 375 | # ax[i][j].axvline(boundaries[iter_num_of_var,1],color = 'black') 376 | ax[i][j].legend() 377 | ax[i][j].set_xlabel("thk of layer {}".format(iter_num_of_var +1- len(self.thk))) 378 | else: 379 | break 380 | 381 | 382 | 383 | return fig 384 | 385 | def plot_best_fit_hist(self, amplitude_range, velRc_range, velRg_range ): 386 | ''' 387 | retrun matplotlib fig 388 | 389 | Plot the best fit of receiver function and surface wave dispersion. 390 | 391 | ''' 392 | fig, ax = plt.subplots(3, 1, figsize=(7,10)) 393 | 394 | velRc_vector = np.linspace(velRc_range[0],velRc_range[1],int(velRc_range[2])) 395 | velRg_vector = np.linspace(velRg_range[0],velRg_range[1],int(velRg_range[2])) 396 | amp_vector = np.linspace(amplitude_range[0],amplitude_range[1],int(amplitude_range[2])) 397 | 398 | 399 | 400 | 401 | hist_grid_swdRc = np.zeros((len(self.obs_data.tRc),len(velRc_vector))) 402 | hist_grid_swdRg = np.zeros((len(self.obs_data.tRg),len(velRg_vector))) 403 | hist_grid_rf = np.zeros((len(self.obs_data.trf),len(amp_vector))) 404 | 405 | thebestmisfit = 1e15 406 | thebestchain = 0 407 | thebestindex = 0 408 | 409 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 410 | if len(all_misfit.shape) == 1: 411 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 412 | max_chain = len(all_misfit[:,0]) 413 | 414 | 415 | for chain_iter in range(max_chain): 416 | 417 | temp = np.argwhere(all_misfit[chain_iter,:]) 418 | mean_chain = np.mean(temp) 419 | cont = 0 420 | for model_iter in range(len(all_misfit[chain_iter,:])): 421 | 422 | if all_misfit[chain_iter,model_iter] > mean_chain: 423 | cont += 1 424 | continue 425 | 426 | 427 | self.find_info_in_chain(chain_iter,"synthetic",model_iter) 428 | self.obs_data.rf, self.obs_data.Rc, self.obs_data.Rg 429 | 430 | for i in range(len(self.obs_data.rf)): 431 | amp_index = np.searchsorted(amp_vector,self.obs_data.rf[i]) 432 | if amp_index >= len(self.obs_data.rf) : 433 | amp_index = len(self.obs_data.rf) - 1 434 | hist_grid_rf[i,amp_index] += 1 435 | 436 | for i in range(len(self.obs_data.Rg)): 437 | vel_index = np.searchsorted(velRg_vector,self.obs_data.Rg[i]) 438 | if vel_index >= len(velRg_vector) : 439 | vel_index = len(velRg_vector) - 1 440 | hist_grid_swdRg[i,vel_index] += 1 441 | 442 | for i in range(len(self.obs_data.Rc)): 443 | vel_index = np.searchsorted(velRc_vector,self.obs_data.Rc[i]) 444 | if vel_index >= len(velRc_vector) : 445 | vel_index = len(velRc_vector) - 1 446 | hist_grid_swdRc[i,vel_index] += 1 447 | 448 | break 449 | 450 | 451 | for i in range(len(hist_grid_rf[:,0])): 452 | hist_grid_rf[i,:] = hist_grid_rf[i,:] / np.max(hist_grid_rf[i,:]) 453 | for i in range(len(hist_grid_swdRc[:,0])): 454 | hist_grid_swdRc[i,:] = hist_grid_swdRc[i,:] / np.max(hist_grid_swdRc[i,:]) 455 | for i in range(len(hist_grid_swdRg[:,0])): 456 | hist_grid_swdRg[i,:] = hist_grid_swdRg[i,:] / np.max(hist_grid_swdRg[i,:]) 457 | 458 | ax[0].pcolor(self.obs_data.trf, amp_vector, hist_grid_rf.T, cmap=plt.cm.jet ) 459 | ax[1].pcolor(self.obs_data.tRc, velRc_vector, hist_grid_swdRc.T, cmap=plt.cm.jet ) 460 | ax[2].pcolor(self.obs_data.tRg, velRg_vector, hist_grid_swdRg.T, cmap=plt.cm.jet ) 461 | 462 | 463 | 464 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 465 | if len(all_misfit.shape) == 1: 466 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 467 | max_chain = len(all_misfit[:,0]) 468 | 469 | thebestmisfit = 1e15 470 | thebestchain = 0 471 | thebestindex = 0 472 | 473 | #plot best syn result in different chain 474 | for chain_iter in range(max_chain): 475 | misfit = np.min(all_misfit[chain_iter,:]) 476 | model_index = np.argmin(all_misfit[chain_iter,:]) 477 | 478 | if misfit < EPS: 479 | continue 480 | 481 | if misfit < thebestmisfit : 482 | thebestmisfit = misfit 483 | thebestchain = chain_iter 484 | thebestindex = model_index 485 | 486 | #plot best syn result 487 | self.find_info_in_chain(thebestchain,"synthetic",thebestindex) 488 | #ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='b', ls='-', lw=1.5, alpha=0.5, label = "best model") 489 | #ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='b', ls='-', lw=1.5, alpha=0.5, label = "best model") 490 | #ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='b', ls='-', lw=1.5, alpha=0.5, label = "best model") 491 | 492 | self.find_info_in_chain(thebestchain,"synthetic_mean",thebestindex) 493 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='black', ls='-', lw=2, alpha=1, label = "mean model") 494 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='black', ls='-', lw=2, alpha=1, label = "mean model") 495 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='black', ls='-', lw=2, alpha=1, label = "mean model") 496 | 497 | #plot real data 498 | if self.real_data is not None: 499 | self.obs_data.rf, self.obs_data.Rc, self.obs_data.Rg = self.unpack_obs(self.real_data, 500 | self.obs_data.ntrf, self.obs_data.ntRc, self.obs_data.ntRg) 501 | ax[0].plot(self.obs_data.trf, self.obs_data.rf , color='white', ls='-', lw=2, alpha=1, label = "real model") 502 | ax[2].plot(self.obs_data.tRg, self.obs_data.Rg , color='white', ls='-', lw=2, alpha=1, label = "real model") 503 | ax[1].plot(self.obs_data.tRc, self.obs_data.Rc , color='white', ls='-', lw=2, alpha=1, label = "real model") 504 | 505 | ax[0].set_ylabel('Amplitude') 506 | #ax[0].set_title('Best rf result from %d chains' %max_chain) 507 | ax[2].set_ylabel('Velocity') 508 | #ax[2].set_title('Best Rg result from %d chains' %max_chain) 509 | ax[1].set_ylabel('Velocity') 510 | #ax[1].set_title('Best Rc result from %d chains' %max_chain) 511 | 512 | for i in range(len(ax)): 513 | ax[i].legend() 514 | 515 | 516 | return fig 517 | ax[-1].set_xlabel('t(s)') 518 | 519 | def plot_bestmodels(self, depth): 520 | """Return fig. 521 | 522 | Plot the best (fit) models ever discovered per each chain, 523 | ignoring outliers. 524 | """ 525 | fig, ax = plt.subplots(figsize=(4, 6.5)) 526 | 527 | 528 | depth_vector = np.linspace(depth[0],depth[1],int(depth[2])) 529 | 530 | thebestmisfit = 1e15 531 | thebestchain = 0 532 | thebestindex = 0 533 | 534 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 535 | if len(all_misfit.shape) == 1: 536 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 537 | max_chain = len(all_misfit[:,0]) 538 | 539 | 540 | 541 | #plot best model in different chain 542 | for chain_iter in range(max_chain): 543 | misfit = np.min(all_misfit[chain_iter,:]) 544 | model_index = np.argmin(all_misfit[chain_iter,:]) 545 | 546 | if misfit < EPS: 547 | continue 548 | 549 | if misfit < thebestmisfit: 550 | thebestmisfit = misfit 551 | thebestchain = chain_iter 552 | thebestindex = model_index 553 | 554 | self.find_info_in_chain(chain_iter,"model",model_index) 555 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 556 | 557 | ax.step(self.vs, self.depth , color='k', ls='-', lw=0.8, alpha=0.5) 558 | 559 | # plot ref_model() 560 | if self.refmodel is not None: 561 | self.depth , self.vs = self.unpack_thk_vs(self.refmodel) 562 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 563 | ref_legend = ax.step(self.vs, self.depth , color='r', ls='-', lw=1.5, alpha=0.5, label = 'real model') 564 | 565 | # plot init_model() 566 | ''' 567 | if self.initmodel is not None: 568 | self.depth , self.vs = self.unpack_thk_vs(self.initmodel) 569 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 570 | ax.step(self.vs, self.depth , color='y', ls='-', lw=1.5, alpha=0.5, label = 'init model') 571 | ''' 572 | #plot best model 573 | self.find_info_in_chain(thebestchain,"model",thebestindex) 574 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 575 | bst_legend = ax.step(self.vs, self.depth , color='b', ls='-', lw=1.5, alpha=0.5, label = 'best model') 576 | 577 | ax.legend() 578 | 579 | ax.invert_yaxis() 580 | ax.set_xlabel('$V_S$ in km/s') 581 | ax.set_ylabel('Depth in km') 582 | 583 | ax.set_title('Best fit models from %d chains' %max_chain) 584 | 585 | return fig 586 | 587 | def plot_bestmodels_hit(self, vel, depth): 588 | """Return fig. 589 | 590 | Plot the best (fit) models ever discovered per each chain, 591 | ignoring outliers. 592 | """ 593 | fig, ax = plt.subplots(figsize=(8, 12.5)) 594 | 595 | vel_vector = np.linspace(vel[0],vel[1],int(vel[2])) 596 | depth_vector = np.linspace(depth[0],depth[1],int(depth[2])) 597 | hist_grid = np.zeros((len(depth_vector),len(vel_vector))) 598 | 599 | 600 | thebestmisfit = 1e15 601 | thebestchain = 0 602 | thebestindex = 0 603 | 604 | all_misfit = np.load(op.join(self.datapath,"misfit.npy")) 605 | if len(all_misfit.shape) == 1: 606 | all_misfit = all_misfit.reshape(1,len(all_misfit)) 607 | max_chain = len(all_misfit[:,0]) 608 | 609 | 610 | for chain_iter in range(max_chain): 611 | mean_chain = np.mean(np.argwhere(all_misfit[chain_iter,:]))/0.1 612 | for model_iter in range(len(all_misfit[chain_iter,:])): 613 | self.find_info_in_chain(chain_iter,"model",model_iter) 614 | 615 | if all_misfit[chain_iter,model_iter] > mean_chain: 616 | continue 617 | 618 | new_depth = depth_vector 619 | self.depth, self.vs = self.interp(self.depth, self.vs, new_depth) 620 | 621 | # fill the gap of vs 622 | 623 | 624 | # print(self.depth, self.vs) 625 | 626 | 627 | 628 | for i in range(len(self.vs)): 629 | vel_index = np.searchsorted(vel_vector,self.vs[i]) 630 | depth_index = np.searchsorted(depth_vector,self.depth[i]) 631 | 632 | 633 | if i >= 1: 634 | if (self.vs[i] - self.vs[i-1] < 0.001 ): 635 | # no gaps, discard 636 | pass 637 | else: 638 | #print(np.searchsorted(vel_vector,self.vs[i]),np.searchsorted(vel_vector,self.vs[i-1])) 639 | vs_index_min = min(np.searchsorted(vel_vector,self.vs[i]), 640 | np.searchsorted(vel_vector,self.vs[i-1])) 641 | vs_index_max = max(np.searchsorted(vel_vector,self.vs[i]), 642 | np.searchsorted(vel_vector,self.vs[i-1])) 643 | 644 | for j in range(vs_index_min,vs_index_max): 645 | #print("junliu debug : vs {}".format(j)) 646 | hist_grid[depth_index,j] += 1 647 | #pass 648 | 649 | 650 | 651 | 652 | if depth_index == int(vel[2]): 653 | continue 654 | if vel_index == int(depth[2]): 655 | continue 656 | 657 | hist_grid[depth_index,vel_index] += 1 658 | 659 | #print("junliu debug: ",len(hist_grid[:0])) 660 | for i in range(len(hist_grid[:,0])): 661 | hist_grid[i,:] = hist_grid[i,:] / np.max(hist_grid[i,:]) 662 | #sprint(hist_grid[i,:]) 663 | 664 | clist = ['red','white','blue' ] 665 | newcmp = LinearSegmentedColormap.from_list('chao',clist) 666 | map = ax.pcolor(vel_vector, depth_vector, hist_grid, cmap=plt.cm.jet ) 667 | #colorbar_pos = fig.add_axes([0.15,0.25,0.35,0.03]) 668 | colorbar_h = plt.colorbar(mappable=map,cax=None,ax=None,shrink=1,fraction=0.05) 669 | colorbar_h.ax.tick_params(labelsize = 16) 670 | colorbar_h.set_label("Probability",fontsize = 16) 671 | 672 | #plot best model in different chain 673 | for chain_iter in range(max_chain): 674 | misfit = np.min(all_misfit[chain_iter,:]) 675 | model_index = np.argmin(all_misfit[chain_iter,:]) 676 | 677 | 678 | if misfit < EPS: 679 | continue 680 | 681 | if misfit < thebestmisfit and misfit > 1e-6: 682 | thebestmisfit = misfit 683 | thebestchain = chain_iter 684 | thebestindex = model_index 685 | 686 | self.find_info_in_chain(chain_iter, "model", model_index) 687 | 688 | 689 | 690 | # plot ref_model() 691 | if self.refmodel is not None: 692 | self.depth , self.vs = self.unpack_thk_vs(self.refmodel) 693 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 694 | ax.step(self.vs, self.depth , color='white', ls='-', lw=3, alpha=1, label = 'real model') 695 | 696 | 697 | 698 | # plot init_model() 699 | ''' 700 | if self.initmodel is not None: 701 | self.depth , self.vs = self.unpack_thk_vs(self.initmodel) 702 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 703 | ax.step(self.vs, self.depth , color='y', ls='-', lw=3, alpha=1, label = 'init model') 704 | ''' 705 | ''' 706 | #plot best model 707 | self.find_info_in_chain(thebestchain,"model",thebestindex) 708 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 709 | ax.step(self.vs, self.depth , color='b', ls='-', lw=3, alpha=1, label = 'best model') 710 | ''' 711 | 712 | print("best chains {}".format(thebestchain)) 713 | self.find_info_in_chain(thebestchain,"model_mean",thebestindex) 714 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 715 | ax.step(self.vs, self.depth , color='black', ls='-', lw=3, alpha=1, label = 'mean model') 716 | 717 | ''' 718 | self.find_info_in_chain(thebestchain,"model_init",thebestindex) 719 | print("junliu debug: {}th chain".format(thebestchain)) 720 | self.depth, self.vs = self.interp(self.depth, self.vs, depth_vector) 721 | ax.step(self.vs, self.depth , color='y', ls='-', lw=3, alpha=1, label = 'init model') 722 | ''' 723 | 724 | #print(plt.rcParams['font.size']) 725 | plt.rcParams['font.size'] = 12.5 726 | 727 | ax.invert_yaxis() 728 | ax.set_xlabel('$V_S$ in km/s',fontdict = {'size':15}) 729 | ax.set_ylabel('Depth in km',fontdict = {'size':15}) 730 | ax.legend() 731 | #ax.set_title('Best fit models from %d chains' %max_chain) 732 | plt.xticks(size = 15) 733 | plt.yticks(size = 15) 734 | 735 | 736 | return fig 737 | 738 | def interp(self, x_before, y_before, x_after): 739 | 740 | # print(y_before) 741 | y_after = np.zeros((len(x_after))) 742 | 743 | x_before_index = 0 744 | 745 | for i in range(len(x_after)): 746 | x = x_before[x_before_index] 747 | if x_after[i] <= x: 748 | y_after[i] = y_before[x_before_index] 749 | else: 750 | if x_before_index >= len(x_before) - 1: 751 | y_after[i] = y_before[x_before_index] 752 | else: 753 | x_before_index += 1 754 | y_after[i] = y_before[x_before_index] 755 | 756 | return x_after, y_after 757 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------