Splitting a bezier curve - math

I'm working on a game for iPhone which creates a path after your character as you move (movement is similar to snake but curvy in terms of steering). The way im doing it now is by just keeping all the vertices the player has been on in an array and then just draw a circle on every one of them each and every frame.
I wanna move on to using bezier curves instead. I've done a lot of reading about them and I understand them quite well, but im not really good with math. I've came to an understanding that i should use DeCasteljau's algorithm to split the curve at a specific t but i haven't found out just which formula to use and how to implement this in code.
So what I currently have is all the controlpoints for a curve at t=1. Now i just want to get all the controlpoints for t<1. Can somebody give me an easy to understand mathematical formula for this or an implementation (preferably in python or objective-c). Maybe there's even a object that you can use in iphone sdk to split curves already?

I managed to get it working, actually really simple math. Just calculate all the tangents for the bezier and you get the points.
Here's some python:
def sliceBezier(points, t):
x1, y1 = points[0]
x2, y2 = points[1]
x3, y3 = points[2]
x4, y4 = points[3]
x12 = (x2-x1)*t+x1
y12 = (y2-y1)*t+y1
x23 = (x3-x2)*t+x2
y23 = (y3-y2)*t+y2
x34 = (x4-x3)*t+x3
y34 = (y4-y3)*t+y3
x123 = (x23-x12)*t+x12
y123 = (y23-y12)*t+y12
x234 = (x34-x23)*t+x23
y234 = (y34-y23)*t+y23
x1234 = (x234-x123)*t+x123
y1234 = (y234-y123)*t+y123
return [(x1, y1), (x12, y12), (x123, y123), (x1234, y1234)]
To call it:
sliceBezier([(point1_x, point1_y),(controlpoint1_x, controlpoint1_y),(controlpoint2_x, controlpoint2_y),(point2_x, point2_y)], 0.23);

I implemented something like that, if you have a modern browser take a look here. It's implemented in javascript, and supports also higher order beziers.

Here is a solution using Apple's SIMD api. It is concise and returns both subdivided curves.
void SplitBezier(float t,
simd_float2 cv[4],
simd_float2 a[4],
simd_float2 b[4]) {
a[0] = cv[0];
b[3] = cv[3];
a[1] = simd_mix(cv[0], cv[1], t);
b[2] = simd_mix(cv[2], cv[3], t);
auto b12 = simd_mix(cv[1], cv[2], t);
a[2] = simd_mix(a[1], b12, t);
b[1] = simd_mix(b12, b[2], t);
a[3] = b[0] = simd_mix(a[2], b[1], t);
}

Related

Setting initial (state) values for ODE system in compiled model (deSolve, Rcpp)

