Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SavingCallback for DAEs: Not clear how to get du #547

Open
MartinOtter opened this issue Jan 14, 2020 · 7 comments
Open

SavingCallback for DAEs: Not clear how to get du #547

MartinOtter opened this issue Jan 14, 2020 · 7 comments

Comments

@MartinOtter
Copy link

MartinOtter commented Jan 14, 2020

The callback save_func(u, t, integrator) can be provided to compute additional outputs y based on the current time t and the current state u (and the model parameters integrator.p): y=f(u,t,p).

To compute additional outputs y for DAEs, also the derivatives of the states du are in general needed (y = f(du, u, t, p)), but du is not in the interface of save_func. Is it possible to get du somehow from the integrator (e.g. integrator.du), or is a function call needed?

@ChrisRackauckas
Copy link
Member

integrator(t,Val{1}) probably does it (Val{1} means first derivative from the interpolant).

@MartinOtter
Copy link
Author

MartinOtter commented Jan 15, 2020

Thanks. In principal this works. However, there are the following issues:

Fails for first time point

At the first time point

du = integrator(t,Val{1})

triggers the following Sundials error:

[IDAS ERROR]  IDAGetDky
  Illegal value for k.

┌ Warning: IDAGetDky failed with error code =
│   flag = -25
└ @ Sundials D:\otter\home\.julia\dev\Sundials\src\simple.jl:20

I guess, this can be seen as a bug in IDA, because IDA has du at the initial point internally stored and could just return it.

Temporarily, I use the code:

function save_func(u, t, integrator)
    p = integrator.p
    if t==0
        du = integrator.du
    else
        du = integrator(t, Val{1})   # allocates memory for der_x
    end
    ...
end

Would be better if this bug-fix would be included in function integrator(...).

I tried whether this problem is present with DASKR, but unfortunately DASKR does not support callbacks.

Allocates memory at every call

du = integrator(t,Val{1})

always allocates (unnecessary) memory for vector du. It would be better to use the call:

integrator(du, t, Val{1})

However, then "somehow" a cache-vector du needs to be provided that is specific for the used integrator (e.g. IDA needs an N_Vector array). Temporarily, I use the code:

function save_func(u, t, integrator)
    p = integrator.p
    if t==0
        p.du = copy(integrator.du)
    else
        integrator(p.du, t, Val{1})
    end
    du = p.du
    ...
end

This is also not nice (e.g. requires to define an Any vector du in p)

It would be better, if a second tmp vector of length(u) could be stored in integrator and this tmp vector could be utilized at this place (in a similar way as for u)

@ChrisRackauckas
Copy link
Member

I guess, this can be seen as a bug in IDA, because IDA has du at the initial point internally stored and could just return it.

Yeah 🤷‍♂

However, then "somehow" a cache-vector du needs to be provided that is specific for the used integrator (e.g. IDA needs an N_Vector array). Temporarily, I use the code:

https://docs.juliadiffeq.org/latest/basics/integrator/#Caches-1

first(get_tmp_cache(integrator)) is likely what you want.

@MartinOtter
Copy link
Author

Unfortunately, this does not work for IDA:

When printing the size of the cache vector:

function save_func(u, t, integrator)
    tmps = get_tmp_cache(integrator)
    println("typeof(tmps) = ", typeof(tmps), ", length(tmps) = ", length(tmps))
    ...
end
...
typeof(tmps) = Tuple{Array{Float64,1}}, length(tmps) = 1

So, there is only one cache vector and this cache vector is already used in function (affect!::SavingAffect) in file DiffEqCallbacks\src\saving.jl:

            # it should really be a mutability trait
            if typeof(integrator.u) <: Union{Number,SArray, ArrayPartition{T, Tuple{SArray{R, T, N, M}, SArray{R, T, N, M}}} where {T, R, N, M}}
                curu = integrator(curt)
            else
                curu = first(get_tmp_cache(integrator))
                integrator(curu,curt) # inplace since save_func allocates
            end
            copyat_or_push!(affect!.saved_values.t, affect!.saveiter, curt)
            copyat_or_push!(affect!.saved_values.saveval, affect!.saveiter,
                            affect!.save_func(curu, curt, integrator),Val{false})

curu = first(get_tmp_cache(integrator)) gets the cache vector and uses it in affect!.safe_func(curu, ...).

Could you add more cache vectors to IDA.

@ChrisRackauckas
Copy link
Member

I think we need to dig into Sundials and see if we can grab more cache vectors that it's already made. That's probably better than adding another register, since there's gotta be one in there.

@MartinOtter
Copy link
Author

function save_func(u, t, integrator)
   p = integrator.p
   if t==0
       p.du = copy(integrator.du)
   else
       integrator(p.du, t, Val{1})
   end
   du = p.du
   ...
end

This is also not nice (e.g. requires to define an Any vector du in p)

I tried to use a Float64 vector instead of an Any vector:

mutable struct Model
   ...
   du::Vector{Float64}
end
p = Model(...)

Surprisingly this worked. Since IDA is using an N_Vector array, this could mean that at every model evaluation there is some overhead associated to copy the N_Vector to a Julia vector.

@ChrisRackauckas
Copy link
Member

It should just be doing an unsafe_wrap, but FFI is not my specialty so it would be good if someone double check this:

https://github.com/JuliaDiffEq/Sundials.jl/blob/master/src/common_interface/function_types.jl#L22-L23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants