Line profiles

Line profiles are kernels that represent the blurring of spectral lines from e.g. an accretion disc around a black hole. They show how the flux is smeared over a range of energies relative to the rest energy due to relativistic effects and Doppler shifts.

Gradus.jl has various methods for calculating line profiles (see Methods). Here we will explore how to compute line profiles with Gradus using the BinningMethod and the TransferFunctionMethod.

As with any Gradus.jl simulation, we start by picking the basic components of our model:

using Gradus

m = KerrMetric(M = 1.0, a = 0.998)
# an infinite thin disc in the equatorial plane
d = ThinDisc(0.0, Inf)
x = SVector(0.0, 10_000.0, deg2rad(60), 0.0)

We can compute a line profile directly using the lineprofile method:

Gradus.lineprofileFunction
lineprofile(m::AbstractMetric, x::SVector, d::AbstractAccretionGeometry; kwargs...)
lineprofile(
    m::AbstractMetric, 
    x::SVector, 
    d::AbstractAccretionDisc, 
    profile::AbstractDiscProfile; 
    kwargs...
)

Compute a line profile for a given set of model components. Returns both the grid and flux.

The dispatch that includes a profile can be used with an AbstractDiscProfile, conventionally calculated by a coronal model.

If no profile is specified, the emissivity is assumed to be a power-law $\varepsilon(r) = r^{-3}$.

This function accepts a number of keyword arguments depending on method used to compute the line profile. Common arguments with their defaults are:

  • bins = collect(range(0.1, 1.5, 180)): the (energy) bins used as the domain of the line profile.
  • method = TransferFunctionMethod(): used to select which method to use when computing the line profile. Alternatives include BinningMethod.

The TransferFunctionMethod dispatch additionally accepts the following keyword arguments:

  • minrₑ = isco(m): the innermost transfer function radius to compute / inner radius of line profile integration.
  • maxrₑ = 50: the outermost transfer function radius to compute / outer radius of line profile integration.
  • numrₑ = 100: the number of transfer functions to calculate.
  • verbose = false: show a progress bar.
  • h = 2e-8: an integration padding value to avoid numerical instabilities. See Dauser et al., 2010 for details.
  • Nr = 1000: the number of radial steps used to interpolate between transfer function branches when integrating.

The BinningMethod dispatch additionally accepts the following keyword arguments:

  • λ_max = 2 * x[2]: the maximum integration time (affine parameter).
  • redshift_pf = ConstPointFunctions.redshift(m, x): the function used to compute redshift values.
  • verbose = false: show a progress bar.
  • minrₑ = isco(m):` the inner radius of the line profile.
  • maxrₑ = T(50):` the outer radius of the line profile.
  • plane = PolarPlane(GeometricGrid(); Nr = 450, Nθ = 1300, r_max = 5maxrₑ): the image plane used in the calculation.

All other keyword arguments are passed to tracegeodesics.

There is an additional dispatch that does not accept bins as a keyword argument:

lineprofile(
    bins,
    ε::Function,
    m::AbstractMetric,
    u,
    d::AbstractAccretionGeometry;
    kwargs...
)

This dispatch is special as it be used to pass any arbitrary function to act as the emissivity profile of the disc.

source

We can invoke this directly

bins, flux = lineprofile(m, x, d)

If a custom (enregy) bins is desired, it can be passed using the keyword arguments. We can plot these vectors directly:

using Plots
plot(bins, flux; xlabel = "g", ylabel = "flux")

To pass a custom emissivity function, we can use a different dispatch that takes the bins and the emissivity function as the first argument. Here is a shallower power-law, reusing the same bins:

emissivity(r) = r^-2
bins, flux = lineprofile(bins, emissivity, m, x, d)
plot!(bins, flux)

To use the emissivity calcualted via an AbstractDiscProfile from a coronal model, we can do something like

model = LampPostModel(h = 10.0)
profile = emissivity_profile(m, d, model)
bins, flux = lineprofile(m, x, d, profile; bins = bins)
plot!(bins, flux)

We can at any point switch to the BinningMethod dispatches, which have largely the same functions calls. These methods take significantly longer to execute, and often give slightly lower resolution, however are much more flexible in the underlying assumptions of the model:

bins, flux = lineprofile(m, x, d, profile; bins = bins, method = BinningMethod())
plot!(bins, flux)

The defaults have been chosen to balance accuracy and speed. The above TransferFunctionMethod calculations only take a handful of seconds to compute from scratch. If all we are changing are properties of the disc, we do not necessarily need to recompute the transfer functions, and can make use of caches instead. Here is an example recipe using the transferfunctions utility:

tfs = transferfunctions(m, x, d)

f1 = integrate_lineprofile(r -> r^-3, tfs, bins)
f2 = integrate_lineprofile(profile, tfs, bins)

plot(bins, f1)
plot!(bins, f2)

Similar pre-computation can be done with the binning method, however there are currently no utility functions available in the same way as with the transfer functions.

Gradus.integrate_lineprofileFunction
integrate_lineprofile(
    prof::AbstractDiscProfile, 
    transfer_functions, 
    g_grid;
    kwargs...
)
integrate_lineprofile(
    emssivity::Function,
    transfer_functions, 
    g_grid;
    kwargs...
)

Integrate a set of InterpolatingTransferBranches (calculated using transferfunctions) with either an AbstractDiscProfile or function representing the emissivity profile of the disc.

The emissivity function must be of the form r -> f(r)::tyepof(r), i.e. represent the emissivty as a function of only radius, returning a single value for each ring.

source