Skip to content

Commit

Permalink
add additional tests, use argumenterror where appropriate
Browse files Browse the repository at this point in the history
  • Loading branch information
TorkelE committed Jul 8, 2024
1 parent 5b6d6ec commit e231734
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/spatial_reaction_systems/lattice_jump_systems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function JumpProcesses.JumpProblem(lrs::LatticeReactionSystem, dprob, aggregator
combinatoric_ratelaws = get_combinatoric_ratelaws(reactionsystem(lrs)), kwargs...)
# Error checks.
if !isnothing(dprob.f.sys)
error("Unexpected `DiscreteProblem` passed into `JumpProblem`. Was a `LatticeReactionSystem` used as input to the initial `DiscreteProblem`?")
throw(ArgumentError("Unexpected `DiscreteProblem` passed into `JumpProblem`. Was a `LatticeReactionSystem` used as input to the initial `DiscreteProblem`?"))
end

# Computes hopping constants and mass action jumps (requires some internal juggling).
Expand Down
18 changes: 10 additions & 8 deletions src/spatial_reaction_systems/lattice_reaction_systems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,22 @@ struct LatticeReactionSystem{Q,R,S,T} <: MT.AbstractTimeDependentSystem
num_verts::Int64, num_edges::Int64, edge_iterator::T) where {Q,R,S,T}
# Error checks.
if !(R <: AbstractSpatialReaction)
error("The second argument must be a vector of AbstractSpatialReaction subtypes.")
throw(ArgumentError("The second argument must be a vector of AbstractSpatialReaction subtypes."))
end
if !iscomplete(rs)
throw(ArgumentError("A non-complete `ReactionSystem` was used as input, this is not permitted."))
end
if !isempty(MT.get_systems(rs))
error("A non-flattened (hierarchical) `ReactionSystem` was used as input. `LatticeReactionSystem`s can only be based on non-hierarchical `ReactionSystem`s.")
throw(ArgumentError("A non-flattened (hierarchical) `ReactionSystem` was used as input. `LatticeReactionSystem`s can only be based on non-hierarchical `ReactionSystem`s."))
end
if length(species(rs)) != length(unknowns(rs))
error("The `ReactionSystem` used as input contain variable unknowns (in addition to species unknowns). This is not permitted (the input `ReactionSystem` must contain species unknowns only).")
throw(ArgumentError("The `ReactionSystem` used as input contain variable unknowns (in addition to species unknowns). This is not permitted (the input `ReactionSystem` must contain species unknowns only)."))
end
if length(reactions(rs)) != length(equations(rs))
error("The `ReactionSystem` used as input contain equations (in addition to reactions). This is not permitted.")
throw(ArgumentError("The `ReactionSystem` used as input contain equations (in addition to reactions). This is not permitted."))
end
if !isempty(MT.continuous_events(rs)) || !isempty(MT.discrete_events(rs))
@warn "The `ReactionSystem` used as input to `LatticeReactionSystem contain events. These will be ignored in any simulations based on the created `LatticeReactionSystem`."
throw(ArgumentError("The `ReactionSystem` used as input to `LatticeReactionSystem contain events. These will be ignored in any simulations based on the created `LatticeReactionSystem`."))
end
if !isempty(observed(rs))
@warn "The `ReactionSystem` used as input to `LatticeReactionSystem contain observables. It will not be possible to access these from the created `LatticeReactionSystem`."
Expand Down Expand Up @@ -295,7 +298,7 @@ E.g. for a lattice `CartesianGrid(4,6)`, `(4,6)` is returned.
grid_size(lrs::LatticeReactionSystem) = grid_size(lattice(lrs))
grid_size(lattice::CartesianGridRej{N,T}) where {N,T} = lattice.dims
grid_size(lattice::Array{Bool, N}) where {N} = size(lattice)
grid_size(lattice::Graph) = error("Grid size is only defined for LatticeReactionSystems with grid-based lattices (not graph-based).")
grid_size(lattice::Graphs.AbstractGraph) = throw(ArgumentError("Grid size is only defined for LatticeReactionSystems with grid-based lattices (not graph-based)."))

