Skip to content

Commit

Permalink
Change names in trophic structure() output + neater warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
alaindanet authored and iago-lito committed Feb 21, 2023
1 parent cd1ef5c commit 157938b
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 114 deletions.
35 changes: 13 additions & 22 deletions src/measures/functioning.jl
Original file line number Diff line number Diff line change
Expand Up @@ -356,20 +356,20 @@ julia> foodweb = FoodWeb([0 1 1; 0 0 0; 0 0 0]);
B0 = [0.5, 0.5, 0.5];
sol = simulate(params, B0; verbose = true);
three_sp = trophic_structure(sol; last = 10);
three_sp[(:max, :avg)]
(max = 2.0, avg = 1.3333333333333333)
three_sp[(:max, :average)]
(max = 2.0, average = 1.3333333333333333)
julia> B0 = [0, 0.5, 0.5];
sol = simulate(params, B0; verbose = true);
no_consumer = trophic_structure(sol; last = 10);
no_consumer[(:max, :avg)]
(max = 1.0, avg = 1.0)
no_consumer[(:max, :average)]
(max = 1.0, average = 1.0)
julia> foodweb = FoodWeb([0 0 0; 0 1 0; 1 1 0]; quiet = true);
params = ModelParameters(foodweb);
B0 = [0.5, 0.5, 0.5];
sol = simulate(params, B0; verbose = false);
sum(trophic_structure(sol; last = 1).A) - sum(foodweb.A)
sum(trophic_structure(sol; last = 1).alive_A) - sum(foodweb.A)
-2
```
"""
Expand All @@ -386,26 +386,17 @@ function trophic_structure(solution; threshold = eps(), idxs = nothing, kwargs..
net = get_parameters(solution).network.A[living_sp.idxs, living_sp.idxs]
tlvl = trophic_levels(net)

out_names =
[:max, :average, :weighted_average, :alive_species, :alive_trophic_level, :alive_A]

if isempty(tlvl)
troph = (
max = NaN,
avg = NaN,
w_avg = NaN,
species = living_sp.species,
tlvl = tlvl,
A = net,
)
variable_values = repeat([NaN], 3)
else
troph = (
max = maximum(tlvl),
avg = mean(tlvl),
w_avg = sum(tlvl .* (bm_sp ./ sum(bm_sp))),
species = living_sp.species,
tlvl = tlvl,
A = net,
)
variable_values = [maximum(tlvl), mean(tlvl), sum(tlvl .* (bm_sp ./ sum(bm_sp)))]
end
troph

out_values = vcat(variable_values, [living_sp.species, tlvl, net])
(; zip(out_names, out_values)...)
end

"""
Expand Down
12 changes: 6 additions & 6 deletions src/measures/stability.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,15 @@ julia> foodweb = FoodWeb([0 1 1; 0 0 0; 0 0 0]); # Two producers and one consume
params = ModelParameters(foodweb);
B0 = [0.5, 0.5, 0.5];
sol = simulate(params, B0; verbose = true);
s = coefficient_of_variation(sol; last = 10);
round.(values(s[(:cv_com, :avg_cv_sp, :synchrony)])); digits = 2)
(cv_com = 0.02, avg_cv_sp = 0.06, synchrony = 0.1)
s = coefficient_of_variation(sol; last = 10)[(:cv_com, :avg_cv_sp, :synchrony)];
keys(s), round.(values(s); digits = 2)
((:cv_com, :avg_cv_sp, :synchrony), (0.02, 0.06, 0.1))
julia> B0 = [0, 0.5, 0.5]; # Two producers
sol = simulate(params, B0; verbose = true);
s = coefficient_of_variation(sol; last = 10);
round.(values(s[(:cv_com, :avg_cv_sp, :synchrony)])); digits = 2)
(cv_com = 0.14, avg_cv_sp = 0.14, synchrony = 1.0)
s = coefficient_of_variation(sol; last = 10)[(:cv_com, :avg_cv_sp, :synchrony)];
keys(s), round.(values(s); digits = 2)
((:cv_com, :avg_cv_sp, :synchrony), (0.14, 0.14, 1.0))
```
"""
function coefficient_of_variation(solution; threshold = eps(), kwargs...)
Expand Down
62 changes: 17 additions & 45 deletions src/measures/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ true
julia> sim = extract_last_timesteps(m; last = 1, idxs = [2]);
sim == extract_last_timesteps(m; last = 1, idxs = "s2")
true
julia> extract_last_timesteps(m; last = 1000, autofix_last = true, quiet = true) ==
extract_last_timesteps(m; last = "10%")
true
```
"""
function extract_last_timesteps(solution; idxs = nothing, kwargs...)
Expand Down Expand Up @@ -88,7 +84,7 @@ function process_idxs(solution, idxs;)
idxs = something.(idxs)

elseif eltype(idxs) <: Integer
check_bound_idxs = map(x -> x >= 1 && x <= length(sp), idxs)
check_bound_idxs = 1 .<= idxs .<= length(sp)
absent_sp = idxs[findall(.!check_bound_idxs)]
all(check_bound_idxs) || throw(ArgumentError("Cannot extract idxs \
$(absent_sp). Each species index \
Expand All @@ -101,66 +97,42 @@ function process_idxs(solution, idxs;)
throw(ArgumentError("`idxs` should be a vector of Integer (species indices) or \
String (species names)"))
end

idxs
end

function process_last_timesteps(solution; last = "10%", autofix_last = false, quiet = false)
function process_last_timesteps(solution; last = "10%", quiet = false)

n_timesteps = length(solution.t)

if last isa AbstractString
endswith(last, "%") || throw(ArgumentError("The `last` argument, when given as a \
string, should end with character '%'"))
string, should end with character '%'"))

perc = parse(Float64, last[1:(end-1)])
is_valid_perc = 0.0 < perc <= 100.0
is_valid_perc || throw(ArgumentError("Cannot extract $(perc)% of the solution's \
timesteps: 0% < `last` <= 100% must hold."))
last = round(Int, n_timesteps * perc / 100)
last > 0 || (autofix_last && quiet) || @warn "$perc% of $n_timesteps \
timesteps correspond to $last output lines: an empty table has been extracted."
last > 0 || quiet || @warn "$perc% of $n_timesteps \
timesteps correspond to $last output lines: an empty table has been extracted."
elseif last isa Integer
last > 0 || throw(ArgumentError("Cannot extract $last timesteps. `last` should be \
a positive integer."))
elseif last isa Float64
throw(ArgumentError("Cannot extract `last` from a decimal number. \
throw(ArgumentError("Cannot extract `last` from a floating point number. \
Did you mean \"$last%\"?"))
else
throw(ArgumentError("Cannot extract timesteps with $last. \
`last` should be a positive integer or a string \
representing a percentage."))
throw(ArgumentError("Cannot extract timesteps with `last=$last` \
of type $(typeof(last)). \
`last` should be a positive integer \
or a string representing a percentage."))
end

if last > n_timesteps
autofix_last || throw(ArgumentError("Cannot extract $last timesteps from a \
trajectory solution with only \
$(n_timesteps) timesteps. \
Consider decreasing the `last` argument value \
and/or specifying it as a percentage instead \
(e.g. `\"10%\"`) or setting \
`autofix_last = true`."))

ten_perc = round(Int, n_timesteps * 10 / 100)
if ten_perc == 0
msg = "1, as 10% of timesteps < 1."
ten_perc = 1
else
msg = "$(ten_perc), i.e. the last 10% of timesteps."
end

quiet || @warn "Cannot extract $last timesteps from a trajectory solution \
with only $(n_timesteps) timesteps. \
We set 'last' to $msg"
last = ten_perc
elseif last == 0 && autofix_last
if autofix_last
quiet || @warn "Cannot extract $last timesteps from a trajectory solution \
with only $(n_timesteps) timesteps. \
We set 'last' to 1."
last = 1
else
last
end
end
last > n_timesteps && throw(ArgumentError("Cannot extract $last timesteps from a \
trajectory solution with only \
$(n_timesteps) timesteps. \
Consider decreasing the `last` argument value \
and/or specifying it as a percentage instead \
(e.g. `\"10%\"`)."))
last
end
23 changes: 13 additions & 10 deletions test/measures/test-functioning.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@

sol0 = simulates(params, [0, 0])

@test living_species(sol0, autofix_last = true, quiet = true).idxs ==
living_species([0 0; 0 0]) == Int64[]
@test living_species(sol0, autofix_last = true, quiet = true).species == String[]
@test living_species(sol0; quiet = true).idxs == living_species([0 0; 0 0]) == Int64[]
@test living_species(sol0; quiet = true).species == String[]

end

Expand All @@ -40,17 +39,21 @@ end
idxs = 2,
)

troph_zero = trophic_structure(sim_zero, autofix_last = true, quiet = true)
troph_zero = trophic_structure(sim_zero; quiet = true)
troph_three = trophic_structure(sim_three)
troph_three_nan = trophic_structure(sim_three; threshold = 1000)

@test all(isnan.(values(troph_zero[(:max, :avg, :w_avg)])))
@test all(isempty.(values(troph_zero[(:species, :A, :tlvl)])))
@test all(isnan.(values(troph_three_nan[(:max, :avg, :w_avg)])))
@test all(isempty.(values(troph_three_nan[(:species, :A, :tlvl)])))
alive_keys = (:alive_species, :alive_A, :alive_trophic_level)
@test all(isnan.(values(troph_zero[(:max, :average, :weighted_average)])))
@test all(isempty.(values(troph_zero[alive_keys])))
@test all(isnan.(values(troph_three_nan[(:max, :average, :weighted_average)])))
@test all(isempty.(values(troph_three_nan[alive_keys])))

@test troph_three[(:species, :A, :tlvl)] ==
(species = foodweb.species, A = foodweb.A, tlvl = [2.0, 1.0, 1.0])
@test troph_three[alive_keys] == (
alive_species = foodweb.species,
alive_A = foodweb.A,
alive_trophic_level = [2.0, 1.0, 1.0],
)
end

@testset "Producer growth rate" begin
Expand Down
38 changes: 7 additions & 31 deletions test/measures/test-utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,27 @@
"Cannot extract 100 timesteps from a trajectory solution \
with only 12 timesteps. Consider decreasing the `last` \
argument value and/or specifying it as a percentage instead \
(e.g. `\"10%\"`) or setting `autofix_last = true`.",
(e.g. `\"10%\"`).",
) extract_last_timesteps(sim, last = 100)
@test_throws ArgumentError("Cannot extract 0 timesteps. `last` should be \
a positive integer.") extract_last_timesteps(sim, last = 0)
@test_throws ArgumentError("Cannot extract -10 timesteps. `last` should be \
a positive integer.") extract_last_timesteps(sim, last = -10)

