Skip to content

Commit 348080d

Browse files
authored
Merge pull request #45 from hersle/los_simultaneous_sources
Line-of-sight integrate several source functions simultaneously
2 parents e124af4 + a20bc70 commit 348080d

File tree

2 files changed

+43
-22
lines changed

2 files changed

+43
-22
lines changed

src/observables/angular.jl

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ function SphericalBesselCache(ls::AbstractVector; xmax = 10*ls[end], dx = 2π/48
2626
ys[:, i] .= jl.(l, xs)
2727
end
2828

29+
xs = collect(xs)
2930
return SphericalBesselCache{typeof(xs)}(ls, is, ys, invdx, xs)
3031
end
3132

@@ -78,34 +79,41 @@ ChainRulesCore.frule((_, _, Δx), ::typeof(jl), l, x) = jl(l, x), jl′(l, x) *
7879
# TODO: use u = k*χ as integration variable, so oscillations of Bessel functions are the same for every k?
7980
# TODO: define and document symbolic dispatch!
8081
"""
81-
los_integrate(Ss::AbstractMatrix{T}, ls::AbstractVector, τs::AbstractVector, ks::AbstractVector, jl::SphericalBesselCache; l_limber = typemax(Int), integrator = TrapezoidalRule(), verbose = false) where {T <: Real}
82+
los_integrate(Ss::AbstractArray{T, 3}, ls::AbstractVector, τs::AbstractVector, ks::AbstractVector, jl::SphericalBesselCache; l_limber = typemax(Int), integrator = TrapezoidalRule(), verbose = false) where {T <: Real}
8283
8384
For the given `ls` and `ks`, compute the line-of-sight-integrals
8485
```math
8586
Iₗ(k) = ∫dτ S(k,τ) jₗ(k(τ₀-τ))
8687
```
8788
over the source function values `Ss` against the spherical Bessel functions ``jₗ(x)`` cached in `jl`.
88-
The element `Ss[i,j]` holds the source function value ``S(kᵢ, τⱼ)``.
89+
The element `Ss[i,j,k]` holds the source function value ``Sᵢ(kⱼ, τₖ)``.
8990
The limber approximation
9091
```math
9192
Iₗ ≈ √(π/(2l+1)) S(k,τ₀-(l+1/2)/k)
9293
```
9394
is used for `l ≥ l_limber`.
9495
"""
95-
function los_integrate(Ss::AbstractMatrix{T}, ls::AbstractVector, τs::AbstractVector, ks::AbstractVector, jl::SphericalBesselCache; l_limber = typemax(Int), integrator = TrapezoidalRule(), verbose = false) where {T <: Real}
96+
function los_integrate(Ss::AbstractArray{T, 3}, ls::AbstractVector, τs::AbstractVector, ks::AbstractVector, jl::SphericalBesselCache; l_limber = typemax(Int), integrator = TrapezoidalRule(), verbose = false) where {T <: Real}
9697
# Julia is column-major; make sure innermost loop indices appear first in slice expressions (https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-column-major)
97-
@assert size(Ss) == (length(τs), length(ks)) "size(Ss) = $(size(Ss))$((length(τs), length(ks)))"
98+
@assert size(Ss, 2) == length(τs) "size(Ss, 2) = $(size(Ss, 2)) and length(τs) = $(length(τs)) differ"
99+
@assert size(Ss, 3) == length(ks) "size(Ss, 3) = $(size(Ss, 3)) and length(ks) = $(length(ks)) differ"
100+
@assert jl.x[begin] 0 "jl.x[begin] < 0"
101+
@assert jl.x[end] ks[end]*τs[end] "jl.x[end] < kmax*τmax"
98102
τs = collect(τs) # force array to avoid floating point errors with ranges in following χs due to (e.g. tiny negative χ)
99103
χs = τs[end] .- τs
100-
Is = similar(Ss, length(ks), length(ls))
104+
halfdτs = 0.5 .* (τs[begin+1:end] .- τs[begin:end-1]) # precompute before loops
105+
NS = size(Ss, 1)
106+
Is = zeros(eltype(Ss), NS, length(ks), length(ls))
101107

102108
verbose && l_limber < typemax(Int) && println("Using Limber approximation for l ≥ $l_limber")
103109

104110
# TODO: skip and set jl to zero if l ≳ kτ0 or another cutoff?
105111
@tasks for il in eachindex(ls) # parallellize independent loop iterations
106112
@local begin # define task-local values (declared once for all loop iterations)
107-
∂I_∂τ = similar(Ss, length(τs))
113+
prevs = similar(Ss, NS)
114+
_Is = similar(Ss, NS)
108115
end
116+
@inbounds begin
109117
l = ls[il]
110118
verbose && print("\rLOS integrating with l = $l")
111119
for ik in eachindex(ks)
@@ -114,26 +122,39 @@ function los_integrate(Ss::AbstractMatrix{T}, ls::AbstractVector, τs::AbstractV
114122
χ = (l+1/2) / k
115123
if χ > χs[1]
116124
# χ > χini > χrec, so source function is definitely zero
117-
S = 0.0
125+
for iS in 1:NS
126+
_Is[iS] = 0.0
127+
end
118128
else
119129
# interpolate between two closest points in saved array
120130
iχ₋ = searchsortedfirst(χs, χ; rev = true)
121131
iχ₊ = iχ₋ - 1
122132
χ₋, χ₊ = χs[iχ₋], χs[iχ₊] # now χ₋ < χ < χ₊
123-
S₋, S₊ = Ss[iχ₋,ik], Ss[iχ₊,ik]
124-
S = S₋ + (S₊-S₋) *-χ₋) / (χ₊-χ₋)
133+
@inbounds @simd for iS in 1:NS
134+
S₋, S₊ = Ss[iS, iχ₋, ik], Ss[iS, iχ₊, ik]
135+
S = S₋ + (S₊-S₋) *-χ₋) / (χ₊-χ₋)
136+
_Is[iS] = /(2l+1)) * S / k
137+
end
125138
end
126-
I = /(2l+1)) * S / k
127139
else
128-
forin eachindex(τs)
129-
S = Ss[iτ,ik]
140+
prevs .= .0 # jl = 0 when χ = 0
141+
_Is .= 0.0
142+
forin length(τs)-1:-1:1
130143
χ = χs[iτ]
131-
= k * χ
132-
∂I_∂τ[iτ] = S * jl(l, kχ) # TODO: rewrite LOS integral to avoid evaluating Rl with dual numbers? # TODO: rewrite LOS integral with y = kτ0 and x=τ/τ0 to cache jls independent of cosmology
144+
_jl = jl(l, k*χ)
145+
halfdτ = halfdτs[iτ]
146+
@inbounds @simd for iS in 1:NS
147+
prev = prevs[iS]
148+
curr = Ss[iS, iτ, ik] * _jl
149+
_Is[iS] += halfdτ * (curr + prev)
150+
prevs[iS] = curr
151+
end
133152
end
134-
I = integrate(τs, ∂I_∂τ; integrator) # integrate over τ # TODO: add starting I(τini) to fix small l?
135153
end
136-
Is[ik,il] = I
154+
for iS in 1:NS
155+
Is[iS, ik, il] = _Is[iS]
156+
end
157+
end
137158
end
138159
end
139160
verbose && println()
@@ -257,14 +278,15 @@ function spectrum_cmb(modes::AbstractVector{<:Symbol}, prob::CosmologyProblem, j
257278
Ss_fine[:, end, :] .= 0.0 # can be Inf, but is always weighted by zero-valued spherical Bessel function in LOS integration
258279

259280
Θls = zeros(eltype(Ss_fine), max(iT, iE, iψ), length(ks_fine), length(ls))
260-
if iT > 0
261-
Θls[iT, :, :] .= los_integrate(@view(Ss_fine[1, :, :]), ls, τs, ks_fine, jl; integrator, verbose, kwargs...)
281+
iTE = max(iT, iE)
282+
if iTE > 0
283+
Θls[1:iTE, :, :] .= los_integrate(@view(Ss_fine[1:iTE, :, :]), ls, τs, ks_fine, jl; integrator, verbose, kwargs...)
262284
end
263285
if iE > 0
264-
Θls[iE, :, :] .= los_integrate(@view(Ss_fine[2, :, :]), ls, τs, ks_fine, jl; integrator, verbose, kwargs...) .* transpose(@. ((ls+2)*(ls+1)*(ls+0)*(ls-1)))
286+
Θls[iE, :, :] .*= transpose(@. ((ls+2)*(ls+1)*(ls+0)*(ls-1)))
265287
end
266288
if> 0
267-
Θls[iψ, :, :] .= los_integrate(@view(Ss_fine[3, :, :]), ls, τs, ks_fine, jl; l_limber, integrator, verbose, kwargs...)
289+
Θls[iψ:iψ, :, :] .= los_integrate(@view(Ss_fine[3:3, :, :]), ls, τs, ks_fine, jl; l_limber, integrator, verbose, kwargs...)
268290
end
269291

270292
P0s = spectrum_primordial(ks_fine, sol) # more accurate

test/runtests.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,10 +384,9 @@ end
384384
Ss = @inferred source_grid(sol, [M.ST0], τs) # TODO: save allocation time with out-of-place version?
385385
@test Ss == source_grid(prob, [M.ST0], τs, ks_coarse; ptopts)
386386
Ss = @inferred source_grid(Ss, ks_coarse, ks_fine) # TODO: save allocation time with out-of-place version?
387-
Ss = @view Ss[1, :, :]
388387
Θ0s = @inferred los_integrate(Ss, ls, τs, ks_fine, jl) # TODO: sequential along τ? # TODO: cache kτ0 and x=τ/τ0 (only depends on l)
389388
P0s = @inferred spectrum_primordial(ks_fine, pars[M.g.h], prob.bg.ps[M.I.As])
390-
Cls = @inferred spectrum_cmb(Θ0s, Θ0s, P0s, ls, ks_fine)
389+
Cls = @inferred spectrum_cmb(Θ0s[1, :, :], Θ0s[1, :, :], P0s, ls, ks_fine)
391390
end
392391

393392
@testset "Background differentiation test" begin

0 commit comments

Comments
 (0)