Skip to content

Commit cc798d2

Browse files
authored
Merge pull request #217 from astro-group-bristol/fergus/adaptive
Brand new root-finder for solving emission radii
2 parents d0579d3 + d64f847 commit cc798d2

17 files changed

Lines changed: 436 additions & 186 deletions

lib/GradusSpectralModels/src/GradusSpectralModels.jl

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ struct LineProfile{D,T} <: AbstractTableModel{T,Additive}
88
"Spin"
99
a::T
1010
"Observer inclination (degrees off of the spin axis)."
11-
θ::T
11+
θ_obs::T
1212
"Inner radius of the accretion disc."
13-
rin::T
13+
inner_r::T
1414
"Outer radius of the accretion disc."
15-
rout::T
15+
outer_r::T
1616
"Central emission line energy (keV)."
17-
E₀::T
17+
lineE::T
1818
end
1919

2020
function Base.copy(m::LineProfile)
@@ -39,37 +39,29 @@ function LineProfile(
3939
profile,
4040
table::Gradus.CunninghamTransferTable;
4141
K = FitParam(1.0),
42-
a = FitParam(0.998),
43-
θ = FitParam(45.0),
44-
rin = FitParam(1.0),
45-
rout = FitParam(100.0, upper_limit = 100.0),
46-
E₀ = FitParam(1.0),
42+
a = FitParam(0.998, upper_limit = 1.0),
43+
θ_obs = FitParam(45.0, upper_limit = 90.0),
44+
inner_r = FitParam(1.0),
45+
outer_r = FitParam(100.0, upper_limit = 1000.0),
46+
lineE = FitParam(6.4),
4747
kwargs...,
4848
)
49-
setup = integration_setup(profile, table((get_value(a), get_value(θ))); kwargs...)
50-
LineProfile((; setup = setup, table = table), K, a, θ, rin, rout, E₀)
49+
setup = integration_setup(profile, table((get_value(a), get_value(θ_obs))); kwargs...)
50+
LineProfile((; setup = setup, table = table), K, a, θ_obs, inner_r, outer_r, lineE)
5151
end
5252

5353
function SpectralFitting.invoke!(output, domain, model::LineProfile)
54-
grid = model.table.table((model.a, model.θ))
55-
rmin = if model.rin < grid.r_grid[1]
56-
grid.r_grid[1]
57-
else
58-
model.rin
59-
end
60-
rout = if model.rout < rmin
61-
rmin
62-
else
63-
model.rout
64-
end
54+
grid = model.table.table((model.a, model.θ_obs))
55+
rmin = (model.inner_r < grid.r_grid[1]) ? grid.r_grid[1] : model.inner_r
56+
outer_r = (model.outer_r < rmin) ? rmin : model.outer_r
6557
Gradus.integrate_lineprofile!(
6658
output,
6759
model.table.setup,
6860
grid,
6961
domain;
7062
rmin = rmin,
71-
rmax = rout,
72-
g_scale = model.E₀,
63+
rmax = outer_r,
64+
g_scale = model.lineE,
7365
)
7466
output
7567
end

