Program Listing for File Dispersal.cpp#
↰ Return to documentation for file (/github/workspace/src/Dispersal.cpp
)
#include <vector>
#include <array>
#include <cmath>
#include <limits>
#include <algorithm>
#include <numeric>
#include "Dispersal.h"
#include "random.h"
#include "constants.h"
#include <iostream>
Dispersal::Dispersal(DispersalParams* params, BoundaryType boundary, double side_x, double side_y)
{
disp_rate = params->disp_rate;
max_disp = params->max_disp;
if (boundary == Toroid) {
boundary_strategy = new ToroidalBoundaryStrategy(side_x, side_y);
}
else if (boundary == Edge) {
boundary_strategy = new EdgeBoundaryStrategy(side_x, side_y);
}
else {
boundary_strategy = new ToroidalBoundaryStrategy(side_x, side_y);
}
}
std::vector<std::array<long long int, constants::num_gen>> Dispersal::M_dispersing_out(const std::vector<Patch*> &sites)
{
std::vector<std::array<long long int, constants::num_gen>> m_move;
std::array<long long int, constants::num_gen> m;
std::array<long long int, constants::num_gen> m_out;
for (int pat=0; pat < sites.size(); ++pat) {
m = sites[pat]->get_M();
for (int i=0; i < constants::num_gen; ++i) {
m_out[i] = random_binomial(m[i], disp_rate); // how many adult males will disperse from the given patch
}
m_move.push_back(m_out);
}
return m_move;
}
std::vector<std::array<std::array<long long int, constants::num_gen>, constants::num_gen>> Dispersal::F_dispersing_out(const std::vector<Patch*> &sites)
{
std::vector<std::array<std::array<long long int, constants::num_gen>, constants::num_gen>> f_move;
std::array<std::array<long long int, constants::num_gen>, constants::num_gen> f;
std::array<std::array<long long int, constants::num_gen>, constants::num_gen> f_out;
for (int pat=0; pat < sites.size(); ++pat) {
f = sites[pat]->get_F();
for (int i=0; i < constants::num_gen; ++i) {
for (int j=0; j < constants::num_gen; ++j) {
f_out[i][j] = random_binomial(f[i][j], disp_rate); // how many adult females will disperse from the given patch
}
}
f_move.push_back(f_out);
}
return f_move;
}
DistanceKernelDispersal::~DistanceKernelDispersal() {
delete boundary_strategy;
}
void DistanceKernelDispersal::set_connecs(std::vector<Patch*> &sites) {
connec_indices.clear();
connec_weights.clear();
auto connecs = compute_connecs(sites);
connec_indices = connecs.first;
connec_weights = connecs.second;
}
void DistanceKernelDispersal::adults_disperse(std::vector<Patch*> &sites) {
// adults dispersing out from each patch
std::vector<std::array<long long int, constants::num_gen>> m_move = M_dispersing_out(sites); // males dispersing from each patch
for (int pat = 0; pat < sites.size(); ++pat) { // update population numbers
sites[pat]->M_disperse_out(m_move[pat]);
}
std::vector<std::array<std::array<long long int, constants::num_gen>, constants::num_gen>> f_move = F_dispersing_out(sites);
for (int pat = 0; pat < sites.size(); ++pat) {
sites[pat]->F_disperse_out(f_move[pat]);
}
// adults dispersing into each patch
std::vector<long long int> m_disp_by_new_pat;
for (int pat=0; pat < sites.size(); ++pat) {
for (int i=0; i < constants::num_gen; ++i) {
// how many males of the given patch and given genotype will disperse to each of its connected patches
m_disp_by_new_pat = random_multinomial(m_move[pat][i], connec_weights[pat]);
for (int new_pat=0; new_pat < m_disp_by_new_pat.size(); ++new_pat) { // update population numbers
sites[connec_indices[pat][new_pat]]->M_disperse_in(i, m_disp_by_new_pat[new_pat]);
}
}
}
std::vector<long long int> f_disp_by_new_pat;
for (int pat = 0; pat < sites.size(); ++pat) {
for (int i = 0; i < constants::num_gen; ++i) {
for (int j=0; j < constants::num_gen; ++j) {
f_disp_by_new_pat = random_multinomial(f_move[pat][i][j], connec_weights[pat]);
for (int new_pat=0; new_pat < f_disp_by_new_pat.size(); ++new_pat) {
sites[connec_indices[pat][new_pat]]->F_disperse_in(i, j, f_disp_by_new_pat[new_pat]);
}
}
}
}
}
std::pair<std::vector<std::vector<int>>, std::vector<std::vector<double>>> DistanceKernelDispersal::compute_connecs(std::vector<Patch*>
&sites)
{
std::vector<std::vector<int>> connec_indices;
std::vector<std::vector<double>> connec_weights;
std::vector<int> connec_indices_pat;
std::vector<double> connec_weights_pat;
for (int pat=0; pat < sites.size(); ++pat) {
connec_indices_pat.clear();
connec_weights_pat.clear();
for (int new_pat=0; new_pat < sites.size(); ++new_pat) {
double dd = boundary_strategy->distance(sites[pat]->get_coords(), sites[new_pat]->get_coords());
if (dd < max_disp) {
connec_indices_pat.push_back(new_pat);
double weight = max_disp - dd;
connec_weights_pat.push_back(weight);
}
}
connec_indices.push_back(connec_indices_pat);
connec_weights.push_back(connec_weights_pat);
}
return {connec_indices, connec_weights};
}
RadialDispersal::RadialDispersal(DispersalParams* params, BoundaryType boundary, double side_x, double side_y): Dispersal(params, boundary, side_x, side_y) {
connec_weights_sum.clear();
}
RadialDispersal::~RadialDispersal() {
delete boundary_strategy;
}
void RadialDispersal::set_connecs(std::vector<Patch*> &sites) {
connec_indices.clear();
connec_weights.clear();
auto connecs = compute_connecs(sites);
connec_indices = connecs.first;
connec_weights = connecs.second;
// calculate the sum of connec weights for each patch to later use for dispersal mortality
connec_weights_sum.clear();
std::vector<double> ws(connec_weights.size());
for (int pat=0; pat < ws.size(); ++pat) {
double sum = std::accumulate(connec_weights[pat].begin(), connec_weights[pat].end(), 0.0);
ws[pat] = sum;
}
connec_weights_sum = ws;
}
void RadialDispersal::adults_disperse(std::vector<Patch*> &sites) {
// adults dispersing out from each patch
std::vector<std::array<long long int, constants::num_gen>> m_move = M_dispersing_out(sites); // males dispersing from each patch
for (int pat = 0; pat < sites.size(); ++pat) { // update population numbers
sites[pat]->M_disperse_out(m_move[pat]);
}
std::vector<std::array<std::array<long long int, constants::num_gen>, constants::num_gen>> f_move = F_dispersing_out(sites);
for (int pat = 0; pat < sites.size(); ++pat) {
sites[pat]->F_disperse_out(f_move[pat]);
}
// adults dispersing into each patch
std::vector<long long int> m_disp_by_new_pat;
for (int pat=0; pat < sites.size(); ++pat) {
for (int i=0; i < constants::num_gen; ++i) {
// how many males survive dispersal due to dispersing in the connected intervals of the catchment radius
// (whilst those dispersing in unconnected directions die)
long long int surv_m = random_binomial(m_move[pat][i], connec_weights_sum[pat]);
// how many males of the given patch and given genotype will disperse to each of its connected patches
//m_disp_by_new_pat = random_multinomial(m_move[pat][i], connec_weights[pat]);
m_disp_by_new_pat = random_multinomial(surv_m, connec_weights[pat]);
for (int new_pat=0; new_pat < m_disp_by_new_pat.size(); ++new_pat) { // update population numbers
sites[connec_indices[pat][new_pat]]->M_disperse_in(i, m_disp_by_new_pat[new_pat]);
}
}
}
std::vector<long long int> f_disp_by_new_pat;
for (int pat = 0; pat < sites.size(); ++pat) {
for (int i = 0; i < constants::num_gen; ++i) {
for (int j=0; j < constants::num_gen; ++j) {
long long int surv_f = random_binomial(f_move[pat][i][j], connec_weights_sum[pat]);
//f_disp_by_new_pat = random_multinomial(f_move[pat][i][j], connec_weights[pat]);
f_disp_by_new_pat = random_multinomial(surv_f, connec_weights[pat]);
for (int new_pat=0; new_pat < f_disp_by_new_pat.size(); ++new_pat) {
sites[connec_indices[pat][new_pat]]->F_disperse_in(i, j, f_disp_by_new_pat[new_pat]);
}
}
}
}
}
std::pair<std::vector<std::vector<int>>, std::vector<std::vector<double>>> RadialDispersal::compute_connecs(std::vector<Patch*> &sites) {
int num_sites = sites.size();
std::vector<std::vector<double>> connec_weights(num_sites);
std::vector<std::vector<int>> connec_indices(num_sites);
std::vector<double> radii;
std::vector<std::pair<double, double>> intervals; // vector to store intervals
std::pair<double, double> qq; // temporary interval
double alpha, theta, smallest_dist;
Point loc1, loc2;
for(int pat=0;pat<num_sites;pat++) {
auto result = compute_distances_site(pat,sites);
auto distances =result.first;
auto local_indices =result.second;
smallest_dist = std::numeric_limits<double>::infinity();
for (double dist : distances) {
if (dist > 0 && dist < smallest_dist) {smallest_dist = dist;}
}
radii.push_back(0.5*smallest_dist);
}
// compute inter-point distances
for (int i=0; i < num_sites; i++) {
loc1 = sites[i]->get_coords();
intervals.clear();
auto result = compute_distances_site(i,sites);
auto distances = result.first;
auto local_indices =result.second;
std::vector<int> order = get_sorted_positions(distances);
for (int j=0; j < order.size(); j++)
{
int loc_index = order[j]; // index among locally connected sites
int glob_index = local_indices[loc_index]; // index among all sites
loc2 = sites[glob_index]->get_coords();
alpha = std::atan(radii[glob_index] / distances[loc_index]);
loc2 = boundary_strategy->relative_pos(loc1, loc2);
double length = 0;
if (loc2.y > loc1.y)
{
if (loc2.x > loc1.x) {
theta = std::atan((loc2.y - loc1.y) / (loc2.x - loc1.x));
}
else if (loc2.x == loc1.x) {
theta = constants::pi/2;
}
else {
theta = constants::pi/2 + std::atan((loc1.x-loc2.x) / (loc2.y-loc1.y));
}
};
if (loc2.y == loc1.y) {
if (loc2.x >= loc1.x) {
theta = 0;
}
else {
theta = constants::pi;
}
}
if (loc2.y < loc1.y) {
if (loc1.x > loc2.x) {
theta = constants::pi + std::atan((loc1.y - loc2.y) / (loc1.x - loc2.x));
}
else if (loc2.x==loc1.x) {
theta = 3 * constants::pi / 2;
}
else {
theta = 3 * constants::pi / 2 + std::atan((loc2.x - loc1.x) / (loc1.y - loc2.y));
}
}
double t_min = wrap_around((theta - alpha) / (2*(constants::pi)), 1);
double t_plus = wrap_around((theta + alpha) / (2*(constants::pi)), 1);
if (t_min > t_plus) {
qq = {t_min, 1};
auto result = compute_interval_union(qq, intervals);
intervals = result.first;
length += result.second;
qq = {0, t_plus};
result = compute_interval_union(qq, intervals);
intervals = result.first;
length += result.second;
}
else {
qq = {t_min, t_plus};
auto result = compute_interval_union(qq, intervals);
intervals = result.first;
length = result.second;
}
if (length > 0) {
connec_weights[i].push_back(length);
connec_indices[i].push_back(glob_index);
}
}
}
return {connec_indices, connec_weights};
}
std::pair<std::vector<double>,std::vector<int>> RadialDispersal::compute_distances_site(int i, std::vector<Patch*> &sites)
{
std::vector<double> distances;
std::vector<int> indices;
double dd;
for (int j=0; j < sites.size(); ++j) {
dd = boundary_strategy->distance(sites[i]->get_coords(), sites[j]->get_coords());
if(dd < max_disp && i != j) {
distances.push_back(dd);
indices.push_back(j);
}
}
return {distances,indices};
}
double RadialDispersal::wrap_around(double value, double range)
{
return std::fmod(std::fmod(value, range) + range, range);
}
std::pair<std::vector<std::pair<double, double>>, double> RadialDispersal::compute_interval_union(const std::pair<double, double>& qq,
const std::vector<std::pair<double, double>>& input)
{
// Create a vector to store the union of intervals
std::vector<std::pair<double, double>> output;
// Merge overlapping intervals in the output vector
std::pair<double, double> merged_interval = qq;
for (const auto& interval : input) {
if (interval.second < merged_interval.first || interval.first > merged_interval.second) {
output.push_back(interval);
}
else {
merged_interval.first = std::min(merged_interval.first, interval.first);
merged_interval.second = std::max(merged_interval.second, interval.second);
}
}
// Add the merged interval
output.push_back(merged_interval);
// Calculate the difference in the sum of lengths
double sum_lengths = 0.0;
for (const auto& interval : output) {
sum_lengths += interval.second - interval.first;
}
double input_sum_lengths = 0.0;
for (const auto& interval : input) {
input_sum_lengths += interval.second - interval.first;
}
double diff = sum_lengths - input_sum_lengths;
std::sort(output.begin(), output.end());
return {output, diff};
}
std::vector<int> RadialDispersal::get_sorted_positions(const std::vector<double>& numbers)
{
// Create a vector of indices (0 to N-1)
std::vector<int> indices(numbers.size());
std::iota(indices.begin(), indices.end(), 0);
// Sort the indices based on the corresponding values in the vector
std::sort(indices.begin(), indices.end(), [&numbers](int a, int b) {return numbers[a] < numbers[b];});
return indices;
}