Skip to content

Commit

Permalink
Add refractive phase screen + power law
Browse files Browse the repository at this point in the history
  • Loading branch information
Anna Tartaglia committed Aug 6, 2023
1 parent e7acef8 commit d967678
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 24 deletions.
14 changes: 10 additions & 4 deletions Manifest.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This file is machine-generated - editing it directly is not advised

julia_version = "1.9.2"
julia_version = "1.9.1"
manifest_format = "2.0"
project_hash = "f648336fc92b203dcb3c3ecf1c7c8cd846d52618"
project_hash = "3180f76478e901329031e093daffb4430881bf4c"

[[deps.ADTypes]]
git-tree-sha1 = "f5c25e8a5b29b5e941b7408bc8cc79fea4d9ef9a"
Expand Down Expand Up @@ -236,7 +236,7 @@ weakdeps = ["Dates", "LinearAlgebra"]
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.5+0"
version = "1.0.2+0"

[[deps.CompositionsBase]]
git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad"
Expand Down Expand Up @@ -1051,7 +1051,7 @@ version = "1.3.0"
[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
version = "1.9.2"
version = "1.9.0"

[[deps.PolarizedTypes]]
deps = ["ChainRulesCore", "DocStringExtensions", "PrecompileTools", "StaticArrays"]
Expand Down Expand Up @@ -1364,6 +1364,12 @@ git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d"
uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
version = "1.4.2"

[[deps.StationaryRandomFields]]
deps = ["DocStringExtensions", "FFTW", "Random", "Statistics"]
git-tree-sha1 = "3b16028fbd13839fb0356adf34511810c706e922"
uuid = "6ab34832-f7e5-40a4-9cd7-47ea82b5c144"
version = "0.1.0"

[[deps.Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Expand Down
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ ComradeBase = "6d8c423b-a35f-4ef1-850c-862fe21f82c4"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
EHTImages = "a1be09f2-4a9d-4f8b-a1e3-812015e25e30"
EHTUtils = "9d0fa6a6-ae25-4c2e-8152-6c4b7f2016aa"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
HypergeometricFunctions = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
StationaryRandomFields = "6ab34832-f7e5-40a4-9cd7-47ea82b5c144"
VLBISkyModels = "d6343c73-7174-4e0f-bb64-562643efbeca"

[compat]
Expand Down
7 changes: 7 additions & 0 deletions src/ScatteringOptics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import ComradeBase:
IsPolarized, NotPolarized,
visanalytic, imanalytic, isprimitive, ispolarized,
visibility_point, flux, radialextent
using StationaryRandomFields
using DocStringExtensions
using EHTUtils: mas2rad
using HypergeometricFunctions
using NonlinearSolve
using QuadGK
using SpecialFunctions
using Random
using FFTW

# kζ finders
include("./kzetafinders/abstractkzetafinder.jl")
Expand All @@ -34,4 +37,8 @@ include("./scatteringmodels/models/dipole.jl")
include("./scatteringmodels/models/periodicboxcar.jl")
include("./scatteringmodels/models/vonmises.jl")

# phase screen
include("./scatteringmodels/phasescreens/powerlaw.jl")
include("./scatteringmodels/phasescreens/refractivemodel.jl")

end
4 changes: 2 additions & 2 deletions src/scatteringmodels/abstractscatteringmodel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Furthermore the following parameters need to be precomputed.
"""
abstract type AbstractScatteringModel end

function (::AbstractScatteringModel, ϕ::Number) end
#function Pϕ(::AbstractScatteringModel, ϕ::Number) end

"""
Dmaj(r, sm::AbstractScatteringModel)
Expand Down Expand Up @@ -116,7 +116,7 @@ Masm exact phase structure function Dϕ(r, ϕ) at observing wavelength `λ`, fir
@inline function Dϕ_exact(sm::AbstractScatteringModel, λ::Number, x::Number, y::Number)
r = (x^2 + y^2)
ϕ = atan(y, x)
return quadgk(ϕq -> dDϕ_dz(sm, λ, r, ϕ, ϕq) * sm.(ϕq), 0, 2π)[1]
return quadgk(ϕq -> dDϕ_dz(sm, λ, r, ϕ, ϕq) * (sm,ϕq), 0, 2π)[1]
end


Expand Down
1 change: 1 addition & 0 deletions src/scatteringmodels/commonfunctions.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# constants
const FWHMfac = (2 * log(2)) / π
const c_cgs = 29979245800.0
const kpc_tp_cm = 3.0857e21

"""
Best-fit parameters of the dipole scattering model derived in Johnson et al. 2018
Expand Down
17 changes: 11 additions & 6 deletions src/scatteringmodels/models/dipole.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ The default numbers are based on the best-fit parameters presented in Johnson et
- `θmin_nas::Number`: FWHM in mas of the minor axis angular broadening at the specified reference wavelength.
- `ϕ_deg::Number`: The position angle of the major axis of the scattering in degree.
- `λ0_cm::Number`: The reference wavelength for the scattering model in cm.
- `D_pc::Number`: The distance from the observer to the scattering screen in pc.
- `R_pc::Number`: The distance from the source to the scattering screen in pc.
- `D_kpc::Number`: The distance from the observer to the scattering screen in kpc.
- `R_kpc::Number`: The distance from the source to the scattering screen in kpc.
"""
struct DipoleScatteringModel{T<:Number} <: AbstractScatteringModel
# Mandatory fields for AbstractScatteringModel
Expand Down Expand Up @@ -46,13 +46,18 @@ struct DipoleScatteringModel{T<:Number} <: AbstractScatteringModel
D2maj::T
D1min::T
D2min::T
Pϕ0::T

# Constructor
function DipoleScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, D_pc=2.82, R_pc=5.53)
function DipoleScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, D_kpc=2.82, R_kpc=5.53)
# compute asymmetry parameters and magnification parameter
A = calc_A(θmaj_mas, θmin_mas)
ζ0 = calc_ζ0(A)
M = calc_M(D_pc, R_pc)
M = calc_M(D_kpc, R_kpc)

# convert D and R to cm
D_cm = kpc_tp_cm*D_kpc
R_cm = kpc_tp_cm*R_kpc

# position angle (measured from Dec axis in CCW)
# to a more tranditional angle measured from RA axis in CW
Expand Down Expand Up @@ -89,8 +94,8 @@ struct DipoleScatteringModel{T<:Number} <: AbstractScatteringModel
D2min = calc_D2(α, Amin, Bmin)

return new{typeof(α)}(
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_pc, R_pc,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_cm, R_cm,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min, Pϕ0
)
end
end
Expand Down
17 changes: 11 additions & 6 deletions src/scatteringmodels/models/periodicboxcar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The default numbers are based on the best-fit parameters presented in Johnson et
- `θmin_nas::Number`: FWHM in mas of the minor axis angular broadening at the specified reference wavelength.
- `ϕ_deg::Number`: The position angle of the major axis of the scattering in degree.
- `λ0_cm::Number`: The reference wavelength for the scattering model in cm.
- `D_pc::Number`: The distance from the observer to the scattering screen in pc.
- `R_pc::Number`: The distance from the source to the scattering screen in pc.
- `D_kpc::Number`: The distance from the observer to the scattering screen in pc.
- `R_kpc::Number`: The distance from the source to the scattering screen in pc.
"""
struct PeriodicBoxCarScatteringModel{T<:Number} <: AbstractScatteringModel
# Mandatory fields for AbstractScatteringModel
Expand Down Expand Up @@ -45,12 +45,17 @@ struct PeriodicBoxCarScatteringModel{T<:Number} <: AbstractScatteringModel
D2maj::T
D1min::T
D2min::T
Pϕ0::T

function PeriodicBoxCarScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, R_pc=5.53, D_pc=2.82)
function PeriodicBoxCarScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, R_kpc=5.53, D_kpc=2.82)
# compute asymmetry parameters and magnification parameter
A = calc_A(θmaj_mas, θmin_mas)
ζ0 = calc_ζ0(A)
M = calc_M(D_pc, R_pc)
M = calc_M(D_kpc, R_kpc)

# convert D and R to cm
D_cm = kpc_tp_cm*D_kpc
R_cm = kpc_tp_cm*R_kpc

# Parameters for the approximate phase structure function
θmaj_rad = calc_θrad(θmaj_mas) # milliarcseconds to radians
Expand Down Expand Up @@ -86,8 +91,8 @@ struct PeriodicBoxCarScatteringModel{T<:Number} <: AbstractScatteringModel
D2min = calc_D2(α, Amin, Bmin)

return new{typeof(α)}(
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_pc, R_pc,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_cm, R_cm,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min, Pϕ0
)
end
end
Expand Down
17 changes: 11 additions & 6 deletions src/scatteringmodels/models/vonmises.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The default numbers are based on the best-fit parameters presented in Johnson et
- `θmin_nas::Number`: FWHM in mas of the minor axis angular broadening at the specified reference wavelength.
- `ϕ_deg::Number`: The position angle of the major axis of the scattering in degree.
- `λ0_cm::Number`: The reference wavelength for the scattering model in cm.
- `D_pc::Number`: The distance from the observer to the scattering screen in pc.
- `R_pc::Number`: The distance from the source to the scattering screen in pc.
- `D_kpc::Number`: The distance from the observer to the scattering screen in pc.
- `R_kpc::Number`: The distance from the source to the scattering screen in pc.
"""
struct vonMisesScatteringModel{T<:Number} <: AbstractScatteringModel
# Mandatory fields for AbstractScatteringModel
Expand Down Expand Up @@ -45,12 +45,17 @@ struct vonMisesScatteringModel{T<:Number} <: AbstractScatteringModel
D2maj::T
D1min::T
D2min::T
Pϕ0::T

function vonMisesScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, R_pc=5.53, D_pc=2.82)
function vonMisesScatteringModel(; α=1.38, rin_cm=800e5, θmaj_mas=1.380, θmin_mas=0.703, ϕpa_deg=81.9, λ0_cm=1.0, R_kpc=5.53, D_kpc=2.82)
# compute asymmetry parameters and magnification parameter
A = calc_A(θmaj_mas, θmin_mas)
ζ0 = calc_ζ0(A)
M = calc_M(D_pc, R_pc)
M = calc_M(D_kpc, R_kpc)

# convert D and R to cm
D_cm = kpc_tp_cm*D_kpc
R_cm = kpc_tp_cm*R_kpc

# Parameters for the approximate phase structure function
θmaj_rad = calc_θrad(θmaj_mas) # milliarcseconds to radians
Expand Down Expand Up @@ -86,8 +91,8 @@ struct vonMisesScatteringModel{T<:Number} <: AbstractScatteringModel
D2min = calc_D2(α, Amin, Bmin)

return new{typeof(α)}(
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_pc, R_pc,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min
α, rin_cm, θmaj_mas, θmin_mas, ϕpa_deg, λ0_cm, D_cm, R_cm,
M, ζ0, A, kζ, Bmaj, Bmin, Qbar, C, Amaj, Amin, ϕ0, D1maj, D2maj, D1min, D2min, Pϕ0
)
end
end
Expand Down
51 changes: 51 additions & 0 deletions src/scatteringmodels/phasescreens/powerlaw.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export PhaseScreenPowerLaw
@doc raw"""
$(TYPEDEF)
# Fields
$(FIELDS)
"""
struct PhaseScreenPowerLaw{N,S<:AbstractScatteringModel, T<:Number} <: AbstractPowerSpectrumModel{2}
sm::S
dx::T
dy::T
Vx_km_per_s::T
Vy_km_per_s::T

