Skip to content
2 changes: 1 addition & 1 deletion docs/src/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ reset_rme()

```julia
using ReefModEngine
using CSV, DataFrames, MAT
using CSV, DataFrames

# Initialize RME (may take a minute or two)
init_rme("path to RME directory")
Expand Down
72 changes: 72 additions & 0 deletions docs/src/results_store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Results

Reefmod Engine outputs and scenario information are recorded from the ReefMod Engine API
after each scenario(s) run, not before.

## Result Store

The result stores holds all model outputs from both counterfactual and interventions.
Information about scenarios is stored in the scenario field of the result store. The reps
field the result store is exactly have the number of scenarios contained in the scenario
Dataframe and result YAX Dataset holdering outcomes. This is because the API forces counterfactual to be
evaluated with every intervention run.

Model inputs are stored in the `results` field of the store and contains the following
variables:

- `total_cover` ~ Total Coral Cover (% of total reef area)
- `dhw` ~ Degree Heating Weeks (°C weeks)
- `dhw_mortality` ~ DHW Mortality (% of population*)
- `cyc_mortality` ~ Cyclone Mortality (% of population*)
- `cyc_cat` ~ Cyclone Category
- `cots` ~ COTS Population (per ha)
- `cots_mortality` ~ Mortality caused by COTS (% of population*)
- `total_taxa_cover` ~ Total Species Cover (% of total reef area)

*\* not formally confirmed*

## Usage

A side effect of the C++ API structure means that each intervention scenario must be
executed separately (**Intervention scenario** meaning a run with a specific intervention
strategy, not referring to runs with differing environmental inputs called repeats).

1. Before storing any results, create the result store.

```julia
result_store = ResultStore(start_year, end_year)
```
2. Perform model run

```julia
...

```

3. Store results

`reps` is the number of repeats executed in the run.

```julia
concat_results!(result_store, start_year, end_year, reps)
```

## Saving Results

Results can be saved using `save_result_store`.
```julia
save_result_store(<dir_name>, <result_store>)
```

The results directory will contain two files `results.nc` and `scenarios.csv`.

The NetCDF file contains all the model inputs and outputs described above and the scenarios
csv file details the intervention parameters used in the model runs and is in the same order
as the scenario dimension in the netcdf fie.

::: warning

If there is already a scenarios Dataframe in the directory being saved to it will
be overwritten

:::
60 changes: 35 additions & 25 deletions src/ResultStore.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ function ResultStore(start_year, end_year, n_reefs)
end

"""
save_result_store(result_store::ResultStore, dir_name::String="")::Nothing
save_result_store(dir_name::String, result_store::ResultStore)::Nothing

Save results to a netcdf file and a dataframe containing the scenario runs. Saved to the
Save results to a netcdf file and a dataframe containing the scenario runs. Saved to the
given directory. The directory is created if it does not exit.
"""
function save_result_store(result_store::ResultStore, dir_name::String="")::Nothing
if dir_name==""
dir_name = "RME_outcomes_$(Dates.format(now(), "yyyy-mm-dd-HH-MM-SS"))"
end
function save_result_store(dir_name::String, result_store::ResultStore)::Nothing
mkpath(dir_name)

result_path = joinpath(dir_name, "results.nc")
Expand All @@ -57,7 +54,7 @@ function create_dataset(start_year::Int, end_year::Int, n_reefs::Int, reps::Int)
year_range = (end_year - start_year) + 1

arr_size = (year_range, n_reefs, 2 * reps)

# Total Coral cover [% of total reef area]
total_cover = DataCube(
zeros(arr_size...);
Expand Down Expand Up @@ -169,7 +166,7 @@ Allocate additional memory before adding an additional result set. Result sets m
same time frame.
"""
function preallocate_concat!(rs, start_year, end_year, reps::Int64)::Nothing
if rs.start_year != start_year && rs.end_year != end_year
if rs.start_year != start_year || rs.end_year != end_year
throw(ArgumentError("Results stored in the same dataset must have equal timeframes"))
end
# If the results dataset is empty construct the initial dataset
Expand All @@ -178,7 +175,7 @@ function preallocate_concat!(rs, start_year, end_year, reps::Int64)::Nothing

return nothing
end

prev_reps::Int = length(rs.results.scenarios)
new_n_reps::Int = prev_reps + 2 * reps
n_reefs::Int = length(rs.results.locations)
Expand All @@ -188,7 +185,7 @@ function preallocate_concat!(rs, start_year, end_year, reps::Int64)::Nothing
Dim{:locations}(1:rs.n_reefs),
Dim{:scenarios}(prev_reps+1:new_n_reps)
)

# Concatenate total_taxa_cover cube separately.
cubes = [
:total_cover,
Expand All @@ -201,8 +198,8 @@ function preallocate_concat!(rs, start_year, end_year, reps::Int64)::Nothing
]
for cube_name in cubes
rs.results.cubes[cube_name] = cat(
rs.results.cubes[cube_name],
YAXArray(axlist, zeros(rs.year_range, n_reefs, 2 * reps));
rs.results.cubes[cube_name],
YAXArray(axlist, zeros(rs.year_range, n_reefs, 2 * reps));
dims=Dim{:scenarios}(1:new_n_reps)
)
end
Expand All @@ -215,8 +212,8 @@ function preallocate_concat!(rs, start_year, end_year, reps::Int64)::Nothing
Dim{:scenarios}(prev_reps+1:new_n_reps)
)
rs.results.cubes[:total_taxa_cover] = cat(
rs.results.cubes[:total_taxa_cover],
YAXArray(axlist, zeros(rs.year_range, n_reefs, n_species, 2 * reps));
rs.results.cubes[:total_taxa_cover],
YAXArray(axlist, zeros(rs.year_range, n_reefs, n_species, 2 * reps));
dims=Dim{:scenarios}(1:new_n_reps)
)
# Axes stored in the dataset are separate from the Cubes and must be updated.
Expand Down Expand Up @@ -247,7 +244,7 @@ function append_scenarios!(rs::ResultStore, reps::Int)::Nothing
enrichment_area::Float64 = 0.0
# Number of locations
enrichment_locs::Float64 = 0.0

n_locs::Vector{Float64} = [0.0]
# This for loop accounts for more complex intervention patterns.
n_iv::Int = @getRME ivCount()::Cint
Expand All @@ -264,26 +261,38 @@ function append_scenarios!(rs::ResultStore, reps::Int)::Nothing
outplant_count += n_years * @getRME ivOutplantCountPerM2(name::Cstring)::Cdouble
outplant_area += n_years * @getRME ivOutplantAreaPct(name::Cstring)::Cdouble
outplant_locs += n_years * n_locs[1]
elseif type=="enrich"
elseif type == "enrich"
n_enrichment_iv += n_years
enrichment_count += n_years * @getRME ivEnrichCountPerM2(name::Cstring)::Cdouble
enrichment_area += n_years * @getRME ivEnrichAreaPct(name::Cstring)::Cdouble
enrichment_locs += n_years * n_locs[1]
end
end

# Avoid division by zero errors
n_outplant_iv = max(1, n_outplant_iv)
n_enrichment_iv = max(1, n_enrichment_iv)
# Create vector for compatibility with c++ pointers.

# Create vector for compatibility with C++ pointers.
dhw_tolerance_outplants::Vector{Float64} = [0.0]
@RME getOption(
"restoration_dhw_tolerance_outplants"::Cstring,
dhw_tolerance_outplants::Ptr{Cdouble}
)::Cint

has_outplants = try
@RME getOption(
"restoration_dhw_tolerance_outplants"::Cstring,
dhw_tolerance_outplants::Ptr{Cdouble}
)::Cint

true
catch err
if !(err isa ArgumentError)
rethrow(err)
end

false
end

df_cf::DataFrame = DataFrame(
counterfactual=repeat(1, reps),
dhw_tolerance=repeat(dhw_tolerance_outplants, reps),
outplant_count_per_m2=0,
outplant_area_pct=0,
Expand All @@ -293,6 +302,7 @@ function append_scenarios!(rs::ResultStore, reps::Int)::Nothing
n_enrichment_locs=0,
)
df_iv::DataFrame = DataFrame(
counterfactual=repeat(0, reps),
dhw_tolerance=repeat(dhw_tolerance_outplants, reps),
outplant_count_per_m2=outplant_count / n_outplant_iv,
outplant_area_pct=outplant_area / n_outplant_iv,
Expand All @@ -314,7 +324,7 @@ end
"""
concat_results!(rs::ResultStore, start_year::Int64, end_year::Int64, reps::Int64)::Nothing

Append results for all runs/replicates.
Append results for all runs/replicates.

# Arguments
- `rs` : Result store to save data to
Expand All @@ -335,7 +345,7 @@ function concat_results!(
n_reefs = 3806
n_species = length(rs.results.total_taxa_cover.taxa)
tmp = zeros(n_reefs)

for r in 1:reps
for yr in start_year:end_year
# "" : Can specify name of a reef set, or empty to indicate all reefs
Expand Down
12 changes: 11 additions & 1 deletion src/rme_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,17 @@ function init_rme(rme_path::String)::Nothing
data_fp = joinpath(rme_path, "data_files")

@RME setDataFilesPath(data_fp::Cstring)::Cint
@RME init(joinpath(rme_path, "data_files", "config", "config.xml")::Cstring)::Cint

try
@RME init(joinpath(rme_path, "data_files", "config", "config.xml")::Cstring)::Cint
catch err
msg = """
Error encountered initializing RME.
See documentation for ReefModEngine.jl and check RME config.xml file.
"""
@info msg
rethrow(err)
end

rme_vers = @RME version()::Cstring
@info "Loaded RME $rme_vers"
Expand Down