diff --git a/src/RScore/.github/workflows/check.yml b/src/RScore/.github/workflows/check.yml new file mode 100644 index 00000000..2e865878 --- /dev/null +++ b/src/RScore/.github/workflows/check.yml @@ -0,0 +1,26 @@ +name: check +on: push + +jobs: + check: + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + include: + - os: windows-latest + path_to_exe: ./build/Debug/RScore.exe + - os: ubuntu-latest + path_to_exe: ./build/RScore + - os: macos-latest + path_to_exe: ./build/RScore + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + + - name: build + run: | + mkdir build && cd build + cmake ../ && cmake --build . + + - name: run + run: ${{ matrix.path_to_exe }} diff --git a/src/RScore/.gitignore b/src/RScore/.gitignore new file mode 100644 index 00000000..9bd72d21 --- /dev/null +++ b/src/RScore/.gitignore @@ -0,0 +1,87 @@ +.gitignore +#README.md + +# CodeLite +/.build-debug/ +/Debug/ +/Release/ +/RNG_test/ +.codelite/ +compile_commands.json +Makefile +.build-release/ +build-Release/ +*.project +*.workspace +*.mk +*.tags + +# Hidden source +/RangeShiftR/src/.* + +# Windows files +/RangeShiftR/src/*.dll + +# History files +.Rhistory +.Rapp.history + +# RStudio files +.Rproj.user/ +/RangeShiftR/.Rproj.user/ +#/RangeShiftR/RangeShiftR.Rproj +/RangeShiftR/Read-and-delete-me +/RangeShiftR/.Rhistory +#/RangeShiftR/.Rbuildignore + +# Session Data files +.RData +tags + +# User-specific files +.Ruserdata + +# Example code in package build process +*-Ex.R + +# Output files from R CMD build +*.tar.gz +/RangeShiftR/src/*.o +/RangeShiftR/src/RangeShiftR.so + + +# Windows files +/RangeShiftR/src/*.dll + +# Output files from R CMD check +/*.Rcheck/ + +# Output from Rcpp compile.attributes() +#/RangeShiftR/R/RcppExports.R +#/RangeShiftR/src/RcppExports.cpp + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf +#/RangeShiftR/man/ + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +/*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md + +# compilation files +*.o + +# Visual Studio files +.vs/ +out/ diff --git a/src/RScore/CMakeLists.txt b/src/RScore/CMakeLists.txt new file mode 100644 index 00000000..c14a3e3a --- /dev/null +++ b/src/RScore/CMakeLists.txt @@ -0,0 +1,30 @@ +# Config file for compilation with CMake + +if (NOT batchmode) # that is, RScore as a standalone + cmake_minimum_required(VERSION 3.10) + # set the project name and version + project(RScore VERSION 2.1.0) + # specify the C++ standard + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED True) + add_executable(RScore Main.cpp Species.cpp Cell.cpp Community.cpp FractalGenerator.cpp Genome.cpp Individual.cpp Landscape.cpp Model.cpp Parameters.cpp Patch.cpp Population.cpp RandomCheck.cpp RSrandom.cpp SubCommunity.cpp Utils.cpp) +else() # that is, RScore compiled as library within RangeShifter_batch + add_library(RScore Species.cpp Cell.cpp Community.cpp FractalGenerator.cpp Genome.cpp Individual.cpp Landscape.cpp Model.cpp Parameters.cpp Patch.cpp Population.cpp RandomCheck.cpp RSrandom.cpp SubCommunity.cpp Utils.cpp) +endif() + +# pass config definitions to compiler +target_compile_definitions(RScore PRIVATE RSWIN64) + +# enable LINUX_CLUSTER macro on Linux + macOS +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + add_compile_definitions("LINUX_CLUSTER") +endif() + +# Debug Mode by default, unless "release" is passed +if(NOT DEFINED release) + add_compile_definitions(RSDEBUG) +endif() + +if(NOT batchmode) + target_include_directories(RScore PUBLIC "${PROJECT_BINARY_DIR}") +endif() \ No newline at end of file diff --git a/src/RScore/CONTRIBUTING.md b/src/RScore/CONTRIBUTING.md new file mode 100644 index 00000000..8055ccd8 --- /dev/null +++ b/src/RScore/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# The RangeShifter platform - An eco-evolutionary modelling framework + +## How to contribute + +Thank you for your interest in contributing to the RangeShifter platform. +In this document we will give you guidance on how to contribute to the RangeShifter project regarding issues, bug fixing and adding new features. + +## Repo structure + +![Rangeshifter repo structure](RS_repos.png) + +RangeShifter is distributed with three user interfaces, each living in their own repo: + +- the RangeShifter GUI (clickable Windows interface)* +- RangeShifter Batch Mode (command line interface) +- the RangeShiftR package (R interface) + +All three share the same source code for the core simulation (i.e., the actual model), which lives in this repo (RScore). Each of the interfaces keeps a copy of this core code in a subfolder called RScore, kept in sync with the RScore repo via a git subtree (see Git subtree usage section). + +⚠️ If you wish to propose a change to one of the interfaces, please do so in the corresponding repo: [RangeShifter batch mode](https://github.com/RangeShifter/RangeShifter_batch_dev), [RangeShiftR package](https://github.com/RangeShifter/RangeShiftR-package-dev). + +*The RangeShifter GUI is currently being rewritten, and is not open source yet. + +## Roles + +#### Maintainers + +- [@JetteReeg](https://github.com/JetteReeg): RScore repo and lead in R package +- [@TheoPannetier](https://github.com/TheoPannetier): RScore repo and lead in batch mode + +Maintainers are responsible for coordinating development efforts and ensuring that RangeShifter keeps building continuously. + +#### Developers + +Regular contributors and members of the [RangeShifter development team](https://github.com/orgs/RangeShifter/people), including maintainers. + +#### Contributors + +Anyone who whishes to make changes to RangeShifter's code, including regular developers. + +## Branching policy + +![](branches.png) + +*Check out the [Git cheatsheet](https://github.com/RangeShifter/RScore/blob/development-guidelines/git_cheatsheet.md) for a reminder on the main git commands* + +This policy applies to RScore and all three RangeShifter interfaces. +RangeShifter uses the following branching structure: + +- `main` is the default branch, where stable releases live. Because it contains the version of RangeShifter that users normally interact with, it must be stable and build at all times. +Only maintainers should make significant changes to `main`, normally by merging `develop` into `main` to make newly developed features available to users, and marking a release while doing so. +- `develop` is the development branch containing new, in-development features. It is the reference branch for all developers. Contributors may make small changes directly to `develop` but should ensure that new changes do not break the build. If one happens to break `develop`, it should be their top priority to fix it as this will disrupt the work of all other contributors. +Larger changes should instead be developed on feature branches. +- Larger changes should be first developed on feature (e.g. `cmake`, `mutualism`, etc.) or contributor (e.g., `theo`) branches. Contributors are welcome to experiment and break such branches at any time, as this will not impact users or other contributors. + +When progress is deemed satisfactory, changes can be brought to `develop`. Please open a pull request on GitHub, and assign at least one maintainer as a reviewer. As a pre-requisite, RangeShifter must build on the branch before merging. Please enter a descriptive title and use the description field to describe what you have changed. + +In the meantime, we encourage contributors to work in small and frequent commits, and to merge `develop` into their branch often to update their branch with newest changes. + +### Contributing to RangeShifter core code + +Any changes regarding the RangeShifter core code should be done in this repository and can afterwards be synced with all interfaces using the git subtree feature (see [Git subtree](https://github.com/RangeShifter/RScore/tree/development-guidelines#usage-git-subtrees) section in the README). + +#### Bugs + +To report a bug, please [open an issue](https://github.com/RangeShifter/RangeShiftR-package-dev/issues/new), using the Bug Report template. +Please do check if a related issue has already open on one of the other interfaces ([here](https://github.com/RangeShifter/RangeShifter_batch/issues) for the batch interface or [here](https://github.com/RangeShifter/RangeShiftR-package-dev) for the R package interface). +To propose a bug fix (thank you!!), please create and work on your own branch or fork, from either `main` or `develop` (preferred), and open a pull request when your fix is ready to be merged into the original branch. + +Maintainers will review the pull request, possibly request changes, and eventually integrate the bug fix into RScore, and update the subtrees to bring the fix to all interfaces. + +#### New features + +Do you have an idea of a new feature in the RangeShifter platform that should be integrated and is of use for other RangeShifter users? +Please get in touch with the RangeShifter development team (rangeshiftr@uni-potsdam.de) to discuss a collaboration. + +⚠️ We advise to contact the developer team as early as possible if you plan on implementing a new feature. This could prevent simultaneous development of the same feature and coordinate potential joint development. + +Alternatively, proceed as with the bug fix above: create your own branch or fork _from `develop`_ and work from there, and submit a pull request when your new features are ready to join the core code. +We recommend that you update your branch regularly to new changes on `develop` (using `git merge develop`) to reduce the risk of merge conflicts or your version getting out-of-touch in the late stages of development. +We also recommend that you work in small commits, as this makes the code easier to debug, and makes it easier for maintainers to understand your contributions when reviewing a pull request. diff --git a/src/RScore/Cell.cpp b/src/RScore/Cell.cpp new file mode 100644 index 00000000..1c0f9378 --- /dev/null +++ b/src/RScore/Cell.cpp @@ -0,0 +1,237 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +//--------------------------------------------------------------------------- + +#include "Cell.h" + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +// Cell functions + +Cell::Cell(int xx,int yy,intptr patch,int hab) +{ +x = xx; y = yy; +pPatch = patch; +envVal = 1.0; // default - no effect of any gradient +envDev = eps = 0.0; +habIxx.push_back(hab); +#if RSDEBUG +//DebugGUI(("Cell::Cell(): this=" + Int2Str((int)this) +// + " x=" + Int2Str(x) + " y=" + Int2Str(y) +// + " habIndex=" + Int2Str(habIndex) +//).c_str()); +#endif +visits = 0; +smsData = 0; +} + +Cell::Cell(int xx,int yy,intptr patch,float hab) +{ +x = xx; y = yy; +pPatch = patch; +envVal = 1.0; // default - no effect of any gradient +envDev = eps = 0.0; +habitats.push_back(hab); +visits = 0; +smsData = 0; +} + +Cell::~Cell() { +#if RSDEBUG +//DEBUGLOG << "Cell::~Cell(): this = " << this << " smsData = " << smsData << endl; +#endif +habIxx.clear(); +habitats.clear(); +if (smsData != 0) { + if (smsData->effcosts != 0) delete smsData->effcosts; + delete smsData; +} +#if RSDEBUG +//DEBUGLOG << "Cell::~Cell(): deleted" << endl; +#endif +} + +void Cell::setHabIndex(short hx) { +#if RSDEBUG +//DebugGUI(("Cell::setHabIndex(): this=" + Int2Str((int)this) +// + " x=" + Int2Str(x) + " y=" + Int2Str(y) +// + " habIx=" + Int2Str(habIx) +//).c_str()); +#endif +if (hx < 0) habIxx.push_back(0); +else habIxx.push_back(hx); +} + +void Cell::changeHabIndex(short ix,short hx) { +if (ix >= 0 && ix < (short)habIxx.size() && hx >= 0) habIxx[ix] = hx; +else habIxx[ix] = 0; +} + +int Cell::getHabIndex(int ix) { +if (ix < 0 || ix >= (int)habIxx.size()) + // nodata cell OR should not occur, but treat as such + return -1; +else return habIxx[ix]; +} +int Cell::nHabitats(void) { +int nh = (int)habIxx.size(); +if ((int)habitats.size() > nh) nh = (int)habitats.size(); +return nh; +} + +void Cell::setHabitat(float q) { +if (q >= 0.0 && q <= 100.0) habitats.push_back(q); +else habitats.push_back(0.0); +} + +float Cell::getHabitat(int ix) { +if (ix < 0 || ix >= (int)habitats.size()) + // nodata cell OR should not occur, but treat as such + return -1.0; +else return habitats[ix]; +} + +void Cell::setPatch(intptr p) { +pPatch = p; +} +intptr Cell::getPatch(void) +{ +#if RSDEBUG +//DebugGUI(("Cell::getPatch(): this=" + Int2Str((int)this) +// + " x=" + Int2Str(x) + " y=" + Int2Str(y) +// + " habIxx[0]=" + Int2Str(habIxx[0]) + " pPatch=" + Int2Str(pPatch) +//).c_str()); +#endif +return pPatch; +} + +locn Cell::getLocn(void) { locn q; q.x = x; q.y = y; return q; } + +void Cell::setEnvDev(float d) { envDev = d; } + +float Cell::getEnvDev(void) { return envDev; } + +void Cell::setEnvVal(float e) { +if (e >= 0.0) envVal = e; +} + +float Cell::getEnvVal(void) { return envVal; } + +void Cell::updateEps(float ac,float randpart) { +eps = eps*ac + randpart; +} + +float Cell::getEps(void) { return eps; } + +// Functions to handle costs for SMS + +int Cell::getCost(void) { +int c; +if (smsData == 0) c = 0; // costs not yet set up +else c = smsData->cost; +return c; +} + +void Cell::setCost(int c) { +if (smsData == 0) { + smsData = new smscosts; + smsData->effcosts = 0; +} +smsData->cost = c; +} + +// Reset the cost and the effective cost of the cell +void Cell::resetCost(void) { +if (smsData != 0) { resetEffCosts(); delete smsData; } +smsData = 0; +} + +array3x3f Cell::getEffCosts(void) { +array3x3f a; +if (smsData == 0 || smsData->effcosts == 0) { // effective costs have not been calculated + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + a.cell[i][j] = -1.0; + } + } +} +else + a = *smsData->effcosts; +return a; +} + +void Cell::setEffCosts(array3x3f a) { +if (smsData->effcosts == 0) smsData->effcosts = new array3x3f; +*smsData->effcosts = a; +} + +// Reset the effective cost, but not the cost, of the cell +void Cell::resetEffCosts(void) { +if (smsData != 0) { + if (smsData->effcosts != 0) { + delete smsData->effcosts; + smsData->effcosts = 0; + } +} +} + +void Cell::resetVisits(void) { visits = 0; } +void Cell::incrVisits(void) { visits++; } +unsigned long int Cell::getVisits(void) { return visits; } + +//--------------------------------------------------------------------------- + +// Initial species distribution cell functions + +DistCell::DistCell(int xx,int yy) { +x = xx; y = yy; initialise = false; +} + +DistCell::~DistCell() { + +} + +void DistCell::setCell(bool init) { +initialise = init; +} + +bool DistCell::toInitialise(locn loc) { +if (loc.x == x && loc.y == y) return initialise; +else return false; +} + +bool DistCell::selected(void) { return initialise; } + +locn DistCell::getLocn(void) { +locn loc; loc.x = x; loc.y = y; return loc; +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + + + + diff --git a/src/RScore/Cell.h b/src/RScore/Cell.h new file mode 100644 index 00000000..5382a1ef --- /dev/null +++ b/src/RScore/Cell.h @@ -0,0 +1,174 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Cell + +Implements the following classes: + +Cell - Landscape cell + +DistCell - Initial species distribution cell + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 14 January 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef CellH +#define CellH + +#include +using namespace std; + +#include "Parameters.h" + +//--------------------------------------------------------------------------- + +struct array3x3f { float cell[3][3]; }; // neighbourhood cell array (SMS) +struct smscosts { int cost; array3x3f *effcosts; }; // cell costs for SMS + +// Landscape cell + +class Cell{ +public: + Cell( // Constructor for habitat codes + int, // x co-ordinate + int, // y co-ordinate + intptr, // pointer (cast as integer) to the Patch to which Cell belongs + int // habitat index number + ); + Cell( // Constructor for habitat % cover or habitat quality + int, // x co-ordinate + int, // y co-ordinate + intptr, // pointer (cast as integer) to the Patch to which Cell belongs + float // habitat proportion or cell quality score + ); + ~Cell(); + void setHabIndex( + short // habitat index number + ); + void changeHabIndex( + short, // landscape change number + short // habitat index number + ); + int getHabIndex( + int // landscape change number + ); + int nHabitats(void); + void setHabitat( + float // habitat proportions or cell quality score + ); + float getHabitat( // Get habitat proportion / quality score + int // habitat index number / landscape change number + ); + void setPatch( + intptr // pointer (cast as integer) to the Patch to which Cell belongs + ); + intptr getPatch(void); + locn getLocn(void); + void setEnvDev( + float // local environmental deviation + ); + float getEnvDev(void); + void setEnvVal( + float // environmental value + ); + float getEnvVal(void); + void updateEps( // Update local environmental stochasticity (epsilon) + float, // autocorrelation coefficient + float // random adjustment + ); + float getEps(void); + void setCost( + int // cost value for SMS + ); + int getCost(void); + void resetCost(void); + array3x3f getEffCosts(void); + void setEffCosts( + array3x3f // 3 x 3 array of effective costs for neighbouring cells + ); + void resetEffCosts(void); // Reset the effective cost, but not the cost, of the cell + void resetVisits(void); + void incrVisits(void); + unsigned long int getVisits(void); + +private: + int x,y; // cell co-ordinates + intptr pPatch; // pointer (cast as integer) to the Patch to which cell belongs + // NOTE: THE FOLLOWING ENVIRONMENTAL VARIABLES COULD BE COMBINED IN A STRUCTURE + // AND ACCESSED BY A POINTER ... + float envVal; // environmental value, representing one of: + // gradient in K, r or extinction probability + float envDev; // local environmental deviation (static, in range -1.0 to +1.0) + float eps; // local environmental stochasticity (epsilon) (dynamic, from N(0,std)) + unsigned long int visits; // no. of times square is visited by dispersers + smscosts *smsData; + + vector habIxx; // habitat indices (rasterType=0) + // NB initially, habitat codes are loaded, then converted to index nos. + // once landscape is fully loaded + vector habitats; // habitat proportions (rasterType=1) or quality (rasterType=2) +}; + +//--------------------------------------------------------------------------- + +// Initial species distribution cell + +class DistCell{ +public: + DistCell( + int, // x co-ordinate + int // y co-ordinate + ); + ~DistCell(); + void setCell( + bool // TRUE if cell is to be initialised, FALSE if not + ); + bool toInitialise( + locn // structure holding co-ordinates of cell + ); + bool selected(void); + locn getLocn(void); + +private: + int x,y; // cell co-ordinates + bool initialise; // cell is to be initialised + +}; + +#if RSDEBUG +extern void DebugGUI(string); +#endif + +//--------------------------------------------------------------------------- + +#endif diff --git a/src/RScore/Community.cpp b/src/RScore/Community.cpp new file mode 100644 index 00000000..286dc6a3 --- /dev/null +++ b/src/RScore/Community.cpp @@ -0,0 +1,1562 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Community.h" + +//--------------------------------------------------------------------------- + + +ofstream outrange; +ofstream outoccup, outsuit; +ofstream outtraitsrows; + +//--------------------------------------------------------------------------- + +Community::Community(Landscape* pLand) { + pLandscape = pLand; + indIx = 0; +} + +Community::~Community(void) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + delete subComms[i]; + } + subComms.clear(); +} + +SubCommunity* Community::addSubComm(Patch* pPch, int num) { + int nsubcomms = (int)subComms.size(); + subComms.push_back(new SubCommunity(pPch, num)); + return subComms[nsubcomms]; +} + +void Community::initialise(Species* pSpecies, int year) +{ + + int nsubcomms, npatches, ndistcells, spratio, patchnum, rr = 0; + locn distloc; + patchData pch; + patchLimits limits; + intptr ppatch, subcomm; + std::vector subcomms; + std::vector selected; + SubCommunity* pSubComm; + Patch* pPatch; + Cell* pCell; + landParams ppLand = pLandscape->getLandParams(); + initParams init = paramsInit->getInit(); + + nsubcomms = (int)subComms.size(); + + spratio = ppLand.spResol / ppLand.resol; + +#if RSDEBUG + DEBUGLOG << endl << "Community::initialise(): this=" << this + << " seedType=" << init.seedType << " freeType=" << init.freeType + << " minSeedX=" << init.minSeedX << " minSeedY=" << init.minSeedY + << " maxSeedX=" << init.maxSeedX << " maxSeedY=" << init.maxSeedY + << " indsFile=" << init.indsFile + << " nsubcomms=" << nsubcomms << " spratio=" << spratio + << endl; +#endif + + switch (init.seedType) { + + case 0: // free initialisation + + switch (init.freeType) { + + case 0: // random + // determine no. of patches / cells within the specified initialisation limits + // and record their corresponding sub-communities in a list + // parallel list records which have been selected + npatches = pLandscape->patchCount(); + limits.xMin = init.minSeedX; limits.xMax = init.maxSeedX; + limits.yMin = init.minSeedY; limits.yMax = init.maxSeedY; + for (int i = 0; i < npatches; i++) { + pch = pLandscape->getPatchData(i); + if (pch.pPatch->withinLimits(limits)) { + if (ppLand.patchModel) { + if (pch.pPatch->getPatchNum() != 0) { + subcomms.push_back(pch.pPatch->getSubComm()); + selected.push_back(false); + } + } + else { // cell-based model - is cell(patch) suitable + if (pch.pPatch->getK() > 0.0) + { + subcomms.push_back(pch.pPatch->getSubComm()); + selected.push_back(false); + } + } + } + } + // select specified no. of patches/cells at random + npatches = (int)subcomms.size(); + if (init.nSeedPatches > npatches / 2) { // use backwards selection method + for (int i = 0; i < npatches; i++) selected[i] = true; + for (int i = 0; i < (npatches - init.nSeedPatches); i++) { + do { + rr = pRandom->IRandom(0, npatches - 1); + } while (!selected[rr]); + selected[rr] = false; + } + } + else { // use forwards selection method + for (int i = 0; i < init.nSeedPatches; i++) { + do { + rr = pRandom->IRandom(0, npatches - 1); + } while (selected[rr]); + selected[rr] = true; + } + } + // selected sub-communities for initialisation + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->setInitial(false); + } + for (int i = 0; i < npatches; i++) { + if (selected[i]) { + pSubComm = (SubCommunity*)subcomms[i]; + pSubComm->setInitial(true); + } + } + break; + + case 1: // all suitable patches/cells + npatches = pLandscape->patchCount(); + limits.xMin = init.minSeedX; limits.xMax = init.maxSeedX; + limits.yMin = init.minSeedY; limits.yMax = init.maxSeedY; + for (int i = 0; i < npatches; i++) { + pch = pLandscape->getPatchData(i); + if (pch.pPatch->withinLimits(limits)) { + patchnum = pch.pPatch->getPatchNum(); + if (patchnum != 0) { + if (pch.pPatch->getK() > 0.0) + { // patch is suitable + subcomm = pch.pPatch->getSubComm(); + if (subcomm == 0) { + // create a sub-community in the patch + pSubComm = addSubComm(pch.pPatch, patchnum); + } + else { + pSubComm = (SubCommunity*)subcomm; + } + pSubComm->setInitial(true); + } + } + } + } + + break; + + case 2: // manually selected patches/cells + break; + + } // end of switch (init.freeType) + nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->initialise(pLandscape, pSpecies); + } + break; + + case 1: // from species distribution + if (ppLand.spDist) + { + // deselect all existing sub-communities + for (int i = 0; i < nsubcomms; i++) { + subComms[i]->setInitial(false); + } + // initialise from loaded species distribution + switch (init.spDistType) { + case 0: // all presence cells + pLandscape->setDistribution(pSpecies, 0); // activate all patches + break; + case 1: // some randomly selected presence cells + pLandscape->setDistribution(pSpecies, init.nSpDistPatches); // activate random patches + break; + case 2: // manually selected presence cells + // cells have already been identified - no further action here + break; + } + + // THE FOLLOWING WILL HAVE TO BE CHANGED FOR MULTIPLE SPECIES... + ndistcells = pLandscape->distCellCount(0); + for (int i = 0; i < ndistcells; i++) { + distloc = pLandscape->getSelectedDistnCell(0, i); + if (distloc.x >= 0) { // distribution cell is selected + // process each landscape cell within the distribution cell + for (int x = 0; x < spratio; x++) { + for (int y = 0; y < spratio; y++) { + pCell = pLandscape->findCell(distloc.x * spratio + x, distloc.y * spratio + y); + if (pCell != 0) { // not a no-data cell + ppatch = pCell->getPatch(); + if (ppatch != 0) { + pPatch = (Patch*)ppatch; + if (pPatch->getSeqNum() != 0) { // not the matrix patch + subcomm = pPatch->getSubComm(); + if (subcomm != 0) { + pSubComm = (SubCommunity*)subcomm; + pSubComm->setInitial(true); + } + } + } + } + } + } + } + } + + nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->initialise(pLandscape, pSpecies); + } + } + else { + // WHAT HAPPENS IF INITIAL DISTRIBUTION IS NOT LOADED ??? .... + // should not occur - take no action - no initialisation will occur + } + break; + + case 2: // initial individuals in specified patches/cells + if (year < 0) { + // initialise matrix sub-community only + subComms[0]->initialise(pLandscape, pSpecies); + indIx = 0; // reset index for initial individuals + } + else { // add any initial individuals for the current year + initInd iind; iind.year = 0; + int ninds = paramsInit->numInitInds(); + while (indIx < ninds && iind.year <= year) { + iind = paramsInit->getInitInd(indIx); + while (iind.year == year) { + if (ppLand.patchModel) { + if (pLandscape->existsPatch(iind.patchID)) { + pPatch = pLandscape->findPatch(iind.patchID); + if (pPatch->getK() > 0.0) + { // patch is suitable + subcomm = pPatch->getSubComm(); + if (subcomm == 0) { + // create a sub-community in the patch + pSubComm = addSubComm(pPatch, iind.patchID); + } + else { + pSubComm = (SubCommunity*)subcomm; + } + pSubComm->initialInd(pLandscape, pSpecies, pPatch, pPatch->getRandomCell(), indIx); + } + } + } + else { // cell-based model + pCell = pLandscape->findCell(iind.x, iind.y); + if (pCell != 0) { + intptr ppatch = pCell->getPatch(); + if (ppatch != 0) { + pPatch = (Patch*)ppatch; + if (pPatch->getK() > 0.0) + { // patch is suitable + subcomm = pPatch->getSubComm(); + if (subcomm == 0) { + // create a sub-community in the patch + pSubComm = addSubComm(pPatch, iind.patchID); + } + else { + pSubComm = (SubCommunity*)subcomm; + } + pSubComm->initialInd(pLandscape, pSpecies, pPatch, pCell, indIx); + } + } + } + } + indIx++; + if (indIx < ninds) { + iind = paramsInit->getInitInd(indIx); + } + else { + iind.year = 99999999; + } + } + } + } + break; + + case 3: // from file + // this condition cannot occur here, as init.seedType will have been changed to 0 or 1 + // when the initialisation file was read + break; + + } // end of switch (init.seedType) + +#if RSDEBUG + DEBUGLOG << "Community::initialise(): this=" << this + << " nsubcomms=" << nsubcomms + << endl; +#endif + +} + +// Add manually selected patches/cells to the selected set for initialisation +void Community::addManuallySelected(void) { + int npatches; + intptr subcomm, patch; + locn initloc; + Cell* pCell; + Patch* pPatch; + SubCommunity* pSubComm; + + landParams ppLand = pLandscape->getLandParams(); + + npatches = pLandscape->initCellCount(); // no. of patches/cells specified +#if RSDEBUG + DEBUGLOG << "Community::addManuallySelected(): this = " << this + << " npatches = " << npatches << endl; +#endif + // identify sub-communities to be initialised + if (ppLand.patchModel) { + for (int i = 0; i < npatches; i++) { + initloc = pLandscape->getInitCell(i); // patch number held in x-coord of list + pPatch = pLandscape->findPatch(initloc.x); + if (pPatch != 0) { + subcomm = pPatch->getSubComm(); + if (subcomm != 0) { + pSubComm = (SubCommunity*)subcomm; + pSubComm->setInitial(true); + } + } + } + } + else { // cell-based model + for (int i = 0; i < npatches; i++) { + initloc = pLandscape->getInitCell(i); + if (initloc.x >= 0 && initloc.x < ppLand.dimX + && initloc.y >= 0 && initloc.y < ppLand.dimY) { + pCell = pLandscape->findCell(initloc.x, initloc.y); + if (pCell != 0) { // not no-data cell + patch = pCell->getPatch(); +#if RSDEBUG + DEBUGLOG << "Community::initialise(): i = " << i + << " x = " << initloc.x << " y = " << initloc.y + << " pCell = " << pCell << " patch = " << patch + << endl; +#endif + if (patch != 0) { + pPatch = (Patch*)patch; + subcomm = pPatch->getSubComm(); +#if RSDEBUG + DEBUGLOG << "Community::initialise(): i = " << i + << " pPatch = " << pPatch << " subcomm = " << subcomm + << endl; +#endif + if (subcomm != 0) { + pSubComm = (SubCommunity*)subcomm; + pSubComm->setInitial(true); +#if RSDEBUG + DEBUGLOG << "Community::initialise(): i = " << i + << " pSubComm = " << pSubComm + << endl; +#endif + } + } + } + } + } + } +} + +void Community::resetPopns(void) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->resetPopns(); + } + // reset the individual ids to start from zero + Individual::indCounter = 0; +} + +void Community::localExtinction(int option) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + if (subComms[i]->getNum() > 0) { // except in matrix + subComms[i]->localExtinction(option); + } + } +} + +void Community::patchChanges(void) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + if (subComms[i]->getNum() > 0) { // except in matrix + subComms[i]->patchChange(); + } + } +} + +void Community::reproduction(int yr) +{ + float eps = 0.0; // epsilon for environmental stochasticity + landParams land = pLandscape->getLandParams(); + envStochParams env = paramsStoch->getStoch(); + int nsubcomms = (int)subComms.size(); +#if RSDEBUG + DEBUGLOG << "Community::reproduction(): this=" << this + << " nsubcomms=" << nsubcomms << endl; +#endif + + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + if (env.stoch) { + if (!env.local) { // global stochasticty + eps = pLandscape->getGlobalStoch(yr); + } + } + subComms[i]->reproduction(land.resol, eps, land.rasterType, land.patchModel); + } +#if RSDEBUG + DEBUGLOG << "Community::reproduction(): finished" << endl; +#endif +} + +void Community::emigration(void) +{ + int nsubcomms = (int)subComms.size(); +#if RSDEBUG + DEBUGLOG << "Community::emigration(): this=" << this + << " nsubcomms=" << nsubcomms << endl; +#endif + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->emigration(); + } +#if RSDEBUG + DEBUGLOG << "Community::emigration(): finished" << endl; +#endif +} + +#if RS_RCPP // included also SEASONAL +void Community::dispersal(short landIx, short nextseason) +#else +void Community::dispersal(short landIx) +#endif // SEASONAL || RS_RCPP +{ +#if RSDEBUG + int t0, t1, t2; + t0 = time(0); +#endif + + simParams sim = paramsSim->getSim(); + + int nsubcomms = (int)subComms.size(); + // initiate dispersal - all emigrants leave their natal community and join matrix community + SubCommunity* matrix = subComms[0]; // matrix community is always the first + for (int i = 0; i < nsubcomms; i++) { // all populations + subComms[i]->initiateDispersal(matrix); + } +#if RSDEBUG + t1 = time(0); + DEBUGLOG << "Community::dispersal(): this=" << this + << " nsubcomms=" << nsubcomms << " initiation time=" << t1 - t0 << endl; +#endif + + // dispersal is undertaken by all individuals now in the matrix patch + // (even if not physically in the matrix) + int ndispersers = 0; + do { + for (int i = 0; i < nsubcomms; i++) { // all populations + subComms[i]->resetPossSettlers(); + } +#if RS_RCPP // included also SEASONAL + ndispersers = matrix->transfer(pLandscape, landIx, nextseason); +#else + ndispersers = matrix->transfer(pLandscape, landIx); +#endif // SEASONAL || RS_RCPP + matrix->completeDispersal(pLandscape, sim.outConnect); + } while (ndispersers > 0); + +#if RSDEBUG + DEBUGLOG << "Community::dispersal(): matrix=" << matrix << endl; + t2 = time(0); + DEBUGLOG << "Community::dispersal(): transfer time=" << t2 - t1 << endl; +#endif + +} + +void Community::survival(short part, short option0, short option1) +{ + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all communities (including in matrix) + subComms[i]->survival(part, option0, option1); + } +} + +void Community::ageIncrement(void) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all communities (including in matrix) + subComms[i]->ageIncrement(); + } +} + +// Calculate total no. of individuals of all species +int Community::totalInds(void) { + popStats p; + int total = 0; + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all communities (including in matrix) + p = subComms[i]->getPopStats(); + total += p.nInds; + } + return total; +} + +// Find the population of a given species in a given patch +Population* Community::findPop(Species* pSp, Patch* pPch) { + Population* pPop = 0; + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all communities (including in matrix) + pPop = subComms[i]->findPop(pSp, pPch); + if (pPop != 0) break; + } + return pPop; +} + +//--------------------------------------------------------------------------- +void Community::createOccupancy(int nrows, int reps) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { + subComms[i]->createOccupancy(nrows); + } + // Initialise array for occupancy of suitable cells/patches + occSuit = new float* [nrows]; + for (int i = 0; i < nrows; i++) + { + occSuit[i] = new float[reps]; + for (int ii = 0; ii < reps; ii++) occSuit[i][ii] = 0.0; + } +} + +void Community::updateOccupancy(int row, int rep) +{ +#if RSDEBUG + DEBUGLOG << "Community::updateOccupancy(): row=" << row << endl; +#endif + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { + subComms[i]->updateOccupancy(row); + } + + commStats s = getStats(); + occSuit[row][rep] = (float)s.occupied / (float)s.suitable; + +} + +void Community::deleteOccupancy(int nrows) { + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { + subComms[i]->deleteOccupancy(); + } + + for (int i = 0; i < nrows; i++) + delete[] occSuit[i]; + delete[] occSuit; + +} + +//--------------------------------------------------------------------------- +// Count no. of sub-communities (suitable patches) and those occupied (non-zero populations) +// Determine range margins +commStats Community::getStats(void) +{ + commStats s; + landParams ppLand = pLandscape->getLandParams(); + s.ninds = s.nnonjuvs = s.suitable = s.occupied = 0; + s.minX = ppLand.maxX; s.minY = ppLand.maxY; s.maxX = s.maxY = 0; + float localK; + popStats patchPop; + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + patchPop = subComms[i]->getPopStats(); + s.ninds += patchPop.nInds; + s.nnonjuvs += patchPop.nNonJuvs; + if (patchPop.pPatch != 0) { // not the matrix patch + if (patchPop.pPatch->getPatchNum() != 0) { // not matrix patch + localK = patchPop.pPatch->getK(); + if (localK > 0.0) s.suitable++; + if (patchPop.nInds > 0 && patchPop.breeding) { + s.occupied++; + patchLimits pchlim = patchPop.pPatch->getLimits(); + if (pchlim.xMin < s.minX) s.minX = pchlim.xMin; + if (pchlim.xMax > s.maxX) s.maxX = pchlim.xMax; + if (pchlim.yMin < s.minY) s.minY = pchlim.yMin; + if (pchlim.yMax > s.maxY) s.maxY = pchlim.yMax; + } + } + } + } + return s; +} + +//--------------------------------------------------------------------------- + +// Functions to control production of output files + +// Open population file and write header record +bool Community::outPopHeaders(Species* pSpecies, int option) { + return subComms[0]->outPopHeaders(pLandscape, pSpecies, option); +} + +// Write records to population file +void Community::outPop(int rep, int yr, int gen) +{ + // generate output for each sub-community (patch) in the community + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->outPop(pLandscape, rep, yr, gen); + } + +} + + +// Write records to individuals file +void Community::outInds(int rep, int yr, int gen, int landNr) { + + if (landNr >= 0) { // open the file + subComms[0]->outInds(pLandscape, rep, yr, gen, landNr); + return; + } + if (landNr == -999) { // close the file + subComms[0]->outInds(pLandscape, rep, yr, gen, -999); + return; + } + // generate output for each sub-community (patch) in the community + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->outInds(pLandscape, rep, yr, gen, landNr); + } +} + +// Write records to genetics file +void Community::outGenetics(int rep, int yr, int gen, int landNr) { + //landParams ppLand = pLandscape->getLandParams(); + if (landNr >= 0) { // open the file + subComms[0]->outGenetics(rep, yr, gen, landNr); + return; + } + if (landNr == -999) { // close the file + subComms[0]->outGenetics(rep, yr, gen, landNr); + return; + } + // generate output for each sub-community (patch) in the community + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + subComms[i]->outGenetics(rep, yr, gen, landNr); + } +} + +// Open range file and write header record +bool Community::outRangeHeaders(Species* pSpecies, int landNr) +{ + + if (landNr == -999) { // close the file + if (outrange.is_open()) outrange.close(); + outrange.clear(); + return true; + } + + string name; + landParams ppLand = pLandscape->getLandParams(); + envStochParams env = paramsStoch->getStoch(); + simParams sim = paramsSim->getSim(); + + // NEED TO REPLACE CONDITIONAL COLUMNS BASED ON ATTRIBUTES OF ONE SPECIES TO COVER + // ATTRIBUTES OF *ALL* SPECIES AS DETECTED AT MODEL LEVEL + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + +#if RSDEBUG + DEBUGLOG << "Community::outRangeHeaders(): simulation=" << sim.simulation + << " sim.batchMode=" << sim.batchMode + << " landNr=" << landNr << endl; +#endif + + if (sim.batchMode) { + name = paramsSim->getDir(2) +#if RS_RCPP + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + + Int2Str(landNr) +#else + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + + Int2Str(landNr) +#endif + + "_Range.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + Int2Str(sim.simulation) + "_Range.txt"; + } + outrange.open(name.c_str()); + outrange << "Rep\tYear\tRepSeason"; + if (env.stoch && !env.local) outrange << "\tEpsilon"; + + outrange << "\tNInds"; + if (dem.stageStruct) { + for (int i = 1; i < sstruct.nStages; i++) outrange << "\tNInd_stage" << i; + outrange << "\tNJuvs"; + } + if (ppLand.patchModel) outrange << "\tNOccupPatches"; + else outrange << "\tNOccupCells"; + outrange << "\tOccup/Suit\tmin_X\tmax_X\tmin_Y\tmax_Y"; + + if (emig.indVar) { + if (emig.sexDep) { + if (emig.densDep) { + outrange << "\tF_meanD0\tF_stdD0\tM_meanD0\tM_stdD0"; + outrange << "\tF_meanAlpha\tF_stdAlpha\tM_meanAlpha\tM_stdAlpha"; + outrange << "\tF_meanBeta\tF_stdBeta\tM_meanBeta\tM_stdBeta"; + } + else { + outrange << "\tF_meanEP\tF_stdEP\tM_meanEP\tM_stdEP"; + } + } + else { + if (emig.densDep) { + outrange << "\tmeanD0\tstdD0\tmeanAlpha\tstdAlpha"; + outrange << "\tmeanBeta\tstdBeta"; + } + else { + outrange << "\tmeanEP\tstdEP"; + } + } + } + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 1) { + outrange << "\tmeanDP\tstdDP\tmeanGB\tstdGB"; + outrange << "\tmeanAlphaDB\tstdAlphaDB\tmeanBetaDB\tstdBetaDB"; + } + if (trfr.moveType == 2) { + outrange << "\tmeanStepLength\tstdStepLength\tmeanRho\tstdRho"; + } + } + else { + if (trfr.sexDep) { + outrange << "\tF_mean_distI\tF_std_distI\tM_mean_distI\tM_std_distI"; + if (trfr.twinKern) + outrange << "\tF_mean_distII\tF_std_distII\tM_mean_distII\tM_std_distII" + << "\tF_meanPfirstKernel\tF_stdPfirstKernel" + << "\tM_meanPfirstKernel\tM_stdPfirstKernel"; + } + else { + outrange << "\tmean_distI\tstd_distI"; + if (trfr.twinKern) + outrange << "\tmean_distII\tstd_distII\tmeanPfirstKernel\tstdPfirstKernel"; + } + } + } + if (sett.indVar) { + if (sett.sexDep) { + outrange << "\tF_meanS0\tF_stdS0\tM_meanS0\tM_stdS0"; + outrange << "\tF_meanAlphaS\tF_stdAlphaS\tM_meanAlphaS\tM_stdAlphaS"; + outrange << "\tF_meanBetaS\tF_stdBetaS\tM_meanBetaS\tM_stdBetaS"; + + } + else { + outrange << "\tmeanS0\tstdS0"; + outrange << "\tmeanAlphaS\tstdAlphaS"; + outrange << "\tmeanBetaS\tstdBetaS"; + } + } + outrange << endl; + +#if RSDEBUG + DEBUGLOG << "Community::outRangeHeaders(): finished" << endl; +#endif + + return outrange.is_open(); +} + +// Write record to range file +void Community::outRange(Species* pSpecies, int rep, int yr, int gen) +{ +#if RSDEBUG + DEBUGLOG << "Community::outRange(): rep=" << rep + << " yr=" << yr << " gen=" << gen << endl; +#endif + + landParams ppLand = pLandscape->getLandParams(); + envStochParams env = paramsStoch->getStoch(); + + // NEED TO REPLACE CONDITIONAL COLUMNS BASED ON ATTRIBUTES OF ONE SPECIES TO COVER + // ATTRIBUTES OF *ALL* SPECIES AS DETECTED AT MODEL LEVEL + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + + outrange << rep << "\t" << yr << "\t" << gen; + if (env.stoch && !env.local) // write global environmental stochasticity + outrange << "\t" << pLandscape->getGlobalStoch(yr); + + commStats s = getStats(); + + if (dem.stageStruct) { + outrange << "\t" << s.nnonjuvs; + int stagepop; + int nsubcomms = (int)subComms.size(); + // all non-juvenile stages + for (int stg = 1; stg < sstruct.nStages; stg++) { + stagepop = 0; + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + stagepop += subComms[i]->stagePop(stg); + } + outrange << "\t" << stagepop; + } + // juveniles born in current reproductive season + stagepop = 0; + for (int i = 0; i < nsubcomms; i++) { // all sub-communities + stagepop += subComms[i]->stagePop(0); + } + outrange << "\t" << stagepop; + } + else { // non-structured species + outrange << "\t" << s.ninds; + } + + float occsuit = 0.0; + if (s.suitable > 0) occsuit = (float)s.occupied / (float)s.suitable; + outrange << "\t" << s.occupied << "\t" << occsuit; + // RANGE MINIMA AND MAXIMA NEED TO BECOME A PROPERTY OF THE SPECIES + if (s.ninds > 0) { + landOrigin origin = pLandscape->getOrigin(); + outrange << "\t" << (float)s.minX * (float)ppLand.resol + origin.minEast + << "\t" << (float)(s.maxX + 1) * (float)ppLand.resol + origin.minEast + << "\t" << (float)s.minY * (float)ppLand.resol + origin.minNorth + << "\t" << (float)(s.maxY + 1) * (float)ppLand.resol + origin.minNorth; + } + else + outrange << "\t0\t0\t0\t0"; + + if (emig.indVar || trfr.indVar || sett.indVar) { // output trait means + traitsums ts; + traitsums scts; // sub-community traits + traitCanvas tcanv; + int ngenes, popsize; + + tcanv.pcanvas[0] = NULL; + + for (int i = 0; i < NSEXES; i++) { + ts.ninds[i] = 0; + ts.sumD0[i] = ts.ssqD0[i] = 0.0; + ts.sumAlpha[i] = ts.ssqAlpha[i] = 0.0; ts.sumBeta[i] = ts.ssqBeta[i] = 0.0; + ts.sumDist1[i] = ts.ssqDist1[i] = 0.0; ts.sumDist2[i] = ts.ssqDist2[i] = 0.0; + ts.sumProp1[i] = ts.ssqProp1[i] = 0.0; + ts.sumDP[i] = ts.ssqDP[i] = 0.0; + ts.sumGB[i] = ts.ssqGB[i] = 0.0; + ts.sumAlphaDB[i] = ts.ssqAlphaDB[i] = 0.0; + ts.sumBetaDB[i] = ts.ssqBetaDB[i] = 0.0; + ts.sumStepL[i] = ts.ssqStepL[i] = 0.0; ts.sumRho[i] = ts.ssqRho[i] = 0.0; + ts.sumS0[i] = ts.ssqS0[i] = 0.0; + ts.sumAlphaS[i] = ts.ssqAlphaS[i] = 0.0; ts.sumBetaS[i] = ts.ssqBetaS[i] = 0.0; + } + + int nsubcomms = (int)subComms.size(); + for (int i = 0; i < nsubcomms; i++) { // all sub-communities (incl. matrix) + scts = subComms[i]->outTraits(tcanv, pLandscape, rep, yr, gen, true); + for (int j = 0; j < NSEXES; j++) { + ts.ninds[j] += scts.ninds[j]; + ts.sumD0[j] += scts.sumD0[j]; ts.ssqD0[j] += scts.ssqD0[j]; + ts.sumAlpha[j] += scts.sumAlpha[j]; ts.ssqAlpha[j] += scts.ssqAlpha[j]; + ts.sumBeta[j] += scts.sumBeta[j]; ts.ssqBeta[j] += scts.ssqBeta[j]; + ts.sumDist1[j] += scts.sumDist1[j]; ts.ssqDist1[j] += scts.ssqDist1[j]; + ts.sumDist2[j] += scts.sumDist2[j]; ts.ssqDist2[j] += scts.ssqDist2[j]; + ts.sumProp1[j] += scts.sumProp1[j]; ts.ssqProp1[j] += scts.ssqProp1[j]; + ts.sumDP[j] += scts.sumDP[j]; ts.ssqDP[j] += scts.ssqDP[j]; + ts.sumGB[j] += scts.sumGB[j]; ts.ssqGB[j] += scts.ssqGB[j]; + ts.sumAlphaDB[j] += scts.sumAlphaDB[j]; ts.ssqAlphaDB[j] += scts.ssqAlphaDB[j]; + ts.sumBetaDB[j] += scts.sumBetaDB[j]; ts.ssqBetaDB[j] += scts.ssqBetaDB[j]; + ts.sumStepL[j] += scts.sumStepL[j]; ts.ssqStepL[j] += scts.ssqStepL[j]; + ts.sumRho[j] += scts.sumRho[j]; ts.ssqRho[j] += scts.ssqRho[j]; + ts.sumS0[j] += scts.sumS0[j]; ts.ssqS0[j] += scts.ssqS0[j]; + ts.sumAlphaS[j] += scts.sumAlphaS[j]; ts.ssqAlphaS[j] += scts.ssqAlphaS[j]; + ts.sumBetaS[j] += scts.sumBetaS[j]; ts.ssqBetaS[j] += scts.ssqBetaS[j]; + } + } + + if (emig.indVar) { + if (emig.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ngenes = 1; + } + else { // sexual reproduction + ngenes = 1; + } + } + double mnD0[2], mnAlpha[2], mnBeta[2], sdD0[2], sdAlpha[2], sdBeta[2]; + for (int g = 0; g < ngenes; g++) { + mnD0[g] = mnAlpha[g] = mnBeta[g] = sdD0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = ts.ninds[g]; + else popsize = ts.ninds[0] + ts.ninds[1]; + if (popsize > 0) { + mnD0[g] = ts.sumD0[g] / (double)popsize; + mnAlpha[g] = ts.sumAlpha[g] / (double)popsize; + mnBeta[g] = ts.sumBeta[g] / (double)popsize; + if (popsize > 1) { + sdD0[g] = ts.ssqD0[g] / (double)popsize - mnD0[g] * mnD0[g]; + if (sdD0[g] > 0.0) sdD0[g] = sqrt(sdD0[g]); else sdD0[g] = 0.0; + sdAlpha[g] = ts.ssqAlpha[g] / (double)popsize - mnAlpha[g] * mnAlpha[g]; + if (sdAlpha[g] > 0.0) sdAlpha[g] = sqrt(sdAlpha[g]); else sdAlpha[g] = 0.0; + sdBeta[g] = ts.ssqBeta[g] / (double)popsize - mnBeta[g] * mnBeta[g]; + if (sdBeta[g] > 0.0) sdBeta[g] = sqrt(sdBeta[g]); else sdBeta[g] = 0.0; + } + else { + sdD0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + } + } + } + if (emig.sexDep) { + outrange << "\t" << mnD0[0] << "\t" << sdD0[0]; + outrange << "\t" << mnD0[1] << "\t" << sdD0[1]; + if (emig.densDep) { + outrange << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outrange << "\t" << mnAlpha[1] << "\t" << sdAlpha[1]; + outrange << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + outrange << "\t" << mnBeta[1] << "\t" << sdBeta[1]; + } + } + else { // sex-independent + outrange << "\t" << mnD0[0] << "\t" << sdD0[0]; + if (emig.densDep) { + outrange << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outrange << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + } + } + } + + if (trfr.indVar) { + if (trfr.moveModel) { + // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + ngenes = 1; + } + else { + if (trfr.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + ngenes = 1; + } + } + double mnDist1[2], mnDist2[2], mnProp1[2], mnStepL[2], mnRho[2]; + double sdDist1[2], sdDist2[2], sdProp1[2], sdStepL[2], sdRho[2]; + double mnDP[2], mnGB[2], mnAlphaDB[2], mnBetaDB[2]; + double sdDP[2], sdGB[2], sdAlphaDB[2], sdBetaDB[2]; + for (int g = 0; g < ngenes; g++) { + mnDist1[g] = mnDist2[g] = mnProp1[g] = mnStepL[g] = mnRho[g] = 0.0; + sdDist1[g] = sdDist2[g] = sdProp1[g] = sdStepL[g] = sdRho[g] = 0.0; + mnDP[g] = mnGB[g] = mnAlphaDB[g] = mnBetaDB[g] = 0.0; + sdDP[g] = sdGB[g] = sdAlphaDB[g] = sdBetaDB[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = ts.ninds[g]; + else popsize = ts.ninds[0] + ts.ninds[1]; + if (popsize > 0) { + mnDist1[g] = ts.sumDist1[g] / (double)popsize; + mnDist2[g] = ts.sumDist2[g] / (double)popsize; + mnProp1[g] = ts.sumProp1[g] / (double)popsize; + mnStepL[g] = ts.sumStepL[g] / (double)popsize; + mnRho[g] = ts.sumRho[g] / (double)popsize; + mnDP[g] = ts.sumDP[g] / (double)popsize; + mnGB[g] = ts.sumGB[g] / (double)popsize; + mnAlphaDB[g] = ts.sumAlphaDB[g] / (double)popsize; + mnBetaDB[g] = ts.sumBetaDB[g] / (double)popsize; + if (popsize > 1) { + sdDist1[g] = ts.ssqDist1[g] / (double)popsize - mnDist1[g] * mnDist1[g]; + if (sdDist1[g] > 0.0) sdDist1[g] = sqrt(sdDist1[g]); else sdDist1[g] = 0.0; + sdDist2[g] = ts.ssqDist2[g] / (double)popsize - mnDist2[g] * mnDist2[g]; + if (sdDist2[g] > 0.0) sdDist2[g] = sqrt(sdDist2[g]); else sdDist2[g] = 0.0; + sdProp1[g] = ts.ssqProp1[g] / (double)popsize - mnProp1[g] * mnProp1[g]; + if (sdProp1[g] > 0.0) sdProp1[g] = sqrt(sdProp1[g]); else sdProp1[g] = 0.0; + sdStepL[g] = ts.ssqStepL[g] / (double)popsize - mnStepL[g] * mnStepL[g]; + if (sdStepL[g] > 0.0) sdStepL[g] = sqrt(sdStepL[g]); else sdStepL[g] = 0.0; + sdRho[g] = ts.ssqRho[g] / (double)popsize - mnRho[g] * mnRho[g]; + if (sdRho[g] > 0.0) sdRho[g] = sqrt(sdRho[g]); else sdRho[g] = 0.0; + sdDP[g] = ts.ssqDP[g] / (double)popsize - mnDP[g] * mnDP[g]; + if (sdDP[g] > 0.0) sdDP[g] = sqrt(sdDP[g]); else sdDP[g] = 0.0; + sdGB[g] = ts.ssqGB[g] / (double)popsize - mnGB[g] * mnGB[g]; + if (sdGB[g] > 0.0) sdGB[g] = sqrt(sdGB[g]); else sdGB[g] = 0.0; + sdAlphaDB[g] = ts.ssqAlphaDB[g] / (double)popsize - mnAlphaDB[g] * mnAlphaDB[g]; + if (sdAlphaDB[g] > 0.0) sdAlphaDB[g] = sqrt(sdAlphaDB[g]); else sdAlphaDB[g] = 0.0; + sdBetaDB[g] = ts.ssqBetaDB[g] / (double)popsize - mnBetaDB[g] * mnBetaDB[g]; + if (sdBetaDB[g] > 0.0) sdBetaDB[g] = sqrt(sdBetaDB[g]); else sdBetaDB[g] = 0.0; + } + } + } + if (trfr.moveModel) { + if (trfr.moveType == 1) { + outrange << "\t" << mnDP[0] << "\t" << sdDP[0]; + outrange << "\t" << mnGB[0] << "\t" << sdGB[0]; + outrange << "\t" << mnAlphaDB[0] << "\t" << sdAlphaDB[0]; + outrange << "\t" << mnBetaDB[0] << "\t" << sdBetaDB[0]; + } + if (trfr.moveType == 2) { + outrange << "\t" << mnStepL[0] << "\t" << sdStepL[0]; + outrange << "\t" << mnRho[0] << "\t" << sdRho[0]; + } + } + else { + if (trfr.sexDep) { + outrange << "\t" << mnDist1[0] << "\t" << sdDist1[0]; + outrange << "\t" << mnDist1[1] << "\t" << sdDist1[1]; + if (trfr.twinKern) + { + outrange << "\t" << mnDist2[0] << "\t" << sdDist2[0]; + outrange << "\t" << mnDist2[1] << "\t" << sdDist2[1]; + outrange << "\t" << mnProp1[0] << "\t" << sdProp1[0]; + outrange << "\t" << mnProp1[1] << "\t" << sdProp1[1]; + } + } + else { // sex-independent + outrange << "\t" << mnDist1[0] << "\t" << sdDist1[0]; + if (trfr.twinKern) + { + outrange << "\t" << mnDist2[0] << "\t" << sdDist2[0]; + outrange << "\t" << mnProp1[0] << "\t" << sdProp1[0]; + } + } + } + } + + if (sett.indVar) { + if (sett.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ngenes = 1; + } + else { // sexual reproduction + ngenes = 1; + } + } + // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + double mnS0[2], mnAlpha[2], mnBeta[2], sdS0[2], sdAlpha[2], sdBeta[2]; + for (int g = 0; g < ngenes; g++) { + mnS0[g] = mnAlpha[g] = mnBeta[g] = sdS0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = ts.ninds[g]; + else popsize = ts.ninds[0] + ts.ninds[1]; + if (popsize > 0) { + mnS0[g] = ts.sumS0[g] / (double)popsize; + mnAlpha[g] = ts.sumAlphaS[g] / (double)popsize; + mnBeta[g] = ts.sumBetaS[g] / (double)popsize; + if (popsize > 1) { + sdS0[g] = ts.ssqS0[g] / (double)popsize - mnS0[g] * mnS0[g]; + if (sdS0[g] > 0.0) sdS0[g] = sqrt(sdS0[g]); else sdS0[g] = 0.0; + sdAlpha[g] = ts.ssqAlphaS[g] / (double)popsize - mnAlpha[g] * mnAlpha[g]; + if (sdAlpha[g] > 0.0) sdAlpha[g] = sqrt(sdAlpha[g]); else sdAlpha[g] = 0.0; + sdBeta[g] = ts.ssqBetaS[g] / (double)popsize - mnBeta[g] * mnBeta[g]; + if (sdBeta[g] > 0.0) sdBeta[g] = sqrt(sdBeta[g]); else sdBeta[g] = 0.0; + } + else { + sdS0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + } + } + } + if (sett.sexDep) { + outrange << "\t" << mnS0[0] << "\t" << sdS0[0]; + outrange << "\t" << mnS0[1] << "\t" << sdS0[1]; + outrange << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outrange << "\t" << mnAlpha[1] << "\t" << sdAlpha[1]; + outrange << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + outrange << "\t" << mnBeta[1] << "\t" << sdBeta[1]; + } + else { + outrange << "\t" << mnS0[0] << "\t" << sdS0[0]; + outrange << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outrange << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + } + } + + } + + outrange << endl; +} + +// Open occupancy file, write header record and set up occupancy array +bool Community::outOccupancyHeaders(int option) +{ + if (option == -999) { // close the files + if (outsuit.is_open()) outsuit.close(); + if (outoccup.is_open()) outoccup.close(); + outsuit.clear(); outoccup.clear(); + return true; + } + + string name, nameI; + simParams sim = paramsSim->getSim(); + landParams ppLand = pLandscape->getLandParams(); + int outrows = (sim.years / sim.outIntOcc) + 1; + + name = paramsSim->getDir(2); + if (sim.batchMode) { + name += "Batch" + Int2Str(sim.batchNum) + "_"; + name += "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(ppLand.landNum); + } + else + name += "Sim" + Int2Str(sim.simulation); + name += "_Occupancy_Stats.txt"; + outsuit.open(name.c_str()); + outsuit << "Year\tMean_OccupSuit\tStd_error" << endl; + + name = paramsSim->getDir(2); + if (sim.batchMode) { + name += "Batch" + Int2Str(sim.batchNum) + "_"; + name += "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(ppLand.landNum); + } + else + name += "Sim" + Int2Str(sim.simulation); + name += "_Occupancy.txt"; + outoccup.open(name.c_str()); + if (ppLand.patchModel) { + outoccup << "PatchID"; + } + else { + outoccup << "X\tY"; + } + for (int i = 0; i < outrows; i++) + outoccup << "\t" << "Year_" << i * sim.outIntOcc; + outoccup << endl; + + // Initialise cells/patches occupancy array + createOccupancy(outrows, sim.reps); + + return outsuit.is_open() && outoccup.is_open(); +} + +void Community::outOccupancy(void) { + landParams ppLand = pLandscape->getLandParams(); + simParams sim = paramsSim->getSim(); + locn loc; + + int nsubcomms = (int)subComms.size(); + for (int i = 1; i < nsubcomms; i++) { // all except matrix sub-community + if (ppLand.patchModel) { + outoccup << subComms[i]->getPatch()->getPatchNum(); + } + else { + loc = subComms[i]->getLocn(); + outoccup << loc.x << "\t" << loc.y; + } + for (int row = 0; row <= (sim.years / sim.outIntOcc); row++) + { + outoccup << "\t" << (double)subComms[i]->getOccupancy(row) / (double)sim.reps; + } + outoccup << endl; + } +} + +void Community::outOccSuit(bool view) { + double sum, ss, mean, sd, se; + simParams sim = paramsSim->getSim(); + for (int i = 0; i < (sim.years / sim.outIntOcc) + 1; i++) { + sum = ss = 0.0; + for (int rep = 0; rep < sim.reps; rep++) { + sum += occSuit[i][rep]; + ss += occSuit[i][rep] * occSuit[i][rep]; + } + mean = sum / (double)sim.reps; + sd = (ss - (sum * sum / (double)sim.reps)) / (double)(sim.reps - 1); + if (sd > 0.0) sd = sqrt(sd); + else sd = 0.0; + se = sd / sqrt((double)(sim.reps)); + outsuit << i * sim.outIntOcc << "\t" << mean << "\t" << se << endl; + if (view) viewOccSuit(i * sim.outIntOcc, mean, se); + } + +} + +// Open traits file and write header record +bool Community::outTraitsHeaders(Species* pSpecies, int landNr) { + return subComms[0]->outTraitsHeaders(pLandscape, pSpecies, landNr); +} + +// Write records to traits file +/* NOTE: for summary traits by rows, which is permissible for a cell-based landscape +only, this function relies on the fact that subcommunities are created in the same +sequence as patches, which is in asecending order of x nested within descending +order of y +*/ +void Community::outTraits(traitCanvas tcanv, Species* pSpecies, + int rep, int yr, int gen) +{ + simParams sim = paramsSim->getSim(); + simView v = paramsSim->getViews(); + landParams land = pLandscape->getLandParams(); + traitsums* ts = 0; + traitsums sctraits; + if (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0) { + // create array of traits means, etc., one for each row + ts = new traitsums[land.dimY]; + for (int y = 0; y < land.dimY; y++) { + for (int i = 0; i < NSEXES; i++) { + ts[y].ninds[i] = 0; + ts[y].sumD0[i] = ts[y].ssqD0[i] = 0.0; + ts[y].sumAlpha[i] = ts[y].ssqAlpha[i] = 0.0; + ts[y].sumBeta[i] = ts[y].ssqBeta[i] = 0.0; + ts[y].sumDist1[i] = ts[y].ssqDist1[i] = 0.0; + ts[y].sumDist2[i] = ts[y].ssqDist2[i] = 0.0; + ts[y].sumProp1[i] = ts[y].ssqProp1[i] = 0.0; + ts[y].sumStepL[i] = ts[y].ssqStepL[i] = 0.0; + ts[y].sumRho[i] = ts[y].ssqRho[i] = 0.0; + ts[y].sumS0[i] = ts[y].ssqS0[i] = 0.0; + ts[y].sumAlphaS[i] = ts[y].ssqAlphaS[i] = 0.0; + ts[y].sumBetaS[i] = ts[y].ssqBetaS[i] = 0.0; + } + } + } + if (v.viewTraits + || ((sim.outTraitsCells && yr >= sim.outStartTraitCell && yr % sim.outIntTraitCell == 0) || + (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0))) + { + // generate output for each sub-community (patch) in the community + int nsubcomms = (int)subComms.size(); + for (int i = 1; i < nsubcomms; i++) { // // all except matrix sub-community + sctraits = subComms[i]->outTraits(tcanv, pLandscape, rep, yr, gen, false); + locn loc = subComms[i]->getLocn(); + int y = loc.y; + if (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0) + { + for (int s = 0; s < NSEXES; s++) { + ts[y].ninds[s] += sctraits.ninds[s]; + ts[y].sumD0[s] += sctraits.sumD0[s]; ts[y].ssqD0[s] += sctraits.ssqD0[s]; + ts[y].sumAlpha[s] += sctraits.sumAlpha[s]; ts[y].ssqAlpha[s] += sctraits.ssqAlpha[s]; + ts[y].sumBeta[s] += sctraits.sumBeta[s]; ts[y].ssqBeta[s] += sctraits.ssqBeta[s]; + ts[y].sumDist1[s] += sctraits.sumDist1[s]; ts[y].ssqDist1[s] += sctraits.ssqDist1[s]; + ts[y].sumDist2[s] += sctraits.sumDist2[s]; ts[y].ssqDist2[s] += sctraits.ssqDist2[s]; + ts[y].sumProp1[s] += sctraits.sumProp1[s]; ts[y].ssqProp1[s] += sctraits.ssqProp1[s]; + ts[y].sumStepL[s] += sctraits.sumStepL[s]; ts[y].ssqStepL[s] += sctraits.ssqStepL[s]; + ts[y].sumRho[s] += sctraits.sumRho[s]; ts[y].ssqRho[s] += sctraits.ssqRho[s]; + ts[y].sumS0[s] += sctraits.sumS0[s]; ts[y].ssqS0[s] += sctraits.ssqS0[s]; + ts[y].sumAlphaS[s] += sctraits.sumAlphaS[s]; ts[y].ssqAlphaS[s] += sctraits.ssqAlphaS[s]; + ts[y].sumBetaS[s] += sctraits.sumBetaS[s]; ts[y].ssqBetaS[s] += sctraits.ssqBetaS[s]; + } + } + } + if (nsubcomms > 0 && sim.outTraitsRows + && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0) { + for (int y = 0; y < land.dimY; y++) { + if ((ts[y].ninds[0] + ts[y].ninds[1]) > 0) { + writeTraitsRows(pSpecies, rep, yr, gen, y, ts[y]); + } + } + } + } + if (ts != 0) { delete[] ts; ts = 0; } +} + +// Write records to trait rows file +void Community::writeTraitsRows(Species* pSpecies, int rep, int yr, int gen, int y, + traitsums ts) +{ + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + double mn, sd; + + // calculate population size in case one phase is sex-dependent and the other is not + // (in which case numbers of individuals are recorded by sex) + int popsize = ts.ninds[0] + ts.ninds[1]; + outtraitsrows << rep << "\t" << yr << "\t" << gen + << "\t" << y; + if ((emig.indVar && emig.sexDep) || (trfr.indVar && trfr.sexDep)) + outtraitsrows << "\t" << ts.ninds[0] << "\t" << ts.ninds[1]; + else + outtraitsrows << "\t" << popsize; + + if (emig.indVar) { + if (emig.sexDep) { + if (ts.ninds[0] > 0) mn = ts.sumD0[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqD0[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumD0[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqD0[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (emig.densDep) { + if (ts.ninds[0] > 0) mn = ts.sumAlpha[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqAlpha[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumAlpha[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqAlpha[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[0] > 0) mn = ts.sumBeta[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqBeta[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumBeta[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqBeta[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + } + } + else { // no sex dependence in emigration + if (popsize > 0) mn = ts.sumD0[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqD0[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (emig.densDep) { + if (popsize > 0) mn = ts.sumAlpha[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqAlpha[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (popsize > 0) mn = ts.sumBeta[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqBeta[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + } + } + } + + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 2) { // CRW + // NB - CURRENTLY CANNOT BE SEX-DEPENDENT... + if (popsize > 0) mn = ts.sumStepL[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqStepL[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (popsize > 0) mn = ts.sumRho[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqRho[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + } + } + else { // dispersal kernel + if (trfr.sexDep) { + if (ts.ninds[0] > 0) mn = ts.sumDist1[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqDist1[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumDist1[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqDist1[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (trfr.twinKern) + { + if (ts.ninds[0] > 0) mn = ts.sumDist2[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqDist2[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumDist2[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqDist2[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[0] > 0) mn = ts.sumProp1[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqProp1[0] / (double)ts.ninds[0] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (ts.ninds[1] > 0) mn = ts.sumProp1[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqProp1[1] / (double)ts.ninds[1] - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + } + } + else { // sex-independent + if (popsize > 0) mn = ts.sumDist1[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqDist1[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (trfr.twinKern) + { + if (popsize > 0) mn = ts.sumDist2[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqDist2[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (popsize > 0) mn = ts.sumProp1[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqProp1[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + } + } + } + } + + if (sett.indVar) { + if (popsize > 0) mn = ts.sumS0[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqS0[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (popsize > 0) mn = ts.sumAlphaS[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqAlphaS[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + if (popsize > 0) mn = ts.sumBetaS[0] / (double)popsize; else mn = 0.0; + if (popsize > 1) sd = ts.ssqBetaS[0] / (double)popsize - mn * mn; else sd = 0.0; + if (sd > 0.0) sd = sqrt(sd); else sd = 0.0; + outtraitsrows << "\t" << mn << "\t" << sd; + // } + } + + outtraitsrows << endl; +} + +// Open trait rows file and write header record +bool Community::outTraitsRowsHeaders(Species* pSpecies, int landNr) { + + if (landNr == -999) { // close file + if (outtraitsrows.is_open()) outtraitsrows.close(); + outtraitsrows.clear(); + return true; + } + + string name; + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + simParams sim = paramsSim->getSim(); + + string DirOut = paramsSim->getDir(2); + if (sim.batchMode) { + name = DirOut + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(landNr) + "_TraitsXrow.txt"; + } + else { + name = DirOut + "Sim" + Int2Str(sim.simulation) + "_TraitsXrow.txt"; + } + outtraitsrows.open(name.c_str()); + + outtraitsrows << "Rep\tYear\tRepSeason\ty"; + if ((emig.indVar && emig.sexDep) || (trfr.indVar && trfr.sexDep)) + outtraitsrows << "\tN_females\tN_males"; + else + outtraitsrows << "\tN"; + + if (emig.indVar) { + if (emig.sexDep) { + if (emig.densDep) { + outtraitsrows << "\tF_meanD0\tF_stdD0\tM_meanD0\tM_stdD0"; + outtraitsrows << "\tF_meanAlpha\tF_stdAlpha\tM_meanAlpha\tM_stdAlpha"; + outtraitsrows << "\tF_meanBeta\tF_stdBeta\tM_meanBeta\tM_stdBeta"; + } + else { + outtraitsrows << "\tF_meanEP\tF_stdEP\tM_meanEP\tM_stdEP"; + } + } + else { + if (emig.densDep) { + outtraitsrows << "\tmeanD0\tstdD0\tmeanAlpha\tstdAlpha"; + outtraitsrows << "\tmeanBeta\tstdBeta"; + } + else { + outtraitsrows << "\tmeanEP\tstdEP"; + } + } + } + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 2) { + outtraitsrows << "\tmeanStepLength\tstdStepLength\tmeanRho\tstdRho"; + } + } + else { // dispersal kernel + if (trfr.sexDep) { + outtraitsrows << "\tF_mean_distI\tF_std_distI\tM_mean_distI\tM_std_distI"; + if (trfr.twinKern) + outtraitsrows << "\tF_mean_distII\tF_std_distII\tM_mean_distII\tM_std_distII" + << "\tF_meanPfirstKernel\tF_stdPfirstKernel" + << "\tM_meanPfirstKernel\tM_stdPfirstKernel"; + } + else { + outtraitsrows << "\tmean_distI\tstd_distI"; + if (trfr.twinKern) + outtraitsrows << "\tmean_distII\tstd_distII\tmeanPfirstKernel\tstdPfirstKernel"; + } + } + } + + if (sett.indVar) { + outtraitsrows << "\tmeanS0\tstdS0"; + outtraitsrows << "\tmeanAlphaS\tstdAlphaS"; + outtraitsrows << "\tmeanBetaS\tstdBetaS"; + } + outtraitsrows << endl; + + return outtraitsrows.is_open(); + +} + +#if RS_RCPP && !R_CMD +Rcpp::IntegerMatrix Community::addYearToPopList(int rep, int yr) { // TODO: define new simparams to control start and interval of output + + landParams ppLand = pLandscape->getLandParams(); + Rcpp::IntegerMatrix pop_map_year(ppLand.dimY, ppLand.dimX); + intptr patch = 0; + Patch* pPatch = 0; + intptr subcomm = 0; + SubCommunity* pSubComm = 0; + popStats pop; + pop.nInds = pop.nAdults = pop.nNonJuvs = 0; + + for (int y = 0; y < ppLand.dimY; y++) { + for (int x = 0; x < ppLand.dimX; x++) { + Cell* pCell = pLandscape->findCell(x, y); + if (pCell == 0) { // no-data cell + pop_map_year(ppLand.dimY - 1 - y, x) = NA_INTEGER; + } + else { + patch = pCell->getPatch(); + if (patch == 0) { // matrix cell + pop_map_year(ppLand.dimY - 1 - y, x) = 0; + } + else { + pPatch = (Patch*)patch; + subcomm = pPatch->getSubComm(); + if (subcomm == 0) { // check if sub-community exists + pop_map_year(ppLand.dimY - 1 - y, x) = 0; + } + else { + pSubComm = (SubCommunity*)subcomm; + pop = pSubComm->getPopStats(); + pop_map_year(ppLand.dimY - 1 - y, x) = pop.nInds; // use indices like this because matrix gets transposed upon casting it into a raster on R-level + } + } + } + } + } + return pop_map_year; +} +#endif + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- diff --git a/src/RScore/Community.h b/src/RScore/Community.h new file mode 100644 index 00000000..c55b3338 --- /dev/null +++ b/src/RScore/Community.h @@ -0,0 +1,223 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Community + +Implements the Community class + +There is ONLY ONE instance of a Community in an individual replicate simulation. +It holds a SubCommunity for each Patch in the Landscape (including the matrix), +and is thus the highest-level entity accessed for most processing concerned with +simulated populations. + +Optionally, the Community maintains a record of the occupancy of suitable cells +or patches during the course of simulation of multiple replicates. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 25 June 2021 by Anne-Kathleen Malchow + +------------------------------------------------------------------------------*/ + +#ifndef CommunityH +#define CommunityH + +#include +#include +using namespace std; + +#include "SubCommunity.h" +#include "Landscape.h" +#include "Patch.h" +#include "Cell.h" +#include "Species.h" + +//--------------------------------------------------------------------------- +struct commStats { +int ninds,nnonjuvs,suitable,occupied; +int minX,maxX,minY,maxY; +}; + +class Community { + +public: + Community(Landscape*); + ~Community(void); + SubCommunity* addSubComm(Patch*,int); + // functions to manage populations occurring in the community + void initialise( + Species*, // pointer to Species + int // year (relevent only for seedType == 2) + ); + void addManuallySelected(void); + void resetPopns(void); + void localExtinction(int); + void patchChanges(void); + void reproduction( + int // year + ); + void emigration(void); +#if RS_RCPP // included also SEASONAL + void dispersal( + short, // landscape change index + short // season / year + ); +#else + void dispersal( + short // landscape change index + ); +#endif // SEASONAL || RS_RCPP + + void survival( + short, // part: 0 = determine survival & development, + // 1 = apply survival changes to the population + short, // option0: 0 = stage 0 (juveniles) only ) + // 1 = all stages ) used by part 0 only + // 2 = stage 1 and above (all non-juvs) ) + short // option1: 0 - development only (when survival is annual) + // 1 - development and survival + ); + void ageIncrement(void); + int totalInds(void); + Population* findPop( // Find the population of a given species in a given patch + Species*, // pointer to Species + Patch* // pointer to Patch + ); + commStats getStats(void); + void createOccupancy( + int, // no. of rows = (no. of years / interval) + 1 + int // no. of replicates + ); + void updateOccupancy( + int, // row = (no. of years / interval) + int // replicate + ); + void deleteOccupancy( + int // no. of rows (as above) + ); + + bool outRangeHeaders( // Open range file and write header record + Species*, // pointer to Species + int // Landscape number (-999 to close the file) + ); + void outRange( // Write record to range file + Species*, // pointer to Species + int, // replicate + int, // year + int // generation + ); + bool outPopHeaders( // Open population file and write header record + Species*, // pointer to Species + int // option: -999 to close the file + ); + void outPop( // Write records to population file + int, // replicate + int, // year + int // generation + ); + + void outInds( // Write records to individuals file + int, // replicate + int, // year + int, // generation + int // Landscape number (>= 0 to open the file, -999 to close the file + // -1 to write data records) + ); + void outGenetics( // Write records to genetics file + int, // replicate + int, // year + int, // generation + int // Landscape number (>= 0 to open the file, -999 to close the file + // -1 to write data records) + ); + // Open occupancy file, write header record and set up occupancy array + bool outOccupancyHeaders( + int // option: -999 to close the file + ); + void outOccupancy(void); + void outOccSuit( + bool // TRUE if occupancy graph is to be viewed on screen + ); + void viewOccSuit( // Update the occupancy graph on the screen + // NULL for the batch version + int, // year + double, // mean occupancy + double // standard error of occupancy + ); + bool outTraitsHeaders( // Open traits file and write header record + Species*, // pointer to Species + int // Landscape number (-999 to close the file) + ); + bool outTraitsRowsHeaders( // Open trait rows file and write header record + Species*, // pointer to Species + int // Landscape number (-999 to close the file) + ); + void outTraits( // Write records to traits file + traitCanvas,// pointers to canvases for drawing variable traits + // see SubCommunity.h + // in the batch version, these are replaced by integers set to zero + Species*, // pointer to Species + int, // replicate + int, // year + int // generation + ); + void writeTraitsRows( // Write records to trait rows file + Species*, // pointer to Species + int, // replicate + int, // year + int, // generation + int, // row number (Y cell co-ordinate) + traitsums // structure holding sums of trait genes for dispersal (see Population.h) + ); + void draw( // Draw the Community on the landscape map and optionally save the map + // NULL for the batch version + int, // replicate + int, // year + int, // generation + int // Landscape number + ); +#if RS_RCPP && !R_CMD + Rcpp::IntegerMatrix addYearToPopList(int,int); +#endif + +private: + Landscape *pLandscape; + int indIx; // index used to apply initial individuals + float **occSuit; // occupancy of suitable cells / patches + std::vector subComms; + +}; + +extern paramSim *paramsSim; +extern paramInit *paramsInit; + + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/FractalGenerator.cpp b/src/RScore/FractalGenerator.cpp new file mode 100644 index 00000000..7a5ccc3a --- /dev/null +++ b/src/RScore/FractalGenerator.cpp @@ -0,0 +1,243 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +//--------------------------------------------------------------------------- + +#include "FractalGenerator.h" +//--------------------------------------------------------------------------- + +vector patches; + +//----- Landscape creation -------------------------------------------------- + +land::land(): x_coord(0), y_coord(0), value(0.0), avail(0) {} + +bool compare(const land& z, const land& zz) //compares only the values of the cells +{ +return z.value < zz.value; +} + +vector& fractal_landscape(int X,int Y,double Hurst,double prop, + double maxValue,double minValue) +{ +#if RSDEBUG +DEBUGLOG << "fractal_landscape(): X=" << X << " Y=" << Y + << " Hurst=" << Hurst << " prop=" << prop + << " maxValue=" << maxValue << " minValue=" << minValue + << endl; +#endif + +int ii, jj, x, y; +int ix, iy; +//int x0, y0, size, kx, kx2, ky, ky2; +int kx,kx2,ky,ky2; + +double range; //range to draw random numbers at each iteration +double nx, ny; +double i, j; +int Nx = X; +int Ny = Y; + +double ran[5]; // to store each time the 5 random numbers for the random displacement + +int Nno; // number of cells NON suitable as habitat + +// exponents used to obtain the landscape dimensions +double pow2x = log(((double)X-1.0))/log(2.0); +double pow2y = log(((double)Y-1.0))/log(2.0); + +double **arena = new double *[X]; +for(ii = 0; ii < X; ii++) { + arena[ii] = new double[Y]; +} + +patches.clear(); +// initialise all the landscape with zeroes +for (jj = 0; jj < X; jj++) { + for (ii = 0; ii < Y; ii++) { + arena[jj][ii]=0; + } +} + +// initialisation of the four corners +arena[0][0] = 1.0 + pRandom->Random() * (maxValue-1.0); +arena[0][Y-1] = 1.0 + pRandom->Random() * (maxValue-1.0); +arena[X-1][0] = 1.0 + pRandom->Random() * (maxValue-1.0); +arena[X-1][Y-1] = 1.0 + pRandom->Random() * (maxValue-1.0); + +/////////////MIDPOINT DISPLACEMENT ALGORITHM////////////////////////////////// +kx = (Nx-1) / 2; +kx2 = 2 * kx; +ky = (Ny-1) / 2; +ky2 = 2 * ky; + +for (ii = 0; ii < 5; ii++) //random displacement +{ + ran[ii] = 1.0 + pRandom->Random() * (maxValue-1.0); +} + +//The diamond step: +arena[kx][ky] = ((arena[0][0] + arena[0][ky2] + arena[kx2][0] + arena[kx2][ky2])/4) + ran[0]; + +//The square step: +//left +arena[0][ky] = ((arena[0][0] +arena[0][ky2] + arena[kx][ky]) / 3) + ran[1]; +//top +arena[kx][0] = ((arena[0][0] + arena[kx][ky] + arena[kx2][0]) / 3) + ran[2]; +//right +arena[kx2][ky] = ((arena[kx2][0] + arena[kx][ky] + arena[kx2][ky2]) / 3) + ran[3]; +//bottom +arena[kx][ky2] = ((arena[0][ky2] + arena[kx][ky] +arena[kx2][ky2]) / 3) + ran[4]; + +range = maxValue*pow(2,-Hurst); + +i = pow2x-1; +j = pow2y-1; + +while (i > 0) { + nx = pow(2,i)+1; + kx = (int)((nx-1) / 2); + kx2 = 2 * kx; + + ny = pow(2,j)+1; + ky = (int)((ny-1) / 2); + ky2 = 2 * ky; + + ix = 0; + while (ix <= (Nx-nx)) { + iy = 0; + while (iy <= (Ny-ny)) { + for (ii = 0; ii < 5; ii++) //random displacement + { + ran[ii] = (int)(pRandom->Random() * 2.0 * range - range); + } + //The diamond step: + + arena[ix+kx][iy+ky] = ((arena[ix][iy] + arena[ix][iy+ky2] + arena[ix+ky2][iy] + + arena[ix+kx2][iy+ky2])/ 4) + ran[0]; + if (arena[ix+kx][iy+ky] < 1) arena[ix+kx][iy+ky] = 1; + + //The square step: + //left + arena[ix][iy+ky] =((arena[ix][iy] +arena[ix][iy+ky2] + arena[ix+kx][iy+ky])/3) + + ran[1]; + if (arena[ix][iy+ky] < 1) arena[ix][iy+ky] = 1; + //top + arena[ix+kx][iy] =((arena[ix][iy] + arena[ix+kx][iy+ky] + arena[ix+kx2][iy])/3) + + ran[2]; + if (arena[ix+kx][iy] < 1) arena[ix+kx][iy] = 1; + //right + arena[ix+kx2][iy+ky] = ((arena[ix+kx2][iy] + arena[ix+kx][iy+ky] + + arena[ix+kx2][iy+ky2]) / 3) + ran[3]; + if (arena[ix+kx2][iy+ky] < 1) arena[ix+kx2][iy+ky] = 1; + //bottom + arena[ix+kx][iy+ky2] = ((arena[ix][iy+ky2] + arena[ix+kx][iy+ky] + + arena[ix+kx2][iy+ky2]) / 3) + ran[4]; + if (arena[ix+kx][iy+ky2] < 1) arena[ix+kx][iy+ky2] = 1; + + iy += ((int)ny-1); + } + ix += ((int)nx-1); + } + if (i==j) j--; + i--; + + range = range*pow(2,-Hurst); //reduce the random number range +} + +// Now all the cells will be sorted and the Nno cells with the lower carrying +// capacity will be set as matrix, i.e. with K = 0 + +land *patch; + +for (x = 0; x < X; x++) // put all the cells with their values in a vector +{ + for (y = 0; y < Y; y++) + { + patch = new land; + patch->x_coord = x; + patch->y_coord = y; + patch->value = (float)arena[x][y]; + patch->avail = 1; + + patches.push_back(*patch); + + delete patch; + } +} + + +sort(patches.begin(),patches.end(),compare); // sorts the vector + +Nno = (int)(prop*X*Y); +for (ii = 0; ii < Nno; ii++) +{ + patches[ii].value = 0.0; + patches[ii].avail = 0; +} + +double min = (double)patches[Nno].value; // variables for the rescaling +double max = (double)patches[X*Y-1].value; + +double diff = max - min; +double diffK = maxValue-minValue; +double new_value; + +vector::iterator iter = patches.begin(); +while (iter != patches.end()) +{ + if (iter->value > 0) // rescale to a range of K between Kmin and Kmax + { + new_value = maxValue - diffK * (max - (double)iter->value) / diff; + + iter->value = (float)new_value; + } + else iter->value = 0; + + iter++; +} + +if (arena != NULL) { +#if RSDEBUG +//DebugGUI(("fractal_landscape(): arena=" + Int2Str((int)arena) +// + " X=" + Int2Str(X) + " Y=" + Int2Str(Y) +// ).c_str()); +#endif + for(ii = 0; ii < X; ii++) { +#if RSDEBUG +//DebugGUI(("fractal_landscape(): ii=" + Int2Str(ii) +// + " arena[ii]=" + Int2Str((int)arena[ii]) +// ).c_str()); +#endif + delete[] arena[ii]; + } + delete[] arena; +} + +return patches; + +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + diff --git a/src/RScore/FractalGenerator.h b/src/RScore/FractalGenerator.h new file mode 100644 index 00000000..24acbc79 --- /dev/null +++ b/src/RScore/FractalGenerator.h @@ -0,0 +1,85 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 FractalGenerator + +Implements the midpoint displacement algorithm for generating a fractal Landscape, +following: + +Saupe, D. (1988). Algorithms for random fractals. In: The Science of Fractal Images +(eds. Pietgen, H.O. & Saupe, D.). Springer, New York, pp. 71113. + + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 15 July 2021 by Anne-Kathleen Malchow + +------------------------------------------------------------------------------*/ + +#ifndef FractalGeneratorH +#define FractalGeneratorH + +#include +#include +//using namespace std; + +#include "Parameters.h" + +class land +{ + public: + land(); + int x_coord; + int y_coord; + float value; + int avail; // if 0 the patch is not available as habitat, if 1 it is + private: +}; + +// IMPORTANT NOTE: X AND Y ARE TRANSPOSED, i.e. X IS THE VERTICAL CO-ORDINATE +// ========================================================================== + +vector& fractal_landscape( + int, // X dimension (Y of LandScape) + int, // Y dimension (X of LandScape) + double, // Hurst exponent + double, // proportion of NON-suitable habitat + double, // maximum quality value + double // minimum quality value +); +bool compare(const land&, const land&); + +extern RSrandom *pRandom; +#if RSDEBUG +extern void DebugGUI(string); +#endif + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Genome.cpp b/src/RScore/Genome.cpp new file mode 100644 index 00000000..59a68b1a --- /dev/null +++ b/src/RScore/Genome.cpp @@ -0,0 +1,419 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + +#include "Genome.h" +//--------------------------------------------------------------------------- + +ofstream outGenetic; + +//--------------------------------------------------------------------------- + +Chromosome::Chromosome(int nloc) +{ + if (nloc > 0) nloci = nloc; else nloci = 1; + pLoci = new locus[nloci]; + for (int i = 0; i < nloci; i++) { + pLoci[i].allele[0] = pLoci[i].allele[1] = 0; + } +} + +Chromosome::~Chromosome() { + if (pLoci != 0) { + delete[] pLoci; pLoci = NULL; + } +} + +short Chromosome::nLoci(void) { return nloci; } + +locus Chromosome::alleles(const int loc) { // return allele values at a specified locus + locus l; l.allele[0] = l.allele[1] = 0; + if (loc >= 0 && loc < nloci) { + l.allele[0] = pLoci[loc].allele[0]; l.allele[1] = pLoci[loc].allele[1]; + } + return l; +} + +double Chromosome::additive(const bool diploid) { + int sum = 0; + for (int i = 0; i < nloci; i++) { + sum += pLoci[i].allele[0]; + if (diploid) sum += pLoci[i].allele[1]; + } + return (double)sum / INTBASE; +} + +double Chromosome::meanvalue(const bool diploid) { + int sum = 0; + double mean; + for (int i = 0; i < nloci; i++) { + sum += pLoci[i].allele[0]; + if (diploid) sum += pLoci[i].allele[1]; + } + mean = (double)sum / (double)nloci; + if (diploid) mean /= 2.0; + mean /= INTBASE; + return mean; +} + +double Chromosome::additive(const short loc, const bool diploid) { + int sum = 0; + sum += pLoci[loc].allele[0]; + if (diploid) sum += pLoci[loc].allele[1]; + return (double)sum / INTBASE; +} + +// Set up chromosome at simulation initialisation +void Chromosome::initialise(const double mean, const double sd, + const bool diploid) { + double avalue; + double intbase = INTBASE; + + for (int i = 0; i < nloci; i++) { + avalue = pRandom->Normal(mean, sd); + if (avalue > 0.0) + pLoci[i].allele[0] = (int)(avalue * intbase + 0.5); + else + pLoci[i].allele[0] = (int)(avalue * intbase - 0.5); + if (diploid) { + avalue = pRandom->Normal(mean, sd); + if (avalue > 0.0) + pLoci[i].allele[1] = (int)(avalue * intbase + 0.5); + else + pLoci[i].allele[1] = (int)(avalue * intbase - 0.5); + } + } + +} + +// Set up specified locus at simulation initialisation +void Chromosome::initialise(const short locus, const short posn, const int aval) +{ + // note that initialising value is ADDED to current value to allow for pleiotropy + pLoci[locus].allele[posn] += aval; +} + +// Inherit from specified parent +void Chromosome::inherit(const Chromosome* parentChr, const short posn, const short nloc, + const double probmutn, const double probcross, const double mutnSD, const bool diploid) +{ + // NOTE: At present for diploid genome, presence of crossover is determined at each + // locus (except first). However, Roslyn has shown that it is more efficient to sample + // crossover locations from geometric distribution if number of loci is large. + // HOW LARGE IS 'LARGE' IN THIS CASE?... + + int ix = 0; // indexes maternal and paternal strands + if (diploid) ix = pRandom->Bernoulli(0.5); // start index at random + for (int i = 0; i < nloc; i++) { + if (diploid) { + pLoci[i].allele[posn] = parentChr->pLoci[i].allele[ix]; + if (pRandom->Bernoulli(probcross)) { // crossover occurs + if (ix == 0) ix = 1; else ix = 0; + } + } + else + pLoci[i].allele[posn] = parentChr->pLoci[i].allele[0]; + if (pRandom->Bernoulli(probmutn)) { // mutation occurs + double intbase = INTBASE; +#if RSDEBUG + int oldval = pLoci[i].allele[posn]; +#endif + double mutnvalue = pRandom->Normal(0, mutnSD); + if (mutnvalue > 0.0) + pLoci[i].allele[posn] += (int)(intbase * mutnvalue + 0.5); + else + pLoci[i].allele[posn] += (int)(intbase * mutnvalue - 0.5); +#if RSDEBUG + MUTNLOG << mutnvalue << " " << oldval << " " << pLoci[i].allele[posn] << " " << endl; +#endif + } + } +} + + +//--------------------------------------------------------------------------- + +// NB THIS FUNCTION IS CURRENTLY NOT BEING CALLED TO CONSTRUCT AN INSTANCE OF Genome +// Genome(int) IS USED INSTEAD + +Genome::Genome() { + pChromosome = NULL; + nChromosomes = 0; +} + +// Set up new genome at initialisation for 1 chromosome per trait +Genome::Genome(int nchromosomes, int nloci, bool d) { + + diploid = d; + if (nchromosomes > 0) nChromosomes = nchromosomes; else nChromosomes = 1; + pChromosome = new Chromosome * [nChromosomes]; + for (int i = 0; i < nChromosomes; i++) { + pChromosome[i] = new Chromosome(nloci); + } + +} + +// Set up new genome at initialisation for trait mapping +Genome::Genome(Species* pSpecies) { + int nloci; + nChromosomes = pSpecies->getNChromosomes(); + diploid = pSpecies->isDiploid(); + pChromosome = new Chromosome * [nChromosomes]; + for (int i = 0; i < nChromosomes; i++) { + nloci = pSpecies->getNLoci(i); + pChromosome[i] = new Chromosome(nloci); + } +} + +// Inherit genome from parent(s) +Genome::Genome(Species* pSpecies, Genome* mother, Genome* father) +{ + genomeData gen = pSpecies->getGenomeData(); + + nChromosomes = mother->nChromosomes; + diploid = mother->diploid; + pChromosome = new Chromosome * [nChromosomes]; + + for (int i = 0; i < nChromosomes; i++) { + pChromosome[i] = new Chromosome(mother->pChromosome[i]->nLoci()); + inherit(mother, 0, i, gen.probMutn, gen.probCrossover, gen.mutationSD); + if (diploid) { + if (father == 0) { // species is hermaphrodite - inherit again from mother + inherit(mother, 1, i, gen.probMutn, gen.probCrossover, gen.mutationSD); + } + else inherit(father, 1, i, gen.probMutn, gen.probCrossover, gen.mutationSD); + } + } + +} + +Genome::~Genome() { + + if (pChromosome == NULL) return; + + for (int i = 0; i < nChromosomes; i++) { + delete pChromosome[i]; + } + delete[] pChromosome; + +} + +//--------------------------------------------------------------------------- + +void Genome::setDiploid(bool dip) { diploid = dip; } +bool Genome::isDiploid(void) { return diploid; } +short Genome::getNChromosomes(void) { return nChromosomes; } + +//--------------------------------------------------------------------------- + +// Inherit from specified parent +void Genome::inherit(const Genome* parent, const short posn, const short chr, + const double probmutn, const double probcross, const double mutnSD) +{ + pChromosome[chr]->inherit(parent->pChromosome[chr], posn, parent->pChromosome[chr]->nLoci(), + probmutn, probcross, mutnSD, diploid); + +} + +void Genome::outGenHeaders(const int rep, const int landNr, const bool xtab) +{ + + if (landNr == -999) { // close file + if (outGenetic.is_open()) { + outGenetic.close(); outGenetic.clear(); + } + return; + } + + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + + "_Land" + Int2Str(landNr) + "_Rep" + Int2Str(rep) + "_Genetics.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + Int2Str(sim.simulation) + + "_Rep" + Int2Str(rep) + "_Genetics.txt"; + } + outGenetic.open(name.c_str()); + + outGenetic << "Rep\tYear\tSpecies\tIndID"; + if (xtab) { + for (int i = 0; i < nChromosomes; i++) { + int nloci = pChromosome[i]->nLoci(); + for (int j = 0; j < nloci; j++) { + outGenetic << "\tChr" << i << "Loc" << j << "Allele0"; + if (diploid) outGenetic << "\tChr" << i << "Loc" << j << "Allele1"; + } + } + outGenetic << endl; + } + else { + outGenetic << "\tChromosome\tLocus\tAllele0"; + if (diploid) outGenetic << "\tAllele1"; + outGenetic << endl; + } + +} + +void Genome::outGenetics(const int rep, const int year, const int spnum, + const int indID, const bool xtab) +{ + locus l; + if (xtab) { + outGenetic << rep << "\t" << year << "\t" << spnum << "\t" << indID; + for (int i = 0; i < nChromosomes; i++) { + int nloci = pChromosome[i]->nLoci(); + for (int j = 0; j < nloci; j++) { + l = pChromosome[i]->alleles(j); + outGenetic << "\t" << l.allele[0]; + if (diploid) outGenetic << "\t" << l.allele[1]; + } + } + outGenetic << endl; + } + else { + for (int i = 0; i < nChromosomes; i++) { + int nloci = pChromosome[i]->nLoci(); + for (int j = 0; j < nloci; j++) { + outGenetic << rep << "\t" << year << "\t" << spnum << "\t" + << indID << "\t" << i << "\t" << j; + l = pChromosome[i]->alleles(j); + outGenetic << "\t" << l.allele[0]; + if (diploid) outGenetic << "\t" << l.allele[1]; + outGenetic << endl; + } + } + } +} + +//--------------------------------------------------------------------------- + +// Set up new gene at initialisation for 1 chromosome per trait +void Genome::setGene(const short chr, const short exp, + const double traitval, const double alleleSD) + // NB PARAMETER exp FOR EXPRESSION TYPE IS NOT CURRENTLY USED... +{ + if (chr >= 0 && chr < nChromosomes) { + pChromosome[chr]->initialise(traitval, alleleSD, diploid); + } +} + +// Set up trait at initialisation for trait mapping +void Genome::setTrait(Species* pSpecies, const int trait, + const double traitval, const double alleleSD) +{ + traitAllele allele; + int nalleles = pSpecies->getNTraitAlleles(trait); + int ntraitmaps = pSpecies->getNTraitMaps(); + + int avalue; + double intbase = INTBASE; + if (trait < ntraitmaps) { + for (int i = 0; i < nalleles; i++) { + allele = pSpecies->getTraitAllele(trait, i); + avalue = (int)(pRandom->Normal(traitval, alleleSD) * intbase); + pChromosome[allele.chromo]->initialise(allele.locus, 0, avalue); + if (diploid) { + avalue = (int)(pRandom->Normal(traitval, alleleSD) * intbase); + pChromosome[allele.chromo]->initialise(allele.locus, 1, avalue); + } + } + } + else { // insufficient traits were defined + // alleles cannot be initialised - all individuals have mean phenotype + } + +} + +// Set up trait at initialisation for trait mapping +void Genome::setNeutralLoci(Species* pSpecies, const double alleleSD) +{ + traitAllele allele; + int nneutral = pSpecies->getNNeutralLoci(); + + double avalue; + double intbase = INTBASE; + for (int i = 0; i < nneutral; i++) { + allele = pSpecies->getNeutralAllele(i); + avalue = pRandom->Normal(0.0, alleleSD); + if (avalue > 0.0) + pChromosome[allele.chromo]->initialise(allele.locus, 0, (int)(avalue * intbase + 0.5)); + else + pChromosome[allele.chromo]->initialise(allele.locus, 0, (int)(avalue * intbase - 0.5)); + if (diploid) { + avalue = pRandom->Normal(0.0, alleleSD); + if (avalue > 0.0) + pChromosome[allele.chromo]->initialise(allele.locus, 1, (int)(avalue * intbase + 0.5)); + else + pChromosome[allele.chromo]->initialise(allele.locus, 1, (int)(avalue * intbase - 0.5)); + } + } +} + +// Return the expressed value of a gene when species has one chromosome per trait +double Genome::express(short chr, short expr, short indsex) +{ + double genevalue = 0.0; + genevalue = pChromosome[chr]->meanvalue(diploid); + return genevalue; +} + +// Return the expressed value of a trait when genetic architecture is defined +double Genome::express(Species* pSpecies, short traitnum) +{ + double genevalue = 0.0; + + traitAllele allele; + int nalleles = pSpecies->getNTraitAlleles(traitnum); + if (nalleles > 0) { + for (int i = 0; i < nalleles; i++) { + allele = pSpecies->getTraitAllele(traitnum, i); + genevalue += pChromosome[allele.chromo]->additive(allele.locus, diploid); + } + genevalue /= (double)nalleles; + if (diploid) genevalue /= 2.0; + } + return genevalue; +} + + +locusOK Genome::getAlleles(short chr, short loc) { + locusOK l; + l.allele[0] = l.allele[1] = 0; l.ok = false; + if (chr >= 0 && chr < nChromosomes) { + if (pChromosome[chr] != 0) { + if (loc >= 0 && loc < pChromosome[chr]->nLoci()) { + locus a = pChromosome[chr]->alleles(loc); + l.allele[0] = a.allele[0]; l.allele[1] = a.allele[1]; l.ok = true; + } + } + } + + return l; +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + diff --git a/src/RScore/Genome.h b/src/RScore/Genome.h new file mode 100644 index 00000000..4f01edec --- /dev/null +++ b/src/RScore/Genome.h @@ -0,0 +1,173 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + +#ifndef GenomeH +#define GenomeH + +#include +#include + +#include "Parameters.h" +#include "Species.h" + +#define INTBASE 100.0; // to convert integer alleles into continuous traits + +struct locus { short allele[2]; }; +struct locusOK { short allele[2]; bool ok; }; + +//--------------------------------------------------------------------------- + +class Chromosome { + +public: + Chromosome(int); + ~Chromosome(); + short nLoci(void); + double additive( // Return trait value on normalised genetic scale + const bool // diploid + ); + double meanvalue( // Return trait value on normalised genetic scale + const bool // diploid + ); + double additive( // Return trait value on normalised genetic scale + const short, // locus + const bool // diploid + ); + locus alleles( // Return allele values at a specified locus + const int // position of locus on chromosome + ); + void initialise( // Set up chromosome at simulation initialisation + const double, // normalised phenotypic trait value + const double, // s.d. of allelic variance (genetic scale) + const bool // diploid + ); + void initialise( // Set up specified locus at simulation initialisation + const short, // locus + const short, // position: 0 from mother, 1 from father + const int // allele value + ); + void inherit( // Inherit chromosome from specified parent + const Chromosome*, // pointer to parent's chromosome + const short, // position: 0 from mother, 1 from father + const short, // no. of loci + const double, // mutation probability + const double, // crossover probability + const double, // s.d. of mutation magnitude (genetic scale) + const bool // diploid + ); + +protected: + +private: + short nloci; + locus* pLoci; + +}; + +//--------------------------------------------------------------------------- + +class Genome { + +public: + Genome(); + Genome(int, int, bool); + Genome(Species*); + Genome(Species*, Genome*, Genome*); + ~Genome(); + void setGene( // Set up new gene at initialisation for 1 chromosome per trait + const short, // chromosome number + const short, // expression type (NOT CURRENTLY USED) + const double, // normalised trait value + const double // s.d. of allelic variance + ); + void setTrait( // Set up trait at initialisation for trait mapping + Species*, // pointer to Species + const int, // trait number + const double, // normalised trait value + const double // s.d. of allelic variance + ); + void setNeutralLoci( // Set up neutral loci at initialisation + Species*, // pointer to Species + const double // s.d. of allelic variance + ); + double express( + // Return the expressed value of a gene when species has one chromosome per trait + short, // chromosome number + short, // expression type (NOT CURRENTLY USED) + short // individual's sex (NOT CURRENTLY USED) + ); + double express( + // Return the expressed value of a trait when genetic architecture is defined + Species*, // pointer to Species + short // trait number + ); + locusOK getAlleles( // Get allele values at a specified locus + short, // chromosome number + short // locus position on chromosome + ); + // SCFP NEW DECLARATIONS + void setDiploid(bool); + bool isDiploid(void); + void inherit( // Inherit from specified parent + const Genome*, // pointer to parent's genome + const short, // position: 0 from mother, 1 from father + const short, // chromasome number + const double, // mutation probability + const double, // crossover probability + const double // s.d. of mutation magnitude (genetic scale) + ); + short getNChromosomes(void); + void outGenHeaders( + const int, // replicate + const int, // landscape number + const bool // output as cross table? + ); + void outGenetics( + const int, // replicate + const int, // year + const int, // species number + const int, // individual ID + const bool // output as cross table? + ); + + +private: + short nChromosomes; // no. of chromosomes + bool diploid; + Chromosome** pChromosome; + +}; + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +extern paramSim* paramsSim; +extern RSrandom* pRandom; + +#if RSDEBUG +extern ofstream DEBUGLOG; +extern ofstream MUTNLOG; +extern void DebugGUI(string); +#endif + +//--------------------------------------------------------------------------- + +#endif diff --git a/src/RScore/Individual.cpp b/src/RScore/Individual.cpp new file mode 100644 index 00000000..cb6c5f86 --- /dev/null +++ b/src/RScore/Individual.cpp @@ -0,0 +1,1860 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Individual.h" +//--------------------------------------------------------------------------- + +int Individual::indCounter = 0; + +//--------------------------------------------------------------------------- + +// Individual constructor +Individual::Individual(Cell* pCell, Patch* pPatch, short stg, short a, short repInt, + float probmale, bool movt, short moveType) +{ + indId = indCounter; + indCounter++; // unique identifier for each individual + + stage = stg; + if (probmale <= 0.0) sex = 0; + else sex = pRandom->Bernoulli(probmale); + age = a; + status = 0; + + if (sex == 0 && repInt > 0) { // set no. of fallow seasons for female + fallow = pRandom->IRandom(0, repInt); + } + else fallow = 9999; + isDeveloping = false; + pPrevCell = pCurrCell = pCell; + pNatalPatch = pPatch; + if (movt) { + locn loc = pCell->getLocn(); + path = new pathData; + path->year = 0; path->total = 0; path->out = 0; + path->pSettPatch = 0; path->settleStatus = 0; +#if RS_RCPP + path->pathoutput = 1; +#endif + if (moveType == 1) { // SMS + // set up location data for SMS + smsData = new smsdata; + smsData->dp = smsData->gb = smsData->alphaDB = 1.0; + smsData->betaDB = 1; + smsData->prev.x = loc.x; + smsData->prev.y = loc.y; // previous location + smsData->goal.x = loc.x; + smsData->goal.y = loc.y; // goal location - initialised for dispersal bias + } + else smsData = 0; + if (moveType == 2) { // CRW + // set up continuous co-ordinates etc. for CRW movement + crw = new crwParams; + crw->xc = ((float)pRandom->Random() * 0.999f) + (float)loc.x; + crw->yc = (float)(pRandom->Random() * 0.999f) + (float)loc.y; + crw->prevdrn = (float)(pRandom->Random() * 2.0 * PI); + crw->stepL = crw->rho = 0.0; + } + else crw = 0; + } + else { + path = 0; crw = 0; smsData = 0; + } + emigtraits = 0; + kerntraits = 0; + setttraits = 0; + pGenome = 0; +} + +Individual::~Individual(void) { + if (path != 0) delete path; + if (crw != 0) delete crw; + if (smsData != 0) delete smsData; + if (emigtraits != 0) delete emigtraits; + if (kerntraits != 0) delete kerntraits; + if (setttraits != 0) delete setttraits; + + if (pGenome != 0) delete pGenome; + +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +// Set genes for individual variation from species initialisation parameters +void Individual::setGenes(Species* pSpecies, int resol) { + demogrParams dem = pSpecies->getDemogr(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + genomeData gen = pSpecies->getGenomeData(); + simParams sim = paramsSim->getSim(); + int ntraits; // first trait for all/female expression, second for male expression + + if (gen.trait1Chromosome) { + pGenome = new Genome(pSpecies->getNChromosomes(), pSpecies->getNLoci(0), + pSpecies->isDiploid()); + } + else { + pGenome = new Genome(pSpecies); + } + + int gposn = 0; // current position on genome + int expr = 0; // gene expression type - NOT CURRENTLY USED + + if (emig.indVar) { // set emigration genes + int emigposn = gposn; + double d0, alpha, beta; + emigParams eparams; + if (emig.sexDep) { // must be a sexual species + ntraits = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction (haploid) + ntraits = 1; + } + else { // sexual reproduction + ntraits = 1; + } + } + for (int g = 0; g < ntraits; g++) { // first trait for females/all, second for males + eparams = pSpecies->getEmigParams(0, g); + d0 = pRandom->Normal(0.0, eparams.d0SD) / eparams.d0Scale; + if (emig.densDep) { + alpha = pRandom->Normal(0.0, eparams.alphaSD) / eparams.alphaScale; + beta = pRandom->Normal(0.0, eparams.betaSD) / eparams.betaScale; + } + if (gen.trait1Chromosome) { + pGenome->setGene(gposn++, expr, d0, gen.alleleSD); + if (emig.densDep) { + pGenome->setGene(gposn++, expr, alpha, gen.alleleSD); + pGenome->setGene(gposn++, expr, beta, gen.alleleSD); + } + } + else { + pGenome->setTrait(pSpecies, gposn++, d0, gen.alleleSD); + if (emig.densDep) { + pGenome->setTrait(pSpecies, gposn++, alpha, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, beta, gen.alleleSD); + } + } + } + // record phenotypic traits + if (emig.densDep) { + setEmigTraits(pSpecies, emigposn, 3, emig.sexDep); + } + else { + setEmigTraits(pSpecies, emigposn, 1, emig.sexDep); + } + } + + if (trfr.indVar) { // set transfer genes + int trfrposn = gposn; + if (trfr.sexDep) { // must be a sexual species + ntraits = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ntraits = 1; + } + else { // sexual reproduction + ntraits = 1; + } + } + if (trfr.moveModel) { + if (trfr.moveType == 1) { // set SMS genes + double dp, gb, alphaDB, betaDB; + trfrSMSParams smsparams = pSpecies->getSMSParams(0, 0); // only traits for females/all + trfrSMSTraits smstraits = pSpecies->getSMSTraits(); + dp = pRandom->Normal(0.0, smsparams.dpSD) / smsparams.dpScale; + gb = pRandom->Normal(0.0, smsparams.gbSD) / smsparams.gbScale; + if (smstraits.goalType == 2) { + alphaDB = pRandom->Normal(0.0, smsparams.alphaDBSD) / smsparams.alphaDBScale; + betaDB = pRandom->Normal(0.0, smsparams.betaDBSD) / smsparams.betaDBScale; + } + if (gen.trait1Chromosome) { + pGenome->setGene(gposn++, expr, dp, gen.alleleSD); + pGenome->setGene(gposn++, expr, gb, gen.alleleSD); + if (smstraits.goalType == 2) { + pGenome->setGene(gposn++, expr, alphaDB, gen.alleleSD); + pGenome->setGene(gposn++, expr, betaDB, gen.alleleSD); + } + } + else { + pGenome->setTrait(pSpecies, gposn++, dp, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, gb, gen.alleleSD); + if (smstraits.goalType == 2) { + pGenome->setTrait(pSpecies, gposn++, alphaDB, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, betaDB, gen.alleleSD); + } + } + // record phenotypic traits + if (smstraits.goalType == 2) + setSMSTraits(pSpecies, trfrposn, 4, false); + else + setSMSTraits(pSpecies, trfrposn, 2, false); + } + if (trfr.moveType == 2) { // set CRW genes + double stepL, rho; + trfrCRWParams m = pSpecies->getCRWParams(0, 0); // only traits for females/all + stepL = pRandom->Normal(0.0, m.stepLgthSD) / m.stepLScale; + rho = pRandom->Normal(0.0, m.rhoSD) / m.rhoScale; + if (gen.trait1Chromosome) { + pGenome->setGene(gposn++, expr, stepL, gen.alleleSD); + pGenome->setGene(gposn++, expr, rho, gen.alleleSD); + } + else { + pGenome->setTrait(pSpecies, gposn++, stepL, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, rho, gen.alleleSD); + } + // record phenotypic traits + setCRWTraits(pSpecies, trfrposn, 2, false); + } + } + else { // set kernel genes + double dist1, dist2, prob1; + trfrKernParams k; + for (int g = 0; g < ntraits; g++) { // first traits for females/all, second for males + k = pSpecies->getKernParams(0, g); + dist1 = pRandom->Normal(0.0, k.dist1SD) / k.dist1Scale; + if (trfr.twinKern) + { + dist2 = pRandom->Normal(0.0, k.dist2SD) / k.dist2Scale; + prob1 = pRandom->Normal(0.0, k.PKern1SD) / k.PKern1Scale; + } + if (gen.trait1Chromosome) { + pGenome->setGene(gposn++, expr, dist1, gen.alleleSD); + if (trfr.twinKern) + { + pGenome->setGene(gposn++, expr, dist2, gen.alleleSD); + pGenome->setGene(gposn++, expr, prob1, gen.alleleSD); + } + } + else { + pGenome->setTrait(pSpecies, gposn++, dist1, gen.alleleSD); + if (trfr.twinKern) + { + pGenome->setTrait(pSpecies, gposn++, dist2, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, prob1, gen.alleleSD); + } + } + } + // record phenotypic traits + if (trfr.twinKern) + { + setKernTraits(pSpecies, trfrposn, 3, resol, trfr.sexDep); + } + else { + setKernTraits(pSpecies, trfrposn, 1, resol, trfr.sexDep); + } + } + } + + if (sett.indVar) { + int settposn = gposn; + double s0, alpha, beta; + settParams sparams; + if (sett.sexDep) { // must be a sexual species + ntraits = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ntraits = 1; + } + else { // sexual reproduction + ntraits = 1; + } + } + for (int g = 0; g < ntraits; g++) { // first trait for females/all, second for males + if (sim.batchMode) { + sparams = pSpecies->getSettParams(0, g); + } + else { // individual variability not (yet) implemented as sex-dependent in GUI + sparams = pSpecies->getSettParams(0, 0); + } + s0 = pRandom->Normal(0.0, sparams.s0SD) / sparams.s0Scale; + alpha = pRandom->Normal(0.0, sparams.alphaSSD) / sparams.alphaSScale; + beta = pRandom->Normal(0.0, sparams.betaSSD) / sparams.betaSScale; + + if (gen.trait1Chromosome) { + pGenome->setGene(gposn++, expr, s0, gen.alleleSD); + pGenome->setGene(gposn++, expr, alpha, gen.alleleSD); + pGenome->setGene(gposn++, expr, beta, gen.alleleSD); + } + else { + pGenome->setTrait(pSpecies, gposn++, s0, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, alpha, gen.alleleSD); + pGenome->setTrait(pSpecies, gposn++, beta, gen.alleleSD); + } + } + // record phenotypic traits + setSettTraits(pSpecies, settposn, 3, sett.sexDep); + } + + if (!gen.trait1Chromosome) { + if (gen.neutralMarkers || pSpecies->getNNeutralLoci() > 0) { + pGenome->setNeutralLoci(pSpecies, gen.alleleSD); + } + } +} + +// Inherit genome from parent(s) +void Individual::setGenes(Species* pSpecies, Individual* mother, Individual* father, + int resol) +{ + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + + Genome* pFatherGenome; + if (father == 0) pFatherGenome = 0; else pFatherGenome = father->pGenome; + + pGenome = new Genome(pSpecies, mother->pGenome, pFatherGenome); + + if (emig.indVar) { + // record emigration traits + if (father == 0) { // haploid + if (emig.densDep) { + setEmigTraits(pSpecies, 0, 3, 0); + } + else { + setEmigTraits(pSpecies, 0, 1, 0); + } + } + else { // diploid + if (emig.densDep) { + setEmigTraits(pSpecies, 0, 3, emig.sexDep); + } + else { + setEmigTraits(pSpecies, 0, 1, emig.sexDep); + } + } + } + + if (trfr.indVar) { + // record movement model traits + if (trfr.moveModel) { + if (trfr.moveType == 1) { // SMS + trfrSMSTraits s = pSpecies->getSMSTraits(); + if (s.goalType == 2) + setSMSTraits(pSpecies, trfr.movtTrait[0], 4, 0); + else + setSMSTraits(pSpecies, trfr.movtTrait[0], 2, 0); + } + if (trfr.moveType == 2) { // CRW + setCRWTraits(pSpecies, trfr.movtTrait[0], 2, 0); + } + } + else { // kernel + if (father == 0) { // haploid + if (trfr.twinKern) + { + setKernTraits(pSpecies, trfr.movtTrait[0], 3, resol, 0); + } + else { + setKernTraits(pSpecies, trfr.movtTrait[0], 1, resol, 0); + } + } + else { // diploid + if (trfr.twinKern) + { + setKernTraits(pSpecies, trfr.movtTrait[0], 3, resol, trfr.sexDep); + } + else { + setKernTraits(pSpecies, trfr.movtTrait[0], 1, resol, trfr.sexDep); + } + } + } + } + + if (sett.indVar) { + // record settlement traits + if (father == 0) { // haploid + setSettTraits(pSpecies, sett.settTrait[0], 3, 0); + } + else { // diploid + setSettTraits(pSpecies, sett.settTrait[0], 3, sett.sexDep); + } + } +} + +//--------------------------------------------------------------------------- + +// Identify whether an individual is a potentially breeding female - +// if so, return her stage, otherwise return 0 +int Individual::breedingFem(void) { + if (sex == 0) { + if (status == 0 || status == 4 || status == 5) return stage; + else return 0; + } + else return 0; +} + +int Individual::getId(void) { return indId; } + +int Individual::getSex(void) { return sex; } + +int Individual::getStatus(void) { return status; } + +indStats Individual::getStats(void) { + indStats s; + s.stage = stage; s.sex = sex; s.age = age; s.status = status; s.fallow = fallow; + s.isDeveloping = isDeveloping; + return s; +} + +Cell* Individual::getLocn(const short option) { + if (option == 0) { // return previous location + return pPrevCell; + } + else { // return current location + return pCurrCell; + } +} + +Patch* Individual::getNatalPatch(void) { return pNatalPatch; } + +void Individual::setYearSteps(int t) { + if (path != 0 && t >= 0) { + if (t >= 0) path->year = t; + else path->year = 666; + } +} + +pathSteps Individual::getSteps(void) { + pathSteps s; + if (path == 0) { + s.year = 0; s.total = 0; s.out = 0; + } + else { + s.year = path->year; s.total = path->total; s.out = path->out; + } + return s; +} + +settlePatch Individual::getSettPatch(void) { + settlePatch s; + if (path == 0) { + s.pSettPatch = 0; s.settleStatus = 0; + } + else { + s.pSettPatch = path->pSettPatch; s.settleStatus = path->settleStatus; + } + return s; +} + +void Individual::setSettPatch(const settlePatch s) { + if (path == 0) { + path = new pathData; + path->year = 0; path->total = 0; path->out = 0; path->settleStatus = 0; +#if RS_RCPP + path->pathoutput = 1; +#endif + } + if (s.settleStatus >= 0 && s.settleStatus <= 2) path->settleStatus = s.settleStatus; + path->pSettPatch = s.pSettPatch; +} + +// Set phenotypic emigration traits +void Individual::setEmigTraits(Species* pSpecies, short emiggenelocn, short nemigtraits, + bool sexdep) { + emigTraits e; e.d0 = e.alpha = e.beta = 0.0; + if (pGenome != 0) { + if (pSpecies->has1ChromPerTrait()) { + if (sexdep) { + if (nemigtraits == 3) { // emigration is density-dependent + e.d0 = (float)pGenome->express(emiggenelocn + 3 * sex, 0, 0); + e.alpha = (float)pGenome->express(emiggenelocn + 3 * sex + 1, 0, 0); + e.beta = (float)pGenome->express(emiggenelocn + 3 * sex + 2, 0, 0); + } + else { + e.d0 = (float)pGenome->express(emiggenelocn + sex, 0, 0); + } + } + else { + e.d0 = (float)pGenome->express(emiggenelocn, 0, 0); + if (nemigtraits == 3) { // emigration is density-dependent + e.alpha = (float)pGenome->express(emiggenelocn + 1, 0, 0); + e.beta = (float)pGenome->express(emiggenelocn + 2, 0, 0); + } + } + } + else { + if (sexdep) { + if (nemigtraits == 3) { // emigration is density-dependent + e.d0 = (float)pGenome->express(pSpecies, emiggenelocn + 3 * sex); + e.alpha = (float)pGenome->express(pSpecies, emiggenelocn + 3 * sex + 1); + e.beta = (float)pGenome->express(pSpecies, emiggenelocn + 3 * sex + 2); + } + else { + e.d0 = (float)pGenome->express(pSpecies, emiggenelocn + sex); + } + } + else { + e.d0 = (float)pGenome->express(pSpecies, emiggenelocn); + if (nemigtraits == 3) { // emigration is density-dependent + e.alpha = (float)pGenome->express(pSpecies, emiggenelocn + 1); + e.beta = (float)pGenome->express(pSpecies, emiggenelocn + 2); + } + } + } + } + + emigParams eparams; + if (sexdep) { + eparams = pSpecies->getEmigParams(0, sex); + } + else { + eparams = pSpecies->getEmigParams(0, 0); + } + emigtraits = new emigTraits; + emigtraits->d0 = (float)(e.d0 * eparams.d0Scale + eparams.d0Mean); + emigtraits->alpha = (float)(e.alpha * eparams.alphaScale + eparams.alphaMean); + emigtraits->beta = (float)(e.beta * eparams.betaScale + eparams.betaMean); + if (emigtraits->d0 < 0.0) emigtraits->d0 = 0.0; + if (emigtraits->d0 > 1.0) emigtraits->d0 = 1.0; + return; +} + +// Get phenotypic emigration traits +emigTraits Individual::getEmigTraits(void) { + emigTraits e; e.d0 = e.alpha = e.beta = 0.0; + if (emigtraits != 0) { + e.d0 = emigtraits->d0; + e.alpha = emigtraits->alpha; + e.beta = emigtraits->beta; + } + return e; +} + +// Set phenotypic transfer by kernel traits +void Individual::setKernTraits(Species* pSpecies, short kerngenelocn, short nkerntraits, + int resol, bool sexdep) { + trfrKernTraits k; k.meanDist1 = k.meanDist2 = k.probKern1 = 0.0; + if (pGenome != 0) { + if (pSpecies->has1ChromPerTrait()) { + if (sexdep) { + if (nkerntraits == 3) { // twin kernel + k.meanDist1 = (float)pGenome->express(kerngenelocn + 3 * sex, 0, sex); + k.meanDist2 = (float)pGenome->express(kerngenelocn + 3 * sex + 1, 0, sex); + k.probKern1 = (float)pGenome->express(kerngenelocn + 3 * sex + 2, 0, sex); + } + else { + k.meanDist1 = (float)pGenome->express(kerngenelocn + sex, 0, sex); + } + } + else { + k.meanDist1 = (float)pGenome->express(kerngenelocn, 0, 0); + if (nkerntraits == 3) { // twin kernel + k.meanDist2 = (float)pGenome->express(kerngenelocn + 1, 0, 0); + k.probKern1 = (float)pGenome->express(kerngenelocn + 2, 0, 0); + } + } + } + else { + if (sexdep) { + if (nkerntraits == 3) { // twin kernel + k.meanDist1 = (float)pGenome->express(pSpecies, kerngenelocn + 3 * sex); + k.meanDist2 = (float)pGenome->express(pSpecies, kerngenelocn + 3 * sex + 1); + k.probKern1 = (float)pGenome->express(pSpecies, kerngenelocn + 3 * sex + 2); + } + else { + k.meanDist1 = (float)pGenome->express(pSpecies, kerngenelocn + sex); + } + } + else { + k.meanDist1 = (float)pGenome->express(pSpecies, kerngenelocn); + if (nkerntraits == 3) { // twin kernel + k.meanDist2 = (float)pGenome->express(pSpecies, kerngenelocn + 1); + k.probKern1 = (float)pGenome->express(pSpecies, kerngenelocn + 2); + } + } + } + } + + trfrKernParams kparams; + if (sexdep) { + kparams = pSpecies->getKernParams(0, sex); + } + else { + kparams = pSpecies->getKernParams(0, 0); + } + kerntraits = new trfrKernTraits; + kerntraits->meanDist1 = (float)(k.meanDist1 * kparams.dist1Scale + kparams.dist1Mean); + kerntraits->meanDist2 = (float)(k.meanDist2 * kparams.dist2Scale + kparams.dist2Mean); + kerntraits->probKern1 = (float)(k.probKern1 * kparams.PKern1Scale + kparams.PKern1Mean); + if (!pSpecies->useFullKernel()) { + // kernel mean(s) may not be less than landscape resolution + if (kerntraits->meanDist1 < resol) kerntraits->meanDist1 = (float)resol; + if (kerntraits->meanDist2 < resol) kerntraits->meanDist2 = (float)resol; + } + if (kerntraits->probKern1 < 0.0) kerntraits->probKern1 = 0.0; + if (kerntraits->probKern1 > 1.0) kerntraits->probKern1 = 1.0; + return; +} + +// Get phenotypic emigration traits +trfrKernTraits Individual::getKernTraits(void) { + trfrKernTraits k; k.meanDist1 = k.meanDist2 = k.probKern1 = 0.0; + if (kerntraits != 0) { + k.meanDist1 = kerntraits->meanDist1; + k.meanDist2 = kerntraits->meanDist2; + k.probKern1 = kerntraits->probKern1; + } + return k; +} + +// Set phenotypic transfer by SMS traits +void Individual::setSMSTraits(Species* pSpecies, short SMSgenelocn, short nSMStraits, + bool sexdep) { + trfrSMSTraits s = pSpecies->getSMSTraits(); + double dp, gb, alphaDB, betaDB; + dp = gb = alphaDB = betaDB = 0.0; + if (pGenome != 0) { + if (pSpecies->has1ChromPerTrait()) { + if (sexdep) { + dp = pGenome->express(SMSgenelocn, 0, 0); + gb = pGenome->express(SMSgenelocn + 1, 0, 0); + if (nSMStraits == 4) { + alphaDB = pGenome->express(SMSgenelocn + 2, 0, 0); + betaDB = pGenome->express(SMSgenelocn + 3, 0, 0); + } + } + else { + dp = pGenome->express(SMSgenelocn, 0, 0); + gb = pGenome->express(SMSgenelocn + 1, 0, 0); + if (nSMStraits == 4) { + alphaDB = pGenome->express(SMSgenelocn + 2, 0, 0); + betaDB = pGenome->express(SMSgenelocn + 3, 0, 0); + } + } + } + else { + if (sexdep) { + dp = pGenome->express(pSpecies, SMSgenelocn); + gb = pGenome->express(pSpecies, SMSgenelocn + 1); + if (nSMStraits == 4) { + alphaDB = pGenome->express(pSpecies, SMSgenelocn + 2); + betaDB = pGenome->express(pSpecies, SMSgenelocn + 3); + } + } + else { + dp = pGenome->express(pSpecies, SMSgenelocn); + gb = pGenome->express(pSpecies, SMSgenelocn + 1); + if (nSMStraits == 4) { + alphaDB = pGenome->express(pSpecies, SMSgenelocn + 2); + betaDB = pGenome->express(pSpecies, SMSgenelocn + 3); + } + } + } + } + trfrSMSParams smsparams; + if (sexdep) { + smsparams = pSpecies->getSMSParams(0, 0); + } + else { + smsparams = pSpecies->getSMSParams(0, 0); + } + smsData->dp = (float)(dp * smsparams.dpScale + smsparams.dpMean); + smsData->gb = (float)(gb * smsparams.gbScale + smsparams.gbMean); + if (s.goalType == 2) { + smsData->alphaDB = (float)(alphaDB * smsparams.alphaDBScale + smsparams.alphaDBMean); + smsData->betaDB = (int)(betaDB * smsparams.betaDBScale + smsparams.betaDBMean + 0.5); + } + else { + smsData->alphaDB = s.alphaDB; + smsData->betaDB = s.betaDB; + } + if (smsData->dp < 1.0) smsData->dp = 1.0; + if (smsData->gb < 1.0) smsData->gb = 1.0; + if (smsData->alphaDB <= 0.0) smsData->alphaDB = 0.000001f; + if (smsData->betaDB < 1) smsData->betaDB = 1; + return; +} + +// Get phenotypic transfer by SMS traits +trfrSMSTraits Individual::getSMSTraits(void) { + trfrSMSTraits s; s.dp = s.gb = s.alphaDB = 1.0; s.betaDB = 1; + if (smsData != 0) { + s.dp = smsData->dp; s.gb = smsData->gb; + s.alphaDB = smsData->alphaDB; s.betaDB = smsData->betaDB; + } + return s; +} + +// Set phenotypic transfer by CRW traits +void Individual::setCRWTraits(Species* pSpecies, short CRWgenelocn, short nCRWtraits, + bool sexdep) { + trfrCRWTraits c; c.stepLength = c.rho = 0.0; + if (pGenome != 0) { + if (pSpecies->has1ChromPerTrait()) { + if (sexdep) { + c.stepLength = (float)pGenome->express(CRWgenelocn + sex, 0, sex); + c.rho = (float)pGenome->express(CRWgenelocn + 2 + sex, 0, sex); + } + else { + c.stepLength = (float)pGenome->express(CRWgenelocn, 0, 0); + c.rho = (float)pGenome->express(CRWgenelocn + 1, 0, 0); + } + } + else { + if (sexdep) { + c.stepLength = (float)pGenome->express(pSpecies, CRWgenelocn + sex); + c.rho = (float)pGenome->express(pSpecies, CRWgenelocn + 2 + sex); + } + else { + c.stepLength = (float)pGenome->express(pSpecies, CRWgenelocn); + c.rho = (float)pGenome->express(pSpecies, CRWgenelocn + 1); + } + } + } + + trfrCRWParams cparams; + if (sexdep) { + cparams = pSpecies->getCRWParams(0, sex); + } + else { + cparams = pSpecies->getCRWParams(0, 0); + } + crw->stepL = (float)(c.stepLength * cparams.stepLScale + cparams.stepLgthMean); + crw->rho = (float)(c.rho * cparams.rhoScale + cparams.rhoMean); + if (crw->stepL < 1.0) crw->stepL = 1.0; + if (crw->rho < 0.0) crw->rho = 0.0; + if (crw->rho > 0.999) crw->rho = 0.999f; + return; +} + +// Get phenotypic transfer by CRW traits +trfrCRWTraits Individual::getCRWTraits(void) { + trfrCRWTraits c; c.stepLength = c.rho = 0.0; + if (crw != 0) { + c.stepLength = crw->stepL; + c.rho = crw->rho; + } + return c; +} + +// Set phenotypic settlement traits +void Individual::setSettTraits(Species* pSpecies, short settgenelocn, short nsetttraits, + bool sexdep) { + settleTraits s; s.s0 = s.alpha = s.beta = 0.0; + if (pGenome != 0) { + if (pSpecies->has1ChromPerTrait()) { + if (sexdep) { + s.s0 = (float)pGenome->express(settgenelocn + 3 * sex, 0, 0); + s.alpha = (float)pGenome->express(settgenelocn + 3 * sex + 1, 0, 0); + s.beta = (float)pGenome->express(settgenelocn + 3 * sex + 2, 0, 0); + } + else { + s.s0 = (float)pGenome->express(settgenelocn, 0, 0); + s.alpha = (float)pGenome->express(settgenelocn + 1, 0, 0); + s.beta = (float)pGenome->express(settgenelocn + 2, 0, 0); + } + } + else { + if (sexdep) { + s.s0 = (float)pGenome->express(pSpecies, settgenelocn + 3 * sex); + s.alpha = (float)pGenome->express(pSpecies, settgenelocn + 3 * sex + 1); + s.beta = (float)pGenome->express(pSpecies, settgenelocn + 3 * sex + 2); + } + else { + s.s0 = (float)pGenome->express(pSpecies, settgenelocn); + s.alpha = (float)pGenome->express(pSpecies, settgenelocn + 1); + s.beta = (float)pGenome->express(pSpecies, settgenelocn + 2); + } + + } + } + + settParams sparams; + if (sexdep) { + sparams = pSpecies->getSettParams(0, sex); + } + else { + sparams = pSpecies->getSettParams(0, 0); + } + setttraits = new settleTraits; + setttraits->s0 = (float)(s.s0 * sparams.s0Scale + sparams.s0Mean); + setttraits->alpha = (float)(s.alpha * sparams.alphaSScale + sparams.alphaSMean); + setttraits->beta = (float)(s.beta * sparams.betaSScale + sparams.betaSMean); + if (setttraits->s0 < 0.0) setttraits->s0 = 0.0; + if (setttraits->s0 > 1.0) setttraits->s0 = 1.0; + return; +} + +// Get phenotypic settlement traits +settleTraits Individual::getSettTraits(void) { + settleTraits s; s.s0 = s.alpha = s.beta = 0.0; + if (setttraits != 0) { + s.s0 = setttraits->s0; + s.alpha = setttraits->alpha; + s.beta = setttraits->beta; + } + + return s; +} + + +void Individual::setStatus(short s) { + if (s >= 0 && s <= 9) status = s; + status = s; +} + +void Individual::developing(void) { + isDeveloping = true; +} + +void Individual::develop(void) { + stage++; isDeveloping = false; +} + +void Individual::ageIncrement(short maxage) { + if (status < 6) { // alive + age++; + if (age > maxage) status = 9; // exceeds max. age - dies + else { + if (path != 0) path->year = 0; // reset annual step count for movement models + if (status == 3) // waiting to continue dispersal + status = 1; + } + } +} + +void Individual::incFallow(void) { fallow++; } + +void Individual::resetFallow(void) { fallow = 0; } + +//--------------------------------------------------------------------------- +// Move to a specified neighbouring cell +void Individual::moveto(Cell* newCell) { + // check that location is indeed a neighbour of the current cell + locn currloc = pCurrCell->getLocn(); + locn newloc = newCell->getLocn(); + double d = sqrt(((double)currloc.x - (double)newloc.x) * ((double)currloc.x - (double)newloc.x) + + ((double)currloc.y - (double)newloc.y) * ((double)currloc.y - (double)newloc.y)); + if (d >= 1.0 && d < 1.5) { // ok + pCurrCell = newCell; + status = 5; + } +} + +//--------------------------------------------------------------------------- +// Move to a new cell by sampling a dispersal distance from a single or double +// negative exponential kernel +// Returns 1 if still dispersing (including having found a potential patch), otherwise 0 +int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, + const short repType, const bool absorbing) +{ + + intptr patch; + int patchNum = 0; + int newX = 0, newY = 0; + int dispersing = 1; + double xrand, yrand, meandist, dist, r1, rndangle, nx, ny; + float localK; + trfrKernTraits kern; + Cell* pCell; + Patch* pPatch; + locn loc = pCurrCell->getLocn(); + + landData land = pLandscape->getLandData(); + + bool usefullkernel = pSpecies->useFullKernel(); + trfrRules trfr = pSpecies->getTrfr(); + settleRules sett = pSpecies->getSettRules(stage, sex); + + pCell = NULL; + pPatch = NULL; + + if (trfr.indVar) { // get individual's kernel parameters + kern.meanDist1 = kern.meanDist2 = kern.probKern1 = 0.0; + if (pGenome != 0) { + kern.meanDist1 = kerntraits->meanDist1; + if (trfr.twinKern) + { + kern.meanDist2 = kerntraits->meanDist2; + kern.probKern1 = kerntraits->probKern1; + } + } + } + else { // get kernel parameters for the species + if (trfr.sexDep) { + if (trfr.stgDep) { + kern = pSpecies->getKernTraits(stage, sex); + } + else { + kern = pSpecies->getKernTraits(0, sex); + } + } + else { + if (trfr.stgDep) { + kern = pSpecies->getKernTraits(stage, 0); + } + else { + kern = pSpecies->getKernTraits(0, 0); + } + } + } + + // scale the appropriate kernel mean to the cell size + if (trfr.twinKern) + { + if (pRandom->Bernoulli(kern.probKern1)) + meandist = kern.meanDist1 / (float)land.resol; + else + meandist = kern.meanDist2 / (float)land.resol; + } + else + meandist = kern.meanDist1 / (float)land.resol; + + // scaled mean may not be less than 1 unless emigration derives from the kernel + // (i.e. the 'use full kernel' option is applied) + if (!usefullkernel && meandist < 1.0) meandist = 1.0; + + int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 + do { + do { + do { + // randomise the cell within the patch, provided that the individual is still in + // its natal cell (i.e. not waiting in the matrix) + // this is because, if the patch is very large, the individual is near the centre + // and the (single) kernel mean is (not much more than) the cell size, an infinite + // loop could otherwise result, as the individual never reaches the patch edge + // (in a cell-based model, this has no effect, other than as a processing overhead) + if (status == 1) { + pCell = pNatalPatch->getRandomCell(); + if (pCell != 0) { + loc = pCell->getLocn(); + } + } + // randomise the position of the individual inside the cell + xrand = (double)loc.x + pRandom->Random() * 0.999; + yrand = (double)loc.y + pRandom->Random() * 0.999; + + r1 = 0.0000001 + pRandom->Random() * (1.0 - 0.0000001); + // dist = (-1.0*meandist)*std::log(r1); + dist = (-1.0 * meandist) * log(r1); // for LINUX_CLUSTER + + rndangle = pRandom->Random() * 2.0 * PI; + nx = xrand + dist * sin(rndangle); + ny = yrand + dist * cos(rndangle); + if (nx < 0.0) newX = -1; else newX = (int)nx; + if (ny < 0.0) newY = -1; else newY = (int)ny; +#if RSDEBUG + if (path != 0) (path->year)++; +#endif + loopsteps++; + } while (loopsteps < 1000 && + ((!absorbing && (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY)) + || (!usefullkernel && newX == loc.x && newY == loc.y)) + ); + if (loopsteps < 1000) { + if (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY) { // beyond absorbing boundary + pCell = 0; + patch = 0; + patchNum = -1; + } + else { + pCell = pLandscape->findCell(newX, newY); + if (pCell == 0) { // no-data cell + patch = 0; + patchNum = -1; + } + else { + patch = pCell->getPatch(); + if (patch == 0) { // matrix + pPatch = 0; + patchNum = 0; + } + else { + pPatch = (Patch*)patch; + patchNum = pPatch->getPatchNum(); + } + } + } + } + else { + patch = 0; + patchNum = -1; + } + } while (!absorbing && patchNum < 0 && loopsteps < 1000); // in a no-data region + } while (!usefullkernel && pPatch == pNatalPatch && loopsteps < 1000); // still in the original (natal) patch + + if (loopsteps < 1000) { + if (pCell == 0) { // beyond absorbing boundary or in no-data cell + pCurrCell = 0; + status = 6; + dispersing = 0; + } + else { + pCurrCell = pCell; + if (pPatch == 0) localK = 0.0; // matrix + else localK = pPatch->getK(); + if (patchNum > 0 && localK > 0.0) { // found a new patch + status = 2; // record as potential settler + } + else { + dispersing = 0; + // can wait in matrix if population is stage structured ... + if (pSpecies->stageStructured()) { + // ... and wait option is applied ... + if (sett.wait) { // ... it is + status = 3; // waiting + } + else // ... it is not + status = 6; // dies (unless there is a suitable neighbouring cell) + } + else + status = 6; // dies (unless there is a suitable neighbouring cell) + } + } + } + else { + status = 6; + dispersing = 0; + } + + // apply dispersal-related mortality, which may be distance-dependent + dist *= (float)land.resol; // re-scale distance moved to landscape scale + if (status < 7) { + double dispmort; + trfrMortParams mort = pSpecies->getMortParams(); + if (trfr.distMort) { + dispmort = 1.0 / (1.0 + exp(-(dist - mort.mortBeta) * mort.mortAlpha)); + } + else { + dispmort = mort.fixedMort; + } + if (pRandom->Bernoulli(dispmort)) { + status = 7; // dies + dispersing = 0; + } + } + + return dispersing; +} + +//--------------------------------------------------------------------------- +// Make a single movement step according to a mechanistic movement model +// Returns 1 if still dispersing (including having found a potential patch), otherwise 0 +int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, + const short landIx, const bool absorbing) +{ + + if (status != 1) return 0; // not currently dispersing + + intptr patch; + int patchNum; + int newX, newY; + locn loc; + int dispersing = 1; + double xcnew, ycnew; + double angle; + double mortprob, rho, steplen; + movedata move; + Patch* pPatch = 0; + bool absorbed = false; + + landData land = pLandscape->getLandData(); + simParams sim = paramsSim->getSim(); + + trfrRules trfr = pSpecies->getTrfr(); + trfrCRWTraits movt = pSpecies->getCRWTraits(); + settleSteps settsteps = pSpecies->getSteps(stage, sex); + + patch = pCurrCell->getPatch(); + + if (patch == 0) { // matrix + pPatch = 0; + patchNum = 0; + } + else { + pPatch = (Patch*)patch; + patchNum = pPatch->getPatchNum(); + } + // apply step-dependent mortality risk ... + if (trfr.habMort) + { // habitat-dependent + int h = pCurrCell->getHabIndex(landIx); + if (h < 0) { // no-data cell - should not occur, but if it does, individual dies + mortprob = 1.0; + } + else mortprob = pSpecies->getHabMort(h); + } + else mortprob = movt.stepMort; + // ... unless individual has not yet left natal patch in emigration year + if (pPatch == pNatalPatch && path->out == 0 && path->year == path->total) { + mortprob = 0.0; + } + if (pRandom->Bernoulli(mortprob)) { // individual dies + status = 7; + dispersing = 0; + } + else { // take a step + (path->year)++; + (path->total)++; + if (patch == 0 || pPatch == 0 || patchNum == 0) { // not in a patch + if (path != 0) path->settleStatus = 0; // reset path settlement status + (path->out)++; + } + loc = pCurrCell->getLocn(); + newX = loc.x; newY = loc.y; + + + switch (trfr.moveType) { + + case 1: // SMS + move = smsMove(pLandscape, pSpecies, landIx, pPatch == pNatalPatch, trfr.indVar, absorbing); + if (move.dist < 0.0) { + // either INTERNAL ERROR CONDITION - INDIVIDUAL IS IN NO-DATA SQUARE + // or individual has crossed absorbing boundary ... + // ... individual dies + status = 6; + dispersing = 0; + } + else { + + // WOULD IT BE MORE EFFICIENT FOR smsMove TO RETURN A POINTER TO THE NEW CELL? ... + + patch = pCurrCell->getPatch(); + if (patch == 0) { + pPatch = 0; + } + else { + pPatch = (Patch*)patch; + } + if (sim.saveVisits && pPatch != pNatalPatch) { + pCurrCell->incrVisits(); + } + } + break; + + case 2: // CRW + if (trfr.indVar) { + if (crw != 0) { + movt.stepLength = crw->stepL; + movt.rho = crw->rho; + } + } + + steplen = movt.stepLength; if (steplen < 0.2 * land.resol) steplen = 0.2 * land.resol; + rho = movt.rho; if (rho > 0.99) rho = 0.99; + if (pPatch == pNatalPatch) { + rho = 0.99; // to promote leaving natal patch + path->out = 0; + } + if (movt.straigtenPath && path->settleStatus > 0) { + // individual is in a patch and has already determined whether to settle + rho = 0.99; // to promote leaving the patch + path->out = 0; + } + int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 + do { + do { + // new direction + if (newX < land.minX || newX > land.maxX || newY < land.minY || newY > land.maxY + || pCurrCell == 0) { + // individual has tried to go out-of-bounds or into no-data area + // allow random move to prevent repeated similar move + angle = wrpcauchy(crw->prevdrn, 0.0); + } + else + angle = wrpcauchy(crw->prevdrn, rho); + // new continuous cell coordinates + xcnew = crw->xc + sin(angle) * steplen / (float)land.resol; + ycnew = crw->yc + cos(angle) * steplen / (float)land.resol; + if (xcnew < 0.0) newX = -1; else newX = (int)xcnew; + if (ycnew < 0.0) newY = -1; else newY = (int)ycnew; + loopsteps++; + } while (!absorbing && loopsteps < 1000 && + (newX < land.minX || newX > land.maxX || newY < land.minY || newY > land.maxY)); + if (newX < land.minX || newX > land.maxX || newY < land.minY || newY > land.maxY) + pCurrCell = 0; + else + pCurrCell = pLandscape->findCell(newX, newY); + if (pCurrCell == 0) { // no-data cell or beyond absorbing boundary + patch = 0; + if (absorbing) absorbed = true; + } + else + patch = pCurrCell->getPatch(); + } while (!absorbing && pCurrCell == 0 && loopsteps < 1000); + crw->prevdrn = (float)angle; + crw->xc = (float)xcnew; crw->yc = (float)ycnew; + if (absorbed) { // beyond absorbing boundary or in no-data square + status = 6; + dispersing = 0; + pCurrCell = 0; + } + else { + if (loopsteps >= 1000) { // unable to make a move + // INTERNAL ERROR CONDITION - INDIVIDUAL IS IN NO-DATA SQUARE + // NEED TO TAKE SOME FORM OF INFORMATIVE ACTION ... + // ... individual dies as it cannot move + status = 6; + dispersing = 0; + // current cell will be invalid (zero), so set back to previous cell + pCurrCell = pPrevCell; + } + } + break; + + } // end of switch (trfr.moveType) + + if (patch > 0 // not no-data area or matrix + && path->total >= settsteps.minSteps) { + pPatch = (Patch*)patch; + if (pPatch != pNatalPatch) + { + // determine whether the new patch is potentially suitable + if (pPatch->getK() > 0.0) + { // patch is suitable + status = 2; + } + } + } + if (status != 2 && status != 6) { // suitable patch not found, not already dead + if (path->year >= settsteps.maxStepsYr) { + status = 3; // waits until next year + } + if (path->total >= settsteps.maxSteps) { + status = 6; // dies + dispersing = 0; + } + } + } // end of single movement step + + return dispersing; + +} + +//--------------------------------------------------------------------------- + +// Functions to implement the SMS algorithm + +// Move to a neighbouring cell according to the SMS algorithm +movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, + const short landIx, const bool natalPatch, const bool indvar, const bool absorbing) +{ + + array3x3d nbr; // to hold weights/costs/probs of moving to neighbouring cells + array3x3d goal; // to hold weights for moving towards a goal location + array3x3f hab; // to hold weights for habitat (includes percep range) + int x2, y2; // x index from 0=W to 2=E, y index from 0=N to 2=S + int newX = 0, newY = 0; + Cell* pCell; + Cell* pNewCell = NULL; + double sum_nbrs = 0.0; + movedata move; + int cellcost, newcellcost; + locn current; + + if (pCurrCell == 0) + { + // x,y is a NODATA square - this should not occur here + // return a negative distance to indicate an error + move.dist = -69.0; move.cost = 0.0; + return move; + } + + landData land = pLand->getLandData(); + trfrSMSTraits movt = pSpecies->getSMSTraits(); + current = pCurrCell->getLocn(); + + //get weights for directional persistence.... + if ((path->out > 0 && path->out <= (movt.pr + 1)) + || natalPatch + || (movt.straigtenPath && path->settleStatus > 0)) { + // inflate directional persistence to promote leaving the patch + if (indvar) nbr = getSimDir(current.x, current.y, 10.0f * smsData->dp); + else nbr = getSimDir(current.x, current.y, 10.0f * movt.dp); + } + else { + if (indvar) nbr = getSimDir(current.x, current.y, smsData->dp); + else nbr = getSimDir(current.x, current.y, movt.dp); + } + if (natalPatch || path->settleStatus > 0) path->out = 0; + + //get weights for goal bias.... + double gb; + if (movt.goalType == 2) { // dispersal bias + int nsteps = 0; + if (path->year == path->total) { // first year of dispersal - use no. of steps outside natal patch + nsteps = path->out; + } + else { // use total no. of steps + nsteps = path->total; + } + if (indvar) { + double exp_arg = -((double)nsteps - (double)smsData->betaDB) * (-smsData->alphaDB); + if (exp_arg > 100.0) exp_arg = 100.0; // to prevent exp() overflow error + gb = 1.0 + (smsData->gb - 1.0) / (1.0 + exp(exp_arg)); + } + else { + double exp_arg = -((double)nsteps - (double)movt.betaDB) * (-movt.alphaDB); + if (exp_arg > 100.0) exp_arg = 100.0; // to prevent exp() overflow error + gb = 1.0 + (movt.gb - 1.0) / (1.0 + exp(exp_arg)); + } + } + else gb = movt.gb; + goal = getGoalBias(current.x, current.y, movt.goalType, (float)gb); + + // get habitat-dependent weights (mean effective costs, given perceptual range) + // first check if costs have already been calculated + + hab = pCurrCell->getEffCosts(); + + if (hab.cell[0][0] < 0.0) { // costs have not already been calculated + hab = getHabMatrix(pLand, pSpecies, current.x, current.y, movt.pr, movt.prMethod, + landIx, absorbing); + pCurrCell->setEffCosts(hab); + } + else { + // they have already been calculated - no action required + } + + // determine weighted effective cost for the 8 neighbours + // multiply directional persistence, goal bias and habitat habitat-dependent weights + for (y2 = 2; y2 > -1; y2--) { + for (x2 = 0; x2 < 3; x2++) { + if (x2 == 1 && y2 == 1) nbr.cell[x2][y2] = 0.0; + else { + if (x2 == 1 || y2 == 1) //not diagonal + nbr.cell[x2][y2] = nbr.cell[x2][y2] * goal.cell[x2][y2] * hab.cell[x2][y2]; + else // diagonal + nbr.cell[x2][y2] = (float)SQRT2 * nbr.cell[x2][y2] * goal.cell[x2][y2] * hab.cell[x2][y2]; + } + } + } + + // determine reciprocal of effective cost for the 8 neighbours + for (y2 = 2; y2 > -1; y2--) { + for (x2 = 0; x2 < 3; x2++) { + if (nbr.cell[x2][y2] > 0.0) nbr.cell[x2][y2] = 1.0f / nbr.cell[x2][y2]; + } + } + + // set any cells beyond the current landscape limits and any no-data cells + // to have zero probability + // increment total for re-scaling to sum to unity + + for (y2 = 2; y2 > -1; y2--) { + for (x2 = 0; x2 < 3; x2++) { + if (!absorbing) { + if ((current.y + y2 - 1) < land.minY || (current.y + y2 - 1) > land.maxY + || (current.x + x2 - 1) < land.minX || (current.x + x2 - 1) > land.maxX) + // cell is beyond current landscape limits + nbr.cell[x2][y2] = 0.0; + else { // check if no-data cell + pCell = pLand->findCell((current.x + x2 - 1), (current.y + y2 - 1)); + if (pCell == 0) nbr.cell[x2][y2] = 0.0; // no-data cell + } + } + + sum_nbrs += nbr.cell[x2][y2]; + } + } + + // scale effective costs as probabilities summing to 1 + if (sum_nbrs > 0.0) { // should always be the case, but safest to check... + for (y2 = 2; y2 > -1; y2--) { + for (x2 = 0; x2 < 3; x2++) { + nbr.cell[x2][y2] = nbr.cell[x2][y2] / (float)sum_nbrs; + } + } + } + + // set up cell selection probabilities + double cumulative[9]; + int j = 0; + cumulative[0] = nbr.cell[0][0]; + for (y2 = 0; y2 < 3; y2++) { + for (x2 = 0; x2 < 3; x2++) { + if (j != 0) cumulative[j] = cumulative[j - 1] + nbr.cell[x2][y2]; + j++; + } + } + + // select direction at random based on cell selection probabilities + // landscape boundaries and no-data cells may be reflective or absorbing + cellcost = pCurrCell->getCost(); + int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 + do { + do { + double rnd = pRandom->Random(); + j = 0; + for (y2 = 0; y2 < 3; y2++) { + for (x2 = 0; x2 < 3; x2++) { + if (rnd < cumulative[j]) { + newX = current.x + x2 - 1; + newY = current.y + y2 - 1; + if (x2 == 1 || y2 == 1) move.dist = (float)(land.resol); + else move.dist = (float)(land.resol) * (float)SQRT2; + y2 = 999; x2 = 999; //to break out of x2 and y2 loops. + } + j++; + } + } + loopsteps++; + } while (loopsteps < 1000 + && (!absorbing && (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY))); + if (loopsteps >= 1000) pNewCell = 0; + else { + if (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY) { + pNewCell = 0; + } + pNewCell = pLand->findCell(newX, newY); + } + } while (!absorbing && pNewCell == 0 && loopsteps < 1000); // no-data cell + if (loopsteps >= 1000 || pNewCell == 0) { + // unable to make a move or crossed absorbing boundary + // flag individual to die + move.dist = -123.0; + if (pNewCell == 0) pCurrCell = pNewCell; + } + else { + newcellcost = pNewCell->getCost(); + move.cost = move.dist * 0.5f * ((float)cellcost + (float)newcellcost); + // make the selected move + if ((short)memory.size() == movt.memSize) { + memory.pop(); // remove oldest memory element + } + memory.push(current); // record previous location in memory + pCurrCell = pNewCell; + } + return move; +} + +// Weight neighbouring cells on basis of current movement direction +array3x3d Individual::getSimDir(const int x, const int y, const float dp) +{ + + array3x3d d; + locn prev; + double theta; + int xx, yy; + + if (memory.empty()) + { // no previous movement, set matrix to unity + for (xx = 0; xx < 3; xx++) { + for (yy = 0; yy < 3; yy++) { + d.cell[xx][yy] = 1; + } + } + } + else { // set up the matrix dependent on relationship of previous location to current + d.cell[1][1] = 0; + prev = memory.front(); + if ((x - prev.x) == 0 && (y - prev.y) == 0) { + // back to 'square 1' (first memory location) - use previous step drn only + prev = memory.back(); + if ((x - prev.x) == 0 && (y - prev.y) == 0) { // STILL HAVE A PROBLEM! + for (xx = 0; xx < 3; xx++) { + for (yy = 0; yy < 3; yy++) { + d.cell[xx][yy] = 1.0; + } + } + return d; + } + } + else { + } + theta = atan2(((double)x - (double)prev.x), ((double)y - (double)prev.y)); + d = calcWeightings(dp, (float)theta); + + } + return d; +} + +// Weight neighbouring cells on basis of goal bias +array3x3d Individual::getGoalBias(const int x, const int y, + const int goaltype, const float gb) +{ + + array3x3d d; + double theta; + int xx, yy; + + if (goaltype == 0) { // no goal set + for (xx = 0; xx < 3; xx++) { + for (yy = 0; yy < 3; yy++) { + d.cell[xx][yy] = 1.0; + } + } + } + else { + d.cell[1][1] = 0; + if ((x - smsData->goal.x) == 0 && (y - smsData->goal.y) == 0) { + // at goal, set matrix to unity + for (xx = 0; xx < 3; xx++) { + for (yy = 0; yy < 3; yy++) { + d.cell[xx][yy] = 1.0; + } + } + return d; + } + if (goaltype == 1) { + // TEMPORARY CODE - GOAL TYPE 1 NOT YET IMPLEMENTED, AS WE HAVE NO MEANS OF + // CAPTURING THE GOAL LOCATION OF EACH INDIVIDUAL + for (xx = 0; xx < 3; xx++) { + for (yy = 0; yy < 3; yy++) { + d.cell[xx][yy] = 1.0; + } + } + return d; + } + else // goaltype == 2 + theta = atan2(((double)x - (double)smsData->goal.x), ((double)y - (double)smsData->goal.y)); + d = calcWeightings(gb, (float)theta); + } + + return d; +} + +// Calculate weightings for neighbouring cells +array3x3d Individual::calcWeightings(const double base, const double theta) { + + array3x3d d; // 3x3 array indexed from SW corner by xx and yy + int dx, dy, xx, yy; + + double i0 = 1.0; // direction of theta - lowest cost bias + double i1 = base; + double i2 = base * base; + double i3 = i2 * base; + double i4 = i3 * base; // opposite to theta - highest cost bias + + if (fabs(theta) > 7.0 * PI / 8.0) { dx = 0; dy = -1; } + else { + if (fabs(theta) > 5.0 * PI / 8.0) { dy = -1; if (theta > 0) dx = 1; else dx = -1; } + else { + if (fabs(theta) > 3.0 * PI / 8.0) { dy = 0; if (theta > 0) dx = 1; else dx = -1; } + else { + if (fabs(theta) > PI / 8.0) { dy = 1; if (theta > 0) dx = 1; else dx = -1; } + else { dy = 1; dx = 0; } + } + } + } + d.cell[1][1] = 0; // central cell has zero weighting + d.cell[dx + 1][dy + 1] = (float)i0; + d.cell[-dx + 1][-dy + 1] = (float)i4; + if (dx == 0 || dy == 0) { // theta points to a cardinal direction + d.cell[dy + 1][dx + 1] = (float)i2; d.cell[-dy + 1][-dx + 1] = (float)i2; + if (dx == 0) { // theta points N or S + xx = dx + 1; if (xx > 1) dx -= 2; yy = dy; + d.cell[xx + 1][yy + 1] = (float)i1; d.cell[-xx + 1][yy + 1] = (float)i1; + d.cell[xx + 1][-yy + 1] = (float)i3; d.cell[-xx + 1][-yy + 1] = (float)i3; + } + else { // theta points W or E + yy = dy + 1; if (yy > 1) dy -= 2; xx = dx; + d.cell[xx + 1][yy + 1] = (float)i1; d.cell[xx + 1][-yy + 1] = (float)i1; + d.cell[-xx + 1][yy + 1] = (float)i3; d.cell[-xx + 1][-yy + 1] = (float)i3; + } + } + else { // theta points to an ordinal direction + d.cell[dx + 1][-dy + 1] = (float)i2; d.cell[-dx + 1][dy + 1] = (float)i2; + xx = dx + 1; if (xx > 1) xx -= 2; d.cell[xx + 1][dy + 1] = (float)i1; + yy = dy + 1; if (yy > 1) yy -= 2; d.cell[dx + 1][yy + 1] = (float)i1; + d.cell[-xx + 1][-dy + 1] = (float)i3; d.cell[-dx + 1][-yy + 1] = (float)i3; + } + + return d; +} + +// Weight neighbouring cells on basis of (habitat) costs +array3x3f Individual::getHabMatrix(Landscape* pLand, Species* pSpecies, + const int x, const int y, const short pr, const short prmethod, const short landIx, + const bool absorbing) +{ + + array3x3f w; // array of effective costs to be returned + int ncells, x4, y4; + double weight, sumweights; + // NW and SE corners of effective cost array relative to the current cell (x,y): + int xmin = 0, ymin = 0, xmax = 0, ymax = 0; + int cost, nodatacost, h; + Cell* pCell; + + landData land = pLand->getLandData(); + if (absorbing) nodatacost = ABSNODATACOST; + else nodatacost = NODATACOST; + + for (int x2 = -1; x2 < 2; x2++) { // index of relative move in x direction + for (int y2 = -1; y2 < 2; y2++) { // index of relative move in x direction + + w.cell[x2 + 1][y2 + 1] = 0.0; // initialise costs array to zeroes + + // set up corners of perceptual range relative to current cell + if (x2 == 0 && y2 == 0) { // current cell - do nothing + xmin = 0; ymin = 0; xmax = 0; ymax = 0; + } + else { + if (x2 == 0 || y2 == 0) { // not diagonal (rook move) + if (x2 == 0) { // vertical (N-S) move + if (pr % 2 == 0) { xmin = -pr / 2; xmax = pr / 2; ymin = y2; ymax = y2 * pr; } // PR even + else { xmin = -(pr - 1) / 2; xmax = (pr - 1) / 2; ymin = y2; ymax = y2 * pr; } // PR odd + } + if (y2 == 0) { // horizontal (E-W) move + if (pr % 2 == 0) { xmin = x2; xmax = x2 * pr; ymin = -pr / 2; ymax = pr / 2; } // PR even + else { xmin = x2; xmax = x2 * pr; ymin = -(pr - 1) / 2; ymax = (pr - 1) / 2; } // PR odd + } + } + else { // diagonal (bishop move) + xmin = x2; xmax = x2 * pr; ymin = y2; ymax = y2 * pr; + } + } + if (xmin > xmax) { int z = xmax; xmax = xmin; xmin = z; } // swap xmin and xmax + if (ymin > ymax) { int z = ymax; ymax = ymin; ymin = z; } // swap ymin and ymax + + // calculate effective mean cost of cells in perceptual range + ncells = 0; weight = 0.0; sumweights = 0.0; + if (x2 != 0 || y2 != 0) { // not central cell (i.e. current cell) + for (int x3 = xmin; x3 <= xmax; x3++) { + for (int y3 = ymin; y3 <= ymax; y3++) { + // if cell is out of bounds, treat landscape as a torus + // for purpose of obtaining a cost, + if ((x + x3) < 0) x4 = x + x3 + land.maxX + 1; + else { if ((x + x3) > land.maxX) x4 = x + x3 - land.maxX - 1; else x4 = x + x3; } + if ((y + y3) < 0) y4 = y + y3 + land.maxY + 1; + else { if ((y + y3) > land.maxY) y4 = y + y3 - land.maxY - 1; else y4 = y + y3; } + if (x4 < 0 || x4 > land.maxX || y4 < 0 || y4 > land.maxY) { + // unexpected problem - e.g. due to ridiculously large PR + // treat as a no-data cell + cost = nodatacost; + } + else { + // add cost of cell to total PR cost + pCell = pLand->findCell(x4, y4); + if (pCell == 0) { // no-data cell + cost = nodatacost; + } + else { + cost = pCell->getCost(); + if (cost < 0) cost = nodatacost; + else { + if (cost == 0) { // cost not yet set for the cell + h = pCell->getHabIndex(landIx); + cost = pSpecies->getHabCost(h); + pCell->setCost(cost); + } + else { + + } + } + } + } + if (prmethod == 1) { // arithmetic mean + w.cell[x2 + 1][y2 + 1] += cost; + ncells++; + } + if (prmethod == 2) { // harmonic mean + if (cost > 0) { + w.cell[x2 + 1][y2 + 1] += (1.0f / (float)cost); + ncells++; + } + } + if (prmethod == 3) { // arithmetic mean weighted by inverse distance + if (cost > 0) { + // NB distance is still given by (x3,y3) + weight = 1.0f / (double)sqrt((pow((double)x3, 2) + pow((double)y3, 2))); + w.cell[x2 + 1][y2 + 1] += (float)(weight * (double)cost); + ncells++; sumweights += weight; + } + } + } //end of y3 loop + } //end of x3 loop + if (ncells > 0) { + if (prmethod == 1) w.cell[x2 + 1][y2 + 1] /= ncells; // arithmetic mean + if (prmethod == 2) w.cell[x2 + 1][y2 + 1] = ncells / w.cell[x2 + 1][y2 + 1]; // hyperbolic mean + if (prmethod == 3 && sumweights > 0) + w.cell[x2 + 1][y2 + 1] /= (float)sumweights; // weighted arithmetic mean + } + } + else { // central cell + // record cost if not already recorded + // has effect of preparing for storing effective costs for the cell + pCell = pLand->findCell(x, y); + cost = pCell->getCost(); + if (cost < 0) cost = nodatacost; + else { + if (cost == 0) { // cost not yet set for the cell + h = pCell->getHabIndex(landIx); + cost = pSpecies->getHabCost(h); + pCell->setCost(cost); + } + } + } + }//end of y2 loop + }//end of x2 loop + + return w; + +} + +//--------------------------------------------------------------------------- +// Write records to individuals file +void Individual::outGenetics(const int rep, const int year, const int spnum, + const int landNr, const bool xtab) +{ + if (landNr == -1) { + if (pGenome != 0) { + pGenome->outGenetics(rep, year, spnum, indId, xtab); + } + } + else { // open/close file + pGenome->outGenHeaders(rep, landNr, xtab); + } + +} + +#if RS_RCPP +//--------------------------------------------------------------------------- +// Write records to movement paths file +void Individual::outMovePath(const int year) +{ + locn loc, prev_loc; + + //if (pPatch != pNatalPatch) { + loc = pCurrCell->getLocn(); + // if still dispersing... + if (status == 1) { + // at first step, record start cell first + if (path->total == 1) { + prev_loc = pPrevCell->getLocn(); + outMovePaths << year << "\t" << indId << "\t" + << "0\t" << prev_loc.x << "\t" << prev_loc.y << "\t" + << "0\t" // status at start cell is 0 + << endl; + } + // then record current step + outMovePaths << year << "\t" << indId << "\t" + << path->total << "\t" << loc.x << "\t" << loc.y << "\t" + << status << "\t" + << endl; + } + // if not anymore dispersing... + if (status > 1 && status < 10) { + prev_loc = pPrevCell->getLocn(); + // record only if this is the first step as non-disperser + if (path->pathoutput) { + // if this is also the first step taken at all, record the start cell first + if (path->total == 1) { + outMovePaths << year << "\t" << indId << "\t" + << "0\t" << prev_loc.x << "\t" << prev_loc.y << "\t" + << "0\t" // status at start cell is 0 + << endl; + } + outMovePaths << year << "\t" << indId << "\t" + << path->total << "\t" << loc.x << "\t" << loc.y << "\t" + << status << "\t" + << endl; + // current cell will be invalid (zero), so set back to previous cell + //pPrevCell = pCurrCell; + path->pathoutput = 0; + } + } +} +#endif + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +double wrpcauchy(double location, double rho) { + double result; + + if (rho < 0.0 || rho > 1.0) { + result = location; + } + + if (rho == 0) + result = pRandom->Random() * M_2PI; + else + if (rho == 1) result = location; + else { + result = fmod(cauchy(location, -log(rho)), M_2PI); + } + return result; +} + +double cauchy(double location, double scale) { + if (scale < 0) return location; + return location + scale * tan(PI * pRandom->Random()); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#if RSDEBUG + + +void testIndividual() { + + Patch* pPatch = new Patch(0, 0); + int cell_x = 2; + int cell_y = 5; + int cell_hab = 2; + Cell* pCell = new Cell(cell_x, cell_y, (intptr)pPatch, cell_hab); + + // Create an individual + short stg = 0; + short age = 0; + short repInt = 0; + float probmale = 0; + bool uses_movt_process = true; + short moveType = 1; + Individual ind(pCell, pPatch, stg, age, repInt, probmale, uses_movt_process, moveType); + + // An individual can move to a neighbouring cell + //ind.moveto(); + + // Gets its sex drawn from pmale + + // Can age or develop + + // + + // Reproduces + // depending on whether it is sexual or not + // depending on the stage + // depending on the trait inheritance + + + // Disperses + // Emigrates + // Transfers + // Settles + + // Survives + + // Develops + +} +#endif // RSDEBUG + diff --git a/src/RScore/Individual.h b/src/RScore/Individual.h new file mode 100644 index 00000000..bdc4ecbd --- /dev/null +++ b/src/RScore/Individual.h @@ -0,0 +1,312 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Individual + +Implements the Individual class + +Various optional attributes (genes for traits, movement parameters, etc.) are +allocated dynamically and accessed by pointers if required. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 26 October 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef IndividualH +#define IndividualH + + +#include +#include +using namespace std; + +#include "Parameters.h" +#include "Species.h" +#include "Landscape.h" +#include "Patch.h" +#include "Cell.h" +#include "Genome.h" + +#define NODATACOST 100000 // cost to use in place of nodata value for SMS +#define ABSNODATACOST 100 // cost to use in place of nodata value for SMS + // when boundaries are absorbing + +//--------------------------------------------------------------------------- + +struct indStats { + short stage; short sex; short age; short status; short fallow; + bool isDeveloping; +}; +struct pathData { // to hold path data common to SMS and CRW models + int year, total, out; // nos. of steps + Patch* pSettPatch; // pointer to most recent patch tested for settlement + short settleStatus; // whether ind may settle in current patch + // 0 = not set, 1 = debarred through density dependence rule + // 2 = OK to settle subject to finding a mate +#if RS_RCPP + short pathoutput; +#endif +}; +struct pathSteps { // nos. of steps for movement model + int year, total, out; +}; +struct settlePatch { + Patch* pSettPatch; short settleStatus; +}; +struct crwParams { // to hold data for CRW movement model + float prevdrn; // direction of previous step (UNITS) + float xc,yc; // continuous cell co-ordinates + float stepL; // phenotypic step length (m) + float rho; // phenotypic step correlation coefficient +}; +struct array3x3d { double cell[3][3]; }; +struct movedata { float dist; float cost; }; +struct smsdata { + locn prev; // location of previous cell + locn goal; // location of goal + float dp; // directional persistence + float gb; // goal bias + float alphaDB; // dispersal bias decay rate + int betaDB; // dispersal bias decay inflection point (no. of steps) +}; + +class Individual { + +public: + static int indCounter; // used to create ID, held by class, not members of class + Individual( // Individual constructor + Cell*, // pointer to Cell + Patch*, // pointer to patch + short, // stage + short, // age + short, // reproduction interval (no. of years/seasons between breeding attempts) + float, // probability that sex is male + bool, // TRUE for a movement model, FALSE for kernel-based transfer + short // movement type: 1 = SMS, 2 = CRW + ); + ~Individual(void); + void setGenes( // Set genes for individual variation from species initialisation parameters + Species*, // pointer to Species + int // Landscape resolution + ); + void setGenes( // Inherit genome from parents + Species*, // pointer to Species + Individual*, // pointer to mother + Individual*, // pointer to father (must be 0 for an asexual Species) + int // Landscape resolution + ); + void setEmigTraits( // Set phenotypic emigration traits + Species*, // pointer to Species + short, // location of emigration genes on genome + short, // number of emigration genes + bool // TRUE if emigration is sex-dependent + ); + emigTraits getEmigTraits(void); // Get phenotypic emigration traits + + void setKernTraits( // Set phenotypic transfer by kernel traits + Species*, // pointer to Species + short, // location of kernel genes on genome + short, // number of kernel genes + int, // Landscape resolution + bool // TRUE if transfer is sex-dependent + ); + trfrKernTraits getKernTraits(void); // Get phenotypic transfer by kernel traits + + void setSMSTraits( // Set phenotypic transfer by SMS traits + Species*, // pointer to Species + short, // location of SMS genes on genome + short, // number of SMS genes + bool // TRUE if transfer is sex-dependent + ); + trfrSMSTraits getSMSTraits(void); // Get phenotypic transfer by SMS traits + void setCRWTraits( // Set phenotypic transfer by CRW traits + Species*, // pointer to Species + short, // location of CRW genes on genome + short, // number of CRW genes + bool // TRUE if transfer is sex-dependent + ); + trfrCRWTraits getCRWTraits(void); // Get phenotypic transfer by CRW traits + + void setSettTraits( // Set phenotypic settlement traits + Species*, // pointer to Species + short, // location of settlement genes on genome + short, // number of settlement genes + bool // TRUE if settlement is sex-dependent + ); + settleTraits getSettTraits(void); // Get phenotypic settlement traits + + // Identify whether an individual is a potentially breeding female - + // if so, return her stage, otherwise return 0 + int breedingFem(void); + int getId(void); + int getSex(void); + int getStatus(void); + indStats getStats(void); + Cell* getLocn( // Return location (as pointer to Cell) + const short // option: 0 = get natal locn, 1 = get current locn + ); // + Patch* getNatalPatch(void); + void setYearSteps(int); + pathSteps getSteps(void); + settlePatch getSettPatch(void); + void setSettPatch(const settlePatch); + void setStatus(short); + void developing(void); + void develop(void); + void ageIncrement( // Age by one year + short // maximum age - if exceeded, the Individual dies + ); + void incFallow(void); // Inrement no. of reproductive seasons since last reproduction + void resetFallow(void); + void moveto( // Move to a specified neighbouring cell + Cell* // pointer to the new cell + ); + // Move to a new cell by sampling a dispersal distance from a single or double + // negative exponential kernel + // Returns 1 if still dispersing (including having found a potential patch), otherwise 0 + int moveKernel( + Landscape*, // pointer to Landscape + Species*, // pointer to Species + const short, // reproduction type (see Species) + const bool // absorbing boundaries? + ); + // Make a single movement step according to a mechanistic movement model + // Returns 1 if still dispersing (including having found a potential patch), otherwise 0 + int moveStep( + Landscape*, // pointer to Landscape + Species*, // pointer to Species + const short, // landscape change index + const bool // absorbing boundaries? + ); + movedata smsMove( // Move to a neighbouring cell according to the SMS algorithm + Landscape*, // pointer to Landscape + Species*, // pointer to Species + const short, // landscape change index + const bool, // TRUE if still in (or returned to) natal patch + const bool, // individual variability? + const bool // absorbing boundaries? + ); + array3x3d getSimDir( // Weight neighbouring cells on basis of current movement direction + const int, // current x co-ordinate + const int, // current y co-ordinate + const float // directional persistence value + ); + array3x3d getGoalBias( // Weight neighbouring cells on basis of goal bias + const int, // current x co-ordinate + const int, // current y co-ordinate + const int, // goal type: 0 = none, 1 = towards goal (NOT IMPLEMENTED), 2 = dispersal bias + const float // GOAL BIAS VALUE + ); + array3x3d calcWeightings( // Calculate weightings for neighbouring cells + const double, // base for power-law (directional persistence or goal bias value) + const double // direction in which lowest (unit) weighting is to be applied + ); + array3x3f getHabMatrix( // Weight neighbouring cells on basis of (habitat) costs + Landscape*, // pointer to Landscape + Species*, // pointer to Species + const int, // current x co-ordinate + const int, // current y co-ordinate + const short, // perceptual range (cells) + const short, // perceptual range evaluation method (see Species) + const short, // landscape change index + const bool // absorbing boundaries? + ); + void outGenetics( // Write records to genetics file + const int, // replicate + const int, // year + const int, // species number + const int, // landscape number + const bool // output as cross table? + ); +#if RS_RCPP + void outMovePath( // Write records to movement paths file + const int // year + ); +#endif + +private: + int indId; + short stage; + short sex; + short age; + short status; // 0 = initial status in natal patch / philopatric recruit + // 1 = disperser + // 2 = disperser awaiting settlement in possible suitable patch + // 3 = waiting between dispersal events + // 4 = completed settlement + // 5 = completed settlement in a suitable neighbouring cell + // 6 = died during transfer by failing to find a suitable patch + // (includes exceeding maximum number of steps or crossing + // absorbing boundary) + // 7 = died during transfer by constant, step-dependent, + // habitat-dependent or distance-dependent mortality + // 8 = failed to survive annual (demographic) mortality + // 9 = exceeded maximum age + short fallow; // reproductive seasons since last reproduction + bool isDeveloping; + Cell *pPrevCell; // pointer to previous Cell + Cell *pCurrCell; // pointer to current Cell + Patch *pNatalPatch; // pointer to natal Patch + emigTraits *emigtraits; // pointer to emigration traits + trfrKernTraits *kerntraits; // pointers to transfer by kernel traits + pathData *path; // pointer to path data for movement model + crwParams *crw; // pointer to CRW traits and data + smsdata *smsData; // pointer to variables required for SMS + settleTraits *setttraits; // pointer to settlement traits + std::queue memory; // memory of last N squares visited for SMS + + Genome *pGenome; + +}; + + +//--------------------------------------------------------------------------- + +double cauchy(double location, double scale) ; +double wrpcauchy (double location, double rho = exp(double(-1))); + +extern RSrandom *pRandom; + +#if RSDEBUG +extern ofstream DEBUGLOG; +#endif + +#if RS_RCPP +extern ofstream outMovePaths; +#endif + +#if RSDEBUG +void testIndividual(); +#endif + +//--------------------------------------------------------------------------- +#endif // IndividualH diff --git a/src/RScore/LICENSE b/src/RScore/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/src/RScore/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/RScore/Landscape.cpp b/src/RScore/Landscape.cpp new file mode 100644 index 00000000..fe1e9d9f --- /dev/null +++ b/src/RScore/Landscape.cpp @@ -0,0 +1,2645 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Landscape.h" +//--------------------------------------------------------------------------- + +ifstream landscape; + +ofstream outConnMat; +ofstream outvisits; +#if RS_RCPP +ofstream outMovePaths; +#endif // RS_RCPP + +//--------------------------------------------------------------------------- + +// Initial species distribution functions + +InitDist::InitDist(Species* pSp) +{ + pSpecies = pSp; + resol = 0; + maxX = 0; + maxY = 0; + minEast = 0.0; + minNorth = 0.0; +} + +InitDist::~InitDist() { + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) + if (cells[i] != NULL) delete cells[i]; + cells.clear(); +} + +void InitDist::setDistribution(int nInit) { + int rr = 0; + int ncells = (int)cells.size(); + if (nInit == 0) { // set all cells to be initialised + for (int i = 0; i < ncells; i++) { + cells[i]->setCell(true); + } + } + else { // set specified number of cells at random to be initialised + if (nInit > ncells / 2) { // use backwards selection method + for (int i = 0; i < ncells; i++) cells[i]->setCell(true); + for (int i = 0; i < (ncells - nInit); i++) { + do { + rr = pRandom->IRandom(0, ncells - 1); + } while (!cells[rr]->selected()); + cells[rr]->setCell(false); + } + } + else { // use forwards selection method + for (int i = 0; i < ncells; i++) cells[i]->setCell(false); + for (int i = 0; i < nInit; i++) { + do { + rr = pRandom->IRandom(0, ncells - 1); + } while (cells[rr]->selected()); + cells[rr]->setCell(true); + } + } + } +} + +// Set a specified cell (by position in cells vector) +void InitDist::setDistCell(int ix, bool init) { + cells[ix]->setCell(init); +} + +// Set a specified cell (by co-ordinates) +void InitDist::setDistCell(locn loc, bool init) { + locn cellloc; + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + cellloc = cells[i]->getLocn(); + if (cellloc.x == loc.x && cellloc.y == loc.y) { + cells[i]->setCell(init); + i = ncells; + } + } +} + +// Specified location is within the initial distribution? +bool InitDist::inInitialDist(locn loc) { + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + if (cells[i]->toInitialise(loc)) { // cell is to be initialised + return true; + } + } + return false; +} + +int InitDist::cellCount(void) { + return (int)cells.size(); +} + +// Return the co-ordinates of a specified initial distribution cell +locn InitDist::getCell(int ix) { + locn loc; + if (ix >= 0 && ix < (int)cells.size()) { + loc = cells[ix]->getLocn(); + } + else { + loc.x = loc.y = -666; // indicates invalid index specified + } + return loc; +} + +// Return the co-ordinates of a specified initial distribution cell if it has been +// selected - otherwise return negative co-ordinates +locn InitDist::getSelectedCell(int ix) { + locn loc; loc.x = loc.y = -666; + if (ix < (int)cells.size()) { + if (cells[ix]->selected()) { + loc = cells[ix]->getLocn(); + } + } + return loc; +} + +locn InitDist::getDimensions(void) { + locn d; d.x = maxX; d.y = maxY; return d; +} + +void InitDist::resetDistribution(void) { + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + cells[i]->setCell(false); + } +} + +//--------------------------------------------------------------------------- + +// Read species initial distribution file + +int InitDist::readDistribution(string distfile) { +#if RS_RCPP + wstring header; +#else + string header; +#endif + int p, nodata; + int ncols, nrows; +#if RS_RCPP + wifstream dfile; // species distribution file input stream +#else + ifstream dfile; // species distribution file input stream +#endif + + // open distribution file +#if !RS_RCPP || RSWIN64 + dfile.open(distfile.c_str()); +#else + dfile.open(distfile, std::ios::binary); + if (spdistraster.utf) { + // apply BOM-sensitive UTF-16 facet + dfile.imbue(std::locale(dfile.getloc(), new std::codecvt_utf16)); + } +#endif + if (!dfile.is_open()) return 21; + + // read landscape data from header records of distribution file + // NB headers of all files have already been compared + dfile >> header >> ncols >> header >> nrows >> header >> minEast >> header >> minNorth + >> header >> resol >> header >> nodata; +#if RS_RCPP + if (!dfile.good()) { + // corrupt file stream + StreamErrorR(distfile); + dfile.close(); + dfile.clear(); + return 144; + } +#endif + + maxX = ncols - 1; maxY = nrows - 1; + + // set up bad integer value to ensure that valid values are read + int badvalue = -9; if (nodata == -9) badvalue = -99; + + for (int y = nrows - 1; y >= 0; y--) { + for (int x = 0; x < ncols; x++) { + p = badvalue; +#if RS_RCPP + if (dfile >> p) { +#else + dfile >> p; +#endif + if (p == nodata || p == 0 || p == 1) { // only valid values + if (p == 1) { // species present + cells.push_back(new DistCell(x, y)); + } + } + else { // error in file + dfile.close(); dfile.clear(); + return 22; + } +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(distfile); + dfile.close(); + dfile.clear(); + return 144; + } +#endif + } + } +#if RS_RCPP +dfile >> p; +if (!dfile.eof()) EOFerrorR(distfile); +#endif + + dfile.close(); dfile.clear(); + return 0; +} + + +//--------------------------------------------------------------------------- + +// Landscape functions + +Landscape::Landscape(void) { + patchModel = false; spDist = false; generated = false; fractal = false; continuous = false; + dynamic = false; habIndexed = false; + resol = spResol = landNum = 0; + rasterType = 0; + nHab = nHabMax = 0; + dimX = dimY = 100; + minX = minY = 0; + maxX = maxY = 99; + minPct = maxPct = propSuit = hurst = 0.0; + maxCells = 100; + gpix = 1.0; + pix = (int)gpix; + minEast = minNorth = 0.0; + cells = 0; + connectMatrix = 0; + epsGlobal = 0; + patchChgMatrix = 0; + costsChgMatrix = 0; +} + +Landscape::~Landscape() { + + if (cells != 0) { + for (int y = dimY - 1; y >= 0; y--) { + + for (int x = 0; x < dimX; x++) { + + if (cells[y][x] != 0) delete cells[y][x]; + } + if (cells[y] != 0) { + delete[] cells[y]; + } + } + delete[] cells; + cells = 0; + } + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) + if (patches[i] != NULL) delete patches[i]; + patches.clear(); + + int ndistns = (int)distns.size(); + for (int i = 0; i < ndistns; i++) + if (distns[i] != NULL) delete distns[i]; + distns.clear(); + + int ninitcells = (int)initcells.size(); + for (int i = 0; i < ninitcells; i++) + if (initcells[i] != NULL) delete initcells[i]; + initcells.clear(); + + patchnums.clear(); + habCodes.clear(); + colours.clear(); + landchanges.clear(); + patchchanges.clear(); + + deleteConnectMatrix(); + deletePatchChgMatrix(); + if (epsGlobal != 0) delete[] epsGlobal; + +} + +// Remove all patches and cells +// Used for replicating artificial landscape without deleting the landscape itself +void Landscape::resetLand(void) { + + resetLandLimits(); + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) if (patches[i] != NULL) delete patches[i]; + patches.clear(); + + if (cells != 0) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) delete cells[y][x]; + } + if (cells[y] != 0) { + delete[] cells[y]; + } + } + delete[] cells; + cells = 0; + } +} + +void Landscape::setLandParams(landParams ppp, bool batchMode) +{ + generated = ppp.generated; patchModel = ppp.patchModel; spDist = ppp.spDist; + dynamic = ppp.dynamic; + landNum = ppp.landNum; + if (ppp.resol > 0) resol = ppp.resol; + if (ppp.spResol > 0 && ppp.spResol % ppp.resol == 0) spResol = ppp.spResol; + if ((ppp.rasterType >= 0 && ppp.rasterType <= 2) || ppp.rasterType == 9) + rasterType = ppp.rasterType; + if (ppp.nHab >= 1) nHab = ppp.nHab; + if (ppp.nHabMax >= 1) nHabMax = ppp.nHabMax; + if (ppp.dimX > 0) dimX = ppp.dimX; + if (ppp.dimY > 0) dimY = ppp.dimY; + if (ppp.minX >= 0 && ppp.maxX >= 0 && ppp.minX <= ppp.maxX && ppp.maxX < dimX) { + minX = ppp.minX; maxX = ppp.maxX; + } + else { + minX = 0; maxX = dimX - 1; + } + if (ppp.minY >= 0 && ppp.maxY >= 0 && ppp.minY <= ppp.maxY && ppp.maxY < dimY) { + minY = ppp.minY; maxY = ppp.maxY; + } + else { + minY = 0; maxY = dimY - 1; + } + if (batchMode && rasterType == 0) { + // in batch mode, set up sequential habitat codes if not already present + if (habCodes.size() == 0) { + for (int i = 0; i < nHabMax; i++) { + habCodes.push_back(i + 1); + } + } + } +} + +landParams Landscape::getLandParams(void) +{ + landParams ppp; + ppp.generated = generated; ppp.patchModel = patchModel; ppp.spDist = spDist; + ppp.dynamic = dynamic; + ppp.landNum = landNum; + ppp.resol = resol; ppp.spResol = spResol; + ppp.rasterType = rasterType; + ppp.nHab = nHab; ppp.nHabMax = nHabMax; + ppp.dimX = dimX; ppp.dimY = dimY; + ppp.minX = minX; ppp.minY = minY; + ppp.maxX = maxX; ppp.maxY = maxY; + return ppp; +} + +landData Landscape::getLandData(void) { + landData dd; + dd.resol = resol; + dd.dimX = dimX; dd.dimY = dimY; + dd.minX = minX; dd.minY = minY; + dd.maxX = maxX; dd.maxY = maxY; + return dd; +} + +void Landscape::setGenLandParams(genLandParams ppp) +{ + fractal = ppp.fractal; + continuous = ppp.continuous; + if (ppp.minPct > 0.0 && ppp.minPct < 100.0) minPct = ppp.minPct; + if (ppp.maxPct > 0.0 && ppp.maxPct <= 100.0) maxPct = ppp.maxPct; + if (ppp.propSuit >= 0.0 && ppp.propSuit <= 1.0) propSuit = ppp.propSuit; + if (ppp.hurst > 0.0 && ppp.hurst < 1.0) hurst = ppp.hurst; + if (ppp.maxCells > 0) maxCells = ppp.maxCells; +} + +genLandParams Landscape::getGenLandParams(void) +{ + genLandParams ppp; + ppp.fractal = fractal; ppp.continuous = continuous; + ppp.minPct = minPct; ppp.maxPct = maxPct; ppp.propSuit = propSuit; ppp.hurst = hurst; + ppp.maxCells = maxCells; + return ppp; +} + +void Landscape::setLandLimits(int x0, int y0, int x1, int y1) { + if (x0 >= 0 && x1 >= 0 && x0 <= x1 && x1 < dimX + && y0 >= 0 && y1 >= 0 && y0 <= y1 && y1 < dimY) { + minX = x0; maxX = x1; minY = y0; maxY = y1; + } +} + +void Landscape::resetLandLimits(void) { + minX = minY = 0; maxX = dimX - 1; maxY = dimY - 1; +} + +//--------------------------------------------------------------------------- + +void Landscape::setLandPix(landPix p) { + if (p.pix > 0) pix = p.pix; + if (p.gpix > 0.0) gpix = p.gpix; +} + +landPix Landscape::getLandPix(void) { + landPix p; + p.pix = pix; p.gpix = gpix; + return p; +} + +void Landscape::setOrigin(landOrigin origin) { + minEast = origin.minEast; minNorth = origin.minNorth; +} + +landOrigin Landscape::getOrigin(void) { + landOrigin origin; + origin.minEast = minEast; origin.minNorth = minNorth; + return origin; +} + +//--------------------------------------------------------------------------- + +// Functions to handle habitat codes + +bool Landscape::habitatsIndexed(void) { return habIndexed; } + +void Landscape::listHabCodes(void) { + int nhab = (int)habCodes.size(); +#if RS_RCPP && !R_CMD + Rcpp::Rcout << endl; + for (int i = 0; i < nhab; i++) { + Rcpp::Rcout << "Habitat code[ " << i << "] = " << habCodes[i] << endl; + } + Rcpp::Rcout << endl; +#else + cout << endl; + for (int i = 0; i < nhab; i++) { + cout << "Habitat code[ " << i << "] = " << habCodes[i] << endl; + } + cout << endl; +#endif +} + +void Landscape::addHabCode(int hab) { + int nhab = (int)habCodes.size(); + bool addCode = true; + for (int i = 0; i < nhab; i++) { + if (hab == habCodes[i]) { + addCode = false; i = nhab + 1; + } + } + if (addCode) { habCodes.push_back(hab); nHab++; } +} + +// Get the index number of the specified habitat in the habitats vector +int Landscape::findHabCode(int hab) { + int nhab = (int)habCodes.size(); + for (int i = 0; i < nhab; i++) { + if (hab == habCodes[i]) return i; + } + return -999; +} + +// Get the specified habitat code +int Landscape::getHabCode(int ixhab) { + if (ixhab < (int)habCodes.size()) return habCodes[ixhab]; + else return -999; +} + +void Landscape::clearHabitats(void) { + habCodes.clear(); + colours.clear(); +} + +void Landscape::addColour(rgb c) { + colours.push_back(c); +} + +void Landscape::changeColour(int i, rgb col) { + int ncolours = (int)colours.size(); + if (i >= 0 && i < ncolours) { + if (col.r >= 0 && col.r <= 255 && col.g >= 0 && col.g <= 255 && col.b >= 0 && col.b <= 255) + colours[i] = col; + } +} + +rgb Landscape::getColour(int ix) { + return colours[ix]; +} + +int Landscape::colourCount(void) { + return (int)colours.size(); +} + +//--------------------------------------------------------------------------- +void Landscape::setCellArray(void) { + if (cells != 0) resetLand(); + cells = new Cell * *[dimY]; + for (int y = dimY - 1; y >= 0; y--) { + cells[y] = new Cell * [dimX]; + for (int x = 0; x < dimX; x++) { + cells[y][x] = 0; + } + } +} + +void Landscape::addPatchNum(int p) { + int npatches = (int)patchnums.size(); + bool addpatch = true; + for (int i = 0; i < npatches; i++) { + if (p == patchnums[i]) { + addpatch = false; i = npatches + 1; + } + } + if (addpatch) patchnums.push_back(p); +} + + +//--------------------------------------------------------------------------- +/* Create an artificial landscape (random or fractal), which can be +either binary (habitat index 0 is the matrix, 1 is suitable habitat) +or continuous (0 is the matrix, >0 is suitable habitat) */ +void Landscape::generatePatches(void) +{ + int x, y, ncells; + double p; + Patch* pPatch; + Cell* pCell; + + vector ArtLandscape; + + setCellArray(); + + int patchnum = 0; // initial patch number for cell-based landscape + // create patch 0 - the matrix patch (even if there is no matrix) + newPatch(patchnum++); + + // as landscape generator returns cells in a random sequence, first set up all cells + // in the landscape in the correct sequence, then update them and create patches for + // habitat cells + for (int yy = dimY - 1; yy >= 0; yy--) { + for (int xx = 0; xx < dimX; xx++) { + addNewCellToLand(xx, yy, 0); + } + } + + if (continuous) rasterType = 2; + else rasterType = 0; + if (fractal) { + p = 1.0 - propSuit; + // fractal_landscape() requires Max_prop > 1 (but does not check it!) + // as in turn it calls runif(1.0,Max_prop) + double maxpct; + if (maxPct < 1.0) maxpct = 100.0; else maxpct = maxPct; + + ArtLandscape = fractal_landscape(dimY, dimX, hurst, p, maxpct, minPct); + + vector::iterator iter = ArtLandscape.begin(); + while (iter != ArtLandscape.end()) { + x = iter->y_coord; y = iter->x_coord; + pCell = findCell(x, y); + if (continuous) { + if (iter->value > 0.0) { // habitat + pPatch = newPatch(patchnum++); + addCellToPatch(pCell, pPatch, iter->value); + } + else { // matrix + addCellToPatch(pCell, patches[0], iter->value); + } + } + else { // discrete + if (iter->avail == 0) { // matrix + addCellToPatch(pCell, patches[0]); + } + else { // habitat + pPatch = newPatch(patchnum++); + addCellToPatch(pCell, pPatch); + pCell->changeHabIndex(0, 1); + } + } + iter++; + } + } + else { // random landscape + int hab = 0; + ncells = (int)((float)(dimX) * (float)(dimY)*propSuit + 0.00001); // no. of cells to initialise + int i = 0; + do { + do { + x = pRandom->IRandom(0, dimX - 1); y = pRandom->IRandom(0, dimY - 1); + pCell = findCell(x, y); + hab = pCell->getHabIndex(0); + } while (hab > 0); + pPatch = newPatch(patchnum++); + pCell = findCell(x, y); + addCellToPatch(pCell, pPatch); + pCell->changeHabIndex(0, 1); + if (continuous) { + pCell->setHabitat((float)(minPct + pRandom->Random() * (maxPct - minPct))); + } + i++; + } while (i < ncells); + // remaining cells need to be added to the matrix patch + p = 0.0; + x = 0; + for (int yy = dimY - 1; yy >= 0; yy--) { + for (int xx = 0; xx < dimX; xx++) { + pCell = findCell(xx, yy); + if (continuous) { + if (pCell->getHabitat(0) <= 0.0) + { + addCellToPatch(pCell, patches[0], (float)p); + } + } + else { // discrete + if (pCell->getHabIndex(0) == 0) { + addCellToPatch(pCell, patches[0], x); + } + } + } + } + } +} + +//--------------------------------------------------------------------------- + +// Landscape patch-management functions + +//--------------------------------------------------------------------------- +/* Create a patch for each suitable cell of a cell-based landscape (all other +habitat cells are added to the matrix patch) */ +void Landscape::allocatePatches(Species* pSpecies) +{ + float habK; + Patch* pPatch; + Cell* pCell; + + // delete all existing patches + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + if (patches[i] != NULL) delete patches[i]; + } + patches.clear(); + // create the matrix patch + patches.push_back(new Patch(0, 0)); + Patch* matrixPatch = patches[0]; + int patchnum = 1; + + switch (rasterType) { + + case 0: // habitat codes + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not no-data cell + pCell = cells[y][x]; + habK = 0.0; + int nhab = pCell->nHabitats(); + for (int i = 0; i < nhab; i++) { + habK += pSpecies->getHabK(pCell->getHabIndex(i)); + } + if (habK > 0.0) { // cell is suitable - create a patch for it + pPatch = newPatch(patchnum++); + addCellToPatch(pCell, pPatch); + } + else { // cell is not suitable - add to the matrix patch + addCellToPatch(pCell, matrixPatch); + pPatch = 0; + } + } + } + } + break; + case 1: // habitat cover + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not no-data cell + pCell = cells[y][x]; + habK = 0.0; + int nhab = pCell->nHabitats(); + for (int i = 0; i < nhab; i++) + { + habK += pSpecies->getHabK(i) * pCell->getHabitat(i) / 100.0f; + } + if (habK > 0.0) { // cell is suitable - create a patch for it + pPatch = newPatch(patchnum++); + addCellToPatch(pCell, pPatch); + } + else { // cell is not suitable - add to the matrix patch + addCellToPatch(pCell, matrixPatch); + pPatch = 0; + } + } + } + } + break; + case 2: // habitat quality + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + + if (cells[y][x] != 0) { // not no-data cell + pCell = cells[y][x]; + habK = 0.0; + int nhab = pCell->nHabitats(); + // for (int i = 0; i < nHab; i++) + for (int i = 0; i < nhab; i++) + { + habK += pSpecies->getHabK(0) * pCell->getHabitat(i) / 100.0f; + } + if (habK > 0.0) { // cell is suitable (at some time) - create a patch for it + pPatch = newPatch(patchnum++); + addCellToPatch(pCell, pPatch); + } + else { // cell is never suitable - add to the matrix patch + addCellToPatch(pCell, matrixPatch); + pPatch = 0; + } + } + } + } + break; + + } // end of switch (rasterType) +} + +Patch* Landscape::newPatch(int num) +{ + int npatches = (int)patches.size(); + patches.push_back(new Patch(num, num)); + return patches[npatches]; +} + +Patch* Landscape::newPatch(int seqnum, int num) +{ + int npatches = (int)patches.size(); + patches.push_back(new Patch(seqnum, num)); + return patches[npatches]; +} + +void Landscape::resetPatches(void) { + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + patches[i]->resetLimits(); + } +} + +void Landscape::addNewCellToLand(int x, int y, float q) { + if (q < 0.0) // no-data cell - no Cell created + cells[y][x] = 0; + else + cells[y][x] = new Cell(x, y, 0, q); +} + +void Landscape::addNewCellToLand(int x, int y, int hab) { + if (hab < 0) // no-data cell - no Cell created + cells[y][x] = 0; + else + cells[y][x] = new Cell(x, y, 0, hab); +} + +void Landscape::addNewCellToPatch(Patch* pPatch, int x, int y, float q) { + if (q < 0.0) { // no-data cell - no Cell created + cells[y][x] = 0; + } + else { // create the new cell + cells[y][x] = new Cell(x, y, (intptr)pPatch, q); + if (pPatch != 0) { // not the matrix patch + // add the cell to the patch + pPatch->addCell(cells[y][x], x, y); + } + } +} + +void Landscape::addNewCellToPatch(Patch* pPatch, int x, int y, int hab) { + if (hab < 0) // no-data cell - no Cell created + cells[y][x] = 0; + else { // create the new cell + cells[y][x] = new Cell(x, y, (intptr)pPatch, hab); + if (pPatch != 0) { // not the matrix patch + // add the cell to the patch + pPatch->addCell(cells[y][x], x, y); + } + } +} + +void Landscape::addCellToPatch(Cell* pCell, Patch* pPatch) { + pCell->setPatch((intptr)pPatch); + locn loc = pCell->getLocn(); + // add the cell to the patch + pPatch->addCell(pCell, loc.x, loc.y); +} + +void Landscape::addCellToPatch(Cell* pCell, Patch* pPatch, float q) { + pCell->setPatch((intptr)pPatch); + // update the habitat type of the cell + pCell->setHabitat(q); + locn loc = pCell->getLocn(); + // add the cell to the patch + pPatch->addCell(pCell, loc.x, loc.y); +} + +void Landscape::addCellToPatch(Cell* pCell, Patch* pPatch, int hab) { + pCell->setPatch((intptr)pPatch); + // update the habitat type of the cell + pCell->setHabIndex(hab); + locn loc = pCell->getLocn(); + // add the cell to the patch + pPatch->addCell(pCell, loc.x, loc.y); +} + +patchData Landscape::getPatchData(int ix) { + patchData ppp; + ppp.pPatch = patches[ix]; ppp.patchNum = patches[ix]->getPatchNum(); + ppp.nCells = patches[ix]->getNCells(); + locn randloc; randloc.x = -666; randloc.y = -666; + Cell* pCell = patches[ix]->getRandomCell(); + if (pCell != 0) { + randloc = pCell->getLocn(); + } + ppp.x = randloc.x; ppp.y = randloc.y; + return ppp; +} + +bool Landscape::existsPatch(int num) { + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + if (num == patches[i]->getPatchNum()) return true; + } + return false; +} + +Patch* Landscape::findPatch(int num) { + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + if (num == patches[i]->getPatchNum()) return patches[i]; + } + return 0; +} + +void Landscape::resetPatchPopns(void) { + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + patches[i]->resetPopn(); + } +} + +void Landscape::updateCarryingCapacity(Species* pSpecies, int yr, short landIx) { + envGradParams grad = paramsGrad->getGradient(); + bool gradK = false; + if (grad.gradient && grad.gradType == 1) gradK = true; // gradient in carrying capacity + patchLimits landlimits; + landlimits.xMin = minX; landlimits.xMax = maxX; + landlimits.yMin = minY; landlimits.yMax = maxY; + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + if (patches[i]->getPatchNum() != 0) { // not matrix patch + patches[i]->setCarryingCapacity(pSpecies, landlimits, + getGlobalStoch(yr), nHab, rasterType, landIx, gradK); + } + } + +} + +Cell* Landscape::findCell(int x, int y) { + if (x >= 0 && x < dimX && y >= 0 && y < dimY) return cells[y][x]; + else return 0; +} + +int Landscape::patchCount(void) { + return (int)patches.size(); +} + +void Landscape::listPatches(void) { + patchLimits p; + int npatches = (int)patches.size(); +#if RS_RCPP && !R_CMD + Rcpp::Rcout << endl; + for (int i = 0; i < npatches; i++) { + p = patches[i]->getLimits(); + Rcpp::Rcout << "Patch " << patches[i]->getPatchNum() + << " xMin = " << p.xMin << " xMax = " << p.xMax + << " \tyMin = " << p.yMin << " yMax = " << p.yMax + << endl; + } + Rcpp::Rcout << endl; +#else + cout << endl; + for (int i = 0; i < npatches; i++) { + p = patches[i]->getLimits(); + cout << "Patch " << patches[i]->getPatchNum() + << " xMin = " << p.xMin << " xMax = " << p.xMax + << " \tyMin = " << p.yMin << " yMax = " << p.yMax + << endl; + } + cout << endl; +#endif +} + +// Check that total cover of any cell does not exceed 100% +// and identify matrix cells +int Landscape::checkTotalCover(void) { + if (rasterType != 1) return 0; // not appropriate test + int nCells = 0; + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) + { // not a no-data cell + float sumCover = 0.0; + for (int i = 0; i < nHab; i++) { + sumCover += cells[y][x]->getHabitat(i); + } + if (sumCover > 100.00001) nCells++; // decimal part to allow for floating point error + if (sumCover <= 0.0) // cell is a matrix cell + cells[y][x]->setHabIndex(0); + else + cells[y][x]->setHabIndex(1); + } + } + } + return nCells; +} + +// Convert habitat codes stored on loading habitat codes landscape to +// sequential sorted index numbers +void Landscape::updateHabitatIndices(void) { + // sort codes + sort(habCodes.begin(), habCodes.end()); + nHab = (int)habCodes.size(); + // convert codes in landscape + int h; + int changes = (int)landchanges.size(); + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not a no-data cell + for (int c = 0; c <= changes; c++) { + h = cells[y][x]->getHabIndex(c); + + if (h >= 0) { + h = findHabCode(h); + + cells[y][x]->changeHabIndex(c, h); + } + } + } + } + } + habIndexed = true; +} + +void Landscape::setEnvGradient(Species* pSpecies, bool initial) +{ + float dist_from_opt, dev; + float habK; + double envval; + // gradient parameters + envGradParams grad = paramsGrad->getGradient(); + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + // NB: gradient lies in range 0-1 for all types, and is applied when necessary... + // ... implies gradient increment will be dimensionless in range 0-1 (but << 1) + if (cells[y][x] != 0) { // not no-data cell + habK = 0.0; + int nhab = cells[y][x]->nHabitats(); + for (int i = 0; i < nhab; i++) { + switch (rasterType) { + case 0: + habK += pSpecies->getHabK(cells[y][x]->getHabIndex(i)); + break; + case 1: + habK += pSpecies->getHabK(i) * cells[y][x]->getHabitat(i) / 100.0f; + break; + case 2: + habK += pSpecies->getHabK(0) * cells[y][x]->getHabitat(i) / 100.0f; + break; + } + } + + if (habK > 0.0) { // suitable cell + if (initial) { // set local environmental deviation + cells[y][x]->setEnvDev((float)pRandom->Random() * (2.0f) - 1.0f); + } + dist_from_opt = (float)(fabs((double)grad.opt_y - (double)y)); + dev = cells[y][x]->getEnvDev(); + envval = 1.0 - dist_from_opt * grad.grad_inc + dev * grad.factor; + if (envval < 0.000001) envval = 0.0; + if (envval > 1.0) envval = 1.0; + } + else envval = 0.0; + cells[y][x]->setEnvVal((float)envval); + } + } + } + +} + +void Landscape::setGlobalStoch(int nyears) { + envStochParams env = paramsStoch->getStoch(); + if (epsGlobal != 0) delete[] epsGlobal; + epsGlobal = new float[nyears]; + epsGlobal[0] = (float)(pRandom->Normal(0.0, env.std) * sqrt(1.0 - (env.ac * env.ac))); + for (int i = 1; i < nyears; i++) { + epsGlobal[i] = (float)(env.ac * epsGlobal[i - 1] + pRandom->Normal(0.0, env.std) * sqrt(1.0 - (env.ac * env.ac))); + } +} + +float Landscape::getGlobalStoch(int yr) { + if (epsGlobal != 0 && yr >= 0) { + return epsGlobal[yr]; + } + else return 0.0; +} + +void Landscape::updateLocalStoch(void) { + envStochParams env = paramsStoch->getStoch(); + float randpart; + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not a no-data cell + randpart = (float)(pRandom->Normal(0.0, env.std) * sqrt(1.0 - (env.ac * env.ac))); + cells[y][x]->updateEps((float)env.ac, randpart); + } + } + } + +} + +void Landscape::resetCosts(void) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not a no-data cell + cells[y][x]->resetCost(); + } + } + } +} + +void Landscape::resetEffCosts(void) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not a no-data cell + cells[y][x]->resetEffCosts(); + } + } + } +} + +//--------------------------------------------------------------------------- + +// Dynamic landscape functions + +void Landscape::setDynamicLand(bool dyn) { dynamic = dyn; } + +void Landscape::addLandChange(landChange c) { + landchanges.push_back(c); +} + +int Landscape::numLandChanges(void) { return (int)landchanges.size(); } + +landChange Landscape::getLandChange(short ix) { + landChange c; c.chgnum = c.chgyear = 0; + c.habfile = c.pchfile = c.costfile = "none"; + int nchanges = (int)landchanges.size(); + if (ix < nchanges) c = landchanges[ix]; + return c; +} + +void Landscape::deleteLandChanges(void) { + while (landchanges.size() > 0) landchanges.pop_back(); + landchanges.clear(); +} + +#if RS_RCPP && !R_CMD +int Landscape::readLandChange(int filenum, bool costs, wifstream& hfile, wifstream& pfile, wifstream& cfile, int habnodata, int pchnodata, int costnodata) +#else +int Landscape::readLandChange(int filenum, bool costs) +#endif +{ + +#if RSDEBUG + DEBUGLOG << "Landscape::readLandChange(): filenum=" << filenum << " costs=" << int(costs) + << endl; +#endif + +#if RS_RCPP + wstring header; +#else + string header; + int ncols, nrows, habnodata, costnodata, pchnodata; + costnodata = 0; + pchnodata = 0; +#endif + int h = 0, p = 0, c = 0, pchseq = 0; + float hfloat, pfloat, cfloat; + simParams sim = paramsSim->getSim(); + + if (filenum < 0) return 19; + if (patchModel) pchseq = patchCount(); + +#if !RS_RCPP + ifstream hfile; // habitat file input stream + ifstream pfile; // patch file input stream + ifstream cfile; // costs file input stream +#endif + +#if !RS_RCPP || R_CMD + // open habitat file and optionally also patch and costs files + hfile.open(landchanges[filenum].habfile.c_str()); + if (!hfile.is_open()) return 30; + if (patchModel) { + pfile.open(landchanges[filenum].pchfile.c_str()); + if (!pfile.is_open()) { + hfile.close(); hfile.clear(); + return 31; + } + } + if (costs) { + cfile.open(landchanges[filenum].costfile.c_str()); + if (!cfile.is_open()) { + hfile.close(); hfile.clear(); + if (pfile.is_open()) { + pfile.close(); pfile.clear(); + } + return 32; + } + } + + // read header records of habitat (and patch) file(s) + // NB headers of all files have already been compared + hfile >> header >> ncols >> header >> nrows >> header >> hfloat >> header >> hfloat + >> header >> hfloat >> header >> habnodata; + if (patchModel) { + for (int i = 0; i < 5; i++) pfile >> header >> pfloat; + pfile >> header >> pchnodata; + } + if (costs) { + for (int i = 0; i < 5; i++) cfile >> header >> cfloat; + cfile >> header >> costnodata; + } +#endif + + // set up bad float values to ensure that valid values are read + float badhfloat = -9.0; if (habnodata == -9) badhfloat = -99.0; + float badpfloat = -9.0; if (pchnodata == -9) badpfloat = -99.0; + float badcfloat = -9.0; if (costnodata == -9) badcfloat = -99.0; + + switch (rasterType) { + + case 0: // raster with habitat codes - 100% habitat each cell + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + hfloat = badhfloat; +#if RS_RCPP + if (hfile >> hfloat) { +#else + hfile >> hfloat; +#endif + h = (int)hfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("habitatchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 171; + } +#endif + if (patchModel) { + pfloat = badpfloat; +#if RS_RCPP + if (pfile >> pfloat) { +#else + pfile >> pfloat; +#endif + p = (int)pfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("patchchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 172; + } +#endif + } + if (costs) { + cfloat = badcfloat; +#if RS_RCPP + if (cfile >> cfloat) { +#else + cfile >> cfloat; +#endif + c = (int)cfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("costchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 173; + } +#endif + } + if (cells[y][x] != 0) { // not a no data cell (in initial landscape) + if (h == habnodata) { // invalid no data cell in change map + hfile.close(); hfile.clear(); + return 36; + } + else { + if (h < 0 || (sim.batchMode && (h < 1 || h > nHabMax))) { + // invalid habitat code + hfile.close(); hfile.clear(); + if (patchModel) { pfile.close(); pfile.clear(); } + return 33; + } + else { + addHabCode(h); + cells[y][x]->setHabIndex(h); + } + } + if (patchModel) { + if (p < 0 || p == pchnodata) { // invalid patch code +#if RS_RCPP && !R_CMD + if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + else Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; +#endif + hfile.close(); hfile.clear(); + pfile.close(); pfile.clear(); + return 34; + } + else { + patchChgMatrix[y][x][2] = p; + if (p > 0 && !existsPatch(p)) { + addPatchNum(p); + newPatch(pchseq++, p); + } + } + } + if (costs) { + if (c < 1) { // invalid cost + hfile.close(); hfile.clear(); + if (pfile.is_open()) { + pfile.close(); pfile.clear(); + } + return 38; + } + else { + costsChgMatrix[y][x][2] = c; + } + } + } + } + } +#if RS_RCPP + hfile >> hfloat; + if (!hfile.eof()) EOFerrorR("habitatchgfile"); + if (patchModel) + { + pfile >> pfloat; + if (!pfile.eof()) EOFerrorR("patchchgfile"); + } + if (costs) + { + cfile >> cfloat; + if (!cfile.eof()) EOFerrorR("costchgfile"); + } +#endif + break; + + case 2: // habitat quality + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + hfloat = badhfloat; +#if RS_RCPP + if (hfile >> hfloat) { +#else + hfile >> hfloat; +#endif + h = (int)hfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("habitatchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 172; + } +#endif + if (patchModel) { + pfloat = badpfloat; +#if RS_RCPP + if (pfile >> pfloat) { +#else + pfile >> pfloat; +#endif + p = (int)pfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("patchchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 175; + } +#endif + } + if (costs) { + cfloat = badcfloat; +#if RS_RCPP + if (cfile >> cfloat) { +#else + cfile >> cfloat; +#endif + c = (int)cfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR("costchgfile"); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 173; + } +#endif + } + if (cells[y][x] != 0) { // not a no data cell (in initial landscape) + if (h == habnodata) { // invalid no data cell in change map + hfile.close(); hfile.clear(); + if (patchModel) { pfile.close(); pfile.clear(); } + return 36; + } + else { + if (hfloat < 0.0 || hfloat > 100.0) { // invalid quality score + hfile.close(); hfile.clear(); + if (patchModel) { pfile.close(); pfile.clear(); } + return 37; + } + else { + cells[y][x]->setHabitat(hfloat); + } + } + if (patchModel) { + if (p < 0 || p == pchnodata) { // invalid patch code +#if RS_RCPP && !R_CMD + if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + else Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; +#endif + hfile.close(); hfile.clear(); + pfile.close(); pfile.clear(); + return 34; + } + else { + patchChgMatrix[y][x][2] = p; + if (p > 0 && !existsPatch(p)) { + addPatchNum(p); + newPatch(pchseq++, p); + } + } + } + if (costs) { + if (c < 1) { // invalid cost + hfile.close(); hfile.clear(); + if (pfile.is_open()) { + pfile.close(); pfile.clear(); + } + return 38; + } + else { + costsChgMatrix[y][x][2] = c; + } + } + } + } + } +#if RS_RCPP + hfile >> hfloat; + if (!hfile.eof()) EOFerrorR("habitatchgfile"); + if (patchModel) + { + pfile >> pfloat; + if (!pfile.eof()) EOFerrorR("patchchgfile"); + } + if (costs) + { + cfile >> cfloat; + if (!cfile.eof()) EOFerrorR("costchgfile"); + } +#endif + break; + +default: + break; + } + + if (hfile.is_open()) { hfile.close(); hfile.clear(); } + if (pfile.is_open()) { pfile.close(); pfile.clear(); } + if (cfile.is_open()) { cfile.close(); cfile.clear(); } + return 0; + +} + +// Create & initialise patch change matrix +void Landscape::createPatchChgMatrix(void) +{ + intptr patch; + Patch* pPatch; + Cell* pCell; + if (patchChgMatrix != 0) deletePatchChgMatrix(); + patchChgMatrix = new int** [dimY]; + for (int y = dimY - 1; y >= 0; y--) { + patchChgMatrix[y] = new int* [dimX]; + for (int x = 0; x < dimX; x++) { + patchChgMatrix[y][x] = new int[3]; + pCell = findCell(x, y); + if (pCell == 0) { // no-data cell + patchChgMatrix[y][x][0] = patchChgMatrix[y][x][1] = 0; + } + else { + // record initial patch number + patch = pCell->getPatch(); + if (patch == 0) { // matrix cell + patchChgMatrix[y][x][0] = patchChgMatrix[y][x][1] = 0; + } + else { + pPatch = (Patch*)patch; + patchChgMatrix[y][x][0] = patchChgMatrix[y][x][1] = pPatch->getPatchNum(); + } + } + patchChgMatrix[y][x][2] = 0; + } + } +} + +void Landscape::recordPatchChanges(int landIx) { + if (patchChgMatrix == 0) return; // should not occur + patchChange chg; + + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (landIx == 0) { // reset to original landscape + if (patchChgMatrix[y][x][0] != patchChgMatrix[y][x][2]) { + // record change of patch for current cell + chg.chgnum = 666666; chg.x = x; chg.y = y; + chg.oldpatch = patchChgMatrix[y][x][2]; + chg.newpatch = patchChgMatrix[y][x][0]; + patchchanges.push_back(chg); + } + } + else { // any other change + if (patchChgMatrix[y][x][2] != patchChgMatrix[y][x][1]) { + // record change of patch for current cell + chg.chgnum = landIx; chg.x = x; chg.y = y; + chg.oldpatch = patchChgMatrix[y][x][1]; + chg.newpatch = patchChgMatrix[y][x][2]; + patchchanges.push_back(chg); + } + } + // reset cell for next landscape change + patchChgMatrix[y][x][1] = patchChgMatrix[y][x][2]; + } + } + +} + +int Landscape::numPatchChanges(void) { return (int)patchchanges.size(); } + +patchChange Landscape::getPatchChange(int i) { + patchChange c; c.chgnum = 99999999; c.x = c.y = c.oldpatch = c.newpatch = -1; + if (i >= 0 && i < (int)patchchanges.size()) c = patchchanges[i]; + return c; +} + +void Landscape::deletePatchChgMatrix(void) { + if (patchChgMatrix != 0) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + delete[] patchChgMatrix[y][x]; + } + delete[] patchChgMatrix[y]; + } + } + patchChgMatrix = 0; +} + +// Create & initialise costs change matrix +void Landscape::createCostsChgMatrix(void) +{ + Cell* pCell; + if (costsChgMatrix != 0) deleteCostsChgMatrix(); + costsChgMatrix = new int** [dimY]; + for (int y = dimY - 1; y >= 0; y--) { + costsChgMatrix[y] = new int* [dimX]; + for (int x = 0; x < dimX; x++) { + costsChgMatrix[y][x] = new int[3]; + pCell = findCell(x, y); + if (pCell == 0) { // no-data cell + costsChgMatrix[y][x][0] = costsChgMatrix[y][x][1] = 0; + } + else { + // record initial cost + costsChgMatrix[y][x][0] = costsChgMatrix[y][x][1] = pCell->getCost(); + } + costsChgMatrix[y][x][2] = 0; + } + } +} + +void Landscape::recordCostChanges(int landIx) { +#if RSDEBUG + DEBUGLOG << "Landscape::recordCostChanges(): landIx=" << landIx << endl; +#endif + if (costsChgMatrix == 0) return; // should not occur + costChange chg; + + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (landIx == 0) { // reset to original landscape + if (costsChgMatrix[y][x][0] != costsChgMatrix[y][x][2]) { + // record change of cost for current cell + chg.chgnum = 666666; chg.x = x; chg.y = y; + chg.oldcost = costsChgMatrix[y][x][2]; + chg.newcost = costsChgMatrix[y][x][0]; + costschanges.push_back(chg); + } + } + else { // any other change + if (costsChgMatrix[y][x][2] != costsChgMatrix[y][x][1]) { + // record change of cost for current cell + chg.chgnum = landIx; chg.x = x; chg.y = y; + chg.oldcost = costsChgMatrix[y][x][1]; + chg.newcost = costsChgMatrix[y][x][2]; + costschanges.push_back(chg); + } + } + // reset cell for next landscape change + costsChgMatrix[y][x][1] = costsChgMatrix[y][x][2]; + } + } + +} + +int Landscape::numCostChanges(void) { return (int)costschanges.size(); } + +costChange Landscape::getCostChange(int i) { + costChange c; c.chgnum = 99999999; c.x = c.y = c.oldcost = c.newcost = -1; + if (i >= 0 && i < (int)costschanges.size()) c = costschanges[i]; + return c; +} + +void Landscape::deleteCostsChgMatrix(void) { + if (costsChgMatrix != 0) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + delete[] costsChgMatrix[y][x]; + } + delete[] costsChgMatrix[y]; + } + } + costsChgMatrix = 0; +} + +//--------------------------------------------------------------------------- + +// Species distribution functions + +int Landscape::newDistribution(Species* pSpecies, string distname) { + int readcode; + int ndistns = (int)distns.size(); + distns.push_back(new InitDist(pSpecies)); + readcode = distns[ndistns]->readDistribution(distname); + if (readcode != 0) { // error encountered + // delete the distribution created above + delete distns[ndistns]; + distns.pop_back(); + } + return readcode; +} + +void Landscape::setDistribution(Species* pSpecies, int nInit) { + // WILL NEED TO SELECT DISTRIBUTION FOR CORRECT SPECIES ... + // ... CURRENTLY IT IS THE ONLY ONE + distns[0]->setDistribution(nInit); +} + +// Specified cell match one of the distribution cells to be initialised? +bool Landscape::inInitialDist(Species* pSpecies, locn loc) { + // convert landscape co-ordinates to distribution co-ordinates + locn initloc; + initloc.x = loc.x * resol / spResol; + initloc.y = loc.y * resol / spResol; + // WILL HAVE TO GET CORRECT SPECIES WHEN THERE ARE MULTIPLE SPECIES ... + bool initialise = distns[0]->inInitialDist(initloc); + return initialise; +} + +void Landscape::deleteDistribution(Species* pSpecies) { + // WILL NEED TO SELECT DISTRIBUTION FOR CORRECT SPECIES ... + // ... CURRENTLY IT IS THE ONLY ONE ... + // ... FOR MULTIPLE SPECIES IT MAY BE BETTER TO USE A DYNAMIC ARRAY FOR + // SPECIES DISTRIBUTIONS INDEXED BY SPECIES NUMBER, RATHER THAN A VECTOR + if (distns[0] != 0) delete distns[0]; distns.clear(); +} + +// Return no. of initial distributions +int Landscape::distnCount(void) { + return (int)distns.size(); +} + +int Landscape::distCellCount(int dist) { + return distns[dist]->cellCount(); +} + +// Set a cell in a specified initial distribution (by position in cells vector) +void Landscape::setDistnCell(int dist, int ix, bool init) { + distns[dist]->setDistCell(ix, init); +} + +// Set a cell in a specified initial distribution (by given co-ordinates) +void Landscape::setDistnCell(int dist, locn loc, bool init) { + distns[dist]->setDistCell(loc, init); +} + +// Get the co-ordinates of a specified cell in a specified initial distribution +locn Landscape::getDistnCell(int dist, int ix) { + return distns[dist]->getCell(ix); +} + +// Get the co-ordinates of a specified cell in a specified initial distribution +// Returns negative co-ordinates if the cell is not selected +locn Landscape::getSelectedDistnCell(int dist, int ix) { + return distns[dist]->getSelectedCell(ix); +} + +// Get the dimensions of a specified initial distribution +locn Landscape::getDistnDimensions(int dist) { + return distns[dist]->getDimensions(); +} + +// Reset the distribution for a given species so that all cells are deselected +void Landscape::resetDistribution(Species* pSp) { + // CURRENTLY WORKS FOR FIRST SPECIES ONLY ... + distns[0]->resetDistribution(); +} + +//--------------------------------------------------------------------------- + +// Initialisation cell functions + +int Landscape::initCellCount(void) { + return (int)initcells.size(); +} + +void Landscape::addInitCell(int x, int y) { + initcells.push_back(new DistCell(x, y)); +} + +locn Landscape::getInitCell(int ix) { + return initcells[ix]->getLocn(); +} + +void Landscape::clearInitCells(void) { + int ncells = (int)initcells.size(); + for (int i = 0; i < ncells; i++) { + delete initcells[i]; + } + initcells.clear(); +} + +//--------------------------------------------------------------------------- + +// Read landscape file(s) +// Returns error code or zero if read correctly + +int Landscape::readLandscape(int fileNum, string habfile, string pchfile, string costfile) +{ + // fileNum == 0 for (first) habitat file and optional patch file + // fileNum > 0 for subsequent habitat files under the %cover option + +#if RS_RCPP + wstring header; +#else + string header; +#endif + int h, seq, p, habnodata; + int pchnodata = 0; + int ncols, nrows; + float hfloat, pfloat; + Patch* pPatch; + simParams sim = paramsSim->getSim(); + + if (fileNum < 0) return 19; + +#if RS_RCPP + wifstream hfile; // habitat file input stream + wifstream pfile; // patch file input stream +#else + ifstream hfile; // habitat file input stream + ifstream pfile; // patch file input stream +#endif + initParams init = paramsInit->getInit(); + + // open habitat file and optionally also patch file +#if !RS_RCPP || RSWIN64 + hfile.open(habfile.c_str()); +#else + hfile.open(habfile, std::ios::binary); + if (landraster.utf) { + // apply BOM-sensitive UTF-16 facet + hfile.imbue(std::locale(hfile.getloc(), new std::codecvt_utf16)); + } +#endif + if (!hfile.is_open()) return 11; + if (fileNum == 0) { + if (patchModel) { +#if !RS_RCPP || RSWIN64 + pfile.open(pchfile.c_str()); +#else + pfile.open(pchfile, std::ios::binary); + if (patchraster.utf) { + // apply BOM-sensitive UTF-16 facet + pfile.imbue(std::locale(pfile.getloc(), new std::codecvt_utf16)); + } +#endif + if (!pfile.is_open()) { + hfile.close(); hfile.clear(); + return 12; + } + } + } + + // read landscape data from header records of habitat file + // NB headers of all files have already been compared + hfile >> header >> ncols >> header >> nrows >> header >> minEast >> header >> minNorth + >> header >> resol >> header >> habnodata; + +#if RS_RCPP + if (!hfile.good()) { + // corrupt file stream + StreamErrorR(habfile); + hfile.close(); + hfile.clear(); + if (patchModel) { + pfile.close(); + pfile.clear(); + } + return 131; + } +#endif + + dimX = ncols; dimY = nrows; minX = maxY = 0; maxX = dimX - 1; maxY = dimY - 1; + if (fileNum == 0) { + // set initialisation limits to landscape limits + init.minSeedX = init.minSeedY = 0; + init.maxSeedX = maxX; init.maxSeedY = maxY; + paramsInit->setInit(init); + } + + if (fileNum == 0) { + if (patchModel) { + for (int i = 0; i < 5; i++) pfile >> header >> pfloat; + pfile >> header >> pchnodata; + } +#if RS_RCPP + if (!pfile.good()) { + // corrupt file stream + StreamErrorR(pchfile); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 135; + } +#endif + setCellArray(); + } + + + // set up bad float values to ensure that valid values are read + float badhfloat = -9.0; if (habnodata == -9) badhfloat = -99.0; + float badpfloat = -9.0; if (pchnodata == -9) badpfloat = -99.0; + + seq = 0; // initial sequential patch landscape + p = 0; // initial patch number for cell-based landscape + // create patch 0 - the matrix patch (even if there is no matrix) + if (fileNum == 0) newPatch(seq++, p++); + + switch (rasterType) { + + case 0: // raster with habitat codes - 100% habitat each cell + if (fileNum > 0) return 19; // error condition - should not occur + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + hfloat = badhfloat; +#if RS_RCPP + if (hfile >> hfloat) { +#else + hfile >> hfloat; +#endif + h = (int)hfloat; + if (patchModel) { + pfloat = badpfloat; +#if RS_RCPP + if (pfile >> pfloat) { +#else + pfile >> pfloat; +#endif + p = (int)pfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(pchfile); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 132; + } +#endif + } +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(habfile); + hfile.close(); + hfile.clear(); + if (patchModel) { + pfile.close(); + pfile.clear(); + } + return 135; + } +#endif + if (h == habnodata) + addNewCellToLand(x, y, -1); // add cell only to landscape + else { + + // THERE IS AN ANOMALY HERE - CURRENTLY HABITAT 0 IS OK FOR GUI VERSION BUT + // NOT ALLOWED FOR BATCH VERSION (HABITATS MUST BE 1...n) + // SHOULD WE MAKE THE TWO VERSIONS AGREE? ... + + if (h < 0 || (sim.batchMode && (h < 1 || h > nHabMax))) { + // invalid habitat code +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat code." << std::endl; +#endif + hfile.close(); hfile.clear(); + if (patchModel) { + pfile.close(); pfile.clear(); + } + return 13; + } + else { + addHabCode(h); + if (patchModel) { + if (p < 0 || p == pchnodata) { // invalid patch code +#if RS_RCPP && !R_CMD + if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + else Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; +#endif + hfile.close(); hfile.clear(); + pfile.close(); pfile.clear(); + return 14; + } + if (p == 0) { // cell is in the matrix + addNewCellToPatch(0, x, y, h); + } + else { + if (existsPatch(p)) { + pPatch = findPatch(p); + addNewCellToPatch(pPatch, x, y, h); + // addNewCellToPatch(findPatch(p),x,y,h); + } + else { + pPatch = newPatch(seq++, p); + addNewCellToPatch(pPatch, x, y, h); + } + } + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, h); + } + } + } + } + } +#if RS_RCPP +hfile >> hfloat; +if (!hfile.eof()) EOFerrorR(habfile); +if (patchModel) +{ + pfile >> pfloat; + if (!pfile.eof()) EOFerrorR(pchfile); +} +#endif +break; + +case 1: // multiple % cover + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + hfloat = badhfloat; +#if RS_RCPP + if (hfile >> hfloat) { +#else + hfile >> hfloat; +#endif + h = (int)hfloat; + if (fileNum == 0) { // first habitat cover layer + if (patchModel) { + pfloat = badpfloat; +#if RS_RCPP + if (pfile >> pfloat) { +#else + pfile >> pfloat; +#endif + p = (int)pfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(pchfile); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 135; + } +#endif + } //end if patchmodel + if (h == habnodata) { + addNewCellToLand(x, y, -1); // add cell only to landscape + } + else { + if (hfloat < 0.0 || hfloat > 100.0) { // invalid cover score +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat cover score." << std::endl; +#endif + hfile.close(); hfile.clear(); + if (patchModel) { + pfile.close(); pfile.clear(); + } + return 17; + } + else { + if (patchModel) { + if (p < 0 || p == pchnodata) { // invalid patch code +#if RS_RCPP && !R_CMD + if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + else Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; +#endif + hfile.close(); hfile.clear(); + pfile.close(); pfile.clear(); + return 14; + } + if (p == 0) { // cell is in the matrix + addNewCellToPatch(0, x, y, hfloat); + } + else { + if (existsPatch(p)) { + pPatch = findPatch(p); + addNewCellToPatch(pPatch, x, y, hfloat); + // addNewCellToPatch(findPatch(p),x,y,hfloat); + } + else { + pPatch = newPatch(seq++, p); + addNewCellToPatch(pPatch, x, y, hfloat); + } + } + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, hfloat); + } + } + } + } +else { // additional habitat cover layers + if (h != habnodata) { + if (hfloat < 0.0 || hfloat > 100.0) { // invalid cover score +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat cover score." << std::endl; +#endif + hfile.close(); hfile.clear(); + if (patchModel) { + pfile.close(); pfile.clear(); + } + return 17; + } + else { + cells[y][x]->setHabitat(hfloat); + } + } // end of h != habnodata +} +#if RS_RCPP + } +else { // couldn't read from hfile + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(habfile); + hfile.close(); + hfile.clear(); + if (patchModel) { + pfile.close(); + pfile.clear(); + } + return 133; +} +#endif + + } + } + habIndexed = true; // habitats are already numbered 1...n in correct order +#if RS_RCPP + hfile >> hfloat; + if (!hfile.eof()) EOFerrorR(habfile); + if (patchModel) + { + pfile >> pfloat; + if (!pfile.eof()) EOFerrorR(pchfile); + } +#endif + break; + +case 2: // habitat quality + if (fileNum > 0) return 19; // error condition - should not occur + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + hfloat = badhfloat; +#if RS_RCPP + if (hfile >> hfloat) { +#else + hfile >> hfloat; +#endif + h = (int)hfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(habfile); + hfile.close(); + hfile.clear(); + if (patchModel) { + pfile.close(); + pfile.clear(); + } + return 134; + } +#endif + if (patchModel) { + pfloat = badpfloat; +#if RS_RCPP + if (pfile >> pfloat) { +#else + pfile >> pfloat; +#endif + p = (int)pfloat; +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(pchfile); + hfile.close(); + hfile.clear(); + pfile.close(); + pfile.clear(); + return 135; + } +#endif + } + if (h == habnodata) { + addNewCellToLand(x, y, -1); // add cell only to landscape + } + else { + if (hfloat < 0.0 || hfloat > 100.0) { // invalid quality score +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat quality score." << std::endl; +#endif + hfile.close(); hfile.clear(); + if (patchModel) { + pfile.close(); pfile.clear(); + } + return 17; + } + else { + if (patchModel) { + if (p < 0 || p == pchnodata) { // invalid patch code +#if RS_RCPP && !R_CMD + if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + else Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; +#endif + hfile.close(); hfile.clear(); + pfile.close(); pfile.clear(); + return 14; + } + if (p == 0) { // cell is in the matrix + addNewCellToPatch(0, x, y, hfloat); + } + else { + if (existsPatch(p)) { + pPatch = findPatch(p); + addNewCellToPatch(pPatch, x, y, hfloat); + // addNewCellToPatch(findPatch(p),x,y,hfloat); + } + else { + addPatchNum(p); + pPatch = newPatch(seq++, p); + addNewCellToPatch(pPatch, x, y, hfloat); + } + } + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, hfloat); + } + } + } + } + } +#if RS_RCPP + hfile >> hfloat; + if (!hfile.eof()) EOFerrorR(habfile); + if (patchModel) + { + pfile >> pfloat; + if (!pfile.eof()) EOFerrorR(pchfile); + } +#endif + break; + +default: + break; + } // end switch(rasterType) + + if (hfile.is_open()) { hfile.close(); hfile.clear(); } + if (pfile.is_open()) { pfile.close(); pfile.clear(); } + + if (sim.batchMode) { + if (costfile != "NULL") { + int retcode = readCosts(costfile); + if (retcode < 0) return 54; + } + } + + return 0; + +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +int Landscape::readCosts(string fname) +{ + +#if RS_RCPP + wifstream costs; // cost map file input stream +#else + ifstream costs; // cost map file input stream +#endif + + //int hc,maxYcost,maxXcost,NODATACost,hab; + int hc, maxYcost, maxXcost, NODATACost; + float minLongCost, minLatCost; int resolCost; + float fcost; +#if RS_RCPP + wstring header; +#else + string header; +#endif + Cell* pCell; +#if !RS_RCPP + simView v = paramsSim->getViews(); +#endif + + int maxcost = 0; + + // open cost file +#if !RS_RCPP || RSWIN64 + costs.open(fname.c_str()); +#else + costs.open(fname, std::ios::binary); + if (costsraster.utf) { + // apply BOM-sensitive UTF-16 facet + costs.imbue(std::locale(costs.getloc(), new std::codecvt_utf16)); + } +#endif + // read headers and check that they correspond to the landscape ones + costs >> header; +#if RS_RCPP + if (!costs.good()) { + // corrupt file stream + StreamErrorR(fname); + costs.close(); + costs.clear(); + return -181; + } + if (header != L"ncols" && header != L"NCOLS") { +#else + if (header != "ncols" && header != "NCOLS") { +#endif + +// MessageDlg("The selected file is not a raster.", +// MessageDlg("Header problem in import_CostsLand()", +// mtError, TMsgDlgButtons() << mbRetry,0); + costs.close(); costs.clear(); + return -1; +} +double tmpresolCost; +costs >> maxXcost >> header >> maxYcost >> header >> minLongCost; +costs >> header >> minLatCost >> header >> tmpresolCost >> header >> NODATACost; +resolCost = (int) tmpresolCost; + + +#if !RS_RCPP + MemoLine("Loading costs map. Please wait..."); +#endif + + for (int y = maxYcost - 1; y > -1; y--) { + for (int x = 0; x < maxXcost; x++) { +#if RS_RCPP + if (costs >> fcost) { +#else + costs >> fcost; +#endif + hc = (int)fcost; // read as float and convert to int +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(fname); + costs.close(); + costs.clear(); + return -181; + } +#endif + if (hc < 1 && hc != NODATACost) { +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Cost map my only contain values of 1 or higher, but found " << fcost << "." << endl; +#endif + // error - zero / negative cost not allowed + costs.close(); costs.clear(); + return -999; + } + pCell = findCell(x, y); + if (pCell != 0) { // not no-data cell + pCell->setCost(hc); + if (hc > maxcost) maxcost = hc; + } + } + } +#if RS_RCPP +costs >> fcost; +if (costs.eof()) { +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Costs map loaded." << endl; +#endif +} +else EOFerrorR(fname); +#else +MemoLine("Costs map loaded."); +#endif + +costs.close(); costs.clear(); + +return maxcost; + +} + +//--------------------------------------------------------------------------- + +rasterdata CheckRasterFile(string fname) +{ + rasterdata r; + string header; + int inint; + ifstream infile; + + r.ok = true; + r.errors = r.ncols = r.nrows = r.cellsize = 0; + r.xllcorner = r.yllcorner = 0.0; + + infile.open(fname.c_str()); + if (infile.is_open()) { + infile >> header >> r.ncols; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " r.ncols=" + Int2Str(r.ncols) + ).c_str()); +#endif + if (header != "ncols" && header != "NCOLS") r.errors++; + infile >> header >> r.nrows; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " r.nrows=" + Int2Str(r.nrows) + ).c_str()); +#endif + if (header != "nrows" && header != "NROWS") r.errors++; + infile >> header >> r.xllcorner; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " r.xllcorner=" + Float2Str(r.xllcorner) + ).c_str()); +#endif + if (header != "xllcorner" && header != "XLLCORNER") r.errors++; + infile >> header >> r.yllcorner; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " r.yllcorner=" + Float2Str(r.yllcorner) + ).c_str()); +#endif + if (header != "yllcorner" && header != "YLLCORNER") r.errors++; + infile >> header >> r.cellsize; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " r.cellsize=" + Int2Str(r.cellsize) + ).c_str()); +#endif + if (header != "cellsize" && header != "CELLSIZE") r.errors++; + infile >> header >> inint; +#if RSDEBUG + DebugGUI(("CheckRasterFile(): header=" + header + " inint=" + Int2Str(inint) + ).c_str()); +#endif + if (header != "NODATA_value" && header != "NODATA_VALUE") r.errors++; + infile.close(); + infile.clear(); + if (r.errors > 0) r.ok = false; + } + else { + r.ok = false; r.errors = -111; + } + infile.clear(); + + return r; +} + +//--------------------------------------------------------------------------- + +// Patch connectivity functions + +// Create & initialise connectivity matrix +void Landscape::createConnectMatrix(void) +{ + if (connectMatrix != 0) deleteConnectMatrix(); + int npatches = (int)patches.size(); +#if RSDEBUG + //DEBUGLOG << "Landscape::createConnectMatrix(): npatches=" << npatches << endl; +#endif + connectMatrix = new int* [npatches]; + for (int i = 0; i < npatches; i++) { + connectMatrix[i] = new int[npatches]; + for (int j = 0; j < npatches; j++) connectMatrix[i][j] = 0; + } +} + +// Re-initialise connectivity matrix +void Landscape::resetConnectMatrix(void) +{ + if (connectMatrix != 0) { + int npatches = (int)patches.size(); + for (int i = 0; i < npatches; i++) { + for (int j = 0; j < npatches; j++) connectMatrix[i][j] = 0; + } + } +} + +// Increment connectivity count between two specified patches +void Landscape::incrConnectMatrix(int p0, int p1) { + int npatches = (int)patches.size(); + if (connectMatrix == 0 || p0 < 0 || p0 >= npatches || p1 < 0 || p1 >= npatches) return; + connectMatrix[p0][p1]++; +} + +// Delete connectivity matrix +void Landscape::deleteConnectMatrix(void) +{ + if (connectMatrix != 0) { + int npatches = (int)patches.size(); + for (int j = 0; j < npatches; j++) { + if (connectMatrix[j] != 0) + delete connectMatrix[j]; + } + delete[] connectMatrix; + connectMatrix = 0; + } +} + +// Write connectivity file headers +bool Landscape::outConnectHeaders(int option) +{ + if (option == -999) { // close the file + if (outConnMat.is_open()) outConnMat.close(); + outConnMat.clear(); + return true; + } + + simParams sim = paramsSim->getSim(); + + string name = paramsSim->getDir(2); + if (sim.batchMode) { + name += "Batch" + Int2Str(sim.batchNum) + "_"; + name += "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(landNum); + } + else + name += "Sim" + Int2Str(sim.simulation); + name += "_Connect.txt"; + outConnMat.open(name.c_str()); + + outConnMat << "Rep\tYear\tStartPatch\tEndPatch\tNinds" << endl; + + return outConnMat.is_open(); +} + +#if RS_RCPP +// Write movement paths file headers +void Landscape::outPathsHeaders(int rep, int option) +{ + if (option == -999) { // close the file + if (outMovePaths.is_open()) outMovePaths.close(); + outMovePaths.clear(); + } + if (option == 0) { // open the file and write header + + simParams sim = paramsSim->getSim(); + string name = paramsSim->getDir(2); + if (sim.batchMode) { + name += "Batch" + Int2Str(sim.batchNum) + + "_Sim" + Int2Str(sim.simulation) + + "_Land" + Int2Str(landNum) + + "_Rep" + Int2Str(rep); + } + else { + name += "Sim" + Int2Str(sim.simulation) + + "_Rep" + Int2Str(rep); + } + name += "_MovePaths.txt"; + + outMovePaths.open(name.c_str()); + if (outMovePaths.is_open()) { + outMovePaths << "Year\tIndID\tStep\tx\ty\tStatus" << endl; + } + else { +#if RSDEBUG + DEBUGLOG << "RunModel(): UNABLE TO OPEN MOVEMENT PATHS FILE" << endl; +#endif + outMovePaths.clear(); + } + } +} +#endif + +void Landscape::outConnect(int rep, int yr) +{ + int patchnum0, patchnum1; + int npatches = (int)patches.size(); + int* emigrants = new int[npatches]; // 1D array to hold emigrants from each patch + int* immigrants = new int[npatches]; // 1D array to hold immigrants to each patch + + for (int i = 0; i < npatches; i++) { + emigrants[i] = immigrants[i] = 0; + } + + for (int i = 0; i < npatches; i++) { + patchnum0 = patches[i]->getPatchNum(); + if (patchnum0 != 0) { + for (int j = 0; j < npatches; j++) { + patchnum1 = patches[j]->getPatchNum(); + if (patchnum1 != 0) { + emigrants[i] += connectMatrix[i][j]; + immigrants[j] += connectMatrix[i][j]; + if (connectMatrix[i][j] > 0) { + outConnMat << rep << "\t" << yr + << "\t" << patchnum0 << "\t" << patchnum1 + << "\t" << connectMatrix[i][j] << endl; + } + } + } + } + } + + for (int i = 0; i < npatches; i++) { + patchnum0 = patches[i]->getPatchNum(); + if (patchnum0 != 0) { + if (patches[i]->getK() > 0.0) + { // suitable patch + outConnMat << rep << "\t" << yr + << "\t" << patchnum0 << "\t-999\t" << emigrants[i] << endl; + outConnMat << rep << "\t" << yr + << "\t-999\t" << patchnum0 << "\t" << immigrants[i] << endl; + } + } + } + + delete[] emigrants; + delete[] immigrants; + +} + +//--------------------------------------------------------------------------- + +void Landscape::resetVisits(void) { + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] != 0) { // not a no-data cell + cells[y][x]->resetVisits(); + } + } + } +} + +// Save SMS path visits map to raster text file +void Landscape::outVisits(int rep, int landNr) { + + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(3) +#if RS_RCPP + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + + "_Land" + Int2Str(landNr) + "_Rep" + Int2Str(rep) +#else + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + + "_land" + Int2Str(landNr) + "_rep" + Int2Str(rep) +#endif + + "_Visits.txt"; + } + else { + name = paramsSim->getDir(3) + + "Sim" + Int2Str(sim.simulation) + + "_land" + Int2Str(landNr) + "_rep" + Int2Str(rep) + + "_Visits.txt"; + } + outvisits.open(name.c_str()); + + outvisits << "ncols " << dimX << endl; + outvisits << "nrows " << dimY << endl; + outvisits << "xllcorner " << minEast << endl; + outvisits << "yllcorner " << minNorth << endl; + outvisits << "cellsize " << resol << endl; + outvisits << "NODATA_value -9" << endl; + + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + if (cells[y][x] == 0) { // no-data cell + outvisits << "-9 "; + } + else { + outvisits << cells[y][x]->getVisits() << " "; + } + } + outvisits << endl; + } + + outvisits.close(); outvisits.clear(); +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- diff --git a/src/RScore/Landscape.h b/src/RScore/Landscape.h new file mode 100644 index 00000000..2a414cc1 --- /dev/null +++ b/src/RScore/Landscape.h @@ -0,0 +1,562 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Landscape + +Implements the following classes: + +InitDist - Initial species distribution + +Landscape - Landscape grid + +The Landscape is a rectangular array of Cells which are grouped together in +Patches. As far as the user is aware, the Landscape is either patch-based or +cell-based (having no Patches), but internally Patches are always present (they +each comprise only one Cell for a cell-based model). The grain of the Landscape +may be any positive integer, and is nominally in metres. + +The Landscape is either input from one or more text files in ArcGIS raster export +format, or it is generated artificially as a random or fractal binary array (in +which case, it must be cell-based). An input 'real' Landscape may hold within each +Cell either discrete habitat classes, or percent cover of habitat classes, or a +continuous quality index (1 to 100%). + +The Landscape may be dynamic, in which case the user specifies a set of changes +to be applied at certain years during each simulation. The changes may be to +habitat only, patches only (if a patch-based model) or habitats and patches. +Although the changes must be supplied as entire habitat / patch files (which +must match the original Landscape in terms of cell size and extent), internally +they are recorded as lists of dynamic habitat and patch changes. + +The initial species distribution is a rectangular array if distribution cells +(DistCell) covering the same spatial extent at the Landscape. Its grain may be +either that of the Landscape or an integer multiple of it. + +The Landscape can record a list (in the vector initcells) of Cells or Patches +to be intialised, which are specified by the user in FormSeeding. This option is +available in the GUI version only. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 2 December 2021 by Steve Palmer +------------------------------------------------------------------------------*/ + +#ifndef LandscapeH +#define LandscapeH + +#include +#include +#include + +using namespace std; + +#include "Parameters.h" +#include "Patch.h" +#include "Cell.h" +#include "Species.h" +#include "FractalGenerator.h" +#if RS_RCPP +#include +#if !RSWIN64 +#include +#endif +#include +#endif + +//--------------------------------------------------------------------------- + +// Initial species distribution + +class InitDist{ +public: + InitDist(Species*); + ~InitDist(); + int readDistribution( + string // name of species distribution file + ); + void setDistribution( + int // no. of distribution cells to be initialised (0 for all cells) + ); + void setDistCell( // Set a specified cell (by position in cells vector) + int, // index no. of DistCell in cells vector + bool // value to be set + ); + void setDistCell( // Set a specified cell (by co-ordinates) + locn, // structure holding x (column) and y (row) co-ordinates + bool + ); + bool inInitialDist( // Specified location is within the initial distribution? + locn // structure holding x (column) and y (row) co-ordinates + ); + int cellCount(void); + locn getCell( // Return the co-ordinates of a specified initial distribution cell + int // index no. of DistCell in cells vector + ); + locn getSelectedCell( // Return the co-ordinates of a specified initial distribution + // cell if it has been selected + // otherwise return negative co-ordinates + int // index no. of DistCell in cells vector + ); + locn getDimensions(void); + void resetDistribution(void); + +private: + Species *pSpecies; // pointer to species + int resol; // species distribution cell size (m) + int maxX, maxY; // dimensions + double minEast; // ) real world min co-ordinates + double minNorth; // ) read from raster file + + // list of cells in the initial distribution + // cells MUST be loaded in the sequence ascending x within descending y + std::vector cells; + +}; + +//--------------------------------------------------------------------------- + +struct landParams { + bool patchModel; bool spDist; bool generated; + bool dynamic; + int landNum; int resol; int spResol; int nHab; int nHabMax; + int dimX,dimY,minX,minY,maxX,maxY; + short rasterType; +}; +struct landData { + int resol; int dimX,dimY,minX,minY,maxX,maxY; +}; +struct genLandParams { + bool fractal; bool continuous; + float minPct,maxPct; float propSuit; float hurst; int maxCells; +}; +struct landPix { + int pix; float gpix; +}; +struct landOrigin { + double minEast; double minNorth; +}; +struct rasterHdr { + bool ok; + int errors,ncols,nrows,cellsize; + double xllcorner,yllcorner; +}; +struct rasterdata { + bool ok; + int errors,ncols,nrows,cellsize; + double xllcorner,yllcorner; +#if RS_RCPP + bool utf; +#endif +}; +struct patchData { + Patch *pPatch; int patchNum,nCells; int x,y; +}; +struct landChange { + int chgnum,chgyear; string habfile,pchfile,costfile; +}; +struct patchChange { + int chgnum, x, y, oldpatch, newpatch; +}; +struct costChange { + int chgnum,x,y,oldcost,newcost; +}; + +class Landscape{ +public: + Landscape(); + ~Landscape(); + void resetLand(void); + + // functions to set and return parameter values + + void setLandParams( + landParams, // structure holding landscape parameters + bool // batch mode + ); + landParams getLandParams(void); + landData getLandData(void); + void setGenLandParams(genLandParams); + genLandParams getGenLandParams(void); + void setLandLimits( + int, // minimum available X + int, // minimum available Y + int, // maximum available X + int // maximum available Y + ); + void resetLandLimits(void); + void setLandPix(landPix); + + landPix getLandPix(void); + void setOrigin(landOrigin); + landOrigin getOrigin(void); + + // functions to handle habitat codes + + bool habitatsIndexed(void); + void listHabCodes(void); + void addHabCode(int); + int findHabCode(int); + int getHabCode(int); + void clearHabitats(void); + void addColour(rgb); + void changeColour(int,rgb); + rgb getColour(int); + int colourCount(void); + + // functions to handle patches and cells + + void setCellArray(void); + void addPatchNum(int); + void generatePatches(void); // create an artificial landscape + void allocatePatches(Species*); // create patches for a cell-based landscape + Patch* newPatch( + int // patch sequential no. (id no. is set to equal sequential no.) + ); + Patch* newPatch( + int, // patch sequential no. + int // patch id no. + ); + void resetPatches(void); + void addNewCellToLand( + int, // x co-ordinate + int, // y co-ordinate + float // habitat quality value + ); + void addNewCellToLand( + int, // x co-ordinate + int, // y co-ordinate + int // habitat class no. + ); + void addCellToPatch( + Cell*, // pointer to Cell + Patch* // pointer to Patch + ); + void addCellToPatch( + Cell*, // pointer to Cell + Patch*, // pointer to Patch + float // habitat quality value + ); + void addCellToPatch( + Cell*, // pointer to Cell + Patch*, // pointer to Patch + int // habitat class no. + ); + void addNewCellToPatch( + Patch*, // pointer to Patch + int, // x co-ordinate + int, // y co-ordinate + int // habitat class no. + ); + void addNewCellToPatch( + Patch*, // pointer to Patch + int, // x co-ordinate + int, // y co-ordinate + float // habitat quality value + ); + patchData getPatchData( + int // index no. of Patch in patches vector + ); + bool existsPatch( + int // Patch id no. + ); + Patch* findPatch( + int // Patch id no. + ); + int checkTotalCover(void); + void resetPatchPopns(void); + void updateCarryingCapacity( + Species*, // pointer to Species + int, // year + short // landscape change index (always zero if not dynamic) + ); + Cell* findCell( + int, // x co-ordinate + int // y co-ordinate + ); + int patchCount(void); + void updateHabitatIndices(void); + void setEnvGradient( + Species*, // pointer to Species + bool // TRUE for initial instance that gradient is set + ); + void setGlobalStoch( + int // no. of years + ); + float getGlobalStoch( + int // year + ); + void updateLocalStoch(void); + void resetCosts(void); + void resetEffCosts(void); + + // functions to handle dynamic changes + + void setDynamicLand(bool); + void addLandChange( + landChange // structure holding landscape change data + ); + int numLandChanges(void); + landChange getLandChange( + short // change number + ); + void deleteLandChanges(void); +#if RS_RCPP && !R_CMD + int readLandChange( + int, // change file number + bool, // change SMS costs? + wifstream&, // habitat file stream + wifstream&, // patch file stream + wifstream&, // cost file stream + int, // habnodata + int, // pchnodata + int // costnodata + ); +#else + int readLandChange( + int, // change file number + bool // change SMS costs? + ); +#endif + void createPatchChgMatrix(void); + void recordPatchChanges(int); + void deletePatchChgMatrix(void); + int numPatchChanges(void); + patchChange getPatchChange( + int // patch change number + ); + void createCostsChgMatrix(void); + void recordCostChanges(int); + void deleteCostsChgMatrix(void); + int numCostChanges(void); + costChange getCostChange( + int // cost change number + ); + + // functions to handle species distributions + + int newDistribution( + Species*, // pointer to Species + string // name of initial distribution file + ); + void setDistribution( + Species*, // pointer to Species + int // no. of distribution squares to initialise + ); + bool inInitialDist( // Specified cell matches one of the distn cells to be initialised? + Species*, // pointer to Species + locn // structure holding co-ordinates of Cell + ); + void deleteDistribution( + Species* // pointer to Species + ); + int distnCount(void); // Return no. of initial distributions in the Landscape + int distCellCount( // Return no. of distribution cells in an initial distribution + int // index no. of InitDist in distns vector + ); + locn getDistnCell( // Get co-ordinates of a specified cell in a specified initial distn + int, // index no. of InitDist in distns vector + int // index no. of DistCell in cells vector + ); + locn getSelectedDistnCell( // Get co-ordinates of a specified cell in a specified initial distn + // Returns negative co-ordinates if the cell is not selected + int, // index no. of InitDist in distns vector + int // index no. of DistCell in cells vector + ); + locn getDistnDimensions( // Get the dimensions of a specified initial distribution + int // index no. of InitDist in distns vector + ); + void setDistnCell( // Set a cell in a specified init distn (by posn in cells vector) + int, // index no. of InitDist in distns vector + int, // index no. of DistCell in cells vector + bool // value to be set + ); + void setDistnCell( // Set a cell in a specified init distn (by given co-ordinates) + int, // index no. of InitDist in distns vector + locn, // structure holding co-ordinates of DistCell + bool // value to be set + ); + void resetDistribution( + Species* // pointer to Species + ); + + // functions to handle initialisation cells + + int initCellCount(void); + void addInitCell( // Create a new DistCell and add to the initcells vector + int, // x co-ordinate + int // y co-ordinate + ); + locn getInitCell( + int // index no. of DistCell in initcells vector + ); + void clearInitCells(void); + + // functions to handle connectivity matrix + + void createConnectMatrix(void); + void resetConnectMatrix(void); + void incrConnectMatrix( + int, // sequential no. of origin Patch + int // sequential no. of settlement Patch + ); + void deleteConnectMatrix(void); + bool outConnectHeaders( // Write connectivity file headers + int // option - set to -999 to close the connectivity file + ); +#if RS_RCPP + void outPathsHeaders(int, int); +#endif + void outConnect( + int, // replicate no. + int // year + ); + + // functions to handle input and output + + int readLandscape( + int, // fileNum == 0 for (first) habitat file and optional patch file + // fileNum > 0 for subsequent habitat files under the %cover option + string, // habitat file name + string, // patch file name + string // cost file name (may be NULL) + ); + void listPatches(void); + int readCosts( + string // costs file name + ); + // the following four functions are implemented for the GUI version only + // in the batch version, they are defined, but empty + void setLandMap(void); + void drawLandscape( + int, // replicate no. + int, // landscape index number (always 0 if landscape is not dynamic) + int // landscape no. + ); + void drawGradient(void); // Draw environmental gradient map + void drawGlobalStoch( // Draw environmental stochasticity time-series + int // no. of years + ); + + void resetVisits(void); + void outVisits(int,int); // save SMS path visits map to raster text file + +private: + bool generated; // artificially generated? + bool patchModel; // + bool spDist; // initial species distribution loaded + bool fractal; // + bool continuous; // + bool dynamic; // landscape changes during simulation + bool habIndexed; // habitat codes have been converted to index numbers + short rasterType; // 0 = habitat codes 1 = % cover 2 = quality 9 = artificial landscape + int landNum; // landscape number + int resol; // cell size (m) + int spResol; // species distribution cell size (m) + int nHab; // no. of habitats + int nHabMax; // max. no. of habitats (used for batch input only) + int dimX,dimY; // dimensions + int minX,minY; // minimum available X and Y co-ordinates + int maxX,maxY; // maximum available X and Y co-ordinates + float minPct,maxPct; // min and max percentage of habitat in a cell + float propSuit; // proportion of suitable cells + float hurst; // Hurst exponent + int maxCells; // max. cells per patch (artificial landscapes) + int pix; // image display ratio + float gpix; // image display ratio for gradient map + double minEast; // ) real world min co-ordinates + double minNorth; // ) read from habitat raster + + // list of cells in the landscape + // cells MUST be loaded in the sequence ascending x within descending y + Cell ***cells; + + // list of patches in the landscape - can be in any sequence + std::vector patches; + + // list of patch numbers in the landscape + std::vector patchnums; + + // list of habitat codes + std::vector habCodes; + + // list of colours for habitat codes + std::vector colours; + + // list of dynamic landscape changes + std::vector landchanges; + std::vector patchchanges; + std::vector costschanges; + + // list of initial individual species distributions + std::vector distns; + + // list of cells to be initialised for ALL species + std::vector initcells; + + // patch connectivity matrix + // indexed by [start patch seq num][end patch seq num] + int **connectMatrix; + + // global environmental stochasticity (epsilon) + float *epsGlobal; // pointer to time-series + + // patch and costs change matrices (temporary - used when reading dynamic landscape) + // indexed by [descending y][x][period] + // where there are three periods, 0=original 1=previous 2=current + int ***patchChgMatrix; + int ***costsChgMatrix; + +}; + +// NOTE: the following function is not a behaviour of Landscape, as it is run by the +// batch routine to check raster files before any Landscape has been initiated +rasterdata CheckRasterFile(string); + +extern paramGrad *paramsGrad; +extern paramStoch *paramsStoch; +extern paramInit *paramsInit; +extern paramSim *paramsSim; +extern RSrandom *pRandom; + +#if RSDEBUG +extern ofstream DEBUGLOG; +extern void DebugGUI(string); +#endif + +extern void MemoLine(string); + +#if RS_RCPP +extern rasterdata landraster,patchraster,spdistraster,costsraster; +extern void EOFerrorR(string); +extern void StreamErrorR(string); +#endif + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Main.cpp b/src/RScore/Main.cpp new file mode 100644 index 00000000..42f783b8 --- /dev/null +++ b/src/RScore/Main.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + +#if LINUX_CLUSTER || R_CMD +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "Individual.h" +#include "Community.h" +#include "RSrandom.h" +#include "Utils.h" +#include "Parameters.h" +#include "Landscape.h" +#include "Species.h" +#include "SubCommunity.h" + +using namespace std; + +void run_unit_tests() { + cout << "******* Unit test output *******" << endl; + testRSrandom(); + testIndividual(); + cout << endl << "************************" << endl; +} + +// Global vars +string habmapname, patchmapname, distnmapname; +string costmapname, genfilename; +string landFile; +paramGrad* paramsGrad; +paramStoch* paramsStoch; +paramInit* paramsInit; +paramSim* paramsSim; +RSrandom* pRandom; +ofstream DEBUGLOG; +ofstream MUTNLOG; +vector hfnames; +Species* pSpecies; +Community* pComm; +void DebugGUI(string msg) { + // nothing +} +void MemoLine(string msg) { + /// nothing +} +traitCanvas SetupTraitCanvas(void) { + traitCanvas tcanv; + for (int i = 0; i < NTRAITS; i++) { tcanv.pcanvas[i] = 0; } + return tcanv; +} +void Landscape::setLandMap(void) { } +void Landscape::drawLandscape(int rep, int yr, int landnum) { } +void Community::viewOccSuit(int year, double mn, double se) { } +void Community::draw(int rep, int yr, int gen, int landNum) { } + +#if LINUX_CLUSTER || RS_RCPP +int main(int argc, char* argv[]) +#else +int _tmain(int argc, _TCHAR* argv[]) +#endif +{ +#ifdef NDEBUG + cout << "This code is only for running tests and not meant to run in release." << endl; + return 1; +# else + assert(0.1 > 0.0); // assert does run correctly + run_unit_tests(); + cout << "All tests have completed." << endl; + return 0; // if tests succeed, we are happy +# endif // NDEBUG +} diff --git a/src/RScore/Model.cpp b/src/RScore/Model.cpp new file mode 100644 index 00000000..fc4fac94 --- /dev/null +++ b/src/RScore/Model.cpp @@ -0,0 +1,2093 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Model.h" + +ofstream outPar; + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +#if RS_RCPP && !R_CMD +Rcpp::List RunModel(Landscape* pLandscape, int seqsim) +#else +int RunModel(Landscape* pLandscape, int seqsim) +#endif +{ + int yr, totalInds; + bool filesOK; + + landParams ppLand = pLandscape->getLandParams(); + envGradParams grad = paramsGrad->getGradient(); + envStochParams env = paramsStoch->getStoch(); + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + trfrRules trfr = pSpecies->getTrfr(); + initParams init = paramsInit->getInit(); + simParams sim = paramsSim->getSim(); + simView v = paramsSim->getViews(); + +#if RSDEBUG + landPix p = pLandscape->getLandPix(); + DEBUGLOG << "RunModel(): reps=" << sim.reps + << " ppLand.nHab=" << ppLand.nHab + << " p.pix=" << p.pix + << endl; + DEBUGLOG << endl; +#endif + + if (!ppLand.generated) { + if (!ppLand.patchModel) { // cell-based landscape + // create patches for suitable cells, adding unsuitable cells to the matrix + // NB this is an overhead here, but is necessary in case the identity of + // suitable habitats has been changed from one simulation to another (GUI or batch) + // substantial time savings may result during simulation in certain landscapes + pLandscape->allocatePatches(pSpecies); + } + pComm = new Community(pLandscape); // set up community + // set up a sub-community associated with each patch (incl. the matrix) + pLandscape->updateCarryingCapacity(pSpecies, 0, 0); + patchData ppp; + int npatches = pLandscape->patchCount(); + for (int i = 0; i < npatches; i++) { + ppp = pLandscape->getPatchData(i); + pComm->addSubComm(ppp.pPatch, ppp.patchNum); // SET UP ALL SUB-COMMUNITIES + } + if (init.seedType == 0 && init.freeType < 2 && init.initFrzYr > 0) { + // restrict available landscape to initialised region + pLandscape->setLandLimits(init.minSeedX, init.minSeedY, + init.maxSeedX, init.maxSeedY); + } + else { + pLandscape->resetLandLimits(); + } + } + +#if RS_RCPP && !R_CMD + Rcpp::List list_outPop; +#endif + + // Loop through replicates + for (int rep = 0; rep < sim.reps; rep++) { +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): starting simulation=" << sim.simulation << " rep=" << rep << endl; +#endif +#if RS_RCPP && !R_CMD + Rcpp::Rcout << endl << "starting replicate " << rep << endl; +#else +#if BATCH + cout << endl << "starting replicate " << rep << endl; +#endif +#endif + + MemoLine(("Running replicate " + Int2Str(rep) + "...").c_str()); + + if (sim.saveVisits && !ppLand.generated) { + pLandscape->resetVisits(); + } + + patchChange patchchange; + costChange costchange; + int npatchchanges = pLandscape->numPatchChanges(); + int ncostchanges = pLandscape->numCostChanges(); + int ixpchchg = 0; + int ixcostchg = 0; +#if RSDEBUG + DEBUGLOG << "RunModel(): npatchchanges=" << npatchchanges << " ncostchanges=" << ncostchanges << endl; +#endif + + if (ppLand.generated) { +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): generating new landscape ..." << endl; +#endif + // delete previous community (if any) + // Note: this must be BEFORE the landscape is reset (as a sub-community accesses + // its corresponding patch upon deletion) + if (pComm != 0) delete pComm; + // generate new cell-based landscape + MemoLine("...generating new landscape..."); + pLandscape->resetLand(); +#if RSDEBUG + DEBUGLOG << "RunModel(): finished resetting landscape" << endl << endl; +#endif + pLandscape->generatePatches(); + if (v.viewLand || sim.saveMaps) { + pLandscape->setLandMap(); + pLandscape->drawLandscape(rep, 0, ppLand.landNum); + } +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): finished generating patches" << endl; +#endif + pComm = new Community(pLandscape); // set up community + // set up a sub-community associated with each patch (incl. the matrix) + pLandscape->updateCarryingCapacity(pSpecies, 0, 0); + patchData ppp; + int npatches = pLandscape->patchCount(); +#if RSDEBUG + DEBUGLOG << "RunModel(): patch count is " << npatches << endl; +#endif + for (int i = 0; i < npatches; i++) { + ppp = pLandscape->getPatchData(i); +#if RSWIN64 +#if LINUX_CLUSTER + pComm->addSubComm(ppp.pPatch, ppp.patchNum); // SET UP ALL SUB-COMMUNITIES +#else + SubCommunity* pSubComm = pComm->addSubComm(ppp.pPatch, ppp.patchNum); // SET UP ALL SUB-COMMUNITIES +#endif +#else + pComm->addSubComm(ppp.pPatch, ppp.patchNum); // SET UP ALL SUB-COMMUNITIES +#endif + } + MemoLine("...completed..."); +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): finished generating populations" << endl; +#endif + } + if (init.seedType == 0 && init.freeType < 2 && init.initFrzYr > 0) { + // restrict available landscape to initialised region + pLandscape->setLandLimits(init.minSeedX, init.minSeedY, + init.maxSeedX, init.maxSeedY); + } + else { + pLandscape->resetLandLimits(); + } + + filesOK = true; + if (rep == 0) { + // open output files + if (sim.outRange) { // open Range file + if (!pComm->outRangeHeaders(pSpecies, ppLand.landNum)) { + MemoLine("UNABLE TO OPEN RANGE FILE"); + filesOK = false; + } + } + if (sim.outOccup && sim.reps > 1) + if (!pComm->outOccupancyHeaders(0)) { + MemoLine("UNABLE TO OPEN OCCUPANCY FILE(S)"); + filesOK = false; + } + if (sim.outPop) { + // open Population file + if (!pComm->outPopHeaders(pSpecies, ppLand.landNum)) { + MemoLine("UNABLE TO OPEN POPULATION FILE"); + filesOK = false; + } + } + if (sim.outTraitsCells) + if (!pComm->outTraitsHeaders(pSpecies, ppLand.landNum)) { + MemoLine("UNABLE TO OPEN TRAITS FILE"); + filesOK = false; + } + if (sim.outTraitsRows) + if (!pComm->outTraitsRowsHeaders(pSpecies, ppLand.landNum)) { + MemoLine("UNABLE TO OPEN TRAITS ROWS FILE"); + filesOK = false; + } + if (sim.outConnect && ppLand.patchModel) // open Connectivity file + if (!pLandscape->outConnectHeaders(0)) { + MemoLine("UNABLE TO OPEN CONNECTIVITY FILE"); + filesOK = false; + } + } +#if RSDEBUG + DEBUGLOG << "RunModel(): completed opening output files" << endl; +#endif + if (!filesOK) { +#if RSDEBUG + DEBUGLOG << "RunModel(): PROBLEM - closing output files" << endl; +#endif + // close any files which may be open + if (sim.outRange) { + pComm->outRangeHeaders(pSpecies, -999); + } + if (sim.outOccup && sim.reps > 1) + pComm->outOccupancyHeaders(-999); + if (sim.outPop) { + pComm->outPopHeaders(pSpecies, -999); + } + if (sim.outTraitsCells) + pComm->outTraitsHeaders(pSpecies, -999); + if (sim.outTraitsRows) + pComm->outTraitsRowsHeaders(pSpecies, -999); + if (sim.outConnect && ppLand.patchModel) + pLandscape->outConnectHeaders(-999); +#if RS_RCPP && !R_CMD + return Rcpp::List::create(Rcpp::Named("Errors") = 666); +#else + return 666; +#endif + } + + if (env.stoch && !env.local) { + // create time series in case of global environmental stochasticity + pLandscape->setGlobalStoch(sim.years + 1); + } + + if (grad.gradient) { // set up environmental gradient + pLandscape->setEnvGradient(pSpecies, true); + } + + if (sim.outConnect && ppLand.patchModel) + pLandscape->createConnectMatrix(); + + // variables to control dynamic landscape + landChange landChg; landChg.chgnum = 0; landChg.chgyear = 999999; + if (!ppLand.generated && ppLand.dynamic) { + landChg = pLandscape->getLandChange(0); // get first change year + } + + // set up populations in the community + pLandscape->updateCarryingCapacity(pSpecies, 0, 0); +#if RSDEBUG + DEBUGLOG << "RunModel(): completed updating carrying capacity" << endl; +#endif + // if (init.seedType != 2) { + pComm->initialise(pSpecies, -1); + // } + bool updateland = false; + int landIx = 0; // landscape change index + +#if RSDEBUG + DEBUGLOG << "RunModel(): completed initialisation, rep=" << rep + << " pSpecies=" << pSpecies << endl; +#endif +#if BATCH && RS_RCPP && !R_CMD + Rcpp::Rcout << "RunModel(): completed initialisation " << endl; +#endif + + // open a new individuals file for each replicate + if (sim.outInds) + pComm->outInds(rep, 0, 0, ppLand.landNum); + // open a new genetics file for each replicate + if (sim.outGenetics) { + pComm->outGenetics(rep, 0, 0, ppLand.landNum); + if (!dem.stageStruct && sim.outStartGenetic == 0) { + // write genetic data for initialised individuals of non-strucutred population + pComm->outGenetics(rep, 0, 0, -1); + } + } +#if RSDEBUG + // output initialised Individuals + if (sim.outInds) + pComm->outInds(rep, -1, -1, -1); +#endif +#if RS_RCPP + // open a new movement paths file for each replicate + if (sim.outPaths) + pLandscape->outPathsHeaders(rep, 0); +#endif + + // years loop + MemoLine("...running..."); + for (yr = 0; yr < sim.years; yr++) { +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): starting simulation=" << sim.simulation + << " rep=" << rep << " yr=" << yr << endl; +#endif +#if RS_RCPP && !R_CMD + Rcpp::checkUserInterrupt(); +#endif + bool updateCC = false; + if (yr < 4 + || (yr < 31 && yr % 10 == 0) + || (yr < 301 && yr % 100 == 0) + || (yr < 3001 && yr % 1000 == 0) + || (yr < 30001 && yr % 10000 == 0) + || (yr < 300001 && yr % 100000 == 0) + || (yr < 3000001 && yr % 1000000 == 0) + ) { +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "starting year " << yr << "..." << endl; +#else + cout << "starting year " << yr << endl; +#endif + } + if (init.seedType == 0 && init.freeType < 2) { + // apply any range restrictions + if (yr == init.initFrzYr) { + // release initial frozen range - reset landscape to its full extent + pLandscape->resetLandLimits(); + updateCC = true; + } + if (init.restrictRange) { + if (yr > init.initFrzYr && yr < init.finalFrzYr) { + if ((yr - init.initFrzYr) % init.restrictFreq == 0) { + // apply dynamic range restriction + commStats s = pComm->getStats(); + int minY = s.maxY - init.restrictRows; + if (minY < 0) minY = 0; +#if RSDEBUG + DEBUGLOG << "RunModel(): restriction yr=" << yr + << " s.minY=" << s.minY << " s.maxY=" << s.maxY + << " init.restrictRows=" << init.restrictRows + << " minY=" << minY + << endl; +#endif + pLandscape->setLandLimits(ppLand.minX, minY, ppLand.maxX, ppLand.maxY); + updateCC = true; + } + } + if (yr == init.finalFrzYr) { + // apply final range restriction + commStats s = pComm->getStats(); +#if RSDEBUG + DEBUGLOG << "RunModel(): final restriction yr=" << yr + << " s.minY=" << s.minY << " s.maxY=" << s.maxY + << endl; +#endif + pLandscape->setLandLimits(ppLand.minX, s.minY, ppLand.maxX, s.maxY); + updateCC = true; + } + } + } + // environmental gradient, stochasticity & local extinction + // or dynamic landscape + updateland = false; + if (env.stoch || grad.gradient || ppLand.dynamic) { + if (grad.shifting && yr > grad.shift_begin && yr < grad.shift_stop) { + paramsGrad->incrOptY(); + pLandscape->setEnvGradient(pSpecies, false); + updateCC = true; + } + if (env.stoch) { + if (env.local) pLandscape->updateLocalStoch(); + updateCC = true; + } + if (ppLand.dynamic) { +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " landChg.chgnum=" << landChg.chgnum + << " landChg.chgyear=" << landChg.chgyear + << " npatchchanges=" << npatchchanges << " ncostchanges=" << ncostchanges + << " ixpchchg=" << ixpchchg << " ixcostchg=" << ixcostchg + << endl; +#endif + if (yr == landChg.chgyear) { // apply landscape change + landIx = landChg.chgnum; + updateland = updateCC = true; + if (ppLand.patchModel) { // apply any patch changes + Patch* pPatch; + Cell* pCell; + patchchange = pLandscape->getPatchChange(ixpchchg++); + while (patchchange.chgnum <= landIx && ixpchchg <= npatchchanges) { + + // move cell from original patch to new patch + pCell = pLandscape->findCell(patchchange.x, patchchange.y); + if (patchchange.oldpatch != 0) { // not matrix + pPatch = pLandscape->findPatch(patchchange.oldpatch); + pPatch->removeCell(pCell); + } + if (patchchange.newpatch == 0) { // matrix + pPatch = 0; + } + else { + pPatch = pLandscape->findPatch(patchchange.newpatch); + pPatch->addCell(pCell, patchchange.x, patchchange.y); + } + pCell->setPatch((intptr)pPatch); + // get next patch change + patchchange = pLandscape->getPatchChange(ixpchchg++); + } + ixpchchg--; + pLandscape->resetPatches(); // reset patch limits + } + if (landChg.costfile != "NULL") { // apply any SMS cost changes +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " landChg.costfile=" << landChg.costfile << endl; +#endif + Cell* pCell; + costchange = pLandscape->getCostChange(ixcostchg++); + while (costchange.chgnum <= landIx && ixcostchg <= ncostchanges) { + pCell = pLandscape->findCell(costchange.x, costchange.y); + if (pCell != 0) { + pCell->setCost(costchange.newcost); + } + costchange = pLandscape->getCostChange(ixcostchg++); + } + ixcostchg--; + pLandscape->resetEffCosts(); + } + if (landIx < pLandscape->numLandChanges()) { // get next change + landChg = pLandscape->getLandChange(landIx); + } + else { + landChg.chgyear = 9999999; + } + } + } + } // end of environmental gradient, etc. + + if (updateCC) { + pLandscape->updateCarryingCapacity(pSpecies, yr, landIx); + } + + + if (sim.outConnect && ppLand.patchModel) + pLandscape->resetConnectMatrix(); + + if (ppLand.dynamic && updateland) { + if (trfr.moveModel && trfr.moveType == 1) { // SMS + if (!trfr.costMap) pLandscape->resetCosts(); // in case habitats have changed + } + // apply effects of landscape change to species present in changed patches + pComm->patchChanges(); +#if RS_RCPP + pComm->dispersal(landIx, yr); +#else + pComm->dispersal(landIx); +#endif // RS_RCPP + } + if (init.restrictRange) { + // remove any population from region removed from restricted range + if (yr > init.initFrzYr && yr < init.finalFrzYr) { + if ((yr - init.initFrzYr) % init.restrictFreq == 0) { + pComm->patchChanges(); + } + } + } + + if (init.seedType == 2) { + // add any new initial individuals for the current year + pComm->initialise(pSpecies, yr); + } + + for (int gen = 0; gen < dem.repSeasons; gen++) // generation loop + { +#if RSDEBUG + // TEMPORARY RANDOM STREAM CHECK + if (yr % 1 == 0) + { + DEBUGLOG << endl << "RunModel(): start of gen " << gen << " in year " << yr + << " for rep " << rep << " ("; + for (int i = 0; i < 5; i++) { + int rrrr = pRandom->IRandom(1000, 2000); + DEBUGLOG << " " << rrrr; + } + DEBUGLOG << " )" << endl; + } +#endif + + if (v.viewPop || (sim.saveMaps && yr % sim.mapInt == 0)) { + if (updateland && gen == 0) { + pLandscape->drawLandscape(rep, landIx, ppLand.landNum); + } + pComm->draw(rep, yr, gen, ppLand.landNum); + } + // Output and pop. visualisation before reproduction + if (v.viewPop || v.viewTraits || sim.outOccup + || sim.outTraitsCells || sim.outTraitsRows || sim.saveMaps) + PreReproductionOutput(pLandscape, pComm, rep, yr, gen); + // for non-structured population, also produce range and population output now + if (!dem.stageStruct && (sim.outRange || sim.outPop)) + RangePopOutput(pComm, rep, yr, gen); +#if RS_RCPP && !R_CMD + if (sim.ReturnPopRaster && sim.outPop && yr >= sim.outStartPop && yr % sim.outIntPop == 0) { + list_outPop.push_back(pComm->addYearToPopList(rep, yr), "rep" + std::to_string(rep) + "_year" + std::to_string(yr)); + } +#endif + // apply local extinction for generation 0 only + // CHANGED TO *BEFORE* RANGE & POPN OUTPUT PRODUCTION IN v1.1, + // SO THAT NOS. OF JUVENILES BORN CAN BE REPORTED + if (!ppLand.patchModel && gen == 0) { + if (env.localExt) pComm->localExtinction(0); + if (grad.gradient && grad.gradType == 3) pComm->localExtinction(1); + } + + // reproduction + pComm->reproduction(yr); + + if (dem.stageStruct) { + if (sstruct.survival == 0) { // at reproduction + pComm->survival(0, 2, 1); // survival of all non-juvenile stages + } + } + + // Output and pop. visualisation AFTER reproduction + if (dem.stageStruct && (sim.outRange || sim.outPop)) + RangePopOutput(pComm, rep, yr, gen); + +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed reproduction" << endl; +#endif + + // Dispersal + + pComm->emigration(); +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed emigration" << endl; +#endif +#if RS_RCPP + pComm->dispersal(landIx, yr); +#else + pComm->dispersal(landIx); +#endif // RS_RCPP +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed dispersal" << endl; +#endif + + // survival part 0 + if (dem.stageStruct) { + if (sstruct.survival == 0) { // at reproduction + pComm->survival(0, 0, 1); // survival of juveniles only + } + if (sstruct.survival == 1) { // between reproduction events + pComm->survival(0, 1, 1); // survival of all stages + } + if (sstruct.survival == 2) { // annually + pComm->survival(0, 1, 0); // development only of all stages + } + } + else { // non-structured population + pComm->survival(0, 1, 1); + } +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed survival part 0" << endl; +#endif + + + // output Individuals + if (sim.outInds && yr >= sim.outStartInd && yr % sim.outIntInd == 0) + pComm->outInds(rep, yr, gen, -1); + // output Genetics + if (sim.outGenetics && yr >= sim.outStartGenetic && yr % sim.outIntGenetic == 0) + pComm->outGenetics(rep, yr, gen, -1); + + // survival part 1 + if (dem.stageStruct) { + pComm->survival(1, 0, 1); + } + else { // non-structured population + pComm->survival(1, 0, 1); + } +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed survival part 1" << endl; +#endif + + } // end of the generation loop +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " completed generation loop" << endl; +#endif + + totalInds = pComm->totalInds(); + if (totalInds <= 0) { yr++; break; } + + // Connectivity Matrix + if (sim.outConnect && ppLand.patchModel + && yr >= sim.outStartConn && yr % sim.outIntConn == 0) + pLandscape->outConnect(rep, yr); + + if (dem.stageStruct && sstruct.survival == 2) { // annual survival - all stages + pComm->survival(0, 1, 2); + pComm->survival(1, 0, 1); +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " completed annual survival" << endl; +#endif + } + + if (dem.stageStruct) { + pComm->ageIncrement(); // increment age of all individuals + if (sim.outInds && yr >= sim.outStartInd && yr % sim.outIntInd == 0) + pComm->outInds(rep, yr, -1, -1); // list any individuals dying having reached maximum age + pComm->survival(1, 0, 1); // delete any such individuals +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " completed Age_increment and final survival" << endl; +#endif + totalInds = pComm->totalInds(); + if (totalInds <= 0) { yr++; break; } + } + + } // end of the years loop + + // Final output and popn. visualisation +#if BATCH + if (sim.saveMaps && yr % sim.mapInt == 0) { + if (updateland) { + pLandscape->drawLandscape(rep, landIx, ppLand.landNum); + } + pComm->draw(rep, yr, 0, ppLand.landNum); + } +#endif + // produce final summary output + if (v.viewPop || v.viewTraits || sim.outOccup + || sim.outTraitsCells || sim.outTraitsRows || sim.saveMaps) + PreReproductionOutput(pLandscape, pComm, rep, yr, 0); + if (sim.outRange || sim.outPop) + RangePopOutput(pComm, rep, yr, 0); +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " completed final summary output" << endl; +#endif + + pComm->resetPopns(); + + //Reset the gradient optimum + if (grad.gradient) paramsGrad->resetOptY(); + + pLandscape->resetLandLimits(); +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " landIx=" << "reset" + << " npatchchanges=" << npatchchanges << " ncostchanges=" << ncostchanges + << " ixpchchg=" << ixpchchg << " ixcostchg=" << ixcostchg + << endl; +#endif + if (ppLand.patchModel && ppLand.dynamic && ixpchchg > 0) { + // apply any patch changes to reset landscape to original configuration + // (provided that at least one has already occurred) + patchChange patchchange; + Patch* pPatch; + Cell* pCell; + patchchange = pLandscape->getPatchChange(ixpchchg++); + while (patchchange.chgnum <= 666666 && ixpchchg <= npatchchanges) { + + // move cell from original patch to new patch + pCell = pLandscape->findCell(patchchange.x, patchchange.y); + if (patchchange.oldpatch != 0) { // not matrix + pPatch = pLandscape->findPatch(patchchange.oldpatch); + pPatch->removeCell(pCell); + } + if (patchchange.newpatch == 0) { // matrix + pPatch = 0; + } + else { + pPatch = pLandscape->findPatch(patchchange.newpatch); + pPatch->addCell(pCell, patchchange.x, patchchange.y); + } + pCell->setPatch((intptr)pPatch); + // get next patch change + patchchange = pLandscape->getPatchChange(ixpchchg++); + } + ixpchchg--; + pLandscape->resetPatches(); + } + if (ppLand.dynamic) { + trfrRules trfr = pSpecies->getTrfr(); + if (trfr.moveModel && trfr.moveType == 1) { // SMS + if (ixcostchg > 0) { + // apply any cost changes to reset landscape to original configuration + // (provided that at least one has already occurred) + Cell* pCell; + costchange = pLandscape->getCostChange(ixcostchg++); + while (costchange.chgnum <= 666666 && ixcostchg <= ncostchanges) { + + pCell = pLandscape->findCell(costchange.x, costchange.y); + if (pCell != 0) { + pCell->setCost(costchange.newcost); + } + costchange = pLandscape->getCostChange(ixcostchg++); + } + ixcostchg--; + pLandscape->resetEffCosts(); + } + if (!trfr.costMap) pLandscape->resetCosts(); // in case habitats have changed + } + } +#if RSDEBUG + DEBUGLOG << "RunModel(): yr=" << yr << " completed reset" + << endl; +#endif + + if (sim.outConnect && ppLand.patchModel) + pLandscape->resetConnectMatrix(); // set connectivity matrix to zeroes + + if (sim.outInds) // close Individuals output file + pComm->outInds(rep, 0, 0, -999); + if (sim.outGenetics) // close Genetics output file + pComm->outGenetics(rep, 0, 0, -999); + + if (sim.saveVisits) { + pLandscape->outVisits(rep, ppLand.landNum); + pLandscape->resetVisits(); + } + +#if RS_RCPP + if (sim.outPaths) + pLandscape->outPathsHeaders(rep, -999); +#endif +#if RSDEBUG + DEBUGLOG << endl << "RunModel(): finished rep=" << rep << endl; +#endif + + } // end of the replicates loop + + if (sim.outConnect && ppLand.patchModel) { + pLandscape->deleteConnectMatrix(); + pLandscape->outConnectHeaders(-999); // close Connectivity Matrix file + } + + // Occupancy outputs + if (sim.outOccup && sim.reps > 1) { + MemoLine("Writing final occupancy output..."); + pComm->outOccupancy(); + pComm->outOccSuit(v.viewGraph); + pComm->deleteOccupancy((sim.years / sim.outIntOcc) + 1); + pComm->outOccupancyHeaders(-999); + MemoLine("...finished"); + } + + if (sim.outRange) { + pComm->outRangeHeaders(pSpecies, -999); // close Range file + } + if (sim.outPop) { + pComm->outPopHeaders(pSpecies, -999); // close Population file + } + if (sim.outTraitsCells) + pComm->outTraitsHeaders(pSpecies, -999); // close Traits file + if (sim.outTraitsRows) + pComm->outTraitsRowsHeaders(pSpecies, -999); // close Traits rows file + // close Individuals & Genetics output files if open + // they can still be open if the simulation was stopped by the user + if (sim.outInds) pComm->outInds(0, 0, 0, -999); + if (sim.outGenetics) pComm->outGenetics(0, 0, 0, -999); + + MemoLine("Deleting community..."); + delete pComm; pComm = 0; + MemoLine("...finished"); + +#if RS_RCPP && !R_CMD + return list_outPop; +#else + return 0; +#endif + +} + +#if RS_EMBARCADERO || LINUX_CLUSTER || RS_RCPP +// Check whether a specified directory path exists +bool is_directory(const char* pathname) { + struct stat info; + if (stat(pathname, &info) != 0) return false; // path does not exist + if (S_ISDIR(info.st_mode)) return true; + return false; +} +#endif + +//--------------------------------------------------------------------------- +bool CheckDirectory(void) +{ + bool errorfolder = false; + + string subfolder; + + subfolder = paramsSim->getDir(0) + "Inputs"; + const char* inputs = subfolder.c_str(); + if (!is_directory(inputs)) errorfolder = true; + subfolder = paramsSim->getDir(0) + "Outputs"; + const char* outputs = subfolder.c_str(); + if (!is_directory(outputs)) errorfolder = true; + subfolder = paramsSim->getDir(0) + "Output_Maps"; + const char* outputmaps = subfolder.c_str(); + if (!is_directory(outputmaps)) errorfolder = true; + + return errorfolder; +} + +//--------------------------------------------------------------------------- +//For outputs and population visualisations pre-reproduction +void PreReproductionOutput(Landscape* pLand, Community* pComm, int rep, int yr, int gen) +{ +#if RSDEBUG + landParams ppLand = pLand->getLandParams(); +#endif + simParams sim = paramsSim->getSim(); + simView v = paramsSim->getViews(); + +#if RSDEBUG + DEBUGLOG << "PreReproductionOutput(): 11111 rep=" << rep << " yr=" << yr << " gen=" << gen + << " landNum=" << ppLand.landNum << " maxX=" << ppLand.maxX << " maxY=" << ppLand.maxY + << endl; + DEBUGLOG << "PreReproductionOutput(): 11112 outRange=" << sim.outRange + << " outIntRange=" << sim.outIntRange + << " outPop=" << sim.outPop << " outIntPop=" << sim.outIntPop + << endl; +#endif + + traitCanvas tcanv; + for (int i = 0; i < NTRAITS; i++) { + tcanv.pcanvas[i] = 0; + } + + // trait outputs and visualisation + + if (v.viewTraits) { + tcanv = SetupTraitCanvas(); + } + + if (v.viewTraits + || ((sim.outTraitsCells && yr >= sim.outStartTraitCell && yr % sim.outIntTraitCell == 0) || + (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0))) + { + pComm->outTraits(tcanv, pSpecies, rep, yr, gen); + } + if (sim.outOccup && yr % sim.outIntOcc == 0 && gen == 0) + pComm->updateOccupancy(yr / sim.outIntOcc, rep); +} + +//For outputs and population visualisations pre-reproduction +void RangePopOutput(Community* pComm, int rep, int yr, int gen) +{ + simParams sim = paramsSim->getSim(); + + if (sim.outRange && (yr % sim.outIntRange == 0 || pComm->totalInds() <= 0)) + pComm->outRange(pSpecies, rep, yr, gen); + + if (sim.outPop && yr >= sim.outStartPop && yr % sim.outIntPop == 0) + pComm->outPop(rep, yr, gen); + +} + +//--------------------------------------------------------------------------- +void OutParameters(Landscape* pLandscape) +{ + double k; + //int nrows,ncols,nsexes,nstages; + int nsexes, nstages; + + landParams ppLand = pLandscape->getLandParams(); + genLandParams ppGenLand = pLandscape->getGenLandParams(); + envGradParams grad = paramsGrad->getGradient(); + envStochParams env = paramsStoch->getStoch(); + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + settleRules srules; + settleSteps ssteps; + settleTraits settleDD; + simParams sim = paramsSim->getSim(); + + string name; + if (sim.batchMode) + name = paramsSim->getDir(2) + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + + "_Land" + Int2Str(ppLand.landNum) + "_Parameters.txt"; + else + name = paramsSim->getDir(2) + "Sim" + Int2Str(sim.simulation) + "_Parameters.txt"; + outPar.open(name.c_str()); + + outPar << "RangeShifter 2.0 "; + +#if !RS_RCPP +#if RSWIN64 + outPar << " - 64 bit implementation"; +#else + outPar << " - 32 bit implementation"; +#endif +#endif + outPar << endl; + + outPar << "================ "; + + outPar << " ====================="; + outPar << endl << endl; + + outPar << "BATCH MODE \t"; + if (sim.batchMode) outPar << "yes" << endl; else outPar << "no" << endl; +#if RS_RCPP + outPar << "SEED \t" << RS_random_seed << endl; +#endif + outPar << "REPLICATES \t" << sim.reps << endl; + outPar << "YEARS \t" << sim.years << endl; + outPar << "REPRODUCTIVE SEASONS / YEAR\t" << dem.repSeasons << endl; + if (ppLand.patchModel) { + outPar << "PATCH-BASED MODEL" << endl; + outPar << "No. PATCHES \t" << pLandscape->patchCount() - 1 << endl; + } + else + outPar << "CELL-BASED MODEL" << endl; + outPar << "BOUNDARIES \t"; + if (sim.absorbing) outPar << "absorbing" << endl; + else outPar << "reflective" << endl; + outPar << endl; + + outPar << "LANDSCAPE:\t"; + if (ppLand.generated) { + outPar << "artificially generated map" << endl; + outPar << "TYPE: \t"; + if (ppGenLand.continuous) outPar << "continuous \t"; + else outPar << "discrete \t"; + if (ppGenLand.fractal) outPar << "fractal"; + else outPar << "random"; + outPar << endl << "PROPORTION OF SUITABLE HABITAT (p)\t" << ppGenLand.propSuit << endl; + if (ppGenLand.fractal) outPar << "HURST EXPONENT\t" << ppGenLand.hurst << endl; + } + else { + outPar << "imported map" << endl; + outPar << "TYPE: \t"; + switch (ppLand.rasterType) { + case 0: + outPar << "habitat codes" << endl; + break; + case 1: + outPar << "habitat % cover" << endl; + break; + case 2: + outPar << "habitat quality" << endl; + break; + } + outPar << "FILE NAME: "; +#if RS_RCPP + if (ppLand.dynamic) { + outPar << name_landscape << endl; + } + else { + outPar << name_landscape << endl; + } + if (ppLand.patchModel) { + outPar << "PATCH FILE: " << name_patch << endl; + } + if (trfr.costMap) { + outPar << "COSTS FILE: " << name_costfile << endl; + } +#else + if (sim.batchMode) outPar << " (see batch file) " << landFile << endl; + else { + outPar << habmapname << endl; + if (ppLand.rasterType == 1) { // habitat % cover - list additional layers + for (int i = 0; i < ppLand.nHab - 1; i++) { + outPar << " " << hfnames[i] << endl; + } + } + if (ppLand.patchModel) { + outPar << "PATCH FILE: " << patchmapname << endl; + } + } +#endif + outPar << "No. HABITATS:\t" << ppLand.nHab << endl; + } + outPar << "RESOLUTION (m): \t" << ppLand.resol << endl; + outPar << "DIMENSIONS: X " << ppLand.dimX << " Y " << ppLand.dimY << endl; + outPar << "AVAILABLE: min.X " << ppLand.minX << " min.Y " << ppLand.minY + << " max.X " << ppLand.maxX << " max.Y " << ppLand.maxY << endl; + if (!ppLand.generated && ppLand.dynamic) { + landChange chg; + outPar << "DYNAMIC LANDSCAPE: " << endl; + int nchanges = pLandscape->numLandChanges(); + for (int i = 0; i < nchanges; i++) { + chg = pLandscape->getLandChange(i); + outPar << "Change no. " << chg.chgnum << " in year " << chg.chgyear << endl; + outPar << "Landscape: " << chg.habfile << endl; + if (ppLand.patchModel) { + outPar << "Patches : " << chg.pchfile << endl; + } + if (chg.costfile != "none" && chg.costfile != "NULL") { + outPar << "Costs : " << chg.costfile << endl; + } + } + } + + outPar << endl << "SPECIES DISTRIBUTION LOADED: \t"; + if (ppLand.spDist) + { + outPar << "yes" << endl; + outPar << "RESOLUTION (m)\t" << ppLand.spResol << endl; + outPar << "FILE NAME: "; +#if !RS_RCPP + if (sim.batchMode) outPar << " (see batch file) " << landFile << endl; + else { + outPar << distnmapname << endl; + } +#else + outPar << name_sp_dist << endl; +#endif + } + else outPar << "no" << endl; + + outPar << endl << "ENVIRONMENTAL GRADIENT:\t "; + if (grad.gradient) + { + switch (grad.gradType) { + case 1: + if (dem.stageStruct) outPar << "Density dependence strength (1/b)" << endl; + else outPar << "Carrying capacity (K)" << endl; + break; + case 2: + if (dem.stageStruct) outPar << "Fecundity" << endl; + else outPar << "Intrinsic growth rate (r)" << endl; + break; + case 3: + outPar << "Local extinction probability" << endl; + break; + default: + outPar << "ERROR ERROR ERROR" << endl; + ; + } + outPar << "G:\t\t " << grad.grad_inc << endl; + outPar << "optimum Y:\t " << grad.opt_y << endl; + outPar << "f:\t\t " << grad.factor << endl; + if (grad.gradType == 3) outPar << "Local extinction prob. at optimum:\t " + << grad.extProbOpt << endl; + outPar << "GRADIENT SHIFTING:\t "; + if (grad.shifting) + { + outPar << "yes" << endl; + outPar << "SHIFTING RATE (rows/year):\t " << grad.shift_rate << endl; + outPar << "SHIFTING START (year):\t\t " << grad.shift_begin << endl; + outPar << "SHIFTING STOP (year):\t\t " << grad.shift_stop << endl; + } + else outPar << "no" << endl; + } + else outPar << "no"; + outPar << endl; + outPar << "ENVIRONMENTAL STOCHASTICITY:\t"; + if (env.stoch) { + outPar << "yes" << endl; + outPar << "TYPE\t in "; + if (dem.stageStruct) { + if (env.inK) outPar << "1/b" << endl; + else outPar << "fecundity" << endl; + } + else { + if (env.inK) outPar << "K" << endl; + else outPar << "R" << endl; + } + outPar << "SPATIAL AUTOCORRELATION\t "; + if (env.local) outPar << "local" << endl; + else outPar << "global" << endl; + outPar << "TEMPORAL AUTOCORRELATION (ac)\t" << env.ac << endl; + outPar << "AMPLITUDE (std)\t" << env.std << endl; + if (dem.stageStruct) { + if (env.inK) { + outPar << "MIN. 1/b\t" << pSpecies->getMinMax(0) + * (10000.0 / (float)(ppLand.resol * ppLand.resol)) << endl; + outPar << "MAX. 1/b\t" << pSpecies->getMinMax(1) + * (10000.0 / (float)(ppLand.resol * ppLand.resol)) << endl; + } + else { + outPar << "MIN. fecundity\t" << pSpecies->getMinMax(0) << endl; + outPar << "MAX. fecundity\t" << pSpecies->getMinMax(1) << endl; + } + } + else { + if (env.inK) { + outPar << "MIN. K\t" << pSpecies->getMinMax(0) + * (10000.0 / (float)(ppLand.resol * ppLand.resol)) << endl; + outPar << "MAX. K\t" << pSpecies->getMinMax(1) + * (10000.0 / (float)(ppLand.resol * ppLand.resol)) << endl; + } + else { + outPar << "MIN. r\t" << pSpecies->getMinMax(0) << endl; + outPar << "MAX. r\t" << pSpecies->getMinMax(1) << endl; + } + } + } + else outPar << "no" << endl; + outPar << "LOCAL EXTINCTION PROBABILITY:\t"; + if (env.localExt) outPar << env.locExtProb << endl; + else outPar << "0.0" << endl; + + outPar << endl << "SPECIES' PARAMETERS." << endl; + outPar << "REPRODUCTION:" << endl; + outPar << "TYPE: "; + switch (dem.repType) { + case 0: + outPar << "Asexual / Only female model" << endl; + break; + case 1: + outPar << "Sexual model (simple)"; + outPar << endl; + outPar << "PROP. of MALES\t" << dem.propMales << endl; + break; + case 2: + outPar << "Sexual model (explicit mating system)" << endl; + outPar << "PROP. of MALES\t" << dem.propMales << endl; + outPar << "MAX. HAREM SIZE (h)\t" << dem.harem << endl; + break; + } + outPar << "STAGE STRUCTURE:\t"; + if (dem.stageStruct) { + outPar << "yes" << endl; + outPar << "PROBABILITY OF REPRODUCING IN SUBSEQUENT SEASONS\t" << sstruct.probRep << endl; + outPar << "No. OF REP. SEASONS BEFORE SUBSEQUENT REPRODUCTIONS\t" << sstruct.repInterval << endl; + if (!ppLand.generated && ppLand.dynamic) { + outPar << "ACTION AFTER POPULATION DESTRUCTION: all individuals "; + if (sstruct.disperseOnLoss) outPar << "disperse" << endl; + else outPar << "die" << endl; + } + outPar << "No. STAGES\t" << sstruct.nStages << endl; + outPar << "MAX. AGE\t" << sstruct.maxAge << endl; + // no sex-specific demographic parameters + if (dem.repType != 2) { + outPar << "MIN. AGES:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage\t" << i << ":\t" << pSpecies->getMinAge(i, 0) << "\tyears" << endl; + } + outPar << "FECUNDITIES:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage\t" << i << ":\t" << pSpecies->getFec(i, 0) << endl; + } + outPar << "DEVELOPMENT PROB.:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage\t" << i << ":\t" << pSpecies->getDev(i, 0) << endl; + } + outPar << "SURVIVAL PROB.:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage\t" << i << ":\t" << pSpecies->getSurv(i, 0) << endl; + } + } + // sex-specific demographic parameters + else { + outPar << "MIN. AGES:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "males " << i << ":\t" << pSpecies->getMinAge(i, 1) << " years;\t"; + outPar << "females " << i << ":\t" << pSpecies->getMinAge(i, 0) << " years" << endl; + } + outPar << "FECUNDITIES:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "males " << i << ":\t" << pSpecies->getFec(i, 1) << endl; + outPar << "females " << i << ":\t" << pSpecies->getFec(i, 0) << endl; + } + outPar << "DEVELOPMENT PROB.:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "males " << i << ":\t" << pSpecies->getDev(i, 1) << endl; + outPar << "females " << i << ":\t" << pSpecies->getDev(i, 0) << endl; + } + outPar << "SURVIVAL PROB.:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "males " << i << ":\t" << pSpecies->getSurv(i, 1) << endl; + outPar << "females " << i << ":\t" << pSpecies->getSurv(i, 0) << endl; + } + } + + outPar << "SCHEDULING OF SURVIVAL: "; + switch (sstruct.survival) { + case 0: + outPar << "At reproduction" << endl; + break; + case 1: + outPar << "Between reproductive events" << endl; + break; + case 2: + outPar << "Annually" << endl; + break; + } + + int mSize; // index for weights matrices + if (dem.repType == 2) mSize = sstruct.nStages * NSEXES; + else mSize = sstruct.nStages; + + outPar << "DENSITY-DEPENDENCE IN FECUNDITY:\t"; + if (sstruct.fecDens) { + outPar << "yes" << endl; + if (sstruct.fecStageDens) { + outPar << "STAGE'S WEIGHTS:" << endl; + for (int i = 0; i < mSize; i++) { + if (dem.repType == 2) { + outPar << "stage " << i / NSEXES << " "; + if (i % NSEXES == 0) outPar << "males : \t"; + else outPar << "females: \t"; + } + else outPar << "stage " << i << ": \t"; + for (int j = 0; j < mSize; j++) outPar << pSpecies->getDDwtFec(j, i) << "\t"; + outPar << endl; + } + } + else outPar << "not stage-dependent" << endl; + } + else outPar << "no" << endl; + + densDepParams ddparams = pSpecies->getDensDep(); + + outPar << "DENSITY-DEPENDENCE IN DEVELOPMENT:\t"; + if (sstruct.devDens) { + outPar << "yes - coefficient: " << ddparams.devCoeff << endl; + if (sstruct.devStageDens) { + outPar << "STAGE'S WEIGHTS:" << endl; + for (int i = 0; i < mSize; i++) { + if (dem.repType == 2) { + outPar << "stage " << i / NSEXES << " "; + if (i % NSEXES == 0) outPar << "males : \t"; + else outPar << "females: \t"; + } + else outPar << "stage " << i << ": \t"; + for (int j = 0; j < mSize; j++) outPar << pSpecies->getDDwtDev(j, i) << "\t"; + outPar << endl; + } + } + else outPar << "not stage-dependent" << endl; + } + else outPar << "no" << endl; + + outPar << "DENSITY-DEPENDENCE IN SURVIVAL:\t\t"; + if (sstruct.survDens) { + outPar << "yes - coefficient: " << ddparams.survCoeff << endl; + if (sstruct.survStageDens) { + outPar << "STAGE'S WEIGHTS:" << endl; + for (int i = 0; i < mSize; i++) { + if (dem.repType == 2) { + outPar << "stage " << i / NSEXES << " "; + if (i % NSEXES == 0) outPar << "males : \t"; + else outPar << "females: \t"; + } + else outPar << "stage " << i << ": \t"; + for (int j = 0; j < mSize; j++) outPar << pSpecies->getDDwtSurv(j, i) << "\t"; + outPar << endl; + } + } + else outPar << "not stage-dependent" << endl; + } + else outPar << "no" << endl; + } // end of if (dem.stageStruct) + else { // not stage-strutured + outPar << "no" << endl; + outPar << "Rmax\t" << dem.lambda << endl; + outPar << "bc\t" << dem.bc << endl; + } + + if (dem.stageStruct) { + outPar << endl << "HABITAT SPECIFIC 1/b:" << endl; + } + else { + outPar << endl << "CARRYING CAPACITIES:" << endl; + } + int nhab = ppLand.nHab; + if (ppLand.generated) { + if (ppGenLand.continuous) nhab = 1; + } + for (int i = 0; i < nhab; i++) { + k = pSpecies->getHabK(i) * (10000.0 / (float)(ppLand.resol * ppLand.resol)); + if (!ppLand.generated && ppLand.rasterType == 0) { // imported & habitat codes + outPar << "Habitat " << pLandscape->getHabCode(i) << ": \t"; + } + else { + outPar << "Habitat " << i << ": "; + } + if (dem.stageStruct) outPar << "1/b "; + else outPar << "K "; + outPar << k << endl; + } + + emigTraits ep0, ep1; + emigParams eparams0, eparams1; + string sexdept = "SEX-DEPENDENT: "; + string stgdept = "STAGE-DEPENDENT: "; + string indvar = "INDIVIDUAL VARIABILITY: "; + string emigstage = "EMIGRATION STAGE: "; + + outPar << endl << "DISPERSAL - EMIGRATION:\t"; + if (emig.densDep) { + outPar << "density-dependent" << endl; + + if (emig.sexDep) { + outPar << sexdept << "yes" << endl; + if (emig.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage " << i << ":" << endl; + ep0 = pSpecies->getEmigTraits(i, 0); + ep1 = pSpecies->getEmigTraits(i, 1); + outPar << "D0: females " << ep0.d0 << " males " << ep1.d0 << endl; + outPar << "alpha: females " << ep0.alpha << " males " << ep1.alpha << endl; + outPar << "beta: females " << ep0.beta << " males " << ep1.beta << endl; + } + } + else { // !emig.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (emig.indVar) { + eparams0 = pSpecies->getEmigParams(0, 0); + eparams1 = pSpecies->getEmigParams(0, 1); + outPar << "yes" << endl; + if (dem.stageStruct) { + outPar << emigstage << emig.emigStage << endl; + } + outPar << "D0 females: mean " << eparams0.d0Mean << " s.d. " << eparams0.d0SD + << " scaling factor " << eparams0.d0Scale << endl; + outPar << "D0 males: mean " << eparams1.d0Mean << " s.d. " << eparams1.d0SD + << " scaling factor " << eparams1.d0Scale << endl; + outPar << "Alpha females: mean " << eparams0.alphaMean << " s.d. " << eparams0.alphaSD + << " scaling factor " << eparams0.alphaScale << endl; + outPar << "Alpha males: mean " << eparams1.alphaMean << " s.d. " << eparams1.alphaSD + << " scaling factor " << eparams1.alphaScale << endl; + outPar << "Beta females: mean " << eparams0.betaMean << " s.d. " << eparams0.betaSD + << " scaling factor " << eparams0.betaScale << endl; + outPar << "Beta males: mean " << eparams1.betaMean << " s.d. " << eparams1.betaSD + << " scaling factor " << eparams1.betaScale << endl; + } + else { + outPar << "no" << endl; + ep0 = pSpecies->getEmigTraits(0, 0); + ep1 = pSpecies->getEmigTraits(0, 1); + outPar << "D0: females " << ep0.d0 << " males " << ep1.d0 << endl; + outPar << "alpha: females " << ep0.alpha << " males " << ep1.alpha << endl; + outPar << "beta: females " << ep0.beta << " males " << ep1.beta << endl; + } + } + } + else { // !emig.sexDep + outPar << sexdept << "no" << endl; + if (emig.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + ep0 = pSpecies->getEmigTraits(i, 0); + outPar << "stage " << i << ": \t" << "D0: " << ep0.d0; + outPar << " \talpha: " << ep0.alpha << " \tbeta: " << ep0.beta << endl; + } + } + else { // !emig.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (emig.indVar) { + eparams0 = pSpecies->getEmigParams(0, 0); + emigScales scale = pSpecies->getEmigScales(); + outPar << "yes" << endl; + if (dem.stageStruct) { + outPar << emigstage << emig.emigStage << endl; + } + outPar << "D0 mean: " << eparams0.d0Mean << " s.d.: " << eparams0.d0SD + << " scaling factor: " << scale.d0Scale << endl; + outPar << "Alpha mean: " << eparams0.alphaMean << " s.d.: " << eparams0.alphaSD + << " scaling factor: " << scale.alphaScale << endl; + outPar << "Beta mean: " << eparams0.betaMean << " s.d.: " << eparams0.betaSD + << " scaling factor: " << scale.betaScale << endl; + } + else { + outPar << "no" << endl; + ep0 = pSpecies->getEmigTraits(0, 0); + outPar << "D0: " << ep0.d0 << endl; + outPar << "alpha: " << ep0.alpha << endl; + outPar << "beta: " << ep0.beta << endl; + } + } + } + } + else { // not density-dependent + string initprob = "INITIAL EMIGRATION PROB. "; + outPar << "density-independent" << endl; + if (!trfr.moveModel) { // transfer by kernel + outPar << "USE FULL KERNEL TO DETERMINE EMIGRATION: "; + if (pSpecies->useFullKernel()) outPar << "yes"; + else outPar << "no"; + outPar << endl; + } + + if (emig.sexDep) { + outPar << sexdept << "yes" << endl; + if (emig.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage " << i << ": \t" << "EMIGRATION PROB.: \tfemales " + << pSpecies->getEmigD0(i, 0) << " \tmales " << pSpecies->getEmigD0(i, 1) << endl; + } + } + else { // !emig.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (emig.indVar) { + eparams0 = pSpecies->getEmigParams(0, 0); + eparams1 = pSpecies->getEmigParams(0, 1); + emigScales scale = pSpecies->getEmigScales(); + outPar << "yes" << endl; + if (dem.stageStruct) { + outPar << emigstage << emig.emigStage << endl; + } + outPar << initprob << "mean: " << "females " << eparams0.d0Mean + << " males " << eparams1.d0Mean << endl; + outPar << initprob << "s.d.: " << "females " << eparams0.d0SD + << " males " << eparams1.d0SD << endl; + outPar << initprob << "scaling factor: " << scale.d0Scale + << endl; + } + else { + outPar << "no" << endl; + outPar << "EMIGRATION PROB.: \tfemales " << pSpecies->getEmigD0(0, 0) + << "\t males " << pSpecies->getEmigD0(0, 1) << endl; + } + } + } + else { // !emig.sexDep + outPar << sexdept << "no" << endl; + if (emig.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage " << i << ": \t" << "EMIGRATION PROB.: " + << pSpecies->getEmigD0(i, 0) << endl; + } + } + else { // !emig.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (emig.indVar) { + eparams0 = pSpecies->getEmigParams(0, 0); + emigScales scale = pSpecies->getEmigScales(); + outPar << "yes" << endl; + if (dem.stageStruct) { + outPar << emigstage << emig.emigStage << endl; + } + outPar << initprob << "mean: " << eparams0.d0Mean << endl; + outPar << initprob << "s.d.: " << eparams0.d0SD << endl; + outPar << initprob << "scaling factor: " << scale.d0Scale << endl; + } + else { + outPar << "no" << endl; + outPar << "EMIGRATION PROB.:\t" << pSpecies->getEmigD0(0, 0) << endl; + } + } + } + } + + // Transfer + + outPar << endl << "DISPERSAL - TRANSFER: \t"; + + if (trfr.moveModel) { + bool straigtenPath; + if (trfr.moveType == 1) { // SMS + trfrSMSTraits move = pSpecies->getSMSTraits(); + straigtenPath = move.straigtenPath; + if (trfr.costMap) { + outPar << "SMS\tcosts from imported cost map" << endl; +#if !RS_RCPP + outPar << "FILE NAME: " << costmapname << endl; +#endif + } + else { + outPar << "SMS\tcosts:" << endl; + if (!ppLand.generated && ppLand.rasterType == 0) { + for (int i = 0; i < ppLand.nHab; i++) + outPar << "\thab. " << pLandscape->getHabCode(i) << "\t" + << pSpecies->getHabCost(i) << endl; + } + else { + for (int i = 0; i < ppLand.nHab; i++) + outPar << "\thab. " << i << "\t" + << pSpecies->getHabCost(i) << endl; + } + } + string pr = "PERCEPTUAL RANGE"; + outPar << pr << ": " << move.pr << endl; + outPar << pr << " METHOD: " << move.prMethod << endl; + if (!trfr.indVar) outPar << "DIRECTIONAL PERSISTENCE: " << move.dp << endl; + outPar << "MEMORY SIZE: " << move.memSize << endl; + outPar << "GOAL TYPE: " << move.goalType << endl; + if (!trfr.indVar) { + if (move.goalType == 2) { // dispersal bias + outPar << "GOAL BIAS: " << move.gb << endl; + outPar << "ALPHA DB: " << move.alphaDB << endl; + outPar << "BETA DB: " << move.betaDB << endl; + } + } + if (trfr.indVar) { + trfrSMSParams s = pSpecies->getSMSParams(0, 0); + outPar << indvar << "yes " << endl; + outPar << "DP mean: " << s.dpMean << " s.d.: " << s.dpSD + << " scaling factor: " << s.dpScale << endl; + outPar << "GB mean: " << s.gbMean << " s.d.: " << s.gbSD + << " scaling factor: " << s.gbScale << endl; + if (move.goalType == 2) { // dispersal bias + outPar << "Alpha DB mean: " << s.alphaDBMean << " s.d.: " << s.alphaDBSD + << " scaling factor: " << s.alphaDBScale << endl; + outPar << "Beta DB mean: " << s.betaDBMean << " s.d.: " << s.betaDBSD + << " scaling factor: " << s.betaDBScale << endl; + } + } + else { + outPar << indvar << "no " << endl; + } + } + else { // CRW + trfrCRWTraits move = pSpecies->getCRWTraits(); + straigtenPath = move.straigtenPath; + outPar << "CRW" << endl; + string lgth = "STEP LENGTH (m) "; + string corr = "STEP CORRELATION"; + if (trfr.indVar) { + trfrCRWParams m = pSpecies->getCRWParams(0, 0); + outPar << indvar << "yes" << endl; + outPar << lgth << " mean: " << m.stepLgthMean; + outPar << " s.d.: " << m.stepLgthSD; + outPar << " scaling factor: " << m.stepLScale << endl; + outPar << corr << " mean: " << m.rhoMean; + outPar << " s.d.: " << m.rhoSD; + outPar << " scaling factor: " << m.rhoScale << endl; + } + else { + outPar << indvar << "no" << endl; + outPar << lgth << ": " << move.stepLength << endl; + outPar << corr << ": " << move.rho << endl; + } + } + outPar << "STRAIGHTEN PATH AFTER DECISION NOT TO SETTLE: "; + if (straigtenPath) outPar << "yes" << endl; + else outPar << "no" << endl; + outPar << "STEP MORTALITY:\t" << endl; + if (trfr.habMort) + { + outPar << "habitat dependent:\t" << endl; + if (!ppLand.generated && ppLand.rasterType == 0) { + for (int i = 0; i < ppLand.nHab; i++) + outPar << "\thab. " << pLandscape->getHabCode(i) << "\t" + << pSpecies->getHabMort(i) << endl; + } + else { + for (int i = 0; i < ppLand.nHab; i++) + outPar << "\thab. " << i << "\t" + << pSpecies->getHabMort(i) << endl; + } + } + else + { + trfrCRWTraits move = pSpecies->getCRWTraits(); + outPar << "constant " << move.stepMort << endl; + } + } // end of movement process + else { // kernel + string meandist = "MEAN DISTANCE"; + string probkern = "PROB. KERNEL I"; + trfrKernTraits kern0, kern1; + trfrKernParams k0, k1; + outPar << "dispersal kernel" << endl << "TYPE: \t"; + if (trfr.twinKern) outPar << "double "; + outPar << "negative exponential" << endl; + + if (trfr.sexDep) { + outPar << sexdept << "yes" << endl; + if (trfr.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + outPar << "stage " << i << ":" << endl; + kern0 = pSpecies->getKernTraits(i, 0); + kern1 = pSpecies->getKernTraits(i, 1); + outPar << meandist << " I: \tfemales " << kern0.meanDist1 << " \tmales " << kern1.meanDist1 << endl; + if (trfr.twinKern) + { + outPar << meandist << " II: \tfemales " << kern0.meanDist2 << " \tmales " << kern1.meanDist2 << endl; + outPar << probkern << ": \tfemales " << kern0.probKern1 << " \tmales " << kern1.probKern1 << endl; + } + } + } + else { // !trfr.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (trfr.indVar) { + k0 = pSpecies->getKernParams(0, 0); + k1 = pSpecies->getKernParams(0, 1); + outPar << "yes" << endl; + outPar << meandist << " I (mean): \tfemales " << k0.dist1Mean + << " \tmales " << k1.dist1Mean << endl; + outPar << meandist << " I (s.d.): \tfemales " << k0.dist1SD + << " \tmales " << k1.dist1SD << endl; + outPar << meandist << " I (scaling factor): \tfemales " << k0.dist1Scale + << " \tmales " << k1.dist1Scale << endl; + if (trfr.twinKern) + { + outPar << meandist << " II (mean): \tfemales " << k0.dist2Mean + << " \tmales " << k1.dist2Mean << endl; + outPar << meandist << " II (s.d.): \tfemales " << k0.dist2SD + << " \tmales " << k1.dist2SD << endl; + outPar << meandist << " II (scaling factor): \tfemales " << k0.dist2Scale + << " \tmales " << k1.dist2Scale << endl; + outPar << probkern << " (mean): \tfemales " << k0.PKern1Mean + << " \tmales " << k1.PKern1Mean << endl; + outPar << probkern << " (s.d.): \tfemales " << k0.PKern1SD + << " \tmales " << k1.PKern1SD << endl; + outPar << probkern << " (scaling factor): \tfemales " << k0.PKern1Scale + << " \tmales " << k1.PKern1Scale << endl; + } + } + else { + outPar << "no" << endl; + kern0 = pSpecies->getKernTraits(0, 0); + kern1 = pSpecies->getKernTraits(0, 1); + outPar << meandist << " I: \tfemales " << kern0.meanDist1 << " \tmales " << kern1.meanDist1 << endl; + if (trfr.twinKern) + { + outPar << meandist << " II: \tfemales " << kern0.meanDist2 << " \tmales " << kern1.meanDist2 << endl; + outPar << probkern << ": \tfemales " << kern0.probKern1 << " \tmales " << kern1.probKern1 << endl; + } + } + } + } + else { // !trfr.sexDep + outPar << sexdept << "no" << endl; + if (trfr.stgDep) { + outPar << stgdept << "yes" << endl; + outPar << indvar << "no" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + kern0 = pSpecies->getKernTraits(i, 0); + outPar << "stage " << i << ": \t" << meandist << " I: " << kern0.meanDist1; + if (trfr.twinKern) + { + outPar << " \t" << meandist << " II: " << kern0.meanDist2; + outPar << " \t" << probkern << ": " << kern0.probKern1; + } + outPar << endl; + } + } + else { // !trfr.stgDep + outPar << stgdept << "no" << endl; + outPar << indvar; + if (trfr.indVar) { + k0 = pSpecies->getKernParams(0, 0); + outPar << "yes" << endl; + outPar << meandist << " I (mean): " << k0.dist1Mean + << " \t(s.d.): " << k0.dist1SD + << " \t(scaling factor): " << k0.dist1Scale << endl; + if (trfr.twinKern) + { + outPar << meandist << " II (mean): " << k0.dist2Mean + << " \t(s.d.): " << k0.dist2SD + << " \t(scaling factor): " << k0.dist2Scale << endl; + outPar << probkern << " (mean): " << k0.PKern1Mean + << " \t(s.d.): " << k0.PKern1SD + << " \t(scaling factor): " << k0.PKern1Scale << endl; + } + } + else { + outPar << "no" << endl; + kern0 = pSpecies->getKernTraits(0, 0); + outPar << meandist << " I: \t" << kern0.meanDist1 << endl; + if (trfr.twinKern) + { + outPar << meandist << " II: \t" << kern0.meanDist2 << endl; + outPar << probkern << ": \t" << kern0.probKern1 << endl; + } + } + } + } + + outPar << "DISPERSAL MORTALITY: "; + trfrMortParams mort = pSpecies->getMortParams(); + if (trfr.distMort) { + outPar << "distance-dependent" << endl; + outPar << "SLOPE: " << mort.mortAlpha << " \tINFLECTION POINT: " << mort.mortBeta << endl; + } + else { + outPar << "constant" << endl << "MORTALITY PROBABILITY: " << mort.fixedMort << endl; + } + } // end of kernel transfer + + // Settlement + + outPar << endl << "DISPERSAL - SETTLEMENT:" << endl; + + if (trfr.moveModel) { + string plusmating = "+ mating requirements"; + ssteps = pSpecies->getSteps(0, 0); + + outPar << "MIN. No. OF STEPS:\t " << ssteps.minSteps << endl; + outPar << "MAX. No. OF STEPS:\t "; + if (ssteps.maxSteps == 99999999) outPar << "not applied" << endl; + else outPar << ssteps.maxSteps << endl; + + if (sett.sexDep) { + nsexes = 2; + outPar << sexdept << "yes" << endl; + if (sett.stgDep) { + nstages = sstruct.nStages; + outPar << stgdept << "yes" << endl; + } + else { // !sett.stgDep + nstages = 1; + outPar << stgdept << "no" << endl; + } + } + else { // !sett.sexDep + nsexes = 1; + outPar << sexdept << "no" << endl; + if (sett.stgDep) { + nstages = sstruct.nStages; + outPar << stgdept << "yes" << endl; + } + else { // !sett.stgDep + nstages = 1; + outPar << stgdept << "no" << endl; + } + } + for (int sx = 0; sx < nsexes; sx++) { + if (sett.sexDep) { + if (sx == 0) outPar << "FEMALES:" << endl; + else outPar << "MALES:" << endl; + } + outPar << "SETTLE IF: "; + for (int i = 0; i < nstages; i++) { + if (dem.stageStruct && nstages > 1) outPar << "stage " << i << ": " << endl; + outPar << "find a suitable cell/patch "; + srules = pSpecies->getSettRules(i, sx); + if (srules.densDep) { + settleDD = pSpecies->getSettTraits(i, sx); + outPar << "+ density dependence "; + if (srules.findMate) outPar << plusmating; + outPar << endl; + if (!sett.indVar) { + outPar << "S0: " << settleDD.s0 << " AlphaS: " << settleDD.alpha + << " BetaS: " << settleDD.beta << endl; + } + } + else { + if (srules.findMate) outPar << plusmating << endl; + else outPar << "(not the natal one)" << endl; + } + if (dem.stageStruct) { + ssteps = pSpecies->getSteps(i, sx); + outPar << "MAX. No. OF STEPS/YEAR:\t "; + if (ssteps.maxStepsYr == 99999999) outPar << "not applied" << endl; + else outPar << ssteps.maxStepsYr << endl; + } + } + } + if (sett.indVar) { + settParams sparams0; + outPar << "DENSITY DEPENDENCE + " << indvar << "yes" << endl; + for (int sex = 0; sex < nsexes; sex++) { + if (sett.sexDep) { + if (sex == 0) outPar << "FEMALES:" << endl; + else outPar << "MALES:" << endl; + } + sparams0 = pSpecies->getSettParams(0, sex); + settScales scale = pSpecies->getSettScales(); + outPar << "S0 - mean: " << sparams0.s0Mean << " s.d.: " << sparams0.s0SD + << " scaling factor: " << scale.s0Scale << endl; + outPar << "AlphaS - mean: " << sparams0.alphaSMean << " s.d.: " << sparams0.alphaSSD + << " scaling factor: " << scale.alphaSScale << endl; + outPar << "BetaS - mean: " << sparams0.betaSMean << " s.d.: " << sparams0.betaSSD + << " scaling factor: " << scale.betaSScale << endl; + } + } + } + else { // kernel-based transfer + string notsuit = "IF THE ARRIVAL CELL/PATCH IS UNSUITABLE: "; + string rchoose = " randomly choose a suitable neighb. cell/patch or "; + string matereq = "MATING REQUIREMENTS: "; + if (sett.sexDep) { + nsexes = 2; + outPar << sexdept << "yes" << endl; + if (sett.stgDep) { + nstages = sstruct.nStages; + outPar << stgdept << "yes" << endl; + outPar << notsuit << endl; + } + else { + nstages = 1; + outPar << stgdept << "no" << endl; + } + } + else { + nsexes = 1; + outPar << sexdept << "no" << endl; + if (sett.stgDep) { + nstages = sstruct.nStages; + outPar << stgdept << "yes" << endl; + outPar << notsuit << endl; + } + else { + nstages = 1; + outPar << stgdept << "no" << endl; + outPar << notsuit; + } + } + for (int i = 0; i < nstages; i++) { + if (sett.stgDep) { + outPar << "stage " << i << ":" << endl; + } + for (int sx = 0; sx < nsexes; sx++) { + if (sett.sexDep) { + if (sx == 0) outPar << "FEMALES: "; + else outPar << "MALES: "; + if (!sett.stgDep) outPar << notsuit; + } + srules = pSpecies->getSettRules(i, sx); + if (srules.go2nbrLocn) { + outPar << rchoose; + if (srules.wait) outPar << "wait" << endl; + else outPar << "die" << endl; + } + else { + if (srules.wait) outPar << "wait" << endl; + else outPar << "die" << endl; + } + outPar << matereq; + if (srules.findMate) outPar << "yes" << endl; + else outPar << "no" << endl; + } + } + } + + // Genetics + + outPar << endl << "GENETICS:" << endl; + int nspptraits = pSpecies->getNTraits(); + outPar << "No. of variable traits: " << nspptraits << endl; + + genomeData d = pSpecies->getGenomeData(); + if (emig.indVar || trfr.indVar || sett.indVar || d.neutralMarkers) + { + if (d.diploid) outPar << "DIPLOID" << endl; else outPar << "HAPLOID" << endl; + int nchromosomes = pSpecies->getNChromosomes(); + outPar << "No. of chromosomes: " << nchromosomes; + if (d.trait1Chromosome) { + outPar << endl << "No. of loci/chromosome: " << d.nLoci << endl; + } + else { + outPar << " (chrom:loci)"; + for (int i = 0; i < nchromosomes; i++) { + outPar << " " << i << ":" << pSpecies->getNLoci(i); + } + outPar << endl; + } + outPar << "Mutation probability: " << d.probMutn << endl; + outPar << "Crossover probability: " << d.probCrossover << endl; + outPar << "Initial allele s.d.: " << d.alleleSD << endl; + outPar << "Mutation s.d.: " << d.mutationSD << endl; + if (d.neutralMarkers) { + outPar << "NEUTRAL MARKERS ONLY" << endl; + } + else { + if (!d.trait1Chromosome) { + traitAllele allele; + outPar << "TRAIT MAPPING:" << endl; + outPar << "Architecture file: " << genfilename << endl; + int ntraitmaps = pSpecies->getNTraitMaps(); + outPar << "No. of traits defined: " << ntraitmaps << endl; + for (int i = 0; i < ntraitmaps; i++) { + int nalleles = pSpecies->getNTraitAlleles(i); + outPar << "Trait " << i << ": (" << pSpecies->getTraitName(i) + << ") alleles: " << nalleles << " (chrom:locus)"; + for (int j = 0; j < nalleles; j++) { + allele = pSpecies->getTraitAllele(i, j); + outPar << " " << allele.chromo << ":" << allele.locus; + } + outPar << endl; + } + if (ntraitmaps < nspptraits) { // list undefined traits + outPar << "WARNING - the following traits were not defined" + << " in the genetic architecture file:" << endl; + for (int i = ntraitmaps; i < nspptraits; i++) { + outPar << "Trait " << i << ": (" << pSpecies->getTraitName(i) + << ") all individuals have mean phenotype" << endl; + } + } + int nneutral = pSpecies->getNNeutralLoci(); + if (nneutral > 0) { + outPar << "Neutral loci: " << nneutral << " (chrom:locus)"; + for (int i = 0; i < nneutral; i++) { + allele = pSpecies->getNeutralAllele(i); + outPar << " " << allele.chromo << ":" << allele.locus; + } + outPar << endl; + } + if (d.pleiotropic) + outPar << "Genome exhibits pleiotropy" << endl; + } + } + } + + // Initialisation + + initParams init = paramsInit->getInit(); + outPar << endl << "INITIALISATION CONDITIONS:" << endl; + switch (init.seedType) { + case 0: + outPar << "Free initialisation: \t"; + switch (init.freeType) { + case 0: + outPar << "Random \t"; + outPar << "No. of cells/patches: " << init.nSeedPatches << endl; + break; + case 1: + outPar << "all suitable cells/patches" << endl; + break; + case 2: + outPar << "manually selected cells/patches" << endl; + break; + } + break; + case 1: + outPar << "From species distribution: \t" << endl; + switch (init.spDistType) { + case 0: + outPar << "all presence cells/patches" << endl; + break; + case 1: + outPar << "some random presence cells/patches" << endl; + break; + case 2: + outPar << "all cells/patches within selected distribution cells" << endl; + break; + } + break; + case 2: + outPar << "From initial individuals file: " << paramsSim->getDir(1) + init.indsFile << endl; + break; + case 3: + outPar << "From file" << endl; + break; + } + if (init.seedType != 2) { + outPar << "INITIAL NO. OF INDIVIDUALS: \t"; + switch (init.initDens) { + case 0: + outPar << "at carrying capacity" << endl; + break; + case 1: + outPar << "at half carrying capacity" << endl; + break; + case 2: + if (ppLand.patchModel) { + outPar << init.indsHa << " individuals per ha" << endl; + } + else { + outPar << init.indsCell << " individuals per cell" << endl; + } + break; + } + if (dem.stageStruct) { + outPar << "INITIAL STAGE PROPORTIONS:" << endl; + for (int i = 1; i < sstruct.nStages; i++) { + outPar << "stage " << i << ": " << paramsInit->getProp(i) << " \t"; + } + outPar << endl; + outPar << "Initial age distribution: "; + switch (init.initAge) { + case 0: + outPar << "lowest possible age"; + break; + case 1: + outPar << "randomised"; + break; + case 2: + outPar << "quasi-equilibrium"; + break; + } + outPar << endl; + } + outPar << "GEOGRAPHICAL CONSTRAINTS (cell numbers): " << endl; + outPar << "min X: " << init.minSeedX << " max X: " << init.maxSeedX << endl; + outPar << "min Y: " << init.minSeedY << " max Y: " << init.maxSeedY << endl; + if (init.seedType == 0 && init.freeType < 2) { + if (init.initFrzYr > 0) { + outPar << "Freeze initial range until year " << init.initFrzYr << endl; + } + if (init.restrictRange) { + outPar << "Restrict range to northern " << init.restrictRows + << " rows every " << init.restrictFreq << " years" << endl; + if (init.finalFrzYr < sim.years) { + outPar << "Freeze range at year " << init.finalFrzYr << endl; + } + } + } + } + + outPar << endl << "OUTPUTS:" << endl; + if (sim.outRange) { + outPar << "Range - every " << sim.outIntRange << " year"; + if (sim.outIntRange > 1) outPar << "s"; + outPar << endl; + } + if (sim.outOccup) { + outPar << "Occupancy - every " << sim.outIntOcc << " year"; + if (sim.outIntOcc > 1) outPar << "s"; + outPar << endl; + } + if (sim.outPop) { + outPar << "Populations - every " << sim.outIntPop << " year"; + if (sim.outIntPop > 1) outPar << "s"; + if (sim.outStartPop > 0) outPar << " starting year " << sim.outStartPop; + outPar << endl; + } + if (sim.outInds) { + outPar << "Individuals - every " << sim.outIntInd << " year"; + if (sim.outIntInd > 1) outPar << "s"; + if (sim.outStartInd > 0) outPar << " starting year " << sim.outStartInd; + outPar << endl; + } + if (sim.outGenetics) { + outPar << "Genetics - every " << sim.outIntGenetic << " year"; + if (sim.outIntGenetic > 1) outPar << "s"; + if (sim.outStartGenetic > 0) outPar << " starting year " << sim.outStartGenetic; + if (dem.stageStruct) { + switch (sim.outGenType) { + case 0: + outPar << " - juveniles only"; + break; + case 1: + outPar << " - all individuals"; + break; + case 2: + outPar << " - adults only"; + break; + } + } + if (sim.outGenXtab) outPar << " (as cross table)"; + outPar << endl; + } + + if (sim.outTraitsCells) { + outPar << "Traits per "; + if (ppLand.patchModel) outPar << "patch"; else outPar << "cell"; + outPar << " - every " << sim.outIntTraitCell << " year"; + if (sim.outIntTraitCell > 1) outPar << "s"; + if (sim.outStartTraitCell > 0) outPar << " starting year " << sim.outStartTraitCell; + outPar << endl; + } + if (sim.outTraitsRows) { + outPar << "Traits per row - every " << sim.outIntTraitRow << " year"; + if (sim.outIntTraitRow > 1) outPar << "s"; + if (sim.outStartTraitRow > 0) outPar << " starting year " << sim.outStartTraitRow; + outPar << endl; + } + if (sim.outConnect) { + outPar << "Connectivity matrix - every " << sim.outIntConn << " year"; + if (sim.outIntConn > 1) outPar << "s"; + if (sim.outStartConn > 0) outPar << " starting year " << sim.outStartConn; + outPar << endl; + } +#if RS_RCPP + if (sim.outPaths) { + outPar << "SMS paths - every " << sim.outIntPaths << " year"; + if (sim.outIntPaths > 1) outPar << "s"; + if (sim.outStartPaths > 0) outPar << " starting year " << sim.outStartPaths; + outPar << endl; + } +#endif + outPar << "SAVE MAPS: "; + if (sim.saveMaps) { + outPar << "yes - every " << sim.mapInt << " year"; + if (sim.mapInt > 1) outPar << "s"; + outPar << endl; + } + else outPar << "no" << endl; + outPar << "SAVE TRAITS MAPS: "; + if (sim.saveTraitMaps) { + outPar << "yes - every " << sim.traitInt << " year"; + if (sim.traitInt > 1) outPar << "s"; + outPar << endl; + } + else outPar << "no" << endl; + if (trfr.moveModel && trfr.moveType == 1) { + outPar << "SMS HEAT MAPS: "; + if (sim.saveVisits) outPar << "yes" << endl; + else outPar << "no" << endl; + } + + outPar.close(); outPar.clear(); + +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- diff --git a/src/RScore/Model.h b/src/RScore/Model.h new file mode 100644 index 00000000..f48d1554 --- /dev/null +++ b/src/RScore/Model.h @@ -0,0 +1,141 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Model + +Implements three functions which run the model and produce output common to both +GUI and batch version. + +RunModel() handles looping through replicates, years and generations + +Further functions are declared here, but defined differently in main function of +GUI and batch versions. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 26 October 2021 by Steve Palmer +------------------------------------------------------------------------------*/ + +#ifndef ModelH +#define ModelH + +#include +#include + +#include "Parameters.h" +#include "Landscape.h" +#include "Community.h" +#include "SubCommunity.h" +#include "Species.h" + +#if !RS_EMBARCADERO && !LINUX_CLUSTER && !RS_RCPP +#include +using namespace std::filesystem; +#endif + +#if RSDEBUG +extern ofstream DEBUGLOG; +#endif + +#if RS_RCPP && !R_CMD +Rcpp::List RunModel( + Landscape*, // pointer to Landscape + int // sequential simulation number +); +#else +int RunModel( + Landscape*, // pointer to Landscape + int // sequential simulation number +); +#endif // RS_RCPP && !R_CMD +bool CheckDirectory(void); +void PreReproductionOutput( + Landscape*, // pointer to Landscape + Community*, // pointer to Community + int, // replicate + int, // year + int // generation +); +void RangePopOutput( + Community*, // pointer to Community + int, // replicate + int, // year + int // generation +); +void Outputs_Visuals_B( + int, // replicate + int, // year + int, // generation + int // Landscape number +); +void RefreshVisualCost(void); +traitCanvas SetupTraitCanvas(void); +void SetupVisualOutput(void); +void ResetVisualOutput(void); +void DrawPopnGraph( + Community*, // pointer to Community + int // year +); +void OutParameters( + Landscape* // pointer to Landscape +); + +extern paramGrad *paramsGrad; +extern paramStoch *paramsStoch; +extern Species *pSpecies; +extern paramSim *paramsSim; +extern paramInit *paramsInit; +extern Community *pComm; + +const bool batchMode = true; +extern string landFile; +extern vector hfnames; +extern string habmapname; // see Main.cpp (batch) +extern string patchmapname; // see Main.cpp (batch) +extern string distnmapname; // see Main.cpp (batch) +extern string costmapname; // see Main.cpp (batch) +extern string genfilename; // see Main.cpp (batch) +extern RSrandom *pRandom; + +// these functions to have different version for GUI and batch applications ... +#if BATCH +extern void MemoLine(string); +#endif +void GUIsetLandScale( + int, // landscape image height (pixels) + int // landscape image width (pixels) +); + +#if RS_RCPP +extern std::uint32_t RS_random_seed; +extern string name_landscape, name_patch, name_costfile, name_sp_dist; +#endif +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Parameters.cpp b/src/RScore/Parameters.cpp new file mode 100644 index 00000000..5cfb3d72 --- /dev/null +++ b/src/RScore/Parameters.cpp @@ -0,0 +1,382 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Parameters.h" +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +// Environmental gradient parameters + +paramGrad::paramGrad(void) { + gradient = false; gradType = 0; grad_inc = 0.05f; + opt_y0 = opt_y = factor = extProbOpt = 0.0; + shifting = false; shift_rate = 0.5; shift_begin = 0; shift_stop = 100; +} + +paramGrad::~paramGrad() { } + +void paramGrad::setGradient(int gtype, float inc, float y, float f, float p) +{ + if (gtype > 0 && gtype < 4) + { // valid gradient type + gradient = true; gradType = gtype; + if (inc >= 0.0 && inc <= 1.0) grad_inc = inc; + if (y >= 0.0) opt_y0 = opt_y = y; + if (f >= 0.0) factor = f; + if (p > 0.0 && p < 1.0) extProbOpt = p; + } + else { + gradient = false; gradType = 0; + } +} + +void paramGrad::setShifting(float r, int begin, int end) +{ + shifting = true; + if (r > 0.0) shift_rate = r; + if (begin >= 0) shift_begin = begin; + if (end > 0) shift_stop = end; +} + +void paramGrad::noGradient(void) { gradient = false; gradType = 0; } + +void paramGrad::noShifting(void) { shifting = false; } + +envGradParams paramGrad::getGradient(void) { + envGradParams g; + g.gradient = gradient; g.gradType = gradType; g.grad_inc = grad_inc; + g.opt_y = opt_y; g.factor = factor; g.extProbOpt = extProbOpt; + g.shifting = shifting; g.shift_rate = shift_rate; + g.shift_begin = shift_begin; g.shift_stop = shift_stop; + return g; +} + +void paramGrad::incrOptY(void) +{ + if (gradient && shifting) opt_y += shift_rate; +} + +void paramGrad::resetOptY(void) { opt_y = opt_y0; } + +//--------------------------------------------------------------------------- + +// Environmental stochasticity parameters + +paramStoch::paramStoch(void) { + stoch = false; local = false; inK = false; localExt = false; + ac = 0.0; std = 0.25; + locExtProb = 0.1; +} + +paramStoch::~paramStoch(void) {} + + +void paramStoch::setStoch(envStochParams e) +{ + stoch = e.stoch; local = e.local; inK = e.inK; localExt = e.localExt; + if (e.ac >= 0.0 && e.ac < 1.0) ac = e.ac; + if (e.std > 0.0 && e.std <= 1.0) std = e.std; + locExtProb = e.locExtProb; +} + +bool paramStoch::envStoch(void) { return stoch; } + +envStochParams paramStoch::getStoch(void) +{ + envStochParams e; + e.stoch = stoch; e.local = local; e.inK = inK; e.localExt = localExt; + e.ac = ac; e.std = std; + e.locExtProb = locExtProb; + return e; +} + + +//--------------------------------------------------------------------------- + +// Initialisation (seeding) parameters + +paramInit::paramInit(void) { + seedType = freeType = spDistType = initDens = 0; + initAge = initFrzYr = 0; + restrictRange = false; + restrictRows = 100; + restrictFreq = 10; + finalFrzYr = 99999999; + indsCell = 1; indsHa = 0.0; + minSeedX = 0; maxSeedX = 99999999; minSeedY = 0; maxSeedY = 99999999; + nSeedPatches = 1; nSpDistPatches = 1; + indsFile = "NULL"; + for (int i = 0; i < NSTAGES; i++) { + initProp[i] = 0.0; + } +} + +paramInit::~paramInit(void) { + initinds.clear(); +} + +void paramInit::setInit(initParams i) { + if (i.seedType >= 0 && i.seedType <= 3) seedType = i.seedType; + if (i.freeType >= 0 && i.freeType <= 2) freeType = i.freeType; + if (i.spDistType >= 0 && i.spDistType <= 2) spDistType = i.spDistType; + initDens = i.initDens; + initAge = i.initAge; + if (i.initFrzYr >= 0) initFrzYr = i.initFrzYr; + restrictRange = i.restrictRange; + if (i.restrictRows > 0) restrictRows = i.restrictRows; + if (i.restrictFreq > 0) restrictFreq = i.restrictFreq; + if (i.finalFrzYr > 0) finalFrzYr = i.finalFrzYr; + if (i.indsCell >= 1) indsCell = i.indsCell; + if (i.indsHa > 0.0) indsHa = i.indsHa; + if (i.minSeedX >= 0) minSeedX = i.minSeedX; + if (i.maxSeedX >= 0) maxSeedX = i.maxSeedX; + if (i.minSeedY >= 0) minSeedY = i.minSeedY; + if (i.maxSeedY >= 0) maxSeedY = i.maxSeedY; + if (i.nSeedPatches >= 1) nSeedPatches = i.nSeedPatches; + if (i.nSpDistPatches >= 1) nSpDistPatches = i.nSpDistPatches; + indsFile = i.indsFile; +} + +initParams paramInit::getInit(void) { + initParams i; + i.seedType = seedType; i.freeType = freeType; i.spDistType = spDistType; + i.initDens = initDens; i.initAge = initAge; + i.initFrzYr = initFrzYr; + i.restrictRange = restrictRange; + i.restrictRows = restrictRows; i.restrictFreq = restrictFreq; + i.finalFrzYr = finalFrzYr; + i.indsCell = indsCell; i.indsHa = indsHa; + i.minSeedX = minSeedX; i.minSeedY = minSeedY; + i.maxSeedX = maxSeedX; i.maxSeedY = maxSeedY; + i.nSeedPatches = nSeedPatches; i.nSpDistPatches = nSpDistPatches; + i.indsFile = indsFile; + return i; +} + +void paramInit::setProp(short stg, float p) { + if (stg >= 0 && stg < NSTAGES && p >= 0.0 && p <= 1.0) initProp[stg] = p; +} + +float paramInit::getProp(short stg) { + float p = 0.0; + if (stg >= 0 && stg < NSTAGES) p = initProp[stg]; + return p; +} + +void paramInit::addInitInd(initInd iind) { + initinds.push_back(iind); +} + +initInd paramInit::getInitInd(int ix) { + initInd iind; + if (ix >= 0 && ix < (int)initinds.size()) { + iind = initinds[ix]; + } + else { + iind.year = iind.patchID = iind.x = iind.y = iind.sex = iind.age = iind.stage = 0; + iind.species = -1; + } + return iind; +} + +void paramInit::resetInitInds(void) { initinds.clear(); } + +int paramInit::numInitInds(void) { return (int)initinds.size(); } + + +//--------------------------------------------------------------------------- + +// Simulation parameters + +paramSim::paramSim(void) { + simulation = 0; + reps = years = 1; + outIntRange = 1; + outStartPop = outStartInd = outStartGenetic = 0; + outStartTraitCell = outStartTraitRow = outStartConn = 0; + outIntOcc = outIntPop = outIntInd = outIntGenetic = 10; + outIntTraitCell = outIntTraitRow = outIntConn = 10; + mapInt = traitInt = 10; + slowFactor = 1; + batchMode = absorbing = false; + outRange = outOccup = outPop = outInds = false; + outGenetics = outGenXtab = false; outGenType = 0; + outTraitsCells = outTraitsRows = outConnect = false; + saveMaps = false; saveTraitMaps = false; + saveVisits = false; +#if RS_RCPP + outStartPaths = 0; outIntPaths = 0; + outPaths = false; ReturnPopRaster = false; CreatePopFile = true; +#endif + drawLoaded = false; + viewLand = false; viewPatch = false; viewGrad = false; viewCosts = false; + viewPop = false; viewTraits = false; viewPaths = false; viewGraph = false; + dir = ' '; +} + +paramSim::~paramSim(void) { } + +void paramSim::setSim(simParams s) { + if (s.batchNum >= 0) batchNum = s.batchNum; + if (s.simulation >= 0) simulation = s.simulation; + if (s.reps >= 1) reps = s.reps; + if (s.years >= 1) years = s.years; + if (s.mapInt >= 1) mapInt = s.mapInt; + if (s.traitInt >= 1) traitInt = s.traitInt; + batchMode = s.batchMode; absorbing = s.absorbing; + outRange = s.outRange; outOccup = s.outOccup; + outPop = s.outPop; outInds = s.outInds; + outGenetics = s.outGenetics; + if (s.outGenType >= 0 && s.outGenType <= 2) { + outGenType = s.outGenType; + } + outGenXtab = s.outGenXtab; + outTraitsCells = s.outTraitsCells; outTraitsRows = s.outTraitsRows; + outConnect = s.outConnect; + if (s.outStartPop >= 0) outStartPop = s.outStartPop; + if (s.outStartInd >= 0) outStartInd = s.outStartInd; + if (s.outStartGenetic >= 0) outStartGenetic = s.outStartGenetic; + if (s.outStartTraitCell >= 0) outStartTraitCell = s.outStartTraitCell; + if (s.outStartTraitRow >= 0) outStartTraitRow = s.outStartTraitRow; + if (s.outStartConn >= 0) outStartConn = s.outStartConn; + if (s.outIntRange >= 1) outIntRange = s.outIntRange; + if (s.outIntOcc >= 1) outIntOcc = s.outIntOcc; + if (s.outIntPop >= 1) outIntPop = s.outIntPop; + if (s.outIntInd >= 1) outIntInd = s.outIntInd; + if (s.outIntGenetic >= 1) outIntGenetic = s.outIntGenetic; + if (s.outIntTraitCell >= 1) outIntTraitCell = s.outIntTraitCell; + if (s.outIntTraitRow >= 1) outIntTraitRow = s.outIntTraitRow; + if (s.outIntConn >= 1) outIntConn = s.outIntConn; + saveMaps = s.saveMaps; saveTraitMaps = s.saveTraitMaps; + saveVisits = s.saveVisits; +#if RS_RCPP + outStartPaths = s.outStartPaths; + outIntPaths = s.outIntPaths; + outPaths = s.outPaths; + ReturnPopRaster = s.ReturnPopRaster; + CreatePopFile = s.CreatePopFile; +#endif + drawLoaded = s.drawLoaded; +} + +simParams paramSim::getSim(void) { + simParams s; + s.batchNum = batchNum; + s.simulation = simulation; s.reps = reps; s.years = years; + s.outRange = outRange; s.outOccup = outOccup; s.outPop = outPop; s.outInds = outInds; + s.outGenetics = outGenetics; s.outGenType = outGenType; s.outGenXtab = outGenXtab; + s.outTraitsCells = outTraitsCells; s.outTraitsRows = outTraitsRows; s.outConnect = outConnect; + s.outStartPop = outStartPop; s.outStartInd = outStartInd; s.outStartGenetic = outStartGenetic; + s.outStartTraitCell = outStartTraitCell; s.outStartTraitRow = outStartTraitRow; + s.outStartConn = outStartConn; + s.outIntRange = outIntRange; + s.outIntOcc = outIntOcc; s.outIntPop = outIntPop; + s.outIntInd = outIntInd; s.outIntGenetic = outIntGenetic; + s.outIntTraitCell = outIntTraitCell; + s.outIntTraitRow = outIntTraitRow; + s.outIntConn = outIntConn; + s.batchMode = batchMode; + s.absorbing = absorbing; + s.saveMaps = saveMaps; s.saveTraitMaps = saveTraitMaps; + s.saveVisits = saveVisits; + s.mapInt = mapInt; s.traitInt = traitInt; +#if RS_RCPP + s.outStartPaths = outStartPaths; + s.outIntPaths = outIntPaths; + s.outPaths = outPaths; + s.ReturnPopRaster = ReturnPopRaster; + s.CreatePopFile = CreatePopFile; +#endif + s.drawLoaded = drawLoaded; + return s; +} + +int paramSim::getSimNum(void) { return simulation; } + +void paramSim::setViews(simView v) { + viewLand = v.viewLand; viewPatch = v.viewPatch; + viewGrad = v.viewGrad; viewCosts = v.viewCosts; + viewPop = v.viewPop; viewTraits = v.viewTraits; + viewPaths = v.viewPaths; viewGraph = v.viewGraph; + if (v.slowFactor > 0) slowFactor = v.slowFactor; +} + +simView paramSim::getViews(void) { + simView v; + v.viewLand = viewLand; v.viewPatch = viewPatch; + v.viewGrad = viewGrad; v.viewCosts = viewCosts; + v.viewPop = viewPop; v.viewTraits = viewTraits; + v.viewPaths = viewPaths; v.viewGraph = viewGraph; + v.slowFactor = slowFactor; + return v; +} + +void paramSim::setDir(string s) { + dir = s; +} + +// return directory name depending on option specified +string paramSim::getDir(int option) { + string s; + switch (option) { + case 0: // working directory + s = dir; + break; +#if LINUX_CLUSTER || RS_RCPP + case 1: // Inputs folder + s = dir + "Inputs/"; + break; + case 2: // Outputs folder + s = dir + "Outputs/"; + break; + case 3: // Maps folder + s = dir + "Output_Maps/"; + break; +#else + case 1: // Inputs folder + s = dir + "Inputs\\"; + break; + case 2: // Outputs folder + s = dir + "Outputs\\"; + break; + case 3: // Maps folder + s = dir + "Output_Maps\\"; + break; +#endif + default: + s = "ERROR_ERROR_ERROR"; + } + return s; +} + +#if RS_RCPP +bool paramSim::getReturnPopRaster(void) { return ReturnPopRaster; } +bool paramSim::getCreatePopFile(void) { return CreatePopFile; } +#endif + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + diff --git a/src/RScore/Parameters.h b/src/RScore/Parameters.h new file mode 100644 index 00000000..9cc51d2e --- /dev/null +++ b/src/RScore/Parameters.h @@ -0,0 +1,386 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Parameters + +Implements the following classes: + +paramGrad - Environmental gradient parameters +paramInit - Initialisation (seeding) parameters +paramSim - Simulation parameters +paramStoch - Environmental stochasticity parameters + +Also declares some structures and functions used throughout the program. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 25 June 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef ParametersH +#define ParametersH + +//#if LINUX_CLUSTER +//#include +//#else +#include +//#endif +#include +#include +//#include +#include +#include +#include +using namespace std; + +#include "RSrandom.h" + +#define NSTAGES 10 // maximum number of stages permitted +#define NSEXES 2 // maximum number of sexes permitted +#define PARAMDEBUG 0 +#define NTRAITS 18 // maximum number of variable traits which can be displayed + // in GUI (VCL version) +#define NSD 3.0 // no. of s.d. to use to control range for displaying traits + +#if RS_RCPP +typedef intptr_t intptr; +#else +#if RSWIN64 +typedef unsigned long long intptr; +#else +typedef unsigned int intptr; +#endif +#endif + +#if RS_RCPP + #ifndef R_EXT_CONSTANTS_H_ // the R headers define PI as a macro, so that the 'else' line results in an error + #define M_2PI 6.283185307179586 + const double PI = 3.141592653589793238462643383279502884197169399375; + #endif +#else + #define M_2PI 6.283185307179586 + const double PI = 3.141592654; +#endif + +const double SQRT2 = std::sqrt(double(2.0)); // more efficient than calculating every time + +//--------------------------------------------------------------------------- + +// Common declarations + +struct locn { int x; int y; }; +struct rgb { // colour scheme for drawing maps + int r,g,b; +}; + +const string Int2Str(const int); +#if RS_RCPP +const string Int2Str(const int, unsigned int); +#endif +const string Float2Str(const float); +const string Double2Str(const double); +const rgb draw_wheel(int); + +//--------------------------------------------------------------------------- + +// Environmental gradient parameters + +// SHOULD THIS BE PART OF LANDSCAPE OBJECT OR A SEPARATE OBJECT????????????? + +struct envGradParams { + bool gradient; bool shifting; + int gradType; float grad_inc; float opt_y; float factor; float extProbOpt; + float shift_rate; int shift_begin; int shift_stop; +}; + +class paramGrad { + +public: + paramGrad(void); + ~paramGrad(void); + void setGradient( + int, // gradient type + float, // gradient steepness + float, // optimum row (Y dimension) + float, // local scaling factor + float // local extinction probability at optimum + ); + void setShifting( + float, // shifting rate (rows/year) + int, // first year of shifting + int // last year of shifting + ); + void noGradient(void); + void noShifting(void); + envGradParams getGradient(void); + void incrOptY(void); + void resetOptY(void); + +private: + bool gradient; // there a gradient + bool shifting; // the gradient is shifting + int gradType; // 0 = none, 1 = carrying capacity, + // 2 = growth rate, 3 = local extinction probability + float grad_inc; // gradient steepness + float opt_y; // optimum row (Y dimension) + float opt_y0; // optimum row at year 0 (internal use only) + float factor; // local scaling factor + float extProbOpt; // local extinction probability at optimum (if gradient = 4, otherwise 0) + float shift_rate; // rows/year + int shift_begin; // first year of shifting + int shift_stop; // last year of shifting +}; + +//--------------------------------------------------------------------------- + +// Environmental stochasticity parameters + +// SHOULD THIS BE PART OF LANDSCAPE OBJECT OR A SEPARATE OBJECT????????????? + +struct envStochParams { + bool stoch; bool local; bool inK; bool localExt; + float ac; float std; + float locExtProb; +}; + +class paramStoch { + +public: + paramStoch(void); + ~paramStoch(void); + void setStoch(envStochParams); + bool envStoch(void); + envStochParams getStoch(void); + +private: + bool stoch; // stochasticity applied + bool local; // applied locally (if not, application is global) + bool inK; // in carrying capacity (if not, in growth rate) + bool localExt; // local extinction applied + float ac; // temporal autocorrelation coefficient + float std; // amplitude of fluctuations: sampled from N(0,std) + float locExtProb; // local extinction probability +}; + +//--------------------------------------------------------------------------- + +// Initialisation (seeding) parameters + +struct initParams { + short seedType; short freeType; short spDistType; short initDens; + short initAge; int initFrzYr; bool restrictRange; + int restrictRows; int restrictFreq; int finalFrzYr; + int indsCell; float indsHa; + int minSeedX; int maxSeedX; int minSeedY; int maxSeedY; + int nSeedPatches; int nSpDistPatches; + string indsFile; +}; + +struct initInd { + int year,patchID,x,y; short species,sex,age,stage; +}; + +class paramInit { + +public: + paramInit(void); + ~paramInit(void); + void setInit(initParams); + initParams getInit(void); + void setProp( + short, // stage + float // initial proportion + ); + float getProp( + short // stage + ); + void addInitInd(initInd); + initInd getInitInd(int); + void resetInitInds(void); + int numInitInds(void); + +private: + short seedType; // initialisation type: 0 = free, 1 = from species distn, + // 2 = initial individuals, 3 = from file + short freeType; // free initialisation type: + // 0 = random (given no.) + // 1 = all suitable cells/patches + // 2 = manually selected cells/patches + short spDistType; // species distribution initialisation type: + // 0 = all suitable cells/patches, + // 1 = some randomly chosen suitable cells/patches, + // 2 = all cells/patches within selected sp. dist. cells + short initDens; // initialisation density: + // 0 = at carrying capacity + // 1 = at half carrying capacity + // 2 = specified no. per cell or density + short initAge; // initial age distribution within each stage: + // 0 = lowest possible age + // 1 = randomised + // 2 = quasi-equilibrium + int initFrzYr; // year until which initial range is frozen + bool restrictRange; // restrict range to northern front + int restrictRows; // no. of rows to retain behind front + int restrictFreq; // frequency of range restriction + int finalFrzYr; // year after which range is frozen + int indsCell; // initial individuals / cell (cell-based model) + float indsHa; // initial density (patch-based model) + int minSeedX; // ) + int maxSeedX; // ) min. and max. of area to initialise (cell numbers) + int minSeedY; // ) only applied if seedType is 0 + int maxSeedY; // ) + int nSeedPatches; // no. of cells/patches to initialise + int nSpDistPatches; // no. of species distribution cells to initialise + string indsFile; // no. of species distribution cells to initialise + float initProp[NSTAGES]; // initial stage proportions (structured population only) + + vector initinds; // individuals to be initialised + +}; + +//--------------------------------------------------------------------------- + +// Simulation parameters + +struct simParams { + int batchNum; + int simulation; int reps; int years; +// int outStartRange; +// int outStartOcc; + int outStartPop; int outStartInd; int outStartGenetic; + int outStartTraitCell; int outStartTraitRow; int outStartConn; + int outIntRange; int outIntOcc; int outIntPop; int outIntInd; int outIntGenetic; + int outIntTraitCell; int outIntTraitRow; int outIntConn; + int mapInt; int traitInt; + bool batchMode; bool absorbing; + bool outRange; bool outOccup; bool outPop; bool outInds; + bool outGenetics; short outGenType; bool outGenXtab; + bool outTraitsCells; bool outTraitsRows; bool outConnect; + bool saveMaps; + bool drawLoaded; bool saveTraitMaps; + bool saveVisits; +#if RS_RCPP + int outStartPaths; int outIntPaths; + bool outPaths; bool ReturnPopRaster; bool CreatePopFile; +#endif +}; + +struct simView { + bool viewLand; bool viewPatch; bool viewGrad; bool viewCosts; + bool viewPop; bool viewTraits; bool viewPaths; bool viewGraph; + int slowFactor; +}; + +class paramSim { + +public: + paramSim(void); + ~paramSim(void); + void setSim(simParams); + simParams getSim(void); + int getSimNum(void); + void setViews(simView); + simView getViews(void); + void setDir(string); + string getDir(int); +#if RS_RCPP + bool getReturnPopRaster(void); + bool getCreatePopFile(void); +#endif + +private: + int batchNum; // batch number + int simulation; // simulation no. + int reps; // no. of replicates + int years; // no. of years +// int outStartRange; // output start year for range file +// int outStartOcc; // output start year for occupancy file + int outStartPop; // output start year for population file + int outStartInd; // output start year for individuals file + int outStartGenetic; // output start year for genetics file + int outStartTraitCell; // output start year for traits by cell file + int outStartTraitRow; // output start year for traits by row file + int outStartConn; // output start year for connectivity matrix + int outIntRange; // output interval for range file + int outIntOcc; // output interval for occupancy file + int outIntPop; // output interval for population file + int outIntInd; // output interval for individuals file + int outIntGenetic; // output interval for genetics file + int outIntTraitCell; // output interval for traits by cell file + int outIntTraitRow; // output interval for traits by row file + int outIntConn; // output interval for connectivity matrix + int mapInt; // output interval for maps + int traitInt; // output interval for evolving traits maps + int slowFactor; // to reduce speed of movement paths on screen + bool batchMode; // + bool absorbing; // landscape boundary and no-data regions are + // absorbing boundaries + bool outRange; // produce output range file? + bool outOccup; // produce output occupancy file? + bool outPop; // produce output population file? + bool outInds; // produce output individuals file? + bool outGenetics; // produce output genetics file? + short outGenType; // produce output genetics for: 0 = juveniles only + // 1 = all individuals, 2 = adults (i.e. final stage) only + bool outGenXtab; // produce output genetics as a cross table? + bool outTraitsCells; // produce output summary traits by cell file? + bool outTraitsRows; // produce output summary traits by row (y) file? + bool outConnect; // produce output connectivity file? + bool saveMaps; // save landscape/population maps? + bool saveVisits; // save dispersal visits heat maps? +#if RS_RCPP + int outStartPaths; + int outIntPaths; + bool outPaths; + bool ReturnPopRaster; + bool CreatePopFile; +#endif + bool drawLoaded; // draw initial distribution on landscape/population maps? + bool saveTraitMaps; // save summary traits maps? + bool viewLand; // view landscape map on screen? + bool viewPatch; // view map of landscape patches on screen? + bool viewGrad; // view gradient map on screen? + bool viewCosts; // view costs map on screen? + bool viewPop; // view population density on landscape map on screen? + bool viewTraits; // view summary traits map(s) on screen? + bool viewPaths; // view individual movement paths on screen? + bool viewGraph; // view population/occupancy graph on screen? + string dir; // full name of working directory + +}; + +#if RSDEBUG +extern ofstream DEBUGLOG; +void DebugGUI(string); +#endif + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Patch.cpp b/src/RScore/Patch.cpp new file mode 100644 index 00000000..60421c45 --- /dev/null +++ b/src/RScore/Patch.cpp @@ -0,0 +1,358 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +//--------------------------------------------------------------------------- + +#include "Patch.h" +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +Patch::Patch(int seqnum,int num) +{ +patchSeqNum = seqnum; patchNum = num; nCells = 0; +xMin = yMin = 999999999; xMax = yMax = 0; x = y = 0; +subCommPtr = 0; +localK = 0.0; +for (int sex = 0; sex < NSEXES; sex++) { + nTemp[sex] = 0; +} +changed = false; +} + +Patch::~Patch() { +cells.clear(); +popns.clear(); +} + +int Patch::getSeqNum(void) { return patchSeqNum; } + +int Patch::getPatchNum(void) { return patchNum; } + +int Patch::getNCells(void) { return nCells; } + +patchLimits Patch::getLimits(void) { +patchLimits p; +p.xMin = xMin; p.xMax = xMax; p.yMin = yMin; p.yMax = yMax; +return p; +} + +// Does the patch fall (partially) within a specified rectangle? +bool Patch::withinLimits(patchLimits rect){ +locn loc; +if (xMin <= rect.xMax && xMax >= rect.xMin && yMin <= rect.yMax && yMax >= rect.yMin) { + // patch is within the rectangle UNLESS it is irregular in shape and lies at a corner + // of the rectangle + if ((xMin >= rect.xMin && xMax <= rect.xMax) + || (yMin >= rect.yMin && yMax <= rect.yMax)) { + // patch lies within or along an edge of the initialistaion rectangle + return true; + } + else { + // check for any cell of the patch lying within the rectangle + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + loc = getCellLocn(i); + if (loc.x >= rect.xMin && loc.x <= rect.xMax + && loc.y >= rect.yMin && loc.y <= rect.yMax) { + // cell lies within the rectangle + return true; + } + } + } + } +return false; +} + +// Reset minimum and maximum co-ordinates of the patch if it has been changed +void Patch::resetLimits(void) { +if (changed) { + // remove any deleted cells + std::vector newcells; // for all retained and added cells + int ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + if (cells[i] != NULL) { + newcells.push_back(cells[i]); + } + } + cells.clear(); + cells = newcells; + // reset patch limits + locn loc; + xMin = yMin = 999999999; xMax = yMax = 0; + ncells = (int)cells.size(); + for (int i = 0; i < ncells; i++) { + loc = getCellLocn(i); + if (loc.x < xMin) xMin = loc.x; + if (loc.x > xMax) xMax = loc.x; + if (loc.y < yMin) yMin = loc.y; + if (loc.y > yMax) yMax = loc.y; + } + changed = false; +} +} + +// Add a cell to the patch +void Patch::addCell(Cell* pCell,int x,int y) { + cells.push_back(pCell); + nCells++; + if (x < xMin) xMin = x; + if (x > xMax) xMax = x; + if (y < yMin) yMin = y; + if (y > yMax) yMax = y; +} + +// Calculate the total carrying capacity (no. of individuals) and +// centroid co-ordinates of the patch +void Patch::setCarryingCapacity(Species *pSpecies,patchLimits landlimits, + float epsGlobal,short nHab,short rasterType,short landIx,bool gradK) { +envStochParams env = paramsStoch->getStoch(); +//Cell *pCell; +locn loc; +int xsum,ysum; +short hx; +float k,q,envval; + +localK = 0.0; // no. of suitable cells (unadjusted K > 0) in the patch +int nsuitable = 0; +double mean; + +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " xMin=" << xMin << " yMin=" << yMin << " xMax=" << xMax << " yMax=" << yMax +// << endl; +#endif + +if (xMin > landlimits.xMax || xMax < landlimits.xMin +|| yMin > landlimits.yMax || yMax < landlimits.yMin) { + // patch lies wholely outwith current landscape limits + // NB the next statement is unnecessary, as localK has been set to zero above + // retained only for consistency in standard variant + localK = 0.0; +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " localK=" << localK +// << endl; +#endif + return; +} + +int ncells = (int)cells.size(); +xsum = ysum = 0; +for (int i = 0; i < ncells; i++) { + if (gradK) // gradient in carrying capacity + envval = cells[i]->getEnvVal(); // environmental gradient value + else envval = 1.0; // no gradient effect + if (env.stoch && env.inK) { // environmental stochasticity in K + if (env.local) { +// pCell = getRandomCell(); +// if (pCell != 0) envval += pCell->getEps(); + envval += cells[i]->getEps(); + } + else { // global stochasticity + envval += epsGlobal; + } + } + switch (rasterType) { + case 0: // habitat codes + hx = cells[i]->getHabIndex(landIx); + k = pSpecies->getHabK(hx); + if (k > 0.0) { + nsuitable++; + localK += envval * k; + } + break; + case 1: // cover % + k = 0.0; + for (int j = 0; j < nHab; j++) { // loop through cover layers + q = cells[i]->getHabitat(j); + k += q * pSpecies->getHabK(j) / 100.0f; + } + if (k > 0.0) { + nsuitable++; + localK += envval * k; + } + break; + case 2: // habitat quality + q = cells[i]->getHabitat(landIx); + if (q > 0.0) { + nsuitable++; + localK += envval * pSpecies->getHabK(0) * q / 100.0f; + } + break; + } +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " i=" << i << " hx=" << hx << " q=" << q << " k=" << k << " localK=" << localK +// << endl; +#endif + loc = cells[i]->getLocn(); + xsum += loc.x; ysum += loc.y; +} +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " epsGlobal=" << epsGlobal << " localK=" << localK +// << endl; +#endif +// calculate centroid co-ordinates +if (ncells > 0) { + mean = (double)xsum / (double)ncells; + x = (int)(mean + 0.5); + mean = (double)ysum / (double)ncells; + y = (int)(mean + 0.5); +} +if (env.stoch && env.inK) { // environmental stochasticity in K + // apply min and max limits to K over the whole patch + // NB limits have been stored as N/cell rather than N/ha + float limit; + limit = pSpecies->getMinMax(0) * (float)nsuitable; + if (localK < limit) localK = limit; +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " limit=" << limit << " localK=" << localK +// << endl; +#endif + limit = pSpecies->getMinMax(1) * (float)nsuitable; + if (localK > limit) localK = limit; +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " limit=" << limit << " localK=" << localK +// << endl; +#endif +} +#if RSDEBUG +//DEBUGLOG << "Patch::setCarryingCapacity(): patchNum=" << patchNum +// << " localK=" << localK +// << endl; +#endif +} + + +float Patch::getK(void) { return localK; } + +// Return co-ordinates of a specified cell +locn Patch::getCellLocn(int ix) { +locn loc; loc.x = -666; loc.y = -666; +int ncells = (int)cells.size(); +if (ix >= 0 && ix < ncells) { + loc = cells[ix]->getLocn(); +} +return loc; +} +// Return pointer to a specified cell +Cell* Patch::getCell(int ix) { +int ncells = (int)cells.size(); +if (ix >= 0 && ix < ncells) return cells[ix]; +else return 0; +} +// Return co-ordinates of patch centroid +locn Patch::getCentroid(void) { +locn loc; loc.x = x; loc.y = y; +return loc; +} + +// Select a Cell within the Patch at random, and return pointer to it +// For a cell-based model, this will be the only Cell +Cell* Patch::getRandomCell(void) { +Cell *pCell = 0; +int ix; +int ncells = (int)cells.size(); +if (ncells > 0) { + if (ncells == 1) ix = 0; + else ix = pRandom->IRandom(0,ncells-1); + pCell = cells[ix]; +} +return pCell; +} + +// Remove a cell from the patch +void Patch::removeCell(Cell* pCell) { +int ncells = (int)cells.size(); +for (int i = 0; i < ncells; i++) { + if (pCell == cells[i]) { + cells[i] = NULL; i = ncells; + nCells--; + changed = true; + } +} +} + +void Patch::setSubComm(intptr sc) +{ subCommPtr = sc; } + +// Get pointer to corresponding Sub-community (cast as an integer) +intptr Patch::getSubComm(void) +{ return subCommPtr; } + +void Patch::addPopn(patchPopn pop) { +popns.push_back(pop); +} + +// Return pointer (cast as integer) to the Population of the specified Species +intptr Patch::getPopn(intptr sp) +{ +int npops = (int)popns.size(); +for (int i = 0; i < npops; i++) { + if (popns[i].pSp == sp) return popns[i].pPop; +} +return 0; +} + +void Patch::resetPopn(void) { +popns.clear(); +} + +void Patch::resetPossSettlers(void) { +for (int sex = 0; sex < NSEXES; sex++) { + nTemp[sex] = 0; +} +} + +// Record the presence of a potential settler within the Patch +void Patch::incrPossSettler(Species *pSpecies,int sex) { +#if RSDEBUG +//DEBUGLOG << "Patch::incrPossSettler(): 5555: patchNum = " << patchNum +// << " sex = " << sex << endl; +#endif +// NOTE: THE FOLLOWING OPERATION WILL NEED TO BE MADE SPECIES-SPECIFIC... +if (sex >= 0 && sex < NSEXES) { + nTemp[sex]++; +} +} + +// Get number of a potential settlers within the Patch +int Patch::getPossSettlers(Species *pSpecies,int sex) { +#if RSDEBUG +//DEBUGLOG << "Patch::getPossSettlers(): 5555: patchNum = " << patchNum +// << " sex = " << sex << endl; +#endif +// NOTE: THE FOLLOWING OPERATION WILL NEED TO BE MADE SPECIES-SPECIFIC... +if (sex >= 0 && sex < NSEXES) return nTemp[sex]; +else return 0; +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + + + diff --git a/src/RScore/Patch.h b/src/RScore/Patch.h new file mode 100644 index 00000000..40959083 --- /dev/null +++ b/src/RScore/Patch.h @@ -0,0 +1,174 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Patch + +Implements the class: Patch + +A patch is a collection of one or more Cells in the the gridded Landscape, +which together provide the area in which a single demographic unit of a Species, +i.e. a Population, can reproduce. One or more Populations (of different Species) +form a Sub-community associated with the Patch. + +There is no requirement that all the Cells be adjacent, although in practice +that would usually be the case. + +Each Patch must have a unique positive integer id number supplied by the user, +and the matrix, i.e. any part of the landscape which is not a breeding patch, +is represented by Patch 0. However, as patch numbers need not be sequential, +an internal sequential number is also applied. + +For a 'cell-based model', the user supplies no patch numbers, and a separate +Patch is generated internally for each Cell, i.e. the 'cell-based model' is a +special case of the 'patch-based model' in which each Patch has a single Cell. +Moreover, there is also the 'matrix' Patch 0, which has no cells, but which +holds the disperser population whilst its Individuals are in transit. + +In a true patch-based model, each Patch hold a list of its constituent Cells, +EXCEPT for the matrix Patch 0. This is because that list would be extremely +long for a very large landscape in which suitable patches are small and/or rare, +and removing Cells from it if the landscape is dynamic would be inefficient. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 25 June 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef PatchH +#define PatchH + +#include +using namespace std; + +#include "Parameters.h" +#include "Cell.h" +#include "Species.h" + +//--------------------------------------------------------------------------- + +struct patchLimits { + int xMin,xMax,yMin,yMax; +}; +struct patchPopn { + intptr pSp,pPop; // pointers to Species and Population cast as integers +}; + +class Patch{ +public: + Patch( + int, // internal sequential number + int // patch id number + ); + ~Patch(); + int getSeqNum(void); + int getPatchNum(void); + int getNCells(void); + patchLimits getLimits(void); // Returns the minimum and maximum co-ordinates of the patch + bool withinLimits( // Does the patch fall (partially) within a specified rectangle? + patchLimits // structure holding the SW and NE co-ordinates of the rectangle + ); + void resetLimits(void); // Reset minimum and maximum co-ordinates of the patch + void addCell( + Cell*, // pointer to the Cell to be added to the Patch + int,int // x (column) and y (row) co-ordinates of the Cell + ); + locn getCellLocn( // Return co-ordinates of a specified cell + int // index no. of the Cell within the vector cells + ); + Cell* getCell( // Return pointer to a specified cell + int // index no. of the Cell within the vector cells + ); + locn getCentroid(void); // Return co-ordinates of patch centroid + void removeCell( + Cell* // pointer to the Cell to be removed from the Patch + ); + Cell* getRandomCell(void); + void setSubComm( + intptr // pointer to the Sub-community cast as an integer + ); + intptr getSubComm(void); + void addPopn( + patchPopn // structure holding pointers to Species and Population cast as integers + ); + intptr getPopn( // return pointer (cast as integer) to the Population of the Species + intptr // pointer to Species cast as integer + ); + void resetPopn(void); + void resetPossSettlers(void); + void incrPossSettler( // Record the presence of a potential settler within the Patch + Species*, // pointer to the Species + int // sex of the settler + ); + int getPossSettlers( // Get number of a potential settlers within the Patch + Species*, // pointer to the Species + int // sex of the settlers + ); + void setCarryingCapacity( // Calculate total Patch carrying capacity (no. of inds) + Species*, // pointer to the Species + patchLimits, // current min and max limits of landscape + float, // global stochasticity value (epsilon) for the current year + short, // no. of habitat classes in the Landscape + short, // rasterType (see Landscape) + short, // landscape change index (always zero if not dynamic) + bool // TRUE if there is a gradient in carrying capacity across the Landscape + ); + float getK(void); + // dummy function for batch version + void drawCells(float,int,rgb); + + private: + int patchSeqNum;// sequential patch number - patch 0 is reserved for the inter-patch matrix + int patchNum; // patch number as supplied by the user (not forced to be sequential) + int nCells; // no. of cells in the patch + int xMin,xMax,yMin,yMax; // min and max cell co-ordinates + int x,y; // centroid co-ordinates (approx.) + intptr subCommPtr; // pointer (cast as integer) to sub-community associated with the patch + // NOTE: FOR MULTI-SPECIES MODEL, PATCH WILL NEED TO STORE K FOR EACH SPECIES + float localK; // patch carrying capacity (individuals) + bool changed; +// NOTE: THE FOLLOWING ARRAY WILL NEED TO BE MADE SPECIES-SPECIFIC... + short nTemp[NSEXES]; // no. of potential settlers in each sex + + std::vector cells; + std::vector popns; + +}; + +//--------------------------------------------------------------------------- + +extern paramStoch *paramsStoch; +extern RSrandom *pRandom; + +#if RSDEBUG +extern ofstream DEBUGLOG; +#endif + +#endif diff --git a/src/RScore/Population.cpp b/src/RScore/Population.cpp new file mode 100644 index 00000000..3dbb6a29 --- /dev/null +++ b/src/RScore/Population.cpp @@ -0,0 +1,1673 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Population.h" +//--------------------------------------------------------------------------- + +ofstream outPop; +ofstream outInds; + +//--------------------------------------------------------------------------- + +Population::Population(void) { + nSexes = nStages = 0; + pPatch = NULL; + pSpecies = NULL; + return; +} + +Population::Population(Species* pSp, Patch* pPch, int ninds, int resol) +{ + // constructor for a Population of a specified size + + int n, nindivs, age = 0, minage, maxage, nAges = 0; + int cumtotal = 0; + float probmale; + double ageprob, ageprobsum; + std::vector ageProb; // for quasi-equilibrium initial age distribution + Cell* pCell; + + if (ninds > 0) { + inds.reserve(ninds); + juvs.reserve(ninds); + } + + pSpecies = pSp; + pPatch = pPch; + // record the new population in the patch + patchPopn pp; + pp.pSp = (intptr)pSpecies; pp.pPop = (intptr)this; + pPatch->addPopn(pp); + + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + genomeData gen = pSpecies->getGenomeData(); + initParams init = paramsInit->getInit(); + + // determine no. of stages and sexes of species to initialise + if (dem.stageStruct) { + nStages = sstruct.nStages; + } + else // non-structured population has 2 stages, but user only ever sees stage 1 + nStages = 2; + if (dem.repType == 0) { nSexes = 1; probmale = 0.0; } + else { nSexes = 2; probmale = dem.propMales; } + + // set up population sub-totals + for (int stg = 0; stg < NSTAGES; stg++) { + for (int sex = 0; sex < NSEXES; sex++) { + nInds[stg][sex] = 0; + } + } + + // set up local copy of minimum age table + short minAge[NSTAGES][NSEXES]; + for (int stg = 0; stg < nStages; stg++) { + for (int sex = 0; sex < nSexes; sex++) { + if (dem.stageStruct) { + if (dem.repType == 1) { // simple sexual model + // both sexes use minimum ages recorded for females + minAge[stg][sex] = pSpecies->getMinAge(stg, 0); + } + else { + minAge[stg][sex] = pSpecies->getMinAge(stg, sex); + } + } + else { // non-structured population + minAge[stg][sex] = 0; + } + } + } + + // individuals of new population must be >= stage 1 + for (int stg = 1; stg < nStages; stg++) { + if (dem.stageStruct) { // allocate to stages according to initialisation conditions + // final stage is treated separately to ensure that correct total + // no. of individuals is created + if (stg == nStages - 1) { + n = ninds - cumtotal; + } + else { + n = (int)(ninds * paramsInit->getProp(stg) + 0.5); + cumtotal += n; + } + } + else { // non-structured - all individuals go into stage 1 + n = ninds; + } + // establish initial age distribution + minage = maxage = stg; + if (dem.stageStruct) { + // allow for stage-dependent minimum ages (use whichever sex is greater) + if (minAge[stg][0] > 0 && minage < minAge[stg][0]) minage = minAge[stg][0]; + if (nSexes == 2 && minAge[stg][1] > 0 && minage < minAge[stg][1]) minage = minAge[stg][1]; + // allow for specified age distribution + if (init.initAge != 0) { // not lowest age + if (stg == nStages - 1) maxage = sstruct.maxAge; // final stage + else { // all other stages - use female max age, as sex of individuals is not predetermined + maxage = minAge[stg + 1][0] - 1; + } + if (maxage < minage) maxage = minage; + nAges = maxage - minage + 1; + if (init.initAge == 2) { // quasi-equilibrium distribution + double psurv = (double)pSpecies->getSurv(stg, 0); // use female survival for the stage + ageProb.clear(); + ageprobsum = 0.0; + ageprob = 1.0; + for (int i = 0; i < nAges; i++) { + ageProb.push_back(ageprob); ageprobsum += ageprob; ageprob *= psurv; + } + for (int i = 0; i < nAges; i++) { + ageProb[i] /= ageprobsum; + if (i > 0) ageProb[i] += ageProb[i - 1]; // to give cumulative probability + } + } + } + } + // create individuals + int sex; + nindivs = (int)inds.size(); + for (int i = 0; i < n; i++) { + pCell = pPatch->getRandomCell(); + if (dem.stageStruct) { + switch (init.initAge) { + case 0: // lowest possible age + age = minage; + break; + case 1: // randomised + if (maxage > minage) age = pRandom->IRandom(minage, maxage); + else age = minage; + break; + case 2: // quasi-equilibrium + if (nAges > 1) { + double rrr = pRandom->Random(); + int ageclass = 0; + while (rrr > ageProb[ageclass]) ageclass++; + age = minage + ageclass; + } + else age = minage; + break; + } + } + else age = stg; +#if RSDEBUG + // NOTE: CURRENTLY SETTING ALL INDIVIDUALS TO RECORD NO. OF STEPS ... + inds.push_back(new Individual(pCell, pPatch, stg, age, sstruct.repInterval, + probmale, true, trfr.moveType)); +#else + inds.push_back(new Individual(pCell, pPatch, stg, age, sstruct.repInterval, + probmale, trfr.moveModel, trfr.moveType)); +#endif + sex = inds[nindivs + i]->getSex(); + if (emig.indVar || trfr.indVar || sett.indVar || gen.neutralMarkers) + { + // individual variation - set up genetics + inds[nindivs + i]->setGenes(pSpecies, resol); + } + nInds[stg][sex]++; + } + } +} + +Population::~Population(void) { + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + if (inds[i] != NULL) delete inds[i]; + } + inds.clear(); + int njuvs = (int)juvs.size(); + for (int i = 0; i < njuvs; i++) { + if (juvs[i] != NULL) delete juvs[i]; + } + juvs.clear(); +} + +traitsums Population::getTraits(Species* pSpecies) { + int g; + traitsums ts; + for (int i = 0; i < NSEXES; i++) { + ts.ninds[i] = 0; + ts.sumD0[i] = ts.ssqD0[i] = 0.0; + ts.sumAlpha[i] = ts.ssqAlpha[i] = 0.0; ts.sumBeta[i] = ts.ssqBeta[i] = 0.0; + ts.sumDist1[i] = ts.ssqDist1[i] = 0.0; ts.sumDist2[i] = ts.ssqDist2[i] = 0.0; + ts.sumProp1[i] = ts.ssqProp1[i] = 0.0; + ts.sumDP[i] = ts.ssqDP[i] = 0.0; + ts.sumGB[i] = ts.ssqGB[i] = 0.0; + ts.sumAlphaDB[i] = ts.ssqAlphaDB[i] = 0.0; + ts.sumBetaDB[i] = ts.ssqBetaDB[i] = 0.0; + ts.sumStepL[i] = ts.ssqStepL[i] = 0.0; ts.sumRho[i] = ts.ssqRho[i] = 0.0; + ts.sumS0[i] = ts.ssqS0[i] = 0.0; + ts.sumAlphaS[i] = ts.ssqAlphaS[i] = 0.0; ts.sumBetaS[i] = ts.ssqBetaS[i] = 0.0; + } + + demogrParams dem = pSpecies->getDemogr(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + int sex = inds[i]->getSex(); + if (emig.sexDep || trfr.sexDep || sett.sexDep) g = sex; else g = 0; + ts.ninds[g] += 1; + // emigration traits + emigTraits e = inds[i]->getEmigTraits(); + if (emig.sexDep) g = sex; else g = 0; + ts.sumD0[g] += e.d0; ts.ssqD0[g] += e.d0 * e.d0; + ts.sumAlpha[g] += e.alpha; ts.ssqAlpha[g] += e.alpha * e.alpha; + ts.sumBeta[g] += e.beta; ts.ssqBeta[g] += e.beta * e.beta; + // transfer traits + trfrKernTraits k = inds[i]->getKernTraits(); + if (trfr.sexDep) g = sex; else g = 0; + ts.sumDist1[g] += k.meanDist1; ts.ssqDist1[g] += k.meanDist1 * k.meanDist1; + ts.sumDist2[g] += k.meanDist2; ts.ssqDist2[g] += k.meanDist2 * k.meanDist2; + ts.sumProp1[g] += k.probKern1; ts.ssqProp1[g] += k.probKern1 * k.probKern1; + trfrSMSTraits sms = inds[i]->getSMSTraits(); + g = 0; // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + ts.sumDP[g] += sms.dp; ts.ssqDP[g] += sms.dp * sms.dp; + ts.sumGB[g] += sms.gb; ts.ssqGB[g] += sms.gb * sms.gb; + ts.sumAlphaDB[g] += sms.alphaDB; ts.ssqAlphaDB[g] += sms.alphaDB * sms.alphaDB; + ts.sumBetaDB[g] += sms.betaDB; ts.ssqBetaDB[g] += sms.betaDB * sms.betaDB; + trfrCRWTraits c = inds[i]->getCRWTraits(); + g = 0; // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + ts.sumStepL[g] += c.stepLength; ts.ssqStepL[g] += c.stepLength * c.stepLength; + ts.sumRho[g] += c.rho; ts.ssqRho[g] += c.rho * c.rho; + // settlement traits + settleTraits s = inds[i]->getSettTraits(); + if (sett.sexDep) g = sex; else g = 0; + ts.sumS0[g] += s.s0; ts.ssqS0[g] += s.s0 * s.s0; + ts.sumAlphaS[g] += s.alpha; ts.ssqAlphaS[g] += s.alpha * s.alpha; + ts.sumBetaS[g] += s.beta; ts.ssqBetaS[g] += s.beta * s.beta; + } + + return ts; +} + +int Population::getNInds(void) { return (int)inds.size(); } + +popStats Population::getStats(void) +{ + popStats p; + int ninds; + float fec; + bool breeders[2]; breeders[0] = breeders[1] = false; + demogrParams dem = pSpecies->getDemogr(); + p.pSpecies = pSpecies; + p.pPatch = pPatch; + p.spNum = pSpecies->getSpNum(); + p.nInds = (int)inds.size(); + p.nNonJuvs = p.nAdults = 0; + p.breeding = false; + for (int stg = 1; stg < nStages; stg++) { + for (int sex = 0; sex < nSexes; sex++) { + ninds = nInds[stg][sex]; + p.nNonJuvs += ninds; + + if (ninds > 0) { + if (pSpecies->stageStructured()) { + if (dem.repType == 2) fec = pSpecies->getFec(stg, sex); + else fec = pSpecies->getFec(stg, 0); + if (fec > 0.0) { breeders[sex] = true; p.nAdults += ninds; } + } + else breeders[sex] = true; + } + } + } + // is there a breeding population present? + if (nSexes == 1) { + p.breeding = breeders[0]; + } + else { + if (breeders[0] && breeders[1]) p.breeding = true; + } + return p; +} + +Species* Population::getSpecies(void) { return pSpecies; } + +int Population::totalPop(void) { + int t = 0; + for (int stg = 0; stg < nStages; stg++) { + for (int sex = 0; sex < nSexes; sex++) { + t += nInds[stg][sex]; + } + } + return t; +} + +int Population::stagePop(int stg) { + int t = 0; + if (stg < 0 || stg >= nStages) return t; + for (int sex = 0; sex < nSexes; sex++) { + t += nInds[stg][sex]; + } + return t; +} + +//--------------------------------------------------------------------------- +// Remove all Individuals +void Population::extirpate(void) { + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + if (inds[i] != NULL) delete inds[i]; + } + inds.clear(); + int njuvs = (int)juvs.size(); + for (int i = 0; i < njuvs; i++) { + if (juvs[i] != NULL) delete juvs[i]; + } + juvs.clear(); + for (int sex = 0; sex < nSexes; sex++) { + for (int stg = 0; stg < nStages; stg++) { + nInds[stg][sex] = 0; + } + } +} + +//--------------------------------------------------------------------------- +// Produce juveniles and hold them in the juvs vector +void Population::reproduction(const float localK, const float envval, const int resol) +{ + + // get population size at start of reproduction + int ninds = (int)inds.size(); + if (ninds == 0) return; + + int nsexes, stage, sex, njuvs, nj, nmales, nfemales; + Cell* pCell; + indStats ind; + double expected; + bool skipbreeding; + + envStochParams env = paramsStoch->getStoch(); + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + genomeData gen = pSpecies->getGenomeData(); + simView v = paramsSim->getViews(); + + if (dem.repType == 0) nsexes = 1; else nsexes = 2; + + // set up local copy of species fecundity table + float fec[NSTAGES][NSEXES]; + for (int stg = 0; stg < sstruct.nStages; stg++) { + for (int sex = 0; sex < nsexes; sex++) { + if (dem.stageStruct) { + if (dem.repType == 1) { // simple sexual model + // both sexes use fecundity recorded for females + fec[stg][sex] = pSpecies->getFec(stg, 0); + } + else fec[stg][sex] = pSpecies->getFec(stg, sex); + } + else { // non-structured population + if (stg == 1) fec[stg][sex] = dem.lambda; // adults + else fec[stg][sex] = 0.0; // juveniles + } + } + } + + if (dem.stageStruct) { + // apply environmental effects and density dependence + // to all non-zero female non-juvenile stages + for (int stg = 1; stg < nStages; stg++) { + if (fec[stg][0] > 0.0) { + // apply any effect of environmental gradient and/or stochasticty + fec[stg][0] *= envval; + if (env.stoch && !env.inK) { + // fecundity (at low density) is constrained to lie between limits specified + // for the species + float limit; + limit = pSpecies->getMinMax(0); + if (fec[stg][0] < limit) fec[stg][0] = limit; + limit = pSpecies->getMinMax(1); + if (fec[stg][0] > limit) fec[stg][0] = limit; + } + if (sstruct.fecDens) { // apply density dependence + float effect = 0.0; + if (sstruct.fecStageDens) { // stage-specific density dependence + // NOTE: matrix entries represent effect of ROW on COLUMN + // AND males precede females + float weight = 0.0; + for (int effstg = 0; effstg < nStages; effstg++) { + for (int effsex = 0; effsex < nSexes; effsex++) { + if (dem.repType == 2) { + if (effsex == 0) weight = pSpecies->getDDwtFec(2 * stg + 1, 2 * effstg + 1); + else weight = pSpecies->getDDwtFec(2 * stg + 1, 2 * effstg); + } + else { + weight = pSpecies->getDDwtFec(stg, effstg); + } + effect += (float)nInds[effstg][effsex] * weight; + } + } + } + else // not stage-specific + effect = (float)totalPop(); + if (localK > 0.0) fec[stg][0] *= exp(-effect / localK); + } + } + } + } + else { // non-structured - set fecundity for adult females only + // apply any effect of environmental gradient and/or stochasticty + fec[1][0] *= envval; + if (env.stoch && !env.inK) { + // fecundity (at low density) is constrained to lie between limits specified + // for the species + float limit; + limit = pSpecies->getMinMax(0); + if (fec[1][0] < limit) fec[1][0] = limit; + limit = pSpecies->getMinMax(1); + if (fec[1][0] > limit) fec[1][0] = limit; + } + // apply density dependence + if (localK > 0.0) { + if (dem.repType == 1 || dem.repType == 2) { // sexual model + // apply factor of 2 (as in manual, eqn. 6) + fec[1][0] *= 2.0; + } + fec[1][0] /= (1.0f + fabs(dem.lambda - 1.0f) * pow(((float)ninds / localK), dem.bc)); + } + } + + double propBreed; + Individual* father; + std::vector fathers; + + switch (dem.repType) { + + case 0: // asexual model + for (int i = 0; i < ninds; i++) { + stage = inds[i]->breedingFem(); + if (stage > 0) { // female of breeding age + if (dem.stageStruct) { + // determine whether she must miss current breeding attempt + ind = inds[i]->getStats(); + if (ind.fallow >= sstruct.repInterval) { + if (pRandom->Bernoulli(sstruct.probRep)) skipbreeding = false; + else skipbreeding = true; + } + else skipbreeding = true; // cannot breed this time + } + else skipbreeding = false; // not structured - always breed + if (skipbreeding) { + inds[i]->incFallow(); + } + else { // attempt to breed + inds[i]->resetFallow(); + expected = fec[stage][0]; + if (expected <= 0.0) njuvs = 0; + else njuvs = pRandom->Poisson(expected); + nj = (int)juvs.size(); + pCell = pPatch->getRandomCell(); + for (int j = 0; j < njuvs; j++) { +#if RSDEBUG + // NOTE: CURRENTLY SETTING ALL INDIVIDUALS TO RECORD NO. OF STEPS ... + juvs.push_back(new Individual(pCell, pPatch, 0, 0, 0, 0.0, true, trfr.moveType)); +#else + juvs.push_back(new Individual(pCell, pPatch, 0, 0, 0, 0.0, trfr.moveModel, trfr.moveType)); +#endif + nInds[0][0]++; + if (emig.indVar || trfr.indVar || sett.indVar || gen.neutralMarkers) + { + // juv inherits genome from parent (mother) + juvs[nj + j]->setGenes(pSpecies, inds[i], 0, resol); + } + } + } + } + } + break; + + case 1: // simple sexual model + case 2: // complex sexual model + // count breeding females and males + // add breeding males to list of potential fathers + + nfemales = nmales = 0; + for (int i = 0; i < ninds; i++) { + ind = inds[i]->getStats(); + if (ind.sex == 0 && fec[ind.stage][0] > 0.0) nfemales++; + if (ind.sex == 1 && fec[ind.stage][1] > 0.0) { + fathers.push_back(inds[i]); + nmales++; + } + } + if (nfemales > 0 && nmales > 0) + { // population can breed + if (dem.repType == 2) { // complex sexual model + // calculate proportion of eligible females which breed + propBreed = (2.0 * dem.harem * nmales) / (nfemales + dem.harem * nmales); + if (propBreed > 1.0) propBreed = 1.0; + } + else propBreed = 1.0; + for (int i = 0; i < ninds; i++) { + stage = inds[i]->breedingFem(); + if (stage > 0 && fec[stage][0] > 0.0) { // (potential) breeding female + if (dem.stageStruct) { + // determine whether she must miss current breeding attempt + ind = inds[i]->getStats(); + if (ind.fallow >= sstruct.repInterval) { + if (pRandom->Bernoulli(sstruct.probRep)) skipbreeding = false; + else skipbreeding = true; + } + else skipbreeding = true; // cannot breed this time + } + else skipbreeding = false; // not structured - always breed + if (skipbreeding) { + inds[i]->incFallow(); + } + else { // attempt to breed + inds[i]->resetFallow(); + // NOTE: FOR COMPLEX SEXUAL MODEL, NO. OF FEMALES *ACTUALLY* BREEDING DOES NOT + // NECESSARILY EQUAL THE EXPECTED NO. FROM EQN. 7 IN THE MANUAL... + if (pRandom->Bernoulli(propBreed)) { + expected = fec[stage][0]; // breeds + } + else expected = 0.0; // fails to breed + if (expected <= 0.0) njuvs = 0; + else njuvs = pRandom->Poisson(expected); + if (njuvs > 0) + { + nj = (int)juvs.size(); + // select father at random from breeding males ... + int rrr = 0; + if (nmales > 1) rrr = pRandom->IRandom(0, nmales - 1); + father = fathers[rrr]; + pCell = pPatch->getRandomCell(); + for (int j = 0; j < njuvs; j++) { +#if RSDEBUG + // NOTE: CURRENTLY SETTING ALL INDIVIDUALS TO RECORD NO. OF STEPS ... + juvs.push_back(new Individual(pCell, pPatch, 0, 0, 0, dem.propMales, true, trfr.moveType)); +#else + juvs.push_back(new Individual(pCell, pPatch, 0, 0, 0, dem.propMales, trfr.moveModel, trfr.moveType)); +#endif + sex = juvs[nj + j]->getSex(); + nInds[0][sex]++; + if (emig.indVar || trfr.indVar || sett.indVar || gen.neutralMarkers) + { + // juv inherits genome from parents + juvs[nj + j]->setGenes(pSpecies, inds[i], father, resol); + } + } + } + } + } + } + } + fathers.clear(); + break; + + } // end of switch (dem.repType) + +// THIS MAY NOT BE CORRECT FOR MULTIPLE SPECIES IF THERE IS SOME FORM OF +// CROSS-SPECIES DENSITY-DEPENDENT FECUNDITY +} + +// Following reproduction of ALL species, add juveniles to the population prior to dispersal +void Population::fledge(void) +{ + demogrParams dem = pSpecies->getDemogr(); + + if (dem.stageStruct) { // juveniles are added to the individuals vector + inds.insert(inds.end(), juvs.begin(), juvs.end()); + } + else { // all adults die and juveniles replace adults + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + delete inds[i]; + } + inds.clear(); + for (int sex = 0; sex < nSexes; sex++) { + nInds[1][sex] = 0; // set count of adults to zero + } + inds = juvs; + } + juvs.clear(); + +} + +// Determine which individuals will disperse +void Population::emigration(float localK) +{ + int nsexes; + double disp, Pdisp, NK; + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + emigTraits eparams; + trfrRules trfr = pSpecies->getTrfr(); + indStats ind; + +// to avoid division by zero, assume carrying capacity is at least one individual +// localK can be zero if there is a moving gradient or stochasticity in K + if (localK < 1.0) localK = 1.0; + NK = (float)totalPop() / localK; + + int ninds = (int)inds.size(); + + // set up local copy of emigration probability table + // used when there is no individual variability + // NB - IT IS DOUBTFUL THIS CONTRIBUTES ANY SUBSTANTIAL TIME SAVING + if (dem.repType == 0) nsexes = 1; else nsexes = 2; + double Pemig[NSTAGES][NSEXES]; + + for (int stg = 0; stg < sstruct.nStages; stg++) { + for (int sex = 0; sex < nsexes; sex++) { + if (emig.indVar) Pemig[stg][sex] = 0.0; + else { + if (emig.densDep) { + if (emig.sexDep) { + if (emig.stgDep) { + eparams = pSpecies->getEmigTraits(stg, sex); + } + else { + eparams = pSpecies->getEmigTraits(0, sex); + } + } + else { // !emig.sexDep + if (emig.stgDep) { + eparams = pSpecies->getEmigTraits(stg, 0); + } + else { + eparams = pSpecies->getEmigTraits(0, 0); + } + } + Pemig[stg][sex] = eparams.d0 / (1.0 + exp(-(NK - eparams.beta) * eparams.alpha)); + } + else { // density-independent + if (emig.sexDep) { + if (emig.stgDep) { + Pemig[stg][sex] = pSpecies->getEmigD0(stg, sex); + } + else { // !emig.stgDep + Pemig[stg][sex] = pSpecies->getEmigD0(0, sex); + } + } + else { // !emig.sexDep + if (emig.stgDep) { + Pemig[stg][sex] = pSpecies->getEmigD0(stg, 0); + } + else { // !emig.stgDep + Pemig[stg][sex] = pSpecies->getEmigD0(0, 0); + } + } + } + } // end of !emig.indVar + } + } + + for (int i = 0; i < ninds; i++) { + ind = inds[i]->getStats(); + if (ind.status < 1) + { + if (emig.indVar) { // individual variability in emigration + if (dem.stageStruct && ind.stage != emig.emigStage) { + // emigration may not occur + Pdisp = 0.0; + } + else { // non-structured or individual is in emigration stage + eparams = inds[i]->getEmigTraits(); + if (emig.densDep) { // density-dependent + NK = (float)totalPop() / localK; + Pdisp = eparams.d0 / (1.0 + exp(-(NK - eparams.beta) * eparams.alpha)); + } + else { // density-independent + if (emig.sexDep) { + Pdisp = Pemig[0][ind.sex] + eparams.d0; + } + else { + Pdisp = Pemig[0][0] + eparams.d0; + } + } + } + } // end of individual variability + else { // no individual variability + + if (emig.densDep) { + if (emig.sexDep) { + if (emig.stgDep) { + Pdisp = Pemig[ind.stage][ind.sex]; + } + else { + Pdisp = Pemig[0][ind.sex]; + } + } + else { // !emig.sexDep + if (emig.stgDep) { + Pdisp = Pemig[ind.stage][0]; + } + else { + Pdisp = Pemig[0][0]; + } + } + } + else { // density-independent + if (emig.sexDep) { + if (emig.stgDep) { + Pdisp = Pemig[ind.stage][ind.sex]; + } + else { // !emig.stgDep + Pdisp = Pemig[0][ind.sex]; + } + } + else { // !emig.sexDep + if (emig.stgDep) { + Pdisp = Pemig[ind.stage][0]; + } + else { // !emig.stgDep + Pdisp = Pemig[0][0]; + } + } + } + + + } // end of no individual variability + + disp = pRandom->Bernoulli(Pdisp); + + if (disp == 1) { // emigrant + inds[i]->setStatus(1); + } + } // end of if (ind.status < 1) condition + } // end of for loop +} + +// All individuals emigrate after patch destruction +void Population::allEmigrate(void) { + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + inds[i]->setStatus(1); + } +} + +// If an Individual has been identified as an emigrant, remove it from the Population +disperser Population::extractDisperser(int ix) { + disperser d; + indStats ind = inds[ix]->getStats(); + if (ind.status == 1) { // emigrant + d.pInd = inds[ix]; d.yes = true; + inds[ix] = 0; + nInds[ind.stage][ind.sex]--; + } + else { + d.pInd = NULL; d.yes = false; + } + return d; +} + +// For an individual identified as being in the matrix population: +// if it is a settler, return its new location and remove it from the current population +// otherwise, leave it in the matrix population for possible reporting before deletion +disperser Population::extractSettler(int ix) { + disperser d; + Cell* pCell; + + indStats ind = inds[ix]->getStats(); + + pCell = inds[ix]->getLocn(1); + d.pInd = inds[ix]; d.pCell = pCell; d.yes = false; + if (ind.status == 4 || ind.status == 5) { // settled + d.yes = true; + inds[ix] = 0; + nInds[ind.stage][ind.sex]--; + } + return d; +} + +// Add a specified individual to the new/current dispersal group +// Add a specified individual to the population +void Population::recruit(Individual* pInd) { + inds.push_back(pInd); + indStats ind = pInd->getStats(); + nInds[ind.stage][ind.sex]++; +} + +//--------------------------------------------------------------------------- + +// Transfer is run for populations in the matrix only +#if RS_RCPP // included also SEASONAL +int Population::transfer(Landscape* pLandscape, short landIx, short nextseason) +#else +int Population::transfer(Landscape* pLandscape, short landIx) +#endif +{ + int ndispersers = 0; + int disperser; + short othersex; + bool mateOK, densdepOK; + intptr patch, popn; + int patchnum; + double localK, popsize, settprob; + Patch* pPatch = 0; + Cell* pCell = 0; + indStats ind; + Population* pNewPopn = 0; + locn newloc, nbrloc; + + landData ppLand = pLandscape->getLandData(); + short reptype = pSpecies->getRepType(); + trfrRules trfr = pSpecies->getTrfr(); + settleType settletype = pSpecies->getSettle(); + settleRules sett; + settleTraits settDD; + settlePatch settle; + simParams sim = paramsSim->getSim(); + + // each individual takes one step + // for dispersal by kernel, this should be the only step taken + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + if (trfr.moveModel) { + disperser = inds[i]->moveStep(pLandscape, pSpecies, landIx, sim.absorbing); + } + else { + disperser = inds[i]->moveKernel(pLandscape, pSpecies, reptype, sim.absorbing); + } + ndispersers += disperser; + if (disperser) { + if (reptype > 0) + { // sexual species - record as potential settler in new patch + if (inds[i]->getStatus() == 2) + { // disperser has found a patch + pCell = inds[i]->getLocn(1); + patch = pCell->getPatch(); + if (patch != 0) { // not no-data area + pPatch = (Patch*)patch; + pPatch->incrPossSettler(pSpecies, inds[i]->getSex()); + } + } + } + } + } + +// each individual which has reached a potential patch decides whether to settle + for (int i = 0; i < ninds; i++) { + ind = inds[i]->getStats(); + if (ind.sex == 0) othersex = 1; else othersex = 0; + if (settletype.stgDep) { + if (settletype.sexDep) sett = pSpecies->getSettRules(ind.stage, ind.sex); + else sett = pSpecies->getSettRules(ind.stage, 0); + } + else { + if (settletype.sexDep) sett = pSpecies->getSettRules(0, ind.sex); + else sett = pSpecies->getSettRules(0, 0); + } + if (ind.status == 2) + { // awaiting settlement + pCell = inds[i]->getLocn(1); + if (pCell == 0) { + // this condition can occur in a patch-based model at the time of a dynamic landscape + // change when there is a range restriction in place, since a patch can straddle the + // range restriction and an individual forced to disperse upon patch removal could + // start its trajectory beyond the boundary of the restrictyed range - such a model is + // not good practice, but the condition must be handled by killing the individual conceerned + ind.status = 6; + } + else { + mateOK = false; + if (sett.findMate) { + // determine whether at least one individual of the opposite sex is present in the + // new population + if (matePresent(pCell, othersex)) mateOK = true; + } + else { // no requirement to find a mate + mateOK = true; + } + + densdepOK = false; + settle = inds[i]->getSettPatch(); + if (sett.densDep) + { + patch = pCell->getPatch(); + if (patch != 0) { // not no-data area + pPatch = (Patch*)patch; + if (settle.settleStatus == 0 + || settle.pSettPatch != pPatch) + // note: second condition allows for having moved from one patch to another + // adjacent one + { + // determine whether settlement occurs in the (new) patch + localK = (double)pPatch->getK(); + popn = pPatch->getPopn((intptr)pSpecies); + if (popn == 0) { // population has not been set up in the new patch + popsize = 0.0; + } + else { + pNewPopn = (Population*)popn; + popsize = (double)pNewPopn->totalPop(); + } + if (localK > 0.0) { + // make settlement decision + if (settletype.indVar) settDD = inds[i]->getSettTraits(); +#if RS_RCPP + else settDD = pSpecies->getSettTraits(ind.stage, ind.sex); +#else + else { + if (settletype.sexDep) { + if (settletype.stgDep) + settDD = pSpecies->getSettTraits(ind.stage, ind.sex); + else + settDD = pSpecies->getSettTraits(0, ind.sex); + } + else { + if (settletype.stgDep) + settDD = pSpecies->getSettTraits(ind.stage, 0); + else + settDD = pSpecies->getSettTraits(0, 0); + } + } +#endif //RS_RCPP + settprob = settDD.s0 / + (1.0 + exp(-(popsize / localK - (double)settDD.beta) * (double)settDD.alpha)); + + if (pRandom->Bernoulli(settprob)) { // settlement allowed + densdepOK = true; + settle.settleStatus = 2; + } + else { // settlement procluded + settle.settleStatus = 1; + } + settle.pSettPatch = pPatch; + } + inds[i]->setSettPatch(settle); + } + else { + if (settle.settleStatus == 2) { // previously allowed to settle + densdepOK = true; + } + } + } + } + else { // no density-dependent settlement + densdepOK = true; + settle.settleStatus = 2; + settle.pSettPatch = pPatch; + inds[i]->setSettPatch(settle); + } + + if (mateOK && densdepOK) { // can recruit to patch + ind.status = 4; + ndispersers--; + } + else { // does not recruit + if (trfr.moveModel) { + ind.status = 1; // continue dispersing, unless ... + // ... maximum steps has been exceeded + pathSteps steps = inds[i]->getSteps(); + settleSteps settsteps = pSpecies->getSteps(ind.stage, ind.sex); + if (steps.year >= settsteps.maxStepsYr) { + ind.status = 3; // waits until next year + } + if (steps.total >= settsteps.maxSteps) { + ind.status = 6; // dies + } + } + else { // dispersal kernel + if (sett.wait) { + ind.status = 3; // wait until next dispersal event + } + else { + ind.status = 6; // (dies unless a neighbouring cell is suitable) + } + ndispersers--; + } + } + } + + inds[i]->setStatus(ind.status); + } +#if RS_RCPP + // write each individuals current movement step and status to paths file + if (trfr.moveModel && sim.outPaths) { + if (nextseason >= sim.outStartPaths && nextseason % sim.outIntPaths == 0) { + inds[i]->outMovePath(nextseason); + } + } +#endif + + if (!trfr.moveModel && sett.go2nbrLocn && (ind.status == 3 || ind.status == 6)) + { + // for kernel-based transfer only ... + // determine whether recruitment to a neighbouring cell is possible + + pCell = inds[i]->getLocn(1); + newloc = pCell->getLocn(); + vector nbrlist; + for (int dx = -1; dx < 2; dx++) { + for (int dy = -1; dy < 2; dy++) { + if (dx != 0 || dy != 0) { //cell is not the current cell + nbrloc.x = newloc.x + dx; nbrloc.y = newloc.y + dy; + if (nbrloc.x >= 0 && nbrloc.x <= ppLand.maxX + && nbrloc.y >= 0 && nbrloc.y <= ppLand.maxY) { // within landscape + // add to list of potential neighbouring cells if suitable, etc. + pCell = pLandscape->findCell(nbrloc.x, nbrloc.y); + if (pCell != 0) { // not no-data area + patch = pCell->getPatch(); + if (patch != 0) { // not no-data area + pPatch = (Patch*)patch; + patchnum = pPatch->getPatchNum(); + if (patchnum > 0 && pPatch != inds[i]->getNatalPatch()) + { // not the matrix or natal patch + if (pPatch->getK() > 0.0) + { // suitable + if (sett.findMate) { + if (matePresent(pCell, othersex)) nbrlist.push_back(pCell); + } + else + nbrlist.push_back(pCell); + } + } + } + } + } + } + } + } + int listsize = (int)nbrlist.size(); + if (listsize > 0) { // there is at least one suitable neighbouring cell + if (listsize == 1) { + inds[i]->moveto(nbrlist[0]); + } + else { // select at random from the list + int rrr = pRandom->IRandom(0, listsize - 1); + inds[i]->moveto(nbrlist[rrr]); + } + } + // else list empty - do nothing - individual retains its current location and status + } + } + return ndispersers; +} + +// Determine whether there is a potential mate present in a patch which a potential +// settler has reached +bool Population::matePresent(Cell* pCell, short othersex) +{ + int patch; + Patch* pPatch; + Population* pNewPopn; + int popsize = 0; + bool matefound = false; + + patch = (int)pCell->getPatch(); + if (patch != 0) { + pPatch = (Patch*)pCell->getPatch(); + if (pPatch->getPatchNum() > 0) { // not the matrix patch + if (pPatch->getK() > 0.0) + { // suitable + pNewPopn = (Population*)pPatch->getPopn((intptr)pSpecies); + if (pNewPopn != 0) { + // count members of other sex already resident in the patch + for (int stg = 0; stg < nStages; stg++) { + popsize += pNewPopn->nInds[stg][othersex]; + } + } + if (popsize < 1) { + // add any potential settlers of the other sex + popsize += pPatch->getPossSettlers(pSpecies, othersex); + } + } + } + } + if (popsize > 0) matefound = true; + return matefound; +} + +//--------------------------------------------------------------------------- +// Determine survival and development and record in individual's status code +// Changes are NOT applied to the Population at this stage + +// FOR MULTIPLE SPECIES, MAY NEED TO SEPARATE OUT THIS IDENTIFICATION STAGE, +// SO THAT IT CAN BE PERFORMED FOR ALL SPECIES BEFORE ANY UPDATING OF POPULATIONS + +void Population::survival0(float localK, short option0, short option1) +{ + // option0: 0 - stage 0 (juveniles) only + // 1 - all stages + // 2 - stage 1 and above (all non-juveniles) + // option1: 0 - development only (when survival is annual) + // 1 - development and survival + // 2 - survival only (when survival is annual) + + densDepParams ddparams = pSpecies->getDensDep(); + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + + // get surrent population size + int ninds = (int)inds.size(); + if (ninds == 0) return; + + // set up local copies of species development and survival tables + int nsexes; + if (dem.repType == 0) nsexes = 1; else nsexes = 2; + float dev[NSTAGES][NSEXES]; + float surv[NSTAGES][NSEXES]; + short minAge[NSTAGES][NSEXES]; + for (int stg = 0; stg < sstruct.nStages; stg++) { + for (int sex = 0; sex < nsexes; sex++) { + if (dem.stageStruct) { + if (dem.repType == 1) { // simple sexual model + // both sexes use development and survival recorded for females + dev[stg][sex] = pSpecies->getDev(stg, 0); + surv[stg][sex] = pSpecies->getSurv(stg, 0); + minAge[stg][sex] = pSpecies->getMinAge(stg, 0); + } + else { + dev[stg][sex] = pSpecies->getDev(stg, sex); + surv[stg][sex] = pSpecies->getSurv(stg, sex); + minAge[stg][sex] = pSpecies->getMinAge(stg, sex); + } + if (option1 == 0) surv[stg][sex] = 1.0; // development only - all survive + if (option1 == 2) dev[stg][sex] = 0.0; // survival only - none develops + } + else { // non-structured population + if (stg == 1) { // adults + dev[stg][sex] = 0.0; surv[stg][sex] = 0.0; minAge[stg][sex] = 0; + } + else { // juveniles + dev[stg][sex] = 1.0; surv[stg][sex] = 1.0; minAge[stg][sex] = 0; + } + } + } + } + + if (dem.stageStruct) { + // apply density dependence in development and/or survival probabilities + for (int stg = 0; stg < nStages; stg++) { + for (int sex = 0; sex < nsexes; sex++) { + if (option1 != 2 && sstruct.devDens && stg > 0) { + // NB DD in development does NOT apply to juveniles, + // which must develop to stage 1 if they survive + float effect = 0.0; + if (sstruct.devStageDens) { // stage-specific density dependence + // NOTE: matrix entries represent effect of ROW on COLUMN + // AND males precede females + float weight = 0.0; + for (int effstg = 0; effstg < nStages; effstg++) { + for (int effsex = 0; effsex < nSexes; effsex++) { + if (dem.repType == 2) { + int rowincr, colincr; + if (effsex == 0) rowincr = 1; else rowincr = 0; + if (sex == 0) colincr = 1; else colincr = 0; + weight = pSpecies->getDDwtDev(2 * stg + colincr, 2 * effstg + rowincr); + } + else { + weight = pSpecies->getDDwtDev(stg, effstg); + } + effect += (float)nInds[effstg][effsex] * weight; + } + } + } + else // not stage-specific + effect = (float)totalPop(); + if (localK > 0.0) + dev[stg][sex] *= exp(-(ddparams.devCoeff * effect) / localK); + } // end of if (sstruct.devDens && stg > 0) + if (option1 != 0 && sstruct.survDens) { + float effect = 0.0; + if (sstruct.survStageDens) { // stage-specific density dependence + // NOTE: matrix entries represent effect of ROW on COLUMN + // AND males precede females + float weight = 0.0; + for (int effstg = 0; effstg < nStages; effstg++) { + for (int effsex = 0; effsex < nSexes; effsex++) { + if (dem.repType == 2) { + int rowincr, colincr; + if (effsex == 0) rowincr = 1; else rowincr = 0; + if (sex == 0) colincr = 1; else colincr = 0; + weight = pSpecies->getDDwtSurv(2 * stg + colincr, 2 * effstg + rowincr); + } + else { + weight = pSpecies->getDDwtSurv(stg, effstg); + } + effect += (float)nInds[effstg][effsex] * weight; + } + } + } + else // not stage-specific + effect = (float)totalPop(); + if (localK > 0.0) + surv[stg][sex] *= exp(-(ddparams.survCoeff * effect) / localK); + } // end of if (sstruct.survDens) + } + } + } + + // identify which individuals die or develop + for (int i = 0; i < ninds; i++) { + indStats ind = inds[i]->getStats(); + if ((ind.stage == 0 && option0 < 2) || (ind.stage > 0 && option0 > 0)) { + // condition for processing the stage is met... + if (ind.status < 6) { // not already doomed + double probsurv = surv[ind.stage][ind.sex]; + // does the individual survive? + if (pRandom->Bernoulli(probsurv)) { // survives + // does the individual develop? + double probdev = dev[ind.stage][ind.sex]; + if (ind.stage < nStages - 1) { // not final stage + if (ind.age >= minAge[ind.stage + 1][ind.sex]) { // old enough to enter next stage + if (pRandom->Bernoulli(probdev)) { + inds[i]->developing(); + } + } + } + } + else { // doomed to die + inds[i]->setStatus(8); + } + } + } + } +} + +// Apply survival changes to the population +void Population::survival1(void) +{ + + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + indStats ind = inds[i]->getStats(); + if (ind.status > 5) { // doomed to die + delete inds[i]; + inds[i] = NULL; + nInds[ind.stage][ind.sex]--; + } + else { + if (ind.isDeveloping) { // develops to next stage + nInds[ind.stage][ind.sex]--; + inds[i]->develop(); + nInds[ind.stage + 1][ind.sex]++; + } + } + } + +// remove pointers to dead individuals + clean(); +} + +void Population::ageIncrement(void) { + int ninds = (int)inds.size(); + stageParams sstruct = pSpecies->getStage(); + for (int i = 0; i < ninds; i++) { + inds[i]->ageIncrement(sstruct.maxAge); + } +} + +//--------------------------------------------------------------------------- +// Remove zero pointers to dead or dispersed individuals +void Population::clean(void) +{ + int ninds = (int)inds.size(); + if (ninds > 0) { + // ALTERNATIVE METHOD: AVOIDS SLOW SORTING OF POPULATION + std::vector survivors; // all surviving individuals + for (int i = 0; i < ninds; i++) { + if (inds[i] != NULL) { + survivors.push_back(inds[i]); + } + } + inds.clear(); + inds = survivors; +#if RS_RCPP + shuffle(inds.begin(), inds.end(), pRandom->getRNG()); +#else + +#if !RSDEBUG + // do not randomise individuals in RSDEBUG mode, as the function uses rand() + // and therefore the randomisation will differ between identical runs of RS + shuffle(inds.begin(), inds.end(), pRandom->getRNG()); +#endif // !RSDEBUG + +#endif // RS_RCPP + } +} + +//--------------------------------------------------------------------------- +// Open population file and write header record +bool Population::outPopHeaders(int landNr, bool patchModel) { + + if (landNr == -999) { // close file + if (outPop.is_open()) outPop.close(); + outPop.clear(); + return true; + } + + string name; + simParams sim = paramsSim->getSim(); + envGradParams grad = paramsGrad->getGradient(); + + // NEED TO REPLACE CONDITIONAL COLUMNS BASED ON ATTRIBUTES OF ONE SPECIES TO COVER + // ATTRIBUTES OF *ALL* SPECIES AS DETECTED AT MODEL LEVEL + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(landNr) + "_Pop.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + Int2Str(sim.simulation) + "_Pop.txt"; + } + outPop.open(name.c_str()); + outPop << "Rep\tYear\tRepSeason"; + if (patchModel) outPop << "\tPatchID\tNcells"; + else outPop << "\tx\ty"; + // determine whether environmental data need be written for populations + bool writeEnv = false; + if (grad.gradient) writeEnv = true; + if (paramsStoch->envStoch()) writeEnv = true; + if (writeEnv) outPop << "\tEpsilon\tGradient\tLocal_K"; + outPop << "\tSpecies\tNInd"; + if (dem.stageStruct) { + if (dem.repType == 0) + { + for (int i = 1; i < sstruct.nStages; i++) outPop << "\tNInd_stage" << i; + outPop << "\tNJuvs"; + } + else { + for (int i = 1; i < sstruct.nStages; i++) + outPop << "\tNfemales_stage" << i << "\tNmales_stage" << i; + outPop << "\tNJuvFemales\tNJuvMales"; + } + } + else { + if (dem.repType != 0) outPop << "\tNfemales\tNmales"; + } + outPop << endl; + + return outPop.is_open(); +} + +//--------------------------------------------------------------------------- +// Write record to population file +void Population::outPopulation(int rep, int yr, int gen, float eps, + bool patchModel, bool writeEnv, bool gradK) +{ + Cell* pCell; +// NEED TO REPLACE CONDITIONAL COLUMNS BASED ON ATTRIBUTES OF ONE SPECIES TO COVER +// ATTRIBUTES OF *ALL* SPECIES AS DETECTED AT MODEL LEVEL + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + popStats p; + + outPop << rep << "\t" << yr << "\t" << gen; + if (patchModel) { + outPop << "\t" << pPatch->getPatchNum(); + outPop << "\t" << pPatch->getNCells(); + } + else { + locn loc = pPatch->getCellLocn(0); + outPop << "\t" << loc.x << "\t" << loc.y; + } + if (writeEnv) { + if (pPatch->getPatchNum() == 0) { // matrix + outPop << "\t0\t0\t0"; + } + else { + float k = pPatch->getK(); + float envval = 0.0; + pCell = pPatch->getRandomCell(); + if (pCell != 0) envval = pCell->getEnvVal(); + outPop << "\t" << eps << "\t" << envval << "\t" << k; + } + } + outPop << "\t" << pSpecies->getSpNum(); + if (dem.stageStruct) { + p = getStats(); + outPop << "\t" << p.nNonJuvs; + // non-juvenile stage totals from permanent array + for (int stg = 1; stg < nStages; stg++) { + for (int sex = 0; sex < nSexes; sex++) { + outPop << "\t" << nInds[stg][sex]; + } + } + // juveniles from permanent array + for (int sex = 0; sex < nSexes; sex++) { + outPop << "\t" << nInds[0][sex]; + } + } + else { // non-structured population + outPop << "\t" << totalPop(); + if (dem.repType != 0) + { // sexual model + outPop << "\t" << nInds[1][0] << "\t" << nInds[1][1]; + } + } + outPop << endl; +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Open individuals file and write header record +void Population::outIndsHeaders(int rep, int landNr, bool patchModel) +{ + + if (landNr == -999) { // close file + if (outInds.is_open()) { + outInds.close(); outInds.clear(); + } + return; + } + + string name; + demogrParams dem = pSpecies->getDemogr(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + + "_Land" + Int2Str(landNr) + "_Rep" + Int2Str(rep) + "_Inds.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + Int2Str(sim.simulation) + + "_Rep" + Int2Str(rep) + "_Inds.txt"; + } + outInds.open(name.c_str()); + + outInds << "Rep\tYear\tRepSeason\tSpecies\tIndID\tStatus"; + if (patchModel) outInds << "\tNatal_patch\tPatchID"; + else outInds << "\tNatal_X\tNatal_Y\tX\tY"; + if (dem.repType != 0) outInds << "\tSex"; + if (dem.stageStruct) outInds << "\tAge\tStage"; + if (emig.indVar) { + if (emig.densDep) outInds << "\tD0\tAlpha\tBeta"; + else outInds << "\tEP"; + } + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 1) { // SMS + outInds << "\tDP\tGB\tAlphaDB\tBetaDB"; + } + if (trfr.moveType == 2) { // CRW + outInds << "\tStepLength\tRho"; + } + } + else { // kernel + outInds << "\tMeanDistI"; + if (trfr.twinKern) outInds << "\tMeanDistII\tPKernelI"; + } + } + if (sett.indVar) { + outInds << "\tS0\tAlphaS\tBetaS"; + } + outInds << "\tDistMoved"; +#if RSDEBUG + // ALWAYS WRITE NO. OF STEPS + outInds << "\tNsteps"; +#else + if (trfr.moveModel) outInds << "\tNsteps"; +#endif + outInds << endl; +} + +//--------------------------------------------------------------------------- +// Write records to individuals file +void Population::outIndividual(Landscape* pLandscape, int rep, int yr, int gen, + int patchNum) +{ + //int x, y, p_id; + bool writeInd; + pathSteps steps; + Cell* pCell; + + landParams ppLand = pLandscape->getLandParams(); + demogrParams dem = pSpecies->getDemogr(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + short spNum = pSpecies->getSpNum(); + + int ninds = (int)inds.size(); + + for (int i = 0; i < ninds; i++) { + indStats ind = inds[i]->getStats(); + if (yr == -1) { // write all initialised individuals + writeInd = true; + outInds << rep << "\t" << yr << "\t" << dem.repSeasons - 1; + } + else { + if (dem.stageStruct && gen < 0) { // write status 9 individuals only + if (ind.status == 9) { + writeInd = true; + outInds << rep << "\t" << yr << "\t" << dem.repSeasons - 1; + } + else writeInd = false; + } + else { + writeInd = true; + outInds << rep << "\t" << yr << "\t" << gen; + } + } + if (writeInd) { + outInds << "\t" << spNum << "\t" << inds[i]->getId(); + if (dem.stageStruct) outInds << "\t" << ind.status; + else { // non-structured population + outInds << "\t" << ind.status; + } + pCell = inds[i]->getLocn(1); + locn loc; + if (pCell == 0) loc.x = loc.y = -1; // beyond boundary or in no-data cell + else loc = pCell->getLocn(); + pCell = inds[i]->getLocn(0); + locn natalloc = pCell->getLocn(); + if (ppLand.patchModel) { + outInds << "\t" << inds[i]->getNatalPatch()->getPatchNum(); + if (loc.x == -1) outInds << "\t-1"; + else outInds << "\t" << patchNum; + } + else { // cell-based model + outInds << "\t" << (float)natalloc.x << "\t" << natalloc.y; + outInds << "\t" << (float)loc.x << "\t" << (float)loc.y; + } + if (dem.repType != 0) outInds << "\t" << ind.sex; + if (dem.stageStruct) outInds << "\t" << ind.age << "\t" << ind.stage; + + if (emig.indVar) { + emigTraits e = inds[i]->getEmigTraits(); + if (emig.densDep) { + outInds << "\t" << e.d0 << "\t" << e.alpha << "\t" << e.beta; + } + else { + outInds << "\t" << e.d0; + } + } // end of if (emig.indVar) + + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 1) { // SMS + trfrSMSTraits s = inds[i]->getSMSTraits(); + outInds << "\t" << s.dp << "\t" << s.gb; + outInds << "\t" << s.alphaDB << "\t" << s.betaDB; + } // end of SMS + if (trfr.moveType == 2) { // CRW + trfrCRWTraits c = inds[i]->getCRWTraits(); + outInds << "\t" << c.stepLength << "\t" << c.rho; + } // end of CRW + } + else { // kernel + trfrKernTraits k = inds[i]->getKernTraits(); + if (trfr.twinKern) + { + outInds << "\t" << k.meanDist1 << "\t" << k.meanDist2 << "\t" << k.probKern1; + } + else { + outInds << "\t" << k.meanDist1; + } + } + } + + if (sett.indVar) { + settleTraits s = inds[i]->getSettTraits(); + outInds << "\t" << s.s0 << "\t" << s.alpha << "\t" << s.beta; + } + + // distance moved (metres) + if (loc.x == -1) outInds << "\t-1"; + else { + float d = ppLand.resol * sqrt((float)((natalloc.x - loc.x) * (natalloc.x - loc.x) + + (natalloc.y - loc.y) * (natalloc.y - loc.y))); + outInds << "\t" << d; + } +#if RSDEBUG + // ALWAYS WRITE NO. OF STEPS + steps = inds[i]->getSteps(); + outInds << "\t" << steps.year; +#else + if (trfr.moveModel) { + steps = inds[i]->getSteps(); + outInds << "\t" << steps.year; + } +#endif + outInds << endl; + } // end of writeInd condition + } +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Write records to genetics file +void Population::outGenetics(const int rep, const int year, const int landNr) +{ + + simParams sim = paramsSim->getSim(); + + if (landNr >= 0) { // open file + Genome* pGenome; + genomeData gen = pSpecies->getGenomeData(); + if (gen.trait1Chromosome) { + pGenome = new Genome(pSpecies->getNChromosomes(), pSpecies->getNLoci(0), + pSpecies->isDiploid()); + } + else { + pGenome = new Genome(pSpecies); + } + pGenome->outGenHeaders(rep, landNr, sim.outGenXtab); + delete pGenome; + return; + } + + if (landNr == -999) { // close file + Genome* pGenome = new Genome(); + pGenome->outGenHeaders(rep, landNr, sim.outGenXtab); + delete pGenome; + return; + } + + short spNum = pSpecies->getSpNum(); + short nstages = 1; + if (pSpecies->stageStructured()) { + stageParams sstruct = pSpecies->getStage(); + nstages = sstruct.nStages; + } + + int ninds = (int)inds.size(); + for (int i = 0; i < ninds; i++) { + indStats ind = inds[i]->getStats(); + if (year == 0 || sim.outGenType == 1 + || (sim.outGenType == 0 && ind.stage == 0) + || (sim.outGenType == 2 && ind.stage == nstages - 1)) { + inds[i]->outGenetics(rep, year, spNum, landNr, sim.outGenXtab); + } + } + +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + + diff --git a/src/RScore/Population.h b/src/RScore/Population.h new file mode 100644 index 00000000..fd1fa660 --- /dev/null +++ b/src/RScore/Population.h @@ -0,0 +1,245 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 Population + +Implements the Population class + +There is ONE instance of a Population for each Species within each SubCommunity +(including the matrix). The Population holds a list of all the Individuals in +the Population. + +The matrix Population(s) hold(s) Individuals which are currently in the process +of transfer through the matrix. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Peer G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 22 January 2022 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef PopulationH +#define PopulationH + +#include +#include +using namespace std; + +#include "Parameters.h" +#include "Individual.h" +#include "Species.h" +#include "Landscape.h" +#include "Patch.h" +#include "Cell.h" + +//--------------------------------------------------------------------------- + +struct popStats { + Species *pSpecies; Patch *pPatch; int spNum,nInds,nNonJuvs,nAdults; bool breeding; +}; +struct disperser { + Individual *pInd; Cell *pCell; bool yes; +}; +struct traitsums { // sums of trait genes for dispersal + int ninds[NSEXES]; // no. of individuals + double sumD0[NSEXES]; // sum of maximum emigration probability + double ssqD0[NSEXES]; // sum of squares of maximum emigration probability + double sumAlpha[NSEXES]; // sum of slope of emigration dens-dep reaction norm + double ssqAlpha[NSEXES]; // sum of squares of slope of emigration den-dep reaction norm + double sumBeta[NSEXES]; // sum of inflection point of emigration reaction norm + double ssqBeta[NSEXES]; // sum of squares of inflection point of emigration reaction norm + double sumDist1[NSEXES]; // sum of kernel I mean + double ssqDist1[NSEXES]; // sum of squares of kernel I mean + double sumDist2[NSEXES]; // sum of kernel II mean + double ssqDist2[NSEXES]; // sum of squares of kernel II mean + double sumProp1[NSEXES]; // sum of propn using kernel I + double ssqProp1[NSEXES]; // sum of squares of propn using kernel I + double sumDP[NSEXES]; // sum of SMS directional persistence + double ssqDP[NSEXES]; // sum of squares of SMS directional persistence + double sumGB[NSEXES]; // sum of SMS goal bias + double ssqGB[NSEXES]; // sum of squares of SMS goal bias + double sumAlphaDB[NSEXES]; // sum of SMS dispersal bias decay rate + double ssqAlphaDB[NSEXES]; // sum of squares of SMS dispersal bias decay rate + double sumBetaDB[NSEXES]; // sum of SMS dispersal bias decay infl. pt. + double ssqBetaDB[NSEXES]; // sum of squares of SMS dispersal bias decay infl. pt. + double sumStepL[NSEXES]; // sum of CRW step length + double ssqStepL[NSEXES]; // sum of squares of CRW step length + double sumRho[NSEXES]; // sum of CRW correlation coefficient + double ssqRho[NSEXES]; // sum of squares of CRW correlation coefficient + double sumS0[NSEXES]; // sum of maximum settlement probability + double ssqS0[NSEXES]; // sum of squares of maximum settlement probability + double sumAlphaS[NSEXES]; // sum of slope of settlement den-dep reaction norm + double ssqAlphaS[NSEXES]; // sum of squares of slope of settlement den-dep reaction norm + double sumBetaS[NSEXES]; // sum of inflection point of settlement reaction norm + double ssqBetaS[NSEXES]; // sum of squares of inflection point of settlement reaction norm +}; + +class Population { + +public: + Population(void); // default constructor + Population( // constructor for a Population of a specified size + Species*, // pointer to Species + Patch*, // pointer to Patch + int, // no. of Individuals + int // Landscape resolution + ); + ~Population(void); + traitsums getTraits(Species*); + popStats getStats(void); + Species* getSpecies(void); + int getNInds(void); + int totalPop(void); + int stagePop( // return no. of Individuals in a specified stage + int // stage + ); + void extirpate(void); // Remove all individuals + void reproduction( + const float, // local carrying capacity + const float, // effect of environmental gradient and/or stochasticty + const int // Landscape resolution + ); + // Following reproduction of ALL species, add juveniles to the population + void fledge(void); + void emigration( // Determine which individuals will disperse + float // local carrying capacity + ); + void allEmigrate(void); // All individuals emigrate after patch destruction + // If an individual has been identified as an emigrant, remove it from the Population + disperser extractDisperser( + int // index no. to the Individual in the inds vector + ); + // For an individual identified as being in the matrix population: + // if it is a settler, return its new location and remove it from the current population + // otherwise, leave it in the matrix population for possible reporting before deletion + disperser extractSettler( + int // index no. to the Individual in the inds vector + ); + void recruit( // Add a specified individual to the population + Individual* // pointer to Individual + ); +#if RS_RCPP + int transfer( // Executed for the Population(s) in the matrix only + Landscape*, // pointer to Landscape + short, // landscape change index + short // year + ); + // Determine whether there is a potential mate present in a patch which a potential + // settler has reached + bool matePresent( + Cell*, // pointer to the Cell which the potential settler has reached + short // sex of the required mate (0 = female, 1 = male) + ); +#else + int transfer( // Executed for the Population(s) in the matrix only + Landscape*, // pointer to Landscape + short // landscape change index + ); + // Determine whether there is a potential mate present in a patch which a potential + // settler has reached + bool matePresent( + Cell*, // pointer to the Cell which the potential settler has reached + short // sex of the required mate (0 = female, 1 = male) + ); +#endif // RS_RCPP + // Determine survival and development and record in individual's status code + // Changes are NOT applied to the Population at this stage + void survival0( + float, // local carrying capacity + short, // option0: 0 - stage 0 (juveniles) only + // 1 - all stages + // 2 - stage 1 and above (all non-juveniles) + short // option1: 0 - development only (when survival is annual) + // 1 - development and survival + // 2 - survival only (when survival is annual) + ); + void survival1(void); // Apply survival changes to the population + void ageIncrement(void); + bool outPopHeaders( // Open population file and write header record + int, // Landscape number (-999 to close the file) + bool // TRUE for a patch-based model, FALSE for a cell-based model + ); + void outPopulation( // Write record to population file + int, // replicate + int, // year + int, // generation + float, // epsilon - global stochasticity value + bool, // TRUE for a patch-based model, FALSE for a cell-based model + bool, // TRUE to write environmental data + bool // TRUE if there is a gradient in carrying capacity + ); + + void outIndsHeaders( // Open individuals file and write header record + int, // replicate + int, // Landscape number (-999 to close the file) + bool // TRUE for a patch-based model, FALSE for a cell-based model + ); + void outIndividual( // Write records to individuals file + Landscape*, // pointer to Landscape + int, // replicate + int, // year + int, // generation + int // Patch number + ); + void outGenetics( // Write records to genetics file + const int, // replicate + const int, // year + const int // landscape number + ); + void clean(void); // Remove zero pointers to dead or dispersed individuals + +private: + short nStages; + short nSexes; + Species *pSpecies; // pointer to the species + Patch *pPatch; // pointer to the patch + int nInds[NSTAGES][NSEXES]; // no. of individuals in each stage/sex + + std::vector inds; // all individuals in population except ... + std::vector juvs; // ... juveniles until reproduction of ALL species + // has been completed + +}; + +//--------------------------------------------------------------------------- + +extern paramGrad *paramsGrad; +extern paramStoch *paramsStoch; +extern paramInit *paramsInit; +extern paramSim *paramsSim; +extern RSrandom *pRandom; + +#if RSDEBUG +extern ofstream DEBUGLOG; +#endif + +//--------------------------------------------------------------------------- +#endif + diff --git a/src/RScore/README.md b/src/RScore/README.md new file mode 100644 index 00000000..3056fe0c --- /dev/null +++ b/src/RScore/README.md @@ -0,0 +1,72 @@ +# RangeShifter core code + +This repo contains the core simulation code for RangeShifter v2.0 and is not meant to be compiled or run on its own. + + + +If you are only interested in using RangeShifter, you can ignore this and head to the repo of one of the interfaces: + +- [WIP] RangeShifter GUI + +- [RangeShiftR](https://github.com/RangeShifter/RangeShiftR-pkg) + +- [RangeShifter-batch](https://github.com/RangeShifter/RangeShifter_batch) + +## Usage: git subtree + +In order to ensure that the same version of RangeShifter's core code is used by all three interfaces (RangeShiftR, RangeShifter-batch and the GUI), each interface repo keeps a copy of RScore as a git subtree. In this section, we describe how to use the git subtrees to update the subfolder and copy this repo anew. + +First, in a local clone of one of the interface repos, add a remote named `RScore` pointing to the RScore repo. This will be convenient as a shortcut for git subtree commands. + +```bash +git remote add RScore https://github.com/RangeShifter/RScore.git +``` + +### Pulling new changes + +To update the RScore subfolder with new changes made to the RScore repo, one can use the `git subtree pull` command: + +```bash +git subtree pull --prefix RScore +``` + +Note the path must match the location of the RScore subfolder, and the branch must match the one the subtree was originally added from (by default, this should be `main`). + +e.g. for RangeShifter-batch, use: + +```bash +git subtree pull --prefix src/RScore RScore main +``` + +while for RangeShiftR, use: + +```bash +git subtree pull --prefix RangeShiftR/src/RScore RScore main +``` + +### Pushing new changes to RScore + +We haven't yet found a way to push new changes made in a RScore subfolder back into the RScore repo. This is why we ask that contributions are made directly inside the RScore repo. + +If you know how to do this, please drop us a line! + +Alternatively, if you have already made changes on the subfolder, you could copy its contents into a new branch in RScore, then open a pull request. + +### Switching the subfolder to a new branch + +There is unfortunately to do so. To track a different branch of RScore, one must delete the RScore subfolder (via git) and import the subtree again: + +```bash +git rm src/RScore -r +git commit -m "switching subtree branch" +git subtree add --prefix src/RScore RScore +``` + +## Contributing to RangeShifter core code + +See [CONTRIBUTING](https://github.com/RangeShifter/RScore/blob/main/CONTRIBUTING.md). + +## Maintainers + +- [@JetteReeg](https://github.com/JetteReeg) +- [@TheoPannetier](https://github.com/TheoPannetier) diff --git a/src/RScore/RS_repos.png b/src/RScore/RS_repos.png new file mode 100644 index 00000000..b138a015 Binary files /dev/null and b/src/RScore/RS_repos.png differ diff --git a/src/RScore/RScore_logo.png b/src/RScore/RScore_logo.png new file mode 100644 index 00000000..dfce2675 Binary files /dev/null and b/src/RScore/RScore_logo.png differ diff --git a/src/RScore/RSrandom.cpp b/src/RScore/RSrandom.cpp new file mode 100644 index 00000000..1b9f5234 --- /dev/null +++ b/src/RScore/RSrandom.cpp @@ -0,0 +1,283 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +#include "RSrandom.h" + +//--------------- 2.) New version of RSrandom.cpp + +#if !RS_RCPP + +#if RSDEBUG +#include "Parameters.h" +extern paramSim* paramsSim; +// ofstream RSRANDOMLOG; +#endif + +int RS_random_seed = 0; + +// C'tor +RSrandom::RSrandom() +{ +#if RSDEBUG + // fixed seed + RS_random_seed = 666; +#else + // random seed +#if LINUX_CLUSTER + std::random_device device; + RS_random_seed = device(); // old versions of g++ on Windows return a constant value within a given Windows + // session; in this case better use time stamp +#else + RS_random_seed = std::time(NULL); +#endif +#endif // RSDEBUG + +#if BATCH && RSDEBUG + DEBUGLOG << "RSrandom::RSrandom(): RS_random_seed=" << RS_random_seed << endl; +#endif // RSDEBUG + + // set up Mersenne Twister RNG + gen = new mt19937(RS_random_seed); + + // Set up standard uniform distribution + pRandom01 = new uniform_real_distribution(0.0, 1.0); + // Set up standard normal distribution + pNormal = new normal_distribution(0.0, 1.0); +} + +RSrandom::~RSrandom(void) +{ + delete gen; + if(pRandom01 != 0) + delete pRandom01; + if(pNormal != 0) + delete pNormal; +} + +mt19937 RSrandom::getRNG(void) +{ + return *gen; +} + +double RSrandom::Random(void) +{ + // return random number between 0 and 1 + return pRandom01->operator()(*gen); +} + +int RSrandom::IRandom(int min, int max) +{ + // return random integer in the interval min <= x <= max + uniform_int_distribution unif(min, max); + return unif(*gen); +} + +int RSrandom::Bernoulli(double p) +{ + if (p < 0) throw runtime_error("Bernoulli's p cannot be negative.\n"); + if (p > 1) throw runtime_error("Bernoulli's p cannot be above 1.\n"); + return Random() < p; +} + +double RSrandom::Normal(double mean, double sd) +{ + return mean + sd * pNormal->operator()(*gen); +} + +int RSrandom::Poisson(double mean) +{ + poisson_distribution poiss(mean); + return poiss(*gen); +} + + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +#else // if RS_RCPP + +//--------------- 3.) R package version of RSrandom.cpp + + #if RSDEBUG + #include "Parameters.h" + extern paramSim *paramsSim; + //ofstream RSRANDOMLOG; + #endif + + std::uint32_t RS_random_seed = 0; + + // C'tor + // if parameter seed is negative, a random seed will be generated, else it is used as seed + RSrandom::RSrandom(std::int64_t seed) + { + // get seed + std::vector random_seed(3); + random_seed[0] = 1967593562; + random_seed[1] = 3271254416; + if (seed < 0) { + // random seed + #if RSWIN64 + random_seed[2] = std::time(NULL) + ( seed * (-17) ); + #else + std::random_device device; + random_seed[2] = device(); + #endif + #if BATCH && RSDEBUG + DEBUGLOG << "RSrandom::RSrandom(): Generated random seed = "; + #endif + } + else{ + // fixed seed + random_seed[2] = seed; + #if BATCH && RSDEBUG + DEBUGLOG << "RSrandom::RSrandom(): Use fixed seed = "; + #endif + } + + RS_random_seed = random_seed[2]; + #if BATCH && RSDEBUG + DEBUGLOG << RS_random_seed << endl; + #endif + + // set up Mersenne Twister random number generator with seed sequence + std::seed_seq seq(random_seed.begin(),random_seed.end()); + gen = new mt19937(seq); + + // Set up standard uniform distribution + pRandom01 = new uniform_real_distribution (0.0,1.0); + // Set up standard normal distribution + pNormal = new normal_distribution (0.0,1.0); + } + + RSrandom::~RSrandom(void) { + delete gen; + if (pRandom01 != 0) delete pRandom01; + if (pNormal != 0) delete pNormal; + } + + mt19937 RSrandom::getRNG(void) { + // return random number generator + return *gen; + } + + double RSrandom::Random(void) { + // return random number between 0 and 1 + return pRandom01->operator()(*gen); + } + + int RSrandom::IRandom(int min,int max) { + // return random integer in the interval min <= x <= max + uniform_int_distribution unif(min,max); + return unif(*gen); + } + + int RSrandom::Bernoulli(double p) { + if (p < 0) throw runtime_error("Bernoulli's p cannot be negative.\n"); + if (p > 1) throw runtime_error("Bernoulli's p cannot be above 1.\n"); + return Random() < p; + } + + double RSrandom::Normal(double mean,double sd) { + return mean + sd * pNormal->operator()(*gen); + } + + int RSrandom::Poisson(double mean) { + poisson_distribution poiss(mean); + return poiss(*gen); + } + + + /* ADDITIONAL DISTRIBUTIONS + + // Beta distribution - sample from two gamma distributions + double RSrandom::Beta(double p0,double p1) { + double g0,g1,beta; + if (p0 > 0.0 && p1 > 0.0) { // valid beta parameters + gamma_distribution gamma0(p0,1.0); + gamma_distribution gamma1(p1,1.0); + g0 = gamma0(*gen); + g1 = gamma1(*gen); + beta = g0 / (g0 + g1); + } + else { // return invalid value + beta = -666.0; + } + return beta; + } + + // Gamma distribution + double RSrandom::Gamma(double p0,double p1) { // using shape (=p0) and scale (=p1) + double p2,gamma; + if (p0 > 0.0 && p1 > 0.0) { // valid gamma parameters + p2 = 1.0 / p1; + gamma_distribution gamma0(p0,p2); // using shape/alpha (=p0) and rate/beta (=p2=1/p1) + gamma = gamma0(*gen); + } + else { // return invalid value + gamma = -666.0; + } + return gamma; + } + + // Cauchy distribution + double RSrandom::Cauchy(double loc, double scale) { + double res; + if (scale > 0.0) { // valid scale parameter + cauchy_distribution cauchy(loc,scale); + res = cauchy(*gen); + } + else { // return invalid value + res = -666.0; + } + return res; + } + + + */ + +#endif // RS_RCPP + + +#if RSDEBUG + void testRSrandom() { + + { + // Bernoulli distribution + // Abuse cases + assert_error("Bernoulli's p cannot be negative.\n", []{ + RSrandom rsr; + rsr.Bernoulli(-0.3); + }); + assert_error("Bernoulli's p cannot be above 1.\n", [] { + RSrandom rsr; + rsr.Bernoulli(1.1); + }); + // Use cases + RSrandom rsr; + assert(rsr.Bernoulli(0) == 0); + assert(rsr.Bernoulli(1) == 1); + int bern_trial = rsr.Bernoulli(0.5); + assert(bern_trial == 0 || bern_trial == 1); + } + } +#endif // RSDEBUG +//--------------------------------------------------------------------------- diff --git a/src/RScore/RSrandom.h b/src/RScore/RSrandom.h new file mode 100644 index 00000000..97672456 --- /dev/null +++ b/src/RScore/RSrandom.h @@ -0,0 +1,131 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 RSrandom + +Implements the RSrandom class + +Authors: Steve Palmer, University of Aberdeen + Anne-Kathleen Malchow, Potsdam University + +Last updated: 12 January 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef RSrandomH +#define RSrandomH + +#include +#include +#include +#include "Utils.h" + +using namespace std; + +#if RSDEBUG +extern ofstream DEBUGLOG; +#endif + +#if !RS_RCPP +//--------------- 2.) New version of RSrandom.cpp + #include + #include + #if !LINUX_CLUSTER + #include + #endif + + class RSrandom + { + + public: + RSrandom(void); + ~RSrandom(void); + double Random(void); + int IRandom(int, int); + int Bernoulli(double); + double Normal(double, double); + int Poisson(double); + mt19937 getRNG(void); + + private: + mt19937* gen; + std::uniform_real_distribution<>* pRandom01; + std::normal_distribution<>* pNormal; + }; + + +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + + + +//--------------- 3.) R package version of RSrandom.cpp + + +#else // if RS_RCPP + + + #include + #include + #if RSWIN64 + #include + #endif + + class RSrandom { + + public: + RSrandom(std::int64_t); // if int is negative, a random seed will be generated, else it is used as seed + ~RSrandom(void); + mt19937 getRNG(void); + double Random(void); + int IRandom(int,int); + int Bernoulli(double); + double Normal(double,double); + int Poisson(double); + /* ADDITIONAL DISTRIBUTIONS + double Beta(double,double); + double Gamma(double,double); // !! make sure correct definition is used: using shape and scale (as defined here) OR using shape/alpha and rate/beta (=1/scale) + double Cauchy(double,double); + */ + + private: + mt19937 *gen; + std::uniform_real_distribution<> *pRandom01; + std::normal_distribution<> *pNormal; + }; + + + +#endif // !RS_RCPP + +#if RSDEBUG + void testRSrandom(); +#endif // RSDEBUG + +//--------------------------------------------------------------------------- + +#endif // RSrandomH + + + diff --git a/src/RScore/RandomCheck.cpp b/src/RScore/RandomCheck.cpp new file mode 100644 index 00000000..e18aeaaa --- /dev/null +++ b/src/RScore/RandomCheck.cpp @@ -0,0 +1,93 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +//--------------------------------------------------------------------------- + +#include "RandomCheck.h" +//--------------------------------------------------------------------------- + +ifstream inRandom; +ofstream outRandom; +ofstream outBernoulli; +ofstream outNormal; +ofstream outPoisson; +ofstream outIRandom; + +void randomCheck(void) +{ + +int samplesize,irandMin,irandMax; +double bernMean,normMean,normSD,poisMean; +string name,header; +simParams sim = paramsSim->getSim(); + + name = paramsSim->getDir(1) + "RandomCheck.txt"; + inRandom.open(name.c_str()); + if (!inRandom.is_open()) { + #if !RS_RCPP + cout << endl << "***** Error opening input file RandomCheck.txt" << endl; + #endif + inRandom.clear(); + return; + } + for (int i = 0; i < 7; i++) { + inRandom >> header; + } + inRandom >> samplesize >> bernMean >> normMean >> normSD >> poisMean >> irandMin >> irandMax; + + name = paramsSim->getDir(2) + "Random.txt"; + outRandom.open(name.c_str()); + name = paramsSim->getDir(2) + "Bernoulli.txt"; + outBernoulli.open(name.c_str()); + name = paramsSim->getDir(2) + "Normal.txt"; + outNormal.open(name.c_str()); + name = paramsSim->getDir(2) + "Poisson.txt"; + outPoisson.open(name.c_str()); + name = paramsSim->getDir(2) + "IRandom.txt"; + outIRandom.open(name.c_str()); + + for (int i = 0; i < samplesize; i++) { + outRandom << pRandom->Random() << endl; + outBernoulli << pRandom->Bernoulli(bernMean) << endl; + outNormal << pRandom->Normal(normMean,normSD) << endl; + outPoisson << pRandom->Poisson(poisMean) << endl; + outIRandom << pRandom->IRandom(irandMin,irandMax) << endl; + } + + inRandom.close(); + inRandom.clear(); + outRandom.close(); + outRandom.clear(); + outBernoulli.close(); + outBernoulli.clear(); + outNormal.close(); + outNormal.clear(); + outPoisson.close(); + outPoisson.clear(); + outIRandom.close(); + outIRandom.clear(); + +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- diff --git a/src/RScore/RandomCheck.h b/src/RScore/RandomCheck.h new file mode 100644 index 00000000..b4992d25 --- /dev/null +++ b/src/RScore/RandomCheck.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +//--------------------------------------------------------------------------- + +#ifndef RandomCheckH +#define RandomCheckH + +#include +using namespace std; + +#include "Parameters.h" +#include "RSrandom.h" + +void randomCheck(void); + +extern paramSim *paramsSim; +extern RSrandom *pRandom; + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Species.cpp b/src/RScore/Species.cpp new file mode 100644 index 00000000..0be29ca4 --- /dev/null +++ b/src/RScore/Species.cpp @@ -0,0 +1,1365 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "Species.h" +//--------------------------------------------------------------------------- + +Species::Species(void) +{ + // initialise demographic parameters + repType = 0; nStages = 2; + stageStruct = false; + propMales = 0.5; harem = 1.0; bc = 1.0; lambda = 1.5; probRep = 1.0; + repSeasons = 1; + repInterval = 0; maxAge = 1000; survival = 1; + fecDens = false; fecStageDens = false; + devDens = false; devStageDens = false; + survDens = false; survStageDens = false; + disperseOnLoss = false; + for (int i = 0; i < NSTAGES; i++) { + for (int j = 0; j < NSEXES; j++) { + fec[i][j] = 0.0; dev[i][j] = 0.0; surv[i][j] = 0.0; + minAge[i][j] = 0; + } + } + devCoeff = survCoeff = 1.0; + ddwtFec = ddwtDev = ddwtSurv = 0; ddwtFecDim = ddwtDevDim = ddwtSurvDim = 0; + habK = 0; habDimK = 0; + minRK = 1.0; maxRK = 2.0; + + // initialise genome attributes + nChromosomes = nTraits = 0; + emigTrait[0] = 0; emigTrait[1] = 0; + movtTrait[0] = 0; movtTrait[1] = 0; + settTrait[0] = 0; settTrait[1] = 0; + diploid = true; + neutralMarkers = false; + pleiotropic = false; + trait1Chromosome = true; + probMutn = 0.0001f; + probCrossover = 0.0001f; + alleleSD = mutationSD = 0.1f; + nNLoci = 0; + nLoci = NULL; + traitdata = NULL; + traitnames = NULL; + nTraitNames = 0; + + // initialise emigration parameters + densDepEmig = false; stgDepEmig = false; sexDepEmig = false; indVarEmig = false; + emigStage = 0; + for (int i = 0; i < NSTAGES; i++) { + for (int j = 0; j < NSEXES; j++) { + d0[i][j] = 0.0; alphaEmig[i][j] = 0.0; betaEmig[i][j] = 1.0; + } + } + for (int j = 0; j < NSEXES; j++) { + d0Mean[0][j] = 0.0; alphaMean[0][j] = 0.0; betaMean[0][j] = 1.0; + d0SD[0][j] = 0.0; alphaSD[0][j] = 0.0; betaSD[0][j] = 1.0; + } + d0Scale = alphaScale = betaScale = 0.0; + + // initialise transfer parameters + moveModel = false; stgDepTrfr = false; sexDepTrfr = false; distMort = false; + indVarTrfr = false; + twinKern = false; + habMort = false; + costMap = false; + moveType = 1; + for (int i = 0; i < NSTAGES; i++) { + for (int j = 0; j < NSEXES; j++) { + meanDist1[i][j] = 100.0f; meanDist2[i][j] = 1000.0f; probKern1[i][j] = 0.99f; + } + } + for (int j = 0; j < NSEXES; j++) { + dist1Mean[0][j] = 100.0; dist1SD[0][j] = 10.0; + dist2Mean[0][j] = 1000.0; dist2SD[0][j] = 100.0; + PKern1Mean[0][j] = 0.9f; PKern1SD[0][j] = 0.01f; + stepLgthMean[0][j] = 10.0; stepLgthSD[0][j] = 1.0; + rhoMean[0][j] = 0.9f; rhoSD[0][j] = 0.01f; + dpMean[0][j] = 1.0; dpSD[0][j] = 0.1f; + gbMean[0][j] = 1.0; gbSD[0][j] = 0.1f; + alphaDBMean[0][j] = 1.0; alphaDBSD[0][j] = 0.1f; + betaDBMean[0][j] = 10.0; betaDBSD[0][j] = 1.0; + } + pr = 1; prMethod = 1; memSize = 1; goalType = 0; + dp = 1.0; gb = 1.0; alphaDB = 1.0; betaDB = 100000; + stepMort = 0.0; stepLength = 10.0; rho = 0.9f; + habStepMort = 0; habCost = 0; + fixedMort = 0.0; mortAlpha = 0.0; mortBeta = 1.0; + dist1Scale = dist2Scale = PKern1Scale = stepLScale = rhoScale = 0.0; + dpScale = 0.1f; gbScale = 0.1f; alphaDBScale = 0.1f; betaDBScale = 1.0; + habDimTrfr = 0; + straigtenPath = false; + fullKernel = false; + + // initialise settlement parameters + stgDepSett = false; sexDepSett = false; indVarSett = false; + minSteps = 0; maxSteps = 99999999; + for (int i = 0; i < NSTAGES; i++) { + for (int j = 0; j < NSEXES; j++) { + densDepSett[i][j] = false; wait[i][j] = false; go2nbrLocn[i][j] = false; findMate[i][j] = false; + maxStepsYr[i][j] = 99999999; + s0[i][j] = 1.0; alphaS[i][j] = 0.0; betaS[i][j] = 1.0; + } + } + for (int j = 0; j < NSEXES; j++) { + alphaSMean[0][j] = 0.0; alphaSSD[0][j] = 0.0; + betaSMean[0][j] = 0.0; betaSSD[0][j] = 0.0; + s0Mean[0][j] = 0.0; s0SD[0][j] = 0.0; + } + alphaSScale = 0.0; betaSScale = 0.0; s0Scale = 0.0; + + // initialise attributes + spNum = 0; + +} + +Species::~Species() { + // demographic parameters + if (habK != NULL) deleteHabK(); + if (ddwtFec != 0) deleteDDwtFec(); + if (ddwtDev != 0) deleteDDwtDev(); + if (ddwtSurv != 0) deleteDDwtSurv(); + // transfer parameters + if (habCost != 0 || habStepMort != 0) deleteHabCostMort(); + if (nLoci != NULL) deleteLoci(); + if (traitdata != NULL) deleteTraitData(); + if (traitnames != NULL) deleteTraitNames(); +} + +short Species::getSpNum(void) { return spNum; } + +//--------------------------------------------------------------------------- + +// Demographic functions + +void Species::setDemogr(const demogrParams d) { + if (d.repType >= 0 && d.repType <= 2) repType = d.repType; + if (d.repSeasons >= 1) repSeasons = d.repSeasons; + stageStruct = d.stageStruct; + if (d.propMales > 0.0 && d.propMales < 1.0) propMales = d.propMales; + if (d.harem > 0.0) harem = d.harem; + if (d.bc > 0.0) bc = d.bc; + if (d.lambda > 0.0) lambda = d.lambda; +} + +demogrParams Species::getDemogr(void) { + demogrParams d; + d.repType = repType; + d.repSeasons = repSeasons; + d.stageStruct = stageStruct; + d.propMales = propMales; + d.harem = harem; + d.bc = bc; + d.lambda = lambda; + return d; +} + +short Species::getRepType(void) { return repType; } + +bool Species::stageStructured(void) { return stageStruct; } + +void Species::createHabK(short nhab) { + if (nhab >= 0) { + habDimK = nhab; + if (habK != 0) deleteHabK(); + habK = new float[nhab]; + for (int i = 0; i < nhab; i++) habK[i] = 0.0; + } +} + +void Species::setHabK(short hx, float k) { + if (hx >= 0 && hx < habDimK) { + if (k >= 0.0) habK[hx] = k; + } +} + +float Species::getHabK(short hx) { + float k = 0.0; + if (hx >= 0 && hx < habDimK) k = habK[hx]; + return k; +} + +float Species::getMaxK(void) { + float k = 0.0; + for (int i = 0; i < habDimK; i++) { + if (habK[i] > k) k = habK[i]; + } + return k; +} + +void Species::deleteHabK(void) { + if (habK != 0) { + delete[] habK; habK = 0; + } +} + +void Species::setStage(const stageParams s) { + if (s.nStages > 1) nStages = s.nStages; + if (s.repInterval >= 0) repInterval = s.repInterval; + if (s.maxAge >= 1) maxAge = s.maxAge; + if (s.survival >= 0 && s.survival <= 2) survival = s.survival; + if (s.probRep > 0.0 && s.probRep <= 1.0) probRep = s.probRep; + fecDens = s.fecDens; fecStageDens = s.fecStageDens; + devDens = s.devDens; devStageDens = s.devStageDens; + survDens = s.survDens; survStageDens = s.survStageDens; + disperseOnLoss = s.disperseOnLoss; +} + +stageParams Species::getStage(void) { + stageParams s; + s.nStages = nStages; s.repInterval = repInterval; s.maxAge = maxAge; + s.survival = survival; s.probRep = probRep; + s.fecDens = fecDens; s.fecStageDens = fecStageDens; + s.devDens = devDens; s.devStageDens = devStageDens; + s.survDens = survDens; s.survStageDens = survStageDens; + s.disperseOnLoss = disperseOnLoss; + return s; +} + +void Species::setFec(short stg, short sex, float f) { + // NB fecundity for stage 0 must always be zero + if (stg > 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES && f >= 0) + fec[stg][sex] = f; +} + +float Species::getFec(short stg, short sex) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + return fec[stg][sex]; + else return 0.0; +} + +float Species::getMaxFec(void) { + float maxfec = 0.0; + if (stageStruct) { + for (int stg = 1; stg < NSTAGES; stg++) { + if (fec[stg][0] > maxfec) maxfec = fec[stg][0]; + } + } + else maxfec = lambda; + return maxfec; +} + +void Species::setDev(short stg, short sex, float d) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES && d >= 0) + dev[stg][sex] = d; +} + +float Species::getDev(short stg, short sex) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + return dev[stg][sex]; + else return 0.0; +} + +void Species::setSurv(short stg, short sex, float s) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES && s >= 0) + surv[stg][sex] = s; +} + +float Species::getSurv(short stg, short sex) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + return surv[stg][sex]; + else return 0.0; +} + +void Species::setMinAge(short stg, short sex, int age) { + // NB min age for stages 0 & 1 must always be zero + if (stg > 1 && stg < NSTAGES && sex >= 0 && sex < NSEXES && age >= 0) + minAge[stg][sex] = age; +} + +short Species::getMinAge(short stg, short sex) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + return minAge[stg][sex]; + else return 0; +} + +void Species::setDensDep(float d, float s) { + if (d > 0.0) devCoeff = d; + if (s > 0.0) survCoeff = s; +} + +densDepParams Species::getDensDep(void) { + densDepParams d; + d.devCoeff = devCoeff; d.survCoeff = survCoeff; + return d; +} + +void Species::createDDwtFec(short mSize) { + if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (ddwtFec != 0) deleteDDwtFec(); + ddwtFecDim = mSize; + ddwtFec = new float* [mSize]; + for (int i = 0; i < mSize; i++) { + ddwtFec[i] = new float[mSize]; + for (int j = 0; j < mSize; j++) ddwtFec[i][j] = 1.0; + } + } +} + +void Species::setDDwtFec(short row, short col, float f) { + if (row >= 0 && row < ddwtFecDim && col >= 0 && col < ddwtFecDim) + ddwtFec[row][col] = f; +} + +float Species::getDDwtFec(short row, short col) { + if (row >= 0 && row < ddwtFecDim && col >= 0 && col < ddwtFecDim) + return ddwtFec[row][col]; + else return 0.0; +} + +void Species::deleteDDwtFec(void) { + if (ddwtFec != 0) { + for (int i = 0; i < ddwtFecDim; i++) if (ddwtFec[i] != 0) { + delete[] ddwtFec[i]; + } + delete[] ddwtFec; ddwtFec = 0; + } +} + +void Species::createDDwtDev(short mSize) { + if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (ddwtDev != 0) deleteDDwtDev(); + ddwtDevDim = mSize; + ddwtDev = new float* [mSize]; + for (int i = 0; i < mSize; i++) { + ddwtDev[i] = new float[mSize]; + for (int j = 0; j < mSize; j++) ddwtDev[i][j] = 1.0; + } + } +} + +void Species::setDDwtDev(short row, short col, float f) { + if (row >= 0 && row < ddwtDevDim && col >= 0 && col < ddwtDevDim) + ddwtDev[row][col] = f; +} + +float Species::getDDwtDev(short row, short col) { + if (row >= 0 && row < ddwtDevDim && col >= 0 && col < ddwtDevDim) + return ddwtDev[row][col]; + else return 0.0; +} + +void Species::deleteDDwtDev(void) { + if (ddwtDev != 0) { + for (int i = 0; i < ddwtDevDim; i++) if (ddwtDev[i] != 0) { + delete[] ddwtDev[i]; + } + delete[] ddwtDev; ddwtDev = 0; + } +} + +void Species::createDDwtSurv(short mSize) { + if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (ddwtSurv != 0) deleteDDwtSurv(); + ddwtSurvDim = mSize; + ddwtSurv = new float* [mSize]; + for (int i = 0; i < mSize; i++) { + ddwtSurv[i] = new float[mSize]; + for (int j = 0; j < mSize; j++) ddwtSurv[i][j] = 1.0; + } + } +} + +void Species::setDDwtSurv(short row, short col, float f) { + if (row >= 0 && row < ddwtSurvDim && col >= 0 && col < ddwtSurvDim) + ddwtSurv[row][col] = f; +} + +float Species::getDDwtSurv(short row, short col) { + if (row >= 0 && row < ddwtSurvDim && col >= 0 && col < ddwtSurvDim) + return ddwtSurv[row][col]; + else return 0.0; +} + +void Species::deleteDDwtSurv(void) { + if (ddwtSurv != 0) { + for (int i = 0; i < ddwtSurvDim; i++) if (ddwtSurv[i] != 0) { + delete[] ddwtSurv[i]; + } + delete[] ddwtSurv; ddwtSurv = 0; + } +} + +// Functions to handle min/max R or K (under environmental stochasticity) +void Species::setMinMax(float min, float max) { + if (min >= 0.0 && max > min) { + minRK = min; maxRK = max; + } +} +float Species::getMinMax(short opt) { + if (opt == 0) return minRK; + else return maxRK; +} + +//--------------------------------------------------------------------------- + +// Genome functions + +void Species::setGenomeData(genomeData d) { + diploid = d.diploid; + neutralMarkers = d.neutralMarkers; + trait1Chromosome = d.trait1Chromosome; + if (trait1Chromosome) { + if (d.nLoci > 0) nLoci[0] = d.nLoci; + } + if (d.probMutn >= 0.0 && d.probMutn <= 1.0) probMutn = d.probMutn; + if (d.probCrossover >= 0.0 && d.probCrossover <= 1.0) probCrossover = d.probCrossover; + if (d.alleleSD > 0.0) alleleSD = d.alleleSD; + if (d.mutationSD > 0.0) mutationSD = d.mutationSD; +} + +genomeData Species::getGenomeData(void) { + genomeData d; + d.diploid = diploid; + d.neutralMarkers = neutralMarkers; + d.pleiotropic = pleiotropic; + d.trait1Chromosome = trait1Chromosome; + if (nLoci != NULL) d.nLoci = nLoci[0]; + else d.nLoci = 0; + d.probMutn = probMutn; + d.probCrossover = probCrossover; + d.alleleSD = alleleSD; + d.mutationSD = mutationSD; + return d; +} + +bool Species::isDiploid(void) { return diploid; } + +// Chromosome functions + +void Species::setNChromosomes(int c) { + if (nLoci != NULL) deleteLoci(); + if (c > 0) { + nChromosomes = nNLoci = c; + nLoci = new short[c]; + for (int i = 0; i < nNLoci; i++) nLoci[i] = 0; + } + else nChromosomes = nNLoci = 0; +} + +int Species::getNChromosomes(void) { return nChromosomes; } + +void Species::setNLoci(const short chr, const short nloc) { + if (chr >= 0 && chr < nNLoci) { + if (nloc > 0) nLoci[chr] = nloc; + else nLoci[chr] = 0; + } +} + +int Species::getNLoci(const short chr) { + if (chr >= 0 && chr < nChromosomes) return nLoci[chr]; + else return 0; +} + +void Species::deleteLoci(void) { + if (nLoci != NULL) { delete[] nLoci; nLoci = NULL; } +} + +// Trait functions + +// Set 1:1 mapping of trait to chromosome +void Species::set1ChromPerTrait(const int nloc) { + nChromosomes = nTraits; + if (nLoci != NULL) deleteLoci(); + nLoci = new short[1]; + if (nloc > 0) nLoci[0] = nloc; + else nLoci[0] = 1; +} + +bool Species::has1ChromPerTrait(void) { return trait1Chromosome; } + +// Set trait attributes for the species +void Species::setTraits(void) { + + emigTrait[0] = 0; emigTrait[1] = 0; + movtTrait[0] = 0; movtTrait[1] = 0; + settTrait[0] = 0; settTrait[1] = 0; + nTraits = 0; +#if RSDEBUG + DebugGUI("Species::setTraits(): 0000 nChromosomes=" + Int2Str(nChromosomes) + + " nTraits=" + Int2Str(nTraits) + + " indVarEmig=" + Int2Str((int)indVarEmig) + + " indVarTrfr=" + Int2Str((int)indVarTrfr) + + " indVarSett=" + Int2Str((int)indVarSett) + ); +#endif + + if (indVarEmig) { + if (sexDepEmig) { + if (densDepEmig) nTraits += 6; else nTraits += 2; + } + else { + if (densDepEmig) nTraits += 3; else nTraits += 1; + } + emigTrait[0] = 0; emigTrait[1] = nTraits; + } + + int movttraits = 0; + if (indVarTrfr) { + if (moveModel) { + if (moveType == 1) { // SMS + movttraits = 1; // in contain batch 2 + if (goalType == 2) movttraits += 3; //in contain batch 2 + } + if (moveType == 2) movttraits = 2; + } + else { + if (sexDepTrfr) { + if (twinKern) movttraits = 6; else movttraits = 2; + } + else { + if (twinKern) movttraits = 3; else movttraits = 1; + } + } + movtTrait[0] = nTraits; movtTrait[1] = movttraits; + nTraits += movttraits; + } + + int setttraits = 0; + if (indVarSett) { + if (sexDepSett) setttraits = 6; else setttraits = 3; + settTrait[0] = nTraits; settTrait[1] = setttraits; + nTraits += setttraits; + } + + setTraitNames(); + +#if RSDEBUG + DebugGUI("Species::setTraits(): 9999 nChromosomes=" + Int2Str(nChromosomes) + + " nTraits=" + Int2Str(nTraits)); +#endif + +} + +void Species::setTraitNames(void) { + deleteTraitNames(); + nTraitNames = nTraits; + traitnames = new string[nTraitNames]; + int trait = 0; + if (indVarEmig) { + if (sexDepEmig) { + if (densDepEmig) { + traitnames[trait++] = "d0_F"; + traitnames[trait++] = "d0_M"; + traitnames[trait++] = "alpha_F"; + traitnames[trait++] = "alpha_M"; + traitnames[trait++] = "beta_F"; + traitnames[trait++] = "beta_M"; + } + else { + traitnames[trait++] = "d0_F"; + traitnames[trait++] = "d0_M"; + } + } + else { + traitnames[trait++] = "d0"; + if (densDepEmig) { + traitnames[trait++] = "alpha"; + traitnames[trait++] = "beta"; + } + } + } + + if (indVarTrfr) { + if (moveModel) { + if (moveType == 1) { // SMS + traitnames[trait++] = "DP"; + if (goalType == 2) { + traitnames[trait++] = "GB"; + traitnames[trait++] = "alphaDB"; + traitnames[trait++] = "betaDB"; + } + } + if (moveType == 2) { // CRW + traitnames[trait++] = "stepL"; + traitnames[trait++] = "rho"; + } + } + else { + if (sexDepTrfr) { + if (twinKern) + { + traitnames[trait++] = "meanDistI_F"; + traitnames[trait++] = "meanDistI_M"; + traitnames[trait++] = "meanDistII_F"; + traitnames[trait++] = "meanDistII_M"; + traitnames[trait++] = "probKernI_F"; + traitnames[trait++] = "probKernI_M"; + } + else { + traitnames[trait++] = "meanDistI_F"; + traitnames[trait++] = "meanDistI_M"; + } + } + else { + traitnames[trait++] = "meanDistI"; + if (twinKern) + { + traitnames[trait++] = "meanDistII"; + traitnames[trait++] = "probKernI"; + } + } + } + } + + if (indVarSett) { + if (sexDepSett) { + traitnames[trait++] = "s0_F"; + traitnames[trait++] = "s0_M"; + traitnames[trait++] = "alphaS_F"; + traitnames[trait++] = "alphaS_M"; + traitnames[trait++] = "betaS_F"; + traitnames[trait++] = "betaS_M"; + } + else { + traitnames[trait++] = "s0"; + traitnames[trait++] = "alphaS"; + traitnames[trait++] = "betaS"; + } + } +} + +void Species::deleteTraitNames(void) { + if (traitnames != NULL) { + delete[] traitnames; + traitnames = NULL; + } +} + +string Species::getTraitName(const int trait) { + string name = "not used"; + if (traitnames != NULL) { + if (trait >= 0 && trait < nTraits) { + name = traitnames[trait]; + } + } + return name; +} + +int Species::getNTraits(void) { return nTraits; } + +void Species::setTraitData(const int ntraits) { + deleteTraitData(); + traitdata = new traitData; + if (ntraits > 0) { + traitdata->nTraitMaps = ntraits; + traitdata->traitmaps = new traitMap * [ntraits]; + for (int i = 0; i < ntraits; i++) { + traitdata->traitmaps[i] = new traitMap; + } + } + else { // neutral markers only + traitdata->nTraitMaps = 0; + } + traitdata->neutralloci = new traitMap; + traitdata->neutralloci->nAlleles = 0; +} + +void Species::deleteTraitData(void) { + if (traitdata != NULL) { + for (int i = 0; i < traitdata->nTraitMaps; i++) { + if (traitdata->traitmaps[i]->traitalleles != 0) { + for (int j = 0; j < traitdata->traitmaps[i]->nAlleles; j++) { + delete traitdata->traitmaps[i]->traitalleles[j]; + } + } + delete[] traitdata->traitmaps[i]; + } + deleteNeutralLoci(); + delete traitdata; + traitdata = NULL; + } +} + +int Species::getNTraitMaps(void) { + if (traitdata == NULL) return 0; + else return traitdata->nTraitMaps; +} + +void Species::setTraitMap(const short trait, const short nalleles) { + traitdata->traitmaps[trait]->nAlleles = nalleles; + traitdata->traitmaps[trait]->traitalleles = new traitAllele * [nalleles]; + for (int i = 0; i < nalleles; i++) { + traitdata->traitmaps[trait]->traitalleles[i] = new traitAllele; + } +} + +int Species::getNTraitAlleles(const int trait) { + int nalleles = 0; + if (traitdata != NULL) { + if (trait >= 0 && trait < traitdata->nTraitMaps) { + nalleles = traitdata->traitmaps[trait]->nAlleles; + } + } + return nalleles; +} + +void Species::setTraitAllele(const short trait, const short allele, + const short chr, const short loc) +{ + traitdata->traitmaps[trait]->traitalleles[allele] = new traitAllele; + if (chr >= 0 && loc >= 0) { + traitdata->traitmaps[trait]->traitalleles[allele]->chromo = chr; + traitdata->traitmaps[trait]->traitalleles[allele]->locus = loc; + } + else { + traitdata->traitmaps[trait]->traitalleles[allele]->chromo = 0; + traitdata->traitmaps[trait]->traitalleles[allele]->locus = 0; + } +} + +traitAllele Species::getTraitAllele(const short trait, const short allele) { + traitAllele a; a.chromo = 0; a.locus = 0; + if (traitdata != NULL) { + if (trait >= 0 && trait < traitdata->nTraitMaps) { + if (allele >= 0 && allele < traitdata->traitmaps[trait]->nAlleles) { + a = *traitdata->traitmaps[trait]->traitalleles[allele]; + } + } + } + return a; +} + +// Neutral loci functions + +// Identify neutral loci and determine whether there is pleiotropy +void Species::setNeutralLoci(bool neutralMarkersOnly) { + bool neutral; + int nneutral = 0; + // find minimum of no. of defined / applied traits + int ntraits; + if (traitdata == 0) ntraits = 0; + else ntraits = traitdata->nTraitMaps; + if (ntraits > nTraits) ntraits = nTraits; + +// determine no. of neutral loci + deleteNeutralLoci(); + for (int i = 0; i < nNLoci; i++) { // each chromosome + for (int j = 0; j < nLoci[i]; j++) { // each locus + neutral = true; + for (int t = 0; t < ntraits; t++) { // each trait + for (int a = 0; a < traitdata->traitmaps[t]->nAlleles; a++) { + if (i == traitdata->traitmaps[t]->traitalleles[a]->chromo + && j == traitdata->traitmaps[t]->traitalleles[a]->locus) { + neutral = false; // as locus contributes to a trait + a = 999999; + } + } + if (!neutral) t = 999999; + } + if (neutral) nneutral++; + } + } + + traitdata->neutralloci = new traitMap; + traitdata->neutralloci->nAlleles = nneutral; + if (nneutral < 1) return; + + // record neutral markers + traitdata->neutralloci->traitalleles = new traitAllele * [nneutral]; + nneutral = 0; + for (int i = 0; i < nNLoci; i++) { // each chromosome + for (int j = 0; j < nLoci[i]; j++) { // each locus + neutral = true; + for (int t = 0; t < ntraits; t++) { // each trait + for (int a = 0; a < traitdata->traitmaps[t]->nAlleles; a++) { + if (i == traitdata->traitmaps[t]->traitalleles[a]->chromo + && j == traitdata->traitmaps[t]->traitalleles[a]->locus) { + neutral = false; // as locus contributes to a trait + a = 999999; + } + } + if (!neutral) t = 999999; + } + if (neutral) { + traitdata->neutralloci->traitalleles[nneutral] = new traitAllele; + traitdata->neutralloci->traitalleles[nneutral]->chromo = i; + traitdata->neutralloci->traitalleles[nneutral]->locus = j; + nneutral++; + } + } + } + + pleiotropic = false; + if (neutralMarkersOnly) return; // pleiotropy cannot apply + + // determine whether there is pleiotropy + int chr, loc; + int nloci = 0; // maximum no. of loci on any one chromosome + for (int i = 0; i < nNLoci; i++) { + if (nloci < nLoci[i]) nloci = nLoci[i]; + } + int*** locfreq; + locfreq = new int** [nNLoci]; + for (int i = 0; i < nNLoci; i++) { + locfreq[i] = new int* [nloci]; + for (int j = 0; j < nloci; j++) { + locfreq[i][j] = new int[ntraits]; + for (int t = 0; t < ntraits; t++) locfreq[i][j][t] = 0; + } + } + for (int t = 0; t < ntraits; t++) { // each trait + for (int a = 0; a < traitdata->traitmaps[t]->nAlleles; a++) { + chr = traitdata->traitmaps[t]->traitalleles[a]->chromo; + loc = traitdata->traitmaps[t]->traitalleles[a]->locus; + locfreq[chr][loc][t]++; + } + } + for (int i = 0; i < nNLoci; i++) { + for (int j = 0; j < nloci; j++) { + // remove multiple contributions of a locus to a particular trait + // (i.e. prevent recording of pseudo-pleiotropy) + for (int t = 0; t < ntraits; t++) { + if (locfreq[i][j][t] > 0) locfreq[i][j][t] = 1; + } + // sum at level of chromosome/locus + for (int t = 1; t < ntraits; t++) { + locfreq[i][j][0] += locfreq[i][j][t]; + } + if (locfreq[i][j][0] > 1) pleiotropic = true; + } + } + for (int i = 0; i < nNLoci; i++) { + for (int j = 0; j < nloci; j++) { + delete[] locfreq[i][j]; + } + delete[] locfreq[i]; + } + delete[] locfreq; + +} + +void Species::deleteNeutralLoci(void) { + if (traitdata->neutralloci != NULL) { + for (int i = 0; i < traitdata->neutralloci->nAlleles; i++) { + delete traitdata->neutralloci->traitalleles[i]; + } + delete[] traitdata->neutralloci; + } + traitdata->neutralloci = NULL; +} + +int Species::getNNeutralLoci(void) { + int nn = 0; + if (traitdata != NULL) { + if (traitdata->neutralloci != NULL) { + nn = traitdata->neutralloci->nAlleles; + } + } + return nn; +} + +traitAllele Species::getNeutralAllele(const short allele) { + traitAllele a; a.chromo = 0; a.locus = 0; + if (traitdata != NULL) { + if (allele >= 0 && allele < traitdata->neutralloci->nAlleles) { + a = *traitdata->neutralloci->traitalleles[allele]; + } + } + return a; +} + +//--------------------------------------------------------------------------- + +// Emigration functions + +void Species::setEmig(const emigRules e) { + densDepEmig = e.densDep; stgDepEmig = e.stgDep; sexDepEmig = e.sexDep; + indVarEmig = e.indVar; + if (e.emigStage >= 0) emigStage = e.emigStage; +} + +emigRules Species::getEmig(void) { + emigRules e; + e.densDep = densDepEmig; e.stgDep = stgDepEmig; e.sexDep = sexDepEmig; + e.indVar = indVarEmig; e.emigStage = emigStage; + e.emigTrait[0] = emigTrait[0]; e.emigTrait[1] = emigTrait[1]; + return e; +} + +void Species::setEmigTraits(const short stg, const short sex, const emigTraits e) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (e.d0 >= 0.0 && e.d0 <= 1.0) d0[stg][sex] = e.d0; + alphaEmig[stg][sex] = e.alpha; betaEmig[stg][sex] = e.beta; + } +} + +emigTraits Species::getEmigTraits(short stg, short sex) { + emigTraits e; + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + e.d0 = d0[stg][sex]; e.alpha = alphaEmig[stg][sex]; e.beta = betaEmig[stg][sex]; + } + else { + e.d0 = e.alpha = e.beta = 0.0; + } + return e; +} + +float Species::getEmigD0(short stg, short sex) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + return d0[stg][sex]; + } + else { + return 0.0; + } +} + +void Species::setEmigParams(const short stg, const short sex, const emigParams e) { + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + if (e.d0Mean >= 0.0 && e.d0Mean < 1.0) d0Mean[stg][sex] = e.d0Mean; + if (e.d0SD > 0.0 && e.d0SD < 1.0) d0SD[stg][sex] = e.d0SD; + alphaMean[stg][sex] = e.alphaMean; + if (e.alphaSD > 0.0) alphaSD[stg][sex] = e.alphaSD; + betaMean[stg][sex] = e.betaMean; + if (e.betaSD > 0.0) betaSD[stg][sex] = e.betaSD; + } +} + +emigParams Species::getEmigParams(short stg, short sex) { + emigParams e; + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + e.d0Mean = d0Mean[stg][sex]; e.d0SD = d0SD[stg][sex]; + e.d0Scale = d0Scale; + e.alphaMean = alphaMean[stg][sex]; e.alphaSD = alphaSD[stg][sex]; + e.alphaScale = alphaScale; + e.betaMean = betaMean[stg][sex]; e.betaSD = betaSD[stg][sex]; + e.betaScale = betaScale; + } + else { + e.d0Mean = e.alphaMean = e.betaMean = e.d0SD = e.alphaSD = e.betaSD = 0.0; + e.d0Scale = d0Scale; + e.alphaScale = alphaScale; + e.betaScale = betaScale; + } + return e; +} + +void Species::setEmigScales(const emigScales s) { + if (s.d0Scale >= 0.0 && s.d0Scale < 1.0) d0Scale = s.d0Scale; + if (s.alphaScale >= 0.0) alphaScale = s.alphaScale; + if (s.betaScale >= 0.0) betaScale = s.betaScale; +} + +emigScales Species::getEmigScales(void) { + emigScales s; + s.d0Scale = d0Scale; s.alphaScale = alphaScale; s.betaScale = betaScale; + return s; +} + +//--------------------------------------------------------------------------- + +// Transfer functions + +void Species::setTrfr(const trfrRules t) { + moveModel = t.moveModel; stgDepTrfr = t.stgDep; sexDepTrfr = t.sexDep; + distMort = t.distMort; indVarTrfr = t.indVar; + twinKern = t.twinKern; + habMort = t.habMort; + moveType = t.moveType; costMap = t.costMap; +} + +trfrRules Species::getTrfr(void) { + trfrRules t; + t.moveModel = moveModel; t.stgDep = stgDepTrfr; t.sexDep = sexDepTrfr; + t.distMort = distMort; t.indVar = indVarTrfr; + t.twinKern = twinKern; + t.habMort = habMort; + t.moveType = moveType; t.costMap = costMap; + t.movtTrait[0] = movtTrait[0]; t.movtTrait[1] = movtTrait[1]; + return t; +} + +void Species::setFullKernel(bool k) { + fullKernel = k; +} + +bool Species::useFullKernel(void) { return fullKernel; } + +void Species::setKernTraits(const short stg, const short sex, + const trfrKernTraits k, const int resol) +{ + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (k.meanDist1 > 0.0 && k.meanDist1 >= (float)resol) meanDist1[stg][sex] = k.meanDist1; + if (k.meanDist2 >= (float)resol) meanDist2[stg][sex] = k.meanDist2; + if (k.probKern1 > 0.0 && k.probKern1 < 1.0) probKern1[stg][sex] = k.probKern1; + } +} + +trfrKernTraits Species::getKernTraits(short stg, short sex) { + trfrKernTraits k; + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + k.meanDist1 = meanDist1[stg][sex]; + k.meanDist2 = meanDist2[stg][sex]; + k.probKern1 = probKern1[stg][sex]; + } + else { + k.meanDist1 = 0.0; k.meanDist2 = 0.0; k.probKern1 = 1.0; + } + return k; +} + +void Species::setMortParams(const trfrMortParams m) { + if (m.fixedMort >= 0.0 && m.fixedMort < 1.0) fixedMort = m.fixedMort; + mortAlpha = m.mortAlpha; + mortBeta = m.mortBeta; +} + +trfrMortParams Species::getMortParams(void) { + trfrMortParams m; + m.fixedMort = fixedMort; m.mortAlpha = mortAlpha; m.mortBeta = mortBeta; + return m; +} + +void Species::setMovtTraits(const trfrMovtTraits m) { + if (m.pr >= 1) pr = m.pr; + if (m.prMethod >= 1 && m.prMethod <= 3) prMethod = m.prMethod; + if (m.memSize >= 1 && m.memSize <= 14) memSize = m.memSize; + if (m.goalType >= 0 && m.goalType <= 2) goalType = m.goalType; + if (m.dp >= 1.0) dp = m.dp; + if (m.gb >= 1.0) gb = m.gb; + if (m.alphaDB > 0.0) alphaDB = m.alphaDB; + if (m.betaDB > 0) betaDB = m.betaDB; + if (m.stepMort >= 0.0 && m.stepMort < 1.0) stepMort = m.stepMort; + if (m.stepLength > 0.0) stepLength = m.stepLength; + if (m.rho > 0.0 && m.rho < 1.0) rho = m.rho; + straigtenPath = m.straigtenPath; +} + +trfrMovtTraits Species::getMovtTraits(void) { + trfrMovtTraits m; + m.pr = pr; m.prMethod = prMethod; m.memSize = memSize; m.goalType = goalType; + m.dp = dp; m.gb = gb; m.alphaDB = alphaDB; m.betaDB = betaDB; + m.stepMort = stepMort; m.stepLength = stepLength; m.rho = rho; + return m; +} + +trfrCRWTraits Species::getCRWTraits(void) { + trfrCRWTraits m; + m.stepMort = stepMort; m.stepLength = stepLength; m.rho = rho; + m.straigtenPath = straigtenPath; + return m; +} + +trfrSMSTraits Species::getSMSTraits(void) { + trfrSMSTraits m; + m.pr = pr; m.prMethod = prMethod; m.memSize = memSize; m.goalType = goalType; + m.dp = dp; m.gb = gb; m.alphaDB = alphaDB; m.betaDB = betaDB; m.stepMort = stepMort; + m.straigtenPath = straigtenPath; + return m; +} + +void Species::setKernParams(const short stg, const short sex, + const trfrKernParams k, const double resol) +{ + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + if (k.dist1Mean > 0.0 && k.dist1Mean >= resol && k.dist1SD > 0.0) { + dist1Mean[stg][sex] = k.dist1Mean; dist1SD[stg][sex] = k.dist1SD; + } + if (k.dist2Mean > 0.0 && k.dist2Mean >= resol && k.dist2SD > 0.0) { + dist2Mean[stg][sex] = k.dist2Mean; dist2SD[stg][sex] = k.dist2SD; + } + if (k.PKern1Mean > 0.0 && k.PKern1Mean < 1.0 && k.PKern1SD > 0.0 && k.PKern1SD < 1.0) { + PKern1Mean[stg][sex] = k.PKern1Mean; PKern1SD[stg][sex] = k.PKern1SD; + } + } +} + +trfrKernParams Species::getKernParams(short stg, short sex) { + trfrKernParams k; + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + k.dist1Mean = dist1Mean[stg][sex]; k.dist1SD = dist1SD[stg][sex]; + k.dist2Mean = dist2Mean[stg][sex]; k.dist2SD = dist2SD[stg][sex]; + k.PKern1Mean = PKern1Mean[stg][sex]; k.PKern1SD = PKern1SD[stg][sex]; + k.dist1Scale = dist1Scale; k.dist2Scale = dist2Scale; k.PKern1Scale = PKern1Scale; + } + else { + k.dist1Mean = 100000.0; k.dist1SD = 0.001f; k.dist1Scale = 1.0; + k.dist2Mean = 100000.0; k.dist2SD = 0.001f; k.dist2Scale = 1.0; + k.PKern1Mean = 0.5; k.PKern1SD = 0.1f; k.PKern1Scale = 0.1f; + } + return k; +} + +void Species::setSMSParams(const short stg, const short sex, const trfrSMSParams s) { + if (stg >= 0 && stg < 1 && sex >= 0 && sex < 1) // implemented for stage 0 & sex 0 only + { + if (s.dpMean >= 1.0 && s.dpSD > 0.0) { + dpMean[stg][sex] = s.dpMean; dpSD[stg][sex] = s.dpSD; + } + if (s.gbMean >= 1.0 && s.gbSD > 0.0) { + gbMean[stg][sex] = s.gbMean; gbSD[stg][sex] = s.gbSD; + } + if (s.alphaDBMean > 0.0 && s.alphaDBSD > 0.0) { + alphaDBMean[stg][sex] = s.alphaDBMean; alphaDBSD[stg][sex] = s.alphaDBSD; + } + if (s.betaDBMean >= 1.0 && s.betaDBSD > 0.0) { + betaDBMean[stg][sex] = s.betaDBMean; betaDBSD[stg][sex] = s.betaDBSD; + } + } +} + +trfrSMSParams Species::getSMSParams(short stg, short sex) { + trfrSMSParams s; + if (stg >= 0 && stg < 1 && sex >= 0 && sex < 1) // implemented for stage 0 & sex 0 only + { + s.dpMean = dpMean[stg][sex]; s.dpSD = dpSD[stg][sex]; + s.gbMean = gbMean[stg][sex]; s.gbSD = gbSD[stg][sex]; + s.alphaDBMean = alphaDBMean[stg][sex]; s.alphaDBSD = alphaDBSD[stg][sex]; + s.betaDBMean = betaDBMean[stg][sex]; s.betaDBSD = betaDBSD[stg][sex]; + s.dpScale = dpScale; s.gbScale = gbScale; + s.alphaDBScale = alphaDBScale; s.betaDBScale = betaDBScale; + } + else { + s.dpMean = 1.0; s.dpSD = 0.1f; s.dpScale = 0.1f; + s.gbMean = 1.0; s.gbSD = 0.1f; s.gbScale = 0.1f; + s.alphaDBMean = 1.0; s.alphaDBSD = 0.1f; s.alphaDBScale = 0.1f; + s.betaDBMean = 10.0; s.betaDBSD = 1.0; s.betaDBScale = 1.0; + } + return s; +} + +void Species::setCRWParams(const short stg, const short sex, const trfrCRWParams m) { + if (stg >= 0 && stg < 1 && sex >= 0 && sex < 1) // implemented for stage 0 & sex 0 only + { + if (m.stepLgthMean > 0.0 && m.stepLgthSD > 0.0) { + stepLgthMean[stg][sex] = m.stepLgthMean; stepLgthSD[stg][sex] = m.stepLgthSD; + } + if (m.rhoMean > 0.0 && m.rhoMean < 1.0 && m.rhoSD > 0.0 && m.rhoSD < 1.0) { + rhoMean[stg][sex] = m.rhoMean; rhoSD[stg][sex] = m.rhoSD; + } + } +} + +trfrCRWParams Species::getCRWParams(short stg, short sex) { + trfrCRWParams m; + if (stg >= 0 && stg < 1 && sex >= 0 && sex < 1) // implemented for stage 0 & sex 0 only + { + m.stepLgthMean = stepLgthMean[stg][sex]; m.stepLgthSD = stepLgthSD[stg][sex]; + m.rhoMean = rhoMean[stg][sex]; m.rhoSD = rhoSD[stg][sex]; + m.stepLScale = stepLScale; m.rhoScale = rhoScale; + } + else { + m.stepLgthMean = 1.0; m.stepLgthSD = 0.1f; m.stepLScale = 0.1f; + m.rhoMean = 0.5; m.rhoSD = 0.1f; m.rhoScale = 0.1f; + } + return m; +} + +void Species::setTrfrScales(const trfrScales s) { + if (s.dist1Scale >= 0.0) dist1Scale = s.dist1Scale; + if (s.dist2Scale >= 0.0) dist2Scale = s.dist2Scale; + if (s.PKern1Scale > 0.0 && s.PKern1Scale < 1.0) PKern1Scale = s.PKern1Scale; + if (s.dpScale > 0.0) dpScale = s.dpScale; + if (s.gbScale > 0.0) gbScale = s.gbScale; + if (s.alphaDBScale > 0.0) alphaDBScale = s.alphaDBScale; + if (s.betaDBScale > 0.0) betaDBScale = s.betaDBScale; + if (s.stepLScale > 0.0) stepLScale = s.stepLScale; + if (s.rhoScale > 0.0 && s.rhoScale < 1.0) rhoScale = s.rhoScale; +} + +trfrScales Species::getTrfrScales(void) { + trfrScales s; + s.dist1Scale = dist1Scale; s.dist2Scale = dist2Scale; s.PKern1Scale = PKern1Scale; + s.dpScale = dpScale; s.gbScale = gbScale; + s.alphaDBScale = alphaDBScale; s.betaDBScale = betaDBScale; + s.stepLScale = stepLScale; s.rhoScale = rhoScale; + return s; +} + +short Species::getMovtHabDim() { return habDimTrfr; } + +void Species::createHabCostMort(short nhab) { + if (nhab >= 0) { + habDimTrfr = nhab; + if (habCost != 0 || habStepMort != 0) deleteHabCostMort(); + habCost = new int[nhab]; + habStepMort = new double[nhab]; + for (int i = 0; i < nhab; i++) { + habCost[i] = 1; habStepMort[i] = 0.0; + } + } +} + +void Species::setHabCost(short hab, int cost) { + if (hab >= 0 && hab < habDimTrfr) { + if (cost >= 1) habCost[hab] = cost; + } +} + +void Species::setHabMort(short hab, double mort) { + if (hab >= 0 && hab < habDimTrfr) { + if (mort >= 0.0 && mort < 1.0) habStepMort[hab] = mort; + } +} + +int Species::getHabCost(short hab) { + int cost = 0; + if (hab >= 0 && hab < habDimTrfr) cost = habCost[hab]; + return cost; +} + +double Species::getHabMort(short hab) { + double pmort = 0.0; + if (hab >= 0 && hab < habDimTrfr) pmort = habStepMort[hab]; + return pmort; +} + +void Species::deleteHabCostMort(void) { + if (habCost != 0) { + delete[] habCost; habCost = 0; + } + if (habStepMort != 0) { + delete[] habStepMort; habStepMort = 0; + } +} + +//--------------------------------------------------------------------------- + +// Settlement functions + +void Species::setSettle(const settleType s) { + stgDepSett = s.stgDep; sexDepSett = s.sexDep; indVarSett = s.indVar; +} + +settleType Species::getSettle(void) { + settleType s; + s.stgDep = stgDepSett; s.sexDep = sexDepSett; s.indVar = indVarSett; + s.settTrait[0] = settTrait[0]; s.settTrait[1] = settTrait[1]; + return s; +} + +void Species::setSettRules(const short stg, const short sex, const settleRules s) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + densDepSett[stg][sex] = s.densDep; wait[stg][sex] = s.wait; + go2nbrLocn[stg][sex] = s.go2nbrLocn; findMate[stg][sex] = s.findMate; + } +} + +settleRules Species::getSettRules(short stg, short sex) { + settleRules s; + s.densDep = false; + s.findMate = false; + s.go2nbrLocn = false; + s.wait = false; + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + s.densDep = densDepSett[stg][sex]; s.wait = wait[stg][sex]; + s.go2nbrLocn = go2nbrLocn[stg][sex]; s.findMate = findMate[stg][sex]; + } + return s; +} + +void Species::setSteps(const short stg, const short sex, const settleSteps s) { + if (stg == 0 && sex == 0) { + if (s.minSteps >= 0) minSteps = s.minSteps; + else minSteps = 0; + if (s.maxSteps >= 1) maxSteps = s.maxSteps; + else maxSteps = 99999999; + } + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (s.maxStepsYr >= 1) maxStepsYr[stg][sex] = s.maxStepsYr; + else maxStepsYr[stg][sex] = 99999999; + } +} + +settleSteps Species::getSteps(short stg, short sex) { + settleSteps s; + s.minSteps = minSteps; + s.maxSteps = maxSteps; + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) s.maxStepsYr = maxStepsYr[stg][sex]; + else s.maxStepsYr = 99999999; + return s; +} + +void Species::setSettTraits(const short stg, const short sex, const settleTraits dd) { + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (dd.s0 > 0.0 && dd.s0 <= 1.0) s0[stg][sex] = dd.s0; + alphaS[stg][sex] = dd.alpha; betaS[stg][sex] = dd.beta; + } +} + +settleTraits Species::getSettTraits(short stg, short sex) { + settleTraits dd; + if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + dd.s0 = s0[stg][sex]; dd.alpha = alphaS[stg][sex]; dd.beta = betaS[stg][sex]; + } + else { dd.s0 = 1.0; dd.alpha = dd.beta = 0.0; } + return dd; +} + +void Species::setSettParams(const short stg, const short sex, const settParams s) { + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + if (s.s0Mean >= 0.0 && s.s0Mean < 1.0) s0Mean[stg][sex] = s.s0Mean; + if (s.s0SD > 0.0 && s.s0SD < 1.0) s0SD[stg][sex] = s.s0SD; + alphaSMean[stg][sex] = s.alphaSMean; + if (s.alphaSSD > 0.0) alphaSSD[stg][sex] = s.alphaSSD; + betaSMean[stg][sex] = s.betaSMean; + if (s.betaSSD > 0.0) betaSSD[stg][sex] = s.betaSSD; + if (sex == 0) { + if (s.s0Scale > 0.0 && s.s0Scale < 1.0) s0Scale = s.s0Scale; + if (s.alphaSScale > 0.0) alphaSScale = s.alphaSScale; + if (s.betaSScale > 0.0) betaSScale = s.betaSScale; + } + } +} + +settParams Species::getSettParams(short stg, short sex) { + settParams s; + if (stg >= 0 && stg < 1 && sex >= 0 && sex < NSEXES) // implemented for stage 0 only + { + s.s0Mean = s0Mean[stg][sex]; s.s0SD = s0SD[stg][sex]; + s.alphaSMean = alphaSMean[stg][sex]; s.alphaSSD = alphaSSD[stg][sex]; + s.betaSMean = betaSMean[stg][sex]; s.betaSSD = betaSSD[stg][sex]; + } + else { + s.s0Mean = s.alphaSMean = s.betaSMean = s.s0SD = s.alphaSSD = s.betaSSD = 0.0; + } + s.s0Scale = s0Scale; + s.alphaSScale = alphaSScale; + s.betaSScale = betaSScale; + return s; +} + +void Species::setSettScales(const settScales s) { + if (s.s0Scale >= 0.0 && s.s0Scale < 1.0) s0Scale = s.s0Scale; + if (s.alphaSScale >= 0.0) alphaSScale = s.alphaSScale; + if (s.betaSScale >= 0.0) betaSScale = s.betaSScale; +} + +settScales Species::getSettScales(void) { + settScales s; + s.s0Scale = s0Scale; s.alphaSScale = alphaSScale; s.betaSScale = betaSScale; + return s; +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + diff --git a/src/RScore/Species.h b/src/RScore/Species.h new file mode 100644 index 00000000..3a004a26 --- /dev/null +++ b/src/RScore/Species.h @@ -0,0 +1,748 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + /*------------------------------------------------------------------------------ + + RangeShifter v2.0 Species + + Implements the Species class + + There is ONE instance of a Species for each species within the Community + AND THIS IS CURRENTLY LIMITED TO A SINGLE SPECIES. + The class holds all the demographic and dispersal parameters of the species. + + For full details of RangeShifter, please see: + Bocedi G., Palmer S.C.F., Pe’er G., Heikkinen R.K., Matsinos Y.G., Watts K. + and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial + eco-evolutionary dynamics and species’ responses to environmental changes. + Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + + Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + + Last updated: 28 July 2021 by Greta Bocedi + + ------------------------------------------------------------------------------*/ + +#ifndef SpeciesH +#define SpeciesH + +#include "Parameters.h" + +// structures for demographic parameters + +struct demogrParams { + short repType; + short repSeasons; + float propMales; float harem; float bc; float lambda; + bool stageStruct; +}; +struct stageParams { + short nStages; short repInterval; short maxAge; short survival; + float probRep; + bool fecDens; bool fecStageDens; bool devDens; bool devStageDens; + bool survDens; bool survStageDens; bool disperseOnLoss; +}; +struct densDepParams { + float devCoeff; float survCoeff; +}; + +// structures for genetics + +struct genomeData { + int nLoci; + bool diploid; bool neutralMarkers; bool pleiotropic; bool trait1Chromosome; + double probMutn, probCrossover, alleleSD, mutationSD; +}; + +struct traitAllele { + short chromo; short locus; +}; + +struct traitMap { + short nAlleles; + traitAllele** traitalleles; +}; + +struct traitData { + short nTraitMaps; + traitMap** traitmaps; + traitMap* neutralloci; +}; + +// structures for emigration parameters + +struct emigRules { + bool densDep; bool stgDep; bool sexDep; bool indVar; + short emigStage; + short emigTrait[2]; +}; +struct emigTraits { + float d0; float alpha; float beta; +}; +struct emigParams { + double d0Mean; double d0SD; double d0Scale; + double alphaMean; double alphaSD; double alphaScale; + double betaMean; double betaSD; double betaScale; +}; +struct emigScales { + double d0Scale; double alphaScale; double betaScale; +}; + +// structures for transfer parameters + +struct trfrRules { + bool moveModel; bool stgDep; bool sexDep; + bool distMort; bool indVar; + bool twinKern; + bool habMort; + short moveType; bool costMap; + short movtTrait[2]; +}; +struct trfrKernTraits { + float meanDist1; float meanDist2; float probKern1; +}; +struct trfrMortParams { + float fixedMort; float mortAlpha; float mortBeta; +}; +struct trfrMovtTraits { + short pr; short prMethod; short memSize; short goalType; + float dp; float gb; float alphaDB; int betaDB; + float stepMort; float stepLength; float rho; + bool straigtenPath; +}; +struct trfrCRWTraits { + float stepMort; float stepLength; float rho; bool straigtenPath; +}; +struct trfrSMSTraits { + short pr; short prMethod; short memSize; short goalType; + float dp; float gb; float alphaDB; int betaDB; float stepMort; + bool straigtenPath; +}; +struct trfrKernParams { + double dist1Mean; double dist1SD; double dist1Scale; + double dist2Mean; double dist2SD; double dist2Scale; + double PKern1Mean; double PKern1SD; double PKern1Scale; +}; +struct trfrSMSParams { + double dpMean; double dpSD; double gbMean; double gbSD; + double alphaDBMean; double alphaDBSD; double betaDBMean; double betaDBSD; + double dpScale; double gbScale; double alphaDBScale; double betaDBScale; +}; +struct trfrCRWParams { + double stepLgthMean; double stepLgthSD; double stepLScale; + double rhoMean; double rhoSD; double rhoScale; +}; +struct trfrScales { + float dist1Scale; float dist2Scale; float PKern1Scale; + float dpScale; float gbScale; float alphaDBScale; float betaDBScale; + float stepLScale; float rhoScale; +}; + +// structures for settlement parameters + +struct settleType { + bool stgDep; bool sexDep; bool indVar; + short settTrait[2]; +}; +struct settleRules { + bool densDep; bool wait; bool go2nbrLocn; bool findMate; +}; +struct settleSteps { + int minSteps; int maxSteps; int maxStepsYr; +}; +struct settleTraits { + float s0; float alpha; float beta; +}; +struct settParams { + double s0Mean; double s0SD; double s0Scale; + double alphaSMean; double alphaSSD; double alphaSScale; + double betaSMean; double betaSSD; double betaSScale; +}; +struct settScales { + double s0Scale; double alphaSScale; double betaSScale; +}; + + +//--------------------------------------------------------------------------- + +class Species { + +public: + Species(void); + ~Species(void); + short getSpNum(void); + + // demographic parameter functions + + void createHabK( // Create habitat carrying capacity table + short // no. of habitats + ); + void setHabK( + short, // habitat index no. (NB may differ from habitat no. supplied by user) + float // carrying capacity (inds/cell) + ); + float getHabK( + short // habitat index no. (NB may differ from habitat no. supplied by user) + ); + float getMaxK(void); // return highest carrying capacity over all habitats + void deleteHabK(void); // Delete habitat carrying capacity table + void setStage( // Set stage structure parameters + const stageParams // structure holding stage structure parameters + ); + stageParams getStage(void); // Get stage structure parameters + void setDemogr( // Set general demographic parameters + const demogrParams // structure holding general demographic parameters + ); + demogrParams getDemogr(void); // Get general demographic parameters + short getRepType(void); + bool stageStructured(void); + void setDensDep( // Set demographic density dependence coefficients + float, // development coefficient + float // survival coefficient + ); + densDepParams getDensDep(void); // Get development and survival coefficients + + void setFec( // Set fecundity + short, // stage (must be > 0) + short, // sex + float // fecundity + ); + float getFec( // Get fecundity + short, // stage + short // sex + ); + void setDev( // Set development probability + short, // stage + short, // sex + float // development probability + ); + float getDev( // Get development probability + short, // stage + short // sex + ); + void setSurv( // Set survival probability + short, // stage + short, // sex + float // survival probability + ); + float getSurv( // Get survival probability + short, // stage + short // sex + ); + + float getMaxFec(void); // Get highest fecundity of any stage + void setMinAge( // Set minimum age + short, // stage + short, // sex + int // minimum age (years) (must be zero for stages 0 and 1) + ); + short getMinAge( // Get minimum age + short, // stage + short // sex + ); + void createDDwtFec( // Create fecundity weights matrix + short // matrix dimension - no. of stages * no. of sexes + ); + void setDDwtFec( // Set fecundity weights matrix element + short, // row + short, // column + float // weight + ); + float getDDwtFec( // Get fecundity weights matrix element + short, // row + short // column + ); + void deleteDDwtFec(void); // Delete fecundity weights matrix + void createDDwtDev( // Create development weights matrix + short // matrix dimension - no. of stages * no. of sexes + ); + void setDDwtDev( // Set development weights matrix element + short, // row + short, // column + float // weight + ); + float getDDwtDev( // Get development weights matrix element + short, // row + short // column + ); + void deleteDDwtDev(void); // Delete development weights matrix + void createDDwtSurv( // Create survival weights matrix + short // matrix dimension - no. of stages * no. of sexes + ); + void setDDwtSurv( // Set survival weights matrix element + short, // row + short, // column + float // weight + ); + float getDDwtSurv( // Get survival weights matrix element + short, // row + short // column + ); + void deleteDDwtSurv(void); // Delete survival weights matrix + // Functions to handle min/max R or K (under environmental stochasticity) + void setMinMax( // Set min and max values + float, // min + float // max + ); + float getMinMax( // Get min/max value + short // option: 0 = return minimum, otherwise = return maximum + ); + + + // genome functions + + void setGenomeData(genomeData); + genomeData getGenomeData(void); + bool isDiploid(void); + void setNChromosomes( // Set no. of chromosomes + int // no. of chromosomes + ); + int getNChromosomes(void); + void setNLoci( + const short, // chromosome no. + const short // locus no. + ); + int getNLoci( + const short // chromosome no. + ); + void deleteLoci(void); + void set1ChromPerTrait( // Set 1:1 mapping of trait to chromosome + const int // no. of loci on each chromosome + ); + bool has1ChromPerTrait(void); + void setTraits(void); // Set trait attributes for the species + void setTraitNames(void); + void deleteTraitNames(void); + string getTraitName( + const int // trait no. + ); + int getNTraits(void); + void setTraitData( + const int // no. of traits + ); + void deleteTraitData(void); + int getNTraitMaps(void); + void setTraitMap( + const short, // trait no. + const short // no. of alleles + ); + int getNTraitAlleles( + const int // trait no. + ); + void setTraitAllele( + const short, // trait no. + const short, // trait allele no. + const short, // chromosome no. + const short // chromosome locus no. + ); + traitAllele getTraitAllele( + const short, // trait no. + const short // trait allele no. + ); + void setNeutralLoci(bool); + void deleteNeutralLoci(void); + int getNNeutralLoci(void); + traitAllele getNeutralAllele( + const short // allele no. + ); + + // emigration parameter functions + + void setEmig( // Set emigration rules + const emigRules // structure holding emigration rules + ); + emigRules getEmig(void); // Get emigration rules + void setEmigTraits( // Set emigration trait parameters + const short, // stage + const short, // sex + const emigTraits // structure holding emigration trait parameters + ); + emigTraits getEmigTraits( // Get emigration trait parameters + short, // stage + short // sex + ); + float getEmigD0( // Get (maximum) emigration probability + short, // stage + short // sex + ); + void setEmigParams( // Set emigration initialisation parameters + const short, // stage (NB implemented for stage 0 only) + const short, // sex + const emigParams // structure holding parameters + ); + emigParams getEmigParams( // Get emigration initialisation parameters + short, // stage (NB implemented for stage 0 only) + short // sex + ); + void setEmigScales( // Set emigration mutation parameters + const emigScales // structure holding emigration mutation parameters + ); + emigScales getEmigScales(void); // Get emigration mutation parameters + + // transfer parameter functions + + void setTrfr( // Set transfer rules + const trfrRules // structure holding transfer rules + ); + trfrRules getTrfr(void); // Get transfer rules + void setFullKernel( // Set fullKernel condition + bool // fullKernel value + ); + bool useFullKernel(void); + void setKernTraits( // Set transfer by kernel parameters + const short, // stage + const short, // sex + const trfrKernTraits, // structure holding transfer by kernel parameters + const int // Landscape resolution + ); + trfrKernTraits getKernTraits( // Get transfer by kernel parameters + short, // stage + short // sex + ); + void setMortParams( // Set transfer mortality parameters + const trfrMortParams // structure holding transfer mortality parameters + ); + trfrMortParams getMortParams(void); // Get transfer mortality parameters + void setMovtTraits( // Set transfer movement model parameters + const trfrMovtTraits // structure holding transfer movement model parameters + ); + trfrMovtTraits getMovtTraits(void); // Get transfer movement model traits + trfrCRWTraits getCRWTraits(void); // Get CRW traits + trfrSMSTraits getSMSTraits(void); // Get SMS traits + void setKernParams( // Set initial transfer by kernel parameter limits + const short, // stage (NB implemented for stage 0 only) + const short, // sex + const trfrKernParams, // structure holding min and max values + const double // Landscape resolution + ); + trfrKernParams getKernParams( // Get initial transfer by kernel parameter limits + short, // stage (NB implemented for stage 0 only) + short // sex + ); + void setSMSParams( // Set initial transfer by SMS parameter limits + const short, // stage (NB implemented for stage 0 only) + const short, // sex (NB implemented for sex 0 only) + const trfrSMSParams // structure holding min and max values + ); + trfrSMSParams getSMSParams( // Get initial transfer by SMS parameter limits + short, // stage (NB implemented for stage 0 only) + short // sex (NB implemented for sex 0 only) + ); + void setCRWParams( // Set initial transfer by CRW parameter limits + const short, // stage (NB implemented for stage 0 only) + const short, // sex (NB implemented for sex 0 only) + const trfrCRWParams // structure holding min and max values + ); + trfrCRWParams getCRWParams( // Get initial transfer by CRW parameter limits + short, // stage (NB implemented for stage 0 only) + short // sex (NB implemented for sex 0 only) + ); + void setTrfrScales( // Set transfer mutation parameters + const trfrScales // structure holding transfer mutation parameters + ); + trfrScales getTrfrScales(void); // Get transfer mutation parameters + // Return dimension of habitat-dependent step mortality and costs matrices + short getMovtHabDim(void); + void createHabCostMort( // Create habitat-dependent costs and mortality matrices + short // no. of habitats + ); + void setHabCost( // Set habitat-dependent cost + short, // habitat index no. + int // cost value + ); + void setHabMort( // Set habitat-dependent per-step mortality + short, // habitat index no. + double // mortality rate + ); + int getHabCost( // Get habitat-dependent cost + short // habitat index no. + ); + double getHabMort( // Get habitat-dependent per-step mortality + short // habitat index no. + ); + void deleteHabCostMort(void); // Delete habitat-dependent costs and mortality matrices + + // settlement parameter functions + + void setSettle( // Set settlement type + const settleType // structure holding settlement type (stage- and/or sex-dependent) + ); + settleType getSettle(void); // Get settlement type + void setSettRules( // Set settlement rules + const short, // stage + const short, // sex + const settleRules // structure holding settlement rules + ); + settleRules getSettRules( // Get settlement rules + short, // stage + short // sex + ); + void setSteps( // Set path step limit parameters + const short, // stage + const short, // sex + const settleSteps // structure holding path step limit parameters + ); + settleSteps getSteps( // Set path step limit parameters + short, // stage + short // sex + ); + void setSettTraits( // Set settlement density dependence traits + const short, // stage + const short, // sex + const settleTraits // structure holding density dependence traits + ); + settleTraits getSettTraits( // Get settlement density dependence traits + short, // stage + short // sex + ); + void setSettParams( // Set settlement initialisation parameters + const short, // stage (NB implemented for stage 0 only) + const short, // sex + const settParams // structure holding parameters + ); + settParams getSettParams( // Get settlement initialisation parameters + short, // stage (NB implemented for stage 0 only) + short // sex + ); + void setSettScales( // Set settlement mutation parameters + const settScales // structure holding settlement mutation parameters + ); + settScales getSettScales(void); // Get settlement mutation parameters + +private: + + // NOTE: SEQUENCE OF PARAMETER VARIABLES MAY NEED TO BE ALTERED FOR EFFICIENTCY ... + // ... but that is of low importance, as there will only be one (or a few) instance(s) + + // demographic parameters + + short repType; // 0 = asexual, 1 = simple two sex, 2 = complex two sex + short nStages; // no. of stages (incl. juveniles) in structured population + float propMales; // proportion of males at birth in sexual model + float harem; // max harem size in complex sexual model + float bc; // competition coefficient for non-structured population + float lambda; // max intrinsic growth rate for non-structured population + float probRep; // probability of reproducing in subsequent seasons + short repSeasons; // no. of reproductive seasons per year + short repInterval; // no. of reproductive seasons between subsequent reproductions + short maxAge; // max age in structured population + short survival; // survival timing: 0 = at reprodn, 1 = between reprodns, 2 = anually + bool stageStruct; + bool fecDens; + bool fecStageDens; + bool devDens; + bool devStageDens; + bool survDens; + bool survStageDens; + bool disperseOnLoss; // individuals disperse on complete loss of patch + // (otherwise they die) + short habDimK; // dimension of carrying capacities matrix + float* habK; // habitat-specific carrying capacities (inds/cell) + float devCoeff; // density-dependent development coefficient + float survCoeff; // density-dependent survival coefficient + float** ddwtFec; // density-dependent weights matrix for fecundity + float** ddwtDev; // density-dependent weights matrix for development + float** ddwtSurv; // density-dependent weights matrix for survival + // NB for the following arrays, sex 0 is females, sex 1 is males + float fec[NSTAGES][NSEXES]; // fecundities + float dev[NSTAGES][NSEXES]; // development probabilities + float surv[NSTAGES][NSEXES]; // survival probabilities + short minAge[NSTAGES][NSEXES]; // minimum age to enter stage + // NOTE - IN THEORY, NEXT 3 VARIABLES COULD BE COMMON, BUT WE WOULD NEED TO ENSURE THAT + // ALL MATRICES ARE DELETED IF THERE IS A CHANGE IN NO. OF STAGES OR REPRODUCTION TYPE + // ***** TO BE RECONSIDERED LATER ***** + short ddwtFecDim; // dimension of density-dependent weights matrix for fecundity + short ddwtDevDim; // dimension of density-dependent weights matrix for fecundity + short ddwtSurvDim; // dimension of density-dependent weights matrix for fecundity + float minRK; // minimum ) growth rate OR carrying capacity + float maxRK; // maximum ) (under environmental stochasticity) + + // genome parameters + + short nTraits; // no. of inheritable traits + short emigTrait[2]; // to record first and no. of emigration traits + short movtTrait[2]; // to record first and no. of transfer traits + short settTrait[2]; // to record first and no. of settlement traits + bool diploid; + bool neutralMarkers; // neutral markers in absence of any adaptive traits + bool pleiotropic; + bool trait1Chromosome; // 1:1 mapping of chromosome to trait + short nChromosomes; // no. of chromosomes + double probMutn; // allelic mutation probability + double probCrossover; // crossover probability at meiosis + double alleleSD; // s.d. of initial allelic values around phenotypic value + double mutationSD; // s.d. of mutation magnitude + short nNLoci; // no. of nLoci set + short* nLoci; // no. of loci per chromosome + short nTraitNames; // no. of trait names set + traitData* traitdata; // for mapping of chromosome loci to traits + string* traitnames; // trait names for parameter output + + // emigration parameters + + bool densDepEmig; // density-dependent emigration + bool stgDepEmig; // stage-dependent emigration + bool sexDepEmig; // sex-dependent emigration + bool indVarEmig; // individual variation in emigration + short emigStage; // stage which emigrates (used for stage-strucutred population + // having individual variability in emigration probability) +// NB for the following arrays, sex 0 is females, sex 1 is males + float d0[NSTAGES][NSEXES]; // maximum emigration probability + float alphaEmig[NSTAGES][NSEXES]; // slope of density-dependent reaction norm + float betaEmig[NSTAGES][NSEXES]; // inflection point of reaction norm (in terms of N/K) + // NB Initialisation parameters are made double to avoid conversion errors (reason unclear) + // on traits maps using FloatToStr() + // As evolving traits are not stage-dependent, no. of rows can be 1 + // Indeed, they could be 1-D arrays + double d0Mean[1][NSEXES]; + double d0SD[1][NSEXES]; + double alphaMean[1][NSEXES]; + double alphaSD[1][NSEXES]; + double betaMean[1][NSEXES]; + double betaSD[1][NSEXES]; + double d0Scale; // scaling factor for d0 + double alphaScale; // scaling factor for alpha + double betaScale; // scaling factor for beta + + // transfer parameters + + bool moveModel; + bool stgDepTrfr; + bool sexDepTrfr; + bool distMort; + bool indVarTrfr; + bool twinKern; + bool habMort; // habitat-dependent mortality + float meanDist1[NSTAGES][NSEXES]; // mean of 1st dispersal kernel (m) + float meanDist2[NSTAGES][NSEXES]; // mean of 2nd dispersal kernel (m) + float probKern1[NSTAGES][NSEXES]; // probability of dispersing with the 1st kernel + // NB INITIAL limits are made double to avoid conversion errors (reason unclear) + // on traits maps using FloatToStr() + // As evolving traits are are not stage-dependent, no. of rows can be 1 + // Indeed, as they are INITIAL limits, which may subsequently be exceeded, they could be + // 1-D arrays + double dist1Mean[1][NSEXES]; // mean of initial mean of the 1st dispersal kernel (m) + double dist1SD[1][NSEXES]; // s.d. of initial mean of the 1st dispersal kernel (m) + double dist2Mean[1][NSEXES]; // mean of initial mean of the 2nd dispersal kernel (m) + double dist2SD[1][NSEXES]; // s.d. of initial mean of the 2nd dispersal kernel (m) + double PKern1Mean[1][NSEXES]; // mean of initial prob. of dispersing with 1st kernel + double PKern1SD[1][NSEXES]; // s.d. of initial prob. of dispersing with 1st kernel + float dist1Scale; // scaling factor for mean of 1st dispersal kernel (m) + float dist2Scale; // scaling factor for mean of 2nd dispersal kernel (m) + float PKern1Scale; // scaling factor for prob. of dispersing with 1st kernel + float fixedMort; // constant mortality probability + float mortAlpha; // slope for mortality distance dependence function + float mortBeta; // inflection point for mortality distance dependence function + short moveType; // 1 = SMS, 2 = CRW + short pr; // SMS perceptual range (cells) + short prMethod; // SMS perceptual range evaluation method: + // 1 = arith. mean, 2 = harmonic mean, 3 = inverse weighted arith. mean + short memSize; // SMS memory size (1-14 steps) + short goalType; // SMS goal bias type: 0 = none, 1 = towards goal, 2 = dispersal bias + float dp; // SMS directional persistence + float gb; // SMS goal bias + float alphaDB; // SMS dispersal bias decay rate + int betaDB; // SMS dispersal bias decay inflection point (no. of steps) + float stepMort; // constant per-step mortality probability for movement models + double* habStepMort; // habitat-dependent per-step mortality probability + float stepLength; // CRW step length (m) + float rho; // CRW correlation coefficient + double dpMean[1][NSEXES]; // mean of initial SMS directional persistence + double dpSD[1][NSEXES]; // s.d. of initial SMS directional persistence + double gbMean[1][NSEXES]; // mean of initial SMS goal bias + double gbSD[1][NSEXES]; // s.d. of initial SMS goal bias + double alphaDBMean[1][NSEXES]; // mean of initial SMS dispersal bias decay rate + double alphaDBSD[1][NSEXES]; // s.d. of initial SMS dispersal bias decay rate + double betaDBMean[1][NSEXES]; // mean of initial SMS dispersal bias decay infl. pt. + double betaDBSD[1][NSEXES]; // s.d. of initial SMS dispersal bias decay infl. pt. + float dpScale; // scaling factor for SMS directional persistence + float gbScale; // scaling factor for SMS goal bias + float alphaDBScale; // scaling factor for SMS dispersal bias decay rate + float betaDBScale; // scaling factor for SMS dispersal bias decay infl. pt. + double stepLgthMean[1][NSEXES]; // mean of initial step length (m) + double stepLgthSD[1][NSEXES]; // s.d. of initial step length (m) + double rhoMean[1][NSEXES]; // mean of initial correlation coefficient + double rhoSD[1][NSEXES]; // s.d. of initial correlation coefficient + float stepLScale; // scaling factor for step length (m) + float rhoScale; // scaling factor for correlation coefficient + short habDimTrfr; // dimension of habitat-dependent step mortality and costs matrices + int* habCost; // habitat costs + bool costMap; // import cost map from file? + bool straigtenPath; // straighten path after decision not to settle + bool fullKernel; // used to indicate special case when density-independent emigration + // is 1.0, and kernel-based movement within the natal cell is used + // to determine philopatry + +// settlement parameters + + bool stgDepSett; + bool sexDepSett; + bool indVarSett; // individual variation in settlement + bool densDepSett[NSTAGES][NSEXES]; + bool wait[NSTAGES][NSEXES]; // wait to continue moving next season (stage-structured model only) + bool go2nbrLocn[NSTAGES][NSEXES]; // settle in neighbouring cell/patch if available (ditto) + bool findMate[NSTAGES][NSEXES]; + int minSteps; // minimum no. of steps + int maxSteps; // maximum total no. of steps + int maxStepsYr[NSTAGES][NSEXES]; // maximum no. of steps in any one dispersal period + float s0[NSTAGES][NSEXES]; // maximum settlement probability + float alphaS[NSTAGES][NSEXES]; // slope of the settlement reaction norm to density + float betaS[NSTAGES][NSEXES]; // inflection point of the settlement reaction norm to density + double s0Mean[1][NSEXES]; // mean of initial maximum settlement probability + double s0SD[1][NSEXES]; // s.d. of initial maximum settlement probability + double alphaSMean[1][NSEXES]; // mean of initial settlement reaction norm slope + double alphaSSD[1][NSEXES]; // s.d. of initial settlement reaction norm slope + double betaSMean[1][NSEXES]; // mean of initial settlement reaction norm inflection point + double betaSSD[1][NSEXES]; // s.d. of initial settlement reaction norm inflection point + float s0Scale; // scaling factor for maximum settlement probability + float alphaSScale; // scaling factor for settlement reaction norm slope + float betaSScale; // scaling factor for settlement reaction norm inflection point + + // other attributes + + int spNum; + +}; + +/* IMPORTANT NOTE: +At the time of writing (24/4/14) the stage- and sex-dependent parameters for emigration +and dispersal (e.g. d0[NSTAGES][NSEXES]) are set according to the appropriate stage- and +sex-dependent settings; thus a species could be structured and sexual, but parameter values +are set for elements [0][0] only if emigration/transfer is stage- and sex-independent. +However, the parameters for settlement are set for ALL stages and sexes appropriate to the +species, regardless of settlement dependency. The rationale for this is that settlement +parameters need to be accessed many more times for a movement model (at each step) than +emigration or transfer parameters, and therefore there will be a performance gain in +avoiding nested if statements in Individual::moveStep() which would otherwise be required +to get the correct parameter values from the settlement arrays. Whether that particular +rationale is justified remains to be tested! +*/ + +//--------------------------------------------------------------------------- + +#if RSDEBUG +//extern ofstream DEBUGLOG; +extern void DebugGUI(string); +#endif + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/SubCommunity.cpp b/src/RScore/SubCommunity.cpp new file mode 100644 index 00000000..d908892a --- /dev/null +++ b/src/RScore/SubCommunity.cpp @@ -0,0 +1,1008 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + + //--------------------------------------------------------------------------- + +#include "SubCommunity.h" +//--------------------------------------------------------------------------- + +ofstream outtraits; + +//--------------------------------------------------------------------------- + +SubCommunity::SubCommunity(Patch* pPch, int num) { + subCommNum = num; + pPatch = pPch; + // record the new sub-community no. in the patch + pPatch->setSubComm((intptr)this); + initial = false; + occupancy = 0; +} + +SubCommunity::~SubCommunity() { + pPatch->setSubComm(0); + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + delete popns[i]; + } + popns.clear(); + if (occupancy != 0) delete[] occupancy; +} + +intptr SubCommunity::getNum(void) { return subCommNum; } + +Patch* SubCommunity::getPatch(void) { return pPatch; } + +locn SubCommunity::getLocn(void) { + locn loc = pPatch->getCellLocn(0); + return loc; +} + +void SubCommunity::setInitial(bool b) { initial = b; } + +void SubCommunity::initialise(Landscape* pLandscape, Species* pSpecies) +{ + int ncells; + landParams ppLand = pLandscape->getLandParams(); + initParams init = paramsInit->getInit(); + // determine size of initial population + int nInds = 0; + if (subCommNum == 0 // matrix patch + || !initial) // not in initial region or distribution + nInds = 0; + else { + float k = pPatch->getK(); + if (k > 0.0) { // patch is currently suitable for this species + switch (init.initDens) { + case 0: // at carrying capacity + nInds = (int)k; + break; + case 1: // at half carrying capacity + nInds = (int)(k / 2.0); + break; + case 2: // specified no. per cell or density + ncells = pPatch->getNCells(); + if (ppLand.patchModel) { + nInds = (int)(init.indsHa * (float)(ncells * ppLand.resol * ppLand.resol) / 10000.0); + } + else { + nInds = init.indsCell * ncells; + } + break; + } + } + else nInds = 0; + } + // create new population only if it is non-zero or the matrix popn + if (subCommNum == 0 || nInds > 0) { + newPopn(pLandscape, pSpecies, pPatch, nInds); + } +} + +// initialise a specified individual +void SubCommunity::initialInd(Landscape* pLandscape, Species* pSpecies, + Patch* pPatch, Cell* pCell, int ix) +{ + + demogrParams dem = pSpecies->getDemogr(); + stageParams sstruct = pSpecies->getStage(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + genomeData gen = pSpecies->getGenomeData(); + short stg, age, repInt; + Individual* pInd; + float probmale; + + // create new population if not already in existence + int npopns = (int)popns.size(); + if (npopns < 1) { + newPopn(pLandscape, pSpecies, pPatch, 0); + } + + // create new individual + initInd iind = paramsInit->getInitInd(ix); + if (dem.stageStruct) { + stg = iind.stage; age = iind.age; repInt = sstruct.repInterval; + } + else { + age = stg = 1; repInt = 0; + } + if (dem.repType == 0) { + probmale = 0.0; + } + else { + if (iind.sex == 1) probmale = 1.0; else probmale = 0.0; + } + pInd = new Individual(pCell, pPatch, stg, age, repInt, probmale, trfr.moveModel, trfr.moveType); + + // add new individual to the population + // NB THIS WILL NEED TO BE CHANGED FOR MULTIPLE SPECIES... + popns[0]->recruit(pInd); + + if (emig.indVar || trfr.indVar || sett.indVar || gen.neutralMarkers) + { + // individual variation - set up genetics + landData land = pLandscape->getLandData(); + pInd->setGenes(pSpecies, land.resol); + } + +} + +// Create a new population, and return its address +Population* SubCommunity::newPopn(Landscape* pLandscape, Species* pSpecies, + Patch* pPatch, int nInds) +{ + landParams land = pLandscape->getLandParams(); + int npopns = (int)popns.size(); + popns.push_back(new Population(pSpecies, pPatch, nInds, land.resol)); + return popns[npopns]; +} + +popStats SubCommunity::getPopStats(void) { + popStats p, pop; + p.pSpecies = 0; p.spNum = 0; p.nInds = p.nAdults = p.nNonJuvs = 0; p.breeding = false; + p.pPatch = pPatch; + // FOR SINGLE SPECIES IMPLEMENTATION, THERE IS ONLY ONE POPULATION IN THE PATCH + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + pop = popns[i]->getStats(); + p.pSpecies = pop.pSpecies; + p.spNum = pop.spNum; + p.nInds += pop.nInds; + p.nNonJuvs += pop.nNonJuvs; + p.nAdults += pop.nAdults; + p.breeding = pop.breeding; + } + return p; +} + +void SubCommunity::resetPopns(void) { + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + delete popns[i]; + } + popns.clear(); + // clear the list of populations in the corresponding patch + pPatch->resetPopn(); +} + +void SubCommunity::resetPossSettlers(void) { + if (subCommNum == 0) return; // not applicable in the matrix + pPatch->resetPossSettlers(); +} + +// Extirpate all populations according to +// option 0 - random local extinction probability +// option 1 - local extinction probability gradient +// NB only applied for cell-based model +void SubCommunity::localExtinction(int option) { + double pExtinct = 0.0; + if (option == 0) { + envStochParams env = paramsStoch->getStoch(); + if (env.localExt) pExtinct = env.locExtProb; + } + else { + envGradParams grad = paramsGrad->getGradient(); + Cell* pCell = pPatch->getRandomCell(); // get only cell in the patch + // extinction prob is complement of cell gradient value plus any non-zero prob at the optimum + pExtinct = 1.0 - pCell->getEnvVal() + grad.extProbOpt; + if (pExtinct > 1.0) pExtinct = 1.0; + } + if (pRandom->Bernoulli(pExtinct)) { + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->extirpate(); + } + } +} + +// Action in event of patch becoming unsuitable owing to landscape change +void SubCommunity::patchChange(void) { + if (subCommNum == 0) return; // no reproduction in the matrix + Species* pSpecies; + float localK = 0.0; + int npops = (int)popns.size(); + // THE FOLLOWING MAY BE MORE EFFICIENT WHILST THERE IS ONLY ONE SPECIES ... + if (npops < 1) return; + localK = pPatch->getK(); + if (localK <= 0.0) { // patch in dynamic landscape has become unsuitable + for (int i = 0; i < npops; i++) { // all populations + pSpecies = popns[i]->getSpecies(); + demogrParams dem = pSpecies->getDemogr(); + if (dem.stageStruct) { + stageParams sstruct = pSpecies->getStage(); + if (sstruct.disperseOnLoss) popns[i]->allEmigrate(); + else popns[i]->extirpate(); + } + else { // non-stage-structured species is destroyed + popns[i]->extirpate(); + } + } + } +} + +void SubCommunity::reproduction(int resol, float epsGlobal, short rasterType, bool patchModel) +{ + if (subCommNum == 0) return; // no reproduction in the matrix + float localK, envval; + Cell* pCell; + envGradParams grad = paramsGrad->getGradient(); + envStochParams env = paramsStoch->getStoch(); + + int npops = (int)popns.size(); + // THE FOLLOWING MAY BE MORE EFFICIENT WHILST THERE IS ONLY ONE SPECIES ... + if (npops < 1) return; + + localK = pPatch->getK(); + if (localK > 0.0) { + if (patchModel) { + envval = 1.0; // environmental gradient is currently not applied for patch-based model + } + else { // cell-based model + if (grad.gradient && grad.gradType == 2) + { // gradient in fecundity + Cell* pCell = pPatch->getRandomCell(); // locate the only cell in the patch + envval = pCell->getEnvVal(); + } + else envval = 1.0; + } + if (env.stoch && !env.inK) { // stochasticity in fecundity + if (env.local) { + if (!patchModel) { // only permitted for cell-based model + pCell = pPatch->getRandomCell(); + if (pCell != 0) envval += pCell->getEps(); + } + } + else { // global stochasticity + envval += epsGlobal; + } + } + for (int i = 0; i < npops; i++) { // all populations + popns[i]->reproduction(localK, envval, resol); + popns[i]->fledge(); + } + } +} + +void SubCommunity::emigration(void) +{ + if (subCommNum == 0) return; // no emigration from the matrix + float localK; + int npops = (int)popns.size(); + // THE FOLLOWING MAY BE MORE EFFICIENT WHILST THERE IS ONLY ONE SPECIES ... + if (npops < 1) return; + localK = pPatch->getK(); + // NOTE that even if K is zero, it could have been >0 in previous time-step, and there + // might be emigrants if there is non-juvenile emigration + for (int i = 0; i < npops; i++) { // all populations + popns[i]->emigration(localK); + } +} + +// Remove emigrants from their natal patch and add to patch 0 (matrix) +void SubCommunity::initiateDispersal(SubCommunity* matrix) { + if (subCommNum == 0) return; // no dispersal initiation in the matrix + popStats pop; + disperser disp; + + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + pop = popns[i]->getStats(); + for (int j = 0; j < pop.nInds; j++) { + disp = popns[i]->extractDisperser(j); + if (disp.yes) { // disperser - has already been removed from natal population + // add to matrix population + matrix->recruit(disp.pInd, pop.pSpecies); + } + } + // remove pointers to emigrants + popns[i]->clean(); + } + +} + +// Add an individual into the local population of its species in the patch +void SubCommunity::recruit(Individual* pInd, Species* pSpecies) { + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + if (pSpecies == popns[i]->getSpecies()) { + popns[i]->recruit(pInd); + } + } +} + +// Transfer through the matrix - run for the matrix sub-community only +#if RS_RCPP +int SubCommunity::transfer(Landscape* pLandscape, short landIx, short nextseason) +#else +int SubCommunity::transfer(Landscape* pLandscape, short landIx) +#endif // RS_RCPP +{ + int ndispersers = 0; + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations +#if RS_RCPP + ndispersers += popns[i]->transfer(pLandscape, landIx, nextseason); +#else + ndispersers += popns[i]->transfer(pLandscape, landIx); +#endif // RS_RCPP + + } + return ndispersers; +} + +//--------------------------------------------------------------------------- + +// Remove emigrants from patch 0 (matrix) and transfer to sub-community +// in which their destination co-ordinates fall +// This function is executed for the matrix patch only + +void SubCommunity::completeDispersal(Landscape* pLandscape, bool connect) +{ + int popsize; + disperser settler; + Species* pSpecies; + Population* pPop; + Patch* pPrevPatch; + Patch* pNewPatch; + Cell* pPrevCell; + SubCommunity* pSubComm; + + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + pSpecies = popns[i]->getSpecies(); + popsize = popns[i]->getNInds(); + for (int j = 0; j < popsize; j++) { + bool settled; + settler = popns[i]->extractSettler(j); + settled = settler.yes; + if (settled) { + // settler - has already been removed from matrix population + // find new patch + pNewPatch = (Patch*)settler.pCell->getPatch(); + // find population within the patch (if there is one) + pPop = (Population*)pNewPatch->getPopn((intptr)pSpecies); + if (pPop == 0) { // settler is the first in a previously uninhabited patch + // create a new population in the corresponding sub-community + pSubComm = (SubCommunity*)pNewPatch->getSubComm(); + pPop = pSubComm->newPopn(pLandscape, pSpecies, pNewPatch, 0); + } + pPop->recruit(settler.pInd); + if (connect) { // increment connectivity totals + int newpatch = pNewPatch->getSeqNum(); + pPrevCell = settler.pInd->getLocn(0); // previous cell + intptr patch = pPrevCell->getPatch(); + if (patch != 0) { + pPrevPatch = (Patch*)patch; + int prevpatch = pPrevPatch->getSeqNum(); + pLandscape->incrConnectMatrix(prevpatch, newpatch); + } + } + } + else { // for group dispersal only + } + } + // remove pointers in the matrix popn to settlers + popns[i]->clean(); + } + +} + +//--------------------------------------------------------------------------- + +void SubCommunity::survival(short part, short option0, short option1) +{ + int npops = (int)popns.size(); + if (npops < 1) return; + if (part == 0) { + float localK = pPatch->getK(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->survival0(localK, option0, option1); + } + } + else { + for (int i = 0; i < npops; i++) { // all populations + popns[i]->survival1(); + } + } +} + +void SubCommunity::ageIncrement(void) { + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->ageIncrement(); + } +} + +// Find the population of a given species in a given patch +Population* SubCommunity::findPop(Species* pSp, Patch* pPch) { +#if RSDEBUG + DEBUGLOG << "SubCommunity::findPop(): this=" << this + << endl; +#endif + + Population* pPop = 0; + popStats pop; + int npops = (int)popns.size(); + + for (int i = 0; i < npops; i++) { // all populations + pop = popns[i]->getStats(); + if (pop.pSpecies == pSp && pop.pPatch == pPch) { // population located + pPop = popns[i]; + break; + } + else pPop = 0; + } + return pPop; +} + +//--------------------------------------------------------------------------- + +void SubCommunity::createOccupancy(int nrows) { + if (occupancy != 0) deleteOccupancy(); + if (nrows > 0) { + occupancy = new int[nrows]; + for (int i = 0; i < nrows; i++) occupancy[i] = 0; + } +} + +void SubCommunity::updateOccupancy(int row) { + popStats pop; + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { + pop = popns[i]->getStats(); + if (pop.nInds > 0 && pop.breeding) { + occupancy[row]++; + i = npops; + } + } +} + +int SubCommunity::getOccupancy(int row) { + if (row >= 0) return occupancy[row]; + else return 0; +} + +void SubCommunity::deleteOccupancy(void) { + delete[] occupancy; + occupancy = 0; +} + +//--------------------------------------------------------------------------- +// Open population file and write header record +bool SubCommunity::outPopHeaders(Landscape* pLandscape, Species* pSpecies, int option) +{ + bool fileOK; + Population* pPop; + landParams land = pLandscape->getLandParams(); + + if (option == -999) { // close the file + // as all populations may have been deleted, set up a dummy one + // species is not necessary + pPop = new Population(); + fileOK = pPop->outPopHeaders(-999, land.patchModel); + delete pPop; + } + else { // open the file + // as no population has yet been created, set up a dummy one + // species is necessary, as columns depend on stage and sex structure + pPop = new Population(pSpecies, pPatch, 0, land.resol); + fileOK = pPop->outPopHeaders(land.landNum, land.patchModel); + delete pPop; + } + return fileOK; +} + +// Write records to population file +void SubCommunity::outPop(Landscape* pLandscape, int rep, int yr, int gen) +{ + landParams land = pLandscape->getLandParams(); + envGradParams grad = paramsGrad->getGradient(); + envStochParams env = paramsStoch->getStoch(); + bool writeEnv = false; + bool gradK = false; + if (grad.gradient) { + writeEnv = true; + if (grad.gradType == 1) gradK = true; // ... carrying capacity + } + if (env.stoch) writeEnv = true; + + // generate output for each population within the sub-community (patch) + // provided that the patch is suitable (i.e. non-zero carrying capacity) + // or the population is above zero (possible if there is stochasticity or a moving gradient) + // or it is the matrix patch in a patch-based model + int npops = (int)popns.size(); + int patchnum; + Cell* pCell; + float localK; + float eps = 0.0; + if (env.stoch) { + if (env.local) { + pCell = pPatch->getRandomCell(); + if (pCell != 0) eps = pCell->getEps(); + } + else { + eps = pLandscape->getGlobalStoch(yr); + } + } + + patchnum = pPatch->getPatchNum(); + for (int i = 0; i < npops; i++) { // all populations + localK = pPatch->getK(); + if (localK > 0.0 || (land.patchModel && patchnum == 0)) { + popns[i]->outPopulation(rep, yr, gen, eps, land.patchModel, writeEnv, gradK); + } + else { + if (popns[i]->totalPop() > 0) { + popns[i]->outPopulation(rep, yr, gen, eps, land.patchModel, writeEnv, gradK); + } + } + } +} + +// Write records to individuals file +void SubCommunity::outInds(Landscape* pLandscape, int rep, int yr, int gen, int landNr) { + landParams ppLand = pLandscape->getLandParams(); + if (landNr >= 0) { // open the file + popns[0]->outIndsHeaders(rep, landNr, ppLand.patchModel); + return; + } + if (landNr == -999) { // close the file + popns[0]->outIndsHeaders(rep, -999, ppLand.patchModel); + return; + } + // generate output for each population within the sub-community (patch) + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->outIndividual(pLandscape, rep, yr, gen, pPatch->getPatchNum()); + } +} + +// Write records to individuals file +void SubCommunity::outGenetics(int rep, int yr, int gen, int landNr) +{ + if (landNr >= 0) { // open the file + popns[0]->outGenetics(rep, yr, landNr); + return; + } + if (landNr == -999) { // close the file + popns[0]->outGenetics(rep, yr, landNr); + return; + } + // generate output for each population within the sub-community (patch) + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->outGenetics(rep, yr, landNr); + } +} + +// Population size of a specified stage +int SubCommunity::stagePop(int stage) { + int popsize = 0; + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { // all populations + popsize += popns[i]->stagePop(stage); + } + return popsize; +} + +// Open traits file and write header record +bool SubCommunity::outTraitsHeaders(Landscape* pLandscape, Species* pSpecies, int landNr) +{ + landParams land = pLandscape->getLandParams(); + if (landNr == -999) { // close file + if (outtraits.is_open()) outtraits.close(); + outtraits.clear(); + return true; + } + + string name; + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + simParams sim = paramsSim->getSim(); + + string DirOut = paramsSim->getDir(2); + if (sim.batchMode) { + if (land.patchModel) { + name = DirOut + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(landNr) + + "_TraitsXpatch.txt"; + } + else { + name = DirOut + + "Batch" + Int2Str(sim.batchNum) + "_" + + "Sim" + Int2Str(sim.simulation) + "_Land" + Int2Str(landNr) + + "_TraitsXcell.txt"; + } + } + else { + if (land.patchModel) { + name = DirOut + "Sim" + Int2Str(sim.simulation) + "_TraitsXpatch.txt"; + } + else { + name = DirOut + "Sim" + Int2Str(sim.simulation) + "_TraitsXcell.txt"; + } + } + outtraits.open(name.c_str()); + + outtraits << "Rep\tYear\tRepSeason"; + if (land.patchModel) outtraits << "\tPatchID"; + else + outtraits << "\tx\ty"; + + if (emig.indVar) { + if (emig.sexDep) { + if (emig.densDep) { + outtraits << "\tF_meanD0\tF_stdD0\tM_meanD0\tM_stdD0"; + outtraits << "\tF_meanAlpha\tF_stdAlpha\tM_meanAlpha\tM_stdAlpha"; + outtraits << "\tF_meanBeta\tF_stdBeta\tM_meanBeta\tM_stdBeta"; + } + else { + outtraits << "\tF_meanEP\tF_stdEP\tM_meanEP\tM_stdEP"; + } + } + else { + if (emig.densDep) { + outtraits << "\tmeanD0\tstdD0\tmeanAlpha\tstdAlpha"; + outtraits << "\tmeanBeta\tstdBeta"; + } + else { + outtraits << "\tmeanEP\tstdEP"; + } + } + } + if (trfr.indVar) { + if (trfr.moveModel) { + if (trfr.moveType == 1) { + outtraits << "\tmeanDP\tstdDP\tmeanGB\tstdGB"; + outtraits << "\tmeanAlphaDB\tstdAlphaDB\tmeanBetaDB\tstdBetaDB"; + } + if (trfr.moveType == 2) { + outtraits << "\tmeanStepLength\tstdStepLength\tmeanRho\tstdRho"; + } + } + else { + if (trfr.sexDep) { + outtraits << "\tF_mean_distI\tF_std_distI\tM_mean_distI\tM_std_distI"; + if (trfr.twinKern) + outtraits << "\tF_mean_distII\tF_std_distII\tM_mean_distII\tM_std_distII" + << "\tF_meanPfirstKernel\tF_stdPfirstKernel" + << "\tM_meanPfirstKernel\tM_stdPfirstKernel"; + } + else { + outtraits << "\tmean_distI\tstd_distI"; + if (trfr.twinKern) + outtraits << "\tmean_distII\tstd_distII\tmeanPfirstKernel\tstdPfirstKernel"; + } + } + } + if (sett.indVar) { + if (sett.sexDep) { + outtraits << "\tF_meanS0\tF_stdS0\tM_meanS0\tM_stdS0"; + outtraits << "\tF_meanAlphaS\tF_stdAlphaS\tM_meanAlphaS\tM_stdAlphaS"; + outtraits << "\tF_meanBetaS\tF_stdBetaS\tM_meanBetaS\tM_stdBetaS"; + } + else { + outtraits << "\tmeanS0\tstdS0"; + outtraits << "\tmeanAlphaS\tstdAlphaS"; + outtraits << "\tmeanBetaS\tstdBetaS"; + } + } + outtraits << endl; + + return outtraits.is_open(); +} + +// Write records to traits file and return aggregated sums +traitsums SubCommunity::outTraits(traitCanvas tcanv, + Landscape* pLandscape, int rep, int yr, int gen, bool commlevel) +{ + int popsize, ngenes; + landParams land = pLandscape->getLandParams(); + simParams sim = paramsSim->getSim(); + bool writefile = false; + if (sim.outTraitsCells && yr % sim.outIntTraitCell == 0 && !commlevel) + writefile = true; + traitsums ts, poptraits; + for (int i = 0; i < NSEXES; i++) { + ts.ninds[i] = 0; + ts.sumD0[i] = ts.ssqD0[i] = 0.0; + ts.sumAlpha[i] = ts.ssqAlpha[i] = 0.0; ts.sumBeta[i] = ts.ssqBeta[i] = 0.0; + ts.sumDist1[i] = ts.ssqDist1[i] = 0.0; ts.sumDist2[i] = ts.ssqDist2[i] = 0.0; + ts.sumProp1[i] = ts.ssqProp1[i] = 0.0; + ts.sumDP[i] = ts.ssqDP[i] = 0.0; + ts.sumGB[i] = ts.ssqGB[i] = 0.0; + ts.sumAlphaDB[i] = ts.ssqAlphaDB[i] = 0.0; + ts.sumBetaDB[i] = ts.ssqBetaDB[i] = 0.0; + ts.sumStepL[i] = ts.ssqStepL[i] = 0.0; ts.sumRho[i] = ts.ssqRho[i] = 0.0; + ts.sumS0[i] = ts.ssqS0[i] = 0.0; + ts.sumAlphaS[i] = ts.ssqAlphaS[i] = 0.0; ts.sumBetaS[i] = ts.ssqBetaS[i] = 0.0; + } + + // generate output for each population within the sub-community (patch) + // provided that the patch is suitable (i.e. non-zero carrying capacity) + int npops = (int)popns.size(); + Species* pSpecies; + float localK; + + for (int i = 0; i < npops; i++) { // all populations + localK = pPatch->getK(); + if (localK > 0.0 && popns[i]->getNInds() > 0) { + pSpecies = popns[i]->getSpecies(); + demogrParams dem = pSpecies->getDemogr(); + emigRules emig = pSpecies->getEmig(); + trfrRules trfr = pSpecies->getTrfr(); + settleType sett = pSpecies->getSettle(); + poptraits = popns[i]->getTraits(pSpecies); + + if (writefile) { + outtraits << rep << "\t" << yr << "\t" << gen; + if (land.patchModel) { + outtraits << "\t" << pPatch->getPatchNum(); + } + else { + locn loc = pPatch->getCellLocn(0); + outtraits << "\t" << loc.x << "\t" << loc.y; + } + } + + if (emig.indVar) { + if (emig.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ngenes = 1; + } + else { // sexual reproduction + ngenes = 1; + } + } + double mnD0[2], mnAlpha[2], mnBeta[2], sdD0[2], sdAlpha[2], sdBeta[2]; + for (int g = 0; g < ngenes; g++) { + mnD0[g] = mnAlpha[g] = mnBeta[g] = sdD0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = poptraits.ninds[g]; + else popsize = poptraits.ninds[0] + poptraits.ninds[1]; + if (popsize > 0) { + mnD0[g] = poptraits.sumD0[g] / (double)popsize; + mnAlpha[g] = poptraits.sumAlpha[g] / (double)popsize; + mnBeta[g] = poptraits.sumBeta[g] / (double)popsize; + if (popsize > 1) { + sdD0[g] = poptraits.ssqD0[g] / (double)popsize - mnD0[g] * mnD0[g]; + if (sdD0[g] > 0.0) sdD0[g] = sqrt(sdD0[g]); else sdD0[g] = 0.0; + sdAlpha[g] = poptraits.ssqAlpha[g] / (double)popsize - mnAlpha[g] * mnAlpha[g]; + if (sdAlpha[g] > 0.0) sdAlpha[g] = sqrt(sdAlpha[g]); else sdAlpha[g] = 0.0; + sdBeta[g] = poptraits.ssqBeta[g] / (double)popsize - mnBeta[g] * mnBeta[g]; + if (sdBeta[g] > 0.0) sdBeta[g] = sqrt(sdBeta[g]); else sdBeta[g] = 0.0; + } + else { + sdD0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + } + } + } + if (writefile) { + if (emig.sexDep) { + outtraits << "\t" << mnD0[0] << "\t" << sdD0[0]; + outtraits << "\t" << mnD0[1] << "\t" << sdD0[1]; + if (emig.densDep) { + outtraits << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outtraits << "\t" << mnAlpha[1] << "\t" << sdAlpha[1]; + outtraits << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + outtraits << "\t" << mnBeta[1] << "\t" << sdBeta[1]; + } + } + else { // sex-independent + outtraits << "\t" << mnD0[0] << "\t" << sdD0[0]; + if (emig.densDep) { + outtraits << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outtraits << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + } + } + } + } + + if (trfr.indVar) { + if (trfr.moveModel) { + // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + ngenes = 1; + } + else { + if (trfr.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + ngenes = 1; + } + } + double mnDist1[2], mnDist2[2], mnProp1[2], mnStepL[2], mnRho[2]; + double sdDist1[2], sdDist2[2], sdProp1[2], sdStepL[2], sdRho[2]; + double mnDP[2], mnGB[2], mnAlphaDB[2], mnBetaDB[2]; + double sdDP[2], sdGB[2], sdAlphaDB[2], sdBetaDB[2]; + for (int g = 0; g < ngenes; g++) { + mnDist1[g] = mnDist2[g] = mnProp1[g] = mnStepL[g] = mnRho[g] = 0.0; + sdDist1[g] = sdDist2[g] = sdProp1[g] = sdStepL[g] = sdRho[g] = 0.0; + mnDP[g] = mnGB[g] = mnAlphaDB[g] = mnBetaDB[g] = 0.0; + sdDP[g] = sdGB[g] = sdAlphaDB[g] = sdBetaDB[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = poptraits.ninds[g]; + else popsize = poptraits.ninds[0] + poptraits.ninds[1]; + if (popsize > 0) { + mnDist1[g] = poptraits.sumDist1[g] / (double)popsize; + mnDist2[g] = poptraits.sumDist2[g] / (double)popsize; + mnProp1[g] = poptraits.sumProp1[g] / (double)popsize; + mnStepL[g] = poptraits.sumStepL[g] / (double)popsize; + mnRho[g] = poptraits.sumRho[g] / (double)popsize; + mnDP[g] = poptraits.sumDP[g] / (double)popsize; + mnGB[g] = poptraits.sumGB[g] / (double)popsize; + mnAlphaDB[g] = poptraits.sumAlphaDB[g] / (double)popsize; + mnBetaDB[g] = poptraits.sumBetaDB[g] / (double)popsize; + if (popsize > 1) { + sdDist1[g] = poptraits.ssqDist1[g] / (double)popsize - mnDist1[g] * mnDist1[g]; + if (sdDist1[g] > 0.0) sdDist1[g] = sqrt(sdDist1[g]); else sdDist1[g] = 0.0; + sdDist2[g] = poptraits.ssqDist2[g] / (double)popsize - mnDist2[g] * mnDist2[g]; + if (sdDist2[g] > 0.0) sdDist2[g] = sqrt(sdDist2[g]); else sdDist2[g] = 0.0; + sdProp1[g] = poptraits.ssqProp1[g] / (double)popsize - mnProp1[g] * mnProp1[g]; + if (sdProp1[g] > 0.0) sdProp1[g] = sqrt(sdProp1[g]); else sdProp1[g] = 0.0; + sdStepL[g] = poptraits.ssqStepL[g] / (double)popsize - mnStepL[g] * mnStepL[g]; + if (sdStepL[g] > 0.0) sdStepL[g] = sqrt(sdStepL[g]); else sdStepL[g] = 0.0; + sdRho[g] = poptraits.ssqRho[g] / (double)popsize - mnRho[g] * mnRho[g]; + if (sdRho[g] > 0.0) sdRho[g] = sqrt(sdRho[g]); else sdRho[g] = 0.0; + sdDP[g] = poptraits.ssqDP[g] / (double)popsize - mnDP[g] * mnDP[g]; + if (sdDP[g] > 0.0) sdDP[g] = sqrt(sdDP[g]); else sdDP[g] = 0.0; + sdGB[g] = poptraits.ssqGB[g] / (double)popsize - mnGB[g] * mnGB[g]; + if (sdGB[g] > 0.0) sdGB[g] = sqrt(sdGB[g]); else sdGB[g] = 0.0; + sdAlphaDB[g] = poptraits.ssqAlphaDB[g] / (double)popsize - mnAlphaDB[g] * mnAlphaDB[g]; + if (sdAlphaDB[g] > 0.0) sdAlphaDB[g] = sqrt(sdAlphaDB[g]); else sdAlphaDB[g] = 0.0; + sdBetaDB[g] = poptraits.ssqBetaDB[g] / (double)popsize - mnBetaDB[g] * mnBetaDB[g]; + if (sdBetaDB[g] > 0.0) sdBetaDB[g] = sqrt(sdBetaDB[g]); else sdBetaDB[g] = 0.0; + } + } + } + if (writefile) { + if (trfr.moveModel) { + if (trfr.moveType == 1) { + outtraits << "\t" << mnDP[0] << "\t" << sdDP[0]; + outtraits << "\t" << mnGB[0] << "\t" << sdGB[0]; + outtraits << "\t" << mnAlphaDB[0] << "\t" << sdAlphaDB[0]; + outtraits << "\t" << mnBetaDB[0] << "\t" << sdBetaDB[0]; + } + if (trfr.moveType == 2) { + outtraits << "\t" << mnStepL[0] << "\t" << sdStepL[0]; + outtraits << "\t" << mnRho[0] << "\t" << sdRho[0]; + } + } + else { + if (trfr.sexDep) { + outtraits << "\t" << mnDist1[0] << "\t" << sdDist1[0]; + outtraits << "\t" << mnDist1[1] << "\t" << sdDist1[1]; + if (trfr.twinKern) + { + outtraits << "\t" << mnDist2[0] << "\t" << sdDist2[0]; + outtraits << "\t" << mnDist2[1] << "\t" << sdDist2[1]; + outtraits << "\t" << mnProp1[0] << "\t" << sdProp1[0]; + outtraits << "\t" << mnProp1[1] << "\t" << sdProp1[1]; + } + } + else { // sex-independent + outtraits << "\t" << mnDist1[0] << "\t" << sdDist1[0]; + if (trfr.twinKern) + { + outtraits << "\t" << mnDist2[0] << "\t" << sdDist2[0]; + outtraits << "\t" << mnProp1[0] << "\t" << sdProp1[0]; + } + } + } + } + } + + if (sett.indVar) { + if (sett.sexDep) { // must be a sexual species + ngenes = 2; + } + else { + if (dem.repType == 0) { // asexual reproduction + ngenes = 1; + } + else { // sexual reproduction + ngenes = 1; + } + } + // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT + double mnS0[2], mnAlpha[2], mnBeta[2], sdS0[2], sdAlpha[2], sdBeta[2]; + for (int g = 0; g < ngenes; g++) { + mnS0[g] = mnAlpha[g] = mnBeta[g] = sdS0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + if (ngenes == 2) popsize = poptraits.ninds[g]; + else popsize = poptraits.ninds[0] + poptraits.ninds[1]; + if (popsize > 0) { + mnS0[g] = poptraits.sumS0[g] / (double)popsize; + mnAlpha[g] = poptraits.sumAlphaS[g] / (double)popsize; + mnBeta[g] = poptraits.sumBetaS[g] / (double)popsize; + if (popsize > 1) { + sdS0[g] = poptraits.ssqS0[g] / (double)popsize - mnS0[g] * mnS0[g]; + if (sdS0[g] > 0.0) sdS0[g] = sqrt(sdS0[g]); else sdS0[g] = 0.0; + sdAlpha[g] = poptraits.ssqAlphaS[g] / (double)popsize - mnAlpha[g] * mnAlpha[g]; + if (sdAlpha[g] > 0.0) sdAlpha[g] = sqrt(sdAlpha[g]); else sdAlpha[g] = 0.0; + sdBeta[g] = poptraits.ssqBetaS[g] / (double)popsize - mnBeta[g] * mnBeta[g]; + if (sdBeta[g] > 0.0) sdBeta[g] = sqrt(sdBeta[g]); else sdBeta[g] = 0.0; + } + else { + sdS0[g] = sdAlpha[g] = sdBeta[g] = 0.0; + } + } + } + if (writefile) { + if (sett.sexDep) { + outtraits << "\t" << mnS0[0] << "\t" << sdS0[0]; + outtraits << "\t" << mnS0[1] << "\t" << sdS0[1]; + outtraits << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outtraits << "\t" << mnAlpha[1] << "\t" << sdAlpha[1]; + outtraits << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + outtraits << "\t" << mnBeta[1] << "\t" << sdBeta[1]; + } + else { // sex-independent + outtraits << "\t" << mnS0[0] << "\t" << sdS0[0]; + outtraits << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; + outtraits << "\t" << mnBeta[0] << "\t" << sdBeta[0]; + } + } + } + + if (writefile) outtraits << endl; + + for (int s = 0; s < NSEXES; s++) { + ts.ninds[s] += poptraits.ninds[s]; + ts.sumD0[s] += poptraits.sumD0[s]; ts.ssqD0[s] += poptraits.ssqD0[s]; + ts.sumAlpha[s] += poptraits.sumAlpha[s]; ts.ssqAlpha[s] += poptraits.ssqAlpha[s]; + ts.sumBeta[s] += poptraits.sumBeta[s]; ts.ssqBeta[s] += poptraits.ssqBeta[s]; + ts.sumDist1[s] += poptraits.sumDist1[s]; ts.ssqDist1[s] += poptraits.ssqDist1[s]; + ts.sumDist2[s] += poptraits.sumDist2[s]; ts.ssqDist2[s] += poptraits.ssqDist2[s]; + ts.sumProp1[s] += poptraits.sumProp1[s]; ts.ssqProp1[s] += poptraits.ssqProp1[s]; + ts.sumDP[s] += poptraits.sumDP[s]; ts.ssqDP[s] += poptraits.ssqDP[s]; + ts.sumGB[s] += poptraits.sumGB[s]; ts.ssqGB[s] += poptraits.ssqGB[s]; + ts.sumAlphaDB[s] += poptraits.sumAlphaDB[s]; ts.ssqAlphaDB[s] += poptraits.ssqAlphaDB[s]; + ts.sumBetaDB[s] += poptraits.sumBetaDB[s]; ts.ssqBetaDB[s] += poptraits.ssqBetaDB[s]; + ts.sumStepL[s] += poptraits.sumStepL[s]; ts.ssqStepL[s] += poptraits.ssqStepL[s]; + ts.sumRho[s] += poptraits.sumRho[s]; ts.ssqRho[s] += poptraits.ssqRho[s]; + ts.sumS0[s] += poptraits.sumS0[s]; ts.ssqS0[s] += poptraits.ssqS0[s]; + ts.sumAlphaS[s] += poptraits.sumAlphaS[s]; ts.ssqAlphaS[s] += poptraits.ssqAlphaS[s]; + ts.sumBetaS[s] += poptraits.sumBetaS[s]; ts.ssqBetaS[s] += poptraits.ssqBetaS[s]; + } + } + } + return ts; +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + + diff --git a/src/RScore/SubCommunity.h b/src/RScore/SubCommunity.h new file mode 100644 index 00000000..3eef73a1 --- /dev/null +++ b/src/RScore/SubCommunity.h @@ -0,0 +1,207 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell + * + * This file is part of RangeShifter. + * + * RangeShifter is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RangeShifter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RangeShifter. If not, see . + * + --------------------------------------------------------------------------*/ + + +/*------------------------------------------------------------------------------ + +RangeShifter v2.0 SubCommunity + +Implements the SubCommunity class + +There is ONE instance of a SubCommunity for each Patch in the Landscape +(including the matrix). The SubCommunity holds a number of Populations, one for +each Species represented in the simulation. +CURRENTLY the number of Populations withn a SubCommunity is LIMITED TO ONE. + +For full details of RangeShifter, please see: +Bocedi G., Palmer S.C.F., Pe’er G., Heikkinen R.K., Matsinos Y.G., Watts K. +and Travis J.M.J. (2014). RangeShifter: a platform for modelling spatial +eco-evolutionary dynamics and species’ responses to environmental changes. +Methods in Ecology and Evolution, 5, 388-396. doi: 10.1111/2041-210X.12162 + +Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + +Last updated: 26 October 2021 by Steve Palmer + +------------------------------------------------------------------------------*/ + +#ifndef SubCommunityH +#define SubCommunityH + +#include +#include +using namespace std; + +#include "Parameters.h" +#include "Landscape.h" +#include "Population.h" + +//--------------------------------------------------------------------------- + +struct traitCanvas { // canvases for drawing variable traits + int *pcanvas[NTRAITS]; // dummy variables for batch version +}; + +class SubCommunity { + +public: + SubCommunity(Patch*,int); + ~SubCommunity(void); + intptr getNum(void); + Patch* getPatch(void); + locn getLocn(void); + + // functions to manage populations occurring in the SubCommunity + popStats getPopStats(void); + void setInitial(bool); + void initialise(Landscape*,Species*); + void initialInd(Landscape*,Species*,Patch*,Cell*,int); + Population* newPopn( // Create a new population, and return its address + Landscape*, // pointer to Landscape + Species*, // pointer to Species + Patch*, // pointer to Patch + int // no. of Individuals + ); + void resetPopns(void); + void resetPossSettlers(void); + void localExtinction( // Extirpate all populations + int // option: 0 - random local extinction probability + // 1 - local extinction probability gradient + ); + void patchChange(void); + void reproduction( + int, // Landscape resolution + float, // epsilon - global stochasticity value + short, // raster type (see Landscape) + bool // TRUE for a patch-based model, FALSE for a cell-based model + ); + void emigration(void); + // Remove emigrants from their natal patch and add to patch 0 (matrix) + void initiateDispersal( + SubCommunity* // pointer to matrix SubCommunity + ); +// Add an individual into the local population of its species in the patch + void recruit( + Individual*, // pointer to Individual + Species* // pointer to Species + ); +#if RS_RCPP + int transfer( // Transfer through matrix - run for matrix SubCommunity only + Landscape*, // pointer to Landscape + short, // landscape change index + short // season / year + ); +#else + int transfer( // Transfer through matrix - run for matrix SubCommunity only + Landscape*, // pointer to Landscape + short // landscape change index + ); +#endif // RS_RCPP + // Remove emigrants from patch 0 (matrix) and transfer to SubCommunity in which + // their destination co-ordinates fall (executed for the matrix patch only) + void completeDispersal( + Landscape*, // pointer to Landscape + bool // TRUE to increment connectivity totals + ); + void survival( + short, // part: 0 = determine survival & development, + // 1 = apply survival changes to the population + short, // option0: 0 = stage 0 (juveniles) only ) + // 1 = all stages ) used by part 0 only + // 2 = stage 1 and above (all non-juvs) ) + short // option1: 0 - development only (when survival is annual) + // 1 - development and survival + ); + void ageIncrement(void); + // Find the population of a given species in a given patch + Population* findPop(Species*,Patch*); + void createOccupancy( + int // no. of rows = (no. of years / interval) + 1 + ); + void updateOccupancy( + int // row = (no. of years / interval) + ); + int getOccupancy( + int // row = (no. of years / interval) + ); + void deleteOccupancy(void); + + bool outPopHeaders( // Open population file and write header record + Landscape*, // pointer to Landscape + Species*, // pointer to Species + int // option: -999 to close the file + ); + void outPop( // Write records to population file + Landscape*, // pointer to Landscape + int, // replicate + int, // year + int // generation + ); + + void outInds( // Write records to individuals file + Landscape*, // pointer to Landscape + int, // replicate + int, // year + int, // generation + int // Landscape number (>= 0 to open the file, -999 to close the file + // -1 to write data records) + ); + void outGenetics( // Write records to genetics file + int, // replicate + int, // year + int, // generation + int // Landscape number (>= 0 to open the file, -999 to close the file + // -1 to write data records) + ); + bool outTraitsHeaders( // Open traits file and write header record + Landscape*, // pointer to Landscape + Species*, // pointer to Species + int // Landscape number (-999 to close the file) + ); + traitsums outTraits( // Write records to traits file and return aggregated sums + traitCanvas, // pointers to canvases for drawing variable traits + // in the batch version, these are replaced by integers set to zero + Landscape*, // pointer to Landscape + int, // replicate + int, // year + int, // generation + bool // true if called to summarise data at community level + ); + int stagePop( // Population size of a specified stage + int // stage + ); + +private: + intptr subCommNum; // SubCommunity number + // 0 is reserved for the SubCommunity in the inter-patch matrix + Patch *pPatch; + int *occupancy; // pointer to occupancy array + std::vector popns; + bool initial; // WILL NEED TO BE CHANGED FOR MULTIPLE SPECIES ... + +}; + +extern paramGrad *paramsGrad; +extern paramStoch *paramsStoch; +extern paramInit *paramsInit; + +//--------------------------------------------------------------------------- +#endif diff --git a/src/RScore/Utils.cpp b/src/RScore/Utils.cpp new file mode 100644 index 00000000..9d36b920 --- /dev/null +++ b/src/RScore/Utils.cpp @@ -0,0 +1,26 @@ +#include "Utils.h" + +const string Int2Str(const int x) +{ + ostringstream o; + if (!(o << x)) return "ERROR"; + return o.str(); +} +const string Float2Str(const float x) { + ostringstream o; + if (!(o << x)) return "ERROR"; + return o.str(); +} +const string Double2Str(const double x) { + ostringstream o; + if (!(o << x)) return "ERROR"; + return o.str(); +} + +// Evaluate a lambda and assert we get the correct error +void assert_error(const string& exptd_err_msg, void (*lambda)(void)) { + string err_msg{ "No error.\n" }; + try { lambda(); } // evaluate + catch (exception& e) { err_msg = e.what(); } + assert(err_msg == exptd_err_msg); +} \ No newline at end of file diff --git a/src/RScore/Utils.h b/src/RScore/Utils.h new file mode 100644 index 00000000..34854a76 --- /dev/null +++ b/src/RScore/Utils.h @@ -0,0 +1,18 @@ +#ifndef UtilsH +#define UtilsH + +#include +#include +#include + +#include +using namespace std; + +const string Int2Str(const int x); +const string Float2Str(const float x); +const string Double2Str(const double x); + +// Evaluate a lambda and assert we get the correct error +void assert_error(const string& exptd_err_msg, void (*x)(void)); + +#endif // UtilsH diff --git a/src/RScore/branches.png b/src/RScore/branches.png new file mode 100644 index 00000000..12e3a96e Binary files /dev/null and b/src/RScore/branches.png differ diff --git a/src/RScore/git_cheatsheet.md b/src/RScore/git_cheatsheet.md new file mode 100644 index 00000000..53ca1750 --- /dev/null +++ b/src/RScore/git_cheatsheet.md @@ -0,0 +1,107 @@ +# Git Cheatsheet + +Quick reference on Git usage for RangeShifter contributors + +#### Creating a local copy of this repo + +```bash +git clone https://github.com/RangeShifter/RScore.git +``` + +#### Enquire about the current state of changes + +``` +git status +``` +This will display whether the local branch is up-to-date with its GitHub counterpart, what files have been changed and if they are staged for commit or not. + +#### Updating the active branch with latest changes (pulling) + +```bash +git pull +``` + +#### Updating the active branch with another branch (merging) + +```bash +git merge +``` + +Merging may trigger a merge conflict is the same lines have been changed on both branches. See Solving Merge Conflict below. + +#### Staging changes to be committed +Changes to local files must first be added to the commit queue ("staged") before being committed. + +```bash +git add # single file +git add . # entire active folder +``` + +#### Creating a commit + +```bash +git commit -m "commit message" +``` + +A good commit message is concise, but descriptive. + +#### Uploading local commits to GitHub (pushing) + +```bash +git push +``` + +#### Switching to an existing branch + +```bash +git checkout +``` +Switching does not update the new active branch with GitHub automatically, so make sure to pull after switching! + +#### Creating a new branch from active branch + +```bash +git branch +``` +You will also need to set the corresponding branch on `origin` (GitHub) before you can push: + +```bash +git push --set-upstream origin +``` + +New branches can also be created on GitHub (drop-down button in the top-left corner of the main page). +New branches on GitHub are brought to the local copy with the next pull. + +#### Deleting a branch locally + +```bash +git branch -d +``` + +#### Instruct Git to not track some files + +Open `.gitignore` and add the path to the files or folders to exclude from the git history. +Check with `git status` that the files are indeed not tracked by git anymore. + +#### Solving a merge conflict +Merge conflicts can arise when multiple contributors simulatneously change the same line of code. +In such cases, git is incapable of deciding which version should be kept and asks for human input. +Git tells you which files are affected by the conflict. +Open each file and resolve **each** section that looks like this: + +``` +After opening the box, we can affirm that +<<<<<<<<<<< HEAD # delete this line +Shroedinger's cat is alive. # delete either this... +================ # delete this line +Shroedinger's cat is dead. # ... or this (or keep both, or none, or a different solution) +>>>>>>>>>>> SHA # delete this line +What an insightful result! +``` + +Ctrl+F "HEAD" is really helpful for finding the conflicts in large files. +When you are done, create a commit stating e.g. "solved merge conflict" and push. + +## Git subtrees + +See [Git subtrees](https://github.com/RangeShifter/RScore/tree/development-guidelines#usage-git-subtree) section in README.