function PhaseScreenPowerLaw(sm::S, dx, dy, Vx_km_per_s=0.0::T, Vy_km_per_s=0.0::T) where {S, T}
new{2,typeof(sm), typeof(dx)}(sm, dx, dy, Vx_km_per_s, Vy_km_per_s)
end
end

@inline fourieranalytic(::PhaseScreenPowerLaw) = IsAnalytic()

function StationaryRandomFields.power_point(model::PhaseScreenPowerLaw, q...)
@assert length(q) == 2

q_r = sum(abs2, q) + 1e-12/model.sm.rin
q_ϕ = atan(q[2], q[1])
return model.sm.Qbar * (q_r*model.sm.rin)^(-(model.sm.α + 2.0)) * exp(-(q_r * model.sm.rin)^2) * (model.sm, q_ϕ)
end

function StationaryRandomFields.amplitude_point(model::PhaseScreenPowerLaw, t_hr, N, FOV, dq, q...)
x_offset_pixels = (model.Vx_km_per_s * 1.e5) * (t_hr*3600.) / (FOV/float(N))
y_offset_pixels = (model.Vy_km_per_s * 1.e5) * (t_hr*3600.) / (FOV/float(N))
q = (q[1]/model.dx, q[2]/model.dy)

return (power_point(model, dq.*q...)) * exp(2.0*π*1im*(q[1]*x_offset_pixels + q[2]*y_offset_pixels)/float(N))
end

