diff --git a/Project.toml b/Project.toml index 9b64ceff..3e2f2122 100644 --- a/Project.toml +++ b/Project.toml @@ -16,6 +16,7 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" diff --git a/src/AccretionFormulae/orbit-interpolations.jl b/src/AccretionFormulae/orbit-interpolations.jl index 4d2c2b6b..d86e927d 100644 --- a/src/AccretionFormulae/orbit-interpolations.jl +++ b/src/AccretionFormulae/orbit-interpolations.jl @@ -52,7 +52,7 @@ end function interpolate_plunging_velocities( m::AbstractMetricParams{T}; - max_time = 10_000, + max_time = 50_000, contra_rotating = false, prograde = true, reltol = 1e-9, diff --git a/src/AccretionFormulae/redshift.jl b/src/AccretionFormulae/redshift.jl index 1b3fdb83..eaedb250 100644 --- a/src/AccretionFormulae/redshift.jl +++ b/src/AccretionFormulae/redshift.jl @@ -228,8 +228,8 @@ function _redshift_guard( gp::FirstOrderGeodesicPoint{T}, max_time, ) where {T} - RedshiftFunctions.redshift_function(m, gp.u, gp.p, gp.t) + RedshiftFunctions.redshift_function(m, gp.u2, gp.p, gp.t2) end function _redshift_guard(m::AbstractMetricParams{T}, gp, max_time) where {T} - RedshiftFunctions.redshift_function(m, gp.u, gp.v) + RedshiftFunctions.redshift_function(m, gp.u2, gp.v2) end diff --git a/src/AccretionGeometry/circular-orbits.jl b/src/AccretionGeometry/circular-orbits.jl index 5ffb80be..c14f0741 100644 --- a/src/AccretionGeometry/circular-orbits.jl +++ b/src/AccretionGeometry/circular-orbits.jl @@ -6,11 +6,7 @@ import ..AccretionGeometry: metric_jacobian, inverse_metric_components -function Ω( - m::AbstractAutoDiffStaticAxisSymmetricParams{T}, - rθ::AbstractVector{T}, - pos, -) where {T} +function Ω(m::AbstractAutoDiffStaticAxisSymmetricParams{T}, rθ, pos) where {T} jacs = metric_jacobian(m, rθ) ∂rg = jacs[:, 1] @@ -22,18 +18,18 @@ function Ω( end end -function __energy(g, Ωϕ) where {T} - -(g[1] + g[5] * Ωϕ) / √(-g[1] - 2g[5] * Ωϕ - g[4] * Ωϕ^2) +function __energy(g, Ωϕ) + @inbounds -(g[1] + g[5] * Ωϕ) / √(-g[1] - 2g[5] * Ωϕ - g[4] * Ωϕ^2) end -energy(m, r; kwargs...) = +energy(m, r; contra_rotating = false) = let rθ = @SVector([r, π / 2]) Ωϕ = Ω(m, rθ, contra_rotating) - __energy(metric_components(m), Ωϕ; kwargs...) + __energy(metric_components(m, rθ), Ωϕ) end function __angmom(g, Ωϕ, prograde) where {T} - res = (g[5] + g[4] * Ωϕ) / √(-g[1] - 2g[5] * Ωϕ - g[4] * Ωϕ^2) + @inbounds res = (g[5] + g[4] * Ωϕ) / √(-g[1] - 2g[5] * Ωϕ - g[4] * Ωϕ^2) if prograde res else @@ -44,7 +40,7 @@ end angmom(m, r; contra_rotating = false, prograde = true) = let rθ = @SVector([r, π / 2]) Ωϕ = Ω(m, rθ, contra_rotating) - __angmom(metric_components(m), Ωϕ, prograde) + __angmom(metric_components(m, rθ), Ωϕ, prograde) end function g_ginv_energy_angmom( @@ -79,12 +75,7 @@ function __vt(ginv, E, L) -E * ginv[1] + L * ginv[5] end -function vt( - m::AbstractAutoDiffStaticAxisSymmetricParams{T}, - r; - contra_rotating = false, - prograde = true, -) where {T} +function vt(m::AbstractAutoDiffStaticAxisSymmetricParams{T}, r; kwargs...) where {T} _, ginv, E, L = g_ginv_energy_angmom(m, r; kwargs...) __vt(ginv, E, L) end diff --git a/src/AccretionGeometry/geometry.jl b/src/AccretionGeometry/geometry.jl index d4282fd3..ebb679d7 100644 --- a/src/AccretionGeometry/geometry.jl +++ b/src/AccretionGeometry/geometry.jl @@ -13,10 +13,10 @@ end """ to_cartesian(gp::GradusBase.AbstractGeodesicPoint{T}) -Return the position of `gp` in Cartesian coordinates. +Return the end position of `gp` in Cartesian coordinates. """ function to_cartesian(gp::GradusBase.AbstractGeodesicPoint{T}) where {T} - @inbounds let r = gp.u[2], ϕ = gp.u[4] + @inbounds let r = gp.u2[2], ϕ = gp.u2[4] x = r * cos(ϕ) y = r * sin(ϕ) SVector{2,T}(x, y) diff --git a/src/DiscProfiles/DiscProfiles.jl b/src/DiscProfiles/DiscProfiles.jl index a61dba0e..00792c4b 100644 --- a/src/DiscProfiles/DiscProfiles.jl +++ b/src/DiscProfiles/DiscProfiles.jl @@ -6,7 +6,7 @@ import StaticArrays: @SVector, SVector import ..GradusBase: - AbstractMetricParams, GeodesicPoint, vector_to_local_sky, getendpoint, inner_radius + AbstractMetricParams, GeodesicPoint, vector_to_local_sky, getgeodesicpoint, inner_radius import ..GeodesicTracer: tracegeodesics import ..AccretionGeometry: AbstractAccretionGeometry, diff --git a/src/DiscProfiles/disc-profiles.jl b/src/DiscProfiles/disc-profiles.jl index d37e4e32..0214959a 100644 --- a/src/DiscProfiles/disc-profiles.jl +++ b/src/DiscProfiles/disc-profiles.jl @@ -35,9 +35,9 @@ end function VoronoiDiscProfile( m::AbstractMetricParams{T}, d::AbstractAccretionDisc{T}, - endpoints::AbstractVector{GeodesicPoint{T}}; + endpoints::AbstractVector{GeodesicPoint{T,V}}; padding = 1, -) where {T} +) where {T,V} dim = d.outer_radius + padding rect = VoronoiCells.Rectangle( GeometryBasics.Point2{T}(-dim, -dim), @@ -64,7 +64,7 @@ function VoronoiDiscProfile( d::AbstractAccretionDisc{T}, sols::AbstractArray{S}, ) where {T,S<:SciMLBase.AbstractODESolution} - VoronoiDiscProfile(m, d, map(sol -> getendpoint(m, sol), sols)) + VoronoiDiscProfile(m, d, map(sol -> getgeodesicpoint(m, sol), sols)) end function VoronoiDiscProfile( diff --git a/src/FirstOrderMethods/FirstOrderMethods.jl b/src/FirstOrderMethods/FirstOrderMethods.jl index e92438c4..6468ad6c 100644 --- a/src/FirstOrderMethods/FirstOrderMethods.jl +++ b/src/FirstOrderMethods/FirstOrderMethods.jl @@ -12,10 +12,9 @@ import ..GradusBase: AbstractMetricParams, inner_radius, AbstractGeodesicPoint, - getendpoint, - geodesic_point_type, unpack_solution, - SciMLBase + SciMLBase, + getgeodesicpoint import ..GeodesicTracer: DiscreteCallback, diff --git a/src/FirstOrderMethods/implementation.jl b/src/FirstOrderMethods/implementation.jl index f886fac9..53a8f800 100644 --- a/src/FirstOrderMethods/implementation.jl +++ b/src/FirstOrderMethods/implementation.jl @@ -1,26 +1,37 @@ -@with_kw struct FirstOrderGeodesicPoint{T,P} <: AbstractGeodesicPoint{T} +@with_kw struct FirstOrderGeodesicPoint{T,V,P} <: AbstractGeodesicPoint{T} retcode::Symbol - t::T - u::AbstractVector{T} - v::AbstractVector{T} + "Start time" + t1::T + "End time" + t2::T + "Start position" + u1::V + "End position" + u2::V + "Start velocity" + v1::V + "End velocity" + v2::V + "First order extra parameter" p::P end -function geodesic_point_type(m::AbstractFirstOrderMetricParams{T}) where {T} - p_type = typeof(make_parameters(T(0), T(0), 1, T)) - FirstOrderGeodesicPoint{T,p_type} -end - -function getendpoint( +@inbounds function getgeodesicpoint( m::AbstractFirstOrderMetricParams{T}, sol::SciMLBase.AbstractODESolution{T,N,S}, ) where {T,N,S} us, ts, p = unpack_solution(sol) - u = us[end] - v = four_velocity(u, m, p) - t = ts[end] - FirstOrderGeodesicPoint(sol.retcode, t, u, convert_velocity_type(u, v), p) + + u_start = SVector{4,T}(us[1][1:4]) + v_start = SVector{4,T}(four_velocity(u_start, m, p)) + t_start = ts[1] + + u_end = SVector{4,T}(us[end][1:4]) + v_end = SVector{4,T}(four_velocity(u_end, m, p)) + t_end = ts[end] + + FirstOrderGeodesicPoint(sol.retcode, t_start, t_end, u_start, u_end, v_start, v_end, p) end function metric_callback( diff --git a/src/Gradus.jl b/src/Gradus.jl index 5edb34fb..6ecae5ec 100644 --- a/src/Gradus.jl +++ b/src/Gradus.jl @@ -18,13 +18,14 @@ using StaticArrays using Parameters using DocStringExtensions +import ForwardDiff +import Roots + # GradusBase export AbstractMetricParams, metric_params, metric, - getendpoint, - getstartpoint, - getpoint, + getgeodesicpoint, GeodesicPoint, vector_to_local_sky, AbstractMetricParams, diff --git a/src/GradusBase/GradusBase.jl b/src/GradusBase/GradusBase.jl index c6a5850d..df653b9b 100644 --- a/src/GradusBase/GradusBase.jl +++ b/src/GradusBase/GradusBase.jl @@ -3,26 +3,22 @@ module GradusBase import Parameters: @with_kw import SciMLBase import Base +import StaticArrays: SVector include("metric-params.jl") include("physical-quantities.jl") include("geodesic-solutions.jl") include("geometry.jl") -export AbstractMetricParams, - metric_params, - metric, - getendpoint, - getstartpoint, - getpoint, - GeodesicPoint, - vector_to_local_sky, - AbstractMetricParams, - geodesic_eq, - geodesic_eq!, - constrain, - onchart, - inner_radius, - metric_type +export AbstractMetricParams, metric_params, metric, getgeodesicpoint +GeodesicPoint, +vector_to_local_sky, +AbstractMetricParams, +geodesic_eq, +geodesic_eq!, +constrain, +onchart, +inner_radius, +metric_type end # module diff --git a/src/GradusBase/geodesic-solutions.jl b/src/GradusBase/geodesic-solutions.jl index 14edad4c..59ba4c6e 100644 --- a/src/GradusBase/geodesic-solutions.jl +++ b/src/GradusBase/geodesic-solutions.jl @@ -1,18 +1,40 @@ abstract type AbstractGeodesicPoint{T} end -@with_kw struct GeodesicPoint{T} <: AbstractGeodesicPoint{T} +@with_kw struct GeodesicPoint{T,V<:AbstractVector} <: AbstractGeodesicPoint{T} retcode::Symbol - t::T - u::AbstractVector{T} - v::AbstractVector{T} + "Start time" + t1::T + "End time" + t2::T + "Start position" + u1::V + "End position" + u2::V + "Start velocity" + v1::V + "End velocity" + v2::V # we don't store the problem parameters # and can create a specialistion for the carter method # then provide dispatched accessor methods # p::P end -function geodesic_point_type(m::AbstractMetricParams{T}) where {T} - GeodesicPoint{T} +@inbounds function getgeodesicpoint( + m::AbstractMetricParams{T}, + sol::SciMLBase.AbstractODESolution{T,N,S}, +) where {T,N,S} + us, ts, _ = unpack_solution(sol) + + u_start = SVector{4,T}(us[1][1:4]) + v_start = SVector{4,T}(us[1][5:8]) + t_start = ts[1] + + u_end = SVector{4,T}(us[end][1:4]) + v_end = SVector{4,T}(us[end][5:8]) + t_end = ts[end] + + GeodesicPoint(sol.retcode, t_start, t_end, u_start, u_end, v_start, v_end) end # TODO: GeodesicPath structure for the full geodesic path @@ -28,38 +50,3 @@ end function unpack_solution(simsol::SciMLBase.AbstractEnsembleSolution{T,N,V}) where {T,N,V} map(unpack_solution, simsol) end - -function getpoint( - m::AbstractMetricParams{T}, - sol::SciMLBase.AbstractODESolution{T,N,S}, - i::Int, -) where {T,N,S} - us, ts, _ = unpack_solution(sol) - if i == -1 - u = us[end][1:4] - v = us[end][5:8] - t = ts[end] - GeodesicPoint(sol.retcode, t, u, v) - else - u = us[i][1:4] - v = us[i][5:8] - t = ts[i] - GeodesicPoint(sol.retcode, t, u, v) - end -end - -function getpoint( - m::AbstractMetricParams{T}, - simsol::Union{SciMLBase.AbstractEnsembleSolution{T,N,S},AbstractArray{P}}, - i, -) where {T,N,S,P<:SciMLBase.AbstractODESolution} - map(sol -> getpoint(m, sol, i), simsol) -end - -function getendpoint(m::AbstractMetricParams{T}, sol_or_simsols) where {T} - getpoint(m, sol_or_simsols, -1) -end - -function getstartpoint(m::AbstractMetricParams{T}, sol_or_simsols) where {T} - getpoint(m, sol_or_simsols, 1) -end diff --git a/src/Rendering/Rendering.jl b/src/Rendering/Rendering.jl index 56c05dcf..4b67dd66 100644 --- a/src/Rendering/Rendering.jl +++ b/src/Rendering/Rendering.jl @@ -21,8 +21,8 @@ function rendergeodesics( m::AbstractMetricParams{T}, init_pos, max_time; - pf = PointFunction((m, gp, mt) -> gp.t) ∘ - FilterPointFunction((m, gp, max_time; kwargs...) -> gp.t < max_time, NaN), + pf = PointFunction((m, gp, mt) -> gp.t2) ∘ + FilterPointFunction((m, gp, max_time; kwargs...) -> gp.t2 < max_time, NaN), image_width = 350, image_height = 250, fov_factor = 3.0, diff --git a/src/Rendering/point-functions.jl b/src/Rendering/point-functions.jl index 318d04d5..5325a428 100644 --- a/src/Rendering/point-functions.jl +++ b/src/Rendering/point-functions.jl @@ -24,7 +24,7 @@ end kwargs..., ) where {M,T,G} ThreadsX.map( - sol -> pf.f(rc.m, getendpoint(m, sol), rc.max_time; kwargs...), + sol -> pf.f(rc.m, getgeodesicpoint(m, sol), rc.max_time; kwargs...), rc.geodesics, ) end diff --git a/src/Rendering/render.jl b/src/Rendering/render.jl index 88e57535..64b29692 100644 --- a/src/Rendering/render.jl +++ b/src/Rendering/render.jl @@ -13,7 +13,7 @@ end function apply_to_image!(m::AbstractMetricParams{T}, image, sols, pf, max_time) where {T} @inbounds @threads for i = 1:length(sols) - image[i] = pf(m, getendpoint(m, sols[i]), max_time) + image[i] = pf(m, getgeodesicpoint(m, sols[i]), max_time) end end @@ -64,7 +64,7 @@ function __prerendergeodesics( kwargs..., ) - point_cache = getendpoint(m, simsols) + point_cache = getgeodesicpoint(m, simsols) EndpointRenderCache( m, diff --git a/src/const-point-functions.jl b/src/const-point-functions.jl index ea7bf6f9..61f682ab 100644 --- a/src/const-point-functions.jl +++ b/src/const-point-functions.jl @@ -3,12 +3,12 @@ import ..Rendering: PointFunction, FilterPointFunction import ..AccretionFormulae: _redshift_guard const filter_early_term = - FilterPointFunction((m, gp, max_time; kwargs...) -> gp.t < max_time, NaN) + FilterPointFunction((m, gp, max_time; kwargs...) -> gp.t2 < max_time, NaN) const filter_intersected = FilterPointFunction((m, gp, max_time; kwargs...) -> gp.retcode == :Intersected, NaN) -const affine_time = PointFunction((m, gp, max_time; kwargs...) -> gp.t) +const affine_time = PointFunction((m, gp, max_time; kwargs...) -> gp.t2) const shadow = affine_time ∘ filter_early_term diff --git a/src/metrics/boyer-lindquist-fo.jl b/src/metrics/boyer-lindquist-fo.jl index 22f16ab7..8045eb77 100644 --- a/src/metrics/boyer-lindquist-fo.jl +++ b/src/metrics/boyer-lindquist-fo.jl @@ -32,7 +32,7 @@ V_r = T^2 - \\Delta \\left[ (L - a E)^2 + Q \\right] From Bardeen et al. (1972) eq. (2.10), for the special case of a null-geodesic ``\\mu = 0``: ```math -V_\\theta = +V_\\theta = Q + \\cos^2 (\\theta) \\left[ a^2 E^2 - \\frac{L^2}{\\sin^2 (\\theta) } \\right]. ``` """ @@ -42,7 +42,7 @@ V_\\theta = """ $(TYPEDSIGNATURES) -The ``t`` compontent of the equation of motion for a photon around a black hole, multiplied +The ``t`` compontent of the equation of motion for a photon around a black hole, multiplied by ``\\Sigma``. From Bardeen et al. (1972) eq. (2.9d): @@ -61,7 +61,7 @@ end """ $(TYPEDSIGNATURES) -The ``r`` compontent of the equation of motion for a photon around a black hole, multiplied +The ``r`` compontent of the equation of motion for a photon around a black hole, multiplied by ``\\Sigma``. Modified from Bardeen et al. (1972) eq. (2.9a): @@ -71,7 +71,7 @@ Modified from Bardeen et al. (1972) eq. (2.9a): \\pm \\sqrt{\\lvert V_r \\rvert}, ``` -where, for implementation reason, the sign is always positive. Instead, the sign is applied +where, for implementation reason, the sign is always positive. Instead, the sign is applied in [`δ`](@ref). """ @inline function Σδr_δλ(E, L, M, Q, r, a) @@ -83,7 +83,7 @@ end """ $(TYPEDSIGNATURES) -The ``\\theta`` compontent of the equation of motion for a photon around a black hole, +The ``\\theta`` compontent of the equation of motion for a photon around a black hole, multiplied by ``\\Sigma``. Modified from Bardeen et al. (1972) eq. (2.9b): @@ -93,7 +93,7 @@ Modified from Bardeen et al. (1972) eq. (2.9b): \\pm \\sqrt{\\lvert V_\\theta \\rvert}, ``` -where, for implementation reason, the sign is always positive. Instead, the sign is applied +where, for implementation reason, the sign is always positive. Instead, the sign is applied in [`δ`](@ref). """ @inline function Σδθ_δλ(E, L, Q, a, θ) @@ -105,7 +105,7 @@ end """ $(TYPEDSIGNATURES) -The ``\\phi`` compontent of the equation of motion for a photon around a black hole, +The ``\\phi`` compontent of the equation of motion for a photon around a black hole, multiplied by ``\\Sigma``. From Bardeen et al. (1972) eq. (2.9c): @@ -136,7 +136,7 @@ S(θ, α, ω) = 1 + α * ω * sin(θ) """ $(TYPEDSIGNATURES) -Calculates and returns the observer's angles ``\\sin \\Theta`` and ``\\sin \\Phi``, where +Calculates and returns the observer's angles ``\\sin \\Theta`` and ``\\sin \\Phi``, where the parameters in the function signature correspond to the Bardeen et al. (1972) paper. From Fanton et al. (1997), eq. (74) and eq. (75): @@ -144,14 +144,14 @@ From Fanton et al. (1997), eq. (74) and eq. (75): ```math \\begin{align*} \\sin \\Theta &= -\\frac{\\alpha \\Sigma}{\\sqrt{A}} -\\left\\{ +\\frac{\\alpha \\Sigma}{\\sqrt{A}} +\\left\\{ \\beta^2 + \\left( \\alpha + a \\sin \\theta \\right)^2 + \\frac{ - A S^2 - \\left( r^2 + a^2 + a \\alpha \\sin \\theta \\right) - }{\\Delta} + A S^2 - \\left( r^2 + a^2 + a \\alpha \\sin \\theta \\right) + }{\\Delta} \\right\\}, \\\\ -\\sin \\Phi &= +\\sin \\Phi &= -\\frac{\\alpha \\Sigma \\sqrt{\\Delta}}{A S \\sin\\Theta}. \\end{align*} ``` @@ -186,10 +186,10 @@ end $(TYPEDSIGNATURES) Calculates conserved quantities -- angular momentum ``L`` +- angular momentum ``L`` - Carter parameter ``Q`` -for a photon described with position described by `x` in a Kerr spacetime given by +for a photon described with position described by `x` in a Kerr spacetime given by `p`. From Fanton et al. (1997), eq. (69): @@ -213,9 +213,9 @@ taken from eq. (72) and eq. (73). From Fanton et al. (1997), eq. (70): ```math -Q = -\\frac{P^2}{\\Delta} -- \\left( \\lambda - a \\right)^2 +Q = +\\frac{P^2}{\\Delta} +- \\left( \\lambda - a \\right)^2 - \\frac{\\Sigma^2}{A} \\left( \\frac{\\cos \\Phi}{\\Upsilon_2} \\right)^2, ``` @@ -289,10 +289,10 @@ $(TYPEDSIGNATURES) From Bardeen et al. (1972) eq. (2.21): ```math -Z_1 = -1 + \\sqrt[3]{1 - \\frac{a^2}{M^2}} -\\left[ - \\sqrt[3]{1 + \\frac{a}{M}} + \\sqrt[3]{1 - \\frac{a}{M}} +Z_1 = +1 + \\sqrt[3]{1 - \\frac{a^2}{M^2}} +\\left[ + \\sqrt[3]{1 + \\frac{a}{M}} + \\sqrt[3]{1 - \\frac{a}{M}} \\right]. ``` """ @@ -334,7 +334,7 @@ The choice of ``\\pm`` is chosen by the sign of ``a``. """ isco(M, a, ±) = M * (3 + Z₂(M, a) ± √((3 - Z₁(M, a)) * (3 + Z₁(M, a) + 2 * Z₂(M, a)))) isco(M, a) = a > 0.0 ? isco(M, a, -) : isco(M, a, +) -end # module +end # module @with_kw struct BoyerLindquistFO{T} <: AbstractFirstOrderMetricParams{T} @deftype T @@ -362,7 +362,7 @@ end function GeodesicTracer.alpha_beta_to_vel(m::BoyerLindquistFO{T}, u, α, β) where {T} sinΦ, sinΨ = __BoyerLindquistFO.sinΦsinΨ(m.M, u[2], m.a, u[3], α, β) - (β < 0.0 ? -1.0 : 1.0, sinΦ, sinΨ) + (β < 0.0 ? 1.0 : -1.0, sinΦ, sinΨ) end # special radii diff --git a/src/metrics/dilaton-axion-ad.jl b/src/metrics/dilaton-axion-ad.jl index 965a9d89..112b1801 100644 --- a/src/metrics/dilaton-axion-ad.jl +++ b/src/metrics/dilaton-axion-ad.jl @@ -10,7 +10,7 @@ using ..StaticArrays A(r, a, Δ, θ) = (r^2 + a^2)^2 - a^2 * Δ * sin(θ)^2 W(βab, θ, βa) = 1 + (βab * (2 * cos(θ) - βab) + βa^2) * csc(θ)^2 -Σ̂(Σ, β, b, r, βb, a, θ, rg) = Σ - (β^2 + 2b * r) + rg^2 * βb * (βb - 2 * (a/rg) * cos(θ)) +Σ̂(Σ, β, b, r, βb, a, θ, rg) = Σ - (β^2 + 2b * r) + rg^2 * βb * (βb - 2 * (a / rg) * cos(θ)) Δ̂(Δ, β, b, r, βb, rg) = Δ - (β^2 + 2b * r) - rg * (rg + 2b) * βb^2 Â(δ, a, Δ̂, W, θ) = δ^2 - a^2 * Δ̂ * W^2 * sin(θ)^2 δ(r, b, a) = r^2 - 2b * r + a^2 diff --git a/src/special-radii.jl b/src/special-radii.jl index 08eaebfc..b716e32a 100644 --- a/src/special-radii.jl +++ b/src/special-radii.jl @@ -5,6 +5,35 @@ Innermost stable circular orbit. """ isco(m::AbstractMetricParams{T}) where {T} = error("Not implemented for $(typeof(m)).") +function isco( + m::AbstractAutoDiffStaticAxisSymmetricParams{T}, + lower_bound, + upper_bound, +) where {T} + dE(r) = ForwardDiff.derivative(x -> CircularOrbits.energy(m, x), r) + d2E(r) = ForwardDiff.derivative(dE, r) + + Roots.find_zero((dE, d2E), (lower_bound, upper_bound)) +end + +function find_lower_isco_bound( + m::AbstractAutoDiffStaticAxisSymmetricParams{T}; + upper_bound = 100.0, + step = -0.005, +) where {T} + # iterate in reverse with a negative step + for r = upper_bound:step:1.0 + if CircularOrbits.energy(m, r) > 1.0 + return r + end + end + # for type stability + return 0.0 +end + +isco(m::AbstractAutoDiffStaticAxisSymmetricParams{T}) where {T} = + isco(m, find_lower_isco_bound(m), 100.0) + """ $(TYPEDSIGNATURES) diff --git a/test/runtests.jl b/test/runtests.jl index cd3a67a3..8a5c39ae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,3 +5,4 @@ include("smoke-tests/tracegeodesics.jl") include("smoke-tests/pointfunctions.jl") include("smoke-tests/circular-orbits.jl") include("smoke-tests/disc-profiles.jl") +include("smoke-tests/special-radii.jl") diff --git a/test/smoke-tests/disc-profiles.jl b/test/smoke-tests/disc-profiles.jl index 8683b88e..5dea16b4 100644 --- a/test/smoke-tests/disc-profiles.jl +++ b/test/smoke-tests/disc-profiles.jl @@ -6,7 +6,7 @@ m = BoyerLindquistAD(M = 1.0, a = 1.0) d = GeometricThinDisc(1.0, 40.0, deg2rad(90.0)) model = LampPostModel(h = 10.0, θ = deg2rad(0.0001)) - simsols = @time tracegeodesics( + simsols = tracegeodesics( m, model, d, @@ -16,7 +16,7 @@ ) intersected_simsols = filter(i -> i.retcode == :Intersected, simsols.u) - sd_endpoints = getendpoint(m, intersected_simsols) + sd_endpoints = map(sol -> getgeodesicpoint(m, sol), intersected_simsols) # test ensemble solution constructor vdp1 = VoronoiDiscProfile(m, d, simsols) diff --git a/test/smoke-tests/special-radii.jl b/test/smoke-tests/special-radii.jl new file mode 100644 index 00000000..1cde93d1 --- /dev/null +++ b/test/smoke-tests/special-radii.jl @@ -0,0 +1,19 @@ +# Tests to make sure the basic different special radii functions work. + +@testset "special-radii" begin + all_metrics = ( + BoyerLindquistFO(1.0, 0.998, 1.0), + BoyerLindquistFO(1.0, -0.998, 1.0), + BoyerLindquistAD(1.0, 0.998), + BoyerLindquistAD(1.0, -0.998), + JohannsenAD(M = 1.0, a = 0.998, α13 = 1.0), + JohannsenAD(M = 1.0, a = 0.998, α22 = 1.0), + DilatonAxionAD(M = 1.0, a = 0.998, β = 0.2, b = 1.0), + ) + + @testset "iscos" begin + for m in all_metrics + @test 100 > Gradus.isco(m) > 0.1 + end + end +end