@test_throws ArgumentError("Cannot extract `last` from a decimal number. \
Did you mean \"4.5%\"?") extract_last_timesteps(
sim,
last = 4.5,
)
@test_throws ArgumentError("Cannot extract `last` from a floating point number. \
Did you mean \"4.5%\"?") extract_last_timesteps(sim, last = 4.5)

@test_throws ArgumentError("Cannot extract timesteps with \
Any[]. \
`last` should be a positive integer or a string \
representing a percentage.") extract_last_timesteps(
@test_throws ArgumentError("Cannot extract timesteps with `last=Any[]` \
of type Vector{Any}. `last` should be a positive integer \
or a string representing a percentage.") extract_last_timesteps(
sim,
last = [],
)

warn_empty_table = "0.001% of 12 timesteps correspond to 0 output lines: \
an empty table has been extracted."
@test_warn warn_empty_table extract_last_timesteps(sim, last = ".001%")
# But returns 1 timestep if we fix it for you
@test extract_last_timesteps(sim; last = ".001%", autofix_last = true, quiet = true) ==
[sim[:, end];;]
msg_warn = "Cannot extract 0 timesteps from a trajectory solution with only 12 \
timesteps. We set 'last' to 1."
@test_warn msg_warn extract_last_timesteps(sim, last = ".0000001%", autofix_last = true)

m1 = simulates(params, [0, 0.5]; tmax = 0.5)
msg_warn = "Cannot extract 0 timesteps from a trajectory solution with only 3 \
timesteps. We set 'last' to 1."
@test_warn msg_warn extract_last_timesteps(m1, last = ".1%", autofix_last = true)
@test_nowarn extract_last_timesteps(m1, last = ".1%", autofix_last = true, quiet = true)

msg_warn = "Cannot extract 100 timesteps from a trajectory solution with only 3 \
timesteps. We set 'last' to 1, as 10% of timesteps < 1."
@test_warn msg_warn extract_last_timesteps(m1, last = 100, autofix_last = true)

msg_warn = "Cannot extract 100 timesteps from a trajectory solution with only 12 \
timesteps. We set 'last' to 1, i.e. the last 10% of timesteps."
@test_warn msg_warn extract_last_timesteps(sim, last = 100, autofix_last = true)
@test_nowarn extract_last_timesteps(sim, last = 100, autofix_last = true, quiet = true)
@test_nowarn extract_last_timesteps(sim, last = ".001%", quiet = true)

# Test species selection
@test extract_last_timesteps(sim; last = "10%", idxs = ["s1"]) ==
Expand Down

0 comments on commit 157938b

Please sign in to comment.