function StationaryRandomFields.amplitude_map(model::PhaseScreenPowerLaw, noisesignal::Union{AbstractNoiseSignal,AbstractContinuousNoiseSignal}, t_hr = 0.)
N = noisesignal.dims[1]
FOV = N * model.sm.D
dq = 2*π/FOV
gridofq = rfftfreq(noisesignal)

prod = collect(Iterators.product(gridofq...))
amp = zeros(size(prod)...)
for i in eachindex(prod)[2:end]
amp[i] = amplitude_point(model, t_hr, N, FOV, dq, prod[i]...)
end
return amp
end
48 changes: 48 additions & 0 deletions src/scatteringmodels/phasescreens/refractivemodel.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export AbstractPhaseScreen
export RefractivePhaseScreen
export phase_screen
export wrapped_grad

abstract type AbstractPhaseScreen end

struct RefractivePhaseScreen{S, T <: Number, N<:AbstractNoiseSignal, P <: AbstractPowerSpectrumModel} <: AbstractPhaseScreen
sm::S
dx::T
dy::T
signal::N
Q::P
function RefractivePhaseScreen(sm::S, Nx::Number, Ny::Number, dx::T, dy::T, Vx_km_per_s=0.0::T, Vy_km_per_s=0.0::T) where {S, T}
dx = dx*sm.D
dy = dy*sm.D
Q = PhaseScreenPowerLaw(sm, dx, dy, Vx_km_per_s, Vy_km_per_s)
signal = NoiseSignal((Nx, Ny))
return new{S, T, NoiseSignal, typeof(Q)}(sm, dx, dy, signal, Q)
end
end

StationaryRandomFields.generate_gaussian_noise(psm::AbstractPhaseScreen; rng = Random.default_rng()) = generate_gaussian_noise(psm.signal; rng = rng)

function phase_screen(psm::AbstractPhaseScreen, noise_screen=nothing)
if isnothing(noise_screen)
noise_screen = generate_gaussian_noise(psm)
end
cns = ContinuousNoiseSignal(psm.signal)
screengen = PSNoiseGenerator(psm.Q, cns)
return generate_signal_noise(screengen)
end

function wrapped_grad(ϕ)
nx, ny = size(ϕ)
gradϕ_x = zeros(Float64, nx, ny)
gradϕ_y = zeros(Float64, nx, ny)
for i=1:nx, j=1:ny
i0 = i == 1 ? nx : i - 1
j0 = j == 1 ? ny : j - 1
i1 = i == nx ? 1 : i + 1
j1 = j == ny ? 1 : j + 1

@inbounds gradϕ_x[i, j] = 0.5 * (ϕ[i1, j] - ϕ[i0, j])
@inbounds gradϕ_y[i, j] = 0.5 * (ϕ[i, j1] - ϕ[i, j0])
end
return (gradϕ_x, gradϕ_y)
end

0 comments on commit d967678

Please sign in to comment.