src/corona/adaptive-sample.jl

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ function bin_emissivity_grid(
230230
d::AbstractAccretionDisc,
231231
r_bins,
232232
ϕ_bins,
233-
sky::AdaptiveSky{T,<:CoronaGridValues},
233+
sky::AdaptiveSky{T,<:CoronaGridValues};
234+
kwargs...,
234235
) where {T}
235236
solid_angle, output = bin_emissivity_grid!(
236237
zeros(eltype(r_bins), (length(r_bins), length(ϕ_bins))),
@@ -239,7 +240,8 @@ function bin_emissivity_grid(
239240
d,
240241
r_bins,
241242
ϕ_bins,
242-
sky,
243+
sky;
244+
kwargs...,
243245
)
244246

245247
for i in eachindex(solid_angle)
@@ -273,7 +275,8 @@ function bin_emissivity_grid!(
273275
d::AbstractAccretionDisc,
274276
r_bins,
275277
ϕ_bins,
276-
sky::AdaptiveSky{T,V},
278+
sky::AdaptiveSky{T,V};
279+
Γ = 2,
277280
) where {T,V<:CoronaGridValues}
278281
@assert size(output) == size(solid_angle)
279282
@assert size(output) == (length(r_bins), length(ϕ_bins))
@@ -302,7 +305,7 @@ function bin_emissivity_grid!(
302305
ϕ_i = searchsortedlast(ϕ_bins, mod2pi(v.ϕ))
303306
if (r_i != 0) && (ϕ_i != 0)
304307
# TODO: allow generic spectrum
305-
output[r_i, ϕ_i] += ΔΩ * v.J * (v.g^2 * area(v.r))
308+
output[r_i, ϕ_i] += ΔΩ * v.J * (v.g^Γ * area(v.r))
306309
solid_angle[r_i, ϕ_i] += ΔΩ
307310
end
308311
end
@@ -392,6 +395,12 @@ function evaluate_refinement_metric(
392395
[_eval_metric(i, j) for i in axis_itt, j in axis_itt]
393396
end
394397

398+
@enum StepBlockCodes begin
399+
converged
400+
not_converged
401+
limit_exceeded
402+
end
403+
395404
"""
396405
function step_block!(
397406
m::AbstractMetric,
@@ -435,6 +444,7 @@ function step_block!(
435444
top = 3,
436445
split = 6,
437446
percentage = 10,
447+
limit = 200_000,
438448
metric = empty_fraction,
439449
kwargs...,
440450
)
@@ -457,7 +467,7 @@ function step_block!(
457467
if check_threshold
458468
_counts = filter(i -> i.score < threshold, _counts)
459469
if length(_counts) == 0
460-
return false
470+
return converged
461471
end
462472
end
463473

@@ -478,15 +488,24 @@ function step_block!(
478488
false
479489
end
480490

481-
trace_step!(sky; check_refine = refiner, kwargs...)
482-
true
491+
to_refine = find_need_refine(sky, refiner)
492+
if length(to_refine) > limit
493+
@warn (
494+
"Convergence failed. Limit $limit exceeded: too many cells to refine (N = $(length(to_refine)))."
495+
)
496+
return limit_exceeded
497+
end
498+
499+
refine_and_trace!(sky, to_refine; kwargs...)
500+
not_converged
483501
end
484502

485503
"""
486504
refine_function(f)
487505
488506
Used to define a new `check_refine` function for an [`AdaptiveSky`](@ref). This
489-
can be passed e.g. to [`refine_all`](@ref).
507+
can be passed e.g. to [`find_need_refine`](@ref) or
508+
[`refine_and_trace!`](@ref).
490509
491510
The function `f` is given each cell's value and should return `true` if the
492511
cell should be refined, else `false`.
@@ -572,43 +591,58 @@ function adaptive_solve!(
572591
end
573592

574593
# trace the initial sky
575-
Gradus.trace_initial!(sky)
594+
trace_initial!(sky)
576595
# this happens outside the loop as the first is so fast it tends to ruin
577596
# the output of the progress bar
578-
Gradus.trace_step!(sky)
597+
trace_step!(sky)
579598
i += 1
580599

581600
for _ = 1:trace_calls
582-
Gradus.trace_step!(sky; progress_bar = progress, showvalues = showvals)
601+
trace_step!(sky; progress_bar = progress, showvalues = showvals)
583602
i += 1
584603
end
585604

586605
# ensure the distant radii are well refined
587-
Gradus.trace_step!(
606+
trace_step!(
588607
sky;
589608
check_refine = fine_refine_function(v -> v.r > 800; percentage = 10),
590609
progress_bar = progress,
591610
showvalues = showvals,
592611
)
593612
i += 1
594613

595-
while step_block!(
596-
m,
597-
d,
614+
r_isco = isco(m)
615+
616+
trace_step!(
598617
sky;
599-
split = 5,
600-
top = 4,
618+
check_refine = fine_refine_function(
619+
v -> (v.r > r_isco) && (v.r < 2.0);
620+
percentage = 50,
621+
),
601622
progress_bar = progress,
602623
showvalues = showvals,
603-
kwargs...,
604624
)
625+
i += 1
626+
627+
status = not_converged
628+
while status == not_converged
629+
status = step_block!(
630+
m,
631+
d,
632+
sky;
633+
split = 5,
634+
top = 4,
635+
progress_bar = progress,
636+
showvalues = showvals,
637+
kwargs...,
638+
)
605639
i += 1
606640
if i >= limit
607641
break
608642
end
609643
end
610644

611-
Gradus.ProgressMeter.finish!(progress)
645+
ProgressMeter.finish!(progress)
612646

613647
i
614648
end

src/corona/models/ring.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,9 @@ function _branch_emissivity(m::AbstractMetric, slice::PointSlice, I::Vector{Int}
539539
Δr = (abs(slice.r[i1] - slice.r[i2]))
540540
Δθ = ang_diff(slice.θ[i1], slice.θ[i2]) / 2
541541

542-
A = _proper_area(m, slice.r[i] + Δr / 2, π / 2) * (abs(slice.drdθ[i1]) + abs(slice.drdθ[i2])) / 2
542+
A =
543+
_proper_area(m, slice.r[i] + Δr / 2, π / 2) *
544+
(abs(slice.drdθ[i1]) + abs(slice.drdθ[i2])) / 2
543545
point_source_equatorial_disc_emissivity(
544546
spectrum,
545547
slice.θ[i],

src/image-planes/adaptive-sky.jl

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ AdaptiveSky(V::Type, calc_v, check_refine; pole_offset = 1e-6) = AdaptiveSky(
4242
)
4343

4444
"""
45-
refine_all!(sky::AdaptiveSky, to_refine::Vector{Int})
45+
trace_all!(sky::AdaptiveSky, to_refine::Vector{Int})
4646
4747
Refine all cells in `to_refine` using a multi-threaded loop.
4848
"""
49-
function refine_all!(
49+
function trace_all!(
5050
sky::AdaptiveSky,
5151
to_refine::Vector{Int};
5252
verbose = false,
@@ -120,25 +120,16 @@ function trace_initial!(sky::AdaptiveSky; level = 3)
120120
end
121121

122122
"""
123-
trace_step!(sky::AdaptiveSky; check_refine = sky.check_refine, verbose = false)
124-
125-
Apply the refinement metric across each cell boundary and refine the cells
126-
where the metric is `true`.
127-
128-
A different refinemenet metric from the default can be used by passing the
129-
`check_refine` kwarg, using the same function signature as documented in
130-
[`AdaptiveSky`](@ref).
123+
find_need_refine(sky::AdaptiveSky, check_refine::Function)::Vector{Int}
131124
132-
If `verbose` is true, a progress bar will be displayed during refinement.
125+
Check all cells with `check_refine`, and return those cell IDs that would need
126+
to refined.
133127
"""
134-
function trace_step!(
135-
sky::AdaptiveSky{T,V};
136-
check_refine = sky.check_refine,
137-
kwargs...,
138-
) where {T,V}
128+
function find_need_refine(sky::AdaptiveSky, check_refine::Function)::Vector{Int}
139129
N = lastindex(sky.grid.cells)
140130

141131
to_refine = Set{Int}()
132+
142133
# TODO: multi-thread using a divide and conquer approach
143134
for cell_index = 1:N
144135
# skip those we've already refined
@@ -155,32 +146,70 @@ function trace_step!(
155146
end
156147
end
157148

149+
sort!(collect(to_refine))
150+
end
151+
152+
"""
153+
function refine_and_trace!(
154+
sky::AdaptiveSky,
155+
cell_ids::Vector{Int};
156+
kwargs...,
157+
)
158+
159+
Given a list of cell IDs, refine them using [`refine!`](@ref), and then trace
160+
the children to populate their values.
161+
162+
All kwargs are forwarded to [`trace_all!`](@ref).
163+
"""
164+
function refine_and_trace!(
165+
sky::AdaptiveSky{T,V},
166+
cell_ids::Vector{Int};
167+
kwargs...,
168+
) where {T,V}
169+
N = lastindex(sky.grid.cells)
170+
171+
to_trace = Int[]
172+
sizehint!(to_trace, length(cell_ids) * 8)
173+
158174
# now that all have been reaped, apply refining
159-
to_trace = map(collect(to_refine)) do index
160-
if Grids.has_children(sky.grid, index)
161-
Int[]
162-
else
163-
_children = Grids.refine!(sky.grid, index)
164-
for (j, _) in enumerate(_children)
165-
if j == 5
166-
push!(sky.values, sky.values[index])
167-
else
168-
push!(sky.values, make_null(V))
169-
end
175+
for index in cell_ids
176+
@assert !Grids.has_children(sky.grid, index)
177+
_children = Grids.refine!(sky.grid, index)
178+
for (j, child_id) in enumerate(_children)
179+
if j == 5
180+
push!(sky.values, sky.values[index])
181+
else
182+
push!(to_trace, child_id)
183+
push!(sky.values, make_null(V))
170184
end
171-
# return all but the middle for tracing
172-
Int[_children[[1, 2, 3, 4, 6, 7, 8, 9]]...]
173185
end
174186
end
175187

176188
if !isempty(to_trace)
177-
all_trace = reduce(vcat, to_trace)
178-
refine_all!(sky, all_trace; kwargs...)
189+
190+
trace_all!(sky, to_trace; kwargs...)
179191
end
180192

181193
sky
182194
end
183195

196+
"""
197+
trace_step!(sky::AdaptiveSky; check_refine = sky.check_refine, verbose = false)
198+
199+
Apply the refinement metric across each cell boundary and refine the cells
200+
where the metric is `true`.
201+
202+
A different refinemenet metric from the default can be used by passing the
203+
`check_refine` kwarg, using the same function signature as documented in
204+
[`AdaptiveSky`](@ref).
205+
206+
If `verbose` is true, a progress bar will be displayed during refinement.
207+
"""
208+
function trace_step!(sky::AdaptiveSky; check_refine = sky.check_refine, kwargs...)
209+
to_refine = find_need_refine(sky, check_refine)
210+
refine_and_trace!(sky, to_refine; kwargs...)
211+
end
212+
184213
vector_average(distances, values) = sum(i -> i[1] * i[2], zip(distances, values))
185214

186215
# TODO: use a buffer

src/line-profiles.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function lineprofile(
126126
d::AbstractAccretionGeometry,
127127
::TransferFunctionMethod,
128128
;
129-
minrₑ = isco(m) + 1e-2, # delta to avoid numerical instabilities
129+
minrₑ = isco(m),
130130
maxrₑ = 50,
131131
numrₑ = 100,
132132
verbose = false,

0 commit comments

Comments
 (0)