I'm quite new to Julia and I'm currently learning how to solve differential equations with it. I tried to run a simple pre-made code by Christopher Rackauckas, but I got an error. The code can be found here. I will also write it here:
using DifferentialEquations
alpha = 0.5 #Setting alpha to 1/2
f(y,t) = alpha*y
u0 = 1.5
prob = ODEProblem(f,u0)
timespan = [0,1] # Solve from time = 0 to time = 1
sol = solve(prob,timespan) # Solves the ODE
using Plots
plot(sol) # Plots the solution using Plots.jl
And the error I'm getting looks like this:
LoadError: MethodError: no methof matching DiffEqBase.ODEProblem{uType,tType,isinplace,FC;MM}(::#f, ::Float64)
I also tried to run other similar codes and even removed the DifferentialEquations.jl -package and then reinstalled it, but nothing changed.
Anyone more experienced having an idea what I might be doing wrong?
The issue is that blog post is from quite a long time ago. Or at least, DifferentialEquations 1.0 had a few breaking changes in this part. You should use the tutorial instead, which fixes this example to the newest version. The solution is:
using DifferentialEquations
alpha = 0.5 #Setting alpha to 1/2
f(y,t) = alpha*y
u0 = 1.5
tspan = (0.0,1.0) # Solve from time = 0 to time = 1
prob = ODEProblem(f,u0,tspan)
sol = solve(prob) # Solves the ODE
using Plots
plot(sol) # Plots the solution using Plots.jl
But now that I know people are still looking at that old post, I updated its syntax to be correct.
Related
I've trying to use DifferentialEquations.jl from Julia. I managed to get it working but I'd like know how to generate output at specific time points. The docs aren't clear on this and I've not found a single example that does this. The code I'm currently using is from the tutorial:
using DifferentialEquations
using Plots
function lorenz(du,u,p,t)
du[1] = 10.0*(u[2]-u[1])
du[2] = u[1]*(28.0-u[3]) - u[2]
du[3] = u[1]*u[2] - (8/3)*u[3]
end
u0 = [1.0;0.0;0.0]
tspan = (0.0,100.0)
prob = ODEProblem(lorenz,u0,tspan)
sol = solve(prob)
plot(sol,vars=(1,2,3))
Currently it generates 1287 points, and I've no idea how it decides that. My question is what if I wanted to generate 20 points between the span 0 to 100?
The ODE tutorial section on "Controlling the Solvers" demonstrates using saveat for this purpose. The demonstration is:
sol = solve(prob,reltol=1e-6,saveat=0.1)
which will save at 0.0, 0.1, ... in your example. Right below that it is noted that:
More generally, saveat can be any collection of time points to save at.
So for example, we can use save at to save only at t=30, 60, and 78 as follows:
sol = solve(prob,saveat=[30.0,60.0,78.0])
These examples should put you in the right direction. For more details, see the Output Controls section of the documentation.
I solved a system of differential equations (van der Pol equations) using DifferentialEquations.
I would like to export the solution. To do this I used convert(Array,sol), however, the converted solution is not the same as the solution I get by sol.
See the code below for more explanation:
using DifferentialEquations
using Plots
function fun(du,u,p,t)
du[1] = u[2]
du[2] = 1000*(1-u[1]^2)*u[2]-u[1]
end
u0 = [2.0,0.0]
tspan = (0.0,3000.0)
prob = ODEProblem(fun,u0,tspan)
sol = solve(prob)
a = convert(Array,sol)#Here I tried to convert the solution to an array
plot(a[1,:])
plot(sol,vars = 1)
a = convert(Array,sol)
plot(a[1,:]) returns:
plot(sol,vars = 1) returns:
The converted solution is the same as what is contained in sol. The problem lies in the fact that the step size for the variable in the x axis (here it is time) is not uniform. So only plotting using plot(a[1,:]) is not enough. We must provide at what time the solution has the value it has. Using plot(sol.t,a[1,:]) plot the right answer.
I am new to Julia, I would like to solve this system:
where k1 and k2 are constant parameters. However, I=0 when y,0 or Ky otherwise, where k is a constant value.
I followed the tutorial about ODE. The question is, how to solve this piecewise differential equation in DifferentialEquations.jl?
Answered on the OP's cross post on Julia Discourse; copied here for completeness.
Here is a (mildly) interesting example $x''+x'+x=\pm p_1$ where the sign of $p_1$ changes when a switching manifold is encountered at $x=p_2$. To make things more interesting, consider hysteresis in the switching manifold such that $p_2\mapsto -p_2$ whenever the switching manifold is crossed.
The code is relatively straightforward; the StaticArrays/SVector/MVector can be ignored, they are only for speed.
using OrdinaryDiffEq
using StaticArrays
f(x, p, t) = SVector(x[2], -x[2]-x[1]+p[1]) # x'' + x' + x = ±p₁
h(u, t, integrator) = u[1]-integrator.p[2] # switching surface x = ±p₂;
g(integrator) = (integrator.p .= -integrator.p) # impact map (p₁, p₂) = -(p₁, p₂)
prob = ODEProblem(f, # RHS
SVector(0.0, 1.0), # initial value
(0.0, 100.0), # time interval
MVector(1.0, 1.0)) # parameters
cb = ContinuousCallback(h, g)
sol = solve(prob, Vern6(), callback=cb, dtmax=0.1)
Then plot sol[2,:] against sol[1,:] to see the phase plane - a nice non-smooth limit cycle in this case.
Note that if you try to use interpolation of the resulting solution (i.e., sol(t)) you need to be very careful around the points that have a discontinuous derivative as the interpolant goes a little awry. That's why I've used dtmax=0.1 to get a smoother solution output in this case. (I'm probably not using the most appropriate integrator either but it's the one that I was using in a previous piece of code that I copied-and-pasted.)
Is it possible to create a simple model of a bouncing ball, using Julia's equation solvers?
I started with this:
using ODE
function bb(t, f)
(y, v) = f
dy_dt = v
dv_dt = -9.81
[dy_dt, dv_dt]
end
const y0 = 50.0 # height
const v0 = 0.0 # velocity
const startpos = [y0; v0]
ts = 0.0:0.25:10 # time span
t, res = ode45(bb, startpos, ts)
which produces useful-looking numbers:
julia> t
44-element Array{Float64,1}:
0.0
0.0551392
0.25
0.5
0.75
1.0
⋮
8.75
9.0
9.25
9.5
9.75
10.0
julia> res
44-element Array{Array{Float64,1},1}:
[50.0,0.0]
[49.9851,-0.540915]
[49.6934,-2.4525]
[48.7738,-4.905]
[47.2409,-7.3575]
⋮
[-392.676,-93.195]
[-416.282,-95.6475]
[-440.5,-98.1]
But somehow it needs to intervene when the height is 0, and reverse the velocity. Or am I on the wrong track?
DifferentialEquations.jl offers sophisticated callbacks and event handling. Since the DifferentialEquations.jl algorithms are about 10x faster while offering a higher order interpolation, these algorithms are clearly the better choose here anyways.
The first link is the documentation which shows how to do the event handling. The easy interface uses the macros. I start by defining the function.
f = #ode_def BallBounce begin
dy = v
dv = -g
end g=9.81
Here I am showing ParameterizedFunctions.jl to make the syntax nicer, but you can define the function directly as an in-place update f(t,u,du) (like Sundials.jl). Next you define the function which determines when an event takes place. It can be any function which is positive and hits zero at the event time. Here, we are checking for when the ball hits the ground, or for when y=0, so:
function event_f(t,u) # Event when event_f(t,u,k) == 0
u[1]
end
Next you say what to do when the event occurs. Here we want to reverse the sign of the velocity:
function apply_event!(u,cache)
u[2] = -u[2]
end
You put these functions together to build the callback using the macros:
callback = #ode_callback begin
#ode_event event_f apply_event!
end
Now you solve as usual. You define the ODEProblem using f and the initial condition, and you call solve on a timespan. The only thing extra is you pass the callback along with the solver:
u0 = [50.0,0.0]
prob = ODEProblem(f,u0)
tspan = [0;15]
sol = solve(prob,tspan,callback=callback)
Then we can use the plot recipe to automatically plot the solution:
plot(sol)
The result is this:
A few things to notice here:
DifferentialEquations.jl will automatically use an interpolation to more safely check for the event. For example, if the event happened within a timestep but not at the ends, DifferentialEquations.jl will still find it. More or less interpolations points can be included as options to the #ode_event macro.
DifferentialEquations.jl used a rootfinding method to hone in on the moment of the event. Even though the adaptive solver steps past the event, by using rootfinding on the interpolation it finds the exact time of the event, and thus gets the discontinuity right. You can see that in the graph since the ball never goes negative.
There is a whole lot more this can do. Check out the docs. You can do pretty much anything with this. For example, have your ODE changing size over the run to model a population of cells with birth and deaths. This is something other solver packages can't do.
Even with all of these features, speed is not compromised.
Let me know if you need any extra functionality added to the "ease of use" interface macros.
Somewhat hacky:
function bb(t, f)
(y, v) = f
dy_dt = v
dv_dt = -9.81*sign(y)
[dy_dt, dv_dt]
end
where you just follow a convention where y and -y refer to the same heights. You can then plot the trajectory of the bouncing ball by just plotting abs(y).
I'm porting a Matlab code into julia and so far i'm having amazing results:
A code that in Matlab runs in more than 5 hours, julia does it in a little more than 8 minutes! however i have a problem...
In matlab i have:
for xx=1:xlong
for yy = 1:ylong
U_alturas(xx,yy,:) = interp1(squeeze(NivelAltura_int(xx,yy,:)),squeeze(U(xx,yy,:)), interpolar_a);
V_alturas(xx,yy,:) = interp1(squeeze(NivelAltura_int(xx,yy,:)),squeeze(V(xx,yy,:)), interpolar_a);
end
end
that produces NaNs whenever a point in interpolar_a is outside the range in NivelAltura_int.
In Julia i'm trying to do the same with:
for xx in 1:xlong
for yy in 1:ylong
AltInterp = interpolate((Znw_r,),A_s_c_r,Gridded(Linear()));
NivelAltura_int[xx,yy,1:end] = AltInterp[Znu[1:end]]
Uinterp = interpolate((squeeze(NivelAltura_int[xx,yy,1:end],(1,2)),),squeeze(U[xx,yy,1:end],(1,2)),Gridded(Linear()));
Vinterp = interpolate((squeeze(NivelAltura_int[xx,yy,1:end],(1,2)),),squeeze(V[xx,yy,1:end],(1,2)),Gridded(Linear()));
U_alturas[xx,yy,1:end] = Uinterp[Alturas[1:end]];
V_alturas[xx,yy,1:end] = Vinterp[Alturas[1:end]];
end
end
using the package Interpolations.jl. Whenever the point is outside the domain, this package extrapolates, which is incorrect for my purposes.
I can add a few lines of code that check and substitutes the values outside the domain with NaNs, but i believe it would add some time to the computation and is not very elegant.
In the documentation of the package, it mentions a kind of object like this:
Uextrap = extrapolate(Uinterp,NaN)
To control the behavior outside the domain, but i haven't find how to use it, i've tried adding it under Uinterp, i've tried evaluating it but it, naturally, won't work that way.
Could you help me on this one?
Thanks!
It looks like you may be running into two issues here. First, there's been some recent work on gridded extrapolations (#101) that may not be in the tagged version yet. If you're willing to live on the edge, you can Pkg.checkout("Interpolations") to use the development version (Pkg.free("Interpolations") will put you back on the stable version again).
Secondly, it looks like there's a still a missing method for vector-valued gridded extrapolations (issue #24):
julia> using Interpolations
itp = interpolate((collect(1:10),), collect(.1:.1:1.), Gridded(Linear()))
etp = extrapolate(itp, NaN);
julia> etp[.5:1:10.5]
ERROR: BoundsError: # ...
in throw_boundserror at abstractarray.jl:156
in getindex at abstractarray.jl:488
As you can see, it's trying to use the generic definitions for all abstract arrays, which will of course throw bounds errors. Interpolations just needs to add its own definition.
In the mean time, you can use a comprehension with scalar indexing:
julia> [etp[x] for x=.5:1:10.5]
11-element Array{Any,1}:
NaN
0.15
0.25
0.35
0.45
0.55
0.65
0.75
0.85
0.95
NaN
The following sample (refer) shows how extrapolate works:
Preparation:
using Interpolations
f(x) = sin((x-3)*2pi/9 - 1)
xmax = 10
A = Float64[f(x) for x in 1:xmax] # domain .EQ. 1:10
itpg = interpolate(A, BSpline(Linear()), OnGrid())
The itpg object extrapolates outside points conforming its interpolation type:
itpg[2] # inside => -0.99190379965505
itpg[-2] # outside => 0.2628561875219271
Now we use extrapolat object to control extrapolation behavior:
etpg = extrapolate(itpg, NaN);
etpg[2]==itpg[2] # same result when point is inside => true
isnan(etpg[-2]) # NaN when the point is outside => true
So an extrapolate object does interpolation conforming its parent while extrapolates in a custom manner.