"""
grid_dims(lrs::LatticeReactionSystem)
Expand All @@ -305,7 +308,7 @@ The output is either `1`, `2`, or `3`.
"""
grid_dims(lrs::LatticeReactionSystem) = grid_dims(lattice(lrs))
grid_dims(lattice::GridLattice{N,T}) where {N,T} = return N
grid_dims(lattice::Graph) = error("Grid dimensions is only defined for LatticeReactionSystems with grid-based lattices (not graph-based).")
grid_dims(lattice::Graphs.AbstractGraph) = throw(ArgumentError("Grid dimensions is only defined for LatticeReactionSystems with grid-based lattices (not graph-based)."))

"""
get_lattice_graph(lrs::LatticeReactionSystem)
Expand Down Expand Up @@ -333,7 +336,6 @@ reactions(lrs::LatticeReactionSystem) = reactions(reactionsystem(lrs))
MT.nameof(lrs::LatticeReactionSystem) = MT.nameof(reactionsystem(lrs))
MT.get_iv(lrs::LatticeReactionSystem) = MT.get_iv(reactionsystem(lrs))
MT.equations(lrs::LatticeReactionSystem) = MT.equations(reactionsystem(lrs))
MT.equations(lrs::LatticeReactionSystem) = MT.equations(reactionsystem(lrs))
MT.unknowns(lrs::LatticeReactionSystem) = MT.unknowns(reactionsystem(lrs))
MT.get_metadata(lrs::LatticeReactionSystem) = MT.get_metadata(reactionsystem(lrs))

Expand Down
2 changes: 1 addition & 1 deletion src/spatial_reaction_systems/spatial_ODE_systems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ function build_odefunction(lrs::LatticeReactionSystem, vert_ps::Vector{Pair{R,Ve
remove_conserved, checks) where {R,S,T}
# Error check.
if remove_conserved
error("Removal of conserved quantities is currently not supported for `LatticeReactionSystem`s")
throw(ArgumentError("Removal of conserved quantities is currently not supported for `LatticeReactionSystem`s"))
end

# Prepares the inputs to the `LatticeTransportODEFunction` functor.
Expand Down
2 changes: 1 addition & 1 deletion src/spatial_reaction_systems/spatial_reactions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,5 @@ function find_parameters_in_rate!(parameters, rateex::ExprValues)
find_parameters_in_rate!(parameters, rateex.args[i])
end
end
nothing
return nothing
end
24 changes: 12 additions & 12 deletions src/spatial_reaction_systems/utility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ end
function lattice_process_input(input::Dict{<:Any, T}, syms::Vector) where {T}
# Error checks
if !isempty(setdiff(keys(input), syms))
error("You have provided values for the following unrecognised parameters/initial conditions: $(setdiff(keys(input), syms)).")
throw(ArgumentError("You have provided values for the following unrecognised parameters/initial conditions: $(setdiff(keys(input), syms))."))
end
if !isempty(setdiff(syms, keys(input)))
error("You have not provided values for the following parameters/initial conditions: $(setdiff(syms, keys(input))).")
throw(ArgumentError("You have not provided values for the following parameters/initial conditions: $(setdiff(syms, keys(input)))."))
end

return [sym => input[sym] for sym in syms]
Expand All @@ -67,7 +67,7 @@ function lattice_process_input(input, syms::Vector)
if ((input isa Vector) || (input isa Tuple)) && all(entry isa Pair for entry in input)
return lattice_process_input(Dict(input), syms)
end
error("Input parameters/initial conditions have the wrong format ($(typeof(input))). These should either be a Dictionary, or a Tuple or a Vector (where each entry is a Pair taking a parameter/species to its value).")
throw(ArgumentError("Input parameters/initial conditions have the wrong format ($(typeof(input))). These should either be a Dictionary, or a Tuple or a Vector (where each entry is a Pair taking a parameter/species to its value)."))
end

# Splits parameters into vertex and edge parameters.
Expand Down Expand Up @@ -100,7 +100,7 @@ function vertex_value_form(values, lrs::LatticeReactionSystem, sym::BasicSymboli
# For the case where the i'th value of the vector corresponds to the value in the i'th vertex.
# This is the only (non-uniform) case possible for graph grids.
if (length(values) != num_verts(lrs))
error("You have provided ($(length(values))) values for $sym. This is not equal to the number of vertices ($(num_verts(lrs))).")
throw(ArgumentError("You have provided ($(length(values))) values for $sym. This is not equal to the number of vertices ($(num_verts(lrs)))."))
end
return values
end
Expand All @@ -113,10 +113,10 @@ end
function vertex_value_form(values::AbstractArray, num_verts::Int64, lattice::CartesianGridRej{N,T},
sym::BasicSymbolic) where {N,T}
if size(values) != lattice.dims
error("The values for $sym did not have the same format as the lattice. Expected a $(lattice.dims) array, got one of size $(size(values))")
throw(ArgumentError("The values for $sym did not have the same format as the lattice. Expected a $(lattice.dims) array, got one of size $(size(values))"))
end
if (length(values) != num_verts)
error("You have provided ($(length(values))) values for $sym. This is not equal to the number of vertices ($(num_verts)).")
throw(ArgumentError("You have provided ($(length(values))) values for $sym. This is not equal to the number of vertices ($(num_verts))."))
end
return [values[flat_idx] for flat_idx in 1:num_verts]
end
Expand All @@ -125,7 +125,7 @@ end
function vertex_value_form(values::AbstractArray, num_verts::Int64, lattice::Array{Bool,T},
sym::BasicSymbolic) where {T}
if size(values) != size(lattice)
error("The values for $sym did not have the same format as the lattice. Expected a $(size(lattice)) array, got one of size $(size(values))")
throw(ArgumentError("The values for $sym did not have the same format as the lattice. Expected a $(size(lattice)) array, got one of size $(size(values))"))
end

# Pre-declares a vector with the values in each vertex (return_values).
Expand All @@ -139,7 +139,7 @@ function vertex_value_form(values::AbstractArray, num_verts::Int64, lattice::Arr

# Checks that the correct number of values was provided, and returns the values.
if (length(return_values) != num_verts)
error("You have provided ($(length(return_values))) values for $sym. This is not equal to the number of vertices ($(num_verts)).")
throw(ArgumentError("You have provided ($(length(return_values))) values for $sym. This is not equal to the number of vertices ($(num_verts))."))
end
return return_values
end
Expand All @@ -158,10 +158,10 @@ function edge_value_form(values, lrs::LatticeReactionSystem, sym)

# Error checks.
if nnz(values) != num_edges(lrs)
error("You have provided ($(nnz(values))) values for $sym. This is not equal to the number of edges ($(num_edges(lrs))).")
throw(ArgumentError("You have provided ($(nnz(values))) values for $sym. This is not equal to the number of edges ($(num_edges(lrs)))."))
end
if !all(Base.isstored(values, e[1], e[2]) for e in edge_iterator(lrs))
error("Values was not provided for some edges for edge parameter $sym.")
throw(ArgumentError("Values was not provided for some edges for edge parameter $sym."))
end

# Unlike initial conditions/vertex parameters, (unless uniform) edge parameters' values are
Expand Down Expand Up @@ -296,7 +296,7 @@ function compute_edge_value(exp, lrs::LatticeReactionSystem, edge_ps)
# Finds the symbols in the expression. Checks that all correspond to edge parameters.
relevant_syms = Symbolics.get_variables(exp)
if !all(any(isequal(sym, p) for p in edge_parameters(lrs)) for sym in relevant_syms)
error("An non-edge parameter was encountered in expressions: $exp. Here, only edge parameters are expected.")
throw(ArgumentError("An non-edge parameter was encountered in expressions: $exp. Here, only edge parameters are expected."))
end

# Creates a Function tha computes the expressions value for a parameter set.
Expand All @@ -320,7 +320,7 @@ function compute_vertex_value(exp, lrs::LatticeReactionSystem; u = [], ps = [])
# Finds the symbols in the expression. Checks that all correspond to unknowns or vertex parameters.
relevant_syms = Symbolics.get_variables(exp)
if any(any(isequal(sym) in edge_parameters(lrs)) for sym in relevant_syms)
error("An edge parameter was encountered in expressions: $exp. Here, only vertex-based components are expected.")
throw(ArgumentError("An edge parameter was encountered in expressions: $exp. Here, only vertex-based components are expected."))
end

# Creates a Function that computes the expressions value for a parameter set.
Expand Down
115 changes: 105 additions & 10 deletions test/spatial_modelling/lattice_reaction_systems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ include("../spatial_test_networks.jl")
# Pre declares a grid.
grids = [very_small_2d_cartesian_grid, very_small_2d_masked_grid, very_small_2d_graph_grid]

### Test Spatial Reactions ###

# Test creation of TransportReaction with non-parameters in rate.
# Tests that it works even when rate is highly nested.
let
@variables t
@species X(t) Y(t)
@parameters D1 D2 D3
@test_throws ErrorException TransportReaction(D1 + D2*(D3 + Y), X)
@test_throws ErrorException TransportReaction(Y, X)
end

# Checks that the `hash` functions works for `TransportReaction`s.
let
tr1 = @transport_reaction D1 X
tr2 = @transport_reaction D1 X
tr3 = @transport_reaction D2 X
hash(tr1, 0x0000000000000001) == hash(tr2, 0x0000000000000001)
hash(tr2, 0x0000000000000001) != hash(tr3, 0x0000000000000001)
end

### Tests LatticeReactionSystem Getters Correctness ###

# Test case 1.
Expand Down Expand Up @@ -127,6 +148,37 @@ let
end
end

# Tests using various more obscure types of getters.
let
# Create LatticeReactionsSystems.
t = default_t()
@parameters p d kB kD
@species X(t) X2(t)
rxs = [
Reaction(p, [], [X])
Reaction(d, [X], [])
Reaction(kB, [X], [X2], [2], [1])
Reaction(kD, [X2], [X], [1], [2])
]
@named rs = ReactionSystem(rxs, t; metadata = "Metadata string")
rs = complete(rs)
tr = @transport_reaction D X2
lrs = LatticeReactionSystem(rs, [tr], small_2d_cartesian_grid)

# Generic ones (simply forwards call to the non-spatial system).
@test isequal(reactions(lrs), rxs)
@test isequal(nameof(lrs), :rs)
@test isequal(ModelingToolkit.get_iv(lrs), t)
@test isequal(equations(lrs), rxs)
@test isequal(unknowns(lrs), [X, X2])
@test isequal(ModelingToolkit.get_metadata(lrs), "Metadata string")
@test isequal(ModelingToolkit.get_eqs(lrs), rxs)
@test isequal(ModelingToolkit.get_unknowns(lrs), [X, X2])
@test isequal(ModelingToolkit.get_ps(lrs), [p, d, kB, kD])
@test isequal(ModelingToolkit.get_systems(lrs), [])
@test isequal(independent_variables(lrs), [t])
end

### Tests Spatial Reactions Getters Correctness ###

# Test case 1.
Expand Down Expand Up @@ -233,16 +285,6 @@ end

### Tests Error generation ###

# Test creation of TransportReaction with non-parameters in rate.
# Tests that it works even when rate is highly nested.
let
@variables t
@species X(t) Y(t)
@parameters D1 D2 D3
@test_throws ErrorException TransportReaction(D1 + D2*(D3 + Y), X)
@test_throws ErrorException TransportReaction(Y, X)
end

# Network where diffusion species is not declared in non-spatial network.
let
rs = @reaction_network begin
Expand Down Expand Up @@ -297,6 +339,59 @@ let
end
end

# Tests various networks with non-permitted content.
let
tr = @transport_reaction D X

# Variable unknowns.
rs1 = @reaction_network begin
@variables V(t)
(p,d), 0 <--> X
end
@test_throws ArgumentError LatticeReactionSystem(rs1, [tr], short_path)

# Non-reaction equations.
rs2 = @reaction_network begin
@equations D(V) ~ X - V
(p,d), 0 <--> X
end
@test_throws ArgumentError LatticeReactionSystem(rs2, [tr], short_path)

# Events.
rs3 = @reaction_network begin
@discrete_events [1.0] => [p ~ p + 1]
(p,d), 0 <--> X
end
@test_throws ArgumentError LatticeReactionSystem(rs3, [tr], short_path)

# Observables (only generates a warning).
rs4 = @reaction_network begin
@observables X2 ~ 2X
(p,d), 0 <--> X
end
@test_logs (:warn, r"The `ReactionSystem` used as input to `LatticeReactionSystem contain observables. It *") match_mode=:any LatticeReactionSystem(rs4, [tr], short_path)
end

# Tests for hierarchical input system.
let
t = default_t()
@parameters d
@species X(t)
rxs = [Reaction(d, [X], [])]
@named rs1 = ReactionSystem(rxs, t)
@named rs2 = ReactionSystem(rxs, t; systems = [rs1])
rs2 = complete(rs2)
@test_throws ArgumentError LatticeReactionSystem(rs2, [tr], short_path)
end

# Tests for non-complete input `ReactionSystem`.
let
tr = @transport_reaction D X
rs = @network_component begin
(p,d), 0 <--> X
end
@test_throws ArgumentError LatticeReactionSystem(rs1, [tr], short_path)
end

### Tests Grid Vertex and Edge Number Computation ###

Expand Down
39 changes: 38 additions & 1 deletion test/spatial_modelling/lattice_reaction_systems_ODEs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ let
@test all(isequal.(ss_1, ss_2))
end

### ODEProblem & Integrator Interfacing ###
### ODEProblem & Integrator Interfacing ###

# Checks that basic interfacing with ODEProblem parameters (getting and setting) works.
let
Expand Down Expand Up @@ -788,4 +788,41 @@ let
end
end
end
end


### Error Tests ###

# Checks that attempting to remove conserved quantities yields an error.
let
lrs = LatticeReactionSystem(binding_system, binding_srs, very_small_2d_masked_grid)
@test_throws ArgumentError ODEProblem(lrs, binding_u0, (0.0, 10.0), binding_p; remove_conserved = true)
end

# Checks that various erroneous inputs to `ODEProblem` yields errors.
let
# Create `LatticeReactionSystem`.
@parameters d1 d2 D [edgeparameter=true]
@species X1(t) X2(t)
rxs = [Reaction(d1, [X1], [])]
@named rs = ReactionSystem(rxs, t)
rs = complete(rs)
lrs = LatticeReactionSystem(rs, [TransportReaction(D, X1)], CartesianGrid((4,)))

# Attempts to create `ODEProblem` using various faulty inputs.
u0 = [X1 => 1.0]
tspan = (0.0, 1.0)
ps = [d1 => 1.0, D => 0.1]
@test_throws ArgumentError ODEProblem(lrs, [1.0], tspan, ps)
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [1.0, 0.1])
@test_throws ArgumentError ODEProblem(lrs, [X1 => 1.0, X2 => 2.0], tspan, ps)
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [d1 => 1.0, d2 => 0.2, D => 0.1])
@test_throws ArgumentError ODEProblem(lrs, [X1 => [1.0, 2.0, 3.0]], tspan, ps)
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [d1 => [1.0, 2.0, 3.0], D => 0.1])
@test_throws ArgumentError ODEProblem(lrs, [X1 => [1.0 2.0; 3.0 4.0]], tspan, ps)
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [d1 => [1.0 2.0; 3.0 4.0], D => 0.1])
bad_D_vals_1 = sparse([0.0 1.0 0.0 1.0; 1.0 0.0 1.0 0.0; 0.0 1.0 0.0 1.0; 1.0 0.0 1.0 0.0])
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [d1 => 1.0, D => bad_D_vals_1])
bad_D_vals_2 = sparse([0.0 0.0 0.0 1.0; 1.0 0.0 1.0 0.0; 0.0 1.0 0.0 1.0; 1.0 0.0 0.0 0.0])
@test_throws ArgumentError ODEProblem(lrs, u0, tspan, [d1 => 1.0, D => bad_D_vals_2])
end
12 changes: 11 additions & 1 deletion test/spatial_modelling/lattice_reaction_systems_jumps.jl
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,14 @@ let
end


### JumpProblem & Integrator Interfacing ###
### JumpProblem & Integrator Interfacing ###


### Other Tests ###

# Checks that providing a non-spatial `DiscreteProblem` to a `JumpProblem` gives an error.
let
lrs = LatticeReactionSystem(binding_system, binding_srs, very_small_2d_masked_grid)
dprob = DiscreteProblem(binding_system, binding_u0, (0.0, 10.0), binding_p[1:2])
@test_throws ArgumentError JumpProblem(lrs, dprob, NSM())
end
Loading

0 comments on commit e231734

Please sign in to comment.