I am struggling with a probably minor problem while calling compiled ODEs to be solved
via the R package 'deSolve' and I seeking advice from more expert users.
Background
I have a couple of ODE systems to be solved with 'deSolve'. I have defined the ODEs in separate C++ functions (one for each model) I am calling through R in conjunction with 'Rcpp'. The initial values of the system change if the function takes input from another model (so basically to have a cascade).
This works quite nicely, however, for one model I have to set the initial parameters for t < 2. I've tried to do this in the C++ function, but it does not seem to work.
Running code example
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export("set_ODE")]]
SEXP set_ODE(double t, NumericVector state, NumericVector parameters) {
List dn(3);
double tau2 = parameters["tau2"];
double Ae2_4 = parameters["Ae2_4"];
double d2 = parameters["d2"];
double N2 = parameters["N2"];
double n2 = state["n2"];
double m4 = state["m4"];
double ne = state["ne"];
// change starting conditions for t < 2
if(t < 2) {
n2 = (n2 * m4) / N2;
m4 = n2;
ne = 0;
}
dn[0] = n2*d2 - ne*Ae2_4 - ne/tau2;
dn[1] = ne/tau2 - n2*d2;
dn[2] = -ne*Ae2_4;
return(Rcpp::List::create(dn));
}
/*** R
state <- c(ne = 10, n2 = 0, m4 = 0)
parameters <- c(N2 = 5e17, tau2 = 1e-8, Ae2_4 = 5e3, d2 = 0)
results <- deSolve::lsoda(
y = state,
times = 1:10,
func = set_ODE,
parms = parameters
)
print(results)
*/
The output reads (here only the first two rows):
time ne n2 m4
1 1 1.000000e+01 0.000000e+00 0.000000e+00
2 2 1.000000e+01 2.169236e-07 -1.084618e-11
Just in case: How to run this code example?
My example was tested using RStudio:
Copy the code into a file with the ending *.cpp
Click on the 'Source' button (or <shift> + <cmd> + <s>)
It should work also without RStudio present, but the packages 'Rcpp' and 'deSolve' must be installed and to compile the code it needs Rtools on Windows, GNU compilers on Linux and Xcode on macOS.
Problem
From my understanding, ne should be 0 for time = 1 (or t < 2). Unfortunately, the solver does not seem to consider what I have provided in the C++ function, except for the ODEs. If I change state in R to another value, however, it works. Somehow the if-condition I have defined in C++ is ignored, but I don't understand why and how I can calculate the initial values in C++ instead of R.
I was able to reproduce your code. It seems to me that this is indeed elegant, even if it does not leverage the full power of the solver. The reason is, that Rcpp creates an interface to the compiled model via an ordinary R function. So back-calls from the slovers (e.g. lsoda) to R are necessary in each time step. Such back-calls are not for the "plain" C/Fortran interface. Here communication between solver and model takes place at the machine code level.
With this informational, I can see that we don't need to expect initialization issues at the C/C++ level, but it looks like a typical case. As the model function is simply the derivative of the model (and only this). The integration is done by the solver "from outside". It calls the model always with the actual integration state, derived from the time step before (roughly speaking). Therefore, it is not possible to force the state variables to fixed values within the model function.
However, there are several options how to resolve this:
chaining of lsoda calls
use of events
The following shows a chained approach, but I am not yet sure about the initialization of the parameters in the first time segment, so may only be part of the solution.
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export("set_ODE")]]
SEXP set_ODE(double t, NumericVector state, NumericVector parameters) {
List dn(3);
double tau2 = parameters["tau2"];
double Ae2_4 = parameters["Ae2_4"];
double d2 = parameters["d2"];
double N2 = parameters["N2"];
double n2 = state["n2"];
double m4 = state["m4"];
double ne = state["ne"];
dn[0] = n2*d2 - ne*Ae2_4 - ne/tau2;
dn[1] = ne/tau2 - n2*d2;
dn[2] = -ne*Ae2_4;
return(Rcpp::List::create(dn));
}
/*** R
state <- c(ne = 10, n2 = 0, m4 = 0)
parameters <- c(N2 = 5e17, tau2 = 1e-8, Ae2_4 = 5e3, d2 = 0)
## the following is not yet clear to me !!!
## especially as it is essentially zero
y1 <- c(ne = 0,
n2 = unname(state["n2"] * state["m4"]/parameters["N2"]),
m4 = unname(state["n2"]))
results1 <- deSolve::lsoda(
y = y,
times = 1:2,
func = set_ODE,
parms = parameters
)
## last time step, except "time" column
y2 <- results1[nrow(results1), -1]
results2 <- deSolve::lsoda(
y = y2,
times = 2:10,
func = set_ODE,
parms = parameters
)
## omit 1st time step in results2
results <- rbind(results1, results2[-1, ])
print(results)
*/
The code has also another potential problem as the parameters span several magnitudes from 1e-8 to 1e17. This can lead to numerical issues, as the relative precision of most software, including R covers only 16 orders of magnitude. Can this be the reason, why the results are all zero? Here it may help to re-scale the model.

Second order delay differential equation in Julia

I'm new to Julia programming I managed to solve some 1st order DDE (Delay Differential Equations) and ODE. I now need to solve a second order delay differential equation but I didn't manage to find documentation about that (I previously used DifferentialEquations.jl).
The equation (where F is a function and τ the delay):
How can I do this?
Here is my code using the given information, it seems that the system stay at rest which is incorrect. I probably did something wrong.
function bc_model(du,u,h,p,t)
# [ u'(t), u''(t) ] = [ u[1], -u[1] + F(ud[0],u[0]) ] // off by one in julia A[0] -> A[1]
γ,σ,Q = p
ud = h(p, t-σ)[1]
du = [u[2], + Q^2*(γ/Q*tanh(ud)-u[1]) - u[2]]
end
u0 = [0.1, 0]
h(p, t) = u0
lags = [σ,0]
tspan = (0.0,σ*100.0)
alg = MethodOfSteps(Tsit5())
p = (γ,σ,Q,ω0)
prob = DDEProblem(bc_model,u0,h,tspan,p; constant_lags=lags)
sol = solve(prob,alg)
plot(sol)
The code is in fact working! It seems that it is my normalization constants that are not consistent. Thank you!
You get a state space of dimension 2, containing u = [u(t),u'(t)]. Consequently the return vector of the right-side function is [u'(t),u''(t)]. Then if ud is the delayed state [u(t-τ),u'(t-τ)] the right side function can be formulated as
[ u'(t), u''(t) ] = [ u[1], -u[1] + F(ud[0],u[0]) ]

Stochastic differential equation sensitivity analysis with specified noise

I am trying to calculate the gradient of a functional of a stochastic differential equation (SDE) solution given a specific realization of the noise. I can successfully calculate these gradients if I leave the noise unspecified, as shown in DiffEqFlux.jl: Using Other Differential Equations. I can also successfully obtain the solution to my SDE for a specific noise realization, like shown in DifferentialEquations.jl: NoiseWrapper Example. When I try and put the two together, though, the code returns an error.
Here is a minimal working example adapted from the two separate examples referenced above:
using StochasticDiffEq, DiffEqBase, DiffEqNoiseProcess, DiffEqSensitivity, Zygote
function lotka_volterra(du,u,p,t)
x, y = u
α, β, δ, γ = p
du[1] = dx = α*x - β*x*y
du[2] = dy = -δ*y + γ*x*y
end
function lotka_volterra_noise(du,u,p,t)
du[1] = 0.1u[1]
du[2] = 0.1u[2]
end
dt = 1//2^(4)
u0 = [1.0,1.0]
p = [2.2, 1.0, 2.0, 0.4]
prob1 = SDEProblem(lotka_volterra,lotka_volterra_noise,u0,(0.0,10.0),p)
sol1 = solve(prob1,EM(),dt=dt,save_noise=true)
W2 = NoiseWrapper(sol1.W)
prob2 = SDEProblem(lotka_volterra,lotka_volterra_noise,u0,(0.0,10.0),p,noise=W2)
sol2 = solve(prob2,EM(),dt=dt)
function predict_sde1(p)
Array(concrete_solve(remake(prob1,p=p),EM(),dt=dt,sensealg=ForwardDiffSensitivity(),saveat=0.1))
end
loss_sde1(p) = sum(abs2,x-1 for x in predict_sde1(p))
loss_sde1(p)
# This gradient is successfully calculated
Zygote.gradient(loss_sde1,p)
function predict_sde2(p)
W2 = NoiseWrapper(sol1.W)
Array(concrete_solve(remake(prob2,p=p,noise=W2),EM(),dt=dt,sensealg=ForwardDiffSensitivity(),saveat=0.1))
end
loss_sde2(p) = sum(abs2,x-1 for x in predict_sde2(p))
# This loss is successfully calculated
loss_sde2(p)
# This gradient calculation raises and error
Zygote.gradient(loss_sde2,p)
The error I get at the end of running this code is
TypeError: in setfield!, expected Float64, got ForwardDiff.Dual{Nothing,Float64,4}
Stacktrace:
[1] setproperty! at ./Base.jl:21 [inlined]
...
followed by an interminable conclusion to the stacktrace (I can post it if you think it would be helpful, but since it's longer than the rest of this question I'd rather not clutter things up off the bat).
Is calculating gradients for SDE problems with specified noise realizations not currently supported, or am I just not making the appropriate function calls? I could easily believe the latter, since it was a bit of a struggle just to get to the point where the working parts of the above code worked, but I couldn't find any clue as to what I had incorrectly supplied after stepping through this code with the Juno debugger.
As a StackOverflow solution, you can use ForwardDiffSensitivity(convert_tspan=false) to work around this. Working code:
using StochasticDiffEq, DiffEqBase, DiffEqNoiseProcess, DiffEqSensitivity, Zygote
function lotka_volterra(du,u,p,t)
x, y = u
α, β, δ, γ = p
du[1] = dx = α*x - β*x*y
du[2] = dy = -δ*y + γ*x*y
end
function lotka_volterra_noise(du,u,p,t)
du[1] = 0.1u[1]
du[2] = 0.1u[2]
end
dt = 1//2^(4)
u0 = [1.0,1.0]
p = [2.2, 1.0, 2.0, 0.4]
prob1 = SDEProblem(lotka_volterra,lotka_volterra_noise,u0,(0.0,10.0),p)
sol1 = solve(prob1,EM(),dt=dt,save_noise=true)
W2 = NoiseWrapper(sol1.W)
prob2 = SDEProblem(lotka_volterra,lotka_volterra_noise,u0,(0.0,10.0),p,noise=W2)
sol2 = solve(prob2,EM(),dt=dt)
function predict_sde1(p)
Array(concrete_solve(remake(prob1,p=p),EM(),dt=dt,sensealg=ForwardDiffSensitivity(convert_tspan=false),saveat=0.1))
end
loss_sde1(p) = sum(abs2,x-1 for x in predict_sde1(p))
loss_sde1(p)
# This gradient is successfully calculated
Zygote.gradient(loss_sde1,p)
function predict_sde2(p)
Array(concrete_solve(prob2,EM(),prob2.u0,p,dt=dt,sensealg=ForwardDiffSensitivity(convert_tspan=false),saveat=0.1))
end
loss_sde2(p) = sum(abs2,x-1 for x in predict_sde2(p))
# This loss is successfully calculated
loss_sde2(p)
# This gradient calculation raises and error
Zygote.gradient(loss_sde2,p)
As a developer... this isn't a nice solution and our default should be better here. I'll work on this. You can track the development here https://github.com/JuliaDiffEq/DiffEqSensitivity.jl/issues/204. It'll probably get solved in an hour or so.
Edit: The fix is released and your original code works.

Complex PDE (Ginzburg Landau) in Julia with Pseudo-Spectral method

I want to teach myself about solving PDEs with Julia and I am trying to solve the complex Ginzburg Landau equation (CGLE) with a pseudospectral method in Julia now. However, I struggle with it and I am a bit of ideas what to try.
The CGLE reads:
With Fourier transform and its inverse , I can transform into the spectral form:
This is for example also given in this old script I found (https://www.uni-muenster.de/Physik.TP/archive/fileadmin/lehre/NumMethoden/SoSe2009/Skript/script.pdf) From the same source I know, that alpha=1, beta=2 and initial conditions with small noise of order 0.01 around 0 should result in plane waves as solutions. Thats what I want to test first.
Following the very nice tutorial from Chris Rackauckas (https://youtu.be/okGybBmihOE), I tried to use ApproxFun and DifferentialEquations in the following way to solve this problem:
EDIT: I corrected two mistakes from the original post, a missing dot and minus sign, but the code is still not giving the correct results
EDIT2: Figured out that I computed the wavenumber k completely wrong
using ApproxFun
using DifferentialEquations
F = Fourier()
n = 512
L = 100
T = ApproxFun.plan_transform(F, n)
Ti = ApproxFun.plan_itransform(F, n)
x = collect(range(-L/2,stop=L/2, length=n))
k = points(F, n)
alpha = 1im
beta = 2im
u0 = 0.01*(rand(ComplexF64, n) .- 0.5)
Fu0 = T*u0
function cgle!(du, u, p, t)
a, b, k, T, Ti = p
invu = Ti*u
du .= (1.0 .- k.^2*(1.0 .+a)).*u .- T*( (1.0 .+b) .* (abs.(invu)).^2 .* invu)
end
pars = alpha, beta, k, T, Ti
prob = ODEProblem(cgle!, Fu0, (0.,50.), pars)
u = solve(prob, Rodas5(autodiff=false))
# plotting on a equidistant time stepping
t = collect(range(0, stop=50, length=1000))
sol = zeros(eltype(u),(n, length(t)))
for it in eachindex(t)
sol[:,it] = Ti*u(t[it])
end
IM = PyPlot.imshow(real.(sol))
cb = PyPlot.colorbar(IM, orientation="horizontal")
gcf()
(edited) I tried different solvers, as also recommended in the video, some apparently wont work for complex numbers, some do, but when I run this code it does not give the expected results. The solution remain very small in value and it wont result in the plane waves that actually should be the result. I also tested other intial conditions that should result in chaos, but those result in the same very small solutions as well. I also alternativly used an explicit Laplace Operator with ApproxFun, but the results are the same. My problem here, is that I am neither really an expert with PDE mathemitacaly, nor with their numerical treatment, so far I mainly worked with ODEs.
EDIT2 This now seems to work more or less. I am still wondering about some things though
How can I compute this on a specified domain like , I am seriously confused about how this works with ApproxFun, as far as I can see the wavenumbers k should be (2pi/L)*[-N/2+1 ; N/2 -1], but I am not so sure about how to do this with ApproxFun
https://codeinthehole.com/tutorial/coherent.html shows the different dynamic regimes / phase portrait of the equation. While I can reproduce some of them, some don't seem to work, like the Spatio-temporal intermittency
EDIT 3: I solved these issues by using FFTW directly instead of ApproxFun. In case somebody knows how to this with ApproxFun, I would still be interessted though. Below follows the code with FFTW (it is also a bit more optimized for performance)
begin
using FFTW
using DifferentialEquations
using PyPlot
end
begin
n = 512
L = 200
n2 = Int(n/2)
alpha = 2im
beta = 1im
x = range(-L/2,stop=L/2,length=n)
u0 = 0.01*(rand(ComplexF64, n) .- 0.5)
k = [0:n/2-1; 0; -n/2+1:-1] .*(2*pi/L);
k2 = k.*k
k2[n2 + 1] = (n2*(2*pi/L))^2
T = plan_fft(u0)
Ti = plan_ifft(T*u0)
LinOp = (1.0 .- k2.*(1.0 .+alpha))
Fu0 = T*u0
end
function cgle!(du, u, p, t)
LinOp, b, T, Ti = p
invu = Ti*u
du .= LinOp.*u .- T*( (1.0 .+b) .* (abs.(invu)).^2 .* invu)
end
pars = LinOp, beta, T, Ti
prob = ODEProblem(cgle!, Fu0, (0.,100.), pars)
#time u = solve(prob)
t = collect(range(0, stop=50, length=1000))
sol = zeros(eltype(u),(n, length(t)))
for it in eachindex(t)
sol[:,it] = Ti*u(t[it])
end
IM = PyPlot.imshow(abs.(sol))
cb = PyPlot.colorbar(IM, orientation="horizontal")
gcf()
EDIT 4: Rodas turned out to be a extremly slow solver for this case, just using the default works out nicely for me.
Any help is appreciated.
du = (1. .- k.^2*(1. .+(im*a))).*u + T*( (1. .+(im*b)) .* abs.(invu).^2 .* invu)
Notice that is replacing the pointer to du, not updating it. Use something like .= instead:
du .= (1. .- k.^2*(1. .+(im*a))).*u + T*( (1. .+(im*b)) .* abs.(invu).^2 .* invu)
Otherwise your derivative is just 0.

Julia program on ephemerides shows inadequate answers

While solving a differential equation on satellite motion encountered this error:
dt <= dtmin. Aborting. If you would like to force continuation with dt=dtmin, set force_dtmin=true
Here is my code:
using JPLEphemeris
spk = SPK("some-path/de430.bsp")
jd = Dates.datetime2julian(DateTime(some-date))#date of the calculatinons
yyyy/mm/dd hh/mm/ss
jd2 = Dates.datetime2julian(DateTime(some-date))#date of the calculatinons
yyyy/mm/dd hh/mm/ss
println(jd)
println(jd2)
st_bar_sun = state(spk, 0, 10, jd)
st_bar_moon_earth = state(spk, 0, 3, jd)
st_bar_me_earth = state(spk, 3, 399, jd)
st_bar_me_moon = state(spk, 3, 301, jd)
moon_cord = st_bar_me_moon - st_bar_me_earth
a = st_bar_moon_earth + st_bar_me_earth
sun_cord = st_bar_sun - a
println(sputnik_cord)
sputnik_cord = [8.0,8.0,8.0,8.0,8.0,8.0,8.0]
moon_sputnik = sputnik_cord - moon_cord
sun_sputnic = sputnik_cord - sun_cord
Req = 6378137
J2 = 1.08262668E-3
GMe = 398600.4418E+9
GMm = 4.903E+12
GMs = 1.32712440018E+20
function f(dy,y,p,t)
re2=(y[1]^2 + y[2]^2 + y[3]^2)
re3=re2^(3/2)
rs3 = ((y[1]-sun_cord[1])^2 + (y[2]-sun_cord[2])^2 + (y[3]-sun_cord[3])^2)^(3/2)
rm3 = ((y[1]-moon_cord[1])^2 + (y[2]-moon_cord[2])^2 + (y[3]-moon_cord[3])^2)^(3/2)
w = 1 + 1.5J2*(Req*Req/re2)*(1 - 5y[3]*y[3]/re2)
w2 = 1 + 1.5J2*(Req*Req/re2)*(3 - 5y[3]*y[3]/re2)
dy[1] = y[4]
dy[2] = y[5]
dy[3] = y[6]
dy[4] = -GMe*y[1]*w/re3
dy[5] = -GMe*y[2]*w/re3
dy[6] = -GMe*y[3]*w2/re3
end
function f2(dy,y,p,t)
re2=(y[1]^2 + y[2]^2 + y[3]^2)
re3=re2^(3/2)
rs3 = ((y[1]-sun_cord[1])^2 + (y[2]-sun_cord[2])^2 + (y[3]-sun_cord[3])^2)^(3/2)
rm3 = ((y[1]-moon_cord[1])^2 + (y[2]-moon_cord[2])^2 + (y[3]-moon_cord[3])^2)^(3/2)
w = 1 + 1.5J2*(Req*Req/re2)*(1 - 5y[3]*y[3]/re2)
w2 = 1 + 1.5J2*(Req*Req/re2)*(3 - 5y[3]*y[3]/re2)
dy[1] = y[4]
dy[2] = y[5]
dy[3] = y[6]
dy[4] = -GMe*y[1]*w/re3 - GMm*y[1]/rm3 - GMs*y[1]/rs3
dy[5] = -GMe*y[2]*w/re3 - GMm*y[2]/rm3 - GMs*y[2]/rs3
dy[6] = -GMe*y[3]*w2/re3- GMm*y[3]/rm3 - GMs*y[3]/rs3
end
y0 = sputnik_cord
jd=jd*86400
jd2=jd2*86400
using DifferentialEquations
prob = ODEProblem(f,y0,(jd,jd2))
sol = solve(prob,DP5(),abstol=1e-13,reltol=1e-13)
prob2 = ODEProblem(f2,y0,(jd,jd2))
sol2 = solve(prob2,DP5(),abstol=1e-13,reltol=1e-13)
println("Without SUN and MOON")
println(sol[end])
for i = (1:6)
println(sputnik_cord[i]-sol[end][i])
end
println("With SUN and MOON")
println(sol2[end])
What(except the values) can be a reason of this? It worked well before I added the terms with sun_coords and moon_coords in definition of dy[4], dy[5], dy[6] in function f2(As I suppose the function f1 works correctly).
There are two reasons this could be happening. For one, you could see this error because the model is unstable due to implementation issues. If you accidentally put something in wrong, the solution may be diverging to infinity and as it diverges the time steps shorten and it exists with this error.
Another thing that can happen is that your model might be stiff. This can happen if you have large time scale differences between different components. In that case, DP5(), an explicit Runge-Kutta method, is not an appropriate algorithm for this problem. Instead, you will want to look at something for stiff equations. I would recommend giving Rosenbrock23() a try: it's not the fastest but it's super stable and if the problem is integrable it'll handle it.
That's a very good way to diagnose these issues: try other integrators. Try Rosenbrock23(), CVODE_BDF(), radau(), dopri5(), Vern9(), etc. If none of these are working, then you will have just tested your algorithm with a mixture of the most well-tested algorithms (some of them Julia implementations, but others are just wrappers to standard classic C++ and Fortran methods) and this suggests that the issue is in your model formulation and not a peculiarity of a specific solver on this problem. Since I cannot run your example (you should make your example runnable, i.e. no extra files required, if you want me to test things out), I cannot be sure that your model implementation is correct and this would be a good way to find out.
My guess is that the model you have written down is not a good implementation because of floating point issues.
GMe = 398600.4418E+9
GMm = 4.903E+12
GMs = 1.32712440018E+20
these values have only precision 16 digits less than their prescribed value:
> eps(1.32712440018E+20)
16384.0
> 1.32712440018E+20 + 16383
1.3271244001800002e20
> 1.32712440018E+20 + 16380
1.3271244001800002e20
> 1.32712440018E+20 + 16000
1.3271244001800002e20
Notice the lack of precision below the machine epsilon for this value. Well, you're asking for
sol = solve(prob,DP5(),abstol=1e-13,reltol=1e-13)
precision to 1e-13 when it's difficult to be precise to 1e5 given the size of your constants. You need to adjust your units or utilize BigFloat numbers if you want this kind of precision on this problem. So what's likely going on is that the differential equation solvers are realizing that they are not hitting 1e-13 precision, shrinking the stepsize, and repeating this indefinitely (because they can never hit 1e-13 precision due to the size of the floating point numbers) until the stepsize is too small and it exits. If you change the units so that way the constants are more reasonable in size then you can fix this problem.

Resources