Skip to content

Support for Inputs #3823

@bradcarman

Description

@bradcarman

There should be official support for the concept of "inputs" to a model. I have hacked together the following code to provide a start for the following features/requirements:

  • provide a way to define model inputs so they are distinguishable from normal "unknowns". For example, inputs(sys::System) should list the inputs. Also an @inputs macro should be available to define model inputs. Input variables should be set directly and shouldn't change with time unless directed to, i.e. an input x has the equation D(x) ~ 0
  • provide an API so that inputs are separate from a problem, such that one can call solve(prob, inputs). This way, if inputs change, the ODEProblem can remain the same.
  • provide an API so that inputs can be set in an indeterminate form with set_input(integrator, var::Num, value::Number)

In the below example is a start to this feature request, the solve(prob, inputs) and set_input(integrator, var, value) are demonstrated. What's missing is the ability to influence initialization of the inputs from data. As can be seen, this provides the ability to run the same model in determinate and indeterminate forms and get the same result.

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq
using Plots
using DiffEqBase: reeval_internals_due_to_modification!, savevalues!
using Test

struct Input
    var::Num
    data::Vector{Float64}
    time::Vector{Float64}
end

function DiffEqBase.solve(prob::SciMLBase.AbstractDEProblem, inputs::Union{Input, Vector{Input}}, args...; kwargs...)

    tstops = Float64[]
    callbacks = DiscreteCallback[]
    if !isa(inputs, Vector)
        inputs = [inputs]
    end

    for input::Input in inputs
        tstops = union(tstops, input.time)

        condition(u,t,integrator) = any(t .== input.time)
        function affect!(integrator) 
            i = findfirst(integrator.t .== input.time)
            integrator[input.var] = input.data[i]
        end
        callback = DiscreteCallback(condition, affect!)

        push!(callbacks, callback)
    end


    return solve(prob, args...; tstops, callback=CallbackSet(callbacks...), kwargs...)
end

function set_input!(integrator, var, value)
    savevalues!(integrator)
    integrator[var] = value
    reeval_internals_due_to_modification!(integrator, false) 
    savevalues!(integrator, true)
end

# -----------------------------------------
# ----- example ---------------------------
# -----------------------------------------

vars = @variables begin
    # inputs
    x(t) = 1

    # states
    y(t) = 0
end

eqs = [
    # inputs    
    D(x) ~ 0
   
    # equations
    D(y) ~ x
]

@mtkcompile sys = System(eqs, t, vars, [])
prob = ODEProblem(sys, [], (0, 4))
input = Input(sys.x, [2,3,4], [1,2,3])

sol = solve(prob, input)


plot(sol; idxs=[sys.x, sys.y])


integrator = init(prob)

for i=1:4
    # set inputs
    set_input!(integrator, sys.x, i)

    # step
    step!(integrator, 1.0, true)

    # outputs
    integrator[sys.y] # |> f
end



p1= plot(integrator.sol; idxs=sys.x, label="indeterminate (x)")
plot!(sol; idxs=sys.x, label="determinate (x)")

p2 = plot(integrator.sol; idxs=sys.y, label="indeterminate (y)")
plot!(sol; idxs=sys.y, label="determinate (y)")

plot(p1, p2; layout=(2,1))
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions