diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
new file mode 100644
index 0000000..b88efc4
--- /dev/null
+++ b/.github/workflows/documentation.yml
@@ -0,0 +1,34 @@
+name: Build documentation
+
+on:
+ push:
+ branches:
+ - main # update to match your development branch (master, main, dev, trunk, ...)
+ tags:
+ - 'v*'
+ pull_request:
+
+jobs:
+ build_docs:
+ permissions:
+ actions: write
+ contents: write
+ pull-requests: read
+ statuses: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: julia-actions/setup-julia@v2
+ with:
+ version: '1'
+ - name: Julia cache
+ if: ${{ !env.ACT }}
+ uses: julia-actions/cache@v2
+ #- uses: julia-actions/cache@v2
+ - name: Install dependencies
+ run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
+ - name: Build docs and deploy
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token
+ DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key
+ run: julia --project=docs/ docs/make.jl
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..1263e0b
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,42 @@
+# Run package tests
+name: Package tests
+
+on:
+ push:
+ branches:
+ - main
+ tags:
+ - 'v*'
+ pull_request:
+
+# needed to allow julia-actions/cache to delete old caches that it has created
+permissions:
+ actions: write
+ contents: read
+
+jobs:
+ run_tests:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ julia-version: ['1'] #['lts', '1', 'pre']
+ julia-arch: [x64] #[x64, x86]
+ os: [ubuntu-latest] #[ubuntu-latest, windows-latest, macOS-latest]
+ exclude:
+ - os: macOS-latest
+ julia-arch: x86
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: julia-actions/setup-julia@v2
+ with:
+ version: ${{ matrix.julia-version }}
+ arch: ${{ matrix.julia-arch }}
+ - name: Julia cache
+ if: ${{ !env.ACT }}
+ uses: julia-actions/cache@v2
+ # - uses: julia-actions/cache@v2
+ - uses: julia-actions/julia-buildpkg@v1
+ - uses: julia-actions/julia-runtest@v1
+ # with:
+ # annotate: true
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 59fdfb3..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-image: julia:1.8 # image comes from Docker hub
-
-before_script:
- # - export
- #- echo $(pwd)
- #- echo $(ls ./)
- ## - julia --project=@. -e 'ENV["JULIA_PKG_USE_CLI_GIT"]=true; using Pkg; Pkg.add(url="https://gitlab.com/JuliaGeoph/GeoPolygons.jl")'
- ## The General registry MUST be added explicitly otherwise only JuliaGeophRegistry will be available.
- - julia --project=@. -e 'import Pkg; Pkg.Registry.add("General"); Pkg.Registry.add( Pkg.RegistrySpec(url="https://gitlab.com/JuliaGeoph/JuliaGeophRegistry"))'
- - julia --project=@. -e 'import Pkg; Pkg.Registry.status()'
- - julia --project=@. -e 'import Pkg; Pkg.build()'
- - apt-get -qq update; apt-get -y install git rsync
-
-
-default:
- stage: test
- script:
- - julia --project=@. -e "import Pkg; Pkg.test(; coverage = false)"
- # - julia --project=test/coverage -e 'import Pkg; Pkg.instantiate()'
- # - julia --project=test/coverage test/coverage/coverage-summary.jl
- rules:
- - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG ) && $DOCUMENTER_KEY
-
-
-pages:
- stage: deploy
- script:
- - julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- - julia --project=docs --color=yes docs/make.jl
- - echo $(ls -1 ./)
- # - git branch -a
- ## the following lines needed to have versioned docs from Documenter.jl
- - mkdir tmppub ; mv .git tmppub/
- - cd tmppub
- - echo $(ls -a)
- - git fetch; git checkout gl-pages ; git pull
- - echo $(pwd)
- - echo $(ls -1 )
- - rsync -rlv --exclude=".git" --exclude=".gitignore" ../tmppub/* ../public/
- - echo $(ls -1 ../public/)
- ## - mv docs/build public # move to the directory picked up by Gitlab pages
- artifacts:
- paths:
- - public
- rules:
- - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG ) && $DOCUMENTER_KEY
-
diff --git a/LICENSE b/LICENSE
index f8ed2d1..c97efcf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2019-2024 Andrea Zunino
+Copyright (c) 2019-2025 Andrea Zunino
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index fc1d2c2..c0695fc 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,18 @@
[](https://ginvlab.github.io/EikonalSolvers.jl/dev)
A library to perform __seismic traveltime__ computations by solving the eikonal equation in two (__2D__) and three dimensions (__3D__) with the possibility of computing the __gradient of a misfit function__ with respect to velocity and source location. The coordinate system can be either regular Cartesian or spherical.
-The forward algorithm is based on a fast marching (FMM) method (2nd order) with a refinement of the grid around the source location. The computation of the gradient relies on the __discrete adjoint__ method.
-
-For historical reasons, alternative solvers are available (poorly maintained), including a first order FMM method and the fast sweeping (FS) method for global updates with different kinds of local stencils. Additionally, a continuos adjoint method to calculate the gradient is also provided.
+The forward algorithm is based on a fast marching (FMM) method (2nd order) with a refinement of the grid around the source location. The computation of the gradients relies on the __discrete adjoint__ method.
Both forward and gradient (adjoint) computations can be run in parallel using either Julia's distributed computing functions for distributed memory or threads for multicore processor. The parallelisation scheme is "by source", distributing calculations for different seismic sources to different processors.
+This code is part of a larger project `G⁻¹Lab` (a superset of HMCLab) targeting probabilistic geophysical inverse problems. Please cite the following papers if you use this code:
+
+* Andrea Zunino, Scott Keating, Andreas Fichtner (2025), **A discrete adjoint method for deterministic and probabilistic eikonal-equation-based inversion of traveltime for velocity and source location**, arXiv preprint arXiv:2501.13532, [https://arxiv.org/abs/2501.13532v1](https://arxiv.org/abs/2501.13532v1).
+
+* Andrea Zunino, Lars Gebraad, Alessandro Ghirotto, Andreas Fichtner (2023), **HMCLab: a framework for solving diverse geophysical inverse problems using the Hamiltonian Monte Carlo method**, Geophysical Journal International, Volume 235, Issue 3, Pages 2979–2991, [https://doi.org/10.1093/gji/ggad403](https://doi.org/10.1093/gji/ggad403)
+
+
## Example of forward calculations in 2D
Here below an example of how to calculate traveltimes at receiver stations in 2D, given a grid geometry and positions of sources and receivers.
@@ -45,10 +50,14 @@ ttimepicks,ttimegrid = eiktraveltime(velmod,grd,coordsrc,coordrec,returntt=true)
```

+Finally, functions are provided to back-trace rays from receivers to sources.
+
+

+
## Example of gradient calculations in 2D
-The gradient of the misfit functional (see documentation) with respect to velocity and source location can be calculated as following. A set of observed traveltimes, error on the measurements and a reference velocity model are also required, see the documentation for a detailed example.
+The **gradient of the misfit functional** (see documentation) with respect to **velocity** and **source location** can be calculated as following. A set of observed traveltimes, error on the measurements and a reference velocity model are also required, see the documentation for a detailed example.
```julia
# calculate the gradient of the misfit function w.r.t. velocity
gradvel,misf = eikgradient(vel0,grd,coordsrc,coordrec,dobs,stdobs,:gradvel)
diff --git a/docs/Project.toml b/docs/Project.toml
index f92ecd3..aa03817 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -1,6 +1,7 @@
[deps]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
+EikonalSolvers = "0631ba50-6b23-11e9-3fc3-378508ba069a"
[compat]
Documenter = ">=0.27"
diff --git a/docs/make.jl b/docs/make.jl
index 2bb650c..b27a036 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -1,7 +1,7 @@
using Documenter, EikonalSolvers
makedocs(modules = [EikonalSolvers],
- repo=Remotes.GitLab("JuliaGeoph","EikonalSolvers.jl"), #"https://gitlab.com/JuliaGeoph/EikonalSolvers.jl/blob/{commit}{path}#{line}",
+ repo=Remotes.GitHub("GinvLab","EikonalSolvers.jl"),
sitename="EikonalSolvers.jl",
authors = "Andrea Zunino",
format = Documenter.HTML(prettyurls=get(ENV,"CI",nothing)=="true"),
@@ -14,30 +14,9 @@ makedocs(modules = [EikonalSolvers],
)
deploydocs(
- repo="gitlab.com/JuliaGeoph/EikonalSolvers.jl.git",
+ repo="github.com/GinvLab/EikonalSolvers.jl.git",
devbranch = "main",
- deploy_config = Documenter.GitLab(),
+ deploy_config = Documenter.GitHubActions(),
branch = "gl-pages",
)
-
-###########################################################
-# GitLab <: DeployConfig
-
-# GitLab implementation of DeployConfig.
-
-# The following environment variables influence the build when using the GitLab configuration:
-
-# DOCUMENTER_KEY: must contain the Base64-encoded SSH private key for the repository. This variable should be set in the GitLab settings. Make sure this variable is marked NOT to be displayed in the build log.
-
-# CI_COMMIT_BRANCH: the name of the commit branch.
-
-# CI_EXTERNAL_PULL_REQUEST_IID: Pull Request ID from GitHub if the pipelines are for external pull requests.
-
-# CI_PROJECT_PATH_SLUG: The namespace with project name. All letters lowercased and non-alphanumeric characters replaced with -.
-
-# CI_COMMIT_TAG: The commit tag name. Present only when building tags.
-
-# CI_PIPELINE_SOURCE: Indicates how the pipeline was triggered.
-
-# The CI_* variables are set automatically on GitLab. More information on how GitLab sets the CI_* variables can be found in the GitLab documentation.
diff --git a/docs/src/images/image10.png b/docs/src/images/image10.png
new file mode 100644
index 0000000..9048c7f
Binary files /dev/null and b/docs/src/images/image10.png differ
diff --git a/docs/src/images/rays2.png b/docs/src/images/rays2.png
new file mode 100644
index 0000000..69046bb
Binary files /dev/null and b/docs/src/images/rays2.png differ
diff --git a/docs/src/index.md b/docs/src/index.md
index 9f09b5c..be5a2e6 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -1,8 +1,4 @@
-
-```@meta
-EditURL = "https://gitlab.com/JuliaGeoph/EikonalSolvers.jl/-/tree/main/docs/src/"
-```
# Contents
```@contents
@@ -19,11 +15,11 @@ For historical reasons, alternative solvers are available (poorly maintained), i
Both forward and gradient (adjoint) computations can be run in paralle using either Julia's distributed computing functions for distributed memory or threads for multicore processor. The parallelisation scheme is "by source", distributing calculations for different seismic sources to different processors.
-This code is part of a larger project `HMCLab` ([^ZuninoGebraadetal2023]) targeting probabilistic geophysical inverse problems. Please cite the following papers if you use this code:
+This code is part of a larger project `G⁻¹Lab` (a superset of HMCLab[^ZuninoGebraadetal2023]) targeting probabilistic geophysical inverse problems. Please cite the following papers if you use this code:
-* Andrea Zunino, Scott Keating, Andreas Fichtner, **Eikonal tomography using the discrete adjoint state method**, in preparation.
+* Andrea Zunino, Scott Keating, Andreas Fichtner (2025), **A discrete adjoint method for deterministic and probabilistic eikonal-equation-based inversion of traveltime for velocity and source location**, arXiv preprint arXiv:2501.13532, [https://arxiv.org/abs/2501.13532v1](https://arxiv.org/abs/2501.13532v1).
-* Andrea Zunino, Lars Gebraad, Alessandro Ghirotto, Andreas Fichtner, **HMCLab: a framework for solving diverse geophysical inverse problems using the Hamiltonian Monte Carlo method**, Geophysical Journal International, Volume 235, Issue 3, December 2023, Pages 2979–2991 [https://doi.org/10.1093/gji/ggad403](https://doi.org/10.1093/gji/ggad403)
+* Andrea Zunino, Lars Gebraad, Alessandro Ghirotto, Andreas Fichtner (2023), **HMCLab: a framework for solving diverse geophysical inverse problems using the Hamiltonian Monte Carlo method**, Geophysical Journal International, Volume 235, Issue 3, Pages 2979–2991, [https://doi.org/10.1093/gji/ggad403](https://doi.org/10.1093/gji/ggad403)
@@ -31,17 +27,18 @@ This code is part of a larger project `HMCLab` ([^ZuninoGebraadetal2023]) target
To install the package simple enter into the package manager mode in Julia by typing "`]`" at the REPL prompt and then use `add`, i.e.,
```
-(v1.8) pkg> add EikonalSolvers
+(v1.11) pkg> registry add https://github.com/Ginvlab/GinvLabRegistry
+(v1.11) pkg> add EikonalSolvers
```
The package will be automatically downloaded from the web and installed.
-Alternatively, use the path where the directory of the package is located, be it remote (GitLab)
+Alternatively, use the path where the directory of the package is located, be it remote (GitHub)
```
-(v1.8) pkg> add https://gitlab.com/JuliaGeoph/EikonalSolvers.jl
+(v1.11) pkg> add https://github.com/GinvLab/EikonalSolvers.jl
```
or local
```
-(v1.8) pkg> add /path/to/EikonalSolvers.jl
+(v1.11) pkg> add ///EikonalSolvers.jl
```
@@ -59,6 +56,7 @@ where ``\tau`` is the travel time, ``x,y,z`` the spatial coordinates and ``v`` t
In the numerical solution to the eikonal equation, there are two major components, the global scheme, which defines the strategy to update to the traveltime on the grid, i.e., the fast marching method and the local scheme, providing the finite difference stencils.
The gradient computations are based on the adjoint state method (see below).
+For details please refer to the paper [^Zuninoetal2025].
## Numerical implementation
@@ -70,7 +68,7 @@ S = \dfrac{1}{2} \sum_i \dfrac{\left( \mathbf{\tau}_i^{\rm{calc}}(\mathbf{v})-\m
```
The gradient of the above functional with respect to the velocity model ``\dfrac{d S}{d \mathbf{v}}`` can be calculated efficiently using the _adjoint_ state method (e.g., [^LeungQian2006], [^TreisterHaber2016]]).
-In this package we employ the __discrete adjoint state method__ to compute both gradients with respect to velocity and source location [^Zuninoetal2024], which takes into account the non-linearity of the forward problem - i.e., _no_ linearisation of the forward model and _no_ rays are employed. The computational cost is almost independent of the number of receivers and, because of the discretization, the result is a "diffuse" sensitivity around the theoretical rays (see an example in the following).
+In this package we employ the __discrete adjoint state method__ to compute both gradients with respect to velocity and source location [^Zuninoetal2025], which takes into account the non-linearity of the forward problem - i.e., _no_ linearisation of the forward model and _no_ rays are employed. The computational cost is almost independent of the number of receivers and, because of the discretization, the result is a "diffuse" sensitivity around the theoretical rays (see an example in the following).
## Exported functions / API
@@ -428,7 +426,7 @@ The gradient is returned as an array where each row contains the partial derivat
[^ZuninoGebraadetal2023]: Zunino A., Gebraad, L., Ghirotto, A. and Fichtner, A., (2023). HMCLab a framework for solving diverse geophysical inverse problems using the Hamiltonian Monte Carlo algorithm, Geophysical Journal International, Volume 235, Issue 3, December 2023, Pages 2979–2991, https://doi.org/10.1093/gji/ggad403.
-[^Zuninoetal2024]: Andrea Zunino, Scott Keating, Andreas Fichtner, Eikonal tomography using the discrete adjoint state method, in preparation.
+[^Zuninoetal2025]: Andrea Zunino, Scott Keating, Andreas Fichtner (2025), **A discrete adjoint method for deterministic and probabilistic eikonal-equation-based inversion of traveltime for velocity and source location, arXiv preprint arXiv:2501.13532.
[^LeungQian2006]: Leung, S. and Qian, J. (2006). An adjoint state method for three-dimensional transmission traveltime tomography using first-arrivals. Communications in Mathematical Sciences, 4(1), 249-266.
diff --git a/examples/test_fwd_grad/test_grad_source_loc.jl b/examples/test_fwd_grad/test_grad_source_loc.jl
index 319481f..2895433 100644
--- a/examples/test_fwd_grad/test_grad_source_loc.jl
+++ b/examples/test_fwd_grad/test_grad_source_loc.jl
@@ -5,145 +5,165 @@ using GLMakie
-# create the Grid2D struct
-hgrid = 180.73
-grd = Grid2DCart(hgrid=hgrid,xinit=0.0,yinit=0.0,nx=380,ny=260)
-# nsrc = 4
-# coordsrc = [hgrid.*LinRange(10.0,190.0,nsrc) hgrid.*100.0.*ones(nsrc)] # coordinates of the sources (4 sources)
-
-nsrc = 1
-nrec = 5
-#coordrec = [[hgrid.*LinRange(5.73,grd.nx-7.34,nrec) hgrid.*5.7.*ones(nrec)] for i=1:nsrc] # coordinates of the receivers (10 receivers)
-
-nrec = 5
-coordrec = [[hgrid.*LinRange(5.73,grd.nx-7.34,nrec) hgrid.*5.7.*ones(nrec)] for i=1:nsrc] # coordinates of the receivers (10 receivers)
-#@show coordrec
-coordrec = [vcat(coordrec...,
- [[hgrid.*LinRange(5.73,grd.nx-7.34,nrec) hgrid.*(grd.ny-10)*ones(nrec)] for i=1:nsrc]... )]
-
-
-@show coordrec
-
-velmod = 2500.0 .* ones(grd.nx,grd.ny) # velocity model
-if true
- # ## increasing velocity with depth...
- for i=1:grd.ny
- velmod[:,i] = 0.034 * i .+ velmod[:,i]
- end
- # velmod = velmod[:,end:-1:1]
- # velmod .+= 1.0.*rand(size(velmod))
- # for i=1:grd.nx
- # velmod[i,:] = 0.034 * i .+ velmod[i,:]
- # end
-else
- println("\n=== Constant velocity model ===\n")
-end
-
-gradtype = :gradvelandsrcloc #:gradvel # :gradvelandsrcloc # :gradvel
-parallelkind = :serial #:sharedmem
-
-
-for refinesrc in [false,true]
-
- extraparams=ExtraParams(parallelkind=parallelkind,
- refinearoundsrc=refinesrc,
- radiussmoothgradsrc=0)
-
- println("\n######## Refinement of the source region? $refinesrc ###########")
-
- #coordsrc = [hgrid*15.0 hgrid*(grd.ny-15.0)]
- #coordsrc = [hgrid*5.4 hgrid*(grd.ny-3.2)]
- coordsrc1 = [hgrid*37.8 hgrid*(grd.ny-28.4)]
- #coordsrc = [hgrid*5.39123 hgrid*(grd.ny-3.1123)]
-
- println("\n-------------- forward ----------------")
- # run the traveltime computation with default algorithm ("ttFMM_hiord")
- ttpicks = eiktraveltime(velmod,grd,coordsrc1,coordrec,
- extraparams=extraparams)
-
-
- # standard deviation of error on observed data
- stdobs = [0.05.*ones(size(ttpicks[1])) for i=1:nsrc]
- # generate a "noise" array to simulate real data
- #noise = [stdobs[i].^2 .* randn(size(stdobs[i])) for i=1:nsrc]
- # add the noise to the synthetic traveltime data
- dobs = ttpicks #.+ noise
-
-
- # # create a guess/"current" model
- #vel0 = copy(velmod)
- vel0 = copy(velmod)
- @assert velmod==vel0
- # vel0 = 2.3 .* ones(grd.nx,grd.ny)
- # nsrc = 1
- coordsrc2 = [hgrid*22.53 hgrid*(grd.ny-38.812)]
- #coordsrc = [hgrid*5.4 hgrid*(grd.ny-3.2)]
-
-
- println("\n-------------- gradient ----------------")
- ## calculate the gradient of the misfit function
- gradvel,∂χ∂xysrc = eikgradient(vel0,grd,coordsrc2,coordrec,dobs,stdobs,
- gradtype,extraparams=extraparams)
+function rungradsrcloc()
+ # create the Grid2D struct
+ hgrid = 10.0
+ grd = Grid2DCart(hgrid=hgrid,cooinit=(0.0,0.0),grsize=(380,260))
+ #nsrc = 3
+ # nrec = 2
+ # coordrec = [[hgrid.*LinRange(5.73,grd.grsize[1]-7.34,nrec) hgrid.*5.7.*ones(nrec)] for i=1:nsrc]
+ #coordrec = [vcat(coordrec...,
+ # [[hgrid.*LinRange(5.73,grd.grsize[1]-7.34,nrec) hgrid.*(grd.grsize[2]-10)*ones(nrec)] for i=1:nsrc]... )]
- println("\n-------------- misfit ----------------")
-
- dh = 0.00001
-
- coordsrc_plusdx = coordsrc2 .+ [dh 0.0]
- misf_pdx = eikttimemisfit(vel0,dobs,stdobs,coordsrc_plusdx,coordrec,grd;
- extraparams=extraparams)
-
- coordsrc_minusdx = coordsrc2 .+ [-dh 0.0]
- misf_mdx = eikttimemisfit(vel0,dobs,stdobs,coordsrc_minusdx,coordrec,grd;
- extraparams=extraparams)
-
- coordsrc_plusdy = coordsrc2 .+ [0.0 dh]
- misf_pdy = eikttimemisfit(vel0,dobs,stdobs,coordsrc_plusdy,coordrec,grd;
- extraparams=extraparams)
-
- coordsrc_minusdy = coordsrc2 .+ [0.0 -dh]
- misf_mdy = eikttimemisfit(vel0,dobs,stdobs,coordsrc_minusdy,coordrec,grd;
- extraparams=extraparams)
-
-
- ∂χ∂x_src_FD = (misf_pdx-misf_mdx)/(2*dh)
- ∂χ∂y_src_FD = (misf_pdy-misf_mdy)/(2*dh)
- ∂χ∂xysrc_FD = [∂χ∂x_src_FD ∂χ∂y_src_FD]
-
- # @show misf_ref
- # @show misf_pdx,misf_mdx
- # @show misf_pdy,misf_mdy
-
-
- if ∂χ∂xysrc!=nothing
- @show ∂χ∂xysrc
- @show ∂χ∂xysrc_FD
- @show ∂χ∂xysrc.-∂χ∂xysrc_FD
+ velmod = 2500.0 .* ones(grd.grsize...) # velocity model
+ if true
+ # ## increasing velocity with depth...
+ for i=1:grd.grsize[2]
+ velmod[:,i] = 0.034 * i .+ velmod[:,i]
+ end
+ # velmod = velmod[:,end:-1:1]
+ # velmod .+= 1.0.*rand(size(velmod))
+ # for i=1:grd.nx
+ # velmod[i,:] = 0.034 * i .+ velmod[i,:]
+ # end
else
- println("No ∂χ∂xysrc requested.")
+ println("\n=== Constant velocity model ===\n")
end
- # println()
- # @show extrema(gradvel)
-
- ##
- if true
- fig = Figure(size=(1000,800))
- ax1 = Axis(fig[1,1],title="Refinement $refinesrc")
- vmax = maximum(abs.(gradvel))
- colorrange = (-vmax,vmax)
- hm = heatmap!(ax1,gradvel,colormap=:balance,colorrange=colorrange)
- Colorbar(fig[1,2], hm)
- ax1.yreversed = true
+ gradtype = :gradvelandsrcloc #:gradvel # :gradvelandsrcloc # :gradvel
+ parallelkind = :serial #:sharedmem
+
+ @show extrema(grd.x)
+ @show extrema(grd.y)
+
+ for refinesrc in [false,true]
+
+ extraparams=ExtraParams(parallelkind=parallelkind,
+ refinearoundsrc=refinesrc,
+ radiussmoothgradsrc=0)
+
+ println("\n######## Refinement of the source region? $refinesrc ###########")
+
+ coordsrc1 = [#hgrid*127.8 hgrid*(grd.grsize[2]-28.4);
+ #hgrid*245.39123 hgrid*(grd.grsize[2]-193.1123);
+ hgrid*315.120 hgrid*(grd.grsize[2]-15.230)]
+
+ nrec = 4
+ coordrec = [[hgrid.*LinRange(5.73,grd.grsize[1]-7.34,nrec) hgrid.*5.7.*ones(nrec)]
+ for i=1:size(coordsrc1,1)]
+ #coordrec = [[hgrid.*grd.grsize[1]-300.34 hgrid.*5.7.*ones(1)] for i=1:size(coordsrc1,1)]
+
+ println("\n-------------- forward ----------------")
+ # run the traveltime computation with default algorithm ("ttFMM_hiord")
+ ttpicks = eiktraveltime(velmod,grd,coordsrc1,coordrec,
+ extraparams=extraparams)
+
+ # standard deviation of error on observed data
+ stdobs = [0.05.*ones(size(ttpicks[1])) for i=1:size(coordsrc1,1)]
+ # generate a "noise" array to simulate real data
+ #noise = [stdobs[i] .* randn(size(stdobs[i])) for i=1:nsrc]
+ # add the noise to the synthetic traveltime data
+ dobs = ttpicks #.+ noise
+
+
+ # # create a guess/"current" model
+ #vel0 = copy(velmod)
+ vel0 = copy(velmod)
+ @assert velmod==vel0
+ # vel0 = 2.3 .* ones(grd.nx,grd.ny)
+ # nsrc = 1
+ coordsrc2 = [#hgrid*22.53 hgrid*(grd.grsize[2]-38.812);
+ #hgrid*260.4 hgrid*(grd.grsize[2]-123.2);
+ #hgrid*280.230 hgrid*(grd.grsize[2]-28.0);
+ hgrid*280.0 2320.00]
+ @show coordsrc2
- #display(GLMakie.Screen(),fig)
- display(fig)
- #sleep(5)
+ println("\n-------------- gradient ----------------")
+ ## calculate the gradient of the misfit function
+ gradvel,∂χ∂xysrc,misf = eikgradient(vel0,grd,coordsrc2,coordrec,dobs,stdobs,
+ gradtype,extraparams=extraparams)
+
+
+ @show coordsrc1
+ @show coordsrc2
+
+ println("\n-------------- misfit ----------------")
+
+ dh = 0.0001
+ @show dh
+
+ ∂χ∂xysrc_FD = zeros(size(coordsrc2,1),2)
+ for s=1:size(coordsrc2,1)
+
+ @show s
+ @show coordsrc2[s:s,:].-coordsrc1[s:s,:]
+ @show dobs[s:s]
+ @show stdobs[s:s]
+
+ coordsrc_plusdx = coordsrc2[s:s,:] .+ [dh 0.0]
+ misf_pdx = eikttimemisfit(vel0,grd,coordsrc_plusdx,coordrec[s:s],dobs[s:s],stdobs[s:s];
+ extraparams=extraparams)
+
+ coordsrc_minusdx = coordsrc2[s:s,:] .+ [-dh 0.0]
+ misf_mdx = eikttimemisfit(vel0,grd,coordsrc_minusdx,coordrec[s:s],dobs[s:s],stdobs[s:s];
+ extraparams=extraparams)
+
+ coordsrc_plusdy = coordsrc2[s:s,:] .+ [0.0 dh]
+ misf_pdy = eikttimemisfit(vel0,grd,coordsrc_plusdy,coordrec[s:s],dobs[s:s],stdobs[s:s];
+ extraparams=extraparams)
+
+ coordsrc_minusdy = coordsrc2[s:s,:] .+ [0.0 -dh]
+ misf_mdy = eikttimemisfit(vel0,grd,coordsrc_minusdy,coordrec[s:s],dobs[s:s],stdobs[s:s];
+ extraparams=extraparams)
+
+ ∂χ∂x_src_FD = (misf_pdx-misf_mdx)/(2*dh)
+ ∂χ∂y_src_FD = (misf_pdy-misf_mdy)/(2*dh)
+ ∂χ∂xysrc_FD[s,:] .= (∂χ∂x_src_FD,∂χ∂y_src_FD)
+
+ @show dh,grd.hgrid
+ @show misf_pdx,misf_mdx
+ @show misf_pdy,misf_mdy
+
+ end
+
+ # @show misf_ref
+ # @show misf_pdx,misf_mdx
+ # @show misf_pdy,misf_mdy
+
+
+ println("∂χ∂xysrc:")
+ display(∂χ∂xysrc)
+ println("∂χ∂xysrc_FD:")
+ display(∂χ∂xysrc_FD)
+ println("∂χ∂xysrc.-∂χ∂xysrc_FD:")
+ display(∂χ∂xysrc.-∂χ∂xysrc_FD)
+
+
+ ##
+ if true
+ fig = Figure(size=(1000,800))
+ ax1 = Axis(fig[1,1],title="Refinement $refinesrc")
+ vmax = maximum(abs.(gradvel))
+ colorrange = (-vmax,vmax)
+ hm = heatmap!(ax1,grd.x,grd.y,gradvel,colormap=:balance,colorrange=colorrange)
+ Colorbar(fig[1,2], hm)
+
+ for i=1:size(coordsrc1,1)
+ scatter!(ax1,coordsrc1[i,1],coordsrc1[i,2],color=:black)
+ scatter!(ax1,coordsrc2[i,1],coordsrc2[i,2],color=:red)
+ end
+
+ #ax1.yreversed = true
+
+ #display(GLMakie.Screen(),fig)
+ display(fig)
+ sleep(3)
+ end
+
end
+ return
end
-
-
diff --git a/examples/test_fwd_grad/test_grad_vel.jl b/examples/test_fwd_grad/test_grad_vel.jl
index 90cf77d..63253b7 100644
--- a/examples/test_fwd_grad/test_grad_vel.jl
+++ b/examples/test_fwd_grad/test_grad_vel.jl
@@ -12,10 +12,8 @@ function rungradvel2d()
# create the Grid2D struct
hgrid = 50.0
grd = Grid2DCart(hgrid=hgrid,
- xinit=0.0,
- yinit=0.0,
- nx=50,
- ny=30)
+ cooinit=(0.0,0.0),
+ grsize=(50,30))
nx,ny = grd.grsize
nrec = 7
@@ -31,7 +29,7 @@ function rungradvel2d()
velmod[:,i] = 23.6 * i .+ velmod[:,i]
end
- srcongrid = false
+ srcongrid = true
if srcongrid
println(" Source on a grid point")
coordsrc1 = [hgrid*(nx÷2) hgrid*(ny÷2)]
@@ -43,7 +41,7 @@ function rungradvel2d()
compare_adj_FD = true
refinesrc = [false,true]
- gradvel_all = Vector{Any}(undef,2)
+ gradvel_all = Vector{Matrix{Float64}}(undef,2)
gradvel_FD_all = [similar(velmod), similar(velmod)]
for col=1:2
@@ -77,15 +75,15 @@ function rungradvel2d()
println("\n-------------- gradient ----------------")
## calculate the gradient of the misfit function
- gradvel_all[col] = eikgradient(vel0,grd,coordsrc2,coordrec,dobs,stdobs,
- :gradvel,extraparams=extraparams)
+ gradvel_all[col],misf = eikgradient(vel0,grd,coordsrc2,coordrec,dobs,stdobs,
+ :gradvel,extraparams=extraparams)
## Compare with brute-force finite differences calculation
dh = 0.001
@show dh
- gradvel_FD = similar(gradvel_all[col])
+ gradvel_FD = similar(velmod)
for j=1:ny
for i=1:nx
if i%10 == 0
@@ -95,9 +93,9 @@ function rungradvel2d()
velpdh[i,j] += dh
velmdh = copy(vel0)
velmdh[i,j] -= dh
- misf_pdh = eikttimemisfit(velpdh,dobs,stdobs,coordsrc2,coordrec,grd;
+ misf_pdh = eikttimemisfit(velpdh,grd,coordsrc2,coordrec,dobs,stdobs;
extraparams=extraparams)
- misf_mdh = eikttimemisfit(velmdh,dobs,stdobs,coordsrc2,coordrec,grd;
+ misf_mdh = eikttimemisfit(velmdh,grd,coordsrc2,coordrec,dobs,stdobs;
extraparams=extraparams)
gradvel_FD_all[col][i,j] = (misf_pdh-misf_mdh)/(2*dh)
end
@@ -177,4 +175,4 @@ function rungradvel2d()
#sleep(5)
return
-end # function
+end
diff --git a/src/EikSolv/eikchecks.jl b/src/EikSolv/eikchecks.jl
index 187e9ca..caecc67 100644
--- a/src/EikSolv/eikchecks.jl
+++ b/src/EikSolv/eikchecks.jl
@@ -2,8 +2,8 @@
function checksrcrecposition(grd::Grid2DCart,coordsrc,coordrec)
- @assert size(coordsrc,1)==length(coordrec)
- @assert all(grd.x[1].<=coordsrc[:,1].<=grd.x[end])
+ @assert size(coordsrc,1)==length(coordrec)
+ @assert all(grd.x[1].<=coordsrc[:,1].<=grd.x[end])
@assert all(grd.y[1].<=coordsrc[:,2].<=grd.y[end])
for r=1:length(coordrec)
@assert all(grd.x[1].<=coordrec[r][:,1].<=grd.x[end])
diff --git a/src/EikSolv/eikmainfwd.jl b/src/EikSolv/eikmainfwd.jl
index d8b3a64..7c949eb 100644
--- a/src/EikSolv/eikmainfwd.jl
+++ b/src/EikSolv/eikmainfwd.jl
@@ -1302,7 +1302,7 @@ function sourceboxloctt!(fmmvars::AbstractFMMVars,vel::Array{Float64,N},srcpos::
end
whichdir = findall(issrchalfway)
- @warn "Shifting the source position along dimension(s) $(whichdir) by $(round(srcshift,sigdigits=3)) [1e-4*grd.hgrid] "
+ @warn "sourceboxloctt(): Shifting the source position along dimension(s) $(whichdir) by $(round(srcshift,sigdigits=3)) [1e-4*grd.hgrid] "
fmmvars.srcboxpar.xyzsrc .= new_srcpos
for l=1:Ncorn
diff --git a/src/EikSolv/eikmaingrad.jl b/src/EikSolv/eikmaingrad.jl
index d53fea5..2887423 100644
--- a/src/EikSolv/eikmaingrad.jl
+++ b/src/EikSolv/eikmaingrad.jl
@@ -47,10 +47,38 @@ function eikgradient(vel::Array{Float64,N},
# some checks
@assert 2<=ndims(vel)<=3
@assert all(vel.>0.0)
+ @assert length(coordrec)==length(pickobs)
+ begin
+ for r=1:length(coordrec)
+ @assert size(coordrec[r],1)==size(pickobs[r],1) "size(coordrec[r],1): $(size(coordrec[r],1)) \
+size(pickobs[r],1): $(size(pickobs[r],1))"
+ @assert size(stdobs[r])==size(pickobs[r])
+ end
+ end
checksrcrecposition(grd,coordsrc,coordrec)
-
+
nsrc = size(coordsrc,1)
+ ##------------------------------
+ ## deal with the case of the source location being
+ ## exactly aligned with one or more axes
+ # shift = 1e-5*grd.hgrid
+ # coordsrc = zeros(eltype(inpcoordsrc),size(inpcoordsrc)...)
+ # for s=1:nsrc
+ # srcpos = inpcoordsrc[s,:]
+ # for d=1:size(coordsrc,2) # loop over axes
+ # align = mod(srcpos[d],grd.hgrid)
+ # if align <= eps(eltype(srcpos))
+ # coordsrc[s,d] = inpcoordsrc[s,d] + shift
+ # @warn "Shifting source position along axis $d by $shift to prevent issues."
+ # else
+ # coordsrc[s,d] = inpcoordsrc[s,d]
+ # end
+ # end
+ # end
+ ##------------------------------
+
+
if extraparams.parallelkind==:distribmem
##====================================
## Distributed memory
@@ -95,8 +123,10 @@ function eikgradient(vel::Array{Float64,N},
Threads.@threads for s=1:nchu
igrs = grpsrc[s,1]:grpsrc[s,2]
- ∂ψ∂vel_all[s],∂ψ∂xyzsrc[igrs,:],ttmisfsrc[s] = calcgradsomesrc(vel,coordsrc[igrs,:],coordrec[igrs],
- grd,stdobs[igrs],pickobs[igrs],
+ ∂ψ∂vel_all[s],∂ψ∂xyzsrc[igrs,:],ttmisfsrc[s] = calcgradsomesrc(vel,coordsrc[igrs,:],
+ coordrec[igrs],
+ grd,stdobs[igrs],
+ pickobs[igrs],
whichgrad,extraparams )
end
∂ψ∂vel = sum(∂ψ∂vel_all)
@@ -311,8 +341,7 @@ function calcgrads_singlesrc!(gradvel1::Union{AbstractArray{Float64},Nothing},
∂fi_∂us = sum( twoDttDS )
##
∂ψ_∂u_s = transpose(P_Areg) * fact2∂ψ∂u
- ∂ψ_∂u_p = transpose(∂fi_∂us) * lambda_fmmord_coarse
-
+ ∂ψ∂up_∂up∂us = transpose(∂fi_∂us) * lambda_fmmord_coarse
####################################################
@@ -337,12 +366,11 @@ function calcgrads_singlesrc!(gradvel1::Union{AbstractArray{Float64},Nothing},
## at the "onsrc" points
## compute gradient
##############################################
-
+
if refinearoundsrc==false
dus_dsr = calc_dus_dsr(lambda_fmmord_coarse,
velcart,
∂ψ_∂u_s,
- ∂ψ_∂u_p,
fmmvars.srcboxpar,
grd,
xyzsrc )
@@ -358,8 +386,15 @@ function calcgrads_singlesrc!(gradvel1::Union{AbstractArray{Float64},Nothing},
end
- #dψ_ds_r = transpose(dus_dsr) * ∂ψ_∂u_p + transpose(dus_dsr) * ∂ψ_∂u_s
- gradsrcpos1 .= transpose(dus_dsr) * ∂ψ_∂u_p + transpose(dus_dsr) * ∂ψ_∂u_s
+ # println("\ndus_dsr:")
+ # display(dus_dsr)
+ # println("\n∂ψ∂up_∂up∂us:")
+ # display(∂ψ∂up_∂up∂us)
+ # println("\n∂ψ_∂u_s:")
+ # display(∂ψ_∂u_s)
+
+ #dψ_ds_r = transpose(dus_dsr) * ∂ψ∂up_∂up∂us + transpose(dus_dsr) * ∂ψ_∂u_s
+ gradsrcpos1 .= transpose(dus_dsr) * ∂ψ∂up_∂up∂us + transpose(dus_dsr) * ∂ψ_∂u_s
end
@@ -420,7 +455,7 @@ function calcgrads_singlesrc!(gradvel1::Union{AbstractArray{Float64},Nothing},
# Gradient with respect to velocity, term T2Va
##===============================================
## FMM ordering
- T2Va = transpose( dus_dva ) * ∂ψ_∂u_p
+ T2Va = transpose( dus_dva ) * ∂ψ∂up_∂up∂us
#T2Va = transpose(lambda_fmmord_coarse) * tmpdfidva
## (i,j) indices of the source points
ijksrcreg = fmmvars.srcboxpar.ijksrc
@@ -515,7 +550,6 @@ function computestuff_finegrid!(fmmvars,adjvars,grd,srcrefvars)
@assert findfirst( adjvars.fmmord.ttime .< 0.0)-1 == Naccpts
## extract computed traveltime values
tt_fmmord = adjvars.fmmord.ttime[1:Naccpts]
- #@show size(tt_fmmord)
onsrccols = adjvars.fmmord.onsrccols[1:Naccpts]
nptsonsrc = count(onsrccols)
@assert length(onsrccols)==length(tt_fmmord)
@@ -575,8 +609,6 @@ function computestuff_finegrid!(fmmvars,adjvars,grd,srcrefvars)
#######################################################
## right hand side adj eq. == -(adjoint source)^T
#######################################################
- # @show size(H)
- # @show size(.!adjvars.fmmord.onsrccols[1:Naccpts])
# bool vector with false for src columns
# only part of the rectangular fine grid has been used, so [1:Naccpts]
rq = .!onsrccols
@@ -691,11 +723,8 @@ function calc_duh_dva_finegrid!(lambda_fmmord_fine,
qorig = srcrefvars.nearneigh_idxcoarse[tmpqorig]
for p=1:Naccpts
- #i = p
# use the "full" indices (including points on source)
porig = idxconv.lfmm2grid[p]
-
- #@show (p,j),q,(porig,qorig)
## extract the proper elements from the interpolation
## matrix in FMM order
dwq_dva[p,j] = nneigh[porig,qorig]
@@ -1111,7 +1140,6 @@ Calculates the derivative of the misfit function with respect to the traveltime
function calc_dus_dsr(lambda_coarse::AbstractArray{Float64},
velcart_coarse::Array{Float64,N},
∂ψ_∂u_s::AbstractVector{Float64},
- ∂ψ_∂u_p::AbstractVector{Float64},
srcboxpar_coarse,
grd::AbstractGridEik,
xyzsrc::Vector{Float64} ) where N
@@ -1145,7 +1173,7 @@ function calc_dus_dsr(lambda_coarse::AbstractArray{Float64},
velsrc = velcart_coarse[ijksrc[p,1],ijksrc[p,2],ijksrc[p,3]]
end
## get the derivative
- dus_dsr[p,:] .= partderivttsrcpos(xyzpt,xyzsrc,velsrc)
+ dus_dsr[p,:] .= partderivttsrcpos(xyzpt,xyzsrc,velsrc,grd.hgrid)
end
return dus_dsr
@@ -1191,7 +1219,7 @@ function calc_dus_dsr_finegrid(lambda_fmmord_fine,
velsrc = srcrefvars.velcart_fine[ijksrc[p,1],ijksrc[p,2],ijksrc[p,3]]
end
## get the derivative
- dtaus_dsr[p,:] .= partderivttsrcpos(xyzpt,xyzsrc,velsrc)
+ dtaus_dsr[p,:] .= partderivttsrcpos(xyzpt,xyzsrc,velsrc,grd_fine.hgrid)
end
trm1 = ∂u_h_dtau_s * dtaus_dsr
@@ -1204,18 +1232,42 @@ end
###########################################################################
-function partderivttsrcpos(xyzpt::AbstractVector,xyzsrc::AbstractVector,vel::Real)
-
- #denom = vel * sqrt((xpt - xsrc)^2 + (ypt - ysrc)^2)
- denom = vel .* sqrt.( sum((xyzpt.-xyzsrc).^2) )
- @assert denom!=0.0 "partderivttsrcpos2D(): Source position and grid node position coincide."
- # deriv_x = -(xpt - xsrc) / denom
- # deriv_y = -(ypt - ysrc) / denom
+function partderivttsrcpos(xyzpt::AbstractVector,inpxyzsrc::AbstractVector,vel::AbstractFloat,
+ hgrid::AbstractFloat)
Ndim = length(xyzpt)
deriv_xyz = zeros(Ndim)
+
+ ##---------------------------------------------
+ ## deal with the case of the source location being
+ ## exactly aligned with one or more axes
+ shift = 1e-4*hgrid
+ xyzsrc = zeros(eltype(inpxyzsrc),size(inpxyzsrc)...)
+
+ for d=1:Ndim # loop over axes
+ align = mod(inpxyzsrc[d],hgrid)
+ if align <= eps(eltype(inpxyzsrc))
+ xyzsrc[d] = inpxyzsrc[d] + shift
+ @warn "partderivttsrcpos(): Shifting source position along axis $d by $shift, issues may arise."
+ else
+ xyzsrc[d] = inpxyzsrc[d]
+ end
+ end
+ ##---------------------------------------------
+
+ denom = vel .* sqrt.( sum((xyzpt.-xyzsrc).^2) )
+
+ if denom==0.0
+ @warn "partderivttsrcpos2D(): Source position and grid node position coincide."
+ deriv_xyz .= 0.0
+ return deriv_xyz
+ end
+
for d=1:Ndim
deriv_xyz[d] = - (xyzpt[d]-xyzsrc[d]) / denom
end
+
+ # deriv_x = -(xpt - xsrc) / denom
+ # deriv_y = -(ypt - ysrc) / denom
return deriv_xyz
end
diff --git a/test/runtests.jl b/test/runtests.jl
index 76eea3c..f01d6b2 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -36,8 +36,7 @@ end
@testset "Eikonal vs. analytical solutions const. vel. [3D Cart. coord.]" begin
test_fwdtt_3D_constvel()
end
-<<<<<<< HEAD
-=======
+
@testset "Eikonal vs. analytical sol., lin. grad. vel. [3D Cart. coord.]" begin
test_fwdtt_3D_lingrad()
end
@@ -48,4 +47,4 @@ end
@testset "Gradient w.r.t. source loc. vs. finite differences [3D Cart. coord.]" begin
test_gradsrc_3D()
end
->>>>>>> Ndim_fwdgrad
+