|
| 1 | +abstract type Homogeneous <: EarthAdmittanceFormulation end |
| 2 | + |
| 3 | +struct Kernel{Tγ1, Tγ2, Tμ2} |
| 4 | + "Layer where the source conductor is placed." |
| 5 | + s::Int |
| 6 | + "Layer where the target conductor is placed." |
| 7 | + t::Int |
| 8 | + "Primary field propagation constant (0 = lossless, 1 = air, 2 = earth)." |
| 9 | + Γx::Int |
| 10 | + "Air propagation constant γ₁(ω, μ, σ, ε)." |
| 11 | + γ1::Tγ1 |
| 12 | + "Earth propagation constant γ₂(ω, μ, σ, ε)." |
| 13 | + γ2::Tγ2 |
| 14 | + "Earth magnetic-constant assumption μ₂(μ)." |
| 15 | + μ2::Tμ2 |
| 16 | +end |
| 17 | + |
| 18 | +struct Papadopoulos{Tγ1, Tγ2, Tμ2} <: Homogeneous |
| 19 | + kernel::Kernel{Tγ1, Tγ2, Tμ2} |
| 20 | +end |
| 21 | + |
| 22 | +Papadopoulos(; s::Int = 2, t::Int = 2, Γx::Int = 2, |
| 23 | + γ1 = (ω, μ, σ, ε) -> sqrt(1im * ω * μ * (σ + 1im*ω*ε)), |
| 24 | + γ2 = (ω, μ, σ, ε) -> sqrt(1im * ω * μ * (σ + 1im*ω*ε)), |
| 25 | + μ2 = μ -> μ) = |
| 26 | + Papadopoulos( |
| 27 | + Kernel{typeof(γ1), typeof(γ2), typeof(μ2)}(s, t, Γx, γ1, γ2, μ2), |
| 28 | + ) |
| 29 | + |
| 30 | +get_description(::Papadopoulos) = "Papadopoulos" |
| 31 | +from_kernel(f::Papadopoulos) = f.kernel |
| 32 | + |
| 33 | + |
| 34 | +struct Pollaczek{Tγ1, Tγ2, Tμ2} <: Homogeneous |
| 35 | + kernel::Kernel{Tγ1, Tγ2, Tμ2} |
| 36 | +end |
| 37 | + |
| 38 | +Pollaczek(; s::Int = 2, t::Int = 2, Γx::Int = 0, |
| 39 | + γ1 = (ω, μ, σ, ε) -> 1im * ω * sqrt(μ * ε), |
| 40 | + γ2 = (ω, μ, σ, ε) -> zero(1im * ω), |
| 41 | + μ2 = μ -> oftype(μ, μ₀)) = |
| 42 | + Pollaczek( |
| 43 | + Kernel{typeof(γ1), typeof(γ2), typeof(μ2)}(s, t, Γx, γ1, γ2, μ2), |
| 44 | + ) |
| 45 | + |
| 46 | +get_description(::Pollaczek) = "Pollaczek" |
| 47 | +from_kernel(f::Pollaczek) = f.kernel |
| 48 | + |
| 49 | +struct Images{Tγ1, Tγ2, Tμ2} <: Homogeneous |
| 50 | + kernel::Kernel{Tγ1, Tγ2, Tμ2} |
| 51 | +end |
| 52 | + |
| 53 | +Images(; s::Int = 1, t::Int = 1, Γx::Int = 0, |
| 54 | + γ1 = (ω, μ, σ, ε) -> 1im * ω * sqrt(μ * ε), |
| 55 | + γ2 = (ω, μ, σ, ε) -> zero(1im * ω), |
| 56 | + μ2 = μ -> oftype(μ, μ₀)) = |
| 57 | + Images( |
| 58 | + Kernel{typeof(γ1), typeof(γ2), typeof(μ2)}(s, t, Γx, γ1, γ2, μ2), |
| 59 | + ) |
| 60 | + |
| 61 | +get_description(::Images) = "Electrostatic images" |
| 62 | +from_kernel(f::Images) = f.kernel |
| 63 | + |
| 64 | +# ρ, ε, μ = ws.rho_g, ws.eps_g, ws.mu_g |
| 65 | +# f(h, d, @view(ρ[:,k]), @view(ε[:,k]), @view(μ[:,k]), ws.freq[k]) |
| 66 | + |
| 67 | +# Functor implementation for all homogeneous earth impedance formulations. |
| 68 | +function (f::Homogeneous)( |
| 69 | + form::Symbol, |
| 70 | + h::AbstractVector{T}, |
| 71 | + yij::T, |
| 72 | + rho_g::AbstractVector{T}, |
| 73 | + eps_g::AbstractVector{T}, |
| 74 | + mu_g::AbstractVector{T}, |
| 75 | + freq::T, |
| 76 | +) where {T <: REALSCALAR} |
| 77 | + Base.@nospecialize form |
| 78 | + return form === :self ? f(Val(:self), h, yij, rho_g, eps_g, mu_g, freq) : |
| 79 | + form === :mutual ? f(Val(:mutual), h, yij, rho_g, eps_g, mu_g, freq) : |
| 80 | + throw(ArgumentError("Unknown earth admittance form: $form")) |
| 81 | +end |
| 82 | + |
| 83 | +function (f::Homogeneous)( |
| 84 | + h::AbstractVector{T}, |
| 85 | + yij::T, |
| 86 | + rho_g::AbstractVector{T}, |
| 87 | + eps_g::AbstractVector{T}, |
| 88 | + mu_g::AbstractVector{T}, |
| 89 | + freq::T, |
| 90 | +) where {T <: REALSCALAR} |
| 91 | + return f(Val(:mutual), h, yij, rho_g, eps_g, mu_g, freq) |
| 92 | +end |
| 93 | + |
| 94 | +@inline _to_σ(ρ) = isinf(ρ) ? zero(ρ) : (iszero(ρ) ? inv(zero(ρ)) : inv(ρ)) |
| 95 | +@inline _not(s::Int) = |
| 96 | + (s == 1 || s == 2) ? (3 - s) : |
| 97 | + throw(ArgumentError("s must be 1 or 2")) |
| 98 | + |
| 99 | +@inline _get_layer(z) = |
| 100 | + z > 0 ? 1 : |
| 101 | + (z < 0 ? 2 : throw(ArgumentError("Conductor at interface (h=0) is invalid"))) |
| 102 | + |
| 103 | +@noinline function _layer_mismatch(which::AbstractString, got::Int, expected::Int) |
| 104 | + throw( |
| 105 | + ArgumentError( |
| 106 | + "conductor $which is in layer $got but formulation expects layer $expected", |
| 107 | + ), |
| 108 | + ) |
| 109 | +end |
| 110 | + |
| 111 | +@inline function validate_layers!(f::Homogeneous, h) |
| 112 | + @boundscheck length(h) == 2 || throw(ArgumentError("h must have length 2")) |
| 113 | + ℓ1 = _get_layer(h[1]) |
| 114 | + ℓ2 = _get_layer(h[2]) |
| 115 | + (ℓ1 == f.s) || _layer_mismatch("i (h[1])", ℓ1, f.s) |
| 116 | + (ℓ2 == f.t) || _layer_mismatch("j (h[2])", ℓ2, f.t) |
| 117 | + return nothing |
| 118 | +end |
| 119 | + |
| 120 | +@inline function _bessel_diff(γs, d::T, D::T) where {T} |
| 121 | + zmax = max(abs(γs)*d, abs(γs)*D) |
| 122 | + zmax < T(1e-6) ? log(D/d) : (besselk(0, γs*d) - besselk(0, γs*D)) |
| 123 | +end |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | +@inline function (f::Homogeneous)( |
| 128 | + ::Val{:mutual}, |
| 129 | + h::AbstractVector{T}, |
| 130 | + yij::T, |
| 131 | + rho_g::AbstractVector{T}, |
| 132 | + eps_g::AbstractVector{T}, |
| 133 | + mu_g::AbstractVector{T}, |
| 134 | + freq::T, |
| 135 | +) where {T <: REALSCALAR} |
| 136 | + |
| 137 | + validate_layers!(f, h) |
| 138 | + |
| 139 | + s = f.s # index of source layer |
| 140 | + o = _not(s) # the other layer |
| 141 | + ω = 2π*freq |
| 142 | + nL = length(rho_g) |
| 143 | + μ = similar(mu_g); |
| 144 | + σ = similar(rho_g); |
| 145 | + @inbounds for i in 1:nL |
| 146 | + μ[i] = (i == 1) ? mu_g[i] : f.μ2(mu_g[i]) # μ₂ for earth layers |
| 147 | + σ[i] = _to_σ(rho_g[i]) |
| 148 | + end |
| 149 | + |
| 150 | + # construct propagation constants according to formulation assumptions |
| 151 | + γ = Vector{Complex{T}}(undef, nL) |
| 152 | + @inbounds for i in 1:nL |
| 153 | + γ[i] = (i == 1 ? f.γ1 : f.γ2)(ω, μ[i], σ[i], eps_g[i]) |
| 154 | + end |
| 155 | + γ_s = γ[s]; |
| 156 | + γ_o = γ[o] |
| 157 | + γs_2 = γ_s^2 |
| 158 | + γo_2 = γ_o^2 |
| 159 | + |
| 160 | + # kx from struct: 0:none, 1:air, 2:source layer |
| 161 | + kx_2 = if f.Γx == 0 # precalc squared |
| 162 | + zero(γs_2) |
| 163 | + else |
| 164 | + ℓ = (f.Γx == 1) ? 1 : s |
| 165 | + oftype(γs_2, (ω^2) * μ[ℓ] * eps_g[ℓ]) |
| 166 | + end |
| 167 | + |
| 168 | + # --- Underground,"no capacitive coupling" --- |
| 169 | + # physics: source in EARTH (s=t=2), kx = 0, γ_earth ≈ 0 |
| 170 | + # ⇒ Pe = 0 |
| 171 | + if f.s == 2 && f.t == 2 && isapprox(γ_s, zero(γ_s)) |
| 172 | + return zero(Complex{T}) |
| 173 | + end |
| 174 | + |
| 175 | + # unpack geometry |
| 176 | + @inbounds hi, hj = abs(h[1]), abs(h[2]) |
| 177 | + dij = hypot(yij, hi - hj) # √(y^2 + (hi - hj)^2) - conductor-conductor |
| 178 | + Dij = hypot(yij, hi + hj) # √(y^2 + (hi + hj)^2) - conductor-image |
| 179 | + |
| 180 | + # perfectly conducting earth term in Bessel form |
| 181 | + Λij = _bessel_diff(γ_s, dij, Dij) |
| 182 | + |
| 183 | + # --- Overhead special case --- |
| 184 | + # physics: source in AIR (s=t=1), kx = 0, σ_air ≈ 0, |
| 185 | + # earth propagation constant negligible γ_earth ≈ 0 |
| 186 | + # ⇒ Sij = Tij = 0, Pe = (jω)/(2π(σ_air+jωε_air)) * Λ ≡ (1/(2π ε0)) * Λ |
| 187 | + if f.s == 1 && f.Γx == 0 && isapprox(γ_o, zero(γ_o)) |
| 188 | + return (1im*ω) / (2π*eps_g[1]) * Λij |
| 189 | + end |
| 190 | + |
| 191 | + # precompute scalars for integrand |
| 192 | + a_s = (λ::T) -> sqrt(λ^2 + γs_2 + kx_2) |
| 193 | + a_o = (λ::T) -> sqrt(λ^2 + γo_2 + kx_2) |
| 194 | + μ_s = μ[s] |
| 195 | + μ_o = μ[o] |
| 196 | + H = hi + hj |
| 197 | + |
| 198 | + # earth correction term |
| 199 | + Fij = (λ) -> begin |
| 200 | + as = a_s(λ); |
| 201 | + ao = a_o(λ) |
| 202 | + μ_o * exp(-as * H) / (as*μ_o + ao*μ_s) |
| 203 | + end |
| 204 | + |
| 205 | + # G_ij(λ) |
| 206 | + # G = μ0 μ1 α1 (γ1² - γ0²) e^{-α1 H} / [ (α1 μ0 + α0 μ1)(α1 γ0² μ1 + α0 γ1² μ0) ] |
| 207 | + # here: 0→other (o), 1→source (s) |
| 208 | + Gij = (λ) -> begin |
| 209 | + as = a_s(λ); |
| 210 | + ao = a_o(λ) |
| 211 | + num = μ_o * μ_s * as * (γs_2 - γo_2) * exp(-as * H) |
| 212 | + den = (as*μ_o + ao*μ_s) * (as*γo_2*μ_s + ao*γs_2*μ_o) |
| 213 | + num / den |
| 214 | + end |
| 215 | + |
| 216 | + # S_ij + T_ij in one go: 2∫₀^∞ (Fij+Gij) cos(yij λ) dλ |
| 217 | + integrand = (λ) -> (Fij(λ) + Gij(λ)) * cos(yij * λ) |
| 218 | + Iij, _ = quadgk(integrand, 0.0, Inf; rtol = 1e-8) |
| 219 | + Iij *= 2 |
| 220 | + |
| 221 | + _σ_s = σ[s] + 1im*ω*eps_g[s] # complex conductivity of source layer |
| 222 | + |
| 223 | + return (1im * ω / (2π * _σ_s)) * (Λij + Iij) |
| 224 | + |
| 225 | +end |
0 commit comments