diff --git a/main.cpp b/main.cpp
index b2f12ac..55ce06c 100644
--- a/main.cpp
+++ b/main.cpp
@@ -262,6 +262,7 @@ struct PoPSOptions
struct Option *probability, *probability_series;
struct Option* spread_rate_output;
struct Option *quarantine, *quarantine_output, *quarantine_directions;
+ struct Option *dispersers_output, *established_dispersers_output;
struct Option *output_frequency, *output_frequency_n;
};
@@ -739,6 +740,20 @@ int main(int argc, char* argv[])
opt.percent_natural_dispersal->options = "0-1";
opt.percent_natural_dispersal->guisection = _("Dispersal");
+ opt.dispersers_output = G_define_standard_option(G_OPT_R_OUTPUT);
+ opt.dispersers_output->key = "dispersers_output";
+ opt.dispersers_output->label = _("Output raster of disperses");
+ opt.dispersers_output->description = _("Dispersers are accumulated over all steps and stochastic runs");
+ opt.dispersers_output->required = NO;
+ opt.dispersers_output->guisection = _("Output");
+
+ opt.established_dispersers_output = G_define_standard_option(G_OPT_R_OUTPUT);
+ opt.established_dispersers_output->key = "established_dispersers_output";
+ opt.established_dispersers_output->label = _("Output raster of established disperses");
+ opt.established_dispersers_output->description = _("Dispersers are accumulated over all steps and stochastic runs");
+ opt.established_dispersers_output->required = NO;
+ opt.established_dispersers_output->guisection = _("Output");
+
opt.infected_to_dead_rate = G_define_option();
opt.infected_to_dead_rate->type = TYPE_DOUBLE;
opt.infected_to_dead_rate->key = "mortality_rate";
@@ -1274,6 +1289,9 @@ int main(int argc, char* argv[])
established_dispersers.emplace_back(
I_species_rast.rows(), I_species_rast.cols());
}
+ std::vector dispersers_rasts(num_runs, Img(S_species_rast, 0));
+ std::vector established_dispersers_rasts(num_runs, Img(S_species_rast, 0));
+
std::vector> soil_reservoirs(
use_soils ? num_runs : 0,
std::vector(
@@ -1401,6 +1419,10 @@ int main(int argc, char* argv[])
Network::null_network(),
suitable_cells);
++weather_step;
+ if (opt.dispersers_output->answer)
+ dispersers_rasts[run] += dispersers[run];
+ if (opt.established_dispersers_output->answer)
+ established_dispersers_rasts[run] += established_dispersers[run];
}
}
@@ -1604,6 +1626,31 @@ int main(int argc, char* argv[])
fprintf(fp, "%s", output.c_str());
G_close_option_file(fp);
}
-
+ if (opt.dispersers_output->answer) {
+ Img dispersers_rasts_sum(I_species_rast.rows(), I_species_rast.cols(), 0);
+ for (unsigned i = 0; i < num_runs; i++)
+ dispersers_rasts_sum += dispersers_rasts[i];
+ if (opt.dispersers_output->answer) {
+ // write final result
+ raster_to_grass(
+ dispersers_rasts_sum,
+ opt.dispersers_output->answer,
+ "Sum of all dispersers",
+ interval.end_date());
+ }
+ }
+ if (opt.established_dispersers_output->answer) {
+ Img established_dispersers_rasts_sum(I_species_rast.rows(), I_species_rast.cols(), 0);
+ for (unsigned i = 0; i < num_runs; i++)
+ established_dispersers_rasts_sum += established_dispersers_rasts[i];
+ if (opt.established_dispersers_output->answer) {
+ // write final result
+ raster_to_grass(
+ established_dispersers_rasts_sum,
+ opt.established_dispersers_output->answer,
+ "Sum of all established dispersers",
+ interval.end_date());
+ }
+ }
return 0;
}
diff --git a/testsuite/test_r_pops_spread.py b/testsuite/test_r_pops_spread.py
index 08fcd06..07598ff 100755
--- a/testsuite/test_r_pops_spread.py
+++ b/testsuite/test_r_pops_spread.py
@@ -191,7 +191,7 @@ def tearDown(self):
"g.remove",
flags="f",
type="raster",
- pattern="average*,single*,stddev*,probability*,dead*",
+ pattern="average*,single*,stddev*,probability*,dead*,*dispersers",
)
def test_outputs(self):
@@ -1406,6 +1406,54 @@ def test_survival_rate_0_percent(self):
# Even with multiple runs, stddev should be still zero.
self.assertRasterFitsUnivar(raster="stddev", reference=values, precision=0.001)
+ def test_outputs_dispersers(self):
+ """Check dead output of mortality"""
+ start = "2019-01-01"
+ end = "2022-12-31"
+ self.assertModule(
+ "r.pops.spread",
+ host="host",
+ total_plants="max_host",
+ infected="infection",
+ average="average",
+ average_series="average",
+ single_series="single",
+ stddev="stddev",
+ stddev_series="stddev",
+ probability="probability",
+ probability_series="probability",
+ start_date=start,
+ end_date=end,
+ seasonality=[1, 12],
+ step_unit="week",
+ step_num_units=1,
+ reproductive_rate=1,
+ natural_dispersal_kernel="exponential",
+ natural_distance=50,
+ natural_direction="W",
+ natural_direction_strength=3,
+ anthropogenic_dispersal_kernel="cauchy",
+ anthropogenic_distance=1000,
+ anthropogenic_direction_strength=0,
+ percent_natural_dispersal=0.95,
+ random_seed=1,
+ runs=5,
+ nprocs=5,
+ dispersers_output="dispersers",
+ established_dispersers_output="established_dispersers",
+ )
+ self.assertRasterExists("dispersers")
+ self.assertRasterExists("established_dispersers")
+
+ values = dict(null_cells=0, min=0, max=15530, mean=522.275)
+ self.assertRasterFitsUnivar(
+ raster="dispersers", reference=values, precision=0.001
+ )
+ values = dict(null_cells=0, min=0, max=129, mean=8.833)
+ self.assertRasterFitsUnivar(
+ raster="established_dispersers", reference=values, precision=0.001
+ )
+
def test_with_and_without_anthropogenic_dispersal_multiple_seeds(self):
"""Check that multiple seeds keep anthropogenic dispersal separate