Testing for correct handling of (missing) globals

When writing a script to e.g. solve an ODE, I often find myself using global (const) variables and using them in my right-hand-side function. This all works well, until I want to solve this ODE on another worker. This worker does not know the global variables referenced in the RHS function, since I forgot to declare them @everywhere! The corresponding code looks something like this:

const parameter = -1.0
function deriv!(du, u, p, t)
    du[:] = parameter .* u
    return nothing
end
prob = ODEProblem(deriv!, [1.0], (0.0,  1.0))

my_function(prob) # calls solve(prob) on another worker

As this happened to me far too often already, I want to make sure that my_function checks for such errors and throws an exception when this happens.
While writing a test, I found some surprising behavior. While the above code throws an exception (as expected) when running in a script or directly in the REPL, wrapping the same code in a @testset does not throw an Exception. I assume that whatever I define in a @testset is not in the global scope, but even if I declare the parameter outside of the testset, like:

const parameter = -1.0

@testset "test" begin
function deriv!(du, u, p, t)
    du[:] = parameter .* u
    return nothing
end
prob = ODEProblem(deriv!, [1.0], (0.0,  1.0))

@test_throws Exception my_function(prob) # calls solve(prob) on another worker
end

my function does not throw an exception. Why is that? And how could I test whether my function responds as I expect to globals referenced but not defined on another worker?

This is exactly what p (the parameters) of the ODE is meant for. This example can be written as

parameter = -1.0
function deriv!(du, u, parameter, t)
    du[:] = parameter .* u
    return nothing
end
prob = ODEProblem(deriv!, [1.0], (0.0,  1.0), parameter)

and by doing so you make it so the external input you need is passed in explicitly.

1 Like