Skip to content

DC (Direct Current) Resistivity

Model

We assume the following subsurface resistivity distribution with 4 layers:

Layer #thickness (m)ρ(Ωm)
1100500
22001000
32010
4100

The DCModel is defined as :

julia
ρ = log10.([500.0, 1000.0, 10.0, 100.0])
h = [100.0, 200.0, 20.0]
m = DCModel(ρ, h)
1D DC Resistivity Model
┌───────┬───────┬────────┬────────┐
 Layer  log-ρ       h       z 
  [Ωm]     [m]     [m] 
├───────┼───────┼────────┼────────┤
│     1 │  2.70 │ 100.00 │ 100.00 │
│     2 │  3.00 │ 200.00 │ 300.00 │
│     3 │  1.00 │  20.00 │ 320.00 │
│     4 │  2.00 │      ∞ │      ∞ │
└───────┴───────┴────────┴────────┘
Code for this figure
julia
fig = Figure()
ax = Axis(fig[1, 1]; xlabel="log ρ (locsm)", ylabel="depth (m)", backgroundcolor=(
    :magenta, 0.05))

plot_model!(ax, m)

Warn

Always use Float64 or Float32 types while defining the vectors for resistivity and thickness. Using Int will throw an InexactError, e.g. : InexactError: Int64(4193.453970907305)

Response

To obtain the responses, that is apparent resistivity ρₐ, we first need to define the current source and electrodes (receivers) locations. We provide two most common source receiver configurations

PrISM.get_schlumberger_array Function
julia
get_schlumberger_array(a, n)

returns electrode spacings for Schlumberger array :

M ←na→ A ←a→ B ←na→ N

Arguments

  • a : electrode spacing AB

  • n : vector of numbers corresponding to spacing

Usage : TODO

source
PrISM.get_wenner_array Function
julia
get_wenner_array(a)

returns electrode spacings for Wenner array :

M ←a→ A ←a→ B ←a→ N

Arguments

  • a : vector of electrode spacing AB

Usage

TODO

source

Here, we demonstrate using the Wenner array for a vertical sounding survey:

julia
locs = get_wenner_array(100:50:2000)
(recs = [-50.0 50.0; -75.0 75.0; … ; -975.0 975.0; -1000.0 1000.0], srcs = Float32[-150.0 150.0; -225.0 225.0; … ; -2925.0 2925.0; -3000.0 3000.0])

and then call the forward dispatch as usual to get MTResponse:

julia
resp = forward(m, locs)
DCResponse{Vector{Float64}}([554.0979075768546, 596.0697919837345, 615.6503568706282, 610.9917723112081, 587.7732852578923, 552.6733686004528, 511.28659870238715, 467.72190083775365, 424.7498699564668, 384.09534170725516  …  114.01883696503232, 112.33447601007096, 110.89607729747706, 109.66998284796625, 108.62494609466799, 107.7312362641314, 106.96272141067718, 106.29731596803161, 105.71409354993084, 105.19324807792805])
Code for this figure
julia
fig = Figure() #size = (800, 600))
ax1 = Axis(fig[1, 1]; backgroundcolor=(:magenta, 0.05), aspect=AxisAspect(1))

ab_2 = abs.(locs.srcs[:, 2] .- locs.srcs[:, 1]) ./ 2
plot_response!([ax1], ab_2, resp; plt_type=:scatter)

In-place operations

Mutating forward calls are also supported. This leads to no extra allocations while calculating the forward responses. This also speeds up the performance, though the calculations in the well-optimized forward calls are way more expensive than allocations to get a significant boost here. We now call the in-place variant forward! and provide an MTResponse variable to be overwritten :

julia
forward!(resp, m, locs)

We can compare the allocations made in the two calls:

julia
@allocated forward!(resp, m, locs)
20928
julia
@allocated forward(m, locs)
21312

Benchmarks

While the exact runtimes will vary across processors, the runtimes increase linearly with number of layers.

Benchmark code
julia
n_layers = Int.(exp2.(2:1:6))
locs = get_wenner_array(100:50:2000)

btime_iip = Float64.(zero(n_layers))
btime_oop = Float64.(zero(n_layers))
for i in eachindex(n_layers)
    ρ = 2 .* randn(n_layers[i])
    h = 100 .* rand(n_layers[i]-1)
    m = DCModel(ρ, h)
    resp = forward(m, locs)
    time_oop = @timed begin
        for i in 1:1000
            forward(m, locs)
        end
    end
    btime_oop[i] = time_oop.time/1e3
    forward!(resp, m, locs)
    time_iip = @timed begin
        for i in 1:1000
            forward!(resp, m, locs)
        end
    end
    btime_iip[i] = time_iip.time/1e3
end

fig = Figure()
ax = Axis(fig[1, 1]; xscale=log2, backgroundcolor=(:magenta, 0.05),
    xlabel="no. of layers", ylabel="time (ms)")
lines!(n_layers, btime_oop .* 1e3; label="out-of place forward calls", color=:tomato)
lines!(n_layers, btime_iip .* 1e3; label="in place forward calls",
    linestyle=:dash, color=:steelblue3)
Legend(fig[1, 2], ax)
Warning: Assignment to `ρ` in soft scope is ambiguous because a global variable by the same name exists: `ρ` will be treated as a new local. Disambiguate by using `local ρ` to suppress this warning or `global ρ` to assign to the existing global variable.
@ dc.md:127
Warning: Assignment to `h` in soft scope is ambiguous because a global variable by the same name exists: `h` will be treated as a new local. Disambiguate by using `local h` to suppress this warning or `global h` to assign to the existing global variable.
@ dc.md:128
Warning: Assignment to `m` in soft scope is ambiguous because a global variable by the same name exists: `m` will be treated as a new local. Disambiguate by using `local m` to suppress this warning or `global m` to assign to the existing global variable.
@ dc.md:129
Warning: Assignment to `resp` in soft scope is ambiguous because a global variable by the same name exists: `resp` will be treated as a new local. Disambiguate by using `local resp` to suppress this warning or `global resp` to assign to the existing global variable.
@ dc.md:130

Reproducibility

The above benchmarks were obtained on AMD EPYC 9V74 80-Core Processor