astro-group-bristol / gradus.jl Goto Github PK
View Code? Open in Web Editor NEWExtensible spacetime agnostic general relativistic ray-tracing (GRRT).
Home Page: https://astro-group-bristol.github.io/Gradus.jl/dev/
License: MIT License
Extensible spacetime agnostic general relativistic ray-tracing (GRRT).
Home Page: https://astro-group-bristol.github.io/Gradus.jl/dev/
License: MIT License
Use a quad-tree to make resolutions of any 2d plot better near distorted edges better, e.g. for exclusions zones in naked singularities, or disc edges or close to the event horizon in rendering.
Would work similarly to adaptive refinement, and could maybe even vendor a library for it?
Tracking allocations and analyzing with Coverage.jl shows that we allocate enormously during problem setup:
CoverageTools.MallocInfo(0, "dev/Gradus/src/metrics/boyer-lindquist-fo.jl.101246.mem", 335)
CoverageTools.MallocInfo(736, "dev/Gradus/src/Rendering/render.jl.101246.mem", 80)
CoverageTools.MallocInfo(736, "dev/Gradus/src/Rendering/render.jl.101246.mem", 81)
CoverageTools.MallocInfo(6528, "dev/Gradus/src/Rendering/render.jl.101246.mem", 77)
CoverageTools.MallocInfo(7360, "dev/Gradus/src/GeodesicTracer/constraints.jl.101246.mem", 29)
The matrix dispatch already has inplace modifications, but for e.g. vectors of static vectors:
Gradus.jl/src/GeodesicTracer/constraints.jl
Lines 26 to 30 in 4356a1a
There is no need to use map here, we should be able to re-use the same container.
The top row of an image calculated with rendergeodesics
is actually the bottom row.
Keyword arguments are passed to the tracer in trace_equitorial_circular_orbit
, since upper_rate
and lower_rate
are not seperately captured / kwargs
passed to both the trace function and solve function.
This is probably related to reusing the integrator state in each thread, but e.g. Feagin10 or Vern6 fail dramatically when used in the context of e.g. lineprofiles
, and I think this is the reason.
The disc intersection algorithm is poor, and may return very different results for slightly different initial parameters (hinted to by the tearing effects at the edge of the disc for large step sizes).
This really needs to be fixed, as it is impacting tolerances for all subsequent calculations.
Using this issue to track progress on clamping down why the transfer function integration and the regular binning method for calculating the line profiles disagree.
Crux of the problem:
Code to reproduce
using Gradus
using Plots
function calculate_line_profile_binned(m, x, d, bins; maxrₑ = 500.0, minrₑ = Gradus.isco(m) + 1e-2)
plane = PolarPlane(GeometricGrid(); Nr = 1000, Nθ = 1000, r_max = 2 * maxrₑ)
_, f = lineprofile(
m,
x,
d,
algorithm = BinnedLineProfile(),
verbose = true,
bins = bins,
maxrₑ = maxrₑ,
minrₑ = minrₑ,
plane = plane
)
f
end
function calculate_line_profile(m, x, d, bins; maxrₑ = 500.0, minrₑ = Gradus.isco(m) + 1e-2)
_, f = lineprofile(
m,
x,
d,
algorithm = CunninghamLineProfile(),
verbose = true,
bins = bins,
maxrₑ = maxrₑ,
minrₑ = minrₑ,
numrₑ = 200,
β₀ = 2,
Nr = 3000,
)
f
end
bins = collect(range(0.1, 1.4, 200))
m = KerrMetric(1.0, 0.998)
x = SVector(0.0, 1000.0, deg2rad(45), 0.0)
ssd = ShakuraSunyaev(m, eddington_ratio = 0.3)
tf30 = @time calculate_line_profile(m, x, ssd, bins)
bn30 = @time calculate_line_profile_binned(m, x, ssd, bins)
plot(bins, tf30, label = "transfer functions")
plot!(bins, bn30, label = "binned")
There already is a function in GradusBase defined for mapping vectors to local sky angles
Gradus.jl/src/GradusBase/geometry.jl
Lines 1 to 3 in 4356a1a
but this is only implmented for the Kerr case at the moment. We either would need a good way of calculating this in other metrics, else find a reliable way to do parallel transport.
Create a vector wrapper type that knows which coordinates it is defined in; essentially an SVector with some additional parameter that encodes if it's a spherical or cartesian vector.
This would allow much more expressive coordinate transformation functions, and fewer mistakes made by users.
Originally posted by phajy February 6, 2023
I'm trying to run the 2D transfer function example you shared earlier, @fjebaker, but I can't get it to work because I think a few things have changed under the hood in Gradus. The two errors I get are that getgeodesicpoint not defined
and radial_sampler not defined
.
Continuing discussion in this issue.
A test runner for PRs should be opened that ensures the examples still pass, as they occasionally break without breaking smoke tests (which also implies the smoke test coverage is poor), e.g. #76
Makes sense doesn't it. They are the same thing, and in cylindrical coordinates, the (projected) radius is denoted
I was going to post this as a follow-up in Precision tolerances but have decided to make it a "new" issue instead.
I've encountered a (possibly?) precision-related error: Roots.ConvergenceFailed("Algorithm failed to converge")
. Here's an example to investigate (based on this example).
a = 0.0
d = GeometricThinDisc(0.0, 400.0, π / 2)
u = @SVector [0.0, 1000.0, deg2rad(60), 0.0]
m = KerrMetric(1.0, a)
# maximal integration radius
maxrₑ = 200.0
# emissivity function
ε(r) = r^(-3)
# g grid to do flux integration over
gs = range(0.0, 1.2, 500)
_, flux = lineprofile(gs, ε, m, u, d, maxrₑ = maxrₑ)
I've briefly experimented with some of the hidden parameters, e.g., increasing the number of g* points or reducing the tolerance but with no success. This differs from the example by having a much larger outer radius where
Modify cross_section
and related functions to receive only AsymmetricThickDisc
that permit more complex structures, such as thick discs that change in the
This has a number of things to it
This issue is carried over from when we used SageMath to generate the symbolic geodesic equations:
JP metric has the sign of the spin reversed, e.g. a=1 corresponds to a=-1 for Kerr.
The issue was not the sign convention as mentioned in the issue above, but is related to using the wrong sign in the constraint equation.
I am yet to check if the AD methods also suffer from this, but leaving the issue open until I've had a chance to confirm.
If the outer radius on the image plane is not a few factors of maxrₑ
, which may occur if a user is specifying the plane, then there is no warning that the sampled region may not actually be the desired region.
The image_plane
function should be reworked, so that the planes themselves store parameters of the plane, but the size of it is scaled internally, so that this mistmatch cannot occur.
This should also still be useable in the rendergeodesic
functions however, so some care in the design needs to be taken.
As part of our tests, we should also keep a benchmark suite to track the performance of our code, and make sure new changes don't accidentally cause our integration to slow down.
This should have --math-mode=ieee
and a variety integration tolerances and image sizes to ensure our methods scale well, and don't git the GC too hard (as they have in the past).
The ThinDisc has the OpticallyThin trait.
I doubt that the relativistic beaming is being properly accounted for, since we do not make a Lorentz transform before the tetrad transformation.
It would be better to get the transfer functions to sample evenly in
The proposed algorithm change is
An issue that might crop up here are those transfer functions where the inferred extrema are worse than the area extrema. In these cases we normally see an error of 1e-4 or so, but that could still be quite damaging to the calculation. Perhaps, however, we can spend more time in the extrema optimizer at the trade off of needing fewer
x
, velocity to be v
, disc velocities to be u
.At the moment the user has to do some pretty non-trivial arithmetic to work out what the corresponding impact parameter axes are when using the *rendergeodesics
methods. I propose instead that a new API works like this:
a, b, img = rendergeodesics(...)
where a and b are the
Could maybe make it easier to switch between pinhole and photographic plane observers, since at the moment we're hardcoding pinhole observer.
To facilitate a nicer analysis API, I suggest we move to wrap the SciMLBase types in our own struct, so that we can dispatch methods more targetedly. This would allow us to add plotting recipes unique to our package, allow overloads with the statistics community's fit
functions, and generally let us be more expressive.
We could then also track which coordinates a given solution is in (Cartesian vs spherical), and provide mappings between them.
Takes about two minutes to pre-compile. This should really be reduced, there is no reason for this to be so long.
See comments in:
MacOS seems to produce wildly different values in the tests in the CI. Need to find out why, and then add other OSs.
There is a relatively small chance that this happens:
nested task error: Something went wrong. Integrator stepped past tstops but the algorithm was dtchangeable. Please report this error.
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] handle_tstop! ...
Given that it is non-deterministic, it is almost certainly related to some threading issue and a race condition being hit.
Turns out this is all related to weird overflows with the progress meter. Commenting out any and all progress meter calls fixes everything, so we need some sort of thread safety here / it is likely that copying the meter state per threads is the culprit.
I'm trying to calculate the ISCO for a black hole spinning at (almost) the limit allowed by the spacetime. Perhaps there is some very small tolerance issue with the integration? It is going to be numerically difficult to figure out both 1) what is the maximum allowed value of
Here's an example
m = JohannsenPsaltisMetric(M = 1.0, a = 0.8168816881688169, ϵ3 = 0.8)
is_naked_singularity(m)
Gradus.isco(m)
says that the singularity is not naked but when calculating the ISCO gives an error
ERROR: ArgumentError: The interval [a,b] is not a bracketing interval.
You need f(a) and f(b) to have different signs (f(a) * f(b) < 0).
Consider a different bracket or try fzero(f, c) with an initial guess c.
I've labelled this "bug" but perhaps it isn't really. If I make
Not urgent!
Self descriptive
For deviations from 90 degress in theta, the beta impact parameter gets stretched, distorting the aspect ratio of the disc. Most noticeable for <60 degrees.
apply
is returning a flat array instead of matching the behaviour of rendergeodesics
with a point function.
There's several arbitrarily set precisions in the tests, and more generally some unusually high discrepencies in the values calculated between different methods (see brief mention in this blog post).
We should have a suite of tests that
I think as an aim, we want our methods to agree within 1e-8 or 1e-9 for relative tolerances. In the literature this isn't really discussed other than occasionally ensuring
and even that has a big "-ish" stamped onto the end, as some authors don't even really seem to check that, and others just check whether it agrees with some other simulation.
I think we can do better and quantify the errors more.
Update the getting started guide and docstrings to reflect the definition of impact parameters
and how they are mapped from an (stationary) LNRF.
The default value of extrapolate
in DataInterpolations has been changed to false
. See SciML/DataInterpolations.jl#194.
This can, in some cases, break Gradus.
Adding ;extrapolate=true)
to all of the DataInterpolations.LinearInterpolation
calls in the following lines seems to fix the problem.
Gradus.jl/src/orbits/orbit-solving.jl
Lines 115 to 121 in e9e8632
I would guess that extrapolations near the edge of the domain are expected. Not sure if this needs to be looked at a bit more?
The project is a little over a year old now and the code bloat is apparent.
Need to go through each function with a fine toothed comb and see what can be made leaner, or even just removed entirely. Also there are a number of bad habits that should be corrected, such as
where {T}
unless actually neededgetgeodesicpoint
vs just process
, as that is what it's really doing.Also worth looking into
before getting too carried away with more features.
Currently, there are some implemenations that are
metric_components(m::AbstractMetric, x::SVector{4})
and others that are
metric_components(m::AbstractMetric, xt::SVector{2}) # only r and theta components
metric_components(m::AbstractMetric, xt::NTuple{2}) # same as above
The return type is also all over the place.
This is needlessly confusing. Suggest we differentiate metric_components
and metric_elements
, the prior is used as the implementation, and metric_elements
is used as an accessor method?
This also seems complex though...
Instead of pre-generating the whole u
and v
array, there should be a dispatch of tracegeodesic where both position and velocity are functions, such that these can be generated on the fly to avoid un-needed allocations.
As described in the comments of
when the observer angle is close to 85, the root finder misses the disc underneath the equitorial plane.
Code to reproduce (v0.2.3):
using Gradus, StaticArrays, Plots
m = KerrMetric(M=1.0, a=0.998)
d = GeometricThinDisc(0.0, 3000.0, π/2)
angle = 85
u = @SVector [0.0, 1000.0, deg2rad(angle), 0.0]
# errors here
ctf = cunningham_transfer_function(m, u, d, 4.0)
mask = @. (ctf.g✶ > 0.001) & (ctf.g✶ < 0.999)
plot(ctf.g✶[mask], ctf.f[mask], label="θᵢ=$angle", xlabel="g✶", ylabel="f")
Setting e.g. offset_max = 15.0
seems to fix it, but produces visible errors in the resulting transfer function:
This was previously discussed in
but updating the state of the problem.
The render caches are quite enormous, much larger than they really should be at 280MB for a 1250x1750 render. I mentioned writing a custom serialiser / de-serialiser to this end for JLD2, which is still an option.
Perhaps we also look into StructArrays.jl for this? May help with storing the geodesic point types, and performance, since we commonly access similar fields (e.g., position).
Opening an issue so I remember to address this later: when using the (forthcoming) LNRF calculator, the tests for the Dilaton-Axion metric fail, since
4×4 Matrix{Float64}:
1.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 1.0 0.0
2.22045e-16 0.0 0.0 -1.0
is not Minkowski. We have the correct signature, but it seems to be in the wrong order?
It's been a few weeks since the last release, and much has changed in the main branch. This issue is to track what still needs to be done before 0.4.17 0.5.0 release:
From #143
The seam in this figure corresponds to the bin edge
The gtol
parameter is a mess. Trying to do something "sophisticated" like in #172 has led to terrible impulse response functions:
Using the old linear gtol * r
scaling is already much much better:
Looking at the 2d integrated heatmap:
"Tuning" (read: randomizing) the magic constants in the _gtol_error
function gives this
This is most likely due to Jacobians going all over the place at the boundaries of _gtol_error
, which means we either need any old
I'm really not happy with how the transfer functions are currently defined and used. Since the integration domain is over
which is then used to find the flux through
This is itself used in the integral
It's pretty obvious that most of the terms cancel. Care when integrating comes from the regions where
The transfer functions could far more conveniently be defined
such that
and the integration is then performed directly over
Possible gripes can easily be fixed in this formalism too:
It seems the transfer functions could be so much easier to use when defined this way.
The equitorial disc velocities used to calculate the redshift are hard coded for the Kerr metric. This should be seperated out, and then allowed to be made generic.
Additionally, should try and build in some generic analytic solutions for e.g. all static, axis-symmetric metrics.
Applies to the redshift calculations in the plunging region. See discussion in:
parity of the radial component of velocity seems to be opposite under time reversal
We need a justification for this.
This has been a point of contention for a while. The current state of DiffEqGPU.jl does not support SecondOrderODEProblems out of the box, and a patch I wrote for DiffEqGPU's problematic parse reveals a more fundemental problem:
We also need to evaluate to what degree we want to provide GPU support for the integration problems, as it may reduce us to Float32 operations, and be an awful lot of work for marginal performance gain.
A single trace is a relatively cheap problem, in the micro-second solve time, so it should be entirely feasible and logical to chuck this on the GPU -- the question is just how.
As a note, the above issue with SecondOrderODEProblems can also be solved by converting our problem to a first order one and manually updating the position vector.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.