CoralBlox is a coral growth and mortality model. It simulates distinct coral
FunctionalGroups
indirectly competing for a limited space over time. The model does not
directly consider disturbances such as mortality caused by cyclone, CoTS or heat stress. It
also does not have representation of anthropogenic restoration and conservation activities
such as coral seeding or cloud brightening. Such considerations can be included by
perturbing model state and CoralBlox parameters between time steps.
In CoralBlox, the corals are grouped according to their approximate diameter, forming
SizeClasses
. A SizeClass
is defined by a diameter interval, so all corals whose diameter
lie within the same interval belong to the same SizeClass
. Hence, at each timestep, coral
growth is represented by a displacement in the diameter space. For each functional group,
corals from distinct SizeClasses
have distinct growth rates. Since all corals within the
same SizeClass
and FunctionalGroup
have the same growth rate, we can visualize the
growth as groups of corals moving on the diameter space in blocks. Further details about
how this growth and mortality are implemented can be found below.
This plot was generated using the following script as an example of how CoralBlox can be used:
It is worth noting that in the example script below there are no environmental disturbances and all parameters (initial coral cover, size classes bounds, linear extensions and survival rates), together with the number of recruits per year, were mocked and adjusted to generate the above plot.
Before running the model, one needs to instantiate an Array to hold the coral cover for each
timestep, FunctionalGroup
and SizeClass
, and a vector of FunctionalGroup
s:
using CoralBlox: FunctionalGroup
n_timesteps::Int64 = 100
n_functional_groups::Int64 = 3
n_size_classes::Int64 = 4
# Coral cover cache
C_cover::Array{Float64, 3} = zeros(n_timesteps, n_functional_groups, n_size_classes)
# Habitable area is the maximum possible cover
habitable_area::Float64 = 1e6
# Each row contains SizeClass' bounds
size_class_bounds::Matrix{Float64} = [
0 0.05 0.8 1.4 1.5;
0 0.05 0.5 0.9 1.0;
0 0.05 0.5 0.9 1.0
]
# Mock initial coral cover
C_cover[1, :, :] = [
0.08 0.05 0.02 0.005;
0.07 0.06 0.03 0.007;
0.05 0.05 0.02 0.003
] .* habitable_area
# Create functional groups
functional_groups::Vector{FunctionalGroup} = FunctionalGroup.(
eachrow(size_class_bounds[:, 1:end-1]), # lower bounds
eachrow(size_class_bounds[:, 2:end]), # upper bounds
eachrow(C_cover[1, :, :]) # initial coral covers
)
In order to run a single timestep, a vector of recruits (representing young corals
entering the system) and matrices containing growth and survival rates for each
FunctionalGroup
and SizeClass
is needed:
using CoralBlox: linear_extension_scale_factors, max_projected_cover,
using CoralBlox: timestep!, coral_cover
# Mock linear extensions
linear_extensions::Matrix{Float64} = [
0.004 0.025 0.1 0.0;
0.003 0.007 0.005 0.0;
0.002 0.004 0.01 0.0
]
# Mock survival rate
survival_rate::Matrix{Float64} = [
0.5 0.6 0.65 0.8;
0.6 0.7 0.8 0.8;
0.5 0.8 0.8 0.8
]
# Caclulate maximum projected cover
habitable_max_projected_cover = max_projected_cover(
linear_extensions,
size_class_bounds,
habitable_area
)
# Only apply linear extension scale factor when cover is above the scale_threshold
# Assuming the effects of competition for space are only relevant when population density
# is high enough. Note that this is not a requirement of CoralBlox.
scale_threshold = 0.9 * habitable_area
# Linear extension scale factor
local linear_extension_scale_factors::Float64
for tstep::Int64 in 2:n_timesteps
# Apply scale factor to linear_extension when cover is above scale_threshold to account
# for spatial competition when population density is high
linear_extension_scale_factors = if sum(C_cover[tstep-1, :, :]) < scale_threshold
1
else
linear_extension_scale_factors(
C_cover[tstep-1, :, :],
habitable_area,
linear_extensions,
size_class_bounds,
habitable_max_projected_cover,
)
end
# Use scale factor to calculate growth to account for spatial competition, as explained
growth_rate::Matrix{Float64} = linear_extensions .* linear_extension_scale_factors
# Mock recruits proportional to each functional group's cover and available space
# This mock is for example purposes only
available_space::Float64 = habitable_area - sum(C_cover[tstep-1, :, :])
available_proportion::Float64 = available_space / habitable_area
adults_cover::Vector{Float64} = dropdims(sum(C_cover[tstep-1, :, 2:end], dims=2), dims=2)
recruits_weights::Vector{Float64} = [0.6, 0.9, 1.5]
availability_weight::Float64 = log(2.5, 1.5 + available_proportion)
# In reality, recruits cover for each functional group would come from a fecundity model
recruits::Vector{Float64} = adults_cover .* recruits_weights .* availability_weight
# Perform timestep
timestep!(
functional_groups,
recruits,
growth_rate,
survival_rate
)
# Write to the cover matrix
coral_cover(functional_groups, @view(C_cover[tstep, :, :]))
end
The linear_extension_scale_factors
calculated at each timestep prevents the corals from outgrowing
the habitable area, taking into account the simultaneous growth across all functional groups
and size classes. Details about how this scale factor is calculated and used can be
found below.
Consideration of external factors that may influence coral growth and mortality
could be included outside of each timestep!
call, potentially informed by a broader
ecosystem model.
Consider the area of each colony approximated by the area of a circumference with diameter
Hence, given a diameter density function
In practice, we work with groups of coral with a constant diameter density over a certain
diameter interval, that we call CoralBlock
. Each CoralBlock
is characterized by a
constant diameter density
The following sections explain in more detail how growth and mortality are represented.
At each timestep CoralBlock
. That is done by multiplying each FunctionalGroup
and SizeClass
survival
rate CoralBlock
diameter density
For each FunctionalGroup
, we can think of a horizontal axis representing the diameter
space with its CoralBlock
s lined up. Then, a growth event is conceived as the displacement
of each block in this axis by a factor FunctionalGroup
SizeClass
SizeClass
width
After a growth event we can have one of the three following situations: (1) the entire block
remains in size class
In (1), the CoralBlock
doesn't cross the frontier between SizeClass
es. In this case,
its movement is constant. In (2) and (3), as soon as part of the block enters the next
SizeClass
, the two parts move with different speeds (one can think of an analogy with a
mass of water flowing between two pipes with distinct diameters).
Each coral colony has a base growth FunctionalGroup
and SizeClass
. As the available space gets more populated, we assume that growth is
negatively affected, since there is a competition for resources. Even more, when there's no
available space left, we want the growth to be zero. To account for that, at each time step
we multiply all linear extensions by the same scale factor FunctionalGroup
s.
First we calculate a projected cover SizeClass
of the FunctionalGroup
with the highest linear
extension, and they growth freely. Two important notes here. First, there are infinite
possible upper bounds and, although the choice will affect the final scale factor, this
shouldn't change the final result drastically. Second, to prevent having to run a whole
timestep twice, for this projected cover (and other calculations related to the scale
factor) we instead use a simplified version of the model where each block moves by with a
constant velocity, even when changing SizeClass
es.
Next, we calculate an adjusted projected cover
Where
Lastly, we find the scale factor CoverBlock
growth rate is given by
CoverBlocks
. The actual growth rate
used to run a timestep