diff --git a/.gitignore b/.gitignore index 9bd72d2..c4a4ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ build-Release/ *.workspace *.mk *.tags +/build/ # Hidden source /RangeShiftR/src/.* diff --git a/Allele.h b/Allele.h new file mode 100644 index 0000000..fa4ad00 --- /dev/null +++ b/Allele.h @@ -0,0 +1,13 @@ +#ifndef ALLELEH +#define ALLELEH + +class Allele { + const float value; + const float dominance; +public: + Allele(float alleleValue, float alleleDominance) : value(alleleValue), dominance(alleleDominance) { } + ~Allele() {} + float getAlleleValue() const { return value; }; + float getDominanceCoef() const { return dominance; }; +}; +#endif \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b8b1679..2cc4d90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,13 +3,18 @@ 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) + project(RScore VERSION 3.0.0) # specify the C++ standard - set(CMAKE_CXX_STANDARD 17) + + set(CMAKE_CXX_STANDARD 20) 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) + + add_executable(RScore Main.cpp Species.cpp Cell.cpp Community.cpp FractalGenerator.cpp GeneticFitnessTrait.cpp Individual.cpp Landscape.cpp Management.cpp Model.cpp NeutralStatsManager.cpp Parameters.cpp Patch.cpp Population.cpp DispersalTrait.cpp RSrandom.cpp NeutralTrait.cpp SpeciesTrait.cpp SubCommunity.cpp Utils.cpp "unit_tests/testIndividual.cpp" "unit_tests/testNeutralStats.cpp" "unit_tests/testPopulation.cpp") + # turn on unit tests + add_compile_definitions("UNIT_TESTS") 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) + + add_library(RScore Species.cpp Cell.cpp Community.cpp FractalGenerator.cpp GeneticFitnessTrait.cpp Individual.cpp Landscape.cpp Management.cpp Model.cpp NeutralStatsManager.cpp Parameters.cpp Patch.cpp Population.cpp DispersalTrait.cpp RSrandom.cpp NeutralTrait.cpp SpeciesTrait.cpp SubCommunity.cpp Utils.cpp) endif() if(OMP) @@ -19,18 +24,11 @@ if(OMP) endif() 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}") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e1b62ff..ec76e62 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# The RangeShifter platform - An eco-evolutionary modelling framework +# The RangeShifter platform - An eco-evolutionary modelling framework ## How to contribute @@ -17,7 +17,7 @@ RangeShifter is distributed with three user interfaces, each living in their own 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). +⚠ 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. @@ -40,10 +40,13 @@ Anyone who whishes to make changes to RangeShifter's code, including regular dev ## Branching policy +<<<<<<<< HEAD:src/RScore/CONTRIBUTING.md ![](branches.png) *Check out the [Git cheatsheet](https://github.com/RangeShifter/RScore/blob/main/git_cheatsheet.md) for a reminder on the main git commands* +======== +>>>>>>>> develop:CONTRIBUTING.md This policy applies to RScore and all three RangeShifter interfaces. RangeShifter uses the following branching structure: @@ -61,12 +64,17 @@ In the meantime, we encourage contributors to work in small and frequent commits 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/main?tab=readme-ov-file#usage-git-subtree) 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-dev/issues) for the batch interface or [here](https://github.com/RangeShifter/RangeShiftR-package-dev) for the R package interface). +To report a bug, please [open an issue](https://github.com/RangeShifter/RangeShiftR-package/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) 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. +As a prerequisite for merging, please ensure that your version passes status check (that is, RScore can still build, and all unit tests are still satisfied). +This can be seen in the Actions panel for every commit and at the bottom of the pull request. + 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 @@ -79,3 +87,5 @@ Please get in touch with the RangeShifter development team (rangeshiftr@uni-pots 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. + +*Do we welcome independent contributions? diff --git a/Cell.cpp b/Cell.cpp index 8eefd31..2e0ffd3 100644 --- a/Cell.cpp +++ b/Cell.cpp @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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" @@ -32,81 +32,80 @@ Cell::Cell(int xx,int yy,Patch *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); -visits = 0; -smsData = 0; + x = xx; y = yy; + pPatch = patch; + envVal = 1.0; // default - no effect of any gradient + envDev = eps = 0.0; + habIxx.push_back(hab); + visits = 0; + smsData = 0; } Cell::Cell(int xx,int yy,Patch *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; + 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; -} + habIxx.clear(); + habitats.clear(); + if (smsData != 0) { + if (smsData->effcosts != 0) delete smsData->effcosts; + delete smsData; + } +demoScalings.clear(); + #if RSDEBUG //DEBUGLOG << "Cell::~Cell(): deleted" << endl; #endif } void Cell::setHabIndex(short hx) { -if (hx < 0) habIxx.push_back(0); -else habIxx.push_back(hx); + 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; + 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]; + 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; + 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); + 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]; + 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(Patch *p) { -pPatch = p; + pPatch = p; } Patch *Cell::getPatch(void) { -return pPatch; + return pPatch; } locn Cell::getLocn(void) { locn q; q.x = x; q.y = y; return q; } @@ -116,13 +115,13 @@ void Cell::setEnvDev(float d) { envDev = d; } float Cell::getEnvDev(void) { return envDev; } void Cell::setEnvVal(float e) { -if (e >= 0.0) envVal = e; + if (e >= 0.0) envVal = e; } float Cell::getEnvVal(void) { return envVal; } void Cell::updateEps(float ac,float randpart) { -eps = eps*ac + randpart; + eps = eps * ac + randpart; } float Cell::getEps(void) { return eps; } @@ -136,65 +135,84 @@ std::unique_lock Cell::lockCost() { #endif int Cell::getCost(void) { -int c; -if (smsData == 0) c = 0; // costs not yet set up -else c = smsData->cost; -return c; + 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; + 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; + 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; + 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; + else + a = *smsData->effcosts; + return a; } -void Cell::setEffCosts(array3x3f a) { -if (smsData->effcosts == 0) smsData->effcosts = new array3x3f; -*smsData->effcosts = 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; + 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; } + +void Cell::addchgDemoScaling(std::vector ds) { + std::for_each(ds.begin(), ds.end(), [](float& perc){ if(perc < 0.0 || perc > 100.0) perc=100; }); + demoScalings.push_back(ds); + return; +} + +std::vector Cell::getDemoScaling(short chgyear) { + if (chgyear < 0 || chgyear >= (int)demoScalings.size()) { + std::vector ret(1, -1); + return ret; + } + else return demoScalings[chgyear]; +} + + + //--------------------------------------------------------------------------- // Initial species distribution cell functions DistCell::DistCell(int xx,int yy) { -x = xx; y = yy; initialise = false; + x = xx; + y = yy; + initialise = false; } DistCell::~DistCell() { @@ -202,18 +220,21 @@ DistCell::~DistCell() { } void DistCell::setCell(bool init) { -initialise = init; + initialise = init; } bool DistCell::toInitialise(locn loc) { -if (loc.x == x && loc.y == y) return initialise; -else return false; + 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; + locn loc; + loc.x = x; + loc.y = y; + return loc; } //--------------------------------------------------------------------------- diff --git a/Cell.h b/Cell.h index 5a15c9d..294671e 100644 --- a/Cell.h +++ b/Cell.h @@ -1,50 +1,53 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 + RangeShifter v2.0 Cell -DistCell - Initial species distribution cell + Implements the following classes: -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 + Cell - Landscape cell -Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + DistCell - Initial species distribution cell -Last updated: 14 January 2021 by Steve Palmer + 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: 14 January 2021 by Steve Palmer + + ------------------------------------------------------------------------------*/ #ifndef CellH #define CellH + +#include + #include using namespace std; @@ -60,11 +63,11 @@ using namespace std; class Patch; // Forward-declaration of the Patch class struct array3x3f { float cell[3][3]; }; // neighbourhood cell array (SMS) -struct smscosts { int cost; array3x3f *effcosts; }; // cell costs for SMS +struct smscosts { int cost; array3x3f* effcosts; }; // cell costs for SMS // Landscape cell -class Cell{ +class Cell { public: Cell( // Constructor for habitat codes int, // x co-ordinate @@ -131,13 +134,18 @@ class Cell{ void incrVisits(void); unsigned long int getVisits(void); + void addchgDemoScaling(std::vector); + void setDemoScaling(std::vector, short); + std::vector getDemoScaling(short); + + private: - int x,y; // cell co-ordinates + int x, y; // cell co-ordinates Patch *pPatch; // pointer 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 + // 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)) #ifdef _OPENMP @@ -145,13 +153,15 @@ class Cell{ #else unsigned long int visits; // no. of times square is visited by dispersers #endif - smscosts *smsData; + smscosts* smsData; vector habIxx; // habitat indices (rasterType=0) - // NB initially, habitat codes are loaded, then converted to index nos. - // once landscape is fully loaded + // 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) + std::vector> demoScalings; // demographic scaling layers (only if rasterType==2) + #ifdef _OPENMP std::mutex cost_mutex; #endif @@ -161,7 +171,7 @@ class Cell{ // Initial species distribution cell -class DistCell{ +class DistCell { public: DistCell( int, // x co-ordinate @@ -178,7 +188,7 @@ class DistCell{ locn getLocn(void); private: - int x,y; // cell co-ordinates + int x, y; // cell co-ordinates bool initialise; // cell is to be initialised }; diff --git a/Community.cpp b/Community.cpp index 7ce7835..e7f4fb4 100644 --- a/Community.cpp +++ b/Community.cpp @@ -24,18 +24,41 @@ #include "Community.h" +#ifdef _OPENMP +#ifdef __has_include +#if __has_include() +#include +#endif +#endif +#include +#if __cpp_lib_barrier >= 201907L && __cpp_lib_optional >= 201606L +#define HAS_BARRIER_LIB +#include +#include +#else +#include +#include +#endif +#include +#endif // _OPENMP + //--------------------------------------------------------------------------- ofstream outrange; ofstream outoccup, outsuit; ofstream outtraitsrows; +ofstream ofsGenes; +ofstream outwcfstat; +ofstream outperlocusfstat; +ofstream outpairwisefst; //--------------------------------------------------------------------------- Community::Community(Landscape* pLand) { pLandscape = pLand; indIx = 0; + pNeutralStatistics = 0; } Community::~Community(void) { @@ -54,7 +77,6 @@ SubCommunity* Community::addSubComm(Patch* pPch, int num) { void Community::initialise(Species* pSpecies, int year) { - int nsubcomms, npatches, ndistcells, spratio, patchnum, rr = 0; locn distloc; patchData pch; @@ -71,16 +93,6 @@ void Community::initialise(Species* pSpecies, int year) 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 @@ -240,7 +252,8 @@ void Community::initialise(Species* pSpecies, int year) indIx = 0; // reset index for initial individuals } else { // add any initial individuals for the current year - initInd iind; iind.year = 0; + initInd iind = initInd(); + iind.year = 0; int ninds = paramsInit->numInitInds(); while (indIx < ninds && iind.year <= year) { iind = paramsInit->getInitInd(indIx); @@ -295,12 +308,6 @@ void Community::initialise(Species* pSpecies, int year) } // 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 @@ -314,10 +321,6 @@ void Community::addManuallySelected(void) { 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++) { @@ -339,26 +342,10 @@ void Community::addManuallySelected(void) { pCell = pLandscape->findCell(initloc.x, initloc.y); if (pCell != 0) { // not no-data cell pPatch = pCell->getPatch(); -#if RSDEBUG - DEBUGLOG << "Community::initialise(): i = " << i - << " x = " << initloc.x << " y = " << initloc.y - << " pCell = " << pCell << " patch = " << (intptr)pPatch - << endl; -#endif if (pPatch != nullptr) { pSubComm = pPatch->getSubComm(); -#if RSDEBUG - DEBUGLOG << "Community::initialise(): i = " << i - << " pPatch = " << pPatch << " subcomm = " << (intptr)pSubComm - << endl; -#endif if (pSubComm != nullptr) { pSubComm->setInitial(true); -#if RSDEBUG - DEBUGLOG << "Community::initialise(): i = " << i - << " pSubComm = " << pSubComm - << endl; -#endif } } } @@ -404,42 +391,86 @@ void Community::reproduction(int yr) eps = pLandscape->getGlobalStoch(yr); } int nsubcomms = (int)subComms.size(); -#if RSDEBUG - DEBUGLOG << "Community::reproduction(): this=" << this - << " nsubcomms=" << nsubcomms << endl; -#endif #pragma omp parallel for schedule(static,128) for (int i = 0; i < nsubcomms; i++) { // all sub-communities 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 + int nsubcomms = static_cast(subComms.size()); #pragma omp parallel for schedule(static, 128) 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 +#ifdef _OPENMP +#ifdef HAS_BARRIER_LIB +typedef std::optional> split_barrier; +#else +class split_barrier { +private: + std::mutex m; + std::condition_variable cv; + int threads_in_section; + int total_threads; + bool may_enter; + bool may_leave; + +public: + split_barrier(): + threads_in_section(0), + may_enter(false), + may_leave(false) + {} + + void emplace(int threads) { + std::lock_guard lock(m); + total_threads = threads; + may_enter = true; + } + + void enter() { + std::unique_lock lock(m); + cv.wait(lock, [this]{return may_enter;}); + if (++threads_in_section == total_threads) { + may_enter = false; + may_leave = true; + lock.unlock(); + cv.notify_all(); + } + } + + void leave() { + std::unique_lock lock(m); + cv.wait(lock, [this]{return may_leave;}); + if (--threads_in_section == 0) { + may_leave = false; + may_enter = true; + lock.unlock(); + cv.notify_all(); + } + } +}; +#endif // HAS_BARRIER_LIB +#endif // _OPENMP + +#if RS_RCPP void Community::dispersal(short landIx, short nextseason) #else void Community::dispersal(short landIx) -#endif // SEASONAL || RS_RCPP +#endif // RS_RCPP { +#ifdef _OPENMP + std::atomic nbStillDispersing; + split_barrier barrier; +#else + int nbStillDispersing; +#endif // _OPENMP + #if RSDEBUG int t0, t1, t2; t0 = time(0); @@ -452,43 +483,69 @@ void Community::dispersal(short landIx) SubCommunity* matrix = subComms[0]; // matrix community is always the first #pragma omp parallel { - std::map> inds_map; + std::map> disperserPool; + + // All individuals in the matrix disperse again + // (= unsettled dispersers from previous generation) + matrix->disperseMatrix(disperserPool); + + // Recruit new emigrants #pragma omp for schedule(static,128) nowait - for (int i = 0; i < nsubcomms; i++) { // all populations - subComms[i]->initiateDispersal(inds_map); - } - for (std::pair>& item : inds_map) { - // add to matrix population - matrix->recruitMany(item.second, item.first); - } + for (int i = 0; i < nsubcomms; i++) { + subComms[i]->recruitDispersers(disperserPool); } -#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; +#ifdef _OPENMP +#pragma omp single + barrier.emplace(omp_get_num_threads()); +#endif // _OPENMP + + + // do { - #pragma omp parallel for schedule(static) - for (int i = 0; i < nsubcomms; i++) { // all populations +#pragma omp for schedule(guided) + for (int i = 0; i < nsubcomms; i++) { subComms[i]->resetPossSettlers(); } -#if RS_RCPP // included also SEASONAL - ndispersers = matrix->transfer(pLandscape, landIx, nextseason); + int localNbDispersers = matrix->resolveTransfer(disperserPool, pLandscape, landIx); +#pragma omp single nowait + nbStillDispersing = 0; +#pragma omp barrier +#if RS_RCPP + localNbDispersers += matrix->resolveSettlement(disperserPool, pLandscape, nextseason); #else - ndispersers = matrix->transfer(pLandscape, landIx); -#endif // SEASONAL || RS_RCPP - matrix->completeDispersal(pLandscape, sim.outConnect); - } while (ndispersers > 0); + localNbDispersers += matrix->resolveSettlement(disperserPool, pLandscape); +#endif // RS_RCPP + nbStillDispersing += localNbDispersers; + +#ifdef _OPENMP +#ifdef HAS_BARRIER_LIB + std::barrier<>::arrival_token token = barrier->arrive(); +#else + barrier.enter(); +#endif // HAS_BARRIER_LIB +#endif // _OPENMP + + matrix->completeDispersal(disperserPool, pLandscape, sim.outConnect); + +#ifdef _OPENMP +#ifdef HAS_BARRIER_LIB + barrier->wait(std::move(token)); +#else + barrier.leave(); +#endif // HAS_BARRIER_LIB +#endif // _OPENMP + + } while (nbStillDispersing > 0); + + // All unsettled dispersers are stored in matrix until next generation + for (auto & it : disperserPool) { + Species* const& pSpecies = it.first; + vector& inds = it.second; + matrix->recruitMany(inds, pSpecies); + } + } -#if RSDEBUG - DEBUGLOG << "Community::dispersal(): matrix=" << matrix << endl; - t2 = time(0); - DEBUGLOG << "Community::dispersal(): transfer time=" << t2 - t1 << endl; -#endif } @@ -557,17 +614,12 @@ void Community::createOccupancy(int nrows, int reps) { 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) { @@ -587,7 +639,7 @@ void Community::deleteOccupancy(int nrows) { // Determine range margins commStats Community::getStats(void) { - commStats s; + commStats s = commStats(); 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; @@ -641,7 +693,6 @@ void Community::outPop(int rep, int yr, int gen) } - // Close individuals file void Community::outIndsFinishReplicate() { subComms[0]->outIndsFinishReplicate(); @@ -661,33 +712,13 @@ void Community::outIndividuals(int rep, int yr, int gen) { } } -// Close genetics file -void Community::outGenFinishReplicate() { - subComms[0]->outGenFinishReplicate(); -} - -// Open genetics file and write header record -void Community::outGenStartReplicate(int rep, int landNr) { - subComms[0]->outGenStartReplicate(rep, landNr); -} - -// Write records to genetics file -void Community::outGenetics(int rep, int yr) { - //landParams ppLand = pLandscape->getLandParams(); - // 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); - } -} - // Close range file bool Community::outRangeFinishLandscape() { - if (outrange.is_open()) outrange.close(); - outrange.clear(); - return true; -} + if (outrange.is_open()) outrange.close(); + outrange.clear(); + return true; + } // Open range file and write header record bool Community::outRangeStartLandscape(Species* pSpecies, int landNr) @@ -699,29 +730,17 @@ bool Community::outRangeStartLandscape(Species* pSpecies, int landNr) // 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); 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" + to_string(sim.batchNum) + "_" - + "Sim" + to_string(sim.simulation) + "_Land" - + to_string(landNr) -#else + "Batch" + to_string(sim.batchNum) + "_" + "Sim" + to_string(sim.simulation) + "_Land" + to_string(landNr) -#endif + "_Range.txt"; } else { @@ -762,7 +781,7 @@ bool Community::outRangeStartLandscape(Species* pSpecies, int landNr) } } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 1) { outrange << "\tmeanDP\tstdDP\tmeanGB\tstdGB"; outrange << "\tmeanAlphaDB\tstdAlphaDB\tmeanBetaDB\tstdBetaDB"; @@ -800,31 +819,21 @@ bool Community::outRangeStartLandscape(Species* pSpecies, int landNr) } } 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); outrange << rep << "\t" << yr << "\t" << gen; @@ -841,14 +850,14 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) 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); + stagepop += subComms[i]->getNbInds(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); + stagepop += subComms[i]->getNbInds(0); } outrange << "\t" << stagepop; } @@ -871,11 +880,11 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) outrange << "\t0\t0\t0\t0"; if (emig.indVar || trfr.indVar || sett.indVar) { // output trait means - traitsums ts; + traitsums ts = traitsums(); traitsums scts; // sub-community traits int ngenes, popsize; - for (int i = 0; i < NSEXES; i++) { + for (int i = 0; i < gMaxNbSexes; 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; @@ -893,7 +902,7 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) int nsubcomms = (int)subComms.size(); for (int i = 0; i < nsubcomms; i++) { // all sub-communities (incl. matrix) scts = subComms[i]->outTraits(pLandscape, rep, yr, gen, true); - for (int j = 0; j < NSEXES; j++) { + for (int j = 0; j < gMaxNbSexes; 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]; @@ -969,7 +978,7 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { // CURRENTLY INDIVIDUAL VARIATION CANNOT BE SEX-DEPENDENT ngenes = 1; } @@ -1026,7 +1035,7 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) } } } - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 1) { outrange << "\t" << mnDP[0] << "\t" << sdDP[0]; outrange << "\t" << mnGB[0] << "\t" << sdGB[0]; @@ -1121,11 +1130,11 @@ void Community::outRange(Species* pSpecies, int rep, int yr, int gen) // Close occupancy file bool Community::outOccupancyFinishLandscape() { - if (outsuit.is_open()) outsuit.close(); - if (outoccup.is_open()) outoccup.close(); - outsuit.clear(); outoccup.clear(); - return true; -} + if (outsuit.is_open()) outsuit.close(); + if (outoccup.is_open()) outoccup.close(); + outsuit.clear(); outoccup.clear(); + return true; + } // Open occupancy file, write header record and set up occupancy array bool Community::outOccupancyStartLandscape() @@ -1193,9 +1202,10 @@ void Community::outOccupancy(void) { } } -void Community::outOccSuit(bool view) { +void Community::outOccSuit() { 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++) { @@ -1207,6 +1217,7 @@ void Community::outOccSuit(bool view) { 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; } @@ -1231,7 +1242,6 @@ order of y void Community::outTraits(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; @@ -1239,7 +1249,7 @@ void Community::outTraits(Species* pSpecies, int rep, int yr, int gen) // 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++) { + for (int i = 0; i < gMaxNbSexes; 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; @@ -1252,12 +1262,12 @@ void Community::outTraits(Species* pSpecies, int rep, int yr, int gen) 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; + ts[y].sumGeneticFitness[i] = ts[y].ssqGeneticFitness[i] = 0.0; } } } - if (v.viewTraits - || ((sim.outTraitsCells && yr >= sim.outStartTraitCell && yr % sim.outIntTraitCell == 0) || - (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0))) + if ((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(); @@ -1267,7 +1277,7 @@ void Community::outTraits(Species* pSpecies, int rep, int yr, int gen) int y = loc.y; if (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0) { - for (int s = 0; s < NSEXES; s++) { + for (int s = 0; s < gMaxNbSexes; 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]; @@ -1280,6 +1290,7 @@ void Community::outTraits(Species* pSpecies, int rep, int yr, int gen) 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]; + ts[y].sumGeneticFitness[s] += sctraits.sumGeneticFitness[s]; ts[y].ssqGeneticFitness[s] += sctraits.ssqGeneticFitness[s]; } } } @@ -1299,8 +1310,8 @@ void Community::outTraits(Species* pSpecies, int rep, int yr, int gen) void Community::writeTraitsRows(Species* pSpecies, int rep, int yr, int gen, int y, traitsums ts) { - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); double mn, sd; @@ -1362,7 +1373,7 @@ void Community::writeTraitsRows(Species* pSpecies, int rep, int yr, int gen, int } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 2) { // CRW // NB - CURRENTLY CANNOT BE SEX-DEPENDENT... if (popsize > 0) mn = ts.sumStepL[0] / (double)popsize; else mn = 0.0; @@ -1438,24 +1449,41 @@ void Community::writeTraitsRows(Species* pSpecies, int rep, int yr, int gen, int 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; - // } } + if (pSpecies->getNbGenLoadTraits() > 0) { + if (gMaxNbSexes > 1) { + if (ts.ninds[0] > 0) mn = ts.sumGeneticFitness[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqGeneticFitness[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.sumGeneticFitness[1] / (double)ts.ninds[1]; else mn = 0.0; + if (ts.ninds[1] > 1) sd = ts.ssqGeneticFitness[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 { + if (ts.ninds[0] > 0) mn = ts.sumGeneticFitness[0] / (double)ts.ninds[0]; else mn = 0.0; + if (ts.ninds[0] > 1) sd = ts.ssqGeneticFitness[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; + } + } outtraitsrows << endl; } // Close trait rows file bool Community::outTraitsRowsFinishLandscape() { - if (outtraitsrows.is_open()) outtraitsrows.close(); - outtraitsrows.clear(); - return true; -} + if (outtraitsrows.is_open()) outtraitsrows.close(); + outtraitsrows.clear(); + return true; + } // Open trait rows file and write header record bool Community::outTraitsRowsStartLandscape(Species* pSpecies, int landNr) { string name; - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); simParams sim = paramsSim->getSim(); @@ -1498,7 +1526,7 @@ bool Community::outTraitsRowsStartLandscape(Species* pSpecies, int landNr) { } } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 2) { outtraitsrows << "\tmeanStepLength\tstdStepLength\tmeanRho\tstdRho"; } @@ -1520,10 +1548,26 @@ bool Community::outTraitsRowsStartLandscape(Species* pSpecies, int landNr) { } if (sett.indVar) { + // if (sett.sexDep) { + // outtraitsrows << "\tF_meanS0\tF_stdS0\tM_meanS0\tM_stdS0"; + // outtraitsrows << "\tF_meanAlphaS\tF_stdAlphaS\tM_meanAlphaS\tM_stdAlphaS"; + // outtraitsrows << "\tF_meanBetaS\tF_stdBetaS\tM_meanBetaS\tM_stdBetaS"; + // } + // else { outtraitsrows << "\tmeanS0\tstdS0"; outtraitsrows << "\tmeanAlphaS\tstdAlphaS"; outtraitsrows << "\tmeanBetaS\tstdBetaS"; + // } + } + + if (pSpecies->getNbGenLoadTraits() > 0) { + if (gMaxNbSexes > 1) { + outtraitsrows << "\tF_meanProbViable\tF_stdProbViable\tM_meanProbViable\tM_stdProbViable"; + } + else + outtraitsrows << "\tmeanProbViable\tstdProbViable"; } + outtraitsrows << endl; return outtraitsrows.is_open(); @@ -1542,7 +1586,7 @@ Rcpp::IntegerMatrix Community::addYearToPopList(int rep, int yr) { // TODO: def for (int y = 0; y < ppLand.dimY; y++) { for (int x = 0; x < ppLand.dimX; x++) { - Cell* pCell = pLandscape->findCell(x, y); + Cell* pCell = pLandscape->findCell(x, y); //if (pLandscape->cells[y][x] == 0) { if (pCell == 0) { // no-data cell pop_map_year(ppLand.dimY - 1 - y, x) = NA_INTEGER; } @@ -1559,15 +1603,391 @@ Rcpp::IntegerMatrix Community::addYearToPopList(int rep, int yr) { // TODO: def else { 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 + //pop_map_year(ppLand.dimY-1-y,x) = pop.nAdults; } } } } } + //list_outPop.push_back(pop_map_year, "rep" + std::to_string(rep) + "_year" + std::to_string(yr)); return pop_map_year; } #endif +bool Community::openOutGenesFile(const bool& isDiploid, const int landNr, const int rep) +{ + if (landNr == -999) { // close the file + if (ofsGenes.is_open()) { + ofsGenes.close(); + ofsGenes.clear(); + } + return true; + } + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + to_string(sim.batchNum) + "_" + + "Sim" + to_string(sim.simulation) + "_Land" + + to_string(landNr) + "_Rep" + to_string(rep) + + "_geneValues.txt"; + } + else { + name = paramsSim->getDir(2) + + "Sim" + to_string(sim.simulation) + "_Land" + + to_string(landNr) + "_Rep" + to_string(rep) + + "_geneValues.txt"; + } + + ofsGenes.open(name.c_str()); + ofsGenes << "Year\tGeneration\tIndID\ttraitType\tlocusPosition" + << "\talleleValueA\tdomCoefA"; + if (isDiploid) ofsGenes << "\talleleValueB\tdomCoefB"; + ofsGenes << endl; + + return ofsGenes.is_open(); +} + +void Community::outputGeneValues(const int& year, const int& gen, Species* pSpecies) { + if (!ofsGenes.is_open()) + throw runtime_error("Could not open output gene values file."); + + const set patchList = pSpecies->getSamplePatches(); + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + if (patch == 0) { + throw runtime_error("Sampled patch does not exist"); + } + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + pPop->outputGeneValues(ofsGenes, year, gen); + } + } +} + +// ---------------------------------------------------------------------------------------- +// Sample individuals from sample patches +// ---------------------------------------------------------------------------------------- + +void Community::sampleIndividuals(Species* pSpecies) { + + const set patchList = pSpecies->getSamplePatches(); + string nbIndsToSample = pSpecies->getNIndsToSample(); + const set stagesToSampleFrom = pSpecies->getStagesToSample(); + + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + if (patch == 0) { + throw runtime_error("Can't sample individuals: patch" + to_string(patchId) + "doesn't exist."); + } + auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + pPop->sampleIndsWithoutReplacement(nbIndsToSample, stagesToSampleFrom); + } + } +} + +// ---------------------------------------------------------------------------------------- +// Open population level Fstat output file +// ---------------------------------------------------------------------------------------- + +bool Community::openNeutralOutputFile(Species* pSpecies, int landNr) +{ + if (landNr == -999) { // close the file + if (outwcfstat.is_open()) outwcfstat.close(); + outwcfstat.clear(); + return true; + } + + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + to_string(sim.batchNum) + "_" + + "Sim" + to_string(sim.simulation) + "_Land" + + to_string(landNr) + + "_neutralGenetics.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) + "_neutralGenetics.txt"; + } + outwcfstat.open(name.c_str()); + outwcfstat << "Rep\tYear\tRepSeason\tnExtantPatches\tnIndividuals\tFstWC\tFisWC\tFitWC\tFstWH\tmeanAllelePerLocus\tmeanAllelePerLocusPatches\tmeanFixedLoci\tmeanFixedLociPatches\tmeanObHeterozygosity"; + outwcfstat << endl; + + return outwcfstat.is_open(); +} + +// ---------------------------------------------------------------------------------------- +// open per locus WC fstat using MS approach, this will output MS calculated FIS, FIT, FST +// in general population neutral genetics output file +// ---------------------------------------------------------------------------------------- + +bool Community::openPerLocusFstFile(Species* pSpecies, Landscape* pLandscape, const int landNr, const int rep) +{ + set patchList; + string samplingOpt = paramsSim->getSim().patchSamplingOption; + if (samplingOpt == "list" + || samplingOpt == "random" + || (samplingOpt == "all" && !pLandscape->getLandParams().dynamic) + ) // list of patches always the same + patchList = pSpecies->getSamplePatches(); + else { // random_occupied or all with dynamic landscape + // then sampled patches may change every year, + // so produce an entry for all patches + patchList = pLandscape->getPatchNbs(); + } + + if (landNr == -999) { // close the file + if (outperlocusfstat.is_open()) outperlocusfstat.close(); + outperlocusfstat.clear(); + return true; + } + + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + to_string(sim.batchNum) + "_" + + "Sim" + to_string(sim.simulation) + "_Land" + + to_string(landNr) + "_Rep" + + to_string(rep) + + "_perLocusNeutralGenetics.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) + "_Rep" + to_string(rep) + "_perLocusNeutralGenetics.txt"; + } + + outperlocusfstat.open(name.c_str()); + outperlocusfstat << "Year\tRepSeason\tLocus\tFst\tFis\tFit\tHet"; + for (int patchId : patchList) { + outperlocusfstat << "\tpatch_" + to_string(patchId) + "_Het"; + } + outperlocusfstat << endl; + + return outperlocusfstat.is_open(); +} + +// ---------------------------------------------------------------------------------------- +// open pairwise fst file +// ---------------------------------------------------------------------------------------- + +bool Community::openPairwiseFstFile(Species* pSpecies, Landscape* pLandscape, const int landNr, const int rep) { + + const set patchList = pSpecies->getSamplePatches(); + + if (landNr == -999) { // close the file + if (outpairwisefst.is_open()) outpairwisefst.close(); + outpairwisefst.clear(); + return true; + } + + string name; + simParams sim = paramsSim->getSim(); + + if (sim.batchMode) { + name = paramsSim->getDir(2) + + "Batch" + to_string(sim.batchNum) + "_" + + "Sim" + to_string(sim.simulation) + "_Land" + + to_string(landNr) + "_Rep" + + to_string(rep) + + "_pairwisePatchNeutralGenetics.txt"; + } + else { + name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) + "_Rep" + to_string(rep) + "_pairwisePatchNeutralGenetics.txt"; + } + outpairwisefst.open(name.c_str()); + outpairwisefst << "Year\tRepSeason\tpatchA\tpatchB\tFst"; + outpairwisefst << endl; + + return outpairwisefst.is_open(); +} + +// ---------------------------------------------------------------------------------------- +// Write population level FST results file +// ---------------------------------------------------------------------------------------- + +void Community::writeNeutralOutputFile(int rep, int yr, int gen, bool outWeirCockerham, bool outWeirHill) { + + outwcfstat << rep << "\t" << yr << "\t" << gen << "\t"; + outwcfstat << pNeutralStatistics->getNbPopulatedSampledPatches() + << "\t" << pNeutralStatistics->getTotalNbSampledInds() << "\t"; + + if (outWeirCockerham) { + outwcfstat << pNeutralStatistics->getFstWC() << "\t" + << pNeutralStatistics->getFisWC() << "\t" + << pNeutralStatistics->getFitWC() << "\t"; + } + else outwcfstat << "NA" << "\t" << "NA" << "\t" << "NA" << "\t"; + + if (outWeirHill) outwcfstat << pNeutralStatistics->getWeightedFst() << "\t"; + else outwcfstat << "NA" << "\t"; + + outwcfstat << pNeutralStatistics->getMeanNbAllPerLocus() << "\t" + << pNeutralStatistics->getMeanNbAllPerLocusPerPatch() << "\t" + << pNeutralStatistics->getTotalFixdAlleles() << "\t" + << pNeutralStatistics->getMeanFixdAllelesPerPatch() << "\t" + << pNeutralStatistics->getHo(); + + outwcfstat << endl; +} + +// ---------------------------------------------------------------------------------------- +// Write per locus FST results file +// ---------------------------------------------------------------------------------------- + +void Community::writePerLocusFstatFile(Species* pSpecies, const int yr, const int gen, const int nLoci, set const& patchList) +{ + string samplingOpt = paramsSim->getSim().patchSamplingOption; + bool samplingFixed = samplingOpt == "list" + || samplingOpt == "random" + || (samplingOpt == "all" && !pLandscape->getLandParams().dynamic); + + const set positions = pSpecies->getSpTrait(NEUTRAL)->getGenePositions(); + + int thisLocus = 0; + for (int position : positions) { + + outperlocusfstat << yr << "\t" + << gen << "\t" + << position << "\t"; + outperlocusfstat << pNeutralStatistics->getPerLocusFst(thisLocus) << "\t" + << pNeutralStatistics->getPerLocusFis(thisLocus) << "\t" + << pNeutralStatistics->getPerLocusFit(thisLocus) << "\t" + << pNeutralStatistics->getPerLocusHo(thisLocus); + + if (samplingFixed) { // then safe to output sampled patches in order + for (int patchId : patchList) { + float het = getPatchHet(pSpecies, patchId, thisLocus); + if (het < 0) // patch empty + outperlocusfstat << "\t" << "NA"; + else outperlocusfstat << "\t" << het; + } + } + else { // sampling may change between generations, must produce output for all patches in Landscape + for (auto patchId : pLandscape->getPatchNbs()) { + if (patchList.contains(patchId)) { + float het = getPatchHet(pSpecies, patchId, thisLocus); + if (het < 0) // patch empty + outperlocusfstat << "\t" << "NA"; + else outperlocusfstat << "\t" << het; + } + else { // patch not sampled + outperlocusfstat << "\t" << "NA"; + } + } + } + ++thisLocus; + outperlocusfstat << endl; + } +} + +// Calculate the observed heterozygosity (Ho) for a patch +// = number of heterozygous individuals at this locus / nb individuals in patch +float Community::getPatchHet(Species* pSpecies, int patchId, int whichLocus) const { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + int nAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + int popSize = 0; + float het = 0; + if (pPop != 0) { + popSize = pPop->sampleSize(); + if (popSize == 0) return -1.0; + else { + for (int a = 0; a < nAlleles; ++a) { + het += static_cast(pPop->getHeteroTally(whichLocus, a)); + } + het /= popSize; + return het; + } + } + else return -1.0; +} + + +// ---------------------------------------------------------------------------------------- +// Write pairwise FST results file +// ---------------------------------------------------------------------------------------- +void Community::writePairwiseFstFile(Species* pSpecies, const int yr, const int gen, const int nAlleles, const int nLoci, set const& patchList) { + + // within patch fst (diagonal of matrix) + int i = 0; + for (int patchId : patchList) { + outpairwisefst << yr << "\t" << gen << "\t"; + outpairwisefst << patchId << "\t" << patchId << "\t" + << pNeutralStatistics->getPairwiseFst(i, i) + << endl; + ++i; + } + + // between patch fst + i = 0; + for (int patchIdA : patchList | std::views::take(patchList.size() - 1)) { + int j = i + 1; + for (int patchIdB : patchList | std::views::drop(j)) { + outpairwisefst << yr << "\t" << gen << "\t"; + outpairwisefst << patchIdA << "\t" << patchIdB << "\t" + << pNeutralStatistics->getPairwiseFst(i, j) + << endl; + ++j; + } + ++i; + } +} + + +// ---------------------------------------------------------------------------------------- +// Output and calculate neutral statistics +// ---------------------------------------------------------------------------------------- + + +void Community::outNeutralGenetics(Species* pSpecies, int rep, int yr, int gen, bool outWeirCockerham, bool outWeirHill) { + + const int maxNbNeutralAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + const int nLoci = (int)pSpecies->getNPositionsForTrait(NEUTRAL); + const set patchList = pSpecies->getSamplePatches(); + int nInds = 0, nbPops = 0; + + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + if (patch == 0) { + throw runtime_error("Sampled patch does not exist"); + } + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { // empty patches do not contribute + nInds += pPop->sampleSize(); + nbPops++; + } + } + + if (pNeutralStatistics == 0) + pNeutralStatistics = make_unique(patchList.size(), nLoci); + + pNeutralStatistics->updateAllNeutralTables(pSpecies, pLandscape, patchList); + pNeutralStatistics->calculateHo(patchList, nInds, nLoci, pSpecies, pLandscape); + pNeutralStatistics->calculatePerLocusHo(patchList, nInds, nLoci, pSpecies, pLandscape); + pNeutralStatistics->calcAllelicDiversityMetrics(patchList, nInds, pSpecies, pLandscape); + + if (outWeirCockerham) { + pNeutralStatistics->calculateFstatWC(patchList, nInds, nLoci, maxNbNeutralAlleles, pSpecies, pLandscape); + } + if (outWeirHill) { + pNeutralStatistics->calcPairwiseWeightedFst(patchList, nInds, nLoci, pSpecies, pLandscape); + } + + writeNeutralOutputFile(rep, yr, gen, outWeirCockerham, outWeirHill); + + if (outWeirCockerham) { + writePerLocusFstatFile(pSpecies, yr, gen, nLoci, patchList); + } + if (outWeirHill) { + writePairwiseFstFile(pSpecies, yr, gen, maxNbNeutralAlleles, nLoci, patchList); + } +} + //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- diff --git a/Community.h b/Community.h index 8db17b1..5b1d8d7 100644 --- a/Community.h +++ b/Community.h @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 @@ -35,9 +35,9 @@ 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. + 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. + 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 @@ -51,6 +51,8 @@ Last updated: 25 June 2021 by Anne-Kathleen Malchow #include #include +#include +#include using namespace std; #include "SubCommunity.h" @@ -58,6 +60,7 @@ using namespace std; #include "Patch.h" #include "Cell.h" #include "Species.h" +#include "NeutralStatsManager.h" //--------------------------------------------------------------------------- struct commStats { @@ -153,23 +156,12 @@ class Community { int, // year int // generation ); - void outGenFinishReplicate(); // Close genetics file - void outGenStartReplicate( // Open genetics file and write header record - int, // replicate - int // Landscape number - ); - void outGenetics( // Write records to genetics file - int, // replicate - int // year - ); // Close occupancy file bool outOccupancyFinishLandscape(); // Open occupancy file, write header record and set up occupancy array bool outOccupancyStartLandscape(); void outOccupancy(void); - void outOccSuit( - bool // TRUE if occupancy graph is to be viewed on screen - ); + void outOccSuit(); bool outTraitsFinishLandscape(); // Close traits file bool outTraitsStartLandscape( // Open traits file and write header record Species*, // pointer to Species @@ -198,12 +190,33 @@ class Community { Rcpp::IntegerMatrix addYearToPopList(int,int); #endif + //sample individuals for genetics (or could be used for anything) + void sampleIndividuals(Species* pSpecies); + + bool openOutGenesFile(const bool& isDiploid, const int landNr, const int rep); + void outputGeneValues(const int& year, const int& gen, Species* pSpecies); + + //control neutral stat output + void outNeutralGenetics(Species* pSpecies, int rep, int yr, int gen, bool outWeirCockerham, bool outWeirHill); + + //file openers + bool openNeutralOutputFile(Species* pSpecies, const int landNr); + bool openPerLocusFstFile(Species* pSpecies, Landscape* pLandscape, const int landNr, const int rep); + bool openPairwiseFstFile(Species* pSpecies, Landscape* pLandscape, const int landNr, const int rep); + + //file writers + void writeNeutralOutputFile(int rep, int yr, int gen, bool outWeirCockerham, bool outWeirHill); + void writePerLocusFstatFile(Species* pSpecies, const int yr, const int gen, const int nLoci, set const& patchList); + void writePairwiseFstFile(Species* pSpecies, const int yr, const int gen, const int nAlleles, const int nLoci, set const& patchList); + float getPatchHet(Species* pSpecies, int patchId, int whichLocus) const; private: Landscape *pLandscape; int indIx; // index used to apply initial individuals float **occSuit; // occupancy of suitable cells / patches std::vector subComms; + //below won't work for multispecies + unique_ptr pNeutralStatistics; }; extern paramSim *paramsSim; diff --git a/DispersalTrait.cpp b/DispersalTrait.cpp new file mode 100644 index 0000000..53eb0e3 --- /dev/null +++ b/DispersalTrait.cpp @@ -0,0 +1,467 @@ +#include "DispersalTrait.h" + +// ---------------------------------------------------------------------------------------- +// Initialisation constructor +// Called when initialising community +// Sets up initial values, and immutable attributes (distributions and parameters) +// that are defined at the species-level +// ---------------------------------------------------------------------------------------- +DispersalTrait::DispersalTrait(SpeciesTrait* P) +{ + pSpeciesTrait = P; + ExpressionType expressionType = pSpeciesTrait->getExpressionType(); + + if (!pSpeciesTrait->isInherited()) // there is a trait for individual variation but this isn't inherited variation it's sampled from initial distribution + _inherit_func_ptr = &DispersalTrait::reInitialiseGenes; + else { + _inherit_func_ptr = (pSpeciesTrait->getPloidy() == 1) ? &DispersalTrait::inheritHaploid : &DispersalTrait::inheritDiploid; //this could be changed if we wanted some alternative form of inheritance + + DistributionType mutationDistribution = pSpeciesTrait->getMutationDistribution(); + map mutationParameters = pSpeciesTrait->getMutationParameters(); + + // Set mutation parameters + switch (mutationDistribution) { + case UNIFORM: + { + if (mutationParameters.count(MAX) != 1) + throw logic_error("mutation uniform distribution parameter must contain max value (e.g. max= ) \n"); + if (mutationParameters.count(MIN) != 1) + throw logic_error("mutation uniform distribution parameter must contain min value (e.g. min= ) \n"); + _mutate_func_ptr = &DispersalTrait::mutateUniform; + break; + } + case NORMAL: + { + if (mutationParameters.count(MEAN) != 1) + throw logic_error("mutation distribution set to normal so parameters must contain mean value (e.g. mean= ) \n"); + if (mutationParameters.count(SD) != 1) + throw logic_error("mutation distribution set to normal so parameters must contain sdev value (e.g. sdev= ) \n"); + _mutate_func_ptr = &DispersalTrait::mutateNormal; + break; + } + default: + { + throw logic_error("wrong parameter value for mutation model, must be uniform/normal \n"); //unless want to add gamma or negative exp + break; + } + } + } + + // Set initialisation parameters + DistributionType initialDistribution = pSpeciesTrait->getInitialDistribution(); + map initialParameters = pSpeciesTrait->getInitialParameters(); + switch (initialDistribution) { + case UNIFORM: + { + if (initialParameters.count(MAX) != 1) + throw logic_error("initial uniform distribution parameter must contain max value (e.g. max= ) \n"); + if (initialParameters.count(MIN) != 1) + throw logic_error("initial uniform distribution parameter must contain min value (e.g. min= ) \n"); + float maxD = initialParameters.find(MAX)->second; + float minD = initialParameters.find(MIN)->second; + initialiseUniform(minD, maxD); + break; + } + case NORMAL: + { + if (initialParameters.count(MEAN) != 1) + throw logic_error("initial normal distribution parameter must contain mean value (e.g. mean= ) \n"); + if (initialParameters.count(SD) != 1) + throw logic_error("initial normal distribution parameter must contain sdev value (e.g. sdev= ) \n"); + float mean = initialParameters.find(MEAN)->second; + float sd = initialParameters.find(SD)->second; + initialiseNormal(mean, sd); + break; + } + default: + { + throw logic_error("wrong parameter value for parameter \"initialisation of dispersal traits\", must be uniform/normal \n"); + break; + } + } + + // Set expression mode parameters + switch (expressionType) { + case AVERAGE: + { + _express_func_ptr = &DispersalTrait::expressAverage; + break; + } + case ADDITIVE: + { + _express_func_ptr = &DispersalTrait::expressAdditive; + break; + } + default: + { + throw logic_error("wrong parameter value for parameter \"expression of dispersal trait\", must be average/additive \n"); + break; + } + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance constructor +// Copies immutable features from a parent trait +// Only called via clone() +// ---------------------------------------------------------------------------------------- +DispersalTrait::DispersalTrait(const DispersalTrait& T) : + pSpeciesTrait(T.pSpeciesTrait), + _mutate_func_ptr(T._mutate_func_ptr), + _inherit_func_ptr(T._inherit_func_ptr), + _express_func_ptr(T._express_func_ptr) {} + +// ---------------------------------------------------------------------------------------- +// Sample and apply mutations from a uniform distribution +// +// Mutations drawn only for existing positions, +// that is no new genes are created during simulation +// ---------------------------------------------------------------------------------------- +void DispersalTrait::mutateUniform() +{ + const int positionsSize = pSpeciesTrait->getPositionsSize(); + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const short ploidy = pSpeciesTrait->getPloidy(); + const float mutationRate = pSpeciesTrait->getMutationRate(); + float newAlleleVal; + + auto rng = pRandom->getRNG(); + + map mutationParameters = pSpeciesTrait->getMutationParameters(); + float maxD = mutationParameters.find(MAX)->second; + float minD = mutationParameters.find(MIN)->second; + + for (int p = 0; p < ploidy; p++) { + + unsigned int NbMut = pRandom->Binomial(positionsSize, mutationRate); + + if (NbMut > 0) { + vector mutationPositions; + sample(genePositions.begin(), genePositions.end(), std::back_inserter(mutationPositions), + NbMut, rng); + + for (int m : mutationPositions) { + auto it = genes.find(m); + if (it == genes.end()) + throw runtime_error("Locus sampled for mutation doesn't exist."); + float currentAlleleVal = it->second[p].get()->getAlleleValue();//current + newAlleleVal = pRandom->FRandom(minD, maxD) + currentAlleleVal; + it->second[p] = make_shared(newAlleleVal, dispDominanceFactor); + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Sample and apply mutations from a normal distribution +// Mutations drawn only for existing positions, +// that is no new genes are created during simulation +// ---------------------------------------------------------------------------------------- +void DispersalTrait::mutateNormal() +{ + const int positionsSize = pSpeciesTrait->getPositionsSize(); + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const short ploidy = pSpeciesTrait->getPloidy(); + const float mutationRate = pSpeciesTrait->getMutationRate(); + + auto rng = pRandom->getRNG(); + + const map mutationParameters = pSpeciesTrait->getMutationParameters(); + const float mean = mutationParameters.find(MEAN)->second; + const float sd = mutationParameters.find(SD)->second; + float newAlleleVal; + + for (int p = 0; p < ploidy; p++) { + + unsigned int NbMut = pRandom->Binomial(positionsSize, mutationRate); + + if (NbMut > 0) { + vector mutationPositions; + sample(genePositions.begin(), genePositions.end(), std::back_inserter(mutationPositions), + NbMut, rng); + + for (int m : mutationPositions) { + auto it = genes.find(m); + if (it == genes.end()) + throw runtime_error("Locus sampled for mutation doesn't exist."); + float currentAlleleVal = it->second[p].get()->getAlleleValue(); //current + newAlleleVal = pRandom->Normal(mean, sd) + currentAlleleVal; + it->second[p] = make_shared(newAlleleVal, dispDominanceFactor); + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Wrapper to inheritance function +// ---------------------------------------------------------------------------------------- +void DispersalTrait::inheritGenes(const bool& fromMother, QuantitativeTrait* parentTrait, set const& recomPositions, int startingChromosome) +{ + auto parentCast = dynamic_cast(parentTrait); // must convert QuantitativeTrait to DispersalTrait + const auto& parent_seq = parentCast->getGenes(); + (this->*_inherit_func_ptr)(fromMother, parent_seq, recomPositions, startingChromosome); +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for diploid, sexual species +// Called once for each parent. +// Pass the correct parental strand, resolving crossing-overs +// after each recombinant site e.g. if parent genotype is +// 0000 +// 1111 +// and position 2 is selected to recombine, then offspring inherits +// 0001 +// Assumes mother genes are inherited first. +// ---------------------------------------------------------------------------------------- +void DispersalTrait::inheritDiploid(const bool& fromMother, map>> const& parentGenes, set const& recomPositions, int parentChromosome) { + + const int lastPosition = parentGenes.rbegin()->first; + auto recomIt = recomPositions.lower_bound(parentGenes.begin()->first); + // If no recombination sites, only breakpoint is last position + // i.e., no recombination occurs + int nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + + // Is the first parent gene position already recombinant? + auto distance = std::distance(recomPositions.begin(), recomIt); + if (distance % 2 != 0) // odd positions = switch, even = switch back + parentChromosome = 1 - parentChromosome; //switch chromosome + + for (auto const& [locus, allelePair] : parentGenes) { + + // Switch chromosome if locus is past recombination site + while (locus > nextBreakpoint) { + parentChromosome = 1 - parentChromosome; + std::advance(recomIt, 1); // go to next recombination site + nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + } + + if (locus <= nextBreakpoint) { + auto& parentAllele = allelePair[parentChromosome]; + auto itGene = genes.find(locus); + if (itGene == genes.end()) { + // locus does not exist yet, create and initialise it + if (!fromMother) throw runtime_error("Father-inherited locus does not exist."); + vector> newAllelePair(2); + newAllelePair[sex_t::FEM] = parentAllele; + genes.insert(make_pair(locus, newAllelePair)); + } + else { // father, locus already exists + if (fromMother) throw runtime_error("Mother-inherited locus already exists."); + itGene->second[sex_t::MAL] = parentAllele; + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for haploid, asexual species +// Simply pass down parent genes +// Arguments are still needed to match overloaded function in base class +// ---------------------------------------------------------------------------------------- +void DispersalTrait::inheritHaploid(const bool& fromMother, map>> const& parentGenes, set const& recomPositions, int parentChromosome) +{ + genes = parentGenes; +} + +// ---------------------------------------------------------------------------------------- +// Non-inheritance +// For cases where isInherited option is turned off +// In this case, offspring alleles are populated using the initialise functions +// Arguments are still needed to match overloaded function in base class +// ---------------------------------------------------------------------------------------- +void DispersalTrait::reInitialiseGenes(const bool& fromMother, map>> const& parentGenes, set const& recomPositions, int parentChromosome) +{ + DistributionType initialDistribution = pSpeciesTrait->getInitialDistribution(); + map initialParameters = pSpeciesTrait->getInitialParameters(); + + switch (initialDistribution) { + case UNIFORM: + { + if (initialParameters.count(MAX) != 1) + throw logic_error("initial uniform distribution parameter must contain max value (e.g. max= ) \n"); + if (initialParameters.count(MIN) != 1) + throw logic_error("initial uniform distribution parameter must contain min value (e.g. min= ) \n"); + float maxD = initialParameters.find(MAX)->second; + float minD = initialParameters.find(MIN)->second; + initialiseUniform(minD, maxD); + break; + } + case NORMAL: + { + if (initialParameters.count(MEAN) != 1) + throw logic_error("initial normal distribution parameter must contain mean value (e.g. mean= ) \n"); + if (initialParameters.count(SD) != 1) + throw logic_error("initial normal distribution parameter must contain sdev value (e.g. sdev= ) \n"); + float mean = initialParameters.find(MEAN)->second; + float sd = initialParameters.find(SD)->second; + initialiseNormal(mean, sd); + break; + } + default: + { + throw logic_error("wrong parameter value for parameter \"initialisation of dispersal trait\", must be uniform/normal \n"); + break; //should return false + } + } +} + +// ---------------------------------------------------------------------------------------- +// Dispersal initialisation options +// ---------------------------------------------------------------------------------------- +void DispersalTrait::initialiseNormal(float mean, float sd) { + + const set genePositions = pSpeciesTrait->getGenePositions(); + short ploidy = pSpeciesTrait->getPloidy(); + + for (auto position : genePositions) { + vector> newAllelePair; + for (int i = 0; i < ploidy; i++) { + float alleleVal = pRandom->Normal(mean, sd); + newAllelePair.emplace_back(make_shared(alleleVal, dispDominanceFactor)); + } + genes.insert(make_pair(position, newAllelePair)); + } +} + +void DispersalTrait::initialiseUniform(float min, float max) { + + const set genePositions = pSpeciesTrait->getGenePositions(); + short ploidy = pSpeciesTrait->getPloidy(); + + for (auto position : genePositions) { + vector> newAllelePair; + for (int i = 0; i < ploidy; i++) { + float alleleVal = pRandom->FRandom(min, max); + newAllelePair.emplace_back(make_shared(alleleVal, dispDominanceFactor)); + } + genes.insert(make_pair(position, newAllelePair)); + } +} + +// ---------------------------------------------------------------------------------------- +// Dispersal gene expression options +// ---------------------------------------------------------------------------------------- +float DispersalTrait::expressAdditive() { + + float phenotype = 0.0; + + for (auto const& [locus, allelePair] : genes) + { + for (const std::shared_ptr m : allelePair) + phenotype += m->getAlleleValue(); + } + trimPhenotype(phenotype); + return phenotype; +} + +float DispersalTrait::expressAverage() { + + int positionsSize = pSpeciesTrait->getPositionsSize(); + short ploidy = pSpeciesTrait->getPloidy(); + float phenotype = 0.0; + + for (auto const& [locus, allelePair] : genes) + { + for (auto& m : allelePair) + phenotype += m->getAlleleValue(); + } + phenotype /= positionsSize * ploidy; + trimPhenotype(phenotype); + return phenotype; +} + +void DispersalTrait::trimPhenotype(float& val) { + const float minPositiveVal = 1e-06; + switch (pSpeciesTrait->getTraitType()) + { + // Values bound between 0 and 1 + case E_D0_F: case E_D0_M: case E_D0: + case S_S0_F: case S_S0_M: case S_S0: + case KERNEL_PROBABILITY_F: case KERNEL_PROBABILITY_M: case KERNEL_PROBABILITY: + case CRW_STEPCORRELATION: + { + if (val < 0.0) val = 0; + else if (val > 1.0) val = 1.0; + break; + } + // Positive values + case KERNEL_MEANDIST_1_F: case KERNEL_MEANDIST_1_M: case KERNEL_MEANDIST_1: + case KERNEL_MEANDIST_2_F: case KERNEL_MEANDIST_2_M: case KERNEL_MEANDIST_2: + case CRW_STEPLENGTH: + { + if (val < 0.0) val = 0; + break; + } + // Strictly positive values + case E_ALPHA_F: case E_ALPHA_M: case E_ALPHA: + case S_ALPHA_F: case S_ALPHA_M: case S_ALPHA: + case SMS_ALPHADB: + { + if (val <= 0.0) val = minPositiveVal; + break; + } + // Minimum 1 + case SMS_DP: + case SMS_GB: + { + if (val <= 1.0) val = 1.0; + break; + } + // Not bound + case E_BETA_F: case E_BETA_M: case E_BETA: + case S_BETA_F: case S_BETA_M: case S_BETA: + case SMS_BETADB: + { + break; + } + default: + break; + } +} + +// ---------------------------------------------------------------------------------------- +// Get allele value at locus +// ---------------------------------------------------------------------------------------- +float DispersalTrait::getAlleleValueAtLocus(short whichChromosome, int position) const { + + auto it = genes.find(position); + if (it == genes.end()) + throw runtime_error("The Dispersal locus queried for its allele value does not exist."); + return it->second[whichChromosome].get()->getAlleleValue(); +} + +float DispersalTrait::getDomCoefAtLocus(short whichChromosome, int position) const { + auto it = genes.find(position); + if (it == genes.end()) + throw runtime_error("The genetic load locus queried for its dominance coefficient does not exist."); + return it->second[whichChromosome]->getDominanceCoef(); +} + +#ifdef UNIT_TESTS + +// Create a default set of alleles for testing +// +// Shorthand function to manually set genotypes for dispersal and +// genetic fitness traits, instead of having to manipulate mutations. +map>> createTestGenotype( + const int genomeSz, const bool isDiploid, + const float valAlleleA, + const float valAlleleB, + const float domCoeffA, + const float domCoeffB +) { + vector> gene(isDiploid ? 2 : 1); + if (isDiploid) { + gene[0] = make_shared(valAlleleA, domCoeffA); + gene[1] = make_shared(valAlleleB, domCoeffB); + } + else { + gene[0] = make_shared(valAlleleA, domCoeffA); + } + map>> genotype; + for (int i = 0; i < genomeSz; i++) { + genotype.emplace(i, gene); + } + return genotype; +} +#endif // UNIT_TESTS diff --git a/DispersalTrait.h b/DispersalTrait.h new file mode 100644 index 0000000..baed08c --- /dev/null +++ b/DispersalTrait.h @@ -0,0 +1,107 @@ +#ifndef DISPTRAITH +#define DISPTRAITH + +#include +#include +#include +#include + +#include "QuantitativeTrait.h" + +using namespace std; + +// Dispersal trait +// +// That is, all evolvable that control emigration, transfer and settlement +class DispersalTrait : public QuantitativeTrait { + +public: + + // Initialisation constructor, set initial values and immutable features + DispersalTrait(SpeciesTrait* P); + + // Inheritance constructor, copies pointers to immutable features when cloning from parent + DispersalTrait(const DispersalTrait& T); + + // Make a shallow copy to pass to offspring trait + // Return new pointer to new trait created by inheritance c'tor + // This avoids copying shared attributes: distributions and parameters + virtual unique_ptr clone() const override { return std::make_unique(*this); } + + virtual ~DispersalTrait() { } + + // Getters + int getNLoci() const override { return pSpeciesTrait->getPositionsSize(); } + float getMutationRate() const override { return pSpeciesTrait->getMutationRate(); } + bool isInherited() const override { return pSpeciesTrait->isInherited(); } + + map>>& getGenes() { return genes; } // returning reference, receiver must be const + + void mutate() override { (this->*_mutate_func_ptr) (); } + float express() override { return (this->*_express_func_ptr) (); } + void inheritGenes(const bool& fromMother, QuantitativeTrait* parent, set const& recomPositions, int startingChromosome) override; + + float getAlleleValueAtLocus(short chromosome, int i) const override; + float getDomCoefAtLocus(short chromosome, int position) const override; + +#ifdef UNIT_TESTS // for testing only + void overwriteGenes(map>> genSeq) { + genes = genSeq; + } + void triggerInherit( + // inheritGenes requires passing a QuantitativeTrait, unfeasible in tests + const bool& fromMother, + map>> const& parentGenes, + set const& recomPositions, + int startChr) { + (this->*_inherit_func_ptr)(fromMother, parentGenes, recomPositions, startChr); + } +#endif + +private: + + const double dispDominanceFactor = 1.0; // no dominance for Dispersal traits (yet?) + + // > + map>> genes; + + // Initialisation + void initialiseUniform(float min, float max); + void initialiseNormal(float mean, float sd); + + // Immutable features, set at initialisation + // and passed down to every subsequent trait copy + //// Species-level trait attributes, invariant across individuals + SpeciesTrait* pSpeciesTrait; + //// Species-level trait functions + void (DispersalTrait::* _mutate_func_ptr) (void); + void (DispersalTrait::* _inherit_func_ptr) (const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + float (DispersalTrait::* _express_func_ptr) (void); + + // Possible values for immutable functions + //// Inheritance + void inheritDiploid(const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + void inheritHaploid(const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + void reInitialiseGenes(const bool& fromMother, map>> const& parentMutations, set const& recomPositions, int parentChromosome); + //// Mutation + void mutateUniform(); + void mutateNormal(); + void trimPhenotype(float& phenotype); + //// Gene expression + float expressAverage(); + float expressAdditive(); +}; + +#ifdef UNIT_TESTS +// Test utilities + +map>> createTestGenotype( + const int genomeSz, const bool isDiploid, + const float valAlleleA, + const float valAlleleB = -99.9, // allow no value for haploids + const float domCoeffA = 1.0, // default for dispersal traits + const float domCoeffB = 1.0 +); +#endif // UNIT_TESTS + +#endif // DISPTRAITH diff --git a/FractalGenerator.cpp b/FractalGenerator.cpp index 85d0747..c0a0392 100644 --- a/FractalGenerator.cpp +++ b/FractalGenerator.cpp @@ -1,26 +1,26 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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" //--------------------------------------------------------------------------- @@ -29,202 +29,195 @@ vector patches; //----- Landscape creation -------------------------------------------------- -land::land(): x_coord(0), y_coord(0), value(0.0), avail(0) {} +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; + return z.value < zz.value; } -vector& fractal_landscape(int X,int Y,double Hurst,double prop, - double maxValue,double minValue) +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; - } -} + int ii, jj, x, y; + int ix, iy; + //int x0, y0, size, kx, kx2, ky, ky2; + int kx, kx2, ky, ky2; -// 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); + double range; //range to draw random numbers at each iteration + double nx, ny; + double i, j; + int Nx = X; + int Ny = Y; -/////////////MIDPOINT DISPLACEMENT ALGORITHM////////////////////////////////// -kx = (Nx-1) / 2; -kx2 = 2 * kx; -ky = (Ny-1) / 2; -ky2 = 2 * ky; + double ran[5]; // to store each time the 5 random numbers for the random displacement -for (ii = 0; ii < 5; ii++) //random displacement -{ - ran[ii] = 1.0 + pRandom->Random() * (maxValue-1.0); -} + int Nno; // number of cells NON suitable as habitat -//The diamond step: -arena[kx][ky] = ((arena[0][0] + arena[0][ky2] + arena[kx2][0] + arena[kx2][ky2])/4) + ran[0]; + // 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); -//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]; + double** arena = new double* [X]; + for (ii = 0; ii < X; ii++) { + arena[ii] = new double[Y]; + } -range = maxValue*pow(2,-Hurst); + patches.clear(); + // initialise all the landscape with zeroes + for (jj = 0; jj < X; jj++) { + for (ii = 0; ii < Y; ii++) { + arena[jj][ii] = 0; + } + } -i = pow2x-1; -j = pow2y-1; + // 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); -while (i > 0) { - nx = pow(2,i)+1; - kx = (int)((nx-1) / 2); + /////////////MIDPOINT DISPLACEMENT ALGORITHM////////////////////////////////// + kx = (Nx - 1) / 2; kx2 = 2 * kx; - - ny = pow(2,j)+1; - ky = (int)((ny-1) / 2); + ky = (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); + 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); } - //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); } - ix += ((int)nx-1); - } - if (i==j) j--; - i--; + if (i == j) j--; + i--; - range = range*pow(2,-Hurst); //reduce the random number range -} + 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 + // 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; + land* patch; -for (x = 0; x < X; x++) // put all the cells with their values in a vector -{ - for (y = 0; y < Y; y++) + for (x = 0; x < X; x++) // put all the cells with their values in a vector { - patch = new land; - patch->x_coord = x; - patch->y_coord = y; - patch->value = (float)arena[x][y]; - patch->avail = 1; + 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); + patches.push_back(*patch); - delete patch; + delete patch; + } } -} -sort(patches.begin(),patches.end(),compare); // sorts the vector + 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; -} + 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 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; + 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 + vector::iterator iter = patches.begin(); + while (iter != patches.end()) { - new_value = maxValue - diffK * (max - (double)iter->value) / diff; - - iter->value = (float)new_value; - } - else iter->value = 0; + if (iter->value > 0) // rescale to a range of K between Kmin and Kmax + { + new_value = maxValue - diffK * (max - (double)iter->value) / diff; - iter++; -} + iter->value = (float)new_value; + } + else iter->value = 0; -if (arena != NULL) { - for(ii = 0; ii < X; ii++) { + iter++; + } - delete[] arena[ii]; + if (arena != NULL) { + for (ii = 0; ii < X; ii++) { + delete[] arena[ii]; + } + delete[] arena; } - delete[] arena; -} -return patches; + return patches; } diff --git a/FractalGenerator.h b/FractalGenerator.h index e3a0bee..c030566 100644 --- a/FractalGenerator.h +++ b/FractalGenerator.h @@ -1,66 +1,66 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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. + RangeShifter v2.0 FractalGenerator + Implements the midpoint displacement algorithm for generating a fractal Landscape, + following: -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 + Saupe, D. (1988). Algorithms for random fractals. In: The Science of Fractal Images + (eds. Pietgen, H.O. & Saupe, D.). Springer, New York, pp. 71113. -Authors: Greta Bocedi & Steve Palmer, University of Aberdeen -Last updated: 15 July 2021 by Anne-Kathleen Malchow + 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; + //using namespace std; #include "Parameters.h" class land { - public: +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: +private: }; // IMPORTANT NOTE: X AND Y ARE TRANSPOSED, i.e. X IS THE VERTICAL CO-ORDINATE @@ -76,9 +76,7 @@ vector& fractal_landscape( ); bool compare(const land&, const land&); -extern RSrandom *pRandom; -#if RSDEBUG -#endif +extern RSrandom* pRandom; //--------------------------------------------------------------------------- #endif diff --git a/GeneticFitnessTrait.cpp b/GeneticFitnessTrait.cpp new file mode 100644 index 0000000..151110a --- /dev/null +++ b/GeneticFitnessTrait.cpp @@ -0,0 +1,536 @@ +#include "GeneticFitnessTrait.h" + +// ---------------------------------------------------------------------------------------- +// Initialisation constructor +// Called when initialising community +// Sets up initial values, and immutable attributes (distributions and parameters) +// that are defined at the species-level +// ---------------------------------------------------------------------------------------- +GeneticFitnessTrait::GeneticFitnessTrait(SpeciesTrait* P) +{ + pSpeciesTrait = P; + ExpressionType expressionType = pSpeciesTrait->getExpressionType(); + + _inherit_func_ptr = (pSpeciesTrait->getPloidy() == 1) ? &GeneticFitnessTrait::inheritHaploid : &GeneticFitnessTrait::inheritDiploid; //this could be changed if we wanted some alternative form of inheritance + + // Set initialisation parameters + DistributionType initialDistribution = pSpeciesTrait->getInitialDistribution(); + map initialParameters = pSpeciesTrait->getInitialParameters(); + switch (initialDistribution) { + case UNIFORM: + { + if (initialParameters.count(MAX) != 1) + throw logic_error("initial uniform distribution parameter must contain max value (e.g. max= ) \n"); + if (initialParameters.count(MIN) != 1) + throw logic_error("initial uniform distribution parameter must contain min value (e.g. min= ) \n"); + break; + } + case NORMAL: + { + if (initialParameters.count(MEAN) != 1) + throw logic_error("initial normal distribution parameter must contain mean value (e.g. mean= ) \n"); + if (initialParameters.count(SD) != 1) + throw logic_error("initial normal distribution parameter must contain sdev value (e.g. sdev= ) \n"); + break; + } + case GAMMA: + { + if (initialParameters.count(SHAPE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one shape value (e.g. shape= ) \n"); + if (initialParameters.count(SCALE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one scale value (e.g. scale= ) \n"); + break; + } + case NEGEXP: + { + if (initialParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to negative exponential (negative decay) so parameters must contain mean value (e.g. mean= ) \n"); + break; + } + case NONE: // initialise with default (i.e. zero) values + break; + default: + { + throw logic_error("wrong parameter value for parameter \"initialisation of dispersal traits\", must be uniform/normal \n"); + break; + } + } + + DistributionType initDomDistribution = pSpeciesTrait->getInitDomDistribution(); + map initDomParameters = pSpeciesTrait->getInitDomParameters(); + switch (initDomDistribution) { + case UNIFORM: + { + if (initDomParameters.count(MAX) != 1) + throw logic_error("genetic load dominance uniform distribution parameter must contain one max value (e.g. max= ) \n"); + if (initDomParameters.count(MIN) != 1) + throw logic_error("genetic load dominance uniform distribution parameter must contain one min value (e.g. min= ) \n"); + break; + } + case NORMAL: + { + if (initDomParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to normal so parameters must contain one mean value (e.g. mean= ) \n"); + if (initDomParameters.count(SD) != 1) + throw logic_error("genetic load dominance distribution set to normal so parameters must contain one sdev value (e.g. sdev= ) \n"); + break; + } + case GAMMA: + { + if (initDomParameters.count(SHAPE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one shape value (e.g. shape= ) \n"); + if (initDomParameters.count(SCALE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one scale value (e.g. scale= ) \n"); + break; + } + case NEGEXP: + { + if (initDomParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to negative exponential (negative decay) so parameters must contain mean value (e.g. mean= ) \n"); + break; + } + case SCALED: + { + if (initDomParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to scaled, so parameters must contain mean dominance value (e.g. mean= ) \n"); + // Set for drawing initial values + setScaledCoeff(initialDistribution, initialParameters); + break; + } + case NONE: // default values, zero-dominance coefficients + break; + default: + { + throw logic_error("wrong parameter value for genetic load dominance model, must be uniform/normal/gamma/negExp/scaled \n"); + break; + } + } + + // Draw initial values + initialise(); + + DistributionType mutationDistribution = pSpeciesTrait->getMutationDistribution(); + map mutationParameters = pSpeciesTrait->getMutationParameters(); + switch (mutationDistribution) { + case UNIFORM: + { + if (mutationParameters.count(MAX) != 1) + throw logic_error("genetic load mutation uniform distribution parameter must contain one max value (e.g. max= ) \n"); + if (mutationParameters.count(MIN) != 1) + throw logic_error("genetic load mutation uniform distribution parameter must contain one min value (e.g. min= ) \n"); + break; + } + case NORMAL: + { + if (mutationParameters.count(MEAN) != 1) + throw logic_error("genetic load mutation distribution set to normal so parameters must contain one mean value (e.g. mean= ) \n"); + if (mutationParameters.count(SD) != 1) + throw logic_error("genetic load mutation distribution set to normal so parameters must contain one sdev value (e.g. sdev= ) \n"); + break; + } + case GAMMA: + { + if (mutationParameters.count(SHAPE) != 1) + throw logic_error("genetic load mutation distribution set to gamma so parameters must contain one shape value (e.g. shape= ) \n"); + if (mutationParameters.count(SCALE) != 1) + throw logic_error("genetic load mutation distribution set to gamma so parameters must contain one scale value (e.g. scale= ) \n"); + break; + } + case NEGEXP: + { + if (mutationParameters.count(MEAN) != 1) + throw logic_error("genetic load mutation distribution set to negative exponential (negative decay) so parameters must contain one mean value (e.g. mean= ) \n"); + break; + } + default: + throw logic_error("wrong parameter value for genetic load mutation model, must be uniform/normal/gamma/negExp \n"); + } + + DistributionType dominanceDistribution = pSpeciesTrait->getDominanceDistribution(); + map dominanceParameters = pSpeciesTrait->getDominanceParameters(); + + switch (dominanceDistribution) { + case UNIFORM: + { + if (dominanceParameters.count(MAX) != 1) + throw logic_error("genetic load dominance uniform distribution parameter must contain one max value (e.g. max= ) \n"); + if (dominanceParameters.count(MIN) != 1) + throw logic_error("genetic load dominance uniform distribution parameter must contain one min value (e.g. min= ) \n"); + break; + } + case NORMAL: + { + if (dominanceParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to normal so parameters must contain one mean value (e.g. mean= ) \n"); + if (dominanceParameters.count(SD) != 1) + throw logic_error("genetic load dominance distribution set to normal so parameters must contain one sdev value (e.g. sdev= ) \n"); + break; + } + case GAMMA: + { + if (dominanceParameters.count(SHAPE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one shape value (e.g. shape= ) \n"); + if (dominanceParameters.count(SCALE) != 1) + throw logic_error("genetic load dominance distribution set to gamma so parameters must contain one scale value (e.g. scale= ) \n"); + break; + } + case NEGEXP: + { + if (dominanceParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to negative exponential (negative decay) so parameters must contain mean value (e.g. mean= ) \n"); + break; + } + case SCALED: + { + if (dominanceParameters.count(MEAN) != 1) + throw logic_error("genetic load dominance distribution set to scaled, so parameters must contain mean dominance value (e.g. mean= ) \n"); + + // Set for drawing mutations (overwrite initial value) + setScaledCoeff(mutationDistribution, mutationParameters); + break; + } + default: + { + throw logic_error("wrong parameter value for genetic load dominance model, must be uniform/normal/gamma/negExp/scaled \n"); + break; + } + } +} + +// Calculate mean selection coeff s_d for calculation of k +void GeneticFitnessTrait::setScaledCoeff(const DistributionType& selCoeffDist, const map& selCoeffParams) +{ + switch (selCoeffDist) + { + case UNIFORM: + scaledDomMeanSelCoeff = (selCoeffParams.find(MIN)->second + selCoeffParams.find(MAX)->second) / 2; + break; + case NORMAL: + scaledDomMeanSelCoeff = selCoeffParams.find(MEAN)->second; + break; + case GAMMA: + scaledDomMeanSelCoeff = selCoeffParams.find(SHAPE)->second * selCoeffParams.find(SCALE)->second; + break; + case NEGEXP: + scaledDomMeanSelCoeff = 1 / selCoeffParams.find(MEAN)->second; + break; + case NONE: + throw logic_error("Scaled dominance distribution cannot be used with default allele distribution."); + default: break; + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance constructor +// Copies immutable features from a parent trait +// Only called via clone() +// ---------------------------------------------------------------------------------------- +GeneticFitnessTrait::GeneticFitnessTrait(const GeneticFitnessTrait& T) : + pSpeciesTrait(T.pSpeciesTrait), + _inherit_func_ptr(T._inherit_func_ptr), + scaledDomMeanSelCoeff(T.scaledDomMeanSelCoeff) +{ + // nothing +} + +void GeneticFitnessTrait::initialise() { + float initSelCoeff; + float initDomCoeff; + short ploidy = pSpeciesTrait->getPloidy(); + auto initDist = pSpeciesTrait->getInitialDistribution(); + auto initParams = pSpeciesTrait->getInitialParameters(); + auto initDomDist = pSpeciesTrait->getInitDomDistribution(); + auto initDomParams = pSpeciesTrait->getInitDomParameters(); + + const set genePositions = pSpeciesTrait->getGenePositions(); + const set initPositions = pSpeciesTrait->getInitPositions(); + + for (auto position : genePositions) { + vector> initialGene(ploidy); + for (int p = 0; p < ploidy; p++) { + initSelCoeff = initDomCoeff = 0.0; + if (initPositions.contains(position)) { + initSelCoeff = drawSelectionCoef(initDist, initParams); + initDomCoeff = drawDominance(initSelCoeff, initDomDist, initDomParams); + } + initialGene[p] = make_shared(initSelCoeff, initDomCoeff); + } + genes.insert(make_pair(position, initialGene)); + } +} + +// ---------------------------------------------------------------------------------------- +// Mutate uniform +// ---------------------------------------------------------------------------------------- +void GeneticFitnessTrait::mutate() +{ + const int positionsSize = pSpeciesTrait->getPositionsSize(); + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const short ploidy = pSpeciesTrait->getPloidy(); + const float mutationRate = pSpeciesTrait->getMutationRate(); + float newSelectionCoef; + float newDominanceCoef; + + auto rng = pRandom->getRNG(); + + for (int p = 0; p < ploidy; p++) { + // Determine nb of mutations + unsigned int NbMut = pRandom->Binomial(positionsSize, mutationRate); + + if (NbMut > 0) { + vector mutationPositions; + // Draw which positions mutate + sample(genePositions.begin(), genePositions.end(), std::back_inserter(mutationPositions), + NbMut, rng); + + for (int m : mutationPositions) { + auto it = genes.find(m); + if (it == genes.end()) + throw runtime_error("Locus sampled for mutation doesn't exist."); + newSelectionCoef = drawSelectionCoef( + pSpeciesTrait->getMutationDistribution(), + pSpeciesTrait->getMutationParameters() + ); + newDominanceCoef = drawDominance( + newSelectionCoef, + pSpeciesTrait->getDominanceDistribution(), + pSpeciesTrait->getDominanceParameters() + ); + it->second[p] = make_shared(newSelectionCoef, newDominanceCoef); + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// get dominance value for new mutation +// ---------------------------------------------------------------------------------------- +float GeneticFitnessTrait::drawDominance(float selCoef, const DistributionType& domDist, const map& domParams) { + + float h = 0.0; + switch (domDist) { + case UNIFORM: + { + float maxD = domParams.find(MAX)->second; + float minD = domParams.find(MIN)->second; + h = pRandom->FRandom(minD, maxD); + break; + } + case NORMAL: + { + const float mean = domParams.find(MEAN)->second; + const float sd = domParams.find(SD)->second; + do { + h = static_cast(pRandom->Normal(mean, sd)); + } while (h <= 0.0); + break; + } + case GAMMA: + { + const float shape = domParams.find(SHAPE)->second; + const float scale = domParams.find(SCALE)->second; + h = static_cast(pRandom->Gamma(shape, scale)); + break; + } + case NEGEXP: + { + const float mean = domParams.find(MEAN)->second; + h = static_cast(pRandom->NegExp(mean)); + break; + } + case SCALED: + { + const float h_d = domParams.find(MEAN)->second; + const float k = -log(2 * h_d) / scaledDomMeanSelCoeff; + const float max = exp(-k * selCoef); + h = pRandom->FRandom(0, max); + break; + } + case NONE: + { + // nothing, s remains 0.0 + break; + } + default: + { + throw logic_error("wrong parameter value for genetic load dominance model, must be uniform/normal/gamma/negExp/scaled/none \n"); + break; + } + } + return h; +} + +// ---------------------------------------------------------------------------------------- +// Get selection coefficient for new mutation +// +// Selection coefficients will usually be between 0 and 1, but may, +// if the mutation distribution enable it, take a negative value +// down to -1 representing the effect of beneficial mutations +// ---------------------------------------------------------------------------------------- +float GeneticFitnessTrait::drawSelectionCoef(const DistributionType& mutationDistribution, const map& mutationParameters) { + + float s = 0.0; // default selection coefficient is 0 + + switch (mutationDistribution) { + case UNIFORM: + { + float maxD = mutationParameters.find(MAX)->second; + float minD = mutationParameters.find(MIN)->second; + s = pRandom->FRandom(minD, maxD); // no check here, min and max should already be constrained to valid values + break; + } + case NORMAL: + { + const float mean = mutationParameters.find(MEAN)->second; + const float sd = mutationParameters.find(SD)->second; + do { + s = static_cast(pRandom->Normal(mean, sd)); + } while (!pSpeciesTrait->isValidTraitVal(s)); + break; + } + case GAMMA: + { + const float shape = mutationParameters.find(SHAPE)->second; + const float scale = mutationParameters.find(SCALE)->second; + do { + s = static_cast(pRandom->Gamma(shape, scale)); + } while (!pSpeciesTrait->isValidTraitVal(s)); + break; + } + case NEGEXP: + { + const float mean = mutationParameters.find(MEAN)->second; + do { + s = static_cast(pRandom->NegExp(mean)); + } while (!pSpeciesTrait->isValidTraitVal(s)); + break; + } + case NONE: + { + // nothing, s remains 0.0 + break; + } + default: + { + throw logic_error("wrong parameter value for genetic load mutation model, must be uniform/normal/gamma/negExp/scaled/none \n"); + break; + } + } + return s; +} + + +// ---------------------------------------------------------------------------------------- +// Wrapper to inheritance function +// ---------------------------------------------------------------------------------------- +void GeneticFitnessTrait::inheritGenes(const bool& fromMother, QuantitativeTrait* parentTrait, set const& recomPositions, int startingChromosome) +{ + auto parentCast = dynamic_cast (parentTrait); // must convert QuantitativeTrait to GeneticFitnessTrait + const auto& parent_seq = parentCast->getGenes(); + (this->*_inherit_func_ptr) (fromMother, parent_seq, recomPositions, startingChromosome); +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for diploid, sexual species +// Called once for each parent. Given a list of recombinant sites, +// populates offspring genes with appropriate parent alleles +// Assumes mother genes are inherited first +// ---------------------------------------------------------------------------------------- +void GeneticFitnessTrait::inheritDiploid(const bool& fromMother, map>> const& parentGenes, set const& recomPositions, int parentChromosome) { + + const int lastPosition = parentGenes.rbegin()->first; + auto recomIt = recomPositions.lower_bound(parentGenes.begin()->first); + // If no recombination sites, only breakpoint is last position + // i.e., no recombination occurs + int nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + + // Is the first parent gene position already recombinant? + auto distance = std::distance(recomPositions.begin(), recomIt); + if (distance % 2 != 0) + parentChromosome = 1 - parentChromosome; // switch chromosome + + for (auto const& [locus, allelePair] : parentGenes) { + + // Switch chromosome if locus is past recombination site + while (locus > nextBreakpoint) { + parentChromosome = 1 - parentChromosome; + std::advance(recomIt, 1); // go to next recombination site + nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + } + + if (locus <= nextBreakpoint) { + auto& parentAllele = allelePair[parentChromosome]; + auto itGene = genes.find(locus); + if (itGene == genes.end()) { + // locus does not exist yet, create and initialise it + if (!fromMother) throw runtime_error("Father-inherited locus does not exist."); + vector> newAllelePair(2); // always diploid + newAllelePair[sex_t::FEM] = parentAllele; + genes.insert(make_pair(locus, newAllelePair)); + } + else { // father, locus already exists + if (fromMother) throw runtime_error("Mother-inherited locus already exists."); + itGene->second[sex_t::MAL] = parentAllele; + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for haploid, asexual species +// Simply pass down parent genes +// Arguments are still needed to match overloaded function in base class +// ---------------------------------------------------------------------------------------- +void GeneticFitnessTrait::inheritHaploid(const bool& fromMother, map>> const& parentGenes, set const& recomPositions, int parentChromosome) +{ + genes = parentGenes; +} + +// ---------------------------------------------------------------------------------------- +// Expression genetic load +// ---------------------------------------------------------------------------------------- +float GeneticFitnessTrait::express() { + + float phenotype = 1.0; // base chance of viability + float sA, sB, hA, hB, sumDomCoeffs, hLocus; + + for (auto const& [locus, pAllelePair] : genes) + { + shared_ptr pAlleleA = pAllelePair[0] == 0 ? wildType : pAllelePair[0]; + + sA = pAlleleA->getAlleleValue(); + hA = pAlleleA->getDominanceCoef(); + + if (pSpeciesTrait->getPloidy() == 2) { + shared_ptr pAlleleB = pAllelePair[1] == 0 ? wildType : pAllelePair[1]; + sB = pAlleleB->getAlleleValue(); + hB = pAlleleB->getDominanceCoef(); + + sumDomCoeffs = hA + hB; + hLocus = sumDomCoeffs == 0.0 ? 0.5 : hA / sumDomCoeffs; + phenotype *= 1 - hLocus * sA - (1 - hLocus) * sB; + } + else { + phenotype *= 1 - sA; + } + } + return phenotype; +} + +// ---------------------------------------------------------------------------------------- +// Get allele value at locus +// ---------------------------------------------------------------------------------------- +float GeneticFitnessTrait::getAlleleValueAtLocus(short whichChromosome, int position) const { + + auto it = genes.find(position); + if (it == genes.end()) + throw runtime_error("The genetic load locus queried for its allele value does not exist."); + return it->second[whichChromosome] == 0 ? wildType->getAlleleValue() : it->second[whichChromosome]->getAlleleValue(); +} + +float GeneticFitnessTrait::getDomCoefAtLocus(short whichChromosome, int position) const { + auto it = genes.find(position); + if (it == genes.end()) + throw runtime_error("The genetic load locus queried for its dominance coefficient does not exist."); + return it->second[whichChromosome] == 0 ? wildType->getDominanceCoef() : it->second[whichChromosome]->getDominanceCoef(); +} \ No newline at end of file diff --git a/GeneticFitnessTrait.h b/GeneticFitnessTrait.h new file mode 100644 index 0000000..db9e7ab --- /dev/null +++ b/GeneticFitnessTrait.h @@ -0,0 +1,89 @@ +#ifndef GENETICFITNESSH +#define GENETICFITNESSH + +#include +#include +#include +#include +#include + +#include "QuantitativeTrait.h" + +using namespace std; + +// Genetic Load trait +// +// Alleles contribute to their value to reducing +// offspring viability. Alleles start with value +// zero but increase through simulation via mutations. +// There can be up to five genetic load traits. +class GeneticFitnessTrait : public QuantitativeTrait { + +public: + + // Initialisation constructor, set initial values and immutable features + GeneticFitnessTrait(SpeciesTrait* P); + + // Inheritance constructor, copies pointers to immutable features when cloning from parent + GeneticFitnessTrait(const GeneticFitnessTrait& T); + + // Make a shallow copy to pass to offspring trait + // Return new pointer to new trait created by inheritance c'tor + // This avoids copying shared attributes: distributions and parameters + virtual unique_ptr clone() const override { return std::make_unique(*this); } + + virtual ~GeneticFitnessTrait() { } + + // Getters + virtual int getNLoci() const override { return pSpeciesTrait->getPositionsSize(); } + float getMutationRate() const override { return pSpeciesTrait->getMutationRate(); } + bool isInherited() const override { return pSpeciesTrait->isInherited(); } + + map>>& getGenes() { return genes; } // returning reference, reciever must be const + + virtual void mutate() override; + virtual float express(); + virtual void inheritGenes(const bool& fromMother, QuantitativeTrait* parent, set const& recomPositions, int startingChromosome) override; + + virtual float getAlleleValueAtLocus(short chromosome, int position) const override; + virtual float getDomCoefAtLocus(short chromosome, int position) const override; + +private: + + // Default allele has value 0 and dominance 0 + inline static shared_ptr wildType = make_shared(0.0, 0.0); + + // > + map>> genes; // position > + + // Initialisation + void initialise(); + + void setScaledCoeff(const DistributionType& selCoeffDist, const map& selCoeffParams); + + // Mutation + float scaledDomMeanSelCoeff = 0; // s_d, only for scaled dominance distribution + float drawDominance( + float selCoef, + const DistributionType& domDist, + const map& domParams + ); + float drawSelectionCoef( + const DistributionType& mutationDistribution, + const map& mutationParameters + ); + + // Immutable features, set at initialisation + // and passed down to every subsequent trait copy + //// Species-level trait attributes, invariant across individuals + SpeciesTrait* pSpeciesTrait; + //// Species-level trait functions + void (GeneticFitnessTrait::* _inherit_func_ptr) (const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + + // Possible values for immutable functions + //// Inheritance + void inheritDiploid(const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + void inheritHaploid(const bool& fromMother, map>> const& parent, set const& recomPositions, int parentChromosome); + +}; +#endif // GENETICFITNESSH diff --git a/Genome.cpp b/Genome.cpp deleted file mode 100644 index d7475ee..0000000 --- a/Genome.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/*---------------------------------------------------------------------------- - * - * 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::outGenFinishReplicate() -{ - if (outGenetic.is_open()) { - outGenetic.close(); outGenetic.clear(); - } -} - -void Genome::outGenStartReplicate(const int rep, const int landNr, const bool xtab) -{ - string name; - simParams sim = paramsSim->getSim(); - - if (sim.batchMode) { - name = paramsSim->getDir(2) - + "Batch" + to_string(sim.batchNum) + "_" - + "Sim" + to_string(sim.simulation) - + "_Land" + to_string(landNr) + "_Rep" + to_string(rep) + "_Genetics.txt"; - } - else { - name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) - + "_Rep" + to_string(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/Genome.h b/Genome.h deleted file mode 100644 index bedd250..0000000 --- a/Genome.h +++ /dev/null @@ -1,173 +0,0 @@ -/*---------------------------------------------------------------------------- - * - * 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 outGenFinishReplicate(); - void outGenStartReplicate( - 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; -#endif - -//--------------------------------------------------------------------------- - -#endif diff --git a/Individual.cpp b/Individual.cpp index ce11b99..7f2c616 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -101,19 +101,20 @@ std::atomic Individual::indCounter = 0; #else // _OPENMP int Individual::indCounter = 0; #endif // _OPENMP +TraitFactory Individual::traitFactory = TraitFactory(); //--------------------------------------------------------------------------- // Individual constructor Individual::Individual(Species* pSpecies, Cell* pCell, Patch* pPatch, short stg, short a, short repInt, float probmale, bool movt, short moveType): - memory(pSpecies->getSMSTraits().memSize) + memory(pSpecies->getSpSMSTraits().memSize) { - indId = indCounter++; // unique identifier for each individual - + indId = indCounter; indCounter++; // unique identifier for each individual + geneticFitness = 1.0; stage = stg; - if (probmale <= 0.0) sex = 0; - else sex = pRandom->Bernoulli(probmale); + if (probmale <= 0.0) sex = FEM; + else sex = pRandom->Bernoulli(probmale) ? MAL : FEM; age = a; status = 0; @@ -124,6 +125,7 @@ Individual::Individual(Species* pSpecies, Cell* pCell, Patch* pPatch, short stg, isDeveloping = false; pPrevCell = pCurrCell = pCell; pNatalPatch = pPatch; + pTrfrData = nullptr; //set to null as default if (movt) { locn loc = pCell->getLocn(); path = new pathData; @@ -134,354 +136,257 @@ Individual::Individual(Species* pSpecies, Cell* pCell, Patch* pPatch, short stg, #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 + pTrfrData = make_unique(loc, loc); + } - 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; + float xc = ((float)pRandom->Random() * 0.999f) + (float)loc.x; + float yc = (float)(pRandom->Random() * 0.999f) + (float)loc.y; + float prevdrn = (float)(pRandom->Random() * 2.0 * PI); + pTrfrData = make_unique(prevdrn, xc, yc); } - else crw = 0; } else { - path = 0; crw = 0; smsData = 0; + path = 0; + pTrfrData = make_unique(0.0, 0.0, 0.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; +void Individual::setEmigTraits(const emigTraits& emig) { + pEmigTraits = make_unique(emig); + } +void Individual::setSettleTraits(const settleTraits& settle) { + pSettleTraits = make_unique(settle); } -//--------------------------------------------------------------------------- +QuantitativeTrait* Individual::getTrait(TraitType trait) const { + auto p = this->spTraitTable.find(trait); + if (p == spTraitTable.end()) + throw runtime_error("Trait does not exist in trait table."); + else return p->second.get(); + } + +set Individual::getTraitTypes() { + auto kv = std::views::keys(this->spTraitTable); + set< TraitType > keys{ kv.begin(), kv.end() }; + return keys; + } //--------------------------------------------------------------------------- +// Inheritance for diploid, sexual species +//--------------------------------------------------------------------------- +void Individual::inherit(Species* pSpecies, const Individual* mother, const Individual* father) { -// 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 + int events = 0; + const set chromosomeEnds = pSpecies->getChromosomeEnds(); + const int genomeSize = pSpecies->getGenomeSize(); - if (gen.trait1Chromosome) { - pGenome = new Genome(pSpecies->getNChromosomes(), pSpecies->getNLoci(0), - pSpecies->isDiploid()); - } - else { - pGenome = new Genome(pSpecies); - } + int maternalStartingChromosome = pRandom->Bernoulli(0.5); + int paternalStartingChromosome = pRandom->Bernoulli(0.5); - int gposn = 0; // current position on genome - int expr = 0; // gene expression type - NOT CURRENTLY USED + set maternalRecomPositions; + set paternalRecomPositions; - 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); - } + // Determine which parental chromosomes are inherited + for (int pos : chromosomeEnds) { + if (pRandom->Bernoulli(0.5)) // switch strand for next chromosome + maternalRecomPositions.insert(pos); + if (pRandom->Bernoulli(0.5)) + paternalRecomPositions.insert(pos); } - 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; + // Draw recombination events for maternal genome + if (pSpecies->getRecombinationRate() > 0.0) + events = pRandom->Binomial(genomeSize, pSpecies->getRecombinationRate()); + // if poisson exceeds genomeSize, bound to genomeSize + int nbrCrossOvers = events + maternalRecomPositions.size(); + if (nbrCrossOvers > genomeSize) { + nbrCrossOvers = genomeSize; } - else { - if (dem.repType == 0) { // asexual reproduction - ntraits = 1; + while (maternalRecomPositions.size() < nbrCrossOvers) { + // Sample recombination sites + maternalRecomPositions.insert(pRandom->IRandom(0, genomeSize)); } - 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); + + // Draw recombination events for paternal genome + if (pSpecies->getRecombinationRate() > 0.0) + events = pRandom->Binomial(genomeSize, pSpecies->getRecombinationRate()); + nbrCrossOvers = events + paternalRecomPositions.size(); + if (nbrCrossOvers > genomeSize) { + nbrCrossOvers = genomeSize; } - // record phenotypic traits - setCRWTraits(pSpecies, trfrposn, 2, false); + while (paternalRecomPositions.size() < nbrCrossOvers) { + paternalRecomPositions.insert(pRandom->IRandom(0, genomeSize)); } - } - 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) + + // Inherit genes for each trait + const auto& spTraits = pSpecies->getTraitTypes(); + for (auto const& trait : spTraits) { - dist2 = pRandom->Normal(0.0, k.dist2SD) / k.dist2Scale; - prob1 = pRandom->Normal(0.0, k.PKern1SD) / k.PKern1Scale; + const auto motherTrait = mother->getTrait(trait); + const auto fatherTrait = father->getTrait(trait); + auto newTrait = motherTrait->clone(); // shallow copy pointer to species-level attributes + + // Inherit from mother first + newTrait->inheritGenes(true, motherTrait, maternalRecomPositions, maternalStartingChromosome); + if (newTrait->isInherited()) { + // Inherit father trait values + newTrait->inheritGenes(false, fatherTrait, paternalRecomPositions, paternalStartingChromosome); + if (newTrait->getMutationRate() > 0 && pSpecies->areMutationsOn()) + newTrait->mutate(); } - 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); + if (trait == GENETIC_LOAD1 || trait == GENETIC_LOAD2 || trait == GENETIC_LOAD3 || trait == GENETIC_LOAD4 || trait == GENETIC_LOAD5) + geneticFitness *= newTrait->express(); + + // Add the inherited trait and genes to the newborn's list + spTraitTable.insert(make_pair(trait, move(newTrait))); } } - else { - pGenome->setTrait(pSpecies, gposn++, dist1, gen.alleleSD); - if (trfr.twinKern) + +//--------------------------------------------------------------------------- +// Inheritance for haploid, asexual species +//--------------------------------------------------------------------------- +void Individual::inherit(Species* pSpecies, const Individual* mother) { + set recomPositions; //not used here cos haploid but need it for inherit function, not ideal + int startingChromosome = 0; + + const auto& spTraits = pSpecies->getTraitTypes(); + + for (auto const& trait : spTraits) { - pGenome->setTrait(pSpecies, gposn++, dist2, gen.alleleSD); - pGenome->setTrait(pSpecies, gposn++, prob1, gen.alleleSD); + const auto motherTrait = mother->getTrait(trait); + auto newTrait = motherTrait->clone(); // shallow copy, pointer to species trait initialised and empty sequence + + newTrait->inheritGenes(true, motherTrait, recomPositions, startingChromosome); + if (newTrait->isInherited()) { + if (newTrait->getMutationRate() > 0 && pSpecies->areMutationsOn()) + newTrait->mutate(); } + + if (trait == GENETIC_LOAD1 || trait == GENETIC_LOAD2 || trait == GENETIC_LOAD3 || trait == GENETIC_LOAD4 || trait == GENETIC_LOAD5) + geneticFitness *= newTrait->express(); + + // Add the inherited trait and genes to the newborn's list + spTraitTable.insert(make_pair(trait, move(newTrait))); } } - // record phenotypic traits - if (trfr.twinKern) + +// Initialise individual trait genes from species-level traits +void Individual::setUpGenes(Species* pSpecies, int resol) { + + // this way to keep spp trait table immutable i.e. not able to call getTraitTable, + // could pass it back by value (copy) instead but could be heavy if large map + const auto& traitTypes = pSpecies->getTraitTypes(); + for (auto const& traitType : traitTypes) { - setKernTraits(pSpecies, trfrposn, 3, resol, trfr.sexDep); + const auto spTrait = pSpecies->getSpTrait(traitType); + this->spTraitTable.emplace(traitType, traitFactory.Create(traitType, spTrait)); } - else { - setKernTraits(pSpecies, trfrposn, 1, resol, trfr.sexDep); + expressDispersalPhenotypes(pSpecies, resol); + expressGeneticLoad(pSpecies); } - } - } - 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); - } - } +void Individual::expressDispersalPhenotypes(Species* pSpecies, int resol) { + + const emigRules emig = pSpecies->getEmigRules(); + const transferRules trfr = pSpecies->getTransferRules(); + const settleType sett = pSpecies->getSettle(); + const settleRules settRules = pSpecies->getSettRules(stage, sex); + // record phenotypic traits - setSettTraits(pSpecies, settposn, 3, sett.sexDep); + if (emig.indVar) setEmigTraits(pSpecies, emig.sexDep, emig.densDep); + if (trfr.indVar) setTransferTraits(pSpecies, trfr, resol); + if (sett.indVar) setSettlementTraits(pSpecies, sett.sexDep, settRules.densDep); } - if (!gen.trait1Chromosome) { - if (gen.neutralMarkers || pSpecies->getNNeutralLoci() > 0) { - pGenome->setNeutralLoci(pSpecies, gen.alleleSD); - } +// Set the fitness attribute of individuals +// Only called at initialisation, otherwise probably faster to compute directly during inheritance +void Individual::expressGeneticLoad(Species* pSpecies) { + const int nbGenLoadTraits = pSpecies->getNbGenLoadTraits(); + const vector whichTrait = { GENETIC_LOAD1 , GENETIC_LOAD2, GENETIC_LOAD3, GENETIC_LOAD4, GENETIC_LOAD5 }; + for (int i = 0; i < nbGenLoadTraits; i++) { + if (spTraitTable.contains(whichTrait[i])) + geneticFitness *= getTrait(whichTrait[i])->express(); } } -// 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; +void Individual::setTransferTraits(Species* pSpecies, transferRules trfr, int resol) { + if (trfr.usesMovtProc) { + if (trfr.moveType == 1) { + setIndSMSTraits(pSpecies); + } + else setIndCRWTraits(pSpecies); + } + else setIndKernelTraits(pSpecies, trfr.sexDep, trfr.twinKern, resol); +} - pGenome = new Genome(pSpecies, mother->pGenome, pFatherGenome); +void Individual::setSettlementTraits(Species* pSpecies, bool sexDep, bool densDep) { - if (emig.indVar) { - // record emigration traits - if (father == 0) { // haploid - if (emig.densDep) { - setEmigTraits(pSpecies, 0, 3, 0); - } - else { - setEmigTraits(pSpecies, 0, 1, 0); + settleTraits s; s.s0 = s.alpha = s.beta = 0.0; + if (sexDep) { + if (this->getSex() == MAL) { + s.s0 = getTrait(S_S0_M)->express(); + if (densDep) { + s.alpha = getTrait(S_ALPHA_M)->express(); + s.beta = getTrait(S_BETA_M)->express(); } } - else { // diploid - if (emig.densDep) { - setEmigTraits(pSpecies, 0, 3, emig.sexDep); - } - else { - setEmigTraits(pSpecies, 0, 1, emig.sexDep); + else if (this->getSex() == FEM) { + s.s0 = getTrait(S_S0_F)->express(); + if (densDep) { + s.alpha = getTrait(S_ALPHA_F)->express(); + s.beta = getTrait(S_BETA_F)->express(); } } + else { + throw runtime_error("Attempt to express invalid emigration trait sex."); + } + } + else { + s.s0 = getTrait(S_S0)->express(); + if (densDep) { + s.alpha = getTrait(S_ALPHA)->express(); + s.beta = getTrait(S_BETA)->express(); + } } - 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); - } + pSettleTraits = make_unique(); + pSettleTraits->s0 = (float)(s.s0); + pSettleTraits->alpha = (float)(s.alpha); + pSettleTraits->beta = (float)(s.beta); + if (pSettleTraits->s0 < 0.0) pSettleTraits->s0 = 0.0; + if (pSettleTraits->s0 > 1.0) pSettleTraits->s0 = 1.0; + return; } - else { // kernel - if (father == 0) { // haploid - if (trfr.twinKern) + + +// Inherit genome from parent(s), diploid +void Individual::inheritTraits(Species* pSpecies, Individual* mother, Individual* father, int resol) { - setKernTraits(pSpecies, trfr.movtTrait[0], 3, resol, 0); + inherit(pSpecies, mother, father); + expressDispersalPhenotypes(pSpecies, resol); } - else { - setKernTraits(pSpecies, trfr.movtTrait[0], 1, resol, 0); - } - } - else { // diploid - if (trfr.twinKern) + +// Inherit genome from mother, haploid +void Individual::inheritTraits(Species* pSpecies, Individual* mother, int resol) { - setKernTraits(pSpecies, trfr.movtTrait[0], 3, resol, trfr.sexDep); + inherit(pSpecies, mother); + expressDispersalPhenotypes(pSpecies, resol); } - 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; + if (sex == FEM) { + if (status == 0 || status == 4 || status == 5 || status == 10) return stage; else return 0; } else return 0; @@ -489,10 +394,17 @@ int Individual::breedingFem(void) { int Individual::getId(void) { return indId; } -int Individual::getSex(void) { return sex; } +sex_t Individual::getSex(void) { return sex; } int Individual::getStatus(void) { return status; } +float Individual::getGeneticFitness(void) { return geneticFitness; } + +bool Individual::isViable() const { + float probViability = geneticFitness > 1.0 ? 1.0 : geneticFitness; + return probViability >= pRandom->Random(); +} + indStats Individual::getStats(void) { indStats s; s.stage = stage; s.sex = sex; s.age = age; s.status = status; s.fallow = fallow; @@ -551,340 +463,218 @@ void Individual::setSettPatch(const settlePatch s) { 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); +void Individual::setEmigTraits(Species* pSpecies, bool sexDep, bool densityDep) { + emigTraits e; + e.d0 = e.alpha = e.beta = 0.0; + if (sexDep) { + if (this->getSex() == MAL) { + e.d0 = this->getTrait(E_D0_M)->express(); + if (densityDep) { + e.alpha = getTrait(E_ALPHA_M)->express(); + e.beta = getTrait(E_BETA_M)->express(); } - else { - e.d0 = (float)pGenome->express(emiggenelocn + sex, 0, 0); } + else if (this->getSex() == FEM) { + e.d0 = this->getTrait(E_D0_F)->express(); + if (densityDep) { + e.alpha = getTrait(E_ALPHA_F)->express(); + e.beta = getTrait(E_BETA_F)->express(); } - 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); + throw runtime_error("Attempt to express invalid emigration trait sex."); } - 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); + e.d0 = this->getTrait(E_D0)->express(); + if (densityDep) { + e.alpha = getTrait(E_ALPHA)->express(); + e.beta = getTrait(E_BETA)->express(); } } - } - } - 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; + pEmigTraits = make_unique(); + pEmigTraits->d0 = e.d0; + pEmigTraits->alpha = e.alpha; + pEmigTraits->beta = e.beta; + + // Below must never trigger, phenotype is bounded in express() + if (pEmigTraits->d0 < 0.0) throw runtime_error("d0 value has become negative."); + if (pEmigTraits->d0 > 1.0) throw runtime_error("d0 value has exceeded 1."); 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; +emigTraits Individual::getIndEmigTraits(void) { + emigTraits e; + e.d0 = e.alpha = e.beta = 0.0; + if (pEmigTraits != 0) { + e.d0 = pEmigTraits->d0; + e.alpha = pEmigTraits->alpha; + e.beta = pEmigTraits->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); +void Individual::setIndKernelTraits(Species* pSpecies, bool sexDep, bool twinKernel, int resol) { + + trfrKernelParams k; + k.meanDist1 = k.meanDist2 = k.probKern1 = 0.0; + + if (sexDep) { + if (this->sex == MAL) { + k.meanDist1 = getTrait(KERNEL_MEANDIST_1_M)->express(); + + if (twinKernel) { // twin kernel + k.meanDist2 = getTrait(KERNEL_MEANDIST_2_M)->express(); + k.probKern1 = getTrait(KERNEL_PROBABILITY_M)->express(); } - else { - k.meanDist1 = (float)pGenome->express(kerngenelocn + sex, 0, sex); } + else if (this->sex == FEM) { + k.meanDist1 = getTrait(KERNEL_MEANDIST_1_F)->express(); + + if (twinKernel) { // twin kernel + k.meanDist2 = getTrait(KERNEL_MEANDIST_2_F)->express(); + k.probKern1 = getTrait(KERNEL_PROBABILITY_F)->express(); } - 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); + throw runtime_error("Attempt to express invalid kernel transfer trait sex."); } - 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); + k.meanDist1 = getTrait(KERNEL_MEANDIST_1)->express(); + + if (twinKernel) { // twin kernel + k.meanDist2 = getTrait(KERNEL_MEANDIST_2)->express(); + k.probKern1 = getTrait(KERNEL_PROBABILITY)->express(); } } - } - } - 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); + float meanDist1 = (float)(k.meanDist1); + float meanDist2 = (float)(k.meanDist2); + float probKern1 = (float)(k.probKern1); + 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 (meanDist1 < resol) meanDist1 = (float)resol; + if (meanDist2 < resol) meanDist2 = (float)resol; } - if (kerntraits->probKern1 < 0.0) kerntraits->probKern1 = 0.0; - if (kerntraits->probKern1 > 1.0) kerntraits->probKern1 = 1.0; + if (probKern1 < 0.0) probKern1 = 0.0; + if (probKern1 > 1.0) probKern1 = 1.0; + auto& pKernel = dynamic_cast(*pTrfrData); + pKernel.meanDist1 = meanDist1; + pKernel.meanDist2 = meanDist2; + pKernel.probKern1 = probKern1; + 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; +trfrKernelParams Individual::getIndKernTraits(void) { + trfrKernelParams k; k.meanDist1 = k.meanDist2 = k.probKern1 = 0.0; + if (pTrfrData != 0) { + + auto& pKernel = dynamic_cast(*pTrfrData); + + k.meanDist1 = pKernel.meanDist1; + k.meanDist2 = pKernel.meanDist2; + k.probKern1 = pKernel.probKern1; } + return k; } -// Set phenotypic transfer by SMS traits -void Individual::setSMSTraits(Species* pSpecies, short SMSgenelocn, short nSMStraits, - bool sexdep) { - trfrSMSTraits s = pSpecies->getSMSTraits(); +void Individual::setIndSMSTraits(Species* pSpecies) { + + trfrSMSTraits s = pSpecies->getSpSMSTraits(); + 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); + dp = getTrait(SMS_DP)->express(); + if (s.goalType == 2) { + gb = getTrait(SMS_GB)->express(); + alphaDB = getTrait(SMS_ALPHADB)->express(); + betaDB = getTrait(SMS_BETADB)->express(); } - } - } - } - 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); + + auto& pSMS = dynamic_cast(*pTrfrData); + pSMS.dp = (float)(dp); + pSMS.gb = (float)(gb); if (s.goalType == 2) { - smsData->alphaDB = (float)(alphaDB * smsparams.alphaDBScale + smsparams.alphaDBMean); - smsData->betaDB = (int)(betaDB * smsparams.betaDBScale + smsparams.betaDBMean + 0.5); + pSMS.alphaDB = (float)(alphaDB); + pSMS.betaDB = (int)(betaDB); } else { - smsData->alphaDB = s.alphaDB; - smsData->betaDB = s.betaDB; + pSMS.alphaDB = s.alphaDB; + pSMS.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; + if (pSMS.dp < 1.0) pSMS.dp = 1.0; + if (pSMS.gb < 1.0) pSMS.gb = 1.0; + if (pSMS.alphaDB <= 0.0) pSMS.alphaDB = 0.000001f; + if (pSMS.betaDB < 1) pSMS.betaDB = 1; return; } +trfrData* Individual::getTrfrData(void) { + return pTrfrData.get(); +} + // Get phenotypic transfer by SMS traits -trfrSMSTraits Individual::getSMSTraits(void) { +trfrSMSTraits Individual::getIndSMSTraits(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; + if (pTrfrData != 0) { + + auto& pSMS = dynamic_cast(*pTrfrData); + + s.dp = pSMS.dp; s.gb = pSMS.gb; + s.alphaDB = pSMS.alphaDB; s.betaDB = pSMS.betaDB; } + return s; } + // Set phenotypic transfer by CRW traits -void Individual::setCRWTraits(Species* pSpecies, short CRWgenelocn, short nCRWtraits, - bool sexdep) { +void Individual::setIndCRWTraits(Species* pSpecies) { 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; + c.stepLength = getTrait(CRW_STEPLENGTH)->express(); + c.rho = getTrait(CRW_STEPCORRELATION)->express(); + + auto& pCRW = dynamic_cast(*pTrfrData); + pCRW.stepLength = (float)(c.stepLength); + pCRW.rho = (float)(c.rho); + if (pCRW.stepLength < 1.0) pCRW.stepLength = 1.0; + if (pCRW.rho < 0.0) pCRW.rho = 0.0; + if (pCRW.rho > 0.999) pCRW.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; +trfrCRWTraits Individual::getIndCRWTraits(void) { + + trfrCRWTraits c; + c.stepLength = c.rho = 0.0; + if (pTrfrData != 0) { + auto& pCRW = dynamic_cast(*pTrfrData); + c.stepLength = pCRW.stepLength; + c.rho = pCRW.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 Individual::getIndSettTraits(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; + if (pSettleTraits != 0) { + s.s0 = pSettleTraits->s0; + s.alpha = pSettleTraits->alpha; + s.beta = pSettleTraits->beta; } return s; @@ -892,11 +682,11 @@ settleTraits Individual::getSettTraits(void) { void Individual::setStatus(short s) { - if (s >= 0 && s <= 9) status = s; + if (s >= 0 && s <= 10) status = s; status = s; } -void Individual::developing(void) { +void Individual::setToDevelop(void) { isDeveloping = true; } @@ -905,7 +695,7 @@ void Individual::develop(void) { } void Individual::ageIncrement(short maxage) { - if (status < 6) { // alive + if (status < 6 || status == 10) { // alive age++; if (age > maxage) status = 9; // exceeds max. age - dies else { @@ -929,8 +719,7 @@ void Individual::moveto(Cell* newCell) { 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; + pCurrCell = newCell; status = 5; } } @@ -938,16 +727,14 @@ void Individual::moveto(Cell* newCell) { // 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) +int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, const bool absorbing) { - int patchNum = 0; int newX = 0, newY = 0; int dispersing = 1; double xrand, yrand, meandist, dist, r1, rndangle, nx, ny; float localK; - trfrKernTraits kern; + trfrKernelParams kern; Cell* pCell; Patch* pPatch; locn loc = pCurrCell->getLocn(); @@ -955,7 +742,7 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, landData land = pLandscape->getLandData(); bool usefullkernel = pSpecies->useFullKernel(); - trfrRules trfr = pSpecies->getTrfr(); + transferRules trfr = pSpecies->getTransferRules(); settleRules sett = pSpecies->getSettRules(stage, sex); pCell = NULL; @@ -963,30 +750,31 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, if (trfr.indVar) { // get individual's kernel parameters kern.meanDist1 = kern.meanDist2 = kern.probKern1 = 0.0; - if (pGenome != 0) { - kern.meanDist1 = kerntraits->meanDist1; + + auto& pKernel = dynamic_cast(*pTrfrData); + + kern.meanDist1 = pKernel.meanDist1; if (trfr.twinKern) { - kern.meanDist2 = kerntraits->meanDist2; - kern.probKern1 = kerntraits->probKern1; + kern.meanDist2 = pKernel.meanDist2; + kern.probKern1 = pKernel.probKern1; } } - } else { // get kernel parameters for the species if (trfr.sexDep) { if (trfr.stgDep) { - kern = pSpecies->getKernTraits(stage, sex); + kern = pSpecies->getSpKernTraits(stage, sex); } else { - kern = pSpecies->getKernTraits(0, sex); + kern = pSpecies->getSpKernTraits(0, sex); } } else { if (trfr.stgDep) { - kern = pSpecies->getKernTraits(stage, 0); + kern = pSpecies->getSpKernTraits(stage, 0); } else { - kern = pSpecies->getKernTraits(0, 0); + kern = pSpecies->getSpKernTraits(0, 0); } } } @@ -1001,10 +789,12 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, } 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) +# ifdef NDEBUG // bypass this requirement for tests if (!usefullkernel && meandist < 1.0) meandist = 1.0; +# endif int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 do { @@ -1023,30 +813,34 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, } } // randomise the position of the individual inside the cell + // so x and y are a corner of the cell? xrand = (double)loc.x + pRandom->Random() * 0.999; yrand = (double)loc.y + pRandom->Random() * 0.999; + // draw factor r1 0 < r1 <= 1 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 + dist = (-1.0 * meandist) * log(r1); 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 +#ifndef NDEBUG if (path != 0) (path->year)++; #endif loopsteps++; } while (loopsteps < 1000 && - ((!absorbing && (newX < land.minX || newX > land.maxX - || newY < land.minY || newY > land.maxY)) + // keep drawing if out of bounds of landscape or same cell + ((!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 + // this cannot be reached if not absorbing? pCell = 0; pPatch = nullptr; patchNum = -1; @@ -1068,7 +862,7 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, } } } - else { + else { // exceeded 1000 attempts pPatch = nullptr; patchNum = -1; } @@ -1077,6 +871,7 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, if (loopsteps < 1000) { if (pCell == 0) { // beyond absorbing boundary or in no-data cell + // only if absorbing=true and out of bounddaries pCurrCell = 0; status = 6; dispersing = 0; @@ -1089,6 +884,7 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, status = 2; // record as potential settler } else { + // unsuitable patch dispersing = 0; // can wait in matrix if population is stage structured ... if (pSpecies->stageStructured()) { @@ -1099,19 +895,18 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, 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; // dies (unless there is a suitable neighbouring cell) } } } - else { + else { // exceeded 1000 attempts 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) { + if (status < 7 || status == 10) { double dispmort; trfrMortParams mort = pSpecies->getMortParams(); if (trfr.distMort) { @@ -1135,7 +930,6 @@ int Individual::moveKernel(Landscape* pLandscape, Species* pSpecies, int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, const short landIx, const bool absorbing) { - if (status != 1) return 0; // not currently dispersing int patchNum; @@ -1148,12 +942,13 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, movedata move; Patch* pPatch = nullptr; bool absorbed = false; + //int popsize; landData land = pLandscape->getLandData(); simParams sim = paramsSim->getSim(); - trfrRules trfr = pSpecies->getTrfr(); - trfrCRWTraits movt = pSpecies->getCRWTraits(); + transferRules trfr = pSpecies->getTransferRules(); + trfrCRWTraits movt = pSpecies->getSpCRWTraits(); settleSteps settsteps = pSpecies->getSteps(stage, sex); pPatch = pCurrCell->getPatch(); @@ -1184,7 +979,7 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, } else { // take a step (path->year)++; - (path->total)++; + (path->total)++; if (pPatch == nullptr || patchNum == 0) { // not in a patch if (path != 0) path->settleStatus = 0; // reset path settlement status (path->out)++; @@ -1192,7 +987,6 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, loc = pCurrCell->getLocn(); newX = loc.x; newY = loc.y; - switch (trfr.moveType) { case 1: // SMS @@ -1205,9 +999,7 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, dispersing = 0; } else { - // WOULD IT BE MORE EFFICIENT FOR smsMove TO RETURN A POINTER TO THE NEW CELL? ... - pPatch = pCurrCell->getPatch(); if (sim.saveVisits && pPatch != pNatalPatch) { pCurrCell->incrVisits(); @@ -1216,20 +1008,21 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, break; case 2: // CRW + + auto & pCRW = dynamic_cast(*pTrfrData); + if (trfr.indVar) { - if (crw != 0) { - movt.stepLength = crw->stepL; - movt.rho = crw->rho; + movt.stepLength = pCRW.stepLength; + movt.rho = pCRW.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; + steplen = movt.stepLength; + rho = movt.rho; if (pPatch == pNatalPatch) { rho = 0.99; // to promote leaving natal patch path->out = 0; } - if (movt.straigtenPath && path->settleStatus > 0) { + if (movt.straightenPath && 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; @@ -1242,13 +1035,13 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, || 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); + angle = wrpcauchy(pCRW.prevdrn, 0.0); } else - angle = wrpcauchy(crw->prevdrn, rho); + angle = wrpcauchy(pCRW.prevdrn, rho); // new continuous cell coordinates - xcnew = crw->xc + sin(angle) * steplen / (float)land.resol; - ycnew = crw->yc + cos(angle) * steplen / (float)land.resol; + xcnew = pCRW.xc + sin(angle) * steplen / (float)land.resol; + ycnew = pCRW.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++; @@ -1265,8 +1058,8 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, else pPatch = pCurrCell->getPatch(); } while (!absorbing && pCurrCell == 0 && loopsteps < 1000); - crw->prevdrn = (float)angle; - crw->xc = (float)xcnew; crw->yc = (float)ycnew; + pCRW.prevdrn = (float)angle; + pCRW.xc = (float)xcnew; pCRW.yc = (float)ycnew; if (absorbed) { // beyond absorbing boundary or in no-data square status = 6; dispersing = 0; @@ -1286,7 +1079,6 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, break; } // end of switch (trfr.moveType) - if (dispersing == 1 && pPatch != nullptr // not no-data area or matrix && path->total >= settsteps.minSteps) { @@ -1311,7 +1103,6 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, } // end of single movement step return dispersing; - } //--------------------------------------------------------------------------- @@ -1322,12 +1113,10 @@ int Individual::moveStep(Landscape* pLandscape, Species* pSpecies, 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 = -9, newY = -9; + int newX = -9, newY = -9; // BUGFIX: must not be 0 because 0,0 is a valid landscape cell Cell* pCell; Cell* pNewCell = NULL; double sum_nbrs = 0.0; @@ -1335,6 +1124,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, int cellcost, newcellcost; locn current; + auto& pSMS = dynamic_cast(*pTrfrData); if (pCurrCell == 0) { // x,y is a NODATA square - this should not occur here @@ -1344,19 +1134,19 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, } landData land = pLand->getLandData(); - trfrSMSTraits movt = pSpecies->getSMSTraits(); + trfrSMSTraits movt = pSpecies->getSpSMSTraits(); current = pCurrCell->getLocn(); //get weights for directional persistence.... if ((path->out > 0 && path->out <= (movt.pr + 1)) || natalPatch - || (movt.straigtenPath && path->settleStatus > 0)) { + || (movt.straightenPath && path->settleStatus > 0)) { // inflate directional persistence to promote leaving the patch - if (indvar) nbr = getSimDir(current.x, current.y, 10.0f * smsData->dp); + if (indvar) nbr = getSimDir(current.x, current.y, 10.0f * pSMS.dp); else nbr = getSimDir(current.x, current.y, 10.0f * movt.dp); } else { - if (indvar) nbr = getSimDir(current.x, current.y, smsData->dp); + if (indvar) nbr = getSimDir(current.x, current.y, pSMS.dp); else nbr = getSimDir(current.x, current.y, movt.dp); } if (natalPatch || path->settleStatus > 0) path->out = 0; @@ -1372,12 +1162,13 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, nsteps = path->total; } if (indvar) { - double exp_arg = -((double)nsteps - (double)smsData->betaDB) * (-smsData->alphaDB); + double exp_arg = -((double)nsteps - (double)pSMS.betaDB) * (-pSMS.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)); + gb = 1.0 + (pSMS.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)); } @@ -1389,24 +1180,23 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // first check if costs have already been calculated { #ifdef _OPENMP - const std::unique_lock lock = pCurrCell->lockCost(); + const std::unique_lock lock = pCurrCell->lockCost(); #endif - hab = pCurrCell->getEffCosts(); + 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 - 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++) { + for (int y2 = 2; y2 > -1; y2--) { + for (int x2 = 0; x2 < 3; x2++) { if (x2 == 1 && y2 == 1) nbr.cell[x2][y2] = 0.0; else { if (x2 == 1 || y2 == 1) //not diagonal @@ -1418,8 +1208,8 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, } // determine reciprocal of effective cost for the 8 neighbours - for (y2 = 2; y2 > -1; y2--) { - for (x2 = 0; x2 < 3; x2++) { + for (int y2 = 2; y2 > -1; y2--) { + for (int x2 = 0; x2 < 3; x2++) { if (nbr.cell[x2][y2] > 0.0) nbr.cell[x2][y2] = 1.0f / nbr.cell[x2][y2]; } } @@ -1428,8 +1218,8 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // 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++) { + for (int y2 = 2; y2 > -1; y2--) { + for (int 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) @@ -1440,15 +1230,14 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, 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++) { + for (int y2 = 2; y2 > -1; y2--) { + for (int x2 = 0; x2 < 3; x2++) { nbr.cell[x2][y2] = nbr.cell[x2][y2] / (float)sum_nbrs; } } @@ -1458,13 +1247,15 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, double cumulative[9]; int j = 0; cumulative[0] = nbr.cell[0][0]; - for (y2 = 0; y2 < 3; y2++) { - for (x2 = 0; x2 < 3; x2++) { + for (int y2 = 0; y2 < 3; y2++) { + for (int x2 = 0; x2 < 3; x2++) { if (j != 0) cumulative[j] = cumulative[j - 1] + nbr.cell[x2][y2]; j++; } } + //to prevent very rare bug that random draw is greater than 0.999999999 + if (cumulative[8] != 1) cumulative[8] = 1; // select direction at random based on cell selection probabilities // landscape boundaries and no-data cells may be reflective or absorbing cellcost = pCurrCell->getCost(); @@ -1473,8 +1264,8 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, do { double rnd = pRandom->Random(); j = 0; - for (y2 = 0; y2 < 3; y2++) { - for (x2 = 0; x2 < 3; x2++) { + for (int y2 = 0; y2 < 3; y2++) { + for (int x2 = 0; x2 < 3; x2++) { if (rnd < cumulative[j]) { newX = current.x + x2 - 1; newY = current.y + y2 - 1; @@ -1513,6 +1304,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, memory.pop(); // remove oldest memory element } memory.push(current); // record previous location in memory + //if (write_out) out << "queue length is " << memory.size() << endl; pCurrCell = pNewCell; } return move; @@ -1527,6 +1319,7 @@ array3x3d Individual::getSimDir(const int x, const int y, const float dp) double theta; int xx, yy; + //if (write_out) out<<"step 0"<(*pTrfrData); if (goaltype == 0) { // no goal set for (xx = 0; xx < 3; xx++) { @@ -1577,7 +1378,7 @@ array3x3d Individual::getGoalBias(const int x, const int y, } else { d.cell[1][1] = 0; - if ((x - smsData->goal.x) == 0 && (y - smsData->goal.y) == 0) { + if ((x - pSMS.goal.x) == 0 && (y - pSMS.goal.y) == 0) { // at goal, set matrix to unity for (xx = 0; xx < 3; xx++) { for (yy = 0; yy < 3; yy++) { @@ -1597,7 +1398,8 @@ array3x3d Individual::getGoalBias(const int x, const int y, return d; } else // goaltype == 2 - theta = atan2(((double)x - (double)smsData->goal.x), ((double)y - (double)smsData->goal.y)); + theta = atan2(((double)x - (double)pSMS.goal.x), ((double)y - (double)pSMS.goal.y)); + // if (write_out) out<<"goalx,goaly: "<getLandData(); - if (absorbing) nodatacost = ABSNODATACOST; - else nodatacost = NODATACOST; + if (absorbing) nodatacost = gAbsorbingNoDataCost; + else nodatacost = gNoDataCost; 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 @@ -1700,6 +1502,7 @@ array3x3f Individual::getHabMatrix(Landscape* pLand, Species* pSpecies, // calculate effective mean cost of cells in perceptual range ncells = 0; weight = 0.0; sumweights = 0.0; + // targetseen = 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++) { @@ -1709,6 +1512,10 @@ array3x3f Individual::getHabMatrix(Landscape* pLand, Species* pSpecies, 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 (write_out && (x4 < 0 || y4 < 0)) { + // out<<"ERROR: x "< land.maxX || y4 < 0 || y4 > land.maxY) { // unexpected problem - e.g. due to ridiculously large PR // treat as a no-data cell @@ -1730,7 +1537,7 @@ array3x3f Individual::getHabMatrix(Landscape* pLand, Species* pSpecies, pCell->setCost(cost); } else { - + // nothing? } } } @@ -1776,6 +1583,8 @@ array3x3f Individual::getHabMatrix(Landscape* pLand, Species* pSpecies, } } } + // if (write_out2) out2<<"effective mean cost "<outGenFinishReplicate(); -} - -// Open genetics file and write header record -void Individual::outGenStartReplicate(const int rep, const int landNr, const bool xtab) -{ - pGenome->outGenStartReplicate(rep, landNr, xtab); -} - -// Write records to genetics file -void Individual::outGenetics(const int rep, const int year, const int spnum, - const bool xtab) -{ - if (pGenome != 0) { - pGenome->outGenetics(rep, year, spnum, indId, xtab); - } -} - #if RS_RCPP #ifdef _OPENMP std::mutex outMovePaths_mutex; @@ -1838,7 +1625,7 @@ void Individual::outMovePath(const int year) << endl; } // if not anymore dispersing... - if (status > 1 && status < 10) { + if (status > 1 && status <= 10) { prev_loc = pPrevCell->getLocn(); // record only if this is the first step as non-disperser if (path->pathoutput) { @@ -1890,53 +1677,72 @@ double cauchy(double location, double scale) { //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- +#ifdef UNIT_TESTS -#if RSDEBUG - - -void testIndividual() { - - Species* pSpecies = new Species(); - - 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, 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(pSpecies, 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 +Cell* Individual::getCurrCell() const { + return pCurrCell; +} +void Individual::setInitAngle(const float angle) { + auto pCRW = dynamic_cast(pTrfrData.get()); + pCRW->prevdrn = angle; +} - // Disperses - // Emigrates - // Transfers - // Settles +// Force mutations to trigger for all traits +void Individual::triggerMutations(Species* pSp) { + for (auto const& [trType, indTrait] : spTraitTable) { + indTrait->mutate(); + if (trType == GENETIC_LOAD1 + || trType == GENETIC_LOAD2 + || trType == GENETIC_LOAD3 + || trType == GENETIC_LOAD4 + || trType == GENETIC_LOAD5) + geneticFitness *= indTrait->express(); + } + this->expressDispersalPhenotypes(pSp, 1.0); +} - // Survives +// Shorthand function to edit a genotype with custom values +void Individual::overrideGenotype(TraitType whichTrait, const map>>& newGenotype) { - // Develops + GeneticFitnessTrait* pGenFitTrait; + DispersalTrait* pDispTrait; -} -#endif // RSDEBUG + switch (whichTrait) + { + case GENETIC_LOAD1: case GENETIC_LOAD2: case GENETIC_LOAD3: case GENETIC_LOAD4: case GENETIC_LOAD5: + pGenFitTrait = dynamic_cast(this->getTrait(whichTrait)); + pGenFitTrait->getGenes() = newGenotype; + break; + case E_D0: case E_ALPHA: case E_BETA: + case S_S0: case S_ALPHA: case S_BETA: + case E_D0_F: case E_ALPHA_F: case E_BETA_F: + case S_S0_F: case S_ALPHA_F: case S_BETA_F: + case E_D0_M: case E_ALPHA_M: case E_BETA_M: + case S_S0_M: case S_ALPHA_M: case S_BETA_M: + case CRW_STEPLENGTH: case CRW_STEPCORRELATION: + case KERNEL_MEANDIST_1: case KERNEL_MEANDIST_2: case KERNEL_PROBABILITY: + case KERNEL_MEANDIST_1_F: case KERNEL_MEANDIST_2_F: case KERNEL_PROBABILITY_F: + case KERNEL_MEANDIST_1_M: case KERNEL_MEANDIST_2_M: case KERNEL_PROBABILITY_M: + case SMS_DP: case SMS_GB: case SMS_ALPHADB: case SMS_BETADB: + pDispTrait = dynamic_cast(this->getTrait(whichTrait)); + pDispTrait->getGenes() = newGenotype; + break; + default: + throw logic_error("Wrong trait type: please choose a valid dispersal or genetic fitness trait."); + break; + } +}; + +void Individual::overrideGenotype(TraitType whichTrait, const map>& newGenotype) { + + if (!whichTrait == NEUTRAL) { + throw logic_error("Attempt to override non-neutral trait with neutral trait genotype.\n"); + } + NeutralTrait* pNeutralTrait; + pNeutralTrait = dynamic_cast(this->getTrait(NEUTRAL)); + pNeutralTrait->getGenes() = newGenotype; +}; + +#endif // UNIT_TESTS diff --git a/Individual.h b/Individual.h index ad84aef..12e4a69 100644 --- a/Individual.h +++ b/Individual.h @@ -1,45 +1,45 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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. + RangeShifter v2.0 Individual -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 + Implements the Individual class -Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + Various optional attributes (genes for traits, movement parameters, etc.) are + allocated dynamically and accessed by pointers if required. -Last updated: 26 October 2021 by Steve Palmer + 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: 28 July 2021 by Greta Bocedi + + ------------------------------------------------------------------------------*/ #ifndef IndividualH #define IndividualH @@ -47,6 +47,7 @@ Last updated: 26 October 2021 by Steve Palmer #include #include +#include #include using namespace std; @@ -55,28 +56,26 @@ using namespace std; #include "Landscape.h" #include "Patch.h" #include "Cell.h" -#include "Genome.h" +#include "TraitFactory.h" #ifdef _OPENMP #include #endif // _OPENMP -#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; + short stage; sex_t 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 + // 0 = not set, + // 1 = debarred through density dependence rule + // 2 = OK to settle subject to finding a mate + #if RS_RCPP short pathoutput; #endif @@ -87,37 +86,99 @@ struct pathSteps { // nos. of steps for movement model struct settlePatch { Patch* pSettPatch; short settleStatus; }; -struct crwParams { // to hold data for CRW movement model + +struct trfrData { + virtual void addMyself(trfrData& toAdd) = 0; + virtual void clone(const trfrData& copyFrom) = 0; + virtual void divideTraitsBy(int) = 0; + virtual movement_t getType() = 0; + virtual ~trfrData() {} +}; + +struct crwData : trfrData { // 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 xc, yc; // continuous cell co-ordinates float rho; // phenotypic step correlation coefficient + float stepLength; // phenotypic step length (m) + + crwData(float prevdrnA, float xcA, float ycA) : prevdrn(prevdrnA), xc(xcA), yc(ycA), rho(0.0), stepLength(0.0) {} + ~crwData() {} + + void addMyself(trfrData& toAdd) { + auto& CRW = dynamic_cast(toAdd); + CRW.stepLength += stepLength; + CRW.rho += rho; + } + + movement_t getType() { return CRW; } + + void clone(const trfrData& copyFrom) { + const crwData& pCopy = dynamic_cast(copyFrom); + stepLength = pCopy.stepLength; + rho = pCopy.rho; + } + + void divideTraitsBy(int i) { + stepLength /= i; + rho /= i; + } + }; struct array3x3d { double cell[3][3]; }; struct movedata { float dist; float cost; }; -struct smsdata { +struct smsData : trfrData { 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) + + smsData(locn prevA, locn goalA) : prev(prevA), goal(goalA), dp(0.0), gb(0.0), alphaDB(0.0), betaDB(0) {} + ~smsData() {} + + void addMyself(trfrData& toAdd) { + auto& SMS = dynamic_cast(toAdd); + SMS.dp += dp; + SMS.gb += gb; + SMS.alphaDB += alphaDB; + SMS.betaDB += betaDB; + } + + movement_t getType() { return SMS; } + + void clone(const trfrData& copyFrom) { + auto& pCopy = dynamic_cast(copyFrom); + dp = pCopy.dp; + gb = pCopy.gb; + alphaDB = pCopy.alphaDB; + betaDB = pCopy.betaDB; + } + + void divideTraitsBy(int i) { + dp /= i; + gb /= i; + alphaDB /= i; + betaDB /= i; + } + }; // A class that mimicks std::queue with a fixed-size circular buffer. template class MemoryQueue { - std::unique_ptr data; + std::unique_ptr data; const std::size_t space; std::size_t begin_idx; std::size_t nb_elts; public: MemoryQueue(std::size_t size); - T &front(); - T const &front() const; - T &back(); - T const &back() const; + T& front(); + T const& front() const; + T& back(); + T const& back() const; void push(const T& value); void push(T&& value); void pop(); @@ -126,6 +187,40 @@ class MemoryQueue { bool full() const; }; +struct kernelData : trfrData { + float meanDist1; + float meanDist2; + float probKern1; + + kernelData(float meanDist1A, float meanDist2A, float probKern1A) : meanDist1(meanDist1A), meanDist2(meanDist2A), probKern1(probKern1A) {} + ~kernelData() {} + + void addMyself(trfrData& toAdd) { + + auto& Kernel = dynamic_cast(toAdd); + + Kernel.meanDist1 += meanDist1; + Kernel.meanDist2 += meanDist2; + Kernel.probKern1 += probKern1; + } + + movement_t getType() { return KERNEL; } + + void clone(const trfrData& copyFrom) { + const kernelData& pCopy = dynamic_cast(copyFrom); + meanDist1 = pCopy.meanDist1; + meanDist2 = pCopy.meanDist2; + probKern1 = pCopy.probKern1; + } + + void divideTraitsBy(int i) { + meanDist1 /= i; + meanDist2 /= i; + probKern1 /= i; + } +}; + + class Individual { public: @@ -134,6 +229,7 @@ class Individual { #else static int indCounter; // used to create ID, held by class, not members of class #endif + static TraitFactory traitFactory; Individual( // Individual constructor Species*, // pointer to species Cell*, // pointer to Cell @@ -146,62 +242,65 @@ class Individual { short // movement type: 1 = SMS, 2 = CRW ); ~Individual(void); - void setGenes( // Set genes for individual variation from species initialisation parameters + void setUpGenes( // Set genes for individual variation from species initialisation parameters Species*, // pointer to Species int // Landscape resolution ); - void setGenes( // Inherit genome from parents + void inheritTraits( // 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 inheritTraits(Species* pSpecies, Individual* mother, int resol); //haploid + + void expressDispersalPhenotypes(Species* pSpecies, int resol); + + void expressGeneticLoad(Species* pSpecies); + + QuantitativeTrait* getTrait(TraitType trait) const; + + set getTraitTypes(); + + void inherit(Species* pSpecies, const Individual* mother, const Individual* father); + + void inherit(Species* pSpecies, const Individual* mother); // haploid + + void setEmigTraits(Species* pSpecies, bool sexDep, bool densityDep); + void setTransferTraits(Species* pSpecies, transferRules trfr, int resol); + + emigTraits getIndEmigTraits(void); // Get phenotypic emigration traits + + void setIndKernelTraits(Species* pSpecies, bool sexDep, bool twinKernel, int resol); + + trfrKernelParams getIndKernTraits(void); // Get phenotypic transfer by kernel traits + + void setIndSMSTraits(Species* pSpecies); + + trfrSMSTraits getIndSMSTraits(void); // Get phenotypic transfer by SMS traits + + void setIndCRWTraits(Species* pSpecies); + + trfrCRWTraits getIndCRWTraits(void); // Get phenotypic transfer by CRW traits + + void setSettlementTraits(Species* pSpecies, bool sexDep, bool densDep); + + settleTraits getIndSettTraits(void); // Get phenotypic settlement traits + + trfrData* getTrfrData(void); + void setEmigTraits(const emigTraits& emig); + void setSettleTraits(const settleTraits& settle); - 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); + sex_t getSex(void); int getStatus(void); + float getGeneticFitness(void); + bool isViable() const; indStats getStats(void); Cell* getPrevCell(); // Return previous location (as pointer to Cell) Cell* getCurrCell(); // Return current location (as pointer to Cell) @@ -211,7 +310,7 @@ class Individual { settlePatch getSettPatch(void); void setSettPatch(const settlePatch); void setStatus(short); - void developing(void); + void setToDevelop(void); void develop(void); void ageIncrement( // Age by one year short // maximum age - if exceeded, the Individual dies @@ -227,7 +326,6 @@ class Individual { 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 @@ -271,78 +369,68 @@ class Individual { const short, // landscape change index const bool // absorbing boundaries? ); - void outGenFinishReplicate(); // Close genetics file - void outGenStartReplicate( // Open genetics file and write header record - const int, // replicate - const int, // landscape number - const bool // output as cross table? - ); - void outGenetics( // Write records to genetics file - const int, // replicate - const int, // year - const int, // species number - const bool // output as cross table? - ); #if RS_RCPP void outMovePath( // Write records to movement paths file const int // year ); #endif +#ifdef UNIT_TESTS + // Testing utilities + Cell* getCurrCell() const; + void setInitAngle(const float angle); + void insertIndDispTrait(TraitType trType, DispersalTrait tr) { + spTraitTable.insert(make_pair(trType, make_unique(tr))); + }; + void triggerMutations(Species* pSp); + // Shorthand function to edit a genotype with custom values + void overrideGenotype(TraitType whichTrait, const map>>& newGenotype); // dispersal + gen. fitness + void overrideGenotype(TraitType whichTrait, const map>& newGenotype); // neutral +#endif private: int indId; + float geneticFitness; short stage; - short sex; + sex_t 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 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 + Cell* pPrevCell; // pointer to previous Cell + Cell* pCurrCell; // pointer to current Cell + Patch* pNatalPatch; // pointer to natal Patch + pathData* path; // pointer to path data for movement model + std::unique_ptr pEmigTraits; // pointer to emigration traits + std::unique_ptr pSettleTraits; // pointer to settlement traits + std::unique_ptr pTrfrData; //can be sms, kernel, crw MemoryQueue memory; // memory of last N squares visited for SMS - - Genome *pGenome; - + map> spTraitTable; }; //--------------------------------------------------------------------------- -double cauchy(double location, double scale) ; -double wrpcauchy (double location, double rho = exp(double(-1))); +double cauchy(double location, double scale); +double wrpcauchy(double location, double rho = exp(double(-1))); -extern RSrandom *pRandom; - -#if RSDEBUG -extern ofstream DEBUGLOG; -#endif +extern RSrandom* pRandom; #if RS_RCPP extern ofstream outMovePaths; #endif -#if RSDEBUG -void testIndividual(); -#endif - //--------------------------------------------------------------------------- #endif // IndividualH diff --git a/Landscape.cpp b/Landscape.cpp index f4f0118..a61a1df 100644 --- a/Landscape.cpp +++ b/Landscape.cpp @@ -40,7 +40,7 @@ ofstream outMovePaths; InitDist::InitDist(Species* pSp) { pSpecies = pSp; - resol = 0; + resol = 1; maxX = 0; maxY = 0; minEast = 0.0; @@ -171,15 +171,15 @@ int InitDist::readDistribution(string distfile) { #endif // open distribution file -#if !RS_RCPP || RSWIN64 +// #if RS_RCPP +// 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)); +// } +// #else 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 +// #endif if (!dfile.is_open()) return 21; // read landscape data from header records of distribution file @@ -244,7 +244,48 @@ if (!dfile.eof()) EOFerrorR(distfile); return 0; } +// Read species initial distribution file for threadsafe option +#if RS_RCPP +int InitDist::readDistribution(Rcpp::NumericMatrix distfile, landOrigin habfile_origin, int spResol) { + + int d=0; + double dfloat=0; + int ncols,nrows; + + ncols = distfile.ncol(); + nrows = distfile.nrow(); + + minEast = habfile_origin.minEast; + minNorth = habfile_origin.minNorth; + resol = spResol; + maxX = ncols-1; + maxY = nrows-1; + + for (int y = nrows-1; y >= 0; y--) { + for (int x = 0; x < ncols; x++) { + + dfloat = distfile(nrows-1-y,x); + + if ( !R_IsNA(dfloat) ){ // check for NA + d = (int)dfloat; + if ( d == 0 || d == 1) { // only valid values + if (d == 1) { // species present + cells.push_back(new DistCell(x,y)); + } + } + else { // error in file +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid value in species distribution raster." << std::endl; +#endif + return 22; + } + } + } + } + return 0; +} +#endif //--------------------------------------------------------------------------- // Landscape functions @@ -252,7 +293,8 @@ if (!dfile.eof()) EOFerrorR(distfile); Landscape::Landscape(void) { patchModel = false; spDist = false; generated = false; fractal = false; continuous = false; dynamic = false; habIndexed = false; - resol = spResol = landNum = 0; + spatialdemog = false; + resol = spResol = 1; landNum = 0; rasterType = 0; nHab = nHabMax = 0; dimX = dimY = 100; @@ -274,9 +316,7 @@ 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) { @@ -286,6 +326,7 @@ Landscape::~Landscape() { delete[] cells; cells = 0; } + int npatches = (int)patches.size(); for (int i = 0; i < npatches; i++) if (patches[i] != NULL) delete patches[i]; @@ -301,7 +342,6 @@ Landscape::~Landscape() { if (initcells[i] != NULL) delete initcells[i]; initcells.clear(); - patchnums.clear(); habCodes.clear(); landchanges.clear(); patchchanges.clear(); @@ -309,18 +349,15 @@ Landscape::~Landscape() { 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++) { @@ -337,9 +374,12 @@ void Landscape::resetLand(void) { void Landscape::setLandParams(landParams ppp, bool batchMode) { - generated = ppp.generated; patchModel = ppp.patchModel; spDist = ppp.spDist; + generated = ppp.generated; + patchModel = ppp.patchModel; + spDist = ppp.spDist; dynamic = ppp.dynamic; landNum = ppp.landNum; + spatialdemog = ppp.spatialdemog; 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) @@ -375,6 +415,7 @@ landParams Landscape::getLandParams(void) landParams ppp; ppp.generated = generated; ppp.patchModel = patchModel; ppp.spDist = spDist; ppp.dynamic = dynamic; + ppp.spatialdemog = spatialdemog; ppp.landNum = landNum; ppp.resol = resol; ppp.spResol = spResol; ppp.rasterType = rasterType; @@ -504,6 +545,7 @@ void Landscape::clearHabitats(void) { //--------------------------------------------------------------------------- void Landscape::setCellArray(void) { if (cells != 0) resetLand(); + //cells = new Cell **[maxY+1]; cells = new Cell * *[dimY]; for (int y = dimY - 1; y >= 0; y--) { cells[y] = new Cell * [dimX]; @@ -513,23 +555,11 @@ void Landscape::setCellArray(void) { } } -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) +void Landscape::generatePatches() { int x, y, ncells; double p; @@ -537,7 +567,6 @@ void Landscape::generatePatches(void) Cell* pCell; vector ArtLandscape; - setCellArray(); int patchnum = 0; // initial patch number for cell-based landscape @@ -705,12 +734,10 @@ void Landscape::allocatePatches(Species* pSpecies) 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; @@ -766,6 +793,14 @@ void Landscape::addNewCellToLand(int x, int y, int hab) { cells[y][x] = new Cell(x, y, 0, hab); } +void Landscape::addCellToLand(Cell* c) { + if (cells == 0) throw runtime_error("Landscape cells member is uninitialised."); + if (c->getHabIndex(0) < 0.0) + throw logic_error("Can't add no-data cell to landscape."); + locn l = c->getLocn(); + cells[l.y][l.x] = c; +} + 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; @@ -845,6 +880,46 @@ Patch* Landscape::findPatch(int num) { return 0; } + +set Landscape::getPatchNbs() const { + set patchNbs; + for (auto& p : patches) + patchNbs.emplace(p->getPatchNum()); + return patchNbs; +} + +set Landscape::samplePatches(const string& samplingOption, int nbToSample, Species* pSpecies) { + + vector sampledPatches; + vector eligiblePatches; + + // Get list of viable patches where the species is present + for (auto p : patches) { + if (p->getPatchNum() == 0) continue; // skip patch 0, the matrix + if (samplingOption == "random" // then all patches are eligible + || p->speciesIsPresent(pSpecies)) // otherwise only patches with at least 1 ind + eligiblePatches.push_back(p->getPatchNum()); + } + + if (samplingOption == "all") { + sampledPatches = eligiblePatches; + } + else if (samplingOption == "random_occupied" || samplingOption == "random") { + if (nbToSample > eligiblePatches.size()) + nbToSample = eligiblePatches.size(); + auto rng = pRandom->getRNG(); + sample(eligiblePatches.begin(), eligiblePatches.end(), std::back_inserter(sampledPatches), + nbToSample, rng); + } + else { + throw logic_error("Sampling option should be random, random_occupied or all when sampling patches."); + } + + set patchIds; + copy(sampledPatches.begin(), sampledPatches.end(), inserter(patchIds, patchIds.end())); + return patchIds; +} + void Landscape::resetPatchPopns(void) { int npatches = (int)patches.size(); for (int i = 0; i < npatches; i++) { @@ -869,11 +944,35 @@ void Landscape::updateCarryingCapacity(Species* pSpecies, int yr, short landIx) } +void Landscape::updateDemoScalings(short landIx) { + + patchLimits landlimits; + landlimits.xMin = minX; landlimits.xMax = maxX; + landlimits.yMin = minY; landlimits.yMax = maxY; + + if(spatialdemog && rasterType == 2) {// demographic scaling only implemented for habitat quality maps + int npatches = (int)patches.size(); // new: for (auto& p : patches) + for (int i = 0; i < npatches; i++) { + if (patches[i]->getPatchNum() != 0) { // not matrix patch + // calculate local scaling for each patch from its constituent cells + patches[i]->setPatchDemoScaling(landIx, landlimits); + } + } + } +} + Cell* Landscape::findCell(int x, int y) { if (x >= 0 && x < dimX && y >= 0 && y < dimY) return cells[y][x]; else return 0; } +bool Landscape::checkDataCell(int x, int y) { + Cell* pCell; + pCell = findCell(x, y); + return true; +} + + int Landscape::patchCount(void) { return (int)patches.size(); } @@ -942,10 +1041,8 @@ void Landscape::updateHabitatIndices(void) { 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); } } @@ -959,6 +1056,7 @@ void Landscape::setEnvGradient(Species* pSpecies, bool initial) { float dist_from_opt, dev; float habK; + //int hab; double envval; // gradient parameters envGradParams grad = paramsGrad->getGradient(); @@ -982,7 +1080,6 @@ void Landscape::setEnvGradient(Species* pSpecies, bool initial) break; } } - if (habK > 0.0) { // suitable cell if (initial) { // set local environmental deviation cells[y][x]->setEnvDev((float)pRandom->Random() * (2.0f) - 1.0f); @@ -1065,8 +1162,8 @@ void Landscape::addLandChange(landChange 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"; + landChange c; c.chgNb = c.chgYear = 0; + c.pathHabFile = c.pathPatchFile = c.pathCostFile = "none"; int nchanges = (int)landchanges.size(); if (ix < nchanges) c = landchanges[ix]; return c; @@ -1076,18 +1173,196 @@ void Landscape::deleteLandChanges(void) { while (landchanges.size() > 0) landchanges.pop_back(); landchanges.clear(); } +#if RS_RCPP +int Landscape::readLandChange(int filenum, Rcpp::NumericMatrix habfile, Rcpp::NumericMatrix pchfile, Rcpp::NumericMatrix costfile, Rcpp::NumericVector scalinglayers){ -#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) + if (filenum < 0) return 19; + + int h = 0, p = 0, c = 0, pchseq = 0; + double hfloat = 0,pfloat = 0,cfloat = 0; + bool costs = false; + if(costfile.nrow()>0 && costfile.ncol()>0) costs = true; + + arma::vec cellDemoScalings; // vector to store local demog scalings + Rcpp::IntegerVector DSdim; + int nrDemogScaleLayers = 0; + if(scalinglayers.attr("dim")==R_NilValue) DSdim = Rcpp::IntegerVector::create(1,1,1); + else{ + DSdim = scalinglayers.attr("dim"); + if(DSdim.size()>2) nrDemogScaleLayers = DSdim[2]; //nr of slices on cube + else nrDemogScaleLayers = 1; + } + arma::cube scalingCube(scalinglayers.begin(),DSdim[0],DSdim[1],nrDemogScaleLayers,false); // turn scaling layers into a cube + + simParams sim = paramsSim->getSim(); + + if (patchModel) pchseq = patchCount(); + + 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++) { + + // get numerics from each raster for this cell + hfloat = habfile(dimY-1-y,x); + if (patchModel) pfloat = pchfile(dimY-1-y,x); + if (costs) cfloat = costfile(dimY-1-y,x); + + if (cells[y][x] != 0) { // not a no data cell (in initial landscape) + if ( R_IsNA(hfloat) ){ // invalid no data cell in change map + return 36; + } + else { + h = (int)hfloat; + if (h < 0 || (sim.batchMode && (h < 1 || h > nHabMax))) { // invalid habitat code + return 33; + } + else { + addHabCode(h); + cells[y][x]->setHabIndex(h); + } + } + if (patchModel) { + if ( R_IsNA(pfloat) ){ + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + #endif + return 34; + } + else { + p = (int)pfloat; + if (p < 0 ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; + #endif + return 34; + } + else { + patchChgMatrix[y][x][2] = p; + if (p > 0 && !existsPatch(p)) { + // addPatchNum(p); // was removed - why? + newPatch(pchseq++,p); + } + } + } + } + if (costs) { + if ( R_IsNA(cfloat) ){ // invalid cost + return 38; + } + else{ + c = (int)cfloat; + if (c < 1) { // invalid cost + return 38; + } + else { + costsChgMatrix[y][x][2] = c; + } + } + } + } + } + } + break; + + case 2: // habitat quality + + for (int y = dimY-1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + + // get numerics from each raster for this cell + hfloat = habfile(dimY-1-y,x); + if (patchModel) pfloat = pchfile(dimY-1-y,x); + if (costs) cfloat = costfile(dimY-1-y,x); + + if (cells[y][x] != 0) { // not a no data cell (in initial landscape) + if ( R_IsNA(hfloat) ){ // invalid no data cell in change map + return 36; + } + else { + if (hfloat < 0.0 || hfloat > 100.0) { // invalid quality score + return 37; + } + else { + cells[y][x]->setHabitat((float)hfloat); + } + } + if (patchModel) { + if ( R_IsNA(pfloat) ){ + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + #endif + return 34; + } + else { + p = (int)pfloat; + if (p < 0 ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; + #endif + return 34; + } + else { + patchChgMatrix[y][x][2] = p; + if (p > 0 && !existsPatch(p)) { + //addPatchNum(p); // was removed - why? + newPatch(pchseq++,p); + } + } + } + } + if (costs) { + if ( R_IsNA(cfloat) ){ // invalid cost + return 38; + } + else{ + c = (int)cfloat; + if (c < 1) { // invalid cost + return 38; + } + else { + costsChgMatrix[y][x][2] = c; + } + } + } + //SPATIALDEMOG + // read demographic scalings + if(nrDemogScaleLayers>0){ + // get tube at (y/x) + cellDemoScalings = scalingCube(arma::span(dimY-1-y), arma::span(x), arma::span::all); + if(cellDemoScalings.n_elem==(unsigned)nDSlayer){ + // set vector percentage values in cell + cells[y][x]->addchgDemoScaling(arma::conv_to< std::vector >::from(cellDemoScalings)); + } + else{// invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Wrong number of demographic scaling layers in cell " << x << " ," << y << " at dyn land change nr " << filenum << std::endl; + #endif + return 39; + } + }// SPATIALDEMOG End + + } + } + } + break; + + default: + break; + } + + return 0; +} #endif -{ -#if RSDEBUG - DEBUGLOG << "Landscape::readLandChange(): filenum=" << filenum << " costs=" << int(costs) - << endl; +#if RS_RCPP && !R_CMD // normal file input +int Landscape::readLandChange(int filenum, bool costs, wifstream& hfile, wifstream& pfile, wifstream& cfile, int habnodata, int pchnodata, int costnodata, vector scalinglayers) +#else +int Landscape::readLandChange(int filenum, bool costs, vector scalinglayers) #endif +{ #if RS_RCPP wstring header; @@ -1112,17 +1387,17 @@ int Landscape::readLandChange(int filenum, bool costs) #if !RS_RCPP || R_CMD // open habitat file and optionally also patch and costs files - hfile.open(landchanges[filenum].habfile.c_str()); + hfile.open(landchanges[filenum].pathHabFile.c_str()); if (!hfile.is_open()) return 30; if (patchModel) { - pfile.open(landchanges[filenum].pchfile.c_str()); + pfile.open(landchanges[filenum].pathPatchFile.c_str()); if (!pfile.is_open()) { hfile.close(); hfile.clear(); return 31; } } if (costs) { - cfile.open(landchanges[filenum].costfile.c_str()); + cfile.open(landchanges[filenum].pathCostFile.c_str()); if (!cfile.is_open()) { hfile.close(); hfile.clear(); if (pfile.is_open()) { @@ -1256,7 +1531,7 @@ int Landscape::readLandChange(int filenum, bool costs) else { patchChgMatrix[y][x][2] = p; if (p > 0 && !existsPatch(p)) { - addPatchNum(p); + // addPatchNum(p); why is it removed? newPatch(pchseq++, p); } } @@ -1397,7 +1672,7 @@ int Landscape::readLandChange(int filenum, bool costs) else { patchChgMatrix[y][x][2] = p; if (p > 0 && !existsPatch(p)) { - addPatchNum(p); + // addPatchNum(p); // was removed - why? newPatch(pchseq++, p); } } @@ -1443,6 +1718,13 @@ int Landscape::readLandChange(int filenum, bool costs) if (hfile.is_open()) { hfile.close(); hfile.clear(); } if (pfile.is_open()) { pfile.close(); pfile.clear(); } if (cfile.is_open()) { cfile.close(); cfile.clear(); } + + // add here the reading of demographic scaling layers + if(scalinglayers.size()>0){ + int retcode = readDemographicScaling(scalinglayers); + if (retcode < 0) return 54; //change number + } + return 0; } @@ -1511,8 +1793,11 @@ void Landscape::recordPatchChanges(int landIx) { 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]; + 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; } @@ -1531,6 +1816,8 @@ void Landscape::deletePatchChgMatrix(void) { // Create & initialise costs change matrix void Landscape::createCostsChgMatrix(void) { + //intptr patch; + //Patch *pPatch; Cell* pCell; if (costsChgMatrix != 0) deleteCostsChgMatrix(); costsChgMatrix = new int** [dimY]; @@ -1552,9 +1839,7 @@ void Landscape::createCostsChgMatrix(void) } void Landscape::recordCostChanges(int landIx) { -#if RSDEBUG - DEBUGLOG << "Landscape::recordCostChanges(): landIx=" << landIx << endl; -#endif + if (costsChgMatrix == 0) return; // should not occur costChange chg; @@ -1609,7 +1894,24 @@ void Landscape::deleteCostsChgMatrix(void) { // Species distribution functions -int Landscape::newDistribution(Species* pSpecies, string distname) { +//If file input as R objects only for #RS_RCPP +#if RS_RCPP +int Landscape::newDistribution(Species *pSpecies, Rcpp::NumericMatrix distname, int spResol) { + int readcode; + int ndistns = (int)distns.size(); + distns.push_back(new InitDist(pSpecies)); + landOrigin habfile_origin = this->getOrigin(); + readcode = distns[ndistns]->readDistribution(distname,habfile_origin,spResol); + if (readcode != 0) { // error encountered + // delete the distribution created above + delete distns[ndistns]; + distns.pop_back(); + } + return readcode; +} +#endif +// Standard file input +int Landscape::newDistribution(Species *pSpecies, string distname) { int readcode; int ndistns = (int)distns.size(); distns.push_back(new InitDist(pSpecies)); @@ -1622,6 +1924,7 @@ int Landscape::newDistribution(Species* pSpecies, string distname) { return readcode; } + void Landscape::setDistribution(Species* pSpecies, int nInit) { // WILL NEED TO SELECT DISTRIBUTION FOR CORRECT SPECIES ... // ... CURRENTLY IT IS THE ONLY ONE @@ -1640,10 +1943,6 @@ bool Landscape::inInitialDist(Species* pSpecies, locn loc) { } 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(); } @@ -1717,367 +2016,663 @@ void Landscape::clearInitCells(void) { // 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 - +// for new landscape input using R objects AND spatial demography: only for RS_RCPP +// RS_THREADSAFE and SPATIALDEMOG #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(); - +int Landscape::readLandscape(int fileNum, Rcpp::NumericMatrix habfile, Rcpp::NumericMatrix pchfile, Rcpp::NumericMatrix costfile, Rcpp::NumericVector scalinglayers) { 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 + int habCode,seq,patchCode,ncols,nrows,hc,maxcost = 0; + double habFloat,patchFloat,costFloat; + Patch *pPatch; + Cell *pCell; + simParams sim = paramsSim->getSim(); 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 -double tmpresol; -hfile >> header >> ncols >> header >> nrows >> header >> minEast >> header >> minNorth - >> header >> tmpresol >> header >> habnodata; -resol = (int) tmpresol; - -#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; + // initialise landscape size + ncols = habfile.ncol(); + nrows = habfile.nrow(); + 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 + patchCode= 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++); + if (fileNum == 0) { + newPatch(seq,patchCode); + seq++; + patchCode++; + } 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 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 - } + + // read value from raster cell + habFloat = habfile(dimY-1-y,x); + // check for NA + if ( R_IsNA(habFloat) ) + addNewCellToLand(x,y,-1); // add cell only to landscape 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; + habCode= static_cast(habFloat); + if (habCode< 0 || (sim.batchMode && (habCode< 1 || habCode> nHabMax))) { + // invalid habitat code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat code." << std::endl; + #endif + return 13; + } + else { // valid habitat code + addHabCode(habCode); + if (patchModel) { + patchFloat = pchfile(dimY-1-y,x); + if ( R_IsNA(patchFloat) ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + #endif + return 14; + } + patchCode= static_cast(patchFloat); + if (patchCode< 0 ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; + #endif + return 14; + } + + // Does the patch already exists? + pPatch = patchCode == 0 ? nullptr : ( // matrix cell + existsPatch(patchCode) ? + findPatch(patchCode) : + newPatch(seq++, patchCode) + ); + addNewCellToPatch(pPatch, x, y, habCode); + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x,y,habCode); + } + } + } // end of habFloat is NA + } // end x + } // end y + break; + + case 1: // multiple % cover + + for (int y = dimY-1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + + habFloat = habfile(dimY-1-y,x); + if (fileNum == 0) { // first habitat cover layer + if ( R_IsNA(habFloat) ) { // check for NA + addNewCellToLand(x,y,-1); // add cell only to landscape + } + else { + if (habFloat < 0.0 || habFloat > 100.0) { // invalid cover score + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat cover score." << std::endl; + #endif + return 17; + } + else { + if (patchModel) { + patchFloat = pchfile(dimY-1-y,x); + if ( R_IsNA(patchFloat) ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + #endif + return 14; + } + + patchCode= static_cast(patchFloat); + + if (patchCode< 0 ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; + #endif + return 14; + } + + pPatch = patchCode == 0 ? nullptr : ( // matrix cell + existsPatch(patchCode) ? + findPatch(patchCode) : + newPatch(seq++, patchCode) + ); + addNewCellToPatch(pPatch, x, y, (float)habFloat); + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x,y,(float)habFloat); + } + } + } + } + else { // additional habitat cover layers + if ( !R_IsNA(habFloat) ) { + if (habFloat < 0.0 || habFloat > 100.0) { // invalid cover score + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat cover score." << std::endl; + #endif + return 17; + } + else { + cells[dimY-1-y][x]->setHabitat((float)habFloat); + } + } // end of habFloat is not NA + } // end additional habitat cover layers + } // end x + } // end y + habIndexed = true; // habitats are already numbered 1...n in correct order + + 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++) { + habFloat = habfile(dimY-1-y,x); + if ( R_IsNA(habFloat) ) { // check for NA + addNewCellToLand(x,y,-1); // add cell only to landscape + } + else { + if (habFloat < 0.0 || habFloat > 100.0) { // invalid quality score + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid habitat quality score." << std::endl; + #endif + return 17; + } + else { // valid quality score + if (patchModel) { + patchFloat = pchfile(dimY-1-y,x); + if ( R_IsNA(patchFloat) ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + #endif + return 14; + } + patchCode= static_cast(patchFloat); + if (patchCode< 0 ) { // invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found negative patch ID in valid habitat cell." << std::endl; + #endif + return 14; + } + + pPatch = patchCode == 0 ? nullptr : ( // matrix cell + existsPatch(patchCode) ? + findPatch(patchCode) : + newPatch(seq++, patchCode) + ); + addNewCellToPatch(pPatch, x, y, (float)habFloat); + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x,y,(float)habFloat); + } + } // end of valid quality score + } // end of habFloat is not NA + } // end x + } // end y + break; + + default: + break; + } // end switch(rasterType) + +//#if SPATIALDEMOG + int SMScosts = costfile.nrow()*costfile.ncol(); + + arma::vec cellDemoScalings; // vector to store local demog scalings + Rcpp::IntegerVector DSdim; + int nrDemogScaleLayers = 0; + if(scalinglayers.attr("dim")==R_NilValue) DSdim = Rcpp::IntegerVector::create(1,1,1); + else{ + DSdim = scalinglayers.attr("dim"); + if(DSdim.size()>2) nrDemogScaleLayers = DSdim[2]; //nr of slices on cube + else nrDemogScaleLayers = 1; + } + arma::cube scalingCube(scalinglayers.begin(),DSdim[0],DSdim[1],nrDemogScaleLayers,false); // turn scaling layers into a cube, what happens if it is not a spatial demog? + + if(SMScosts || nrDemogScaleLayers){ // are there SMS costs or demographic scaling layers to read? + + for (int y = dimY-1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + + // find the cell + pCell = findCell(x,y); + if (pCell != 0) { // not no-data cell + + // read cost raster + if(SMScosts) { + costFloat = costfile(dimY-1-y,x); + if ( !R_IsNA(costFloat) ) { + hc = (int)costFloat; + if ( hc < 1 ) { + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Cost map may only contain values of 1 or higher, but found " << hc << "." << endl; + #endif + return 54; + } + // set cost value + pCell->setCost(hc); + if (hc > maxcost) maxcost = hc; + } + } + + // read demographic scalings + if(nrDemogScaleLayers){ + // get tube at (y/x) + cellDemoScalings = scalingCube(arma::span(dimY-1-y), arma::span(x), arma::span::all); + if(cellDemoScalings.n_elem==(unsigned)nDSlayer){ + // set vector percentage values in cell + pCell->addchgDemoScaling(arma::conv_to< std::vector >::from(cellDemoScalings)); + } + else{// invalid patch code + #if RS_RCPP && !R_CMD + Rcpp::Rcout << "Wrong number of demographic scaling layers in cell " << x << " ," << y << " of first layer array." << std::endl; + #endif + return 64; + } + } + } + } + } + } + return 0; +} #endif - StreamErrorR(habfile); - hfile.close(); - hfile.clear(); - if (patchModel) { - pfile.close(); - pfile.clear(); + +int Landscape::readLandscape(int fileNum, string habfile, string pchfile, string costfile, vector scalinglayers) // vector scalinglayers is the vector of filenames for spatial demography of year 1 +{ + // 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 habCode, seq, patchCode, noDataHabCode; + int noDataPatch = 0; + int ncols, nrows; + float habFloat, patchFloat; + Patch* pPatch; + simParams sim = paramsSim->getSim(); + + if (fileNum < 0) return 19; + +#if RS_RCPP + wifstream ifsHabMap; // habitat file input stream + wifstream ifsPatchMap; // patch file input stream +#else + ifstream ifsHabMap; // habitat file input stream + ifstream ifsPatchMap; // patch file input stream +#endif + initParams init = paramsInit->getInit(); + + // Open habitat file and optionally also patch file +// #if RS_RCPP +// 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)); +// } +// #else + ifsHabMap.open(habfile.c_str()); +// #endif +// #endif + + if (!ifsHabMap.is_open()) return 11; + + if (fileNum == 0) { + if (patchModel) { +// #if RS_RCPP +// 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)); +// } +// #else + ifsPatchMap.open(pchfile.c_str()); +// #endif +// #endif + if (!ifsPatchMap.is_open()) { + ifsHabMap.close(); + ifsHabMap.clear(); + return 12; } + } + } + +// read landscape data from header records of habitat file +// NB headers of all files have already been compared +// !! DO NOT REMOVE THIS WORKAROUND !! NEEDED FOR LANDSCAPES GENERATED BY TERRA (R PACKAGE) +double tmpresol; + ifsHabMap >> header >> ncols + >> header >> nrows + >> header >> minEast + >> header >> minNorth + >> header >> tmpresol + >> header >> noDataHabCode; +resol = (int) tmpresol; +#if RS_RCPP + if (!ifsHabMap.good()) { + // corrupt file stream + StreamErrorR(habfile); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); + } + return 131; + } +#endif + + dimX = ncols; + dimY = nrows; + minX = maxY = 0; // bottom-left / south-west corner + maxX = dimX - 1; + maxY = dimY - 1; + + if (fileNum == 0) { // First map layer + + // Set initialisation limits to landscape limits + init.minSeedX = init.minSeedY = 0; + init.maxSeedX = maxX; + init.maxSeedY = maxY; + paramsInit->setInit(init); + + if (patchModel) { + for (int i = 0; i < 5; i++) + ifsPatchMap >> header >> patchFloat; + ifsPatchMap >> header >> noDataPatch; + } + +#if RS_RCPP + if (!ifsPatchMap.good()) { + // corrupt file stream + StreamErrorR(pchfile); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); return 135; } #endif - if (h == habnodata) - addNewCellToLand(x, y, -1); // add cell only to landscape - else { + setCellArray(); + } - // 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? ... + // set up bad float values to ensure that valid values are read + float badHabFloat = -9.0; + if (noDataHabCode == -9) badHabFloat = -99.0; + float badPatchFloat = -9.0; + if (noDataPatch == -9) badPatchFloat = -99.0; - 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; + seq = 0; // initial sequential patch landscape + patchCode = 0; // initial patch number for cell-based landscape + + // Create the matrix patch (even if there is no matrix) + if (fileNum == 0) { + newPatch(seq, patchCode); + seq++; + patchCode++; + } + + 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++) { + + habFloat = badHabFloat; +#if RS_RCPP + if (ifsHabMap >> habFloat) { + habCode = (int)habFloat; + if (patchModel) { + patchFloat = badPatchFloat; + if (ifsPatchMap >> patchFloat) { + patchCode = (int)patchFloat; + } + else { // corrupt file stream +#if !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(pchfile); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); + return 132; + } + } + } + else { // corrupt file stream +#if !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; #endif - hfile.close(); hfile.clear(); + StreamErrorR(habfile); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); + } + return 135; + } +#else + // Read habitat code in this cell + ifsHabMap >> habFloat; + habCode = static_cast(habFloat); if (patchModel) { - pfile.close(); pfile.clear(); + // Read patch code in this cell + patchFloat = badPatchFloat; + ifsPatchMap >> patchFloat; + patchCode = static_cast(patchFloat); } - return 13; - } - else { - addHabCode(h); - if (patchModel) { - if (p < 0 || p == pchnodata) { // invalid patch code +#endif + if (habCode == noDataHabCode) { + // No-data cell + addNewCellToLand(x, y, -1); // x, y is a null cell + } + else if (habCode < 0 || (sim.batchMode && (habCode < 1 || habCode > nHabMax))) { + // Invalid habitat 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; + Rcpp::Rcout << "Found invalid habitat code." << 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); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); } - 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); + return 13; + } + else { // Valid habitat code + addHabCode(habCode); + if (patchModel) { + if (patchCode < 0 || patchCode == noDataPatch) { // invalid patch code +#if RS_RCPP && !R_CMD + if (patchCode == noDataPatch) 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 + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); + return 14; } + // Does the patch already exists? + pPatch = patchCode == 0 ? nullptr : ( // matrix cell + existsPatch(patchCode) ? + findPatch(patchCode) : + newPatch(seq++, patchCode) + ); + addNewCellToPatch(pPatch, x, y, habCode); + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, habCode); } } - else { // cell-based model - // add cell to landscape (patches created later) - addNewCellToLand(x, y, h); - } - } - } - } - } + + } // for x + } // for y + #if RS_RCPP -hfile >> hfloat; -if (!hfile.eof()) EOFerrorR(habfile); -if (patchModel) -{ - pfile >> pfloat; - if (!pfile.eof()) EOFerrorR(pchfile); -} + ifsHabMap >> habFloat; + if (!ifsHabMap.eof()) EOFerrorR(habfile); + if (patchModel) { + ifsPatchMap >> patchFloat; + if (!ifsPatchMap.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; + + habFloat = badHabFloat; #if RS_RCPP + if (ifsHabMap >> habFloat) { + habCode = static_cast(habFloat); + if (fileNum == 0) { // first habitat cover layer + if (patchModel) { + patchFloat = badPatchFloat; + if (ifsPatchMap >> patchFloat) { + patchCode = static_cast(patchFloat); } else { // corrupt file stream -#if RS_RCPP && !R_CMD +#if !R_CMD Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; #endif - StreamErrorR(pchfile); - hfile.close(); - hfile.clear(); - pfile.close(); - pfile.clear(); + StreamErrorR(pchfile); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); return 135; } + } // end if patchModel +#else + ifsHabMap >> habFloat; + habCode = static_cast(habFloat); + + if (fileNum == 0) { // first habitat cover layer + if (patchModel) { + patchFloat = badPatchFloat; + ifsPatchMap >> patchFloat; + patchCode = static_cast(patchFloat); + } // end if patchModel #endif - } //end if patchmodel - if (h == habnodata) { + if (habCode == noDataHabCode) { addNewCellToLand(x, y, -1); // add cell only to landscape } - else { - if (hfloat < 0.0 || hfloat > 100.0) { // invalid cover score + else if (habFloat < 0.0 || habFloat > 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(); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); + } + return 17; } - return 17; - } - else { - if (patchModel) { - if (p < 0 || p == pchnodata) { // invalid patch code + else { + if (patchModel) { + if (patchCode < 0 || patchCode == noDataPatch) { // invalid patch code #if RS_RCPP && !R_CMD - if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + if (patchCode == noDataPatch) 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); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); + return 14; } + + pPatch = patchCode == 0 ? nullptr : ( // matrix cell + existsPatch(patchCode) ? + findPatch(patchCode) : + newPatch(seq++, patchCode) + ); + addNewCellToPatch(pPatch, x, y, habFloat); + } // end patchModel + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, habFloat); } - } - else { // cell-based model - // add cell to landscape (patches created later) - addNewCellToLand(x, y, hfloat); - } + } // if valid habFloat } - } - } else { // additional habitat cover layers - if (h != habnodata) { - if (hfloat < 0.0 || hfloat > 100.0) { // invalid cover score + if (habCode != noDataHabCode) { + if (habFloat < 0.0 || habFloat > 100.0) { // invalid cover score #if RS_RCPP && !R_CMD Rcpp::Rcout << "Found invalid habitat cover score." << std::endl; #endif - hfile.close(); hfile.clear(); + ifsHabMap.close(); + ifsHabMap.clear(); if (patchModel) { - pfile.close(); pfile.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); } return 17; } else { - cells[y][x]->setHabitat(hfloat); - } - } // end of h != habnodata -} + cells[y][x]->setHabitat(habFloat); + } + } + } #if RS_RCPP - } -else { // couldn't read from hfile + } + else { // not ifsHabMap >> habFloat // 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(); + StreamErrorR(habfile); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); } return 133; -} + }// end not ifsHabMap >> habFloat #endif - } - } + } // for x + } // for y + 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); + ifsHabMap >> habFloat; + if (!ifsHabMap.eof()) EOFerrorR(habfile); + if (patchModel) { + ifsPatchMap >> patchFloat; + if (!ifsPatchMap.eof()) EOFerrorR(pchfile); } #endif break; @@ -2086,110 +2681,111 @@ 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; + + habFloat = badHabFloat; #if RS_RCPP + if (ifsHabMap >> habFloat) { + habCode = static_cast(habFloat); + } else { // corrupt file stream -#if RS_RCPP && !R_CMD +#if !R_CMD Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; #endif - StreamErrorR(habfile); - hfile.close(); - hfile.clear(); - if (patchModel) { - pfile.close(); - pfile.clear(); + StreamErrorR(habfile); + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); } return 134; } -#endif if (patchModel) { - pfloat = badpfloat; -#if RS_RCPP - if (pfile >> pfloat) { -#else - pfile >> pfloat; -#endif - p = (int)pfloat; -#if RS_RCPP + patchFloat = badPatchFloat; + if (ifsPatchMap >> patchFloat) { + patchCode = static_cast(patchFloat); } 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(); + StreamErrorR(pchfile); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); return 135; } + } +#else + ifsHabMap >> habFloat; + habCode = static_cast(habFloat); + if (patchModel) { + patchFloat = badPatchFloat; + ifsPatchMap >> patchFloat; + patchCode = static_cast(patchFloat); + } #endif - } - if (h == habnodata) { + if (habCode == noDataHabCode) { addNewCellToLand(x, y, -1); // add cell only to landscape } - else { - if (hfloat < 0.0 || hfloat > 100.0) { // invalid quality score + else if (habFloat < 0.0 || habFloat > 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 + ifsHabMap.close(); + ifsHabMap.clear(); + if (patchModel) { + ifsPatchMap.close(); + ifsPatchMap.clear(); + } + return 17; + } + else { + if (patchModel) { + if (patchCode < 0 || patchCode == noDataPatch) { // invalid patch code #if RS_RCPP && !R_CMD - if (p == pchnodata) Rcpp::Rcout << "Found patch NA in valid habitat cell." << std::endl; + if (patchCode == noDataPatch) 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); + ifsHabMap.close(); + ifsHabMap.clear(); + ifsPatchMap.close(); + ifsPatchMap.clear(); + return 14; + } + if (patchCode == 0) { // cell is in the matrix + addNewCellToPatch(0, x, y, habFloat); } else { - if (existsPatch(p)) { - pPatch = findPatch(p); - addNewCellToPatch(pPatch, x, y, hfloat); - // addNewCellToPatch(findPatch(p),x,y,hfloat); + if (existsPatch(patchCode)) { + pPatch = findPatch(patchCode); } else { - addPatchNum(p); - pPatch = newPatch(seq++, p); - addNewCellToPatch(pPatch, x, y, hfloat); + pPatch = newPatch(seq++, patchCode); } + addNewCellToPatch(pPatch, x, y, habFloat); } - } - else { // cell-based model - // add cell to landscape (patches created later) - addNewCellToLand(x, y, hfloat); - } - } - } - } - } + } + else { // cell-based model + // add cell to landscape (patches created later) + addNewCellToLand(x, y, habFloat); + } + } + + } // for x + } // for y + #if RS_RCPP - hfile >> hfloat; - if (!hfile.eof()) EOFerrorR(habfile); - if (patchModel) - { - pfile >> pfloat; - if (!pfile.eof()) EOFerrorR(pchfile); + ifsHabMap >> habFloat; + if (!ifsHabMap.eof()) EOFerrorR(habfile); + if (patchModel) + { + ifsPatchMap >> patchFloat; + if (!ifsPatchMap.eof()) EOFerrorR(pchfile); } #endif break; @@ -2198,18 +2794,29 @@ case 2: // habitat quality break; } // end switch(rasterType) - if (hfile.is_open()) { hfile.close(); hfile.clear(); } - if (pfile.is_open()) { pfile.close(); pfile.clear(); } + if (ifsHabMap.is_open()) { + ifsHabMap.close(); + ifsHabMap.clear(); + } + if (ifsPatchMap.is_open()) { + ifsPatchMap.close(); + ifsPatchMap.clear(); + } if (sim.batchMode) { if (costfile != "NULL") { int retcode = readCosts(costfile); if (retcode < 0) return 54; + } + if (scalinglayers.size() > 0) { + if (scalinglayers.size() == nDSlayer) { + int retcode = readDemographicScaling(scalinglayers); + if (retcode < 0) return 54; //change number + } } } return 0; - } //--------------------------------------------------------------------------- @@ -2237,22 +2844,19 @@ int Landscape::readCosts(string fname) string header; #endif Cell* pCell; -#if !RS_RCPP - simView v = paramsSim->getViews(); -#endif int maxcost = 0; // open cost file -#if !RS_RCPP || RSWIN64 +// #if RS_RCPP +// 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)); +// } +// #else 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 +// #endif // read headers and check that they correspond to the landscape ones costs >> header; #if RS_RCPP @@ -2267,10 +2871,6 @@ int Landscape::readCosts(string fname) #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; } @@ -2315,13 +2915,12 @@ resolCost = (int) tmpresolCost; if (hc > maxcost) maxcost = hc; } else { // if cost value is below 0 #if RS_RCPP && !R_CMD - Rcpp::Rcout << "Cost map may only contain values of 1 or higher in habiat cells, but found " << hc << " in cell x: " << x << " y: " << y << "." << endl; + Rcpp::Rcout << "Cost map may only contain values of 1 or higher in habitat cells, but found " << hc << " in cell x: " << x << " y: " << y << "." << endl; #endif - // costs.close(); costs.clear(); // not sure if it should stop at this point - // return -999; + throw runtime_error("Found negative- or zero-cost habitat cell."); } - } // end not no data vell + } // end not no data cell } } #if RS_RCPP @@ -2340,6 +2939,134 @@ return maxcost; } + //--------------------------------------------------------------------------- + int Landscape::readDemographicScaling(vector scalinglayers){ + // Create a temporary landscape to store the vectors for each cell + // I bet there is a better way to implement it, but I couldn't think of it for now + // each cell will contain a vector of three floats + std::vector>> landscape(dimY, + std::vector>(dimX, + std::vector(scalinglayers.size(), 0.0f))); //length of string is determined by DSlayer + +#if RS_RCPP + wstring header; +#else + string header; +#endif + + int DSnb = 0; // first position is 0 + int DS; + int DSnodata; + float DSfloat; // float for reading in data from file + +#if RS_RCPP + wifstream DSfile; // DS file input stream +#else + ifstream DSfile; // DS file input stream +#endif + + + + // for each element of scalinglayers + for (const auto& DSlayer : scalinglayers) { //DSlayer is a reference to file string + // open file + DSfile.open(DSlayer.c_str()); + // if file couldn't be opened, return a failure message (and the readLandscape function: close all file connections and exit) + if (!DSfile.is_open()) return -11; // might need to change the code number + + // if it opened + // skip header, but store no data value + for (int i = 0; i < 5; i++) + DSfile >> header >> DSfloat; + DSfile >> header >> DSnodata; // 6th line + +#if RS_RCPP + if (!DSfile.good()) { + // corrupt file stream + StreamErrorR(DSlayer); + DSfile.close(); + DSfile.clear(); + //do I also need to close the habitat, and potentially patchfile? or is this taken care of? + return -181; // need to change the code + } +#endif + + // set badfloat + float badDSfloat = -9.0; if (DSnodata == -9) DSfloat = -99.0; + + // loop over landscape x+y + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + // read in cell value + DSfloat = badDSfloat; // set to bad float value + // do I need to check this value for any inconsistencies? -> better safe than sorry +#if RS_RCPP + if (DSfile >> DSfloat) { +#else + DSfile >> DSfloat; +#endif + // DS = (int)DSfloat; I guess non integer values are totally fine? +#if RS_RCPP + } + else { + // corrupt file stream +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "At (x,y) = " << x << "," << y << " :" << std::endl; +#endif + StreamErrorR(DSlayer); + DSfile.close(); + DSfile.clear(); + return -134; // need to change number code + } +#endif + // check if the value is a no data value + if (DSfloat == DSnodata) { + // if it is, do I need to do anything? + } + else { + if (DSfloat < 0.0 || DSfloat > 100.0) { // check for negative values or values above 100 +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Found invalid demographic scaling score." << std::endl; +#endif + DSfile.close(); DSfile.clear(); + return -17; // need to change code + } + else { + // if the value is ok, + // set the vector value of this cell at this location to the float value read in: + landscape[y][x][DSnb] = DSfloat; // Assignment to specific cell's vector + } + } + } // end x loop + } // end y loop + + // use Rcpp::Rcout to print the landscape at DSnb=0: + // close file connection +#if RS_RCPP + DSfile >> DSfloat; + if (!DSfile.eof()) EOFerrorR(DSlayer); + else { +#if RS_RCPP && !R_CMD + Rcpp::Rcout << "Demographic scaling layer " << DSnb + 1 << " loaded." << endl; +#endif + } +#endif + if (DSfile.is_open()) { DSfile.close(); DSfile.clear(); } + DSnb ++; // increase the reference number and read in next file + }; // end loop over scalinglayers + + // add the cells' vector of DSfloats to the actual landscape using the function addchgDemoScaling + // loop over all cells + for (int y = dimY - 1; y >= 0; y--) { + for (int x = 0; x < dimX; x++) { + // extract the vector in landscape at x, y + std::vector localDS = landscape[y][x]; + cells[y][x]->addchgDemoScaling(localDS); // add the vector to the cell + } + } + return 0; // return success code + }; + //--------------------------------------------------------------------------- rasterdata CheckRasterFile(string fname) @@ -2356,24 +3083,18 @@ rasterdata CheckRasterFile(string fname) infile.open(fname.c_str()); if (infile.is_open()) { infile >> header >> r.ncols; - if (header != "ncols" && header != "NCOLS") r.errors++; infile >> header >> r.nrows; - if (header != "nrows" && header != "NROWS") r.errors++; infile >> header >> r.xllcorner; - if (header != "xllcorner" && header != "XLLCORNER") r.errors++; infile >> header >> r.yllcorner; - if (header != "yllcorner" && header != "YLLCORNER") r.errors++; double tmpcellsize; infile >> header >> tmpcellsize; r.cellsize = (int) tmpcellsize; - if (header != "cellsize" && header != "CELLSIZE") r.errors++; infile >> header >> inint; - if (header != "NODATA_value" && header != "NODATA_VALUE") r.errors++; infile.close(); infile.clear(); @@ -2396,9 +3117,6 @@ 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]; @@ -2441,10 +3159,10 @@ void Landscape::deleteConnectMatrix(void) // Close connectivity file bool Landscape::outConnectFinishLandscape() { - if (outConnMat.is_open()) outConnMat.close(); - outConnMat.clear(); - return true; -} + if (outConnMat.is_open()) outConnMat.close(); + outConnMat.clear(); + return true; + } // Open connectivity file and write header record bool Landscape::outConnectStartLandscape() @@ -2470,36 +3188,33 @@ bool Landscape::outConnectStartLandscape() // Close movement paths file void Landscape::outPathsFinishReplicate() { - if (outMovePaths.is_open()) outMovePaths.close(); - outMovePaths.clear(); -} + if (outMovePaths.is_open()) outMovePaths.close(); + outMovePaths.clear(); + } // Open movement paths file and write header record void Landscape::outPathsStartReplicate(int rep) { - simParams sim = paramsSim->getSim(); - string name = paramsSim->getDir(2); - if (sim.batchMode) { - name += "Batch" + to_string(sim.batchNum) - + "_Sim" + to_string(sim.simulation) - + "_Land" + to_string(landNum) - + "_Rep" + to_string(rep); - } - else { - name += "Sim" + to_string(sim.simulation) - + "_Rep" + to_string(rep); - } - name += "_MovePaths.txt"; + simParams sim = paramsSim->getSim(); + string name = paramsSim->getDir(2); + if (sim.batchMode) { + name += "Batch" + to_string(sim.batchNum) + + "_Sim" + to_string(sim.simulation) + + "_Land" + to_string(landNum) + + "_Rep" + to_string(rep); + } + else { + name += "Sim" + to_string(sim.simulation) + + "_Rep" + to_string(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(); + outMovePaths.open(name.c_str()); + if (outMovePaths.is_open()) { + outMovePaths << "Year\tIndID\tStep\tx\ty\tStatus" << endl; + } + else { + outMovePaths.clear(); } } #endif @@ -2609,11 +3324,51 @@ void Landscape::outVisits(int rep, int landNr) { outvisits << endl; } - outvisits.close(); outvisits.clear(); + outvisits.close(); + outvisits.clear(); } //--------------------------------------------------------------------------- +#ifdef UNIT_TESTS +// Tests only: shortcut setup utilities + +Landscape createLandscapeFromCells(vector cells, const landParams& lp, Species sp) { + // Set up landscape + Landscape ls; + ls.setLandParams(lp, true); + // Add cells + ls.setCellArray(); + for (auto c : cells) { + ls.addCellToLand(c); + } + ls.allocatePatches(&sp); + return ls; +} + +landParams createDefaultLandParams(const int& dim) { + + landParams ls_params; + ls_params.dimX = ls_params.dimY = dim; + ls_params.minX = ls_params.minY = 0; + ls_params.maxX = ls_params.maxY = ls_params.dimX - 1; + ls_params.resol = ls_params.spResol = 1; + ls_params.rasterType = 0; // habitat types + + ls_params.patchModel = false; + ls_params.spDist = false; + ls_params.generated = false; + ls_params.dynamic = false; + ls_params.landNum = 0; + ls_params.nHab = ls_params.nHabMax = 0; // irrelevant for habitat codes + return ls_params; +} + +void testLandscape() { + // test coordinate system... +} +#endif // UNIT_TESTS + //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- diff --git a/Landscape.h b/Landscape.h index e8c0c29..da7213b 100644 --- a/Landscape.h +++ b/Landscape.h @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 @@ -58,14 +58,14 @@ 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. + 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. + 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 + Last updated: 28 July 2021 by Greta Bocedi ------------------------------------------------------------------------------*/ #ifndef LandscapeH @@ -88,9 +88,17 @@ using namespace std; #include #endif #include +#include #endif + //--------------------------------------------------------------------------- +// not sure whether it needs to be added here for the readDistribution() function to work + +struct landOrigin { + double minEast; double minNorth; +}; + // Initial species distribution @@ -98,9 +106,17 @@ class InitDist{ public: InitDist(Species*); ~InitDist(); +#if RS_RCPP int readDistribution( + Rcpp::NumericMatrix, + landOrigin, + int string // name of species distribution file ); +#endif + int readDistribution( + string // name of species distribution file + ); void setDistribution( int // no. of distribution cells to be initialised (0 for all cells) ); @@ -142,9 +158,10 @@ class InitDist{ //--------------------------------------------------------------------------- -struct landParams { +struct landParams { bool patchModel; bool spDist; bool generated; bool dynamic; + bool spatialdemog; int landNum; int resol; int spResol; int nHab; int nHabMax; int dimX,dimY,minX,minY,maxX,maxY; short rasterType; @@ -159,9 +176,11 @@ struct genLandParams { struct landPix { int pix; float gpix; }; +/* struct landOrigin { double minEast; double minNorth; }; + */ struct rasterHdr { bool ok; int errors,ncols,nrows,cellsize; @@ -179,7 +198,8 @@ struct patchData { Patch *pPatch; int patchNum,nCells; int x,y; }; struct landChange { - int chgnum,chgyear; string habfile,pchfile,costfile; + int chgNb, chgYear; + string pathHabFile, pathPatchFile, pathCostFile, pathSpatDemogFile; }; struct patchChange { int chgnum, x, y, oldpatch, newpatch; @@ -229,8 +249,7 @@ class Landscape{ // functions to handle patches and cells void setCellArray(void); - void addPatchNum(int); - void generatePatches(void); // create an artificial landscape + void generatePatches(); // 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.) @@ -250,6 +269,9 @@ class Landscape{ int, // y co-ordinate int // habitat class no. ); + void addCellToLand( + Cell* // cell to add to landscape + ); void addCellToPatch( Cell*, // pointer to Cell Patch* // pointer to Patch @@ -285,6 +307,8 @@ class Landscape{ Patch* findPatch( int // Patch id no. ); + set getPatchNbs() const; + set samplePatches(const string& samplingOption, int nbToSample, Species* pSpecies); int checkTotalCover(void); void resetPatchPopns(void); void updateCarryingCapacity( @@ -296,6 +320,10 @@ class Landscape{ int, // x co-ordinate int // y co-ordinate ); + bool checkDataCell( + int, // x co-ordinate + int // y co-ordinate + ); int patchCount(void); void updateHabitatIndices(void); void setEnvGradient( @@ -323,7 +351,19 @@ class Landscape{ short // change number ); void deleteLandChanges(void); +//#if RS_THREADSAFE #if RS_RCPP && !R_CMD + int readLandChange( + int, // change number + Rcpp::NumericMatrix,// habitat raster + Rcpp::NumericMatrix,// patch raster + Rcpp::NumericMatrix // cost raster +//#if SPATIALDEMOG + ,Rcpp::NumericVector// array of demographic scaling layers +//#endif + ); +//#else +// #if RS_RCPP && !R_CMD int readLandChange( int, // change file number bool, // change SMS costs? @@ -332,12 +372,16 @@ class Landscape{ wifstream&, // cost file stream int, // habnodata int, // pchnodata - int // costnodata + int, // costnodata + vector // vector of demographic scaling layers +// TODO: add spatial demography with normal file input ); #else + // TODO: add spatial demography for batch version int readLandChange( - int, // change file number - bool // change SMS costs? + int, // change file numbers + bool, // change SMS costs? + vector // vector of demographic scaling layers ); #endif void createPatchChgMatrix(void); @@ -354,6 +398,9 @@ class Landscape{ costChange getCostChange( int // cost change number ); +//#if SPATIALDEMOG + void updateDemoScalings(short); +//#endif // SPATIALDEMOG // functions to handle species distributions @@ -361,6 +408,19 @@ class Landscape{ Species*, // pointer to Species string // name of initial distribution file ); + + // function for new file input +#if RS_RCPP + int newDistribution( + Species*, // pointer to Species + //#if RS_THREADSAFE + Rcpp::NumericMatrix, + int + //#else + string // name of initial distribution file + ); +#endif + void setDistribution( Species*, // pointer to Species int // no. of distribution squares to initialise @@ -436,17 +496,28 @@ class Landscape{ // functions to handle input and output +#if RS_RCPP + int readLandscape( + int, // no. of seasonss + Rcpp::NumericMatrix,// habitat raster + Rcpp::NumericMatrix,// patch raster + Rcpp::NumericMatrix // cost raster + ,Rcpp::NumericVector // array of demographic scaling layers + ); +#endif 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) + string, // cost file name (may be NULL) + vector // demographic scaling layers (may be empty) ); void listPatches(void); int readCosts( string // costs file name ); + int readDemographicScaling(vector ); void resetVisits(void); void outVisits(int,int); // save SMS path visits map to raster text file @@ -458,6 +529,9 @@ class Landscape{ bool continuous; // bool dynamic; // landscape changes during simulation bool habIndexed; // habitat codes have been converted to index numbers +//#if SPATIALDEMOG + bool spatialdemog; // are there spatially varying demographic rates? +//#endif // SPATIALDEMOG short rasterType; // 0 = habitat codes 1 = % cover 2 = quality 9 = artificial landscape int landNum; // landscape number int resol; // cell size (m) @@ -465,7 +539,7 @@ class Landscape{ 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 minX, minY; // minimum available X and Y co-ordinates, i.e. coordinates of the bottom-right corner 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 @@ -483,9 +557,6 @@ class Landscape{ // 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; @@ -505,7 +576,7 @@ class Landscape{ int **connectMatrix; // global environmental stochasticity (epsilon) - float *epsGlobal; // pointer to time-series + float* epsGlobal; // pointer to time-series // patch and costs change matrices (temporary - used when reading dynamic landscape) // indexed by [descending y][x][period] @@ -525,8 +596,9 @@ extern paramInit *paramsInit; extern paramSim *paramsSim; extern RSrandom *pRandom; -#if RSDEBUG -extern ofstream DEBUGLOG; +#ifdef UNIT_TESTS +landParams createDefaultLandParams(const int& dim); +void testLandscape(); #endif #if RS_RCPP diff --git a/Main.cpp b/Main.cpp index 57c54ea..c7804f1 100644 --- a/Main.cpp +++ b/Main.cpp @@ -34,33 +34,41 @@ #include "RSrandom.h" #include "Utils.h" #include "Parameters.h" +#include "Population.h" #include "Landscape.h" #include "Species.h" #include "SubCommunity.h" +#include "Management.h" using namespace std; +#ifdef UNIT_TESTS +void testIndividual(); +void testNeutralStats(); +void testPopulation(); + void run_unit_tests() { cout << "******* Unit test output *******" << endl; testRSrandom(); + testLandscape(); testIndividual(); + testPopulation(); + testNeutralStats(); cout << endl << "************************" << endl; } +#endif // UNIT_TESTS // 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; +Management* pManagement; // pointer to management routines Species* pSpecies; Community* pComm; +short nDSlayer=gMaxNbLayers; #if LINUX_CLUSTER || RS_RCPP int main(int argc, char* argv[]) @@ -68,10 +76,18 @@ int main(int argc, char* argv[]) 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; +#ifndef UNIT_TESTS + cout << "This version is only for running unit tests." << endl; return 1; -# else +#else + + // Initialise globals + paramsGrad = new paramGrad; + paramsStoch = new paramStoch; + paramsInit = new paramInit; + paramsSim = new paramSim; + pRandom = new RSrandom; + assert(0.1 > 0.0); // assert does run correctly try { @@ -82,6 +98,7 @@ int _tmain(int argc, _TCHAR* argv[]) cerr << endl << "Error: " << e.what() << endl; } cout << "All tests have completed." << endl; + return 0; // if tests succeed, we are happy # endif // NDEBUG } diff --git a/Management.cpp b/Management.cpp new file mode 100644 index 0000000..c6c7c53 --- /dev/null +++ b/Management.cpp @@ -0,0 +1,326 @@ +/*---------------------------------------------------------------------------- + * + * 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 "Management.h" + +//--------------------------------------------------------------------------- +/* + * Initialize management class + */ + +Management::Management(void) { + translocation = false; + catching_rate = 1.0; // Catching rate + non_dispersed = false; // do not consider non-dispersed individuals + std::vector translocation_years; // Number of years of translocation + std::map< int, std::vector > source; // Source patch or cell: should be a vector of arrays + std::map< int, std::vector > target; // Target patch or cell + std::map< int, std::vector > nb; // number of ttanslocated individuals + std::map< int, std::vector > min_age; // Minimum age of translocated individuals + std::map< int, std::vector > max_age; // Maximum age of translocated individuals + std::map< int, std::vector > stage; // Stage of translocated individuals + std::map< int, std::vector > sex; // Sex of translocated individuals + +} + +Management::~Management(void) { + translocation_years.clear(); + source.clear(); + target.clear(); + nb.clear(); + min_age.clear(); + max_age.clear(); + stage.clear(); +} + +managementParams Management::getManagementParams(void) { + managementParams m; + m.translocation = translocation; + return m; +} + +void Management::setManagementParams(const managementParams m){ + translocation = m.translocation; +}; + +translocationParams Management::getTranslocationParams(void) { + translocationParams t; + t.catching_rate = catching_rate; + t.translocation_years = translocation_years; + t.source = source; + t.target = target; + t.nb = nb; + t.min_age = min_age; + t.max_age = max_age; + t.stage = stage; + t.sex = sex; + return t; +} + +// not sure if this is a good way, so won't use it for now +void Management::setTranslocationParams(const translocationParams t){ + catching_rate = t.catching_rate; + translocation_years = t.translocation_years; + source = t.source; + target = t.target; + nb = t.nb; + min_age = t.min_age; + max_age = t.max_age; + stage = t.stage; + sex = t.sex; + +}; + +void Management::translocate(int yr + , Landscape* pLandscape + , Species* pSpecies + ){ +#if RS_RCPP + Rcpp::Rcout << "Start translocation events in year " << yr << endl; +#endif +#ifndef NDEBUG + cout << "Start translocation events in year " << yr << endl; +#endif + landParams ppLand = pLandscape->getLandParams(); + auto it = nb.find(yr); // the number of translocation events is determined by the number of elements of the maps at year yr + auto nb_it = nb.find(yr); + auto source_it = source.find(yr); + auto target_it = target.find(yr); + auto min_age_it = min_age.find(yr); + auto max_age_it = max_age.find(yr); + auto stage_it = stage.find(yr); + auto sex_it = sex.find(yr); + // iterate over the number of events + for (int e = 0; e < it->second.size(); e++) { +#if RS_RCPP + Rcpp::Rcout << "Translocation event " << e << " in year " << yr << endl; +#endif +#ifndef NDEBUG + cout << "Translocation event " << e << " in year " << yr << endl; +#endif + // find the source patch + Patch* s_patch; + Population* s_pPop; + if(ppLand.patchModel){ + if(pLandscape->existsPatch(source_it->second[e].x)){ +#if RS_RCPP + Rcpp::Rcout << "Source patch exist." << endl; +#endif +#ifndef NDEBUG + cout << "Source patch exist." << endl; +#endif + + s_patch = pLandscape->findPatch(source_it->second[e].x); + if (s_patch) { // if it is not a nullpointer + // test if population in patch is not zero + s_pPop = s_patch->getPopn(pSpecies); // returns the population of the species in that cell + if (s_pPop && s_pPop->getNbInds() > 0){ + } else { +#if RS_RCPP + Rcpp::Rcout << "Population does not exist in source patch or is 0! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Population does not exist in source patch or is 0! skipping translocation event." << endl; +#endif + return; + } + } else { +#if RS_RCPP + Rcpp::Rcout << "Source patch was found but NULL! skipping translocation event." << endl; // not sure if this ever happens +#endif +#ifndef NDEBUG + cout << "Source patch was found but NULL! skipping translocation event." << endl; // not sure if this ever happens +#endif + return; + } + // + } else{ +#if RS_RCPP + Rcpp::Rcout << "Source patch was not found in landscape! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Source patch was not found in landscape! skipping translocation event." << endl; +#endif + return; + } + } else{ + Cell* pCell; + pCell = pLandscape->findCell(source_it->second[e].x, source_it->second[e].y); + if (pCell != 0) { +#if RS_RCPP + Rcpp::Rcout << "Source cell was found" << endl; +#endif +#ifndef NDEBUG + cout << "Source cell was found" << endl; +#endif + Patch *s_ppatch = pCell->getPatch(); + if (s_ppatch) { + s_patch = s_ppatch; + // test if population in patch is not zero + s_pPop = s_patch->getPopn(pSpecies); // returns the population of the species in that cell + if (s_pPop && s_pPop->getNbInds() > 0){ + } else { +#if RS_RCPP + Rcpp::Rcout << "Population does not exist in source cell or is 0! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Population does not exist in source cell or is 0! skipping translocation event." << endl; +#endif + return; + } + } else { +#if RS_RCPP + Rcpp::Rcout << "Source cell does not exist! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Source cell does not exist! skipping translocation event." << endl; +#endif + + return; + } + } else { +#if RS_RCPP + Rcpp::Rcout << "Cell does not belong to landscape! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Cell does not belong to landscape! skipping translocation event." << endl; +#endif + return; + } + } + // find the target patch and check for existence + Patch* t_patch; + Population* t_pPop; + if(ppLand.patchModel){ + if(pLandscape->existsPatch(target_it->second[e].x)){ +#if RS_RCPP + Rcpp::Rcout << "Target patch exist." << endl; +#endif +#ifndef NDEBUG + cout << "Target patch exist." << endl; +#endif + t_patch = pLandscape->findPatch(target_it->second[e].x); + } else{ +#if RS_RCPP + Rcpp::Rcout << "Target patch was not found in landscape! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Target patch was not found in landscape! skipping translocation event." << endl; +#endif + return; + } + } else{ + Cell* pCell; + pCell = pLandscape->findCell(target_it->second[e].x, target_it->second[e].y); + if (pCell != 0) { +#if RS_RCPP + Rcpp::Rcout << "Target cell was found" << endl; +#endif +#ifndef NDEBUG + cout << "Target cell was found" << endl; +#endif + Patch *t_ppatch = pCell->getPatch(); + if (t_ppatch) { + t_patch = t_ppatch; + } else { +#if RS_RCPP + Rcpp::Rcout << "Target cell does not exist! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Target cell does not exist! skipping translocation event." << endl; +#endif + return; + } + } else { +#if RS_RCPP + Rcpp::Rcout << "Target cell does not belong to landscape! skipping translocation event." << endl; +#endif +#ifndef NDEBUG + cout << "Target cell does not belong to landscape! skipping translocation event." << endl; +#endif + return; + } + } + + // only if source and target cell/patch exist, we can translocate individuals: + // get individuals with the given characteristics in that population + int min_age = min_age_it->second[e]; + int max_age = max_age_it->second[e]; + int stage = stage_it->second[e]; + int sex = sex_it->second[e]; + int nb = nb_it->second[e]; + int nbSampledInds = 0; + // We made already sure by now that in s_pPop at least some individuals exist + nbSampledInds = s_pPop->sampleIndividuals(nb, min_age, max_age, stage, sex); // checking values was done when reading in the parameters + popStats s_stats = s_pPop->getStats(s_patch->getDemoScaling()); + Individual* catched_individual; + int translocated = 0; + // loop over all indsividuals, extract sampled individuals, try to catch individual + translocate them to new patch + for (int j = 0; j < s_stats.nInds; j++) { + // if there are individuals to catch + if(s_pPop->getSizeSampledInds()){ + // if this individual is matching one of the sampled individuals + catched_individual = s_pPop->catchIndividual(catching_rate, j); // catch individual in the source patch + if (catched_individual !=NULL) { // translocated individual - has already been removed from natal population + // Check if a population of this species already exists in target patch t_patch + t_pPop = t_patch->getPopn(pSpecies); + if (!t_pPop) { // translocated individual is the first in a previously uninhabited patch +#if RS_RCPP + Rcpp::Rcout << "Population does not exist in target patch. Creating new population." << endl; +#endif +#ifndef NDEBUG + cout << "Population does not exist in target patch. Creating new population." << endl; +#endif + // create a new population in the corresponding sub-community + SubCommunity* pSubComm = (SubCommunity*)t_patch->getSubComm(); + t_pPop = pSubComm->newPopn(pLandscape, pSpecies, t_patch, 0); + } + catched_individual->setStatus(10); // make sure individual is not dispersing after the translocation + t_pPop->recruit(catched_individual); // recruit individual to target population TODO: maybe use a specified function which also updates pCurrCell + pPrevCell to a random cell in target patch? + translocated ++; + // NOTE: + // the variables pCurrCell and pPrevCell are not updated! These are important for the dispersal process! + // currently, translocated individuals are not considered as potential emigrants, thus there is no problem in changing that + // however, if we want to consider dispersal events after translocation, we need to adapt that; but that would also mean, that we might loose the information + // about the natal patch of an individual? + simParams sim = paramsSim->getSim(); + if (sim.outConnect) { // increment connectivity totals + int newpatch = t_patch->getSeqNum(); + int prevpatch = s_patch->getSeqNum(); + pLandscape->incrConnectMatrix(prevpatch, newpatch); + } + + } + } + } +#if RS_RCPP + Rcpp::Rcout << "Successfully translocated " << translocated << " out of " << nb_it->second[e] << " individuals in translocation event " << e <<"." << endl; +#endif +#ifndef NDEBUG + cout << "Successfully translocated " << translocated << " out of " << nb_it->second[e] << " individuals in translocation event " << e <<"." << endl; +#endif + // remove pointers to sampled individuals + s_pPop->clean(); + } +}; diff --git a/Management.h b/Management.h new file mode 100644 index 0000000..efa0723 --- /dev/null +++ b/Management.h @@ -0,0 +1,135 @@ +/*---------------------------------------------------------------------------- + * + * Copyright (C) 2024 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell, Jette Reeg + * + * 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: + + paramManagement - Management parameters + paramTranslocation - Translocation parameters + + + Last updated: 12 March 2024 by Jette Reeg + + ------------------------------------------------------------------------------*/ + + +#ifndef ManagementH +#define ManagementH + +#include +#include +#include +#include +#include +#include +#include +using namespace std; +#if RS_RCPP +#include // for Rcpp::Rcout +#endif +#include "Parameters.h" +#include "Species.h" +#include "Cell.h" +#include "Landscape.h" + +#include "SubCommunity.h" +#include "Population.h" + + +#if RS_RCPP +typedef intptr_t intptr; +#else +typedef unsigned long long intptr; +#endif // RS_RCPP + + + +//--------------------------------------------------------------------------- + +/* + * Management settings + */ + +// Structure for management parameters +struct managementParams { + bool translocation; // Translocation +}; + +// Structure for translocation parameters +struct translocationParams { + double catching_rate; // Catching rate + std::vector translocation_years; // Number of years of translocation -> will be increased at the beginning of a simulation + std::map< int, std::vector > source; // Source patch or cell: should be a vector of arrays + std::map< int, std::vector > target; // Target patch or cell + std::map< int, std::vector > nb; // number of ttanslocated individuals + std::map< int, std::vector > min_age; // Minimum age of translocated individuals + std::map< int, std::vector > max_age; // Maximum age of translocated individuals + std::map< int, std::vector > stage; // Stage of translocated individuals + std::map< int, std::vector > sex; // Sex of translocated individuals +}; + + +//--------------------------------------------------------------------------- + +class Management{ +public: + Management(void); + ~Management(void); + void setManagementParams( // function to set management parameters + const managementParams // structure holding general management parameters + ); + managementParams getManagementParams(void); // get management parameters + void setTranslocationParams( // function to set translocation parameters + const translocationParams // structure holding translocation parameters + ); + translocationParams getTranslocationParams(void); + void translocate( // Translocation + int , // year of translocation + Landscape* , // pointer to the landscape + // Community*, // pointer to the community + Species* // pointer to the species + ); + + // + bool translocation; // Translocation + double catching_rate; // Catching rate + bool non_dispersed; // whether non-dispersed individuals should be translocated + std::vector translocation_years; // Number of years of translocation -> should be a dynamic vector + std::map< int, std::vector > source; // Source patch or cell: should be a vector of arrays + std::map< int, std::vector > target; // Target patch or cell + std::map< int, std::vector > nb; // number of ttanslocated individuals + std::map< int, std::vector > min_age; // Minimum age of translocated individuals + std::map< int, std::vector > max_age; // Maximum age of translocated individuals + std::map< int, std::vector > stage; // Stage of translocated individuals + std::map< int, std::vector > sex; // Sex of translocated individuals + +}; + +//--------------------------------------------------------------------------- + +extern paramSim *paramsSim; + +//--------------------------------------------------------------------------- +#endif diff --git a/Model.cpp b/Model.cpp index 0adc81c..46a0554 100644 --- a/Model.cpp +++ b/Model.cpp @@ -25,11 +25,11 @@ #include "Model.h" ofstream outPar; - +using namespace std::chrono; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #if RS_RCPP && !R_CMD -Rcpp::List RunModel(Landscape* pLandscape, int seqsim) +Rcpp::List RunModel(Landscape* pLandscape, int seqsim, Rcpp::S4 ParMaster) //Rcpp::S4 ParMaster new for all variants #else int RunModel(Landscape* pLandscape, int seqsim) #endif @@ -40,21 +40,13 @@ int RunModel(Landscape* pLandscape, int seqsim) landParams ppLand = pLandscape->getLandParams(); envGradParams grad = paramsGrad->getGradient(); envStochParams env = paramsStoch->getStoch(); - demogrParams dem = pSpecies->getDemogr(); - stageParams sstruct = pSpecies->getStage(); - trfrRules trfr = pSpecies->getTrfr(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + transferRules trfr = pSpecies->getTransferRules(); + managementParams manage = pManagement->getManagementParams(); + translocationParams transloc = pManagement->getTranslocationParams(); 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 @@ -62,11 +54,18 @@ int RunModel(Landscape* pLandscape, int seqsim) // 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 + // if using neutral markers, set up patches to sample from 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); + + //if SPATIALDEMOG + if (ppLand.rasterType == 2 && ppLand.spatialdemog) + pLandscape->updateDemoScalings(0); // TODO -> is this needed independent of whether it is on or off? + // endif SPATIALDEMOG + patchData ppp; int npatches = pLandscape->patchCount(); for (int i = 0; i < npatches; i++) { @@ -81,6 +80,13 @@ int RunModel(Landscape* pLandscape, int seqsim) else { pLandscape->resetLandLimits(); } + + // Random patches are sampled once per landscape + if (sim.patchSamplingOption == "random") { + int nbToSample = pSpecies->getNbPatchesToSample(); + auto patchesToSample = pLandscape->samplePatches(sim.patchSamplingOption, nbToSample, pSpecies); + pSpecies->setSamplePatchList(patchesToSample); + } } #if RS_RCPP && !R_CMD @@ -89,71 +95,46 @@ int RunModel(Landscape* pLandscape, int seqsim) // 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 + + cout << "Running replicate " << rep + 1 << " / " << sim.reps << endl; if (sim.saveVisits && !ppLand.generated) { pLandscape->resetVisits(); } + if (sim.fixReplicateSeed) { + pRandom->fixNewSeed(rep); + } 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 pLandscape->resetLand(); -#if RSDEBUG - DEBUGLOG << "RunModel(): finished resetting landscape" << endl << endl; -#endif pLandscape->generatePatches(); -#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 } -#if RSDEBUG - DEBUGLOG << endl << "RunModel(): finished generating populations" << endl; -#endif + if (sim.patchSamplingOption == "random") { + // Then patches must be resampled for new landscape + int nbToSample = pSpecies->getNbPatchesToSample(); + auto patchesToSample = pLandscape->samplePatches(sim.patchSamplingOption, nbToSample, pSpecies); + pSpecies->setSamplePatchList(patchesToSample); + } } if (init.seedType == 0 && init.freeType < 2 && init.initFrzYr > 0) { // restrict available landscape to initialised region @@ -165,6 +146,20 @@ int RunModel(Landscape* pLandscape, int seqsim) } filesOK = true; +#if RS_RCPP + if(init.seedType==2 && init.indsFile=="NULL"){ // initialisation from InitInds list of dataframes + if(rep > 0){ + int error_init = 0; + Rcpp::S4 InitParamsR("InitialisationParams"); + InitParamsR = Rcpp::as(ParMaster.slot("init")); + Rcpp::List InitIndsList = Rcpp::as(InitParamsR.slot("InitIndsList")); + error_init = ReadInitIndsFileR(0, pLandscape, Rcpp::as(InitIndsList[rep])); + if(error_init>0) { + filesOK = false; + } + } + } +#endif if (rep == 0) { // open output files if (sim.outRange) { // open Range file @@ -176,7 +171,11 @@ int RunModel(Landscape* pLandscape, int seqsim) if (!pComm->outOccupancyStartLandscape()) { filesOK = false; } +#if RS_RCPP + if (sim.outPop && sim.CreatePopFile) { +#else if (sim.outPop) { +#endif // open Population file if (!pComm->outPopStartLandscape(pSpecies)) { filesOK = false; @@ -194,14 +193,13 @@ int RunModel(Landscape* pLandscape, int seqsim) if (!pLandscape->outConnectStartLandscape()) { filesOK = false; } + if (sim.outputWeirCockerham || sim.outputWeirHill) { // open neutral genetics file + if (!pComm->openNeutralOutputFile(pSpecies, ppLand.landNum)) { + 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->outRangeFinishLandscape(); @@ -217,6 +215,9 @@ int RunModel(Landscape* pLandscape, int seqsim) pComm->outTraitsRowsFinishLandscape(); if (sim.outConnect && ppLand.patchModel) pLandscape->outConnectFinishLandscape(); + if (sim.outputWeirCockerham || sim.outputWeirHill) { + pComm->openNeutralOutputFile(pSpecies, -999); + } #if RS_RCPP && !R_CMD return Rcpp::List::create(Rcpp::Named("Errors") = 666); #else @@ -237,26 +238,22 @@ int RunModel(Landscape* pLandscape, int seqsim) pLandscape->createConnectMatrix(); // variables to control dynamic landscape - landChange landChg; landChg.chgnum = 0; landChg.chgyear = 999999; + landChange landChg; landChg.chgNb = 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) { + + if (ppLand.rasterType == 2 && ppLand.spatialdemog) + pLandscape->updateDemoScalings(0); + +// 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 @@ -265,18 +262,18 @@ int RunModel(Landscape* pLandscape, int seqsim) if (sim.outInds) pComm->outIndsStartReplicate(rep, ppLand.landNum); // open a new genetics file for each replicate - if (sim.outGenetics) { - pComm->outGenStartReplicate(rep, ppLand.landNum); - if (!dem.stageStruct && sim.outStartGenetic == 0) { - // write genetic data for initialised individuals of non-strucutred population - pComm->outGenetics(rep, 0); + if (sim.outputGeneValues) { + bool geneOutFileHasOpened = pComm->openOutGenesFile(pSpecies->isDiploid(), ppLand.landNum, rep); + if (!geneOutFileHasOpened) throw logic_error("Output gene value file could not be initialised."); } + + // open a new genetics file for each replicate for per locus and pairwise stats + if (sim.outputWeirCockerham) { + pComm->openPerLocusFstFile(pSpecies, pLandscape, ppLand.landNum, rep); + } + if (sim.outputWeirHill) { + pComm->openPairwiseFstFile(pSpecies, pLandscape, ppLand.landNum, rep); } -#if RSDEBUG - // output initialised Individuals - if (sim.outInds) - pComm->outIndividuals(rep, -1, -1); -#endif #if RS_RCPP // open a new movement paths file for each replicate if (sim.outPaths) @@ -285,10 +282,6 @@ int RunModel(Landscape* pLandscape, int seqsim) // years loop 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 @@ -302,9 +295,9 @@ int RunModel(Landscape* pLandscape, int seqsim) || (yr < 3000001 && yr % 1000000 == 0) ) { #if RS_RCPP && !R_CMD - Rcpp::Rcout << "starting year " << yr << "..." << endl; + Rcpp::Rcout << "Starting year " << yr << "..." << endl; #else - cout << "starting year " << yr << endl; + cout << "Starting year " << yr << endl; #endif } if (init.seedType == 0 && init.freeType < 2) { @@ -321,13 +314,6 @@ int RunModel(Landscape* pLandscape, int seqsim) 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; } @@ -335,11 +321,6 @@ int RunModel(Landscape* pLandscape, int seqsim) 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; } @@ -359,22 +340,14 @@ int RunModel(Landscape* pLandscape, int seqsim) 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; + if (yr == landChg.chgYear) { // apply landscape change + landIx = landChg.chgNb; 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 @@ -395,10 +368,7 @@ int RunModel(Landscape* pLandscape, int seqsim) 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 + if (landChg.pathCostFile != "NULL") { // apply any SMS cost changes Cell* pCell; costchange = pLandscape->getCostChange(ixcostchg++); while (costchange.chgnum <= landIx && ixcostchg <= ncostchanges) { @@ -415,7 +385,7 @@ int RunModel(Landscape* pLandscape, int seqsim) landChg = pLandscape->getLandChange(landIx); } else { - landChg.chgyear = 9999999; + landChg.chgYear = 9999999; } } } @@ -423,14 +393,17 @@ int RunModel(Landscape* pLandscape, int seqsim) if (updateCC) { pLandscape->updateCarryingCapacity(pSpecies, yr, landIx); - } + if (ppLand.rasterType == 2 && ppLand.spatialdemog) //ppLand.spatialdemog false by default + pLandscape->updateDemoScalings((short)landIx); + + } if (sim.outConnect && ppLand.patchModel) pLandscape->resetConnectMatrix(); if (ppLand.dynamic && updateland) { - if (trfr.moveModel && trfr.moveType == 1) { // SMS + if (trfr.usesMovtProc && 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 @@ -457,23 +430,16 @@ int RunModel(Landscape* pLandscape, int seqsim) 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; + // TODO move translocation before dispersal? + if (manage.translocation && std::find(transloc.translocation_years.begin(), transloc.translocation_years.end(), yr) != transloc.translocation_years.end()) { + pManagement->translocate(yr + , pLandscape + , pSpecies + ); } -#endif // Output and pop. visualisation before reproduction - if (v.viewPop || v.viewTraits || sim.outOccup - || sim.outTraitsCells || sim.outTraitsRows || sim.saveMaps) + if (sim.outOccup || sim.outTraitsCells || sim.outTraitsRows) PreReproductionOutput(pLandscape, pComm, rep, yr, gen); // for non-structured population, also produce range and population output now if (!dem.stageStruct && (sim.outRange || sim.outPop)) @@ -504,24 +470,13 @@ int RunModel(Landscape* pLandscape, int seqsim) 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) { @@ -538,36 +493,44 @@ int RunModel(Landscape* pLandscape, int seqsim) else { // non-structured population pComm->survival0(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->outIndividuals(rep, yr, gen); - // output Genetics - if (sim.outGenetics && yr >= sim.outStartGenetic && yr % sim.outIntGenetic == 0) - pComm->outGenetics(rep, yr); - // survival part 1 - if (dem.stageStruct) { - pComm->survival1(); - } - else { // non-structured population - pComm->survival1(); + if ((sim.outputGeneValues || sim.outputWeirCockerham || sim.outputWeirHill) + && yr >= sim.outStartGenetics + && yr % sim.outputGeneticInterval == 0) { + + simParams sim = paramsSim->getSim(); + if (sim.patchSamplingOption != "list" && sim.patchSamplingOption != "random") { + // then patches must be re-sampled every gen + int nbToSample = pSpecies->getNbPatchesToSample(); + auto patchesToSample = pLandscape->samplePatches(sim.patchSamplingOption, nbToSample, pSpecies); + pSpecies->setSamplePatchList(patchesToSample); + } + // otherwise always use the user-specified list (even if patches are empty) + pComm->sampleIndividuals(pSpecies); + + if (sim.outputGeneValues) { + pComm->outputGeneValues(yr, gen, pSpecies); + } + if (sim.outputWeirCockerham || sim.outputWeirHill) { + pComm->outNeutralGenetics(pSpecies, rep, yr, gen, sim.outputWeirCockerham, sim.outputWeirHill); + } } -#if RSDEBUG - DEBUGLOG << "RunModel(): yr=" << yr << " gen=" << gen << " completed survival part 1" << endl; -#endif + + // Resolve survival and devlpt + pComm->survival1(); } // end of the generation loop -#if RSDEBUG - DEBUGLOG << "RunModel(): yr=" << yr << " completed generation loop" << endl; -#endif totalInds = pComm->totalInds(); - if (totalInds <= 0) { yr++; break; } + if (totalInds <= 0) { + cout << "All populations went extinct." << endl; + yr++; + break; + } // Connectivity Matrix if (sim.outConnect && ppLand.patchModel @@ -577,9 +540,6 @@ int RunModel(Landscape* pLandscape, int seqsim) if (dem.stageStruct && sstruct.survival == 2) { // annual survival - all stages pComm->survival0(1, 2); pComm->survival1(); -#if RSDEBUG - DEBUGLOG << "RunModel(): yr=" << yr << " completed annual survival" << endl; -#endif } if (dem.stageStruct) { @@ -587,25 +547,22 @@ int RunModel(Landscape* pLandscape, int seqsim) if (sim.outInds && yr >= sim.outStartInd && yr % sim.outIntInd == 0) pComm->outIndividuals(rep, yr, -1); // list any individuals dying having reached maximum age pComm->survival1(); // 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; } + if (totalInds <= 0) { + cout << "All populations went extinct." << endl; + yr++; + break; + } } } // end of the years loop // Final output // produce final summary output - if (v.viewPop || v.viewTraits || sim.outOccup - || sim.outTraitsCells || sim.outTraitsRows || sim.saveMaps) + if (sim.outOccup || sim.outTraitsCells || sim.outTraitsRows) 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(); @@ -613,12 +570,6 @@ int RunModel(Landscape* pLandscape, int seqsim) 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) @@ -627,7 +578,6 @@ int RunModel(Landscape* pLandscape, int seqsim) 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 @@ -649,15 +599,14 @@ int RunModel(Landscape* pLandscape, int seqsim) pLandscape->resetPatches(); } if (ppLand.dynamic) { - trfrRules trfr = pSpecies->getTrfr(); - if (trfr.moveModel && trfr.moveType == 1) { // SMS + transferRules trfr = pSpecies->getTransferRules(); + if (trfr.usesMovtProc && 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); @@ -670,18 +619,21 @@ int RunModel(Landscape* pLandscape, int seqsim) 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->outIndsFinishReplicate(); - if (sim.outGenetics) // close Genetics output file - pComm->outGenFinishReplicate(); + + if (sim.outputGeneValues) { // close genetic values output file + pComm->openOutGenesFile(false, -999, rep); + } + + if (sim.outputWeirCockerham) //close per locus file + pComm->openPerLocusFstFile(pSpecies, pLandscape, -999, rep); + if (sim.outputWeirHill) //close per locus file + pComm->openPairwiseFstFile(pSpecies, pLandscape, -999, rep); if (sim.saveVisits) { pLandscape->outVisits(rep, ppLand.landNum); @@ -692,9 +644,6 @@ int RunModel(Landscape* pLandscape, int seqsim) if (sim.outPaths) pLandscape->outPathsFinishReplicate(); #endif -#if RSDEBUG - DEBUGLOG << endl << "RunModel(): finished rep=" << rep << endl; -#endif } // end of the replicates loop @@ -706,7 +655,7 @@ int RunModel(Landscape* pLandscape, int seqsim) // Occupancy outputs if (sim.outOccup && sim.reps > 1) { pComm->outOccupancy(); - pComm->outOccSuit(v.viewGraph); + pComm->outOccSuit(); pComm->deleteOccupancy((sim.years / sim.outIntOcc) + 1); pComm->outOccupancyFinishLandscape(); } @@ -714,7 +663,11 @@ int RunModel(Landscape* pLandscape, int seqsim) if (sim.outRange) { pComm->outRangeFinishLandscape(); // close Range file } +#if RS_RCPP + if (sim.outPop && sim.CreatePopFile) { +#else if (sim.outPop) { +#endif pComm->outPopFinishLandscape(); // close Population file } if (sim.outTraitsCells) @@ -724,9 +677,16 @@ int RunModel(Landscape* pLandscape, int seqsim) // close Individuals & Genetics output files if open // they can still be open if the simulation was stopped by the user if (sim.outInds) pComm->outIndsFinishReplicate(); - if (sim.outGenetics) pComm->outGenFinishReplicate(); + if (sim.outputGeneValues) pComm->openOutGenesFile(0, -999, 0); + if (sim.outputWeirCockerham || sim.outputWeirHill) { + pComm->openNeutralOutputFile(pSpecies, -999); + } + if (sim.outputWeirCockerham) { + pComm->openPerLocusFstFile(pSpecies, pLandscape, -999, 0); + } + if (sim.outputWeirHill) pComm->openPairwiseFstFile(pSpecies, pLandscape, -999, 0); - delete pComm; + delete pComm; pComm = 0; #if RS_RCPP && !R_CMD @@ -737,6 +697,7 @@ int RunModel(Landscape* pLandscape, int seqsim) } + #if LINUX_CLUSTER || RS_RCPP // Check whether a specified directory path exists bool is_directory(const char* pathname) { @@ -748,49 +709,44 @@ bool is_directory(const char* pathname) { #endif //--------------------------------------------------------------------------- -bool CheckDirectory(void) +bool CheckDirectory(const string& pathToProjDir) { bool errorfolder = false; string subfolder; - subfolder = paramsSim->getDir(0) + "Inputs"; + subfolder = pathToProjDir + "Inputs"; const char* inputs = subfolder.c_str(); if (!is_directory(inputs)) errorfolder = true; - subfolder = paramsSim->getDir(0) + "Outputs"; + subfolder = pathToProjDir + "Outputs"; const char* outputs = subfolder.c_str(); if (!is_directory(outputs)) errorfolder = true; - subfolder = paramsSim->getDir(0) + "Output_Maps"; + subfolder = pathToProjDir + "Output_Maps"; const char* outputmaps = subfolder.c_str(); if (!is_directory(outputmaps)) errorfolder = true; - return errorfolder; + if (errorfolder) { + cout << endl << "***** Invalid working directory: " << pathToProjDir + << endl << endl; + cout << "***** Working directory must contain Inputs, Outputs and Output_Maps folders" + << endl << endl; + cout << "*****" << endl; + cout << "***** Simulation ABORTED" << endl; + cout << "*****" << endl; + return false; +} + else return true; } //--------------------------------------------------------------------------- //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 // trait outputs and visualisation - if (v.viewTraits - || ((sim.outTraitsCells && yr >= sim.outStartTraitCell && yr % sim.outIntTraitCell == 0) || - (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0))) + if ((sim.outTraitsCells && yr >= sim.outStartTraitCell && yr % sim.outIntTraitCell == 0) + || (sim.outTraitsRows && yr >= sim.outStartTraitRow && yr % sim.outIntTraitRow == 0)) { pComm->outTraits(pSpecies, rep, yr, gen); } @@ -806,7 +762,11 @@ void RangePopOutput(Community* pComm, int rep, int yr, int gen) if (sim.outRange && (yr % sim.outIntRange == 0 || pComm->totalInds() <= 0)) pComm->outRange(pSpecies, rep, yr, gen); +#if RS_RCPP +if (sim.outPop && sim.CreatePopFile && yr >= sim.outStartPop && yr%sim.outIntPop == 0) +#else if (sim.outPop && yr >= sim.outStartPop && yr % sim.outIntPop == 0) +#endif pComm->outPop(rep, yr, gen); } @@ -822,10 +782,10 @@ void OutParameters(Landscape* pLandscape) 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); settleRules srules; settleSteps ssteps; @@ -842,15 +802,8 @@ void OutParameters(Landscape* pLandscape) name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) + "_Parameters.txt"; outPar.open(name.c_str()); - outPar << "RangeShifter 2.0 "; + outPar << "RangeShifter 3.0 "; -#if !RS_RCPP -#if RSWIN64 - outPar << " - 64 bit implementation"; -#else - outPar << " - 32 bit implementation"; -#endif -#endif outPar << endl; outPar << "================ "; @@ -859,10 +812,9 @@ void OutParameters(Landscape* pLandscape) 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 + if (sim.batchMode) outPar << "yes" << endl; + else outPar << "no" << endl; + outPar << "SEED \t" << pRandom->getSeed() << endl; outPar << "REPLICATES \t" << sim.reps << endl; outPar << "YEARS \t" << sim.years << endl; outPar << "REPRODUCTIVE SEASONS / YEAR\t" << dem.repSeasons << endl; @@ -918,17 +870,6 @@ void OutParameters(Landscape* pLandscape) } #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; } @@ -942,17 +883,17 @@ void OutParameters(Landscape* pLandscape) 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; + outPar << "Change no. " << chg.chgNb << " in year " << chg.chgYear << endl; + outPar << "Landscape: " << chg.pathHabFile << endl; if (ppLand.patchModel) { - outPar << "Patches : " << chg.pchfile << endl; + outPar << "Patches : " << chg.pathPatchFile << endl; } - if (chg.costfile != "none" && chg.costfile != "NULL") { - outPar << "Costs : " << chg.costfile << endl; + if (chg.pathCostFile != "none" && chg.pathCostFile != "NULL") { + outPar << "Costs : " << chg.pathCostFile << endl; } + } } - outPar << endl << "SPECIES DISTRIBUTION LOADED: \t"; if (ppLand.spDist) { @@ -961,9 +902,6 @@ void OutParameters(Landscape* pLandscape) 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 @@ -1140,7 +1078,7 @@ void OutParameters(Landscape* pLandscape) } int mSize; // index for weights matrices - if (dem.repType == 2) mSize = sstruct.nStages * NSEXES; + if (dem.repType == 2) mSize = sstruct.nStages * gMaxNbSexes; else mSize = sstruct.nStages; outPar << "DENSITY-DEPENDENCE IN FECUNDITY:\t"; @@ -1150,8 +1088,8 @@ void OutParameters(Landscape* pLandscape) 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"; + outPar << "stage " << i / gMaxNbSexes << " "; + if (i % gMaxNbSexes == 0) outPar << "males : \t"; else outPar << "females: \t"; } else outPar << "stage " << i << ": \t"; @@ -1172,8 +1110,8 @@ void OutParameters(Landscape* pLandscape) 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"; + outPar << "stage " << i / gMaxNbSexes << " "; + if (i % gMaxNbSexes == 0) outPar << "males : \t"; else outPar << "females: \t"; } else outPar << "stage " << i << ": \t"; @@ -1192,8 +1130,8 @@ void OutParameters(Landscape* pLandscape) 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"; + outPar << "stage " << i / gMaxNbSexes << " "; + if (i % gMaxNbSexes == 0) outPar << "males : \t"; else outPar << "females: \t"; } else outPar << "stage " << i << ": \t"; @@ -1204,6 +1142,49 @@ void OutParameters(Landscape* pLandscape) else outPar << "not stage-dependent" << endl; } else outPar << "no" << endl; + + if (ppLand.spatialdemog){ + outPar << "SPATIALLY VARYING DEMOGRAPHY:\t in" << endl; + // file names for the spatial layers + + if(pSpecies->getFecSpatial()){ + outPar << "FECUNDITY" << endl; + outPar << "LAYERS:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + if (dem.repType == 2){ + outPar << "stage: " << i << "females: \t" << pSpecies->getFecLayer(i,0) << "\tmales: \t" << pSpecies->getFecLayer(i,1) << endl; + } else{ + outPar << "stage: " << i << pSpecies->getFecLayer(i,0) << endl; + } + } + } + + if(pSpecies->getDevSpatial()){ + outPar << "DEVELOPMENT" << endl; + outPar << "LAYERS:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + if (dem.repType == 2){ + outPar << "stage: " << i << "females: \t" << pSpecies->getDevLayer(i,0) << "\tmales: \t" << pSpecies->getDevLayer(i,1) << endl; + } else{ + outPar << "stage: " << i << pSpecies->getDevLayer(i,0) << endl; + } + } + } + if(pSpecies->getSurvSpatial()){ + outPar << "SURVIVAL" << endl; + outPar << "LAYERS:" << endl; + for (int i = 0; i < sstruct.nStages; i++) { + if (dem.repType == 2){ + outPar << "stage: " << i << "females: \t" << pSpecies->getSurvLayer(i,0) << "\tmales: \t" << pSpecies->getSurvLayer(i,1) << endl; + } else{ + outPar << "stage: " << i << pSpecies->getSurvLayer(i,0) << endl; + } + } + } + } + else { + outPar << "SPATIALLY VARYING DEMOGRAPHY:\t no" << endl; + } } // end of if (dem.stageStruct) else { // not stage-strutured outPar << "no" << endl; @@ -1233,9 +1214,7 @@ void OutParameters(Landscape* pLandscape) else outPar << "K "; outPar << k << endl; } - emigTraits ep0, ep1; - emigParams eparams0, eparams1; string sexdept = "SEX-DEPENDENT: "; string stgdept = "STAGE-DEPENDENT: "; string indvar = "INDIVIDUAL VARIABILITY: "; @@ -1244,7 +1223,6 @@ void OutParameters(Landscape* pLandscape) outPar << endl << "DISPERSAL - EMIGRATION:\t"; if (emig.densDep) { outPar << "density-dependent" << endl; - if (emig.sexDep) { outPar << sexdept << "yes" << endl; if (emig.stgDep) { @@ -1252,8 +1230,8 @@ void OutParameters(Landscape* pLandscape) 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); + ep0 = pSpecies->getSpEmigTraits(i, 0); + ep1 = pSpecies->getSpEmigTraits(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; @@ -1261,79 +1239,37 @@ void OutParameters(Landscape* pLandscape) } 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); + ep0 = pSpecies->getSpEmigTraits(0, 0); + ep1 = pSpecies->getSpEmigTraits(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); + ep0 = pSpecies->getSpEmigTraits(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); + ep0 = pSpecies->getSpEmigTraits(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 + if (!trfr.usesMovtProc) { // transfer by kernel outPar << "USE FULL KERNEL TO DETERMINE EMIGRATION: "; if (pSpecies->useFullKernel()) outPar << "yes"; else outPar << "no"; @@ -1347,34 +1283,15 @@ void OutParameters(Landscape* pLandscape) 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; + << pSpecies->getSpEmigD0(i, 0) << " \tmales " << pSpecies->getSpEmigD0(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 << "EMIGRATION PROB.: \tfemales " << pSpecies->getSpEmigD0(0, 0) + << "\t males " << pSpecies->getSpEmigD0(0, 1) << 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) { @@ -1382,45 +1299,27 @@ void OutParameters(Landscape* pLandscape) outPar << indvar << "no" << endl; for (int i = 0; i < sstruct.nStages; i++) { outPar << "stage " << i << ": \t" << "EMIGRATION PROB.: " - << pSpecies->getEmigD0(i, 0) << endl; + << pSpecies->getSpEmigD0(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 << "EMIGRATION PROB.:\t" << pSpecies->getSpEmigD0(0, 0) << 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.usesMovtProc) { + bool straightenPath; if (trfr.moveType == 1) { // SMS - trfrSMSTraits move = pSpecies->getSMSTraits(); - straigtenPath = move.straigtenPath; + trfrSMSTraits move = pSpecies->getSpSMSTraits(); + straightenPath = move.straightenPath; 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; @@ -1448,48 +1347,19 @@ void OutParameters(Landscape* pLandscape) 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; + trfrCRWTraits move = pSpecies->getSpCRWTraits(); + straightenPath = move.straightenPath; 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; + if (straightenPath) outPar << "yes" << endl; else outPar << "no" << endl; outPar << "STEP MORTALITY:\t" << endl; if (trfr.habMort) @@ -1508,15 +1378,14 @@ void OutParameters(Landscape* pLandscape) } else { - trfrCRWTraits move = pSpecies->getCRWTraits(); + trfrCRWTraits move = pSpecies->getSpCRWTraits(); 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; + trfrKernelParams kern0, kern1; outPar << "dispersal kernel" << endl << "TYPE: \t"; if (trfr.twinKern) outPar << "double "; outPar << "negative exponential" << endl; @@ -1528,8 +1397,8 @@ void OutParameters(Landscape* pLandscape) 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); + kern0 = pSpecies->getSpKernTraits(i, 0); + kern1 = pSpecies->getSpKernTraits(i, 1); outPar << meandist << " I: \tfemales " << kern0.meanDist1 << " \tmales " << kern1.meanDist1 << endl; if (trfr.twinKern) { @@ -1540,37 +1409,8 @@ void OutParameters(Landscape* pLandscape) } 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); + kern0 = pSpecies->getSpKernTraits(0, 0); + kern1 = pSpecies->getSpKernTraits(0, 1); outPar << meandist << " I: \tfemales " << kern0.meanDist1 << " \tmales " << kern1.meanDist1 << endl; if (trfr.twinKern) { @@ -1579,14 +1419,13 @@ void OutParameters(Landscape* pLandscape) } } } - } 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); + kern0 = pSpecies->getSpKernTraits(i, 0); outPar << "stage " << i << ": \t" << meandist << " I: " << kern0.meanDist1; if (trfr.twinKern) { @@ -1598,26 +1437,7 @@ void OutParameters(Landscape* pLandscape) } 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); + kern0 = pSpecies->getSpKernTraits(0, 0); outPar << meandist << " I: \t" << kern0.meanDist1 << endl; if (trfr.twinKern) { @@ -1626,7 +1446,6 @@ void OutParameters(Landscape* pLandscape) } } } - } outPar << "DISPERSAL MORTALITY: "; trfrMortParams mort = pSpecies->getMortParams(); @@ -1643,7 +1462,7 @@ void OutParameters(Landscape* pLandscape) outPar << endl << "DISPERSAL - SETTLEMENT:" << endl; - if (trfr.moveModel) { + if (trfr.usesMovtProc) { string plusmating = "+ mating requirements"; if (sett.sexDep) { @@ -1719,7 +1538,7 @@ void OutParameters(Landscape* pLandscape) outPar << "find a suitable cell/patch "; srules = pSpecies->getSettRules(i, sx); if (srules.densDep) { - settleDD = pSpecies->getSettTraits(i, sx); + settleDD = pSpecies->getSpSettTraits(i, sx); outPar << "+ density dependence "; if (srules.findMate) outPar << plusmating; outPar << endl; @@ -1740,25 +1559,7 @@ void OutParameters(Landscape* pLandscape) } } } - 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 "; @@ -1818,72 +1619,65 @@ void OutParameters(Landscape* pLandscape) } // 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); - } + // only if genetics are simulated + if(sim.outputGeneValues) { + set traitList = pSpecies->getTraitTypes(); + + if (pSpecies->isDiploid()) outPar << "DIPLOID" << endl; else outPar << "HAPLOID" << endl; + outPar << "Genome size: " << pSpecies->getGenomeSize() << endl; + outPar << "Chromosome breaks : "; + + for (auto end : pSpecies->getChromosomeEnds()) + outPar << end << " "; 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; - } - } + outPar << "Recombination rate: " << pSpecies->getRecombinationRate() << endl; + outPar << "Traits modelled: " << endl; + for (auto trait : traitList) + outPar << trait << endl; + } else { + outPar << "No genetics simulated" << endl; + } + + + // Management + managementParams manage = pManagement->getManagementParams(); + translocationParams transloc = pManagement->getTranslocationParams(); + if(manage.translocation){ + outPar << endl << "MANAGEMENT - TRANSLOCATION: \t"; + // loop over translocation_years and print them + outPar << endl; + outPar << "Catching rate: " << transloc.catching_rate << endl; + for( int i = 0; i < transloc.translocation_years.size(); i++ ) { + auto yr = transloc.translocation_years[i]; + auto it = transloc.nb.find(yr); + auto nb_it = transloc.nb.find(yr); + auto source_it = transloc.source.find(yr); + auto target_it = transloc.target.find(yr); + auto min_age_it = transloc.min_age.find(yr); + auto max_age_it = transloc.max_age.find(yr); + auto stage_it = transloc.stage.find(yr); + auto sex_it = transloc.sex.find(yr); + outPar << " Translocation events in year: " << yr << endl; + for( int j = 0; j < it->second.size(); j++ ){ + outPar << " Event Nr. " << j+1 << " :" << endl; + // if it is a cell based model + if(ppLand.patchModel){ + outPar << " Source patch ID: " << source_it->second[j].x << endl; + outPar << " Target patch ID: " << target_it->second[j].x << endl; + } else{ + outPar << " Source cell: X " << source_it->second[j].x << " Y " << source_it->second[j].y << endl; + outPar << " Target cell: X " << target_it->second[j].x << " Y " << target_it->second[j].y << endl; + } + outPar << " Min age: " << min_age_it->second[j] << endl; + outPar << " Max age: " << max_age_it->second[j] << endl; + outPar << " Stage: " << stage_it->second[j] << endl; + outPar << " Sex: " << sex_it->second[j] << endl; + outPar << " Number of individuals: " << nb_it->second[j] << endl; + + } + } } // Initialisation @@ -1968,6 +1762,9 @@ void OutParameters(Landscape* pLandscape) 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 != 1 && init.freeType < 2 && init.initFrzYr > 0) { + // outPar << "Freeze initial range until year " << init.initFrzYr << endl; + // } if (init.seedType == 0 && init.freeType < 2) { if (init.initFrzYr > 0) { outPar << "Freeze initial range until year " << init.initFrzYr << endl; @@ -1986,11 +1783,13 @@ void OutParameters(Landscape* pLandscape) if (sim.outRange) { outPar << "Range - every " << sim.outIntRange << " year"; if (sim.outIntRange > 1) outPar << "s"; + // if (sim.outStartRange > 0) outPar << " starting year " << sim.outStartRange; outPar << endl; } if (sim.outOccup) { outPar << "Occupancy - every " << sim.outIntOcc << " year"; if (sim.outIntOcc > 1) outPar << "s"; + // if (sim.outStartOcc > 0) outPar << " starting year " << sim.outStartOcc; outPar << endl; } if (sim.outPop) { @@ -2005,24 +1804,11 @@ void OutParameters(Landscape* pLandscape) 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)"; + if (sim.outputWeirCockerham || sim.outputWeirHill) { + outPar << "Neutral genetics - every " << sim.outputGeneticInterval << " year"; + if (sim.outputGeneticInterval > 1) outPar << "s"; + if (sim.outputWeirHill) outPar << " outputting pairwise patch fst"; + if (sim.outputWeirCockerham) outPar << " outputting per locus fst "; outPar << endl; } @@ -2054,28 +1840,13 @@ void OutParameters(Landscape* pLandscape) 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) { + + if (trfr.usesMovtProc && trfr.moveType == 1) { outPar << "SMS HEAT MAPS: "; if (sim.saveVisits) outPar << "yes" << endl; else outPar << "no" << endl; } - outPar.close(); outPar.clear(); - } //--------------------------------------------------------------------------- diff --git a/Model.h b/Model.h index eaf0c67..6ce2e46 100644 --- a/Model.h +++ b/Model.h @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 @@ -33,14 +33,14 @@ 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. + 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. + 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 + Last updated: 28 July 2021 by Greta Bocedi ------------------------------------------------------------------------------*/ #ifndef ModelH @@ -48,26 +48,29 @@ Last updated: 26 October 2021 by Steve Palmer #include #include +#if RS_RCPP +#include +#include "../Rinterface.h" +#endif // RS_RCPP +#include #include "Parameters.h" #include "Landscape.h" #include "Community.h" #include "SubCommunity.h" #include "Species.h" +#include "Management.h" #if !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 + int, // sequential simulation number + Rcpp::S4 // parameter master to read initial conditions in each replicate simulation ); #else int RunModel( @@ -75,7 +78,9 @@ int RunModel( int // sequential simulation number ); #endif // RS_RCPP && !R_CMD -bool CheckDirectory(void); + +bool CheckDirectory(const string& pathToProjDir); + void PreReproductionOutput( Landscape*, // pointer to Landscape Community*, // pointer to Community @@ -100,15 +105,9 @@ extern Species *pSpecies; extern paramSim *paramsSim; extern paramInit *paramsInit; extern Community *pComm; +extern Management *pManagement; -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; #if RS_RCPP diff --git a/NeutralStatsManager.cpp b/NeutralStatsManager.cpp new file mode 100644 index 0000000..14e216d --- /dev/null +++ b/NeutralStatsManager.cpp @@ -0,0 +1,513 @@ + +#include "NeutralStatsManager.h" +#include "Population.h" + +/*---------------------------------------------------------------------------- + * + * 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 . + * + * File Created by Roslyn Henry March 2023. Code adapted from NEMO (https://nemo2.sourceforge.io/) + --------------------------------------------------------------------------*/ + + // ---------------------------------------------------------------------------------------- + // Constructor + // ---------------------------------------------------------------------------------------- +NeutralStatsManager::NeutralStatsManager(const int& nbSampledPatches, const int nLoci) { + this->pairwiseFstMatrix = PatchMatrix(nbSampledPatches, nbSampledPatches); + commNeutralCountTables.reserve(nLoci); //don't have to be pointers, not shared or moved + + perLocusFst = perLocusFis = perLocusFit = vector(nLoci, 0.0); +} + +// ---------------------------------------------------------------------------------------- +// Populate population and community-level NEUTRAL count tables +// Update allele occurrence and heterozygosity counts, and allele frequencies +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::updateAllNeutralTables(Species* pSpecies, Landscape* pLandscape, set const& patchList) { + + const int nLoci = pSpecies->getNPositionsForTrait(NEUTRAL); + const int maxNbNeutralAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + const int ploidy = pSpecies->isDiploid() ? 2 : 1; + + // Create / Update community-level NEUTRAL counts table + if (!commNeutralCountTables.empty()) { + resetCommNeutralTables(); + } + else { // populate the tables with default values + for (int thisLocus = 0; thisLocus < nLoci; thisLocus++) { + NeutralCountsTable newNeutralTbl = NeutralCountsTable(maxNbNeutralAlleles); + commNeutralCountTables.push_back(newNeutralTbl); + } + } + + int nbSampledInds = 0; + int patchAlleleCount; + + // Update counts for each population + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + // Update this population's NEUTRAL counts tables + pPop->updatePopNeutralTables(); + nbSampledInds += pPop->sampleSize(); + } + // Add population-level counts to community-level counts + for (int thisLocus = 0; thisLocus < nLoci; thisLocus++) { + for (int allele = 0; allele < maxNbNeutralAlleles; allele++) { + + if (pPop != 0) { + patchAlleleCount = pPop->getAlleleTally(thisLocus, allele); + } + else { + patchAlleleCount = 0; + } + commNeutralCountTables[thisLocus].incrementTallyBy(patchAlleleCount, allele); + } + } + } + + // Update community-level frequencies + std::for_each(commNeutralCountTables.begin(), + commNeutralCountTables.end(), + [&](NeutralCountsTable& v) -> void { + v.setFrequencies(nbSampledInds * ploidy); + }); +} + +// ---------------------------------------------------------------------------------------- +// Reset allele tables in NeutralTable structs +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::resetCommNeutralTables() { + for (auto& entry : commNeutralCountTables) { + entry.reset(); + } +} + +// ---------------------------------------------------------------------------------------- +// Calculate allelic diversity metrics +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calcAllelicDiversityMetrics(set const& patchList, const int nInds, Species* pSpecies, Landscape* pLandscape) +{ + int i, j; + const int nLoci = pSpecies->getNPositionsForTrait(NEUTRAL); + const int nAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + const int ploidy = pSpecies->isDiploid() ? 2 : 1; + unsigned int nbPops = 0; + int nbAllelesInPatch = 0; + double meanAllelicDivInPatch = 0; + bool alleleExistsInPop = 0; + + vector> alleleExistsInCommTable(nLoci); + for (i = 0; i < nLoci; ++i) { + alleleExistsInCommTable[i] = vector(nAlleles, false); + } + + // Compute mean nb alleles per locus per patch + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + if (pPop->sampleSize() > 0) { + nbPops++; + nbAllelesInPatch = 0; + for (i = 0; i < nLoci; ++i) + for (j = 0; j < nAlleles; ++j) { + alleleExistsInPop = pPop->getAlleleTally(i, j) != 0; + nbAllelesInPatch += alleleExistsInPop; + alleleExistsInCommTable[i][j] = alleleExistsInCommTable[i][j] || alleleExistsInPop; + } + // add mean nb of alleles per locus for Patch k to the pop mean + meanAllelicDivInPatch += static_cast(nbAllelesInPatch) / nLoci; + } + } + } + meanNbAllelesPerLocusPerPatch = nbPops > 0 ? meanAllelicDivInPatch / nbPops : 0; + + // Compute mean nb alleles per locus + meanNbAllelesPerLocus = 0; + for (i = 0; i < nLoci; ++i) + for (j = 0; j < nAlleles; ++j) + meanNbAllelesPerLocus += alleleExistsInCommTable[i][j]; + meanNbAllelesPerLocus /= nLoci; + + // Compute number of fixed loci per patch + // mean number of loci that are fixed at pop level per pop + meanNbFixedLociPerPatch = 0; + if (nbPops > 0) { + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + for (i = 0; i < nLoci; ++i) + for (j = 0; j < nAlleles; ++j) + meanNbFixedLociPerPatch += pPop->getAlleleFrequency(i, j) == 1; + } + } + meanNbFixedLociPerPatch /= nbPops; + } + + // Compute number of fixed loci + meanFixedLoci = 0; + for (i = 0; i < nLoci; ++i) + for (j = 0; j < nAlleles; ++j) + meanFixedLoci += commNeutralCountTables[i].getFrequency(j) == 1; +} + +// ---------------------------------------------------------------------------------------- +// Calculate Ho per Nei and Chesser +// Average (observed) heterozygosity per individual +// Sum (nb of heterozygote loci) across individuals / nb individuals / nb loci +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calculateHo(set const& patchList, const int nbInds, const int nbrLoci, Species* pSpecies, Landscape* pLandscape) { + + int nbHetero = 0; + + if (nbInds != 0 && pSpecies->isDiploid()) { + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) nbHetero += pPop->countHeterozygoteLoci(); + } + ho = static_cast(nbHetero) / (nbInds * nbrLoci); + } + else ho = 0.0; +} + +// ---------------------------------------------------------------------------------------- +// Calculate Hs per Nei and Chesser +// Average expected population-level heterozygosity per locus per population +// currently not used but may be useful +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calculateHs(set const& patchList, const int nbrLoci, Species* pSpecies, Landscape* pLandscape) { + + double hs = 0; + int nPatches = 0; + + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop->sampleSize() > 0) { + nPatches++; + hs += pPop->computeHs(); + } + } + hs = (nPatches != 0 ? hs / (nbrLoci * nPatches) : 0.0); +} + +// ---------------------------------------------------------------------------------------- +// Calculate Ht per Nei and Chesser +// Average expected community-level heterozygosity per locus +// Currently not used but may be useful +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calculateHt(Species* pSpecies, Landscape* pLandscape, const int nLoci, const int nAlleles) { + + double ht = 0; + int nPatches = 0; + vectorlocihet(nLoci, 1); + double freq; + + for (int thisLocus = 0; thisLocus < nLoci; ++thisLocus) { + for (int allele = 0; allele < nAlleles; ++allele) { + freq = commNeutralCountTables[thisLocus].getFrequency(allele); + freq *= freq; //squared frequencies + locihet[thisLocus] -= freq; //1 - sum of p2 = expected heterozygosity + } + ht += locihet[thisLocus]; + } + ht = ht / nLoci; +} + +// ---------------------------------------------------------------------------------------- +// Calculate Ho per locus as per Nei and Chesser +// Observed proportion of heterozygote individuals for each locus +// Sum (nb of heterozygote individuals) / nb individuals for each locus +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calculatePerLocusHo(set const& patchList, const int nbInds, const int nbrLoci, Species* pSpecies, Landscape* pLandscape) { + + vector nbHeterosInComm(nbrLoci, 0); + vector nbHeterosInPop(nbrLoci); + + if (pSpecies->isDiploid()) { + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + if (pPop->sampleSize() > 0) { + nbHeterosInPop = pPop->countNbHeterozygotesEachLocus(); + // Add counts to community total + transform(nbHeterosInComm.begin(), nbHeterosInComm.end(), nbHeterosInPop.begin(), + nbHeterosInComm.begin(), plus()); + } + } + } + } + + perLocusHo = vector(nbrLoci, 0); + if (nbInds != 0) { + for (int i = 0; i < nbHeterosInComm.size(); i++) { + perLocusHo[i] = static_cast(nbHeterosInComm[i]) / nbInds; + } + } +} + +// ---------------------------------------------------------------------------------------- +// Fstat Weir & Cockerham +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calculateFstatWC(set const& patchList, const int nbSampledIndsInComm, const int nLoci, const int nAlleles, Species* pSpecies, Landscape* pLandscape) { + + double inverseNtotal; + double sumWeights = 0; + double nBar, nC, inverseNbar; + unsigned int nbPops = 0; + const int totalSampleSize = nbSampledIndsInComm; // r * n_bar + + // Reset per-locus vectors between generations + perLocusFst = perLocusFis = perLocusFit = vector(nLoci, 0.0); + + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + int popSampleSize = pPop->sampleSize(); // n_i + if (popSampleSize > 0) { + nbPops++; + sumWeights += static_cast(popSampleSize * popSampleSize) / totalSampleSize; // sum(n_i^2/rn_bar) + } + } + } + + nbExtantPops = nbPops; // r + totalNbSampledInds = nbSampledIndsInComm; + + if (nbPops > 1) { + + // Calculate F stats + nBar = static_cast(totalSampleSize) / nbPops; // average sample size, cannot be less than 1 + nC = (totalSampleSize - sumWeights) / (nbPops - 1); + double nBarMinusOne = (nBar == 1.0) ? 1.0 : nBar - 1.0; // avoid / 0 if exactly 1 ind per pop + inverseNbar = 1.0 / nBarMinusOne; + inverseNtotal = 1.0 / totalSampleSize; + + double var, intermediateTerm; + double s2, pBar, hBar; + double s2Denom = (nbPops - 1) * nBar; + double rTerm = static_cast(nbPops - 1) / nbPops; + double hBarFactor = (2 * nBar - 1) / (4 * nBar); + + double numFst = 0.0, numFis = 0.0, numFit = 0.0; + double denomFst = 0.0, denomFis = 0.0, denomFit = 0.0; + + for (int l = 0; l < nLoci; ++l) { + + // Sums of a_u, b_u, c_u for all alleles u at locus l + double a_l = 0, b_l = 0, c_l = 0; + + for (int u = 0; u < nAlleles; ++u) { + + s2 = hBar = 0; + pBar = commNeutralCountTables[l].getFrequency(u); + for (int patchId : patchList) { + const auto patch = pLandscape->findPatch(patchId); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + var = pPop->getAlleleFrequency(l, u) - pBar; + var *= var; + s2 += var * pPop->sampleSize(); + hBar += pPop->getHeteroTally(l, u); // n_i * h_i + } + } //end for pop + + s2 /= s2Denom; + hBar /= static_cast(totalSampleSize); // / (r * n_bar) + + intermediateTerm = pBar * (1 - pBar) - rTerm * s2; + a_l += s2 - inverseNbar * (intermediateTerm - 0.25 * hBar); + b_l += intermediateTerm - hBarFactor * hBar; + c_l += hBar; + + } // end for allele + + a_l *= nBar / nC; + b_l *= nBar / nBarMinusOne; + c_l *= 0.5; + + perLocusFst[l] = a_l / (a_l + b_l + c_l); + perLocusFis[l] = (b_l + c_l) == 0.0 ? 0.0 : b_l / (b_l + c_l); + perLocusFit[l] = a_l + b_l / (a_l + b_l + c_l); + + numFst += a_l; + numFis += b_l; + numFit += a_l + b_l; + denomFst += a_l + b_l + c_l; + denomFis += b_l + c_l; + + } // end for locus + + denomFit = denomFst; // same quantity + + fst = numFst / denomFst; // theta hat in eq. 1 in WC 1984 + fis = (denomFis == 0.0) ? 0.0 : numFis / denomFis; // f hat + fit = numFit / denomFit; // F hat + } + else { // zero or one sampled pops, cannot compute F-stats + fst = 0.0; + fis = 0.0; + fit = 0.0; + } +} + +// ---------------------------------------------------------------------------------------- +// Patch pairwise Fst +// Computes the weighted within and between patch Fst's as well as the overall Fst (Theta). +// The method used here is that of Weir & Hill 2002, Ann.Rev.Genet. 36:721 - 750. +// The weighting is done for samples(patches) of unequal sizes. +// ---------------------------------------------------------------------------------------- +void NeutralStatsManager::calcPairwiseWeightedFst(set const& patchList, const int nInds, const int nLoci, Species* pSpecies, Landscape* pLandscape) { + + const int nAlleles = (int)pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + + // Needs to be in vector to iterate over, copy preserves order + vector patchVect; + copy(patchList.begin(), patchList.end(), std::back_inserter(patchVect)); + + int nPatches = static_cast(patchList.size()); + int nbPops = 0; + + // Initialise + pairwiseFstMatrix = PatchMatrix(nPatches, nPatches); + + // Reset table + pairwiseFstMatrix.setAll(0.0); // or nanf("NULL")? + + //init + vector popWeights(nPatches); + vector popSizes(nPatches); + vector> numeratorPairwiseFst(nPatches); + for (int i = 0; i < nPatches; i++) numeratorPairwiseFst[i].resize(nPatches); + double totSize; + double numeratorWeightedFst = 0; + double denominator = 0; + double sumWeights = 0; + + totalNbSampledInds = nInds; + totSize = nInds; + + // Calculate weight (n_ic) terms + for (int i = 0; i < nPatches; ++i) { + const auto patch = pLandscape->findPatch(patchVect[i]); + const auto pPop = patch->getPopn(pSpecies); + if (pPop != 0) { + popSizes[i] = pPop->sampleSize(); + } // else popSizes[i] remain default init value 0, safe + popWeights[i] = popSizes[i] - (popSizes[i] * popSizes[i] / totSize); // n_ic in Weir & Hill 2002 + sumWeights += popWeights[i]; + if (popSizes[i] > 0) nbPops++; + + // Fill the pairwise Fst matrix with default value 0 + for (int j = 0; j < nPatches; j++) + numeratorPairwiseFst[i][j] = 0; + } + + nbExtantPops = nbPops; + + if (nbPops > 1) { + // Calculate Fst numerators and denominators + double p, pq, pBar, sqDist, num; + for (int i = 0; i < nPatches; ++i) { + if (popSizes[i] == 0) continue; + const auto patch = pLandscape->findPatch(patchVect[i]); + const auto pPop = patch->getPopn(pSpecies); + + for (int l = 0; l < nLoci; ++l) { + for (int u = 0; u < nAlleles; ++u) { + p = pPop->getAlleleFrequency(l, u); //p_liu + pq = p * (1 - p); + pBar = commNeutralCountTables[l].getFrequency(u); + sqDist = p - pBar; //(p_liu - pbar_u)^2 + sqDist *= sqDist; + + num = pq * popSizes[i] ; // eq. 8 Weir & Hill 2002 + num /= popSizes[i] == 1 ? 1 : popSizes[i] - 1; // avoid division by zero + numeratorPairwiseFst[i][i] += num; + numeratorWeightedFst += num * popSizes[i]; // see equ. 9, Weir & Hill 2002 + denominator += popSizes[i] * sqDist + popWeights[i] * pq; //common denominator + + } // end for allele + } // end for locus + } // end for pop + + // Diagonals + double pairwiseFst; + for (int i = 0; i < nPatches; ++i) { + if (popSizes[i] == 0) continue; + else if (denominator != 0) + { + pairwiseFst = 1 - (numeratorPairwiseFst[i][i] * sumWeights / denominator); + pairwiseFstMatrix.set(i, i, pairwiseFst); + } + // else remain 0 + } + + // Add allele frequencies to numerators + double pi, pj; + for (int l = 0; l < nLoci; ++l) + for (int u = 0; u < nAlleles; ++u) + for (int i = 0; i < nPatches - 1; ++i) { // nPatches-1 bc bottom row not filled + if (popSizes[i] == 0) continue; + const auto patch = pLandscape->findPatch(patchVect[i]); + const auto pPopI = patch->getPopn(pSpecies); + + for (int j = i + 1; j < nPatches; ++j) { // fill only upper half of matrix + if (popSizes[j] == 0) continue; + const auto patch = pLandscape->findPatch(patchVect[j]); + const auto pPopJ = patch->getPopn(pSpecies); + + pi = pPopI->getAlleleFrequency(l, u); + pj = pPopJ->getAlleleFrequency(l, u); + numeratorPairwiseFst[i][j] += pi * (1 - pj) + pj * (1 - pi); // equ. 7 of Weir & Hill 2002 + } + } + + // Final estimates of pairwise Fst (beta_ii' in eq. 7 in WC 2002) + for (int i = 0; i < nPatches - 1; ++i) { + if (popSizes[i] == 0) continue; // Fst for this pair remains NULL + for (int j = i + 1; j < nPatches; ++j) { + if (popSizes[j] == 0) continue; + else if (denominator != 0) { + pairwiseFst = 1 - (numeratorPairwiseFst[i][j] * sumWeights) / (2 * denominator); + pairwiseFstMatrix.set(i, j, pairwiseFst); + } + // else remain 0 + } + } + + // Estimator of global Fst weighted by sample sizes (beta_W in eq. 9 in WH 2002) + if (denominator != 0) { + weightedFst = 1 - (numeratorWeightedFst * sumWeights) / (denominator * totSize); // beta_w in Eq. 9 in WH 2002 + } + else { + weightedFst = 0.0; + } + } + else { // zero or one pop, cannot calculate Fst + // pairwiseFstMatrix keeps default values (0) + weightedFst = 0.0; + } +} + diff --git a/NeutralStatsManager.h b/NeutralStatsManager.h new file mode 100644 index 0000000..9df4397 --- /dev/null +++ b/NeutralStatsManager.h @@ -0,0 +1,167 @@ +#ifndef NEUTRALSTATSH +#define NEUTRALSTATSH + +#include "Species.h" +#include "Landscape.h" + +using namespace std; + +// Patch * patch matrix to store pairwise Fst calculations +/** Creates an array of doubles of size = rows*cols, taken from NEMO **/ +struct PatchMatrix +{ +public: + PatchMatrix(unsigned int nRows = 0, unsigned int nCols = 0) : + rows{ nRows }, + cols{ nCols }, + nbCells{nCols * nRows} { + value.resize(nbCells); + }; + + // Get value at specified position + double get(unsigned int i, unsigned int j) { + if (i >= cols || j >= rows) + throw runtime_error("PatchMatrix::get() out of range!\n"); + else return value[i * cols + j]; + } + + int getNbCells() { return nbCells; }; + + /** Sets element at row i and column j to value val **/ + void set(unsigned int i, unsigned int j, double val) { + if (i >= cols || j >= rows) + throw runtime_error("PatchMatrix::set() out of range!\n"); + else value[i * cols + j] = val; + } + + /** Assigns a value to all elements of the matrix. */ + void setAll(double val) + { + for (unsigned int i = 0; i < nbCells; ++i) value[i] = val; + } + +private: + unsigned int rows, cols, nbCells; + vector value; +}; + +// Counts of NEUTRAL allele occurrences in populations +// for neutral statistics calculations +struct NeutralCountsTable { + +public: + NeutralCountsTable(int maxNbNeutralAlleles) : alleleTallies(maxNbNeutralAlleles), alleleFrequencies(maxNbNeutralAlleles), alleleHeterozygoteTallies(maxNbNeutralAlleles) {}; + + void reset() { + fill(alleleTallies.begin(), alleleTallies.end(), 0); fill(alleleFrequencies.begin(), alleleFrequencies.end(), 0); + fill(alleleHeterozygoteTallies.begin(), alleleHeterozygoteTallies.end(), 0); + } + + // Getters + int getTally(int whichAllele) { return alleleTallies[whichAllele]; }; + double getFrequency(int whichAllele) { return alleleFrequencies[whichAllele]; }; + int getHeteroTally(int whichAllele) { + return alleleHeterozygoteTallies[whichAllele]; + }; + + // Setters / increments + void incrementTally(int whichAllele) { alleleTallies[whichAllele]++; }; + void incrementTallyBy(int count, int whichAllele) { this->alleleTallies[whichAllele] += count; } + void incrementHeteroTally(int whichAllele) { this->alleleHeterozygoteTallies[whichAllele]++; } + void setFrequencies(int sampleSize) { + for (int i = 0; i < alleleFrequencies.size(); i++) { + alleleFrequencies[i] = sampleSize > 0 ? static_cast(alleleTallies[i]) / sampleSize : 0.0; + } + }; + +private: + // Tallies, one for each possible allele (so absolute max size is 255) + vector alleleTallies; // number of occurrences of each allele in pop + vector alleleFrequencies; // frequency of each allele in pop + vector alleleHeterozygoteTallies; // nb of times each allele is found in a heterozygous pair +}; + + +// Handles calculations of neutral statistics +class NeutralStatsManager { + +public: + + NeutralStatsManager(const int& nbSampledPatches, const int nLoci); + + // Count alleles and their frequencies in all pops and community + void updateAllNeutralTables(Species* pSpecies, Landscape* pLandscape, set const& patchList); + void resetCommNeutralTables(); + + void calcAllelicDiversityMetrics(set const& patchList, const int nInds, Species* pSpecies, Landscape* pLandscape); + + // Heterozygosity calculations + void calculateHo(set const& patchList, const int totalNbSampledInds, const int nbrLoci, Species* pSpecies, Landscape* pLandscape); + void calculateHs(set const& patchList, const int nbrLoci, Species* pSpecies, Landscape* pLandscape); + void calculateHt(Species* pSpecies, Landscape* pLandscape, const int nLoci, const int nAlleles); + void calculatePerLocusHo(set const& patchList, const int totalNbSampledInds, const int nbrLoci, Species* pSpecies, Landscape* pLandscape); + + // F-stats calculations + void calculateFstatWC(set const& patchList, const int nInds, const int nLoci, const int nAlleles, Species* pSpecies, Landscape* pLandscape); + void calcPairwiseWeightedFst(set const& patchList, const int nInds, const int nLoci, Species* pSpecies, Landscape* pLandscape); + + // Getters + int getNbPopulatedSampledPatches() const { return nbExtantPops; } + int getTotalNbSampledInds() const { return totalNbSampledInds; } + + double getMeanNbAllPerLocusPerPatch() const { return meanNbAllelesPerLocusPerPatch; } + double getMeanNbAllPerLocus() const { return meanNbAllelesPerLocus; } + double getMeanFixdAllelesPerPatch() const { return meanNbFixedLociPerPatch; } + double getTotalFixdAlleles() const { return meanFixedLoci; } + + double getHo() const { return ho; } + double getHs() const { return hs; } + double getHt() const { return ht; } + + double getPerLocusHo(int i) const { return perLocusHo[i]; } + + double getFstWC() const { return fst; } + double getFisWC() const { return fis; } + double getFitWC() const { return fit; } + + double getWeightedFst() { return weightedFst; } + + double getPerLocusFst(int i) const { return perLocusFst[i]; } + double getPerLocusFis(int i) const { return perLocusFis[i]; } + double getPerLocusFit(int i) const { return perLocusFit[i]; } + + double getPairwiseFst(int i, int j) { return pairwiseFstMatrix.get(i, j); } + +private: + + int nbExtantPops, totalNbSampledInds; + vector commNeutralCountTables; // community-level tallies of allele counts and freqs + + double meanNbAllelesPerLocusPerPatch, meanNbAllelesPerLocus; + double meanNbFixedLociPerPatch, meanFixedLoci; + + double ho; // observed heterozygosity + double hs; // expected population-level heterozygosity + double ht; // expected community-level heterozygosity + + vector perLocusHo; // Per-locus observed heterozygosity + + // F-statistics + // Weir & Cockerham (1984) F-stat estimates. + double fst, fis, fit; + + // Weir & Hill (2002) F-stat estimates + double weightedFst; + + // Per-locus F-stats (Weir & Cockerham). + vector perLocusFst, perLocusFis, perLocusFit; + + // Pairwise Fst matrix + PatchMatrix pairwiseFstMatrix; +}; + +#endif + + + + diff --git a/NeutralTrait.cpp b/NeutralTrait.cpp new file mode 100644 index 0000000..c9774e1 --- /dev/null +++ b/NeutralTrait.cpp @@ -0,0 +1,319 @@ +#include "NeutralTrait.h" + +// ---------------------------------------------------------------------------------------- +// Initialisation constructor +// Called when initialising community +// Sets up initial values, and immutable attributes (distributions and parameters) +// that are defined at the species-level +// ---------------------------------------------------------------------------------------- +NeutralTrait::NeutralTrait(SpeciesTrait* P) +{ + pSpeciesTrait = P; + + DistributionType mutationDistribution = pSpeciesTrait->getMutationDistribution(); + map mutationParameters = pSpeciesTrait->getMutationParameters(); + + // Set default value to user-specified max + wildType = (int)mutationParameters.find(MAX)->second; + if (wildType > NeutralValUpperBound) + throw logic_error("max number of alleles cannot exceed " + to_string(NeutralValUpperBound) + ".\n"); + + _inherit_func_ptr = (pSpeciesTrait->getPloidy() == 1) ? &NeutralTrait::inheritHaploid : &NeutralTrait::inheritDiploid; //this could be changed if we wanted some alternative form of inheritance + + if (mutationDistribution == SSM) + _mutate_func_ptr = &NeutralTrait::mutate_SSM; + + if (mutationDistribution == KAM) + _mutate_func_ptr = &NeutralTrait::mutate_KAM; + + if (mutationDistribution != SSM && mutationDistribution != KAM) + throw logic_error("wrong mutation distribution for neutral markers, must be KAM or SSM \n"); + + if (mutationParameters.count(MAX) != 1) + throw logic_error("KAM or SSM mutation distribution parameter must contain max value (e.g. max= ), max cannot exceed 256 \n"); + + DistributionType initialDistribution = pSpeciesTrait->getInitialDistribution(); + map initialParameters = pSpeciesTrait->getInitialParameters(); + + if (mutationDistribution == SSM && initialDistribution != UNIFORM) + throw logic_error("If using SSM mutation model for neutral trait, must use uniform initial distribution.\n"); + + switch (initialDistribution) { + case UNIFORM: + { + if (initialParameters.count(MAX) != 1) + throw logic_error("initial distribution parameter must contain one max value if set to UNIFORM (e.g. max= ), max cannot exceed " + to_string(NeutralValUpperBound) + "\n"); + + float maxNeutralVal = initialParameters.find(MAX)->second; + if (maxNeutralVal > NeutralValUpperBound) { + throw logic_error("initial distribution parameter max cannot exceed " + to_string(NeutralValUpperBound) + ", resetting to " + to_string(NeutralValUpperBound) + "\n"); + maxNeutralVal = NeutralValUpperBound; //reserve 255 for wildtype + } + initialiseUniform(maxNeutralVal); + break; + } + default: + { + throw logic_error("wrong parameter value for parameter \"initialisation of neutral trait\", must be left uniform \n"); + break; //should return false + } + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance constructor +// Copies immutable features from a parent trait +// Only called via clone() +// ---------------------------------------------------------------------------------------- +NeutralTrait::NeutralTrait(const NeutralTrait& T) : + pSpeciesTrait(T.pSpeciesTrait), _mutate_func_ptr(T._mutate_func_ptr), _inherit_func_ptr(T._inherit_func_ptr) { +} + +// ---------------------------------------------------------------------------------------- +// mutate options +// ---------------------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------------------- +// Draw and apply mutations according to a KAM process +// +// Mutations drawn only for existing positions, +// that is no new genes are created during simulation +// KAM = randomly drawn value in 0-MAX, differs from previous value +// ---------------------------------------------------------------------------------------- +void NeutralTrait::mutate_KAM() +{ + const int positionsSize = pSpeciesTrait->getPositionsSize(); + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const short ploidy = pSpeciesTrait->getPloidy(); + const float mutationRate = pSpeciesTrait->getMutationRate(); + auto rng = pRandom->getRNG(); + unsigned char mut; + + map mutationParameters = pSpeciesTrait->getMutationParameters(); + + int maxNeutralVal = (int)mutationParameters.find(MAX)->second; + if (maxNeutralVal > NeutralValUpperBound) maxNeutralVal = NeutralValUpperBound; //reserve max value for wildtype + + for (int whichChromosome = 0; whichChromosome < ploidy; whichChromosome++) { + + unsigned int NbMut = pRandom->Binomial(positionsSize, mutationRate); + if (NbMut > 0) { + vector mutationPositions; + sample(genePositions.begin(), genePositions.end(), std::back_inserter(mutationPositions), + NbMut, rng); // without replacement + + for (int m : mutationPositions) { + mut = (unsigned char)pRandom->IRandom(0, maxNeutralVal); // draw new mutation, could draw wildtype + + auto it = genes.find(m); + if (it == genes.end()) + throw runtime_error("Locus selected for mutation doesn't exist."); + auto currentChar = it->second[whichChromosome]; // current allele + if (maxNeutralVal > 0) { // dodge the infinite loop + do { + mut = (unsigned char)pRandom->IRandom(0, maxNeutralVal); + } while (mut == currentChar); // new allele value is different + } + else mut = 0; + it->second[whichChromosome] = mut; //overwrite with new value + } + } + } +} + + +// ---------------------------------------------------------------------------------------- +// Draw and apply single-step mutations (SSM) +// +// Mutations drawn only for existing positions, +// that is no new genes are created during simulation +// Increment previous value by 1 or -1, +// unless already 0 (then always +1) or MAX (then always -1) +// ---------------------------------------------------------------------------------------- +void NeutralTrait::mutate_SSM() +{ + const int positionsSize = pSpeciesTrait->getPositionsSize(); + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const short ploidy = pSpeciesTrait->getPloidy(); + const float mutationRate = pSpeciesTrait->getMutationRate(); + auto rng = pRandom->getRNG(); + + map mutationParameters = pSpeciesTrait->getMutationParameters(); + + int maxNeutralVal = (int)mutationParameters.find(MAX)->second; + if (maxNeutralVal > NeutralValUpperBound) maxNeutralVal = NeutralValUpperBound; //reserved max value for wildtype + + for (int whichChromosome = 0; whichChromosome < ploidy; whichChromosome++) { + + unsigned int NbMut = pRandom->Binomial(positionsSize, mutationRate); + if (NbMut > 0) { + vector mutationPositions; + sample(genePositions.begin(), genePositions.end(), std::back_inserter(mutationPositions), + NbMut, rng); + + for (int m : mutationPositions) { + int mutateUp = pRandom->Bernoulli(0.5); + auto it = genes.find(m); + if (it == genes.end()) + throw runtime_error("Locus selected for mutation doesn't exist."); + auto currentAllele = it->second[whichChromosome]; + if (mutateUp == 1 && currentAllele < maxNeutralVal) + it->second[whichChromosome] += 1; // one step up + else if (currentAllele > 0) // step down or already max + it->second[whichChromosome] -= 1; // one step down + else // current allele is 0, step up + it->second[whichChromosome] += 1; + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Wrapper to inheritance function +// ---------------------------------------------------------------------------------------- +void NeutralTrait::inheritGenes(const bool& fromMother, QuantitativeTrait* parent, set const& recomPositions, int startingChromosome) +{ + auto parentCast = dynamic_cast (parent); // must convert QuantitativeTrait to NeutralTrait + const auto& parent_seq = parentCast->getGenes(); + (this->*_inherit_func_ptr) (fromMother, parent_seq, recomPositions, startingChromosome); +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for diploid, sexual species +// Called once for each parent. Given a list of recombinant sites, +// populates offspring genes with appropriate parent alleles +// Assumes mother genes are inherited first +// ---------------------------------------------------------------------------------------- +void NeutralTrait::inheritDiploid(const bool& fromMother, map> const& parentGenes, set const& recomPositions, int parentChromosome) { + + const int lastPosition = parentGenes.rbegin()->first; + auto recomIt = recomPositions.lower_bound(parentGenes.begin()->first); + // If no recombination sites, only breakpoint is last position + // i.e., no recombination occurs + int nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + + // Is the first parent gene position already recombinant? + auto distance = std::distance(recomPositions.begin(), recomIt); + if (distance % 2 != 0) + parentChromosome = 1 - parentChromosome; //switch chromosome + + for (auto const& [locus, allelePair] : parentGenes) { + + // Switch chromosome if locus is past recombination site + while (locus > nextBreakpoint) { + parentChromosome = 1 - parentChromosome; + std::advance(recomIt, 1); // go to next recombination site + nextBreakpoint = recomIt == recomPositions.end() ? lastPosition : *recomIt; + } + + if (locus <= nextBreakpoint) { + unsigned char parentAllele = allelePair[parentChromosome]; + auto it = genes.find(locus); + if (it == genes.end()) { + // locus does not exist yet, create and initialise it + if (!fromMother) throw runtime_error("Father-inherited locus does not exist."); + vector newAllelePair(2, wildType); + newAllelePair[sex_t::FEM] = parentAllele; + genes.insert(make_pair(locus, newAllelePair)); + } + else { // father, locus already exists + if (fromMother) throw runtime_error("Mother-inherited locus already exists."); + it->second[sex_t::MAL] = parentAllele; + } + } + } +} + +// ---------------------------------------------------------------------------------------- +// Inheritance for haploid, asexual species +// Simply pass down parent genes +// Arguments are still needed to match overloaded function in base class +// ---------------------------------------------------------------------------------------- +void NeutralTrait::inheritHaploid(const bool& fromMother, map> const& parentGenes, set const& recomPositions, int parentChromosome) +{ + genes = parentGenes; +} + +// ---------------------------------------------------------------------------------------- +// Initialise neutral loci +// ---------------------------------------------------------------------------------------- +void NeutralTrait::initialiseUniform(int maxAlleleVal) +{ + const auto& genePositions = pSpeciesTrait->getGenePositions(); + const auto& initPositions = pSpeciesTrait->getInitPositions(); + short ploidy = pSpeciesTrait->getPloidy(); + + for (auto position : genePositions) { + vector allelePair; + + for (int i = 0; i < ploidy; i++) { + unsigned char alleleVal = char(0); + if (initPositions.contains(position)) { + // allele values span 0 - max inclusive, max is wildtype + alleleVal = (unsigned char)pRandom->IRandom(0, maxAlleleVal); + } + allelePair.emplace_back(alleleVal); + } + genes.insert(make_pair(position, allelePair)); + } +} + + +// ---------------------------------------------------------------------------------------- +// Check if particular loci is heterozygote +// ---------------------------------------------------------------------------------------- +bool NeutralTrait::isHeterozygoteAtLocus(int locus) const { + // assumes diploidy + auto it = genes.find(locus); + if (it == genes.end()) + throw runtime_error("Neutral gene queried for heterozygosity does not exist."); + else + return(it->second[0] != it->second[1]); +} + +// ---------------------------------------------------------------------------------------- +// Count heterozygote loci in genome +// ---------------------------------------------------------------------------------------- +int NeutralTrait::countHeterozygoteLoci() const { + // assumes diploidy + int count = 0; + for (auto const& [locus, allelePair] : genes) { + count += (allelePair[0] != allelePair[1]); + } + return count; +} + +// ---------------------------------------------------------------------------------------- +// Get allele value at loci +// ---------------------------------------------------------------------------------------- +float NeutralTrait::getAlleleValueAtLocus(short whichChromosome, int position) const { + + auto it = genes.find(position); + if (it == genes.end()) //no mutations there + throw runtime_error("The neutral locus queried for its allele value does not exist."); + return it->second[whichChromosome]; +} + +#ifdef UNIT_TESTS // Testing only + +// Create a default set of neutral alleles for testing +// +// Shorthand function to manually set genotypes for neutral +// traits, instead of having to manipulate mutations. +map> createTestNeutralGenotype( + const int genomeSz, const bool isDiploid, + const unsigned char valAlleleA, + const unsigned char valAlleleB +) { + vector gene(isDiploid ? 2 : 1); + gene[0] = valAlleleA; + if (isDiploid) gene[1] = valAlleleB; + + map> genotype; + for (int i = 0; i < genomeSz; i++) { + genotype.emplace(i, gene); + } + return genotype; +} + +#endif // UNIT_TESTS diff --git a/NeutralTrait.h b/NeutralTrait.h new file mode 100644 index 0000000..86976da --- /dev/null +++ b/NeutralTrait.h @@ -0,0 +1,94 @@ +#ifndef NeutralTRAITH +#define NeutralTRAITH + +#include +#include +#include +#include + +#include "QuantitativeTrait.h" + +using namespace std; + +// Neutral traits +// +// Not expressed and are only used to compute neutral statistics +// e.g. the Fst. +// To save on mem usage, allele values are represented by character types, +// taking a value between 0 and a user-specified max >= 255 +class NeutralTrait : public QuantitativeTrait { + +public: + + // Initialisation constructor, set initial values and immutable features + NeutralTrait(SpeciesTrait* P); + + // Inheritance constructor, copies pointers to immutable features when cloning from parent + NeutralTrait(const NeutralTrait& T); + + // Make a shallow copy to pass to offspring trait + // Return new pointer to new trait created by inheritance c'tor + // This avoids copying shared attributes: distributions and parameters + virtual unique_ptr clone() const override { return std::make_unique(*this); } + + virtual ~NeutralTrait() { } + + // Getters + virtual int getNLoci() const override { return pSpeciesTrait->getPositionsSize(); } + float getMutationRate() const override { return pSpeciesTrait->getMutationRate(); } + bool isInherited() const override { return pSpeciesTrait->isInherited(); } + map>& getGenes() { return genes; } //returning reference, reciever must be const + + virtual void mutate() override { (this->*_mutate_func_ptr) (); } + virtual void inheritGenes(const bool& fromMother, QuantitativeTrait* parent, set const& recomPositions, int startingChromosome) override; + virtual float express() { + throw runtime_error("Neutral trait shouldn't be expressed."); + return -9999; + } + + virtual float getAlleleValueAtLocus(short chromosome, int position) const override; + virtual float getDomCoefAtLocus(short chromosome, int position) const override { + return 0.0; + } + + int countHeterozygoteLoci() const; + bool isHeterozygoteAtLocus(int locus) const; + +private: + + inline static int wildType; // default allele value, value set at construction + const int NeutralValUpperBound = UCHAR_MAX; // alleles are char, can take value 0-255 + + // > + map> genes; + + // Initialisation + void initialiseUniform(int max); + + // Immutable features, set at initialisation + // and passed down to every subsequent trait copy + //// Species-level trait attributes, invariant across individuals + SpeciesTrait* pSpeciesTrait; + //// Species-level trait functions + void (NeutralTrait::* _mutate_func_ptr) (void); + void (NeutralTrait::* _inherit_func_ptr) (const bool& fromMother, map> const& parent, set const& recomPositions, int parentChromosome); + + // Possible values for immutable functions + //// Inheritance + void inheritDiploid(const bool& fromMother, map> const&, set const& recomPositions, int parentChromosome); + void inheritHaploid(const bool& fromMother, map> const& parentMutations, set const& recomPositions, int parentChromosome); + //// Mutation + void mutate_KAM(); + void mutate_SSM(); // single-step mutations + +}; + +#ifdef UNIT_TESTS // for testing purposes only +map> createTestNeutralGenotype( + const int genomeSz, const bool isDiploid, + const unsigned char valAlleleA, + const unsigned char valAlleleB = char(0) // if haploid +); +#endif + +#endif // NeutralTraitH diff --git a/Parameters.cpp b/Parameters.cpp index 375922d..9e959a5 100644 --- a/Parameters.cpp +++ b/Parameters.cpp @@ -128,7 +128,7 @@ paramInit::paramInit(void) { minSeedX = 0; maxSeedX = 99999999; minSeedY = 0; maxSeedY = 99999999; nSeedPatches = 1; nSpDistPatches = 1; indsFile = "NULL"; - for (int i = 0; i < NSTAGES; i++) { + for (int i = 0; i < gMaxNbStages; i++) { initProp[i] = 0.0; } } @@ -176,12 +176,12 @@ initParams paramInit::getInit(void) { } void paramInit::setProp(short stg, float p) { - if (stg >= 0 && stg < NSTAGES && p >= 0.0 && p <= 1.0) initProp[stg] = p; + if (stg >= 0 && stg < gMaxNbStages && 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]; + if (stg >= 0 && stg < gMaxNbStages) p = initProp[stg]; return p; } @@ -210,29 +210,26 @@ int paramInit::numInitInds(void) { return (int)initinds.size(); } // Simulation parameters -paramSim::paramSim(void) { +paramSim::paramSim(const string& pathToProjDir) : + dir{pathToProjDir} +{ simulation = 0; reps = years = 1; outIntRange = 1; - outStartPop = outStartInd = outStartGenetic = 0; + outStartPop = outStartInd = 0; outStartTraitCell = outStartTraitRow = outStartConn = 0; - outIntOcc = outIntPop = outIntInd = outIntGenetic = 10; + outIntOcc = outIntPop = outIntInd = outputGeneticInterval = 10; outIntTraitCell = outIntTraitRow = outIntConn = 10; - mapInt = traitInt = 10; - slowFactor = 1; + traitInt = 10; batchMode = absorbing = false; outRange = outOccup = outPop = outInds = false; - outGenetics = outGenXtab = false; outGenType = 0; outTraitsCells = outTraitsRows = outConnect = false; - saveMaps = false; saveTraitMaps = false; + outputGenes = outputWeirCockerham = outputWeirHill = false; saveVisits = false; #if RS_RCPP outStartPaths = 0; outIntPaths = 0; outPaths = false; ReturnPopRaster = false; CreatePopFile = true; #endif - viewLand = false; viewPatch = false; viewGrad = false; viewCosts = false; - viewPop = false; viewTraits = false; viewPaths = false; viewGraph = false; - dir = ' '; } paramSim::~paramSim(void) { } @@ -242,21 +239,14 @@ void paramSim::setSim(simParams s) { 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; @@ -264,11 +254,9 @@ void paramSim::setSim(simParams s) { 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; @@ -277,63 +265,56 @@ void paramSim::setSim(simParams s) { ReturnPopRaster = s.ReturnPopRaster; CreatePopFile = s.CreatePopFile; #endif + fixReplicateSeed = s.fixReplicateSeed; +} + +void paramSim::setGeneticSim(string patchSamplingOption, bool outputGeneticValues, bool outputWeirCockerham, bool outputWeirHill, int outputStartGenetics, int outputGeneticInterval) { + this->patchSamplingOption = patchSamplingOption; + this->outputGenes = outputGeneticValues; + this->outputWeirCockerham = outputWeirCockerham; + this->outputWeirHill = outputWeirHill; + this->outputStartGenetics = outputStartGenetics; + this->outputGeneticInterval = outputGeneticInterval; } -simParams paramSim::getSim(void) { +simParams paramSim::getSim() { 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.outStartPop = outStartPop; s.outStartInd = outStartInd; 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.outIntInd = outIntInd; 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; + s.traitInt = traitInt; #if RS_RCPP + s.saveVisits = saveVisits; s.outStartPaths = outStartPaths; s.outIntPaths = outIntPaths; s.outPaths = outPaths; s.ReturnPopRaster = ReturnPopRaster; s.CreatePopFile = CreatePopFile; #endif + s.patchSamplingOption = patchSamplingOption; + s.outputGeneValues = outputGenes; + s.outputWeirCockerham = outputWeirCockerham; + s.outputWeirHill = outputWeirHill; + s.outStartGenetics = outputStartGenetics; + s.outputGeneticInterval = outputGeneticInterval; + 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; @@ -368,12 +349,102 @@ string paramSim::getDir(int option) { return s; } +string to_string(const TraitType& tr) { + switch (tr) + { + case NEUTRAL: return "NEUTRAL"; + case GENETIC_LOAD: return "GENETIC_LOAD"; + case GENETIC_LOAD1: return "GENETIC_LOAD1"; + case GENETIC_LOAD2: return "GENETIC_LOAD2"; + case GENETIC_LOAD3: return "GENETIC_LOAD3"; + case GENETIC_LOAD4: return "GENETIC_LOAD4"; + case GENETIC_LOAD5: return "GENETIC_LOAD5"; + + case E_D0: return "E_D0"; + case E_D0_M: return "E_D0_M"; + case E_D0_F: return "E_D0_F"; + case E_ALPHA: return "E_ALPHA"; + case E_ALPHA_M: return "E_ALPHA_M"; + case E_ALPHA_F: return "E_ALPHA_F"; + case E_BETA: return "E_BETA"; + case E_BETA_M: return "E_BETA_M"; + case E_BETA_F: return "E_BETA_F"; + + case S_S0: return "S_S0"; + case S_S0_M: return "S_S0_M"; + case S_S0_F: return "S_S0_F"; + case S_ALPHA: return "S_ALPHA"; + case S_ALPHA_M: return "S_ALPHA_M"; + case S_ALPHA_F: return "S_ALPHA_F"; + case S_BETA: return "S_BETA"; + case S_BETA_M: return "S_BETA_M"; + case S_BETA_F: return "S_BETA_F"; + + case CRW_STEPLENGTH: return "CRW_STEPLENGTH"; + case CRW_STEPCORRELATION: return "CRW_STEPCORRELATION"; + case KERNEL_MEANDIST_1: return "KERNEL_MEANDIST_1"; + case KERNEL_MEANDIST_2: return "KERNEL_MEANDIST_2"; + case KERNEL_MEANDIST_1_F: return "KERNEL_MEANDIST_1_F"; + case KERNEL_MEANDIST_2_F: return "KERNEL_MEANDIST_2_F"; + case KERNEL_MEANDIST_1_M: return "KERNEL_MEANDIST_1_M"; + case KERNEL_MEANDIST_2_M: return "KERNEL_MEANDIST_2_M"; + case KERNEL_PROBABILITY: return "KERNEL_PROBABILITY"; + case KERNEL_PROBABILITY_F: return "KERNEL_PROBABILITY_F"; + case KERNEL_PROBABILITY_M: return "KERNEL_PROBABILITY_M"; + + case SMS_DP: return "SMS_DP"; + case SMS_GB: return "SMS_GB"; + case SMS_ALPHADB: return "SMS_ALPHADB"; + case SMS_BETADB: return "SMS_BETADB"; + case INVALID_TRAIT: return "INVALID_TRAIT"; + default: return ""; + } +} + +string to_string(const GenParamType& param) { + switch (param) + { + case MEAN: return "MEAN"; + case SD: return "SD"; + case MIN: return "MIN"; + case MAX: return "MAX"; + case SHAPE: return "SHAPE"; + case SCALE: return "SCALE"; + case INVALID: return "INVALID"; + default: return ""; + } +} + +string to_string(const DistributionType& dist) { + switch (dist) + { + case UNIFORM: return "UNIFORM"; + case NORMAL: return "NORMAL"; + case GAMMA: return "GAMMA"; + case NEGEXP: return "NEGEXP"; + case SCALED: return "SCALED"; + case KAM: return "KAM"; + case SSM: return "SSM"; + case NONE: return "NONE"; + default: return ""; + } +} + +string to_string(const ExpressionType& expr) { + switch (expr) + { + case AVERAGE: return "AVERAGE"; + case ADDITIVE: return "ADDITIVE"; + case NOTEXPR: return "NOTEXPR"; + case MULTIPLICATIVE: return "MULTIPLICATIVE"; + default: return ""; + } +} + #if RS_RCPP bool paramSim::getReturnPopRaster(void) { return ReturnPopRaster; } bool paramSim::getCreatePopFile(void) { return CreatePopFile; } #endif //--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- diff --git a/Parameters.h b/Parameters.h index 29d6096..f3f78b3 100644 --- a/Parameters.h +++ b/Parameters.h @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 @@ -34,9 +34,9 @@ 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. + 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. + 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 @@ -48,37 +48,30 @@ 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 +#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 +constexpr int gNoDataCost = 100000; // cost to use in place of nodata value for SMS; +constexpr int gAbsorbingNoDataCost = 100; // cost to use in place of nodata value for SMS; +// when boundaries are absorbing +constexpr int gMaxNbStages = 10; // maximum number of stages permitted +constexpr int gMaxNbSexes = 2; // maximum number of sexes permitted +constexpr int gMaxNbLayers = 3*gMaxNbSexes*gMaxNbStages; // maximum number of demographic scaling layers permitted #if RS_RCPP typedef intptr_t intptr; #else -#if RSWIN64 typedef unsigned long long intptr; -#else -typedef unsigned int intptr; -#endif -#endif +#endif // RS_RCPP #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 @@ -97,6 +90,56 @@ const double SQRT2 = std::sqrt(double(2.0)); // more efficient than calculating // Common declarations struct locn { int x; int y; }; +//-------------------------------------------------------------------------- + +/** Trait types **/ + +enum TraitType { + NEUTRAL, + GENETIC_LOAD, GENETIC_LOAD1, GENETIC_LOAD2, GENETIC_LOAD3, GENETIC_LOAD4, GENETIC_LOAD5, + + E_D0, E_ALPHA, E_BETA, + S_S0, S_ALPHA, S_BETA, + + E_D0_F, E_ALPHA_F, E_BETA_F, + S_S0_F, S_ALPHA_F, S_BETA_F, + + E_D0_M, E_ALPHA_M, E_BETA_M, + S_S0_M, S_ALPHA_M, S_BETA_M, + + CRW_STEPLENGTH, CRW_STEPCORRELATION, + + KERNEL_MEANDIST_1, KERNEL_MEANDIST_2, KERNEL_PROBABILITY, + KERNEL_MEANDIST_1_F, KERNEL_MEANDIST_2_F, KERNEL_PROBABILITY_F, + KERNEL_MEANDIST_1_M, KERNEL_MEANDIST_2_M, KERNEL_PROBABILITY_M, + + SMS_DP, SMS_GB, SMS_ALPHADB, SMS_BETADB, + + INVALID_TRAIT // error +}; + +enum GenParamType { MEAN, SD, MIN, MAX, SHAPE, SCALE, INVALID }; +enum DistributionType { UNIFORM, NORMAL, GAMMA, NEGEXP, SCALED, KAM, SSM, NONE }; +enum ExpressionType { AVERAGE, ADDITIVE, NOTEXPR, MULTIPLICATIVE }; + +string to_string(const TraitType& tr); + +string to_string(const GenParamType& param); + +string to_string(const DistributionType& dist); + +string to_string(const ExpressionType& expr); + +/** Param's types **/ +typedef enum { KERNEL, SMS, CRW} movement_t; + +//sex types +typedef enum { + FEM = 0, MAL = 1, + NA, // not applicable. e.g. for NEUTRAL or genetic load trait + INVALID_SEX // error +} sex_t; + //--------------------------------------------------------------------------- // Environmental gradient parameters @@ -153,7 +196,7 @@ class paramGrad { struct envStochParams { bool stoch; bool local; bool inK; bool localExt; - float ac; float std; + float ac; float std; float locExtProb; }; @@ -171,7 +214,7 @@ class paramStoch { 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 ac; // temporal autocorrelation coefficient float std; // amplitude of fluctuations: sampled from N(0,std) float locExtProb; // local extinction probability }; @@ -191,7 +234,8 @@ struct initParams { }; struct initInd { - int year,patchID,x,y; short species,sex,age,stage; + int year, patchID, x, y; + short species, sex, age, stage; }; class paramInit { @@ -246,7 +290,7 @@ class paramInit { 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) + float initProp[gMaxNbStages]; // initial stage proportions (structured population only) vector initinds; // individuals to be initialised @@ -258,23 +302,41 @@ class paramInit { struct simParams { int batchNum; - int simulation; int reps; int years; - 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; + int simulation; + int reps; + int years; + int outStartPop; + int outStartInd; + int outIntInd; + int outStartTraitCell; + int outStartTraitRow; + int outStartConn; + int outIntRange; + int outIntOcc; + int outIntPop; bool saveVisits; + int outIntTraitCell; + int outIntTraitRow; + int outIntConn; + int traitInt; + bool batchMode; + bool absorbing; + bool outRange; + bool outOccup; + bool outPop; + bool outInds; + bool outTraitsCells; + bool outTraitsRows; + bool outConnect; #if RS_RCPP int outStartPaths; int outIntPaths; bool outPaths; bool ReturnPopRaster; bool CreatePopFile; #endif + bool fixReplicateSeed; + string patchSamplingOption; + bool outputGeneValues; + bool outputWeirCockerham, outputWeirHill; + int outputGeneticInterval, outStartGenetics; }; struct simView { @@ -286,57 +348,49 @@ struct simView { class paramSim { public: - paramSim(void); + paramSim(const string& pathToProjDir = ""); ~paramSim(void); void setSim(simParams); + void setGeneticSim(string patchSamplingOption, bool outputGeneticValues, bool outputWeirCockerham, bool outputWeirHill, int outputStartGenetics, int outputGeneticInterval); simParams getSim(void); int getSimNum(void); - void setViews(simView); - simView getViews(void); - void setDir(string); string getDir(int); + void setBatchNum(const int& batchNb) { + batchNum = batchNb; + batchMode = true; + } #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 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 batchNum; // batch number + int simulation; // simulation no. + int reps; // no. of replicates + int years; // no. of years + int outStartPop; // output start year for population file + int outStartInd; // output start year for individuals 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 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 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? + int outIntConn; // output interval for connectivity matrix + int traitInt; // output interval for evolving traits maps + 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 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 outConnect; // produce output connectivity file? bool saveVisits; // save dispersal visits heat maps? #if RS_RCPP int outStartPaths; @@ -345,22 +399,17 @@ class paramSim { bool ReturnPopRaster; bool CreatePopFile; #endif - 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 - + string dir; // full name of working directory + bool fixReplicateSeed; + string patchSamplingOption; + bool outputGenes; + bool outputWeirCockerham; + bool outputWeirHill; + int outputStartGenetics; + int outputGeneticInterval; }; -#if RSDEBUG -extern ofstream DEBUGLOG; -#endif +extern RSrandom* pRandom; //--------------------------------------------------------------------------- #endif diff --git a/Patch.cpp b/Patch.cpp index ef70120..473baae 100644 --- a/Patch.cpp +++ b/Patch.cpp @@ -1,47 +1,49 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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) +Patch::Patch(int seqnum, int num) { -patchSeqNum = seqnum; patchNum = num; nCells = 0; -xMin = yMin = 999999999; xMax = yMax = 0; x = y = 0; + patchSeqNum = seqnum; patchNum = num; nCells = 0; + xMin = yMin = 999999999; xMax = yMax = 0; x = y = 0; subCommPtr = nullptr; -localK = 0.0; -for (int sex = 0; sex < NSEXES; sex++) { - nTemp[sex] = 0; -} -changed = false; + localK = 0.0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + nTemp[sex] = 0; + } + localDemoScaling.assign(nDSlayer,1.0); + changed = false; } Patch::~Patch() { -cells.clear(); -popns.clear(); + cells.clear(); + popns.clear(); + localDemoScaling.clear(); } int Patch::getSeqNum(void) { return patchSeqNum; } @@ -51,68 +53,68 @@ 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; + 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; - } +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; + } + 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]); + 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; } - 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) { +void Patch::addCell(Cell* pCell, int x, int y) { cells.push_back(pCell); nCells++; if (x < xMin) xMin = x; @@ -123,184 +125,194 @@ void Patch::addCell(Cell* pCell,int x,int 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; - } +void Patch::setCarryingCapacity(Species* pSpecies, patchLimits landlimits, + float epsGlobal, short nHab, short rasterType, short landIx, bool gradK) { + envStochParams env = paramsStoch->getStoch(); + 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 (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; + return; } - 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; + + 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) { + envval += cells[i]->getEps(); + } + else { // global stochasticity + envval += epsGlobal; + } } - break; - case 2: // habitat quality - q = cells[i]->getHabitat(landIx); - if (q > 0.0) { - nsuitable++; - localK += envval * pSpecies->getHabK(0) * q / 100.0f; + 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; } - break; + loc = cells[i]->getLocn(); + xsum += loc.x; ysum += loc.y; } -#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 + 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; + limit = pSpecies->getMinMax(1) * (float)nsuitable; + if (localK > limit) localK = limit; + } } float Patch::getK(void) { return localK; } +// SPATIALDEMOG +void Patch::setDemoScaling(std::vector ds) { + + std::for_each(ds.begin(), ds.end(), [](float& perc){ if(perc < 0.0 || perc > 1.0) perc=1; }); + + localDemoScaling.assign(ds.begin(), ds.end()); + + return; +} + +std::vector Patch::getDemoScaling(void) { return localDemoScaling; } + +void Patch::setPatchDemoScaling(short landIx, patchLimits landlimits) { + + // if patch wholly outside current landscape boundaries + if (xMin > landlimits.xMax || xMax < landlimits.xMin + || yMin > landlimits.yMax || yMax < landlimits.yMin) { + localDemoScaling.assign(nDSlayer,0.0); // set all local scales to zero + return; + } + + // loop through constituent cells of the patch + int ncells = (int)cells.size(); + std::vector patchDS(nDSlayer, 0.0); + std::vector cellDS(nDSlayer, 0.0); + + for (int i = 0; i < ncells; i++) { + cellDS = cells[i]->getDemoScaling(landIx); // is that ok? + + //add cell value to patch value + for (int ly = 0; ly < nDSlayer; ly++) { + patchDS[ly] += cellDS[ly]; + } + } + + // take mean over cells and divide by 100 to scale to range [0,1] + for (int ly = 0; ly < nDSlayer; ly++) { + patchDS[ly] = patchDS[ly] / ncells / 100.0f; + } + + // set values + setDemoScaling(patchDS); + + return; +} +//SPATIALDEMOG + // 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; + 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; +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; + 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; + 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; + 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(SubCommunity *sc) -{ subCommPtr = sc; } +void Patch::setSubComm(SubCommunity* sc) +{ + subCommPtr = sc; +} // Get pointer to corresponding Sub-community -SubCommunity *Patch::getSubComm(void) +SubCommunity* Patch::getSubComm(void) { return subCommPtr; } #ifdef _OPENMP @@ -310,50 +322,47 @@ std::unique_lock Patch::lockPopns() { #endif void Patch::addPopn(patchPopn pop) { -popns.push_back(pop); + popns.push_back(pop); } // Return pointer to the Population of the specified Species -Population *Patch::getPopn(Species *sp) +Population* Patch::getPopn(Species *sp) { -int npops = (int)popns.size(); -for (int i = 0; i < npops; i++) { - if (popns[i].pSp == sp) return popns[i].pPop; -} -return 0; + 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(); + popns.clear(); } void Patch::resetPossSettlers(void) { -for (int sex = 0; sex < NSEXES; sex++) { - nTemp[sex] = 0; -} + for (int sex = 0; sex < gMaxNbSexes; 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 +void Patch::incrPossSettler(Species* pSpecies, int sex) { // NOTE: THE FOLLOWING OPERATION WILL NEED TO BE MADE SPECIES-SPECIFIC... -if (sex >= 0 && sex < NSEXES) { - nTemp[sex]++; -} + if (sex >= 0 && sex < gMaxNbSexes) { + 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 +int Patch::getPossSettlers(Species* pSpecies, int sex) { // NOTE: THE FOLLOWING OPERATION WILL NEED TO BE MADE SPECIES-SPECIFIC... -if (sex >= 0 && sex < NSEXES) return nTemp[sex]; -else return 0; + if (sex >= 0 && sex < gMaxNbSexes) return nTemp[sex]; + else return 0; +} + +bool Patch::speciesIsPresent(Species* pSpecies) { + const auto pPop = this->getPopn(pSpecies); + return pPop != 0; } //--------------------------------------------------------------------------- diff --git a/Patch.h b/Patch.h index 89e4de5..5f74c50 100644 --- a/Patch.h +++ b/Patch.h @@ -1,66 +1,66 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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. + RangeShifter v2.0 Patch -There is no requirement that all the Cells be adjacent, although in practice -that would usually be the case. + Implements the class: Patch -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. + 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. -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. + There is no requirement that all the Cells be adjacent, although in practice + that would usually be the case. -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. + 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 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 + 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. -Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + 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. -Last updated: 25 June 2021 by Steve Palmer + 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 @@ -77,20 +77,20 @@ using namespace std; #include #endif -//--------------------------------------------------------------------------- + //--------------------------------------------------------------------------- class Population; class SubCommunity; struct patchLimits { - int xMin,xMax,yMin,yMax; + int xMin, xMax, yMin, yMax; }; struct patchPopn { Species *pSp; // pointers to Species Population *pPop; // pointers to Population }; -class Patch{ +class Patch { public: Patch( int, // internal sequential number @@ -107,7 +107,7 @@ class Patch{ 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 + 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 @@ -153,22 +153,27 @@ class Patch{ bool // TRUE if there is a gradient in carrying capacity across the Landscape ); float getK(void); + bool speciesIsPresent(Species* pSpecies); + void setDemoScaling(std::vector ); + std::vector getDemoScaling(void); + void setPatchDemoScaling(short, patchLimits); // calculate demog. scalings of patch from its cells //TODO arguments - private: +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.) + int xMin, xMax, yMin, yMax; // min and max cell co-ordinates + int x, y; // centroid co-ordinates (approx.) SubCommunity *subCommPtr; // pointer 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) + std::vector localDemoScaling; bool changed; -// NOTE: THE FOLLOWING ARRAY WILL NEED TO BE MADE SPECIES-SPECIFIC... + // NOTE: THE FOLLOWING ARRAY WILL NEED TO BE MADE SPECIES-SPECIFIC... #ifdef _OPENMP - std::atomic nTemp[NSEXES]; // no. of potential settlers in each sex + std::atomic nTemp[gMaxNbSexes]; // no. of potential settlers in each sex #else - short nTemp[NSEXES]; // no. of potential settlers in each sex + short nTemp[gMaxNbSexes]; // no. of potential settlers in each sex #endif std::vector cells; @@ -181,11 +186,9 @@ class Patch{ //--------------------------------------------------------------------------- -extern paramStoch *paramsStoch; -extern RSrandom *pRandom; +extern paramStoch* paramsStoch; +extern RSrandom* pRandom; -#if RSDEBUG -extern ofstream DEBUGLOG; -#endif +extern short nDSlayer; #endif diff --git a/Population.cpp b/Population.cpp index 80bdb15..34dbd0f 100644 --- a/Population.cpp +++ b/Population.cpp @@ -62,12 +62,11 @@ Population::Population(Species* pSp, Patch* pPch, int ninds, int resol) pp.pSp = pSpecies; pp.pPop = this; pPatch->addPopn(pp); - demogrParams dem = pSpecies->getDemogr(); - stageParams sstruct = pSpecies->getStage(); - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); - genomeData gen = pSpecies->getGenomeData(); initParams init = paramsInit->getInit(); // determine no. of stages and sexes of species to initialise @@ -80,14 +79,14 @@ Population::Population(Species* pSp, Patch* pPch, int ninds, int resol) 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++) { + for (int stg = 0; stg < gMaxNbStages; stg++) { + for (int sex = 0; sex < gMaxNbSexes; sex++) { nInds[stg][sex] = 0; } } // set up local copy of minimum age table - short minAge[NSTAGES][NSEXES]; + short minAge[gMaxNbStages][gMaxNbSexes]; for (int stg = 0; stg < nStages; stg++) { for (int sex = 0; sex < nSexes; sex++) { if (dem.stageStruct) { @@ -176,21 +175,15 @@ Population::Population(Species* pSp, Patch* pPch, int ninds, int resol) } } else age = stg; -#if RSDEBUG - // NOTE: CURRENTLY SETTING ALL INDIVIDUALS TO RECORD NO. OF STEPS ... - inds.push_back(new Individual(pSpecies, pCell, pPatch, stg, age, sstruct.repInterval, - probmale, true, trfr.moveType)); -#else - inds.push_back(new Individual(pSpecies, 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); + + Individual* newInd = new Individual(pSpecies, pCell, pPatch, stg, age, sstruct.repInterval, + probmale, trfr.usesMovtProc, trfr.moveType); + + if (pSpecies->getNTraits() > 0) { + newInd->setUpGenes(pSpecies, resol); } - nInds[stg][sex]++; + inds.push_back(newInd); + nInds[stg][newInd->getSex()]++; } } } @@ -206,78 +199,253 @@ Population::~Population(void) { if (juvs[i] != NULL) delete juvs[i]; } juvs.clear(); + int nsampledInds = (int)sampledInds.size(); + for (int i = 0; i < nsampledInds; i++) { + if (sampledInds[i] != NULL) sampledInds[i]=NULL; + } + sampledInds.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; +traitsums Population::getIndTraitsSums(Species* pSpecies) { + + traitsums ts = traitsums(); + for (int sex = 0; sex < gMaxNbSexes; sex++) { + ts.ninds[sex] = 0; + ts.sumD0[sex] = ts.ssqD0[sex] = 0.0; + ts.sumAlpha[sex] = ts.ssqAlpha[sex] = 0.0; + ts.sumBeta[sex] = ts.ssqBeta[sex] = 0.0; + ts.sumDist1[sex] = ts.ssqDist1[sex] = 0.0; + ts.sumDist2[sex] = ts.ssqDist2[sex] = 0.0; + ts.sumProp1[sex] = ts.ssqProp1[sex] = 0.0; + ts.sumDP[sex] = ts.ssqDP[sex] = 0.0; + ts.sumGB[sex] = ts.ssqGB[sex] = 0.0; + ts.sumAlphaDB[sex] = ts.ssqAlphaDB[sex] = 0.0; + ts.sumBetaDB[sex] = ts.ssqBetaDB[sex] = 0.0; + ts.sumStepL[sex] = ts.ssqStepL[sex] = 0.0; + ts.sumRho[sex] = ts.ssqRho[sex] = 0.0; + ts.sumS0[sex] = ts.ssqS0[sex] = 0.0; + ts.sumAlphaS[sex] = ts.ssqAlphaS[sex] = 0.0; + ts.sumBetaS[sex] = ts.ssqBetaS[sex] = 0.0; + ts.sumGeneticFitness[sex] = ts.ssqGeneticFitness[sex] = 0.0; } - demogrParams dem = pSpecies->getDemogr(); - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); 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; + for (auto& ind : inds) { + + int sex = ind->getSex(); + ts.ninds[sex] += 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; + emigTraits e = ind->getIndEmigTraits(); + ts.sumD0[sex] += e.d0; + ts.ssqD0[sex] += e.d0 * e.d0; + ts.sumAlpha[sex] += e.alpha; + ts.ssqAlpha[sex] += e.alpha * e.alpha; + ts.sumBeta[sex] += e.beta; + ts.ssqBeta[sex] += 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; + if (trfr.usesMovtProc) { + + switch (trfr.moveType) { + + case 1: { // SMS + trfrSMSTraits sms = ind->getIndSMSTraits(); + ts.sumDP[sex] += sms.dp; + ts.ssqDP[sex] += sms.dp * sms.dp; + ts.sumGB[sex] += sms.gb; + ts.ssqGB[sex] += sms.gb * sms.gb; + ts.sumAlphaDB[sex] += sms.alphaDB; + ts.ssqAlphaDB[sex] += sms.alphaDB * sms.alphaDB; + ts.sumBetaDB[sex] += sms.betaDB; + ts.ssqBetaDB[sex] += sms.betaDB * sms.betaDB; + break; + } + case 2: { + trfrCRWTraits c = ind->getIndCRWTraits(); + ts.sumStepL[sex] += c.stepLength; + ts.ssqStepL[sex] += c.stepLength * c.stepLength; + ts.sumRho[sex] += c.rho; + ts.ssqRho[sex] += c.rho * c.rho; + break; + } + default: + throw runtime_error("usesMoveProcess is ON but moveType is neither 1 (SMS) or 2 (CRW)."); + break; + } + } + else { + trfrKernelParams k = ind->getIndKernTraits(); + ts.sumDist1[sex] += k.meanDist1; + ts.ssqDist1[sex] += k.meanDist1 * k.meanDist1; + ts.sumDist2[sex] += k.meanDist2; + ts.ssqDist2[sex] += k.meanDist2 * k.meanDist2; + ts.sumProp1[sex] += k.probKern1; + ts.ssqProp1[sex] += k.probKern1 * k.probKern1; + } // 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; + settleTraits s = ind->getIndSettTraits(); + ts.sumS0[sex] += s.s0; + ts.ssqS0[sex] += s.s0 * s.s0; + ts.sumAlphaS[sex] += s.alpha; + ts.ssqAlphaS[sex] += s.alpha * s.alpha; + ts.sumBetaS[sex] += s.beta; + ts.ssqBetaS[sex] += s.beta * s.beta; + + double fitness = ind->getGeneticFitness(); + ts.sumGeneticFitness[sex] += fitness; + ts.ssqGeneticFitness[sex] += fitness * fitness; } - return ts; } -int Population::getNInds(void) { return (int)inds.size(); } +//int Population::getNInds() { return static_cast(inds.size()); } + +// ---------------------------------------------------------------------------------------- +// reset allele table +// ---------------------------------------------------------------------------------------- +void Population::resetPopNeutralTables() { + for (auto& entry : popNeutralCountTables) { + entry.reset(); + } +} + +// ---------------------------------------------------------------------------------------- +// Populate population-level NEUTRAL count tables +// Update allele occurrence and heterozygosity counts, and allele frequencies +// ---------------------------------------------------------------------------------------- +void Population::updatePopNeutralTables() { + + const int nLoci = pSpecies->getNPositionsForTrait(NEUTRAL); + const int nAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + const auto& positions = pSpecies->getSpTrait(NEUTRAL)->getGenePositions(); + const int ploidy = pSpecies->isDiploid() ? 2 : 1; + + // Create /reset empty tables + if (popNeutralCountTables.size() != 0) + resetPopNeutralTables(); + else { + popNeutralCountTables.reserve(nLoci); + + for (int l = 0; l < nLoci; l++) { + popNeutralCountTables.push_back(NeutralCountsTable(nAlleles)); + } + } + + // Fill tallies for each locus + for (Individual* individual : sampledInds) { + + const auto trait = individual->getTrait(NEUTRAL); + int whichLocus = 0; + for (auto position : positions) { + + int alleleOnChromA = (int)trait->getAlleleValueAtLocus(0, position); + popNeutralCountTables[whichLocus].incrementTally(alleleOnChromA); + + if (ploidy == 2) { // second allele and heterozygosity + int alleleOnChromB = (int)trait->getAlleleValueAtLocus(1, position); + popNeutralCountTables[whichLocus].incrementTally(alleleOnChromB); + + bool isHetero = alleleOnChromA != alleleOnChromB; + if (isHetero) { + popNeutralCountTables[whichLocus].incrementHeteroTally(alleleOnChromA); + popNeutralCountTables[whichLocus].incrementHeteroTally(alleleOnChromB); + } + } + whichLocus++; + } + } + + // Fill frequencies + if (sampledInds.size() > 0) { + std::for_each( + popNeutralCountTables.begin(), + popNeutralCountTables.end(), + [&](NeutralCountsTable& thisLocus) -> void { + thisLocus.setFrequencies(static_cast(sampledInds.size()) * ploidy); + }); + } +} + +double Population::getAlleleFrequency(int thisLocus, int whichAllele) { + return popNeutralCountTables[thisLocus].getFrequency(whichAllele); +} + +int Population::getAlleleTally(int thisLocus, int whichAllele) { + return popNeutralCountTables[thisLocus].getTally(whichAllele); +} + +int Population::getHeteroTally(int thisLocus, int whichAllele) { + return popNeutralCountTables[thisLocus].getHeteroTally(whichAllele); +} + +// ---------------------------------------------------------------------------------------- +// Count number of heterozygotes loci in sampled individuals +// ---------------------------------------------------------------------------------------- +int Population::countHeterozygoteLoci() { + int nbHetero = 0; + if (pSpecies->isDiploid()) { + for (Individual* ind : sampledInds) { + const NeutralTrait* trait = (NeutralTrait*)(ind->getTrait(NEUTRAL)); + nbHetero += trait->countHeterozygoteLoci(); + } + } + return nbHetero; +} + +// ---------------------------------------------------------------------------------------- +// Count number of heterozygotes among sampled individuals for each locus +// ---------------------------------------------------------------------------------------- +vector Population::countNbHeterozygotesEachLocus() { + const auto& positions = pSpecies->getSpTrait(NEUTRAL)->getGenePositions(); + vector hetero(positions.size(), 0); + + if (pSpecies->isDiploid()) { + for (Individual* ind : sampledInds) { + const NeutralTrait* trait = (NeutralTrait*)ind->getTrait(NEUTRAL); + int counter = 0; + for (auto position : positions) { + hetero[counter] += trait->isHeterozygoteAtLocus(position); + counter++; + } + } + } + return hetero; +} + +// ---------------------------------------------------------------------------------------- +// Compute the expected heterozygosity for population +// ---------------------------------------------------------------------------------------- +double Population::computeHs() { + int nLoci = pSpecies->getNPositionsForTrait(NEUTRAL); + int nAlleles = pSpecies->getSpTrait(NEUTRAL)->getNbNeutralAlleles(); + double hs = 0; + double freq; + vector locihet(nLoci, 1); + + if (sampledInds.size() > 0) { + for (int thisLocus = 0; thisLocus < nLoci; ++thisLocus) { + for (int allele = 0; allele < nAlleles; ++allele) { + freq = getAlleleFrequency(thisLocus, allele); + freq *= freq; //squared frequencies (expected _homozygosity) + locihet[thisLocus] -= freq; // 1 - sum of p2 = expected heterozygosity + } + hs += locihet[thisLocus]; + } + } + return hs; +} -popStats Population::getStats(void) +popStats Population::getStats(std::vector localDemoScaling) { - popStats p; + popStats p = popStats(); int ninds; float fec; - bool breeders[2]; breeders[0] = breeders[1] = false; - demogrParams dem = pSpecies->getDemogr(); + bool breeders[2] = { false, false }; + demogrParams dem = pSpecies->getDemogrParams(); p.pSpecies = pSpecies; p.pPatch = pPatch; p.spNum = pSpecies->getSpNum(); @@ -288,11 +456,20 @@ popStats Population::getStats(void) 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 (dem.repType == 2) { + if (pSpecies->getFecSpatial() && pSpecies->getFecLayer(stg,sex)>=0){ + fec = pSpecies->getFec(stg,sex)*localDemoScaling[pSpecies->getFecLayer(stg,sex)]; + } + else fec = pSpecies->getFec(stg,sex); + } + else { + if (pSpecies->getFecSpatial() && pSpecies->getFecLayer(stg,0)>=0){ + fec = pSpecies->getFec(stg,0)*localDemoScaling[pSpecies->getFecLayer(stg,0)]; + } + else fec = pSpecies->getFec(stg,0); + } if (fec > 0.0) { breeders[sex] = true; p.nAdults += ninds; } } else breeders[sex] = true; @@ -311,25 +488,24 @@ popStats Population::getStats(void) 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::getNbInds() const { + return inds.size(); } -int Population::stagePop(int stg) { +int Population::getNbInds(int stg) const { int t = 0; - if (stg < 0 || stg >= nStages) return t; + if (stg < 0 || stg >= nStages) throw runtime_error("Attempt to get nb individuals for stage " + to_string(stg) + ", no such stage."); for (int sex = 0; sex < nSexes; sex++) { t += nInds[stg][sex]; } return t; } +int Population::getNbInds(int stg, int sex) const { + if (stg < 0 || stg >= nStages) throw runtime_error("Attempt to get nb individuals for stage " + to_string(stg) + ", no such stage."); + return nInds[stg][sex]; +} + //--------------------------------------------------------------------------- // Remove all Individuals void Population::extirpate(void) { @@ -352,7 +528,7 @@ void Population::extirpate(void) { //--------------------------------------------------------------------------- // Produce juveniles and hold them in the juvs vector -void Population::reproduction(const float localK, const float envval, const int resol) +void Population::reproduction(const float localK, const float envval, const int resol, std::vector localDemoScaling) { // get population size at start of reproduction @@ -366,26 +542,35 @@ void Population::reproduction(const float localK, const float envval, const int bool skipbreeding; envStochParams env = paramsStoch->getStoch(); - demogrParams dem = pSpecies->getDemogr(); - stageParams sstruct = pSpecies->getStage(); - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); - genomeData gen = pSpecies->getGenomeData(); - simView v = paramsSim->getViews(); - if (dem.repType == 0) nsexes = 1; else nsexes = 2; + if (dem.repType == 0) + nsexes = 1; + else nsexes = 2; + // set up local copy of species fecundity table - float fec[NSTAGES][NSEXES]; + float fec[gMaxNbStages][gMaxNbSexes]; 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); + if (pSpecies->getFecSpatial() && pSpecies->getFecLayer(stg,0)>=0){ + fec[stg][sex] = pSpecies->getFec(stg,0)*localDemoScaling[pSpecies->getFecLayer(stg,0)]; + } + else fec[stg][sex] = pSpecies->getFec(stg,0); + } + else { + if (pSpecies->getFecSpatial() && pSpecies->getFecLayer(stg,sex)>=0){ + fec[stg][sex] = pSpecies->getFec(stg,sex)*localDemoScaling[pSpecies->getFecLayer(stg,sex)]; + } + else fec[stg][sex] = pSpecies->getFec(stg,sex); } - else fec[stg][sex] = pSpecies->getFec(stg, sex); } else { // non-structured population if (stg == 1) fec[stg][sex] = dem.lambda; // adults @@ -413,7 +598,7 @@ void Population::reproduction(const float localK, const float envval, const int 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 + // NOTE: matrix entries represent effect of ROW on COLUMN // AND males precede females float weight = 0.0; for (int effstg = 0; effstg < nStages; effstg++) { @@ -430,7 +615,7 @@ void Population::reproduction(const float localK, const float envval, const int } } else // not stage-specific - effect = (float)totalPop(); + effect = (float)getNbInds(); if (localK > 0.0) fec[stg][0] *= exp(-effect / localK); } } @@ -459,7 +644,7 @@ void Population::reproduction(const float localK, const float envval, const int } double propBreed; - Individual* father; + Individual* father = nullptr; std::vector fathers; switch (dem.repType) { @@ -489,17 +674,20 @@ void Population::reproduction(const float localK, const float envval, const int 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(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, true, trfr.moveType)); -#else - juvs.push_back(new Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, trfr.moveModel, trfr.moveType)); -#endif + + Individual* newJuv; + newJuv = new Individual(pSpecies, pCell, pPatch, 0, 0, 0, dem.propMales, trfr.usesMovtProc, trfr.moveType); + + if (pSpecies->getNTraits() > 0) { + newJuv->inheritTraits(pSpecies, inds[i], resol); + } + + if (!newJuv->isViable()) { + delete newJuv; + } + else { + juvs.push_back(newJuv); 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); } } } @@ -511,7 +699,6 @@ void Population::reproduction(const float localK, const float envval, const int 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(); @@ -555,6 +742,7 @@ void Population::reproduction(const float localK, const float envval, const int 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(); @@ -564,18 +752,21 @@ void Population::reproduction(const float localK, const float envval, const int 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(pSpecies, pCell, pPatch, 0, 0, 0, dem.propMales, true, trfr.moveType)); -#else - juvs.push_back(new Individual(pSpecies, pCell, pPatch, 0, 0, 0, dem.propMales, trfr.moveModel, trfr.moveType)); -#endif - sex = juvs[nj + j]->getSex(); + Individual* newJuv; + + newJuv = new Individual(pSpecies, pCell, pPatch, 0, 0, 0, dem.propMales, trfr.usesMovtProc, trfr.moveType); + + if (pSpecies->getNTraits() > 0) { + newJuv->inheritTraits(pSpecies, inds[i], father, resol); + } + + if (!newJuv->isViable()) { + delete newJuv; + } + else { + juvs.push_back(newJuv); + sex = newJuv->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); } } } @@ -595,7 +786,7 @@ void Population::reproduction(const float localK, const float envval, const int // Following reproduction of ALL species, add juveniles to the population prior to dispersal void Population::fledge(void) { - demogrParams dem = pSpecies->getDemogr(); + demogrParams dem = pSpecies->getDemogrParams(); if (dem.stageStruct) { // juveniles are added to the individuals vector inds.insert(inds.end(), juvs.begin(), juvs.end()); @@ -615,69 +806,119 @@ void Population::fledge(void) } +Individual* Population::sampleInd() const { + int index = pRandom->IRandom(0, static_cast(inds.size() - 1)); + return inds[index]; +} + +void Population::sampleIndsWithoutReplacement(string strNbToSample, const set& sampleStages) { + + if (sampledInds.size() > 0) { + sampledInds.clear(); + } + auto rng = pRandom->getRNG(); + vector stagedInds; + + // Stage individuals in eligible stages + for (int stage : sampleStages) { + vector toAdd = getIndividualsInStage(stage); + stagedInds.insert(stagedInds.begin(), toAdd.begin(), toAdd.end()); + } + + if (strNbToSample == "all") { + // Sample all individuals in selected stages + sampledInds = stagedInds; + } + else { // random + int nbToSample = stoi(strNbToSample); + if (stagedInds.size() <= nbToSample) { + // Sample all individuals in selected stages + sampledInds = stagedInds; + } + else { + // Sample n individuals across selected stages + sample(stagedInds.begin(), stagedInds.end(), std::back_inserter(sampledInds), nbToSample, rng); + } + } +} + +int Population::sampleSize() const { + return static_cast(sampledInds.size()); +} + +vector Population::getIndividualsInStage(int stage) { + vector indsInStage; + for (auto ind : inds) { + if (ind->getStats().stage == stage) + indsInStage.push_back(ind); + } + return indsInStage; +} + // 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(); + double disp, pbDisp, NK; + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); emigTraits eparams; - trfrRules trfr = pSpecies->getTrfr(); + transferRules trfr = pSpecies->getTransferRules(); 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; + NK = static_cast(getNbInds()) / localK; - int ninds = (int)inds.size(); + int ninds = static_cast(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]; + if (dem.repType == 0) nsexes = 1; + else nsexes = 2; + double pbEmig[gMaxNbStages][gMaxNbSexes]; for (int stg = 0; stg < sstruct.nStages; stg++) { for (int sex = 0; sex < nsexes; sex++) { - if (emig.indVar) Pemig[stg][sex] = 0.0; + if (emig.indVar) pbEmig[stg][sex] = 0.0; else { if (emig.densDep) { if (emig.sexDep) { if (emig.stgDep) { - eparams = pSpecies->getEmigTraits(stg, sex); + eparams = pSpecies->getSpEmigTraits(stg, sex); } else { - eparams = pSpecies->getEmigTraits(0, sex); + eparams = pSpecies->getSpEmigTraits(0, sex); } } else { // !emig.sexDep if (emig.stgDep) { - eparams = pSpecies->getEmigTraits(stg, 0); + eparams = pSpecies->getSpEmigTraits(stg, 0); } else { - eparams = pSpecies->getEmigTraits(0, 0); + eparams = pSpecies->getSpEmigTraits(0, 0); } } - Pemig[stg][sex] = eparams.d0 / (1.0 + exp(-(NK - eparams.beta) * eparams.alpha)); + pbEmig[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); + pbEmig[stg][sex] = pSpecies->getSpEmigD0(stg, sex); } else { // !emig.stgDep - Pemig[stg][sex] = pSpecies->getEmigD0(0, sex); + pbEmig[stg][sex] = pSpecies->getSpEmigD0(0, sex); } } else { // !emig.sexDep if (emig.stgDep) { - Pemig[stg][sex] = pSpecies->getEmigD0(stg, 0); + pbEmig[stg][sex] = pSpecies->getSpEmigD0(stg, 0); } else { // !emig.stgDep - Pemig[stg][sex] = pSpecies->getEmigD0(0, 0); + pbEmig[stg][sex] = pSpecies->getSpEmigD0(0, 0); } } } @@ -687,25 +928,25 @@ void Population::emigration(float localK) for (int i = 0; i < ninds; i++) { ind = inds[i]->getStats(); - if (ind.status < 1) + if (ind.status < 1) // ToDo: Maybe allow dispersal after translocation? If so, we need to update the pPrevCell and pCurrCell variables of the translocated individuals! { if (emig.indVar) { // individual variability in emigration if (dem.stageStruct && ind.stage != emig.emigStage) { // emigration may not occur - Pdisp = 0.0; + pbDisp = 0.0; } else { // non-structured or individual is in emigration stage - eparams = inds[i]->getEmigTraits(); + eparams = inds[i]->getIndEmigTraits(); if (emig.densDep) { // density-dependent - NK = (float)totalPop() / localK; - Pdisp = eparams.d0 / (1.0 + exp(-(NK - eparams.beta) * eparams.alpha)); + NK = (float)getNbInds() / localK; + pbDisp = eparams.d0 / (1.0 + exp(-(NK - eparams.beta) * eparams.alpha)); } else { // density-independent if (emig.sexDep) { - Pdisp = Pemig[0][ind.sex] + eparams.d0; + pbDisp = pbEmig[0][ind.sex] + eparams.d0; } else { - Pdisp = Pemig[0][0] + eparams.d0; + pbDisp = pbEmig[0][0] + eparams.d0; } } } @@ -715,44 +956,41 @@ void Population::emigration(float localK) if (emig.densDep) { if (emig.sexDep) { if (emig.stgDep) { - Pdisp = Pemig[ind.stage][ind.sex]; + pbDisp = pbEmig[ind.stage][ind.sex]; } else { - Pdisp = Pemig[0][ind.sex]; + pbDisp = pbEmig[0][ind.sex]; } } else { // !emig.sexDep if (emig.stgDep) { - Pdisp = Pemig[ind.stage][0]; + pbDisp = pbEmig[ind.stage][0]; } else { - Pdisp = Pemig[0][0]; + pbDisp = pbEmig[0][0]; } } } else { // density-independent if (emig.sexDep) { if (emig.stgDep) { - Pdisp = Pemig[ind.stage][ind.sex]; + pbDisp = pbEmig[ind.stage][ind.sex]; } else { // !emig.stgDep - Pdisp = Pemig[0][ind.sex]; + pbDisp = pbEmig[0][ind.sex]; } } else { // !emig.sexDep if (emig.stgDep) { - Pdisp = Pemig[ind.stage][0]; + pbDisp = pbEmig[ind.stage][0]; } else { // !emig.stgDep - Pdisp = Pemig[0][0]; + pbDisp = pbEmig[0][0]; } } } - - } // end of no individual variability - - disp = pRandom->Bernoulli(Pdisp); + disp = pRandom->Bernoulli(pbDisp); if (disp == 1) { // emigrant inds[i]->setStatus(1); @@ -769,13 +1007,23 @@ void Population::allEmigrate(void) { } } +// Remove an Individual from the Population +Individual* Population::extractIndividual(int ix) { + Individual* pInd = inds[ix]; + indStats ind = pInd->getStats(); + inds[ix] = nullptr; + nInds[ind.stage][ind.sex]--; + return pInd; +} + // If an Individual has been identified as an emigrant, remove it from the Population disperser Population::extractDisperser(int ix) { - disperser d; + disperser d = disperser(); indStats ind = inds[ix]->getStats(); if (ind.status == 1) { // emigrant - d.pInd = inds[ix]; d.yes = true; - inds[ix] = 0; + d.pInd = inds[ix]; + d.yes = true; + inds[ix] = nullptr; nInds[ind.stage][ind.sex]--; } else { @@ -784,11 +1032,12 @@ disperser Population::extractDisperser(int ix) { 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; + disperser d = disperser(); Cell* pCell; indStats ind = inds[ix]->getStats(); @@ -806,7 +1055,7 @@ disperser Population::extractSettler(int ix) { // Add a specified individual to the new/current dispersal group // Add a specified individual to the population void Population::recruit(Individual* pInd) { - indStats ind = pInd->getStats(); + indStats ind = pInd->getStats(); // potentially I need to add localscalings or so? nInds[ind.stage][ind.sex]++; #ifdef _OPENMP const std::lock_guard lock(inds_mutex); @@ -816,302 +1065,16 @@ void Population::recruit(Individual* pInd) { // Add specified individuals to the new/current dispersal group // Add specified individuals to the population -void Population::recruitMany(std::vector& new_inds) { - if (new_inds.empty()) return; - for (Individual* pInd : new_inds) { - indStats ind = pInd->getStats(); - nInds[ind.stage][ind.sex]++; - } +void Population::recruitMany(std::vector& recruits) { + if (recruits.empty()) return; + for (Individual* pInd : recruits) { + indStats ind = pInd->getStats(); + nInds[ind.stage][ind.sex]++; +} #ifdef _OPENMP const std::lock_guard lock(inds_mutex); #endif // _OPENMP - inds.insert(inds.end(), new_inds.begin(), new_inds.end()); -} - -//--------------------------------------------------------------------------- - -// 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; - 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(); - #pragma omp parallel for reduction(+:ndispersers) private(disperser, pCell, pPatch) schedule(static,128) - 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]->getCurrCell(); - pPatch = pCell->getPatch(); - if (pPatch != nullptr) { // not no-data area - pPatch->incrPossSettler(pSpecies, inds[i]->getSex()); - } - } - } - } - } - -// each individual which has reached a potential patch decides whether to settle - #pragma omp parallel for reduction(-:ndispersers) default(none) shared(ninds, settletype, pRandom, trfr, ppLand, pLandscape) private(ind, othersex, sett, pCell, mateOK, densdepOK, settle, pPatch, localK, popsize, pNewPopn, settDD, settprob, newloc, nbrloc, patchnum) schedule(static) - 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]->getCurrCell(); - 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) - { - pPatch = pCell->getPatch(); - if (pPatch != nullptr) { // not no-data area - 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(); - pNewPopn = pPatch->getPopn(pSpecies); - if (pNewPopn == nullptr) { // population has not been set up in the new patch - popsize = 0.0; - } - else { - 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]->getCurrCell(); - 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 - pPatch = pCell->getPatch(); - if (pPatch != nullptr) { // not no-data area - 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) -{ - Patch* pPatch; - Population* pNewPopn; - int popsize = 0; - bool matefound = false; - - pPatch = pCell->getPatch(); - if (pPatch != nullptr) { - if (pPatch->getPatchNum() > 0) { // not the matrix patch - if (pPatch->getK() > 0.0) - { // suitable - pNewPopn = pPatch->getPopn(pSpecies); - if (pNewPopn != nullptr) { - // 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; + inds.insert(inds.end(), recruits.begin(), recruits.end()); } //--------------------------------------------------------------------------- @@ -1121,41 +1084,53 @@ bool Population::matePresent(Cell* pCell, short othersex) // 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) +void Population::survival0(float localK, short option0, short option1, std::vector localDemoScaling) { // 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); - // get surrent population size - int ninds = (int)inds.size(); + // get current population size + int ninds = 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]; + int nsexes = dem.repType == 0 ? 1 : 2; + float dev[gMaxNbStages][gMaxNbSexes]; + float surv[gMaxNbStages][gMaxNbSexes]; + short minAge[gMaxNbStages][gMaxNbSexes]; + 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); + if (pSpecies->getDevSpatial() && pSpecies->getDevLayer(stg,0)>=0){ + dev[stg][sex] = pSpecies->getDev(stg,0)*localDemoScaling[pSpecies->getDevLayer(stg,0)]; + } + else dev[stg][sex] = pSpecies->getDev(stg,0); + if (pSpecies->getSurvSpatial() && pSpecies->getSurvLayer(stg,0)>=0){ + surv[stg][sex] = pSpecies->getSurv(stg,0)*localDemoScaling[pSpecies->getSurvLayer(stg,0)]; + } + else 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); + if (pSpecies->getDevSpatial() && pSpecies->getDevLayer(stg,sex)>=0){ + dev[stg][sex] = pSpecies->getDev(stg,sex)*localDemoScaling[pSpecies->getDevLayer(stg,sex)]; + } + else dev[stg][sex] = pSpecies->getDev(stg,sex); + if (pSpecies->getSurvSpatial() && pSpecies->getSurvLayer(stg,sex)>=0){ + surv[stg][sex] = pSpecies->getSurv(stg,sex)*localDemoScaling[pSpecies->getSurvLayer(stg,sex)]; + } + else 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 @@ -1171,17 +1146,14 @@ void Population::survival0(float localK, short option0, short option1) } } } - 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 + // NOTE: matrix entries represent effect of ROW on COLUMN // AND males precede females float weight = 0.0; for (int effstg = 0; effstg < nStages; effstg++) { @@ -1200,14 +1172,15 @@ void Population::survival0(float localK, short option0, short option1) } } else // not stage-specific - effect = (float)totalPop(); + effect = (float)getNbInds(); 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 + // NOTE: matrix entries represent effect of ROW on COLUMN // AND males precede females float weight = 0.0; for (int effstg = 0; effstg < nStages; effstg++) { @@ -1226,20 +1199,20 @@ void Population::survival0(float localK, short option0, short option1) } } else // not stage-specific - effect = (float)totalPop(); + effect = (float)getNbInds(); 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 + if (ind.status < 6 || ind.status == 10) { // not already doomed double probsurv = surv[ind.stage][ind.sex]; // does the individual survive? if (pRandom->Bernoulli(probsurv)) { // survives @@ -1248,7 +1221,7 @@ void Population::survival0(float localK, short option0, short option1) 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(); + inds[i]->setToDevelop(); } } } @@ -1264,13 +1237,15 @@ void Population::survival0(float localK, short option0, short option1) // 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 + + if (ind.status > 5 && ind.status != 10) { // doomed to die; status 10 is translocated? + if (ind.status != 10) //not going into cold storage -> is there a new status 10 in this new_genetics version?? delete inds[i]; - inds[i] = NULL; + inds[i] = nullptr; nInds[ind.stage][ind.sex]--; } else { @@ -1281,14 +1256,12 @@ void Population::survival1(void) } } } - -// remove pointers to dead individuals clean(); } void Population::ageIncrement(void) { int ninds = (int)inds.size(); - stageParams sstruct = pSpecies->getStage(); + stageParams sstruct = pSpecies->getStageParams(); for (int i = 0; i < ninds; i++) { inds[i]->ageIncrement(sstruct.maxAge); } @@ -1305,11 +1278,11 @@ void Population::clean(void) shuffle(inds.begin(), inds.end(), pRandom->getRNG()); #else -#if !RSDEBUG - // do not randomise individuals in RSDEBUG mode, as the function uses rand() +#ifdef NDEBUG + // do not randomise individuals in DEBUG 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 // NDEBUG #endif // RS_RCPP } @@ -1318,10 +1291,10 @@ void Population::clean(void) //--------------------------------------------------------------------------- // Close population file bool Population::outPopFinishLandscape() { - if (outPop.is_open()) outPop.close(); - outPop.clear(); - return true; -} + if (outPop.is_open()) outPop.close(); + outPop.clear(); + return true; + } //--------------------------------------------------------------------------- // Open population file and write header record @@ -1333,8 +1306,11 @@ bool Population::outPopStartLandscape(int landNr, bool patchModel) { // 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + name = paramsSim->getDir(2) + + (sim.batchMode ? "Batch" + to_string(sim.batchNum) + "_" : "") + + "Sim" + to_string(sim.simulation) + "_Land" + to_string(landNr) + "_Pop.txt"; if (sim.batchMode) { name = paramsSim->getDir(2) @@ -1370,7 +1346,6 @@ bool Population::outPopStartLandscape(int landNr, bool patchModel) { if (dem.repType != 0) outPop << "\tNfemales\tNmales"; } outPop << endl; - return outPop.is_open(); } @@ -1380,12 +1355,11 @@ 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; + demogrParams dem = pSpecies->getDemogrParams(); + popStats p; outPop << rep << "\t" << yr << "\t" << gen; if (patchModel) { outPop << "\t" << pPatch->getPatchNum(); @@ -1409,7 +1383,7 @@ void Population::outPopulation(int rep, int yr, int gen, float eps, } outPop << "\t" << pSpecies->getSpNum(); if (dem.stageStruct) { - p = getStats(); + p = getStats(pPatch->getDemoScaling()); outPop << "\t" << p.nNonJuvs; // non-juvenile stage totals from permanent array for (int stg = 1; stg < nStages; stg++) { @@ -1423,7 +1397,7 @@ void Population::outPopulation(int rep, int yr, int gen, float eps, } } else { // non-structured population - outPop << "\t" << totalPop(); + outPop << "\t" << getNbInds(); if (dem.repType != 0) { // sexual model outPop << "\t" << nInds[1][0] << "\t" << nInds[1][1]; @@ -1438,46 +1412,41 @@ void Population::outPopulation(int rep, int yr, int gen, float eps, // Close individuals file void Population::outIndsFinishReplicate() { - if (outInds.is_open()) { - outInds.close(); outInds.clear(); + if (outInds.is_open()) { + outInds.close(); outInds.clear(); + } + return; } - return; -} //--------------------------------------------------------------------------- // Open individuals file and write header record void Population::outIndsStartReplicate(int rep, int landNr, bool patchModel) { string name; - demogrParams dem = pSpecies->getDemogr(); - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + demogrParams dem = pSpecies->getDemogrParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); simParams sim = paramsSim->getSim(); - if (sim.batchMode) { name = paramsSim->getDir(2) - + "Batch" + to_string(sim.batchNum) + "_" + + (sim.batchMode ? "Batch" + to_string(sim.batchNum) + "_" : "") + "Sim" + to_string(sim.simulation) + "_Land" + to_string(landNr) + "_Rep" + to_string(rep) + "_Inds.txt"; - } - else { - name = paramsSim->getDir(2) + "Sim" + to_string(sim.simulation) - + "_Rep" + to_string(rep) + "_Inds.txt"; - } - outInds.open(name.c_str()); + 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 (pSpecies->getNbGenLoadTraits() > 0) outInds << "\tProbViable"; if (emig.indVar) { if (emig.densDep) outInds << "\tD0\tAlpha\tBeta"; else outInds << "\tEP"; } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 1) { // SMS outInds << "\tDP\tGB\tAlphaDB\tBetaDB"; } @@ -1494,11 +1463,10 @@ void Population::outIndsStartReplicate(int rep, int landNr, bool patchModel) outInds << "\tS0\tAlphaS\tBetaS"; } outInds << "\tDistMoved"; -#if RSDEBUG - // ALWAYS WRITE NO. OF STEPS +#ifndef NDEBUG outInds << "\tNsteps"; #else - if (trfr.moveModel) outInds << "\tNsteps"; + if (trfr.usesMovtProc) outInds << "\tNsteps"; #endif outInds << endl; } @@ -1508,20 +1476,17 @@ void Population::outIndsStartReplicate(int rep, int landNr, bool patchModel) 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(); + demogrParams dem = pSpecies->getDemogrParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); 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 @@ -1565,8 +1530,10 @@ void Population::outIndividual(Landscape* pLandscape, int rep, int yr, int gen, if (dem.repType != 0) outInds << "\t" << ind.sex; if (dem.stageStruct) outInds << "\t" << ind.age << "\t" << ind.stage; + if (pSpecies->getNbGenLoadTraits() > 0) outInds << "\t" << inds[i]->getGeneticFitness(); + if (emig.indVar) { - emigTraits e = inds[i]->getEmigTraits(); + emigTraits e = inds[i]->getIndEmigTraits(); if (emig.densDep) { outInds << "\t" << e.d0 << "\t" << e.alpha << "\t" << e.beta; } @@ -1574,21 +1541,20 @@ void Population::outIndividual(Landscape* pLandscape, int rep, int yr, int gen, outInds << "\t" << e.d0; } } // end of if (emig.indVar) - if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 1) { // SMS - trfrSMSTraits s = inds[i]->getSMSTraits(); + trfrSMSTraits s = inds[i]->getIndSMSTraits(); 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(); + trfrCRWTraits c = inds[i]->getIndCRWTraits(); outInds << "\t" << c.stepLength << "\t" << c.rho; } // end of CRW } else { // kernel - trfrKernTraits k = inds[i]->getKernTraits(); + trfrKernelParams k = inds[i]->getIndKernTraits(); if (trfr.twinKern) { outInds << "\t" << k.meanDist1 << "\t" << k.meanDist2 << "\t" << k.probKern1; @@ -1600,7 +1566,7 @@ void Population::outIndividual(Landscape* pLandscape, int rep, int yr, int gen, } if (sett.indVar) { - settleTraits s = inds[i]->getSettTraits(); + settleTraits s = inds[i]->getIndSettTraits(); outInds << "\t" << s.s0 << "\t" << s.alpha << "\t" << s.beta; } @@ -1611,76 +1577,244 @@ void Population::outIndividual(Landscape* pLandscape, int rep, int yr, int gen, + (natalloc.y - loc.y) * (natalloc.y - loc.y))); outInds << "\t" << d; } -#if RSDEBUG +#ifndef NDEBUG // ALWAYS WRITE NO. OF STEPS steps = inds[i]->getSteps(); outInds << "\t" << steps.year; #else - if (trfr.moveModel) { + if (trfr.usesMovtProc) { steps = inds[i]->getSteps(); outInds << "\t" << steps.year; } #endif outInds << endl; } // end of writeInd condition + } } -//--------------------------------------------------------------------------- +void Population::outputGeneValues(ofstream& ofsGenes, const int& yr, const int& gen) const { -//--------------------------------------------------------------------------- -// Close genetics file -void Population::outGenFinishReplicate() -{ - Genome* pGenome = new Genome(); - pGenome->outGenFinishReplicate(); - delete pGenome; -} - -// Open genetics file and write header record -void Population::outGenStartReplicate(const int rep, const int landNr) -{ + const bool isDiploid = pSpecies->isDiploid(); + int indID; + float alleleOnChromA, alleleOnChromB; + float domCoefA, domCoefB; - simParams sim = paramsSim->getSim(); - Genome* pGenome; - genomeData gen = pSpecies->getGenomeData(); - if (gen.trait1Chromosome) { - pGenome = new Genome(pSpecies->getNChromosomes(), pSpecies->getNLoci(0), - pSpecies->isDiploid()); - } - else { - pGenome = new Genome(pSpecies); + // Subset traits that are selected to be output + set traitTypes = pSpecies->getTraitTypes(); + set outputTraitTypes; + for (auto trType : traitTypes) { + if (pSpecies->getSpTrait(trType)->isOutput()) + outputTraitTypes.insert(trType); } - pGenome->outGenStartReplicate(rep, landNr, sim.outGenXtab); - delete pGenome; - return; -} -// Write records to genetics file -void Population::outGenetics(const int rep, const int year) -{ - simParams sim = paramsSim->getSim(); - short spNum = pSpecies->getSpNum(); - short nstages = 1; - if (pSpecies->stageStructured()) { - stageParams sstruct = pSpecies->getStage(); - nstages = sstruct.nStages; - } + // Fetch map to positions for each trait + // Presumably faster than fetching for every individual + map> allGenePositions; + for (auto trType : outputTraitTypes) { + set traitPositions = pSpecies->getSpTrait(trType)->getGenePositions(); + allGenePositions.insert(make_pair(trType, traitPositions)); + } - 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, sim.outGenXtab); + set positions; + for (Individual* ind : sampledInds) { + indID = ind->getId(); + for (auto trType : outputTraitTypes) { + positions = allGenePositions[trType]; + auto indTrait = ind->getTrait(trType); + for (auto pos : positions) { + alleleOnChromA = indTrait->getAlleleValueAtLocus(0, pos); + if (trType == GENETIC_LOAD1 || trType == GENETIC_LOAD2 || trType == GENETIC_LOAD3 || trType == GENETIC_LOAD4 || trType == GENETIC_LOAD5) { + domCoefA = indTrait->getDomCoefAtLocus(0, pos); + } + else { + domCoefA = 0.0; + } + ofsGenes << yr << '\t' << gen << '\t' << indID << '\t' << to_string(trType) << '\t' << pos << '\t' << alleleOnChromA << '\t' << domCoefA; + if (isDiploid) { + alleleOnChromB = indTrait->getAlleleValueAtLocus(1, pos); + if (trType == GENETIC_LOAD1 || trType == GENETIC_LOAD2 || trType == GENETIC_LOAD3 || trType == GENETIC_LOAD4 || trType == GENETIC_LOAD5) { + domCoefB = indTrait->getDomCoefAtLocus(1, pos); + } + else { + domCoefB = 0.0; + } + ofsGenes << '\t' << alleleOnChromB << '\t' << domCoefB; + } + ofsGenes << endl; + } } } +} +// --------------------------------------------------------------------------- +// Extract all individuals of a population with certain characteristics based on age, stage and sex +// returns a set of pointers to the individuals +// --------------------------------------------------------------------------- +std::vector Population::getIndsWithCharacteristics( // Select a set of individuals with specified characteristics + int min_age, // min age (0 if not set) + int max_age, // max age (max age if not set) + int stage, // stage + int sex //sex +){ + // get all suitable individuals based on settings + std::vector filteredInds; + int ninds = (int)inds.size(); +#if RS_RCPP + Rcpp::Rcout << "Number individuals in cell: " << ninds << endl; +#endif + if (ninds > 0) { + // copy ALL individuals to filteredInds + for (int i = 0; i < ninds; i++) { + filteredInds.push_back(inds[i]); + } + + // check status of inividuals + for (int i = 0; i < ninds; i++) { + if (inds[i] != NULL && inds[i]->getStats().status != 0 && inds[i]->getStats().status != 4 && inds[i]->getStats().status != 5){ // only accept individuals with status 0, 4 or 5 (not in transfer phase + not dead + not already translocated) + // Rcpp::Rcout << "Status: " << inds[i]->getStats().status << endl; + filteredInds[i] = NULL; // set it to NULL + } + } + + // Check minimal age + if (min_age!=-9){ + // loop over all number of individuals in cell + for (int i = 0; i < ninds; i++) { + if (filteredInds[i] != NULL && inds[i]->getStats().age < min_age){ // if not already NULL + age too young + filteredInds[i] = NULL; // set it to NULL + } + } + } + // check max age + if (max_age!=-9){ + // loop over all number of individuals in cell + for (int i = 0; i < ninds; i++) { + if (filteredInds[i] != NULL && inds[i]->getStats().age < max_age){// if not already NULL + age too old + if (filteredInds[i] != NULL) filteredInds[i] = NULL; // set it to NULL if not already NULL + } + } + } + // check stage + if (stage!=-9){ + // loop over all number of individuals in cell + for (int i = 0; i < ninds; i++) { + if (filteredInds[i] != NULL && inds[i]->getStats().stage != stage){// if not already NULL + stage not correct + if (filteredInds[i] != NULL) filteredInds[i] = NULL; // set it to NULL if not already NULL + } + } + } + // check sex + if (sex!=-9){ + // loop over all number of individuals in cell + for (int i = 0; i < ninds; i++) { + if (filteredInds[i] != NULL && inds[i]->getStats().sex != sex){// if not already NULL + sex not correct + if (filteredInds[i] != NULL) filteredInds[i] = NULL; // set it to NULL if not already NULL + } + } + } + } else { +#if RS_RCPP + Rcpp::Rcout << "No individuals in source patch" << endl; +#endif + return filteredInds; + } + int nfiltered = 0; + for ( auto filtered : filteredInds){ + if (filtered != NULL) nfiltered++; + } + + // loop over iterator of filteredInds and remove NULL values + filteredInds.erase(std::remove(filteredInds.begin(), filteredInds.end(), nullptr), filteredInds.end()); + + return filteredInds; +}; +// --------------------------------------------------------------------------- +// Clean the sampled individuals +// --------------------------------------------------------------------------- +void Population::cleanSampledInds(Individual* pInd // Return a set of individuals with specified characteristics +){ + // find inds[j] and remove it from sampledInds + sampledInds.erase(std::remove(sampledInds.begin(), sampledInds.end(), pInd), sampledInds.end()); +}; +// --------------------------------------------------------------------------- +// Sample N individuals from the population with a given set of characteristics +// --------------------------------------------------------------------------- +int Population::sampleIndividuals( // Select a set of individuals with specified characteristics +// void Population::sampleIndividuals( // Select a set of individuals with specified characteristics + int nb, // number of individuals to sample + int min_age, // min age (0 if not set) + int max_age, // max age (max age if not set) + int stage, // stage + int sex //sex + ){ + if(sampledInds.size() > 0) sampledInds.clear(); // clear old vector + auto rng = pRandom->getRNG(); // random number for sampling from suitable individuals + + // get individuals with the characteristics + std::vector filtered; + filtered = getIndsWithCharacteristics(min_age, max_age, stage, sex); +#if RS_RCPP + Rcpp::Rcout << "Number of individuals with fitting characteristics: " << filtered.size() << endl; +#endif + if (filtered.size() <= nb) + // Sample all individuals in selected stages + sampledInds = filtered; + else { + vector out; + // Sample n individuals across filtered individuals + std::sample(filtered.begin(), filtered.end(), std::back_inserter(out), nb, rng); + std::copy(out.begin(), out.end(), std::inserter(sampledInds, sampledInds.end())); + } + + int nb_sampled = 0; + if (sampledInds.size() > 0) { + for (int i = 0; i < (int)sampledInds.size(); i++) { + if (sampledInds[i] != NULL) nb_sampled++; + } + } + return nb_sampled; +} +// --------------------------------------------------------------------------- +// catch individuals according to catching rate +// --------------------------------------------------------------------------- +Individual* Population::catchIndividual( // Translocate a set of individuals with specified characteristics + double catching_rate, + int j +){ + Individual* catched; + int id = inds[j]->getId(); + // If individual is part of the sampledInds vector: + if (std::find(sampledInds.begin(), sampledInds.end(), inds[j]) != std::end(sampledInds)){ + // try to catch individual +#if RS_RCPP + if(catching_rate > 1) Rcpp::Rcout << "Catching rate: " << catching_rate << std::endl; +#endif + if (pRandom->Bernoulli(catching_rate)){ + indStats indstat = inds[j]->getStats(); + catched = inds[j]; + // remove individual from source patch + inds[j] = 0; + nInds[indstat.stage][indstat.sex]--; + cleanSampledInds(catched); // clean vector of sampled individuals after the event + return catched; + }else { + cleanSampledInds(inds[j]); // clean vector of sampled individuals after the event + return NULL; + } + } else { + return NULL; + } } -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +bool Population::getSizeSampledInds( +){ + bool size = false; + if (sampledInds.size() > 0) size = true; + return size; +}; + //--------------------------------------------------------------------------- diff --git a/Population.h b/Population.h index 0b64779..dbf30e1 100644 --- a/Population.h +++ b/Population.h @@ -1,25 +1,25 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 @@ -34,14 +34,14 @@ 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. + 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. + 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 + Last updated: 25 June 2021 by Steve Palmer ------------------------------------------------------------------------------*/ @@ -58,6 +58,7 @@ using namespace std; #include "Landscape.h" #include "Patch.h" #include "Cell.h" +#include "NeutralStatsManager.h" #ifdef _OPENMP #include @@ -72,38 +73,43 @@ struct popStats { struct disperser { Individual *pInd; Cell *pCell; bool yes; }; +struct zombie { + Individual* pInd; +}; 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 + int ninds[gMaxNbSexes]; // no. of individuals + double sumD0[gMaxNbSexes]; // sum of maximum emigration probability + double ssqD0[gMaxNbSexes]; // sum of squares of maximum emigration probability + double sumAlpha[gMaxNbSexes]; // sum of slope of emigration dens-dep reaction norm + double ssqAlpha[gMaxNbSexes]; // sum of squares of slope of emigration den-dep reaction norm + double sumBeta[gMaxNbSexes]; // sum of inflection point of emigration reaction norm + double ssqBeta[gMaxNbSexes]; // sum of squares of inflection point of emigration reaction norm + double sumDist1[gMaxNbSexes]; // sum of kernel I mean + double ssqDist1[gMaxNbSexes]; // sum of squares of kernel I mean + double sumDist2[gMaxNbSexes]; // sum of kernel II mean + double ssqDist2[gMaxNbSexes]; // sum of squares of kernel II mean + double sumProp1[gMaxNbSexes]; // sum of propn using kernel I + double ssqProp1[gMaxNbSexes]; // sum of squares of propn using kernel I + double sumDP[gMaxNbSexes]; // sum of SMS directional persistence + double ssqDP[gMaxNbSexes]; // sum of squares of SMS directional persistence + double sumGB[gMaxNbSexes]; // sum of SMS goal bias + double ssqGB[gMaxNbSexes]; // sum of squares of SMS goal bias + double sumAlphaDB[gMaxNbSexes]; // sum of SMS dispersal bias decay rate + double ssqAlphaDB[gMaxNbSexes]; // sum of squares of SMS dispersal bias decay rate + double sumBetaDB[gMaxNbSexes]; // sum of SMS dispersal bias decay infl. pt. + double ssqBetaDB[gMaxNbSexes]; // sum of squares of SMS dispersal bias decay infl. pt. + double sumStepL[gMaxNbSexes]; // sum of CRW step length + double ssqStepL[gMaxNbSexes]; // sum of squares of CRW step length + double sumRho[gMaxNbSexes]; // sum of CRW correlation coefficient + double ssqRho[gMaxNbSexes]; // sum of squares of CRW correlation coefficient + double sumS0[gMaxNbSexes]; // sum of maximum settlement probability + double ssqS0[gMaxNbSexes]; // sum of squares of maximum settlement probability + double sumAlphaS[gMaxNbSexes]; // sum of slope of settlement den-dep reaction norm + double ssqAlphaS[gMaxNbSexes]; // sum of squares of slope of settlement den-dep reaction norm + double sumBetaS[gMaxNbSexes]; // sum of inflection point of settlement reaction norm + double ssqBetaS[gMaxNbSexes]; // sum of squares of inflection point of settlement reaction norm + double sumGeneticFitness[gMaxNbSexes]; + double ssqGeneticFitness[gMaxNbSexes]; }; class Population { @@ -117,19 +123,20 @@ class Population { 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 + traitsums getIndTraitsSums(Species*); + popStats getStats( + std::vector ); + Species* getSpecies(void); + int getNbInds() const; + int getNbInds(int stg) const ; + int getNbInds(int stg, int sex) const; 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 + const int, // Landscape resolution + std::vector // local demographic scaling ); // Following reproduction of ALL species, add juveniles to the population void fledge(void); @@ -137,6 +144,10 @@ class Population { float // local carrying capacity ); void allEmigrate(void); // All individuals emigrate after patch destruction + // Remove an individual from the Population + Individual* extractIndividual( + int // index no. to the Individual in the inds vector + ); // 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 @@ -150,33 +161,14 @@ class Population { void recruit( // Add a specified individual to the population Individual* // pointer to Individual ); + Individual* sampleInd() const; + void sampleIndsWithoutReplacement(string n, const set& sampleStages); + int sampleSize() const; + vector getIndividualsInStage(int stage); void recruitMany( // Add specified individuals to the population std::vector& // vector of pointers to Individuals ); -#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( @@ -184,9 +176,10 @@ class Population { 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) + short, // option1: 0 - development only (when survival is annual) // 1 - development and survival // 2 - survival only (when survival is annual) + std::vector // local demographic scaling ); void survival1(void); // Apply survival changes to the population void ageIncrement(void); @@ -218,16 +211,56 @@ class Population { int, // generation int // Patch number ); - void outGenFinishReplicate(); // Close genetics file - void outGenStartReplicate( // Open genetics file and write header record - const int, // replicate - const int // landscape number + void outputGeneValues(ofstream& ofsGenes, const int& yr, const int& gen) const; + void clean(void); // Remove zero pointers to dead or dispersed individuals + + void updatePopNeutralTables(); + double getAlleleFrequency(int locus, int allele); + int getAlleleTally(int locus, int allele); + int getHeteroTally(int locus, int allele); + int countHeterozygoteLoci(); + vector countNbHeterozygotesEachLocus(); + double computeHs(); + std::vector getIndsWithCharacteristics( // Return a set of individuals with specified characteristics + int, // min age + int, // max age + int, // stage + int //sex ); - void outGenetics( // Write records to genetics file - const int, // replicate - const int // year + void cleanSampledInds( + Individual* // individual to remove from sampled individuals vector + ); // clean sampled individuals vector + + int sampleIndividuals( // Select a set of individuals with specified characteristics; return the number of individuals with those characteristics + // void sampleIndividuals( // Select a set of individuals with specified characteristics; return the number of individuals with those characteristics + int, //number of individuals to sample + int, // min age (0 if not set) + int, // max age (max age if not set) + int, // stage + int //sex ); - void clean(void); // Remove zero pointers to dead or dispersed individuals + + Individual* catchIndividual( + double, // catching rate + int + ); + + // void completeTranslocation( + // std::vector // catched individuals + // ); + + // void recruitTranslocated( + // Individual* + // ); + + bool getSizeSampledInds( + ); + +#ifdef UNIT_TESTS + // Testing only + void clearInds() { inds.clear(); } // empty inds vector to avoid deallocating individual is used separately in test + void shuffleInds() { shuffle(inds.begin(), inds.end(), pRandom->getRNG()); } +#endif // UNIT_TESTS private: short nStages; @@ -235,14 +268,19 @@ class Population { Species *pSpecies; // pointer to the species Patch *pPatch; // pointer to the patch #ifdef _OPENMP - std::atomic nInds[NSTAGES][NSEXES]; // no. of individuals in each stage/sex + std::atomic nInds[gMaxNbStages][gMaxNbSexes]; // no. of individuals in each stage/sex #else - int nInds[NSTAGES][NSEXES]; // no. of individuals in each stage/sex + int nInds[gMaxNbStages][gMaxNbSexes]; // no. of individuals in each stage/sex #endif // _OPENMP - std::vector inds; // all individuals in population except ... - std::vector juvs; // ... juveniles until reproduction of ALL species + vector inds; // all individuals in population except ... + vector juvs; // ... juveniles until reproduction of ALL species // has been completed + + vector sampledInds; + //std::vector sampledInds; // individuals with specified characteristics from translocation!!! + vector popNeutralCountTables; + void resetPopNeutralTables(); #ifdef _OPENMP std::mutex inds_mutex; #endif // _OPENMP @@ -256,10 +294,6 @@ extern paramInit *paramsInit; extern paramSim *paramsSim; extern RSrandom *pRandom; -#if RSDEBUG -extern ofstream DEBUGLOG; -#endif - //--------------------------------------------------------------------------- #endif diff --git a/QuantitativeTrait.h b/QuantitativeTrait.h new file mode 100644 index 0000000..53ae033 --- /dev/null +++ b/QuantitativeTrait.h @@ -0,0 +1,31 @@ +#ifndef TRAITTH +#define TRAITTH + +#include +#include +#include + +#include "Parameters.h" +#include "Allele.h" +#include "SpeciesTrait.h" + +using namespace std; + +// Base interface for all genetic traits +class QuantitativeTrait { +public: + virtual void mutate() = 0; + virtual unique_ptr clone() const = 0; //copies parameters (if not static) not gene seqeunces + virtual void inheritGenes(const bool&, QuantitativeTrait*, set const& , int ) = 0; + virtual int getNLoci() const = 0; + virtual float getMutationRate() const = 0; + virtual bool isInherited() const = 0; + virtual float getAlleleValueAtLocus(short chromosome, int i) const = 0; + virtual float getDomCoefAtLocus(short whichChromosome, int position) const = 0; + virtual float express() = 0; + virtual ~QuantitativeTrait() { } +}; + +extern RSrandom* pRandom; + +#endif \ No newline at end of file diff --git a/RSrandom.cpp b/RSrandom.cpp index 3eff662..b712f49 100644 --- a/RSrandom.cpp +++ b/RSrandom.cpp @@ -19,285 +19,219 @@ * --------------------------------------------------------------------------*/ - #include "RSrandom.h" #ifdef _OPENMP #include #endif // _OPENMP - -//--------------- 2.) New version of RSrandom.cpp - -#if !RS_RCPP - -#if RSDEBUG +#ifndef NDEBUG #include "Parameters.h" extern paramSim* paramsSim; -// ofstream RSRANDOMLOG; #endif +#if RS_RCPP +std::uint32_t RS_random_seed = 0; +#else int RS_random_seed = 0; +#endif // C'tor -RSrandom::RSrandom() +#if RS_RCPP +// if parameter seed is negative, a random seed will be generated, else it is used as seed +RSrandom::RSrandom(std::int64_t seed) { -#if RSDEBUG - // fixed seed - RS_random_seed = 666; + // 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 - // 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); + std::random_device device; + random_seed[2] = device(); #endif -#endif // RSDEBUG + } + else { + // fixed seed + random_seed[2] = seed; + } -#if BATCH && RSDEBUG - DEBUGLOG << "RSrandom::RSrandom(): RS_random_seed=" << RS_random_seed << endl; -#endif // RSDEBUG + RS_random_seed = random_seed[2]; + + // set up Mersenne Twister random number generator with seed sequence + std::seed_seq seq(random_seed.begin(), random_seed.end()); - // set up Mersenne Twister RNG #ifdef _OPENMP - int nb_generators = omp_get_max_threads(); - gens.reserve(nb_generators); - for (int i = 0; i < nb_generators; i++) - gens.emplace_back(RS_random_seed+i); -#else // _OPENMP - gens.reserve(1); - gens.emplace_back(RS_random_seed); + int nb_generators = omp_get_max_threads(); + gens.reserve(nb_generators); + for (int i = 0; i < nb_generators; i++) + gens.emplace_back(seq); +#else + gens.reserve(1); + gens.emplace_back(seq); #endif // _OPENMP - // 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); + + // 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); } +#else +RSrandom::RSrandom() { -RSrandom::~RSrandom(void) -{ +#ifndef NDEBUG + // fixed seed + RS_random_seed = 11011; +#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 // NDEBUG + + // set up Mersenne Twister RNG +#ifdef _OPENMP + int nb_generators = omp_get_max_threads(); + gens.reserve(nb_generators); + for (int i = 0; i < nb_generators; i++) + gens.emplace_back(RS_random_seed + i); +#else + gens.reserve(1); + gens.emplace_back(RS_random_seed); +#endif // _OPENMP + + // 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); +} +#endif // RS_RCPP + +RSrandom::~RSrandom(void) { gens.clear(); - if(pRandom01 != 0) - delete pRandom01; - if(pNormal != 0) - delete pNormal; + if (pRandom01 != 0) + delete pRandom01; + if (pNormal != 0) + delete pNormal; } -mt19937 RSrandom::getRNG(void) -{ +mt19937 RSrandom::getRNG() { #ifdef _OPENMP - return gens[omp_get_thread_num() % gens.size()]; -#else // _OPENMP - return gens[0]; + return gens[omp_get_thread_num() % gens.size()]; +#else + return gens[0]; #endif // _OPENMP } -double RSrandom::Random(void) -{ +double RSrandom::Random() { // return random number between 0 and 1 #ifdef _OPENMP - return pRandom01->operator()(gens[omp_get_thread_num() % gens.size()]); -#else // _OPENMP - return pRandom01->operator()(gens[0]); + return pRandom01->operator()(gens[omp_get_thread_num() % gens.size()]); +#else + return pRandom01->operator()(gens[0]); #endif // _OPENMP } -int RSrandom::IRandom(int min, int max) -{ +int RSrandom::IRandom(int min, int max) { // return random integer in the interval min <= x <= max + if (min == max) + return min; + uniform_int_distribution unif(min, max); #ifdef _OPENMP - return unif(gens[omp_get_thread_num() % gens.size()]); -#else // _OPENMP - return unif(gens[0]); + return unif(gens[omp_get_thread_num() % gens.size()]); +#else + return unif(gens[0]); #endif // _OPENMP } -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"); +float RSrandom::FRandom(float min, float max) { + if (min == max) return min; + // return random double in the interval min <= x <= max + uniform_real_distribution unif(min, max); + +#ifdef _OPENMP + return unif(gens[omp_get_thread_num() % gens.size()]); +#else + return unif(gens[0]); +#endif +} + +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) -{ +int RSrandom::Binomial(const int& n, const double& p) { + binomial_distribution binom(n, p); #ifdef _OPENMP - return mean + sd * pNormal->operator()(gens[omp_get_thread_num() % gens.size()]); -#else // _OPENMP - return mean + sd * pNormal->operator()(gens[0]); + return binom(gens[omp_get_thread_num() % gens.size()]); +#else + return binom(gens[0]); +#endif +} + +double RSrandom::Normal(double mean, double sd) { +#ifdef _OPENMP + return mean + sd * pNormal->operator()(gens[omp_get_thread_num() % gens.size()]); +#else + return mean + sd * pNormal->operator()(gens[0]); #endif // _OPENMP } -int RSrandom::Poisson(double mean) -{ +int RSrandom::Poisson(double mean) { poisson_distribution poiss(mean); #ifdef _OPENMP - return poiss(gens[omp_get_thread_num() % gens.size()]); -#else // _OPENMP - return poiss(gens[0]); + return poiss(gens[omp_get_thread_num() % gens.size()]); +#else + return poiss(gens[0]); #endif // _OPENMP } +double RSrandom::Gamma(double shape, double scale) { //scale = mean/shape, shape must be positive and scale can be positive or negative -//-------------------------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------------------------- - -#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()); + gamma_distribution<> gamma(shape, abs(scale)); #ifdef _OPENMP - int nb_generators = omp_get_max_threads(); - gens.reserve(nb_generators); - for (int i = 0; i < nb_generators; i++) - gens.emplace_back(seq); + double x = poiss(gens[omp_get_thread_num() % gens.size()]); #else - gens.reserve(1); - gens.emplace_back(seq); + double x = gamma(gens[0]); #endif // _OPENMP + if (scale < 0) x = -x; + return x; +} - // 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; - } - - - */ +double RSrandom::NegExp(double mean) { + double r1 = 0.0000001 + this->Random() * (1.0 - 0.0000001); + double x = (-1.0 * mean) * log(r1); + return x; +} -#endif // RS_RCPP +void RSrandom::fixNewSeed(int seed) { +#ifdef _OPENMP + for (int i = 0; i < omp_get_max_threads(); i++) + gens[i].seed(seed + i); +#else + gens[0].seed(seed); +#endif // _OPENMP +} +//-------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- -#if RSDEBUG && !RS_RCPP +#ifdef UNIT_TESTS +#if !RS_RCPP void testRSrandom() { { @@ -319,5 +253,6 @@ int RSrandom::Poisson(double mean) assert(bern_trial == 0 || bern_trial == 1); } } -#endif // RSDEBUG +#endif +#endif // UNIT_TESTS //--------------------------------------------------------------------------- diff --git a/RSrandom.h b/RSrandom.h index 6f3db35..ac4ee31 100644 --- a/RSrandom.h +++ b/RSrandom.h @@ -1,37 +1,37 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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 + RangeShifter v2.0 RSrandom -Last updated: 12 January 2021 by Steve Palmer + 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 @@ -39,90 +39,55 @@ Last updated: 12 January 2021 by Steve Palmer #include #include #include -#include +#include +#include +#include #include "Utils.h" +#if !LINUX_CLUSTER +#include +#endif using namespace std; -#if RSDEBUG -extern ofstream DEBUGLOG; +#if RS_RCPP +typedef uint32_t seed_t; +#else +typedef int seed_t; #endif +class RSrandom +{ + +public: #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); + RSrandom(void); +#else + RSrandom(std::int64_t); // if int is negative, a random seed will be generated, else it is used as seed +#endif + ~RSrandom(void); + double Random(void); + int IRandom(int, int); + float FRandom(float, float); + int Bernoulli(double); + int Binomial(const int& n, const double& p); + double Normal(double, double); + double Gamma(double, double); + double NegExp(double); + int Poisson(double); + mt19937 getRNG(void); + void fixNewSeed(int); + seed_t getSeed() const { return RS_random_seed; }; private: + seed_t RS_random_seed; std::vector gens; 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 +#ifdef UNIT_TESTS void testRSrandom(); -#endif // RSDEBUG +#endif // UNIT_TESTS //--------------------------------------------------------------------------- diff --git a/RandomCheck.cpp b/RandomCheck.cpp deleted file mode 100644 index e18aeaa..0000000 --- a/RandomCheck.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/*---------------------------------------------------------------------------- - * - * 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/RandomCheck.h b/RandomCheck.h deleted file mode 100644 index b4992d2..0000000 --- a/RandomCheck.h +++ /dev/null @@ -1,40 +0,0 @@ -/*---------------------------------------------------------------------------- - * - * 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/Species.cpp b/Species.cpp index fadc395..0b344fb 100644 --- a/Species.cpp +++ b/Species.cpp @@ -25,115 +25,120 @@ #include "Species.h" //--------------------------------------------------------------------------- -Species::Species(void) +Species::Species(const short& repro, const short& nbRepSeasons, const bool& hasStgStruct, const short& nStg, const bool& usesMovtProc, const short& movementType) : + repType{repro}, + repSeasons{nbRepSeasons}, + stageStruct{hasStgStruct}, + nStages{nStg}, + usesMovtProcess{usesMovtProc}, + moveType{movementType} { // 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; + propMales = 0.0; + harem = 1.0; + bc = 1.0; + lambda = 1.5; + probRep = 1.0; + repInterval = 0; + maxAge = 1000; + survival = 1; + fecDens = false; + fecStageDens = false; + devDens = false; + devStageDens = false; + survDens = false; + survStageDens = false; + fecSpatial = false; + survSpatial = false; + devSpatial = 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; + for (int i = 0; i < gMaxNbStages; i++) { + for (int j = 0; j < gMaxNbSexes; j++) { + fec[i][j] = 0.0; + dev[i][j] = 0.0; + surv[i][j] = 0.0; minAge[i][j] = 0; + fecLayer[i][j] = -9; + devLayer[i][j] = -9; + survLayer[i][j] = -9; } } 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; + ddwtFec = ddwtDev = ddwtSurv = 0; + ddwtFecDim = ddwtDevDim = ddwtSurvDim = 0; + habK = 0; + habDimK = 0; + minRK = 1.0; + maxRK = 2.0; // initialise emigration parameters - densDepEmig = false; stgDepEmig = false; sexDepEmig = false; indVarEmig = false; + 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 i = 0; i < gMaxNbStages; i++) { + for (int j = 0; j < gMaxNbSexes; 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; + 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; + for (int i = 0; i < gMaxNbStages; i++) { + for (int j = 0; j < gMaxNbSexes; j++) { + meanDist1[i][j] = 100.0f; + meanDist2[i][j] = 1000.0f; + probKern1[i][j] = 0.99f; + } + } + 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; habDimTrfr = 0; - straigtenPath = false; + straightenPath = false; fullKernel = false; - // initialise settlement parameters - stgDepSett = false; sexDepSett = false; indVarSett = false; - - 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; minSteps[i][j] = 0; maxSteps[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 + stgDepSett = false; + sexDepSett = false; + indVarSett = false; + for (int i = 0; i < gMaxNbStages; i++) { + for (int j = 0; j < gMaxNbSexes; j++) { + densDepSett[i][j] = false; + wait[i][j] = false; + go2nbrLocn[i][j] = false; + findMate[i][j] = false; + maxStepsYr[i][j] = 99999999; + minSteps[i][j] = 0; + maxSteps[i][j] = 99999999; + s0[i][j] = 1.0; + alphaS[i][j] = 0.0; + betaS[i][j] = 1.0; + } + } + // initialise attribute defaults spNum = 0; - + resetGeneticParameters(); } Species::~Species() { @@ -144,9 +149,6 @@ Species::~Species() { 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; } @@ -157,6 +159,8 @@ short Species::getSpNum(void) { return spNum; } void Species::setDemogr(const demogrParams d) { if (d.repType >= 0 && d.repType <= 2) repType = d.repType; + if (d.repType == 1 || d.repType == 2) diploid = true; + else diploid = false; if (d.repSeasons >= 1) repSeasons = d.repSeasons; stageStruct = d.stageStruct; if (d.propMales > 0.0 && d.propMales < 1.0) propMales = d.propMales; @@ -165,7 +169,7 @@ void Species::setDemogr(const demogrParams d) { if (d.lambda > 0.0) lambda = d.lambda; } -demogrParams Species::getDemogr(void) { +demogrParams Species::getDemogrParams(void) { demogrParams d; d.repType = repType; d.repSeasons = repSeasons; @@ -222,13 +226,16 @@ void Species::setStage(const stageParams s) { 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; + 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 Species::getStageParams(void) { stageParams s; s.nStages = nStages; s.repInterval = repInterval; s.maxAge = maxAge; s.survival = survival; s.probRep = probRep; @@ -241,12 +248,12 @@ stageParams Species::getStage(void) { 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) + if (stg > 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && f >= 0) fec[stg][sex] = f; } float Species::getFec(short stg, short sex) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) return fec[stg][sex]; else return 0.0; } @@ -254,7 +261,7 @@ float Species::getFec(short stg, short sex) { float Species::getMaxFec(void) { float maxfec = 0.0; if (stageStruct) { - for (int stg = 1; stg < NSTAGES; stg++) { + for (int stg = 1; stg < gMaxNbStages; stg++) { if (fec[stg][0] > maxfec) maxfec = fec[stg][0]; } } @@ -263,35 +270,80 @@ float Species::getMaxFec(void) { } void Species::setDev(short stg, short sex, float d) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES && d >= 0) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && d >= 0) dev[stg][sex] = d; } float Species::getDev(short stg, short sex) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) 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) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && s >= 0) surv[stg][sex] = s; } float Species::getSurv(short stg, short sex) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) return surv[stg][sex]; else return 0.0; } +void Species::setFecSpatial(bool spat) { + fecSpatial = spat; +} + +void Species::setDevSpatial(bool spat) { + devSpatial = spat; +} + +void Species::setSurvSpatial(bool spat) { + survSpatial = spat; +} + +void Species::setFecLayer(short stg,short sex,short l) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && l >= 0) + fecLayer[stg][sex] = l; +} + +short Species::getFecLayer(short stg,short sex) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) + return fecLayer[stg][sex]; +else return -9; +} + +void Species::setDevLayer(short stg,short sex,short l) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && l >= 0) + devLayer[stg][sex] = l; +} + +short Species::getDevLayer(short stg,short sex) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) + return devLayer[stg][sex]; +else return -9; +} + +void Species::setSurvLayer(short stg,short sex,short l) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && l >= 0) + survLayer[stg][sex] = l; +} + +short Species::getSurvLayer(short stg,short sex) { +if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) + return survLayer[stg][sex]; +else return -9; +} + 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) + if (stg > 1 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes && age >= 0) minAge[stg][sex] = age; } short Species::getMinAge(short stg, short sex) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) return minAge[stg][sex]; else return 0; } @@ -308,7 +360,7 @@ densDepParams Species::getDensDep(void) { } void Species::createDDwtFec(short mSize) { - if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (mSize >= 0 && mSize < (gMaxNbStages * gMaxNbSexes)) { if (ddwtFec != 0) deleteDDwtFec(); ddwtFecDim = mSize; ddwtFec = new float* [mSize]; @@ -340,7 +392,7 @@ void Species::deleteDDwtFec(void) { } void Species::createDDwtDev(short mSize) { - if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (mSize >= 0 && mSize < (gMaxNbStages * gMaxNbSexes)) { if (ddwtDev != 0) deleteDDwtDev(); ddwtDevDim = mSize; ddwtDev = new float* [mSize]; @@ -372,7 +424,7 @@ void Species::deleteDDwtDev(void) { } void Species::createDDwtSurv(short mSize) { - if (mSize >= 0 && mSize < (NSTAGES * NSEXES)) { + if (mSize >= 0 && mSize < (gMaxNbStages * gMaxNbSexes)) { if (ddwtSurv != 0) deleteDDwtSurv(); ddwtSurvDim = mSize; ddwtSurv = new float* [mSize]; @@ -402,8 +454,7 @@ void Species::deleteDDwtSurv(void) { delete[] ddwtSurv; ddwtSurv = 0; } } - -// Functions to handle min/max R or K (under environmental stochasticity) +//void Species::setMinMax(float min,float max) { void Species::setMinMax(float min, float max) { if (min >= 0.0 && max > min) { minRK = min; maxRK = max; @@ -416,487 +467,148 @@ float Species::getMinMax(short opt) { //--------------------------------------------------------------------------- -// 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::areMutationsOn(void) { + return mutationsOn; } -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; - } +void Species::resetGeneticParameters() { + mutationsOn = true; + nbGeneticFitnessTraits = 0; + genomeSize = -9999; + recombinationRate = -9999; + nPatchesToSample = 0; + nIndsToSample = ""; + chromosomeEnds.clear(); + samplePatchList.clear(); } -int Species::getNLoci(const short chr) { - if (chr >= 0 && chr < nChromosomes) return nLoci[chr]; - else return 0; +bool Species::isDiploid() const { + return diploid; } -void Species::deleteLoci(void) { - if (nLoci != NULL) { delete[] nLoci; nLoci = NULL; } +int Species::incrNbGenLoadTraits() +{ + nbGeneticFitnessTraits++; + return nbGeneticFitnessTraits; } -// 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; +int Species::getNbGenLoadTraits() const +{ + return nbGeneticFitnessTraits; } -bool Species::has1ChromPerTrait(void) { return trait1Chromosome; } +void Species::addTrait(TraitType traitType, const SpeciesTrait& trait) { -// Set trait attributes for the species -void Species::setTraits(void) { + TraitType traitT = traitType; + // hack to deal with multiple genetic load traits, could be handled better + if (traitType == GENETIC_LOAD) { + int n = incrNbGenLoadTraits(); - emigTrait[0] = 0; emigTrait[1] = 0; - movtTrait[0] = 0; movtTrait[1] = 0; - settTrait[0] = 0; settTrait[1] = 0; - nTraits = 0; - - if (indVarEmig) { - if (sexDepEmig) { - if (densDepEmig) nTraits += 6; else nTraits += 2; + switch (n) { + case 1: + { + traitT = GENETIC_LOAD1; + break; } - else { - if (densDepEmig) nTraits += 3; else nTraits += 1; + case 2: + { + traitT = GENETIC_LOAD2; + break; } - 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(); -} - -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"; - } + case 3: + { + traitT = GENETIC_LOAD3; + break; } - } - - 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"; - } + case 4: + { + traitT = GENETIC_LOAD4; + break; } - 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"; - } - } + case 5: + { + traitT = GENETIC_LOAD5; + break; } - } - - 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"; + default: + { + cout << endl << ("Too many genetic load traits in Traits file, max = 5 \n"); + break; } - else { - traitnames[trait++] = "s0"; - traitnames[trait++] = "alphaS"; - traitnames[trait++] = "betaS"; } } + spTraitTable.emplace(traitT, make_unique(trait)); } -void Species::deleteTraitNames(void) { - if (traitnames != NULL) { - delete[] traitnames; - traitnames = NULL; - } +SpeciesTrait* Species::getSpTrait(TraitType trait) const { + return spTraitTable.find(trait)->second.get(); } -string Species::getTraitName(const int trait) { - string name = "not used"; - if (traitnames != NULL) { - if (trait >= 0 && trait < nTraits) { - name = traitnames[trait]; - } - } - return name; +void Species::clearTraitTable() { + spTraitTable.clear(); } -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; - } +set Species::getTraitTypes() { + auto kv = std::ranges::views::keys(spTraitTable); + set keys{ kv.begin(), kv.end() }; + return keys; } -int Species::getNTraitMaps(void) { - if (traitdata == NULL) return 0; - else return traitdata->nTraitMaps; +int Species::getNTraits() const { + return static_cast(spTraitTable.size()); } -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::getNPositionsForTrait(const TraitType trait) const { + return this->getSpTrait(trait)->getPositionsSize(); } -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; +int Species::getGenomeSize() const { + return genomeSize; } -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; - } +float Species::getRecombinationRate() const { + return recombinationRate; } -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; +set Species::getChromosomeEnds() const { + return chromosomeEnds; } //--------------------------------------------------------------------------- // Emigration functions - -void Species::setEmig(const emigRules e) { - densDepEmig = e.densDep; stgDepEmig = e.stgDep; sexDepEmig = e.sexDep; +void Species::setEmigRules(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 Species::getEmigRules(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]; + e.densDep = densDepEmig; + e.stgDep = stgDepEmig; + e.sexDep = sexDepEmig; + e.indVar = indVarEmig; + e.emigStage = emigStage; return e; } -void Species::setEmigTraits(const short stg, const short sex, const emigTraits e) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { +void Species::setSpEmigTraits(const short stg, const short sex, const emigTraits e) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { if (e.d0 >= 0.0 && e.d0 <= 1.0) d0[stg][sex] = e.d0; - alphaEmig[stg][sex] = e.alpha; betaEmig[stg][sex] = e.beta; + alphaEmig[stg][sex] = e.alpha; + betaEmig[stg][sex] = e.beta; } } -emigTraits Species::getEmigTraits(short stg, short sex) { +emigTraits Species::getSpEmigTraits(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]; + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { + e.d0 = d0[stg][sex]; + e.alpha = alphaEmig[stg][sex]; + e.beta = betaEmig[stg][sex]; } else { e.d0 = e.alpha = e.beta = 0.0; @@ -904,8 +616,8 @@ emigTraits Species::getEmigTraits(short stg, short sex) { return e; } -float Species::getEmigD0(short stg, short sex) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { +float Species::getSpEmigD0(short stg, short sex) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { return d0[stg][sex]; } else { @@ -913,70 +625,29 @@ float Species::getEmigD0(short stg, short sex) { } } -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; +void Species::setTrfrRules(const transferRules t) { + usesMovtProcess = t.usesMovtProc; + stgDepTrfr = t.stgDep; + sexDepTrfr = t.sexDep; + distMort = t.distMort; + indVarTrfr = t.indVar; twinKern = t.twinKern; habMort = t.habMort; - moveType = t.moveType; costMap = t.costMap; + moveType = t.moveType; + costMap = t.costMap; } -trfrRules Species::getTrfr(void) { - trfrRules t; - t.moveModel = moveModel; t.stgDep = stgDepTrfr; t.sexDep = sexDepTrfr; +transferRules Species::getTransferRules(void) { + transferRules t; + t.usesMovtProc = usesMovtProcess; 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; } @@ -986,19 +657,19 @@ void Species::setFullKernel(bool k) { bool Species::useFullKernel(void) { return fullKernel; } -void Species::setKernTraits(const short stg, const short sex, - const trfrKernTraits k, const int resol) +void Species::setSpKernTraits(const short stg, const short sex, + const trfrKernelParams k, const int resol) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { 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; + 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) { +trfrKernelParams Species::getSpKernTraits(short stg, short sex) { + trfrKernelParams k; + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { k.meanDist1 = meanDist1[stg][sex]; k.meanDist2 = meanDist2[stg][sex]; k.probKern1 = probKern1[stg][sex]; @@ -1010,7 +681,7 @@ trfrKernTraits Species::getKernTraits(short stg, short sex) { } void Species::setMortParams(const trfrMortParams m) { - if (m.fixedMort >= 0.0 && m.fixedMort < 1.0) fixedMort = m.fixedMort; + if (m.fixedMort >= 0.0 && m.fixedMort <= 1.0) fixedMort = m.fixedMort; mortAlpha = m.mortAlpha; mortBeta = m.mortBeta; } @@ -1021,7 +692,7 @@ trfrMortParams Species::getMortParams(void) { return m; } -void Species::setMovtTraits(const trfrMovtTraits m) { +void Species::setSpMovtTraits(const trfrMovtParams 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; @@ -1030,155 +701,35 @@ void Species::setMovtTraits(const trfrMovtTraits m) { 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.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; + if (m.rho > 0.0 && m.rho <= 1.0) rho = m.rho; + straightenPath = m.straightenPath; } -trfrMovtTraits Species::getMovtTraits(void) { - trfrMovtTraits m; +trfrMovtParams Species::getSpMovtTraits(void) { + trfrMovtParams 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 Species::getSpCRWTraits(void) { trfrCRWTraits m; m.stepMort = stepMort; m.stepLength = stepLength; m.rho = rho; - m.straigtenPath = straigtenPath; + m.straightenPath = straightenPath; return m; } -trfrSMSTraits Species::getSMSTraits(void) { +trfrSMSTraits Species::getSpSMSTraits(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; - } + m.straightenPath = straightenPath; 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) { @@ -1237,12 +788,11 @@ void Species::setSettle(const settleType s) { 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) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { densDepSett[stg][sex] = s.densDep; wait[stg][sex] = s.wait; go2nbrLocn[stg][sex] = s.go2nbrLocn; findMate[stg][sex] = s.findMate; } @@ -1254,7 +804,7 @@ settleRules Species::getSettRules(short stg, short sex) { s.findMate = false; s.go2nbrLocn = false; s.wait = false; - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { s.densDep = densDepSett[stg][sex]; s.wait = wait[stg][sex]; s.go2nbrLocn = go2nbrLocn[stg][sex]; s.findMate = findMate[stg][sex]; } @@ -1262,7 +812,7 @@ settleRules Species::getSettRules(short stg, short sex) { } void Species::setSteps(const short stg, const short sex, const settleSteps s) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { if (s.maxStepsYr >= 1) maxStepsYr[stg][sex] = s.maxStepsYr; else maxStepsYr[stg][sex] = 99999999; if (s.minSteps >= 0) minSteps[stg][sex] = s.minSteps; @@ -1274,7 +824,7 @@ void Species::setSteps(const short stg, const short sex, const settleSteps s) { settleSteps Species::getSteps(short stg, short sex) { settleSteps s; - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { s.maxStepsYr = maxStepsYr[stg][sex]; s.minSteps = minSteps[stg][sex]; s.maxSteps = maxSteps[stg][sex]; @@ -1287,69 +837,89 @@ settleSteps Species::getSteps(short stg, short sex) { return s; } -void Species::setSettTraits(const short stg, const short sex, const settleTraits dd) { - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { +void Species::setSpSettTraits(const short stg, const short sex, const settleTraits dd) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { 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 Species::getSpSettTraits(short stg, short sex) { settleTraits dd; - if (stg >= 0 && stg < NSTAGES && sex >= 0 && sex < NSEXES) { + if (stg >= 0 && stg < gMaxNbStages && sex >= 0 && sex < gMaxNbSexes) { 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; - } - } +void Species::setGeneticParameters(const std::set& chromosomeEnds, const int genomeSize, const float recombinationRate, + const std::set& samplePatchList, const string nIndsToSample, const std::set& stagesToSampleFrom, int nPatchesToSampleFrom) +{ + this->genomeSize = genomeSize; + this->chromosomeEnds = chromosomeEnds; + this->recombinationRate = recombinationRate; + this->samplePatchList = samplePatchList; + this->nPatchesToSample = nPatchesToSampleFrom; + this->nIndsToSample = nIndsToSample; + this->stagesToSampleFrom = stagesToSampleFrom; } -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; +// only called for cell based landscape +void Species::setSamplePatchList(const set& samplePatchList) { + this->samplePatchList = samplePatchList; } -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; +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#ifdef UNIT_TESTS +// For testing purposes only + +Species* createDefaultSpecies() { + short repType = 0; + short repSeasons = 1; + bool stagestruct = false; + int nStages = 2; + bool usesMovtProc = false; + short movtType = 1; + Species* pSpecies = new Species(repType, repSeasons, stagestruct, nStages, usesMovtProc, movtType); + return pSpecies; } -settScales Species::getSettScales(void) { - settScales s; - s.s0Scale = s0Scale; s.alphaSScale = alphaSScale; s.betaSScale = betaSScale; - return s; +// Set kernel parameters, but ignore constraints on values +// Used to test dispersal with values < resolution +void Species::overrideKernels(const short stg, const short sex, + const trfrKernelParams k) +{ + meanDist1[stg][sex] = k.meanDist1; + meanDist2[stg][sex] = k.meanDist2; + probKern1[stg][sex] = k.probKern1; } -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- +demogrParams createDefaultHaploidDemogrParams() { + demogrParams d; + d.repType = 0; + d.repSeasons = 1; + d.stageStruct = false; + d.propMales = 0.0; + d.harem = 1.0; + d.bc = 1.0; + d.lambda = 2.0; + return d; +} + +demogrParams createDefaultDiploidDemogrParams() { + demogrParams d; + d.repType = 1; + d.repSeasons = 1; + d.stageStruct = false; + d.propMales = 0.5; + d.harem = 1.0; + d.bc = 1.0; + d.lambda = 2.0; + return d; +} +#endif // UNIT_TESTS diff --git a/Species.h b/Species.h index 4a288bd..e214241 100644 --- a/Species.h +++ b/Species.h @@ -45,140 +45,166 @@ #ifndef SpeciesH #define SpeciesH +#include +#include +#include +#include + #include "Parameters.h" +#include "SpeciesTrait.h" +#include "QuantitativeTrait.h" + +class SpeciesTrait; -// structures for demographic parameters + // structures for demographic parameters struct demogrParams { short repType; short repSeasons; - float propMales; float harem; float bc; float lambda; + float propMales; + float harem; + float bc; + float lambda; bool stageStruct; }; struct stageParams { - short nStages; short repInterval; short maxAge; short survival; + short nStages; + short repInterval; + short maxAge; + short survival; float probRep; - bool fecDens; bool fecStageDens; bool devDens; bool devStageDens; - bool survDens; bool survStageDens; bool disperseOnLoss; + 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; + float devCoeff; + float survCoeff; }; // structures for emigration parameters struct emigRules { - bool densDep; bool stgDep; bool sexDep; bool indVar; + 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; + float d0; + float alpha; + float beta; + + emigTraits() : d0(0.0), alpha(0.0), beta(0.0) {}; + + emigTraits(const emigTraits& e) : d0(e.d0), alpha(e.alpha), beta(e.beta) {}; + + emigTraits* clone() { return new emigTraits(*this); } + + void divideTraitsBy(int i) { + + d0 /= i; + alpha /= i; + beta /= i; + } }; // structures for transfer parameters -struct trfrRules { - bool moveModel; bool stgDep; bool sexDep; - bool distMort; bool indVar; +struct transferRules { + bool usesMovtProc; + bool stgDep; + bool sexDep; + bool distMort; + bool indVar; bool twinKern; bool habMort; - short moveType; bool costMap; + short moveType; + bool costMap; short movtTrait[2]; }; -struct trfrKernTraits { - float meanDist1; float meanDist2; float probKern1; +struct trfrKernelParams { + float meanDist1; + float meanDist2; + float probKern1; }; struct trfrMortParams { - float fixedMort; float mortAlpha; float mortBeta; + 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 trfrMovtParams { + short pr; + short prMethod; + short memSize; + short goalType; + float dp; + float gb; + float alphaDB; + int betaDB; + float stepMort; + float stepLength; + float rho; + bool straightenPath; }; struct trfrCRWTraits { - float stepMort; float stepLength; float rho; bool straigtenPath; + float stepMort; + float stepLength; + float rho; + bool straightenPath; }; 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; + short pr; + short prMethod; + short memSize; + short goalType; + float dp; + float gb; + float alphaDB; + int betaDB; + float stepMort; + bool straightenPath; }; // structures for settlement parameters struct settleType { - bool stgDep; bool sexDep; bool indVar; + bool stgDep; + bool sexDep; + bool indVar; short settTrait[2]; }; struct settleRules { - bool densDep; bool wait; bool go2nbrLocn; bool findMate; + bool densDep; + bool wait; + bool go2nbrLocn; + bool findMate; }; struct settleSteps { - int minSteps; int maxSteps; int maxStepsYr; + 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; + float s0; + float alpha; + float beta; + + settleTraits() : s0(0.0), alpha(0.0), beta(0.0) {}; + + settleTraits(const settleTraits& e) : s0(e.s0), alpha(e.alpha), beta(e.beta) {}; + + void divideTraitsBy(int i) { + s0 /= i; + alpha /= i; + beta /= i; + } }; @@ -187,7 +213,14 @@ struct settScales { class Species { public: - Species(void); + Species( + const short& repro = 0, + const short& nbRepSeasons = 1, + const bool& hasStgStruct = false, + const short& nStg = 2, + const bool& usesMovtProc = false, + const short& movementType = 1 + ); ~Species(void); short getSpNum(void); @@ -208,11 +241,11 @@ class Species { void setStage( // Set stage structure parameters const stageParams // structure holding stage structure parameters ); - stageParams getStage(void); // Get stage structure parameters + stageParams getStageParams(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 + demogrParams getDemogrParams(void); // Get general demographic parameters short getRepType(void); bool stageStructured(void); void setDensDep( // Set demographic density dependence coefficients @@ -249,6 +282,45 @@ class Species { short // sex ); + void setFecSpatial(bool); + bool getFecSpatial(void){return fecSpatial;}; + void setDevSpatial(bool); + bool getDevSpatial(void){return devSpatial;}; + void setSurvSpatial(bool); + bool getSurvSpatial(void){return survSpatial;}; + void setFecLayer( // set the layer of the spatial demographic scaling used for fecundity + short, // stage + short, // sex + short // layer + ); + + short getFecLayer( // get the layer of the spatial demographic scaling used for fecundity + short, // stage + short // sex + ); + + void setDevLayer( // set the layer of the spatial demographic scaling used for development + short, // stage + short, // sex + short // layer + ); + + short getDevLayer( // get the layer of the spatial demographic scaling used for development + short, // stage + short // sex + ); + + void setSurvLayer( // set the layer of the spatial demographic scaling used for survival + short, // stage + short, // sex + short // layer + ); + + short getSurvLayer( // get the layer of the spatial demographic scaling used for survival + short, // stage + short // sex + ); + float getMaxFec(void); // Get highest fecundity of any stage void setMinAge( // Set minimum age short, // stage @@ -307,114 +379,73 @@ class Species { short // option: 0 = return minimum, otherwise = return maximum ); + std::set& getSamplePatches() { + return samplePatchList; + }; - // genome functions + string getNIndsToSample() { + return nIndsToSample; + }; - 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. - ); + std::set& getStagesToSample() { + return stagesToSampleFrom; + } + + int getNbPatchesToSample() { + return nPatchesToSample; + } + + // Genetic functions + void resetGeneticParameters(); + bool areMutationsOn(void); + bool isDiploid() const; + int incrNbGenLoadTraits(); + int getNbGenLoadTraits() const; // emigration parameter functions - void setEmig( // Set emigration rules + void setEmigRules( // Set emigration rules const emigRules // structure holding emigration rules ); - emigRules getEmig(void); // Get emigration rules - void setEmigTraits( // Set emigration trait parameters + emigRules getEmigRules(void); // Get emigration rules + void setSpEmigTraits( // Set emigration trait parameters const short, // stage const short, // sex const emigTraits // structure holding emigration trait parameters ); - emigTraits getEmigTraits( // Get emigration trait parameters + emigTraits getSpEmigTraits( // Get emigration trait parameters short, // stage short // sex ); - float getEmigD0( // Get (maximum) emigration probability + float getSpEmigD0( // 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 + void setTrfrRules( // Set transfer rules + const transferRules // structure holding transfer rules ); - trfrRules getTrfr(void); // Get transfer rules + transferRules getTransferRules(void); // Get transfer rules void setFullKernel( // Set fullKernel condition bool // fullKernel value ); bool useFullKernel(void); - void setKernTraits( // Set transfer by kernel parameters + void setSpKernTraits( // Set transfer by kernel parameters const short, // stage const short, // sex - const trfrKernTraits, // structure holding transfer by kernel parameters + const trfrKernelParams, // structure holding transfer by kernel parameters const int // Landscape resolution ); - trfrKernTraits getKernTraits( // Get transfer by kernel parameters + +#ifdef UNIT_TESTS + // Testing: set dispersal but ignore resolution + void overrideKernels(const short stg, const short sex, + const trfrKernelParams k); +# endif // UNIT_TESTS + + trfrKernelParams getSpKernTraits( // Get transfer by kernel parameters short, // stage short // sex ); @@ -422,44 +453,12 @@ class Species { 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 setSpMovtTraits( // Set transfer movement model parameters + const trfrMovtParams // structure holding transfer movement model parameters ); - 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 + trfrMovtParams getSpMovtTraits(void); // Get transfer movement model traits + trfrCRWTraits getSpCRWTraits(void); // Get CRW traits + trfrSMSTraits getSpSMSTraits(void); // Get SMS traits // Return dimension of habitat-dependent step mortality and costs matrices short getMovtHabDim(void); void createHabCostMort( // Create habitat-dependent costs and mortality matrices @@ -505,28 +504,32 @@ class Species { short, // stage short // sex ); - void setSettTraits( // Set settlement density dependence traits + void setSpSettTraits( // 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 + settleTraits getSpSettTraits( // 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 + + void addTrait(TraitType traitType, const SpeciesTrait& trait); + + void clearTraitTable(); + + SpeciesTrait* getSpTrait(TraitType trait) const; + + std::set getTraitTypes(); + + int getNTraits() const; + int getNPositionsForTrait(const TraitType trait) const; + int getGenomeSize() const; + float getRecombinationRate() const; + std::set getChromosomeEnds() const; + void setGeneticParameters(const std::set& chromosomeEnds, const int genomeSize, const float recombinationRate, + const std::set& samplePatchList, const string nIndsToSample, const std::set& stagesToSampleFrom, int nPatchesToSampleFrom); + void setSamplePatchList(const std::set& samplePatchList); private: @@ -555,6 +558,7 @@ class Species { bool survStageDens; bool disperseOnLoss; // individuals disperse on complete loss of patch // (otherwise they die) + bool fecSpatial, devSpatial, survSpatial; short habDimK; // dimension of carrying capacities matrix float* habK; // habitat-specific carrying capacities (inds/cell) float devCoeff; // density-dependent development coefficient @@ -563,10 +567,13 @@ class Species { 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 + float fec[gMaxNbStages][gMaxNbSexes]; // fecundities + float dev[gMaxNbStages][gMaxNbSexes]; // development probabilities + float surv[gMaxNbStages][gMaxNbSexes]; // survival probabilities + short minAge[gMaxNbStages][gMaxNbSexes]; // minimum age to enter stage + int fecLayer[gMaxNbStages][gMaxNbSexes]; // layer for spatial varying fecundity + int devLayer[gMaxNbStages][gMaxNbSexes]; // layer for spatial varying development + int survLayer[gMaxNbStages][gMaxNbSexes]; // layer for spatial varying survival // 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 ***** @@ -578,24 +585,18 @@ class Species { // 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 + /**The traits table.*/ + std::map> spTraitTable; + std::set chromosomeEnds; + int genomeSize; 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 + bool mutationsOn; + int nbGeneticFitnessTraits; + float recombinationRate; + std::set samplePatchList; + int nPatchesToSample; //for cell based landscape + std::set stagesToSampleFrom; + string nIndsToSample; //could be integer or 'all', all means in in selected patches not necessarily all in population // emigration parameters @@ -606,49 +607,27 @@ class Species { 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) + float d0[gMaxNbStages][gMaxNbSexes]; // maximum emigration probability + float alphaEmig[gMaxNbStages][gMaxNbSexes]; // slope of density-dependent reaction norm + float betaEmig[gMaxNbStages][gMaxNbSexes]; // 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 usesMovtProcess; 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 + float meanDist1[gMaxNbStages][gMaxNbSexes]; // mean of 1st dispersal kernel (m) + float meanDist2[gMaxNbStages][gMaxNbSexes]; // mean of 2nd dispersal kernel (m) + float probKern1[gMaxNbStages][gMaxNbSexes]; // 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 @@ -666,28 +645,10 @@ class Species { 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 straightenPath; // 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 @@ -697,47 +658,31 @@ class Species { 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[NSTAGES][NSEXES]; // minimum no. of steps - int maxSteps[NSTAGES][NSEXES]; // 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 + bool densDepSett[gMaxNbStages][gMaxNbSexes]; + bool wait[gMaxNbStages][gMaxNbSexes]; // wait to continue moving next season (stage-structured model only) + bool go2nbrLocn[gMaxNbStages][gMaxNbSexes]; // settle in neighbouring cell/patch if available (ditto) + bool findMate[gMaxNbStages][gMaxNbSexes]; + int minSteps[gMaxNbStages][gMaxNbSexes]; // minimum no. of steps + int maxSteps[gMaxNbStages][gMaxNbSexes]; // maximum total no. of steps + int maxStepsYr[gMaxNbStages][gMaxNbSexes]; // maximum no. of steps in any one dispersal period + float s0[gMaxNbStages][gMaxNbSexes]; // maximum settlement probability + float alphaS[gMaxNbStages][gMaxNbSexes]; // slope of the settlement reaction norm to density + float betaS[gMaxNbStages][gMaxNbSexes]; // inflection point of the settlement reaction norm to density // 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! -*/ //--------------------------------------------------------------------------- +#ifdef UNIT_TESTS +// For testing purposes only +Species* createDefaultSpecies(); +demogrParams createDefaultHaploidDemogrParams(); +demogrParams createDefaultDiploidDemogrParams(); +#endif // UNIT_TESTS + //--------------------------------------------------------------------------- #endif diff --git a/SpeciesTrait.cpp b/SpeciesTrait.cpp new file mode 100644 index 0000000..418c222 --- /dev/null +++ b/SpeciesTrait.cpp @@ -0,0 +1,299 @@ + +#include "SpeciesTrait.h" + +// Species trait constructor +SpeciesTrait::SpeciesTrait( + const TraitType& trType, + const sex_t& sx, + const set& pos, + const ExpressionType& expr, + const set& initialPositions, + const DistributionType& initDist, + const map initParams, + const DistributionType& initDomDist, + const map initDomParams, + bool isInherited, + const float& mutRate, + const DistributionType& mutationDist, + const map mutationParams, + const DistributionType& dominanceDist, + const map dominanceParams, + const int nPloidy, + const bool isOutput) : + traitType{ trType }, + sex{ sx }, + genePositions{ pos }, + expressionType{ expr }, + initPositions{ initialPositions }, + initialDistribution{ initDist }, + initialParameters{ initParams }, + initialDomDistribution{ initDomDist }, + initialDomParameters{ initDomParams }, + dominanceDistribution{ dominanceDist }, + dominanceParameters{ dominanceParams }, + inherited{ isInherited }, + mutationDistribution{ mutationDist }, + mutationParameters{ mutationParams }, + mutationRate{ mutRate }, + ploidy{ nPloidy }, + traitIsOutput{ isOutput } +{ + // Check distribution parameters + // Initial allele distribution + for (auto [paramType, paramVal] : initParams) { + switch (paramType) + { + case MIN: case MAX: case MEAN: + if (!isValidTraitVal(paramVal)) + throw logic_error("Invalid parameter value: initial parameter " + to_string(paramType) + " must have a valid value for trait " + to_string(traitType) + "."); + break; + case SD: + if (paramVal <= 0.0) + throw logic_error("Invalid parameter value: initial parameter " + to_string(paramType) + " must be strictly positive"); + break; + default: + break; + } + } + + // Initial dominance distribution + for (auto [paramType, paramVal] : initDomParams) { + switch (paramType) + { + case MIN: case MAX: case MEAN: + if (paramVal < 0.0) + throw logic_error("Invalid parameter value: initial dominance parameter " + to_string(paramType) + " must not be negative."); + break; + case SD: case SHAPE: case SCALE: + if (paramVal <= 0.0) + throw logic_error("Invalid parameter value: initial dominance parameter " + to_string(paramType) + " must be strictly positive"); + break; + default: + break; + } + } + + // Mutation distribution + for (auto [paramType, paramVal] : mutationParams) { + switch (paramType) + { + case MIN: case MAX: case MEAN: + if ( + (trType == NEUTRAL || trType == GENETIC_LOAD || trType == GENETIC_LOAD1 || + trType == GENETIC_LOAD2 || trType == GENETIC_LOAD3 || trType == GENETIC_LOAD4 || trType == GENETIC_LOAD5) + && !isValidTraitVal(paramVal) + // dispersal traits are cumulative so no value is invalid + ) + throw logic_error("Invalid parameter value: mutation parameter " + to_string(paramType) + " must have a valid value for trait " + to_string(traitType) + "."); + break; + case SD: case SHAPE: case SCALE: + if (paramVal <= 0.0) + throw logic_error("Invalid parameter value: mutation parameter " + to_string(paramType) + " must be strictly positive"); + break; + default: + break; + } + } + + // Dominance distribution + for (auto [paramType, paramVal] : dominanceParams) { + switch (paramType) + { + case MIN: case MAX: case MEAN: + if (paramVal < 0.0) + throw logic_error("Invalid parameter value: dominance parameter " + to_string(paramType) + " must not be negative."); + break; + case SD: case SHAPE: case SCALE: + if (paramVal <= 0.0) + throw logic_error("Invalid parameter value: dominance parameter " + to_string(paramType) + " must be strictly positive"); + break; + default: + break; + } + } +} + +bool SpeciesTrait::isValidTraitVal(const float& val) const { + switch (traitType) + { + // Neutral trait + case NEUTRAL: // only need to check for input parameters + { + return val >= 0.0 && val <= 255.0; + } + // Genetic Load + case GENETIC_LOAD: case GENETIC_LOAD1: case GENETIC_LOAD2: case GENETIC_LOAD3: case GENETIC_LOAD4: case GENETIC_LOAD5: + { + return val >= -1.0 // genetic fitness traits can be beneficial + && val <= 1.0; + break; + } + // Dispersal traits + /// Emigration + case E_D0_F: case E_D0_M: case E_D0: { + return val >= 0.0 && val <= 1.0; // is a probability + break; + } + case E_ALPHA_F: case E_ALPHA_M: case E_ALPHA: + case E_BETA_F: case E_BETA_M: case E_BETA: + { + return true; // slope and inflexion point can be any value + break; + } + /// Settlement + case S_S0_F: case S_S0_M: case S_S0: + { + return val >= 0.0 && val <= 1.0; + break; + } + case S_ALPHA_F: case S_ALPHA_M: case S_ALPHA: + case S_BETA_F: case S_BETA_M: case S_BETA: + { + return true; // slope and inflection point can be any value + break; + } + /// Transfer - Kernels + case KERNEL_MEANDIST_1_F: case KERNEL_MEANDIST_1_M: case KERNEL_MEANDIST_1: + case KERNEL_MEANDIST_2_F: case KERNEL_MEANDIST_2_M: case KERNEL_MEANDIST_2: + { + return val >= 0.0; // is a distance + break; + } + case KERNEL_PROBABILITY_F: case KERNEL_PROBABILITY_M: case KERNEL_PROBABILITY: + { + return val >= 0.0 && val <= 1.0; + break; + } + /// Transfer - Correlated random walk + case CRW_STEPLENGTH: + { + return val >= 0.0; + break; + } + case CRW_STEPCORRELATION: + { + return val >= 0.0 && val <= 1.0; + break; + } + /// Transfer - Stochastic Movement Simulator + case SMS_DP: + { + return val >= 1.0; // according to parameter doc + break; + } + case SMS_GB: + { + return val >= 1.0; // according to parameter doc + break; + } + case SMS_ALPHADB: + { + return val > 0.0; + break; + } + case SMS_BETADB: + { + return true; + break; + } + default: + throw logic_error("Invalid trait type " + to_string(traitType) + " passed to isValidTraitVal()."); + break; + } +} + +#ifdef UNIT_TESTS // Testing only + +// Create a default set of gene positions ranging from zero to genome size +set createTestGenePositions(const int genomeSz) { + set genePositions; + for (int i = 0; i < genomeSz; i++) genePositions.insert(i); + return genePositions; +} + +SpeciesTrait* createTestEmigSpTrait(const set& genePositions, const bool& isDiploid) { + // Create species trait + const map distParams{ + pair{GenParamType::MIN, 0.0}, + pair{GenParamType::MAX, 1.0} + }; + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::E_D0, + sex_t::NA, + genePositions, + ExpressionType::ADDITIVE, + genePositions, + DistributionType::UNIFORM, + distParams, + DistributionType::NONE, // no dominance + distParams, + true, // isInherited + 0.0, // mutation rate + DistributionType::UNIFORM, + distParams, + DistributionType::NONE, // no dominance + distParams, + isDiploid ? 2 : 1, + false + ); + return spTr; +} + +SpeciesTrait* createTestGenLoadTrait(const set& genePositions, const bool& isDiploid) { + // Create species trait + const map distParams{ + pair{GenParamType::MIN, 0.0}, + pair{GenParamType::MAX, 1.0} + }; + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD, + sex_t::NA, + genePositions, + ExpressionType::MULTIPLICATIVE, + genePositions, + DistributionType::NONE, + distParams, + DistributionType::NONE, // initialise dominance to zero + distParams, + true, // isInherited + 0.0, // mutation rate + DistributionType::UNIFORM, + distParams, + DistributionType::UNIFORM, + distParams, + isDiploid ? 2 : 1, + false + ); + return spTr; +} + +SpeciesTrait* createTestNeutralSpTrait(const float& maxAlleleVal, const set& genePositions, const bool& isDiploid) { + + const map distParams{ + // Set max allele value + pair{GenParamType::MAX, maxAlleleVal} + }; + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::NEUTRAL, + sex_t::NA, + genePositions, + ExpressionType::NOTEXPR, + genePositions, + // Sample initial values from uniform(0, max) + DistributionType::UNIFORM, distParams, + DistributionType::NONE, // No dominance + map{}, + true, // isInherited + 0.0, // mutation rate + // Mutation sampled from a uniform(0, max) + DistributionType::KAM, + distParams, + DistributionType::NONE, // No dominance + map{}, + isDiploid ? 2 : 1, + false + ); + return spTr; +} + +#endif // UNIT_TESTS diff --git a/SpeciesTrait.h b/SpeciesTrait.h new file mode 100644 index 0000000..69b1313 --- /dev/null +++ b/SpeciesTrait.h @@ -0,0 +1,106 @@ +#ifndef SPECIESTRAITH +#define SPECIESTRAITH + +#include "Parameters.h" +#include "Species.h" + +#include +#include +#include +#include + +class Species; // forward declaration to overcome circularity issue + +// Species-level traits +// Features of traits that are shared across all individuals in the same species +class SpeciesTrait { +public: + SpeciesTrait( + const TraitType& traitType, + const sex_t& sex, + const set& pos, + const ExpressionType& expr, + const set& initialPositions, + const DistributionType& initDist, + const map initParams, + const DistributionType& initDomDist, + const map initDomParams, + bool isInherited, + const float& mutationRate, + const DistributionType& mutationDist, + const map mutationParams, + const DistributionType& dominanceDist, + const map dominanceParams, + const int ploidy, + const bool isOutput + ); + + bool isValidTraitVal(const float& val) const; + TraitType getTraitType() const { return traitType; } + bool isOutput() const { return traitIsOutput; } + + // Getters + sex_t getSex() const { return sex; } + float getMutationRate() const { return mutationRate; } + short getPloidy() const { return ploidy; } + set& getGenePositions() { return genePositions; } // returning by reference, make sure receiver is const + set getInitPositions() const { return initPositions; } + int getPositionsSize() const { return static_cast(genePositions.size()); } + bool isInherited() const { return inherited; } + + DistributionType getInitialDistribution() const { return initialDistribution; }; + map getInitialParameters() const { return initialParameters; }; + DistributionType getInitDomDistribution() const { return initialDomDistribution; }; + map getInitDomParameters() const { return initialDomParameters; }; + DistributionType getMutationDistribution() const { return mutationDistribution; }; + map getMutationParameters() const { return mutationParameters; }; + DistributionType getDominanceDistribution() const { return dominanceDistribution; }; + map getDominanceParameters() const { return dominanceParameters; }; + + ExpressionType getExpressionType() const { return expressionType; }; + + int getNbNeutralAlleles() const { + if (!traitType == NEUTRAL) throw logic_error("getNbNeutralAlleles() should only be called for neutral traits."); + else { + int maxAlleleVal = max( + getMutationParameters().find(MAX)->second + 1, + getInitialParameters().find(MAX)->second + 1 + ); // possible values range from 0 to MAX + return maxAlleleVal; + } + } + +private: + + int ploidy; + float mutationRate; + TraitType traitType; + bool traitIsOutput; + sex_t sex; + + // Positions in the genome of all genes (loci) pertaining to this trait + // The genome itself is not modelled explicitly + set genePositions; + set initPositions; + + ExpressionType expressionType; + DistributionType initialDistribution; + map initialParameters; + DistributionType initialDomDistribution; + map initialDomParameters; + DistributionType dominanceDistribution; + map dominanceParameters; + bool inherited; + DistributionType mutationDistribution; + map mutationParameters; +}; + +#ifdef UNIT_TESTS // Testing only +// Create a default set of gene positions ranging from zero to genome size +set createTestGenePositions(const int genomeSz); +SpeciesTrait* createTestEmigSpTrait(const set& genePositions, const bool& isDiploid); +SpeciesTrait* createTestGenLoadTrait(const set& genePositions, const bool& isDiploid); +SpeciesTrait* createTestNeutralSpTrait(const float& maxAlleleVal, const set& genePositions, const bool& isDiploid); +#endif // UNIT_TESTS + +#endif // SPECIESTRAITH diff --git a/SubCommunity.cpp b/SubCommunity.cpp index 12c154c..d776406 100644 --- a/SubCommunity.cpp +++ b/SubCommunity.cpp @@ -64,6 +64,7 @@ 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 @@ -92,23 +93,23 @@ void SubCommunity::initialise(Landscape* pLandscape, Species* pSpecies) } 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(); + demogrParams dem = pSpecies->getDemogrParams(); + stageParams sstruct = pSpecies->getStageParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); - genomeData gen = pSpecies->getGenomeData(); short stg, age, repInt; Individual* pInd; float probmale; @@ -133,19 +134,15 @@ void SubCommunity::initialInd(Landscape* pLandscape, Species* pSpecies, else { if (iind.sex == 1) probmale = 1.0; else probmale = 0.0; } - pInd = new Individual(pSpecies, pCell, pPatch, stg, age, repInt, probmale, trfr.moveModel, trfr.moveType); + pInd = new Individual(pSpecies, pCell, pPatch, stg, age, repInt, probmale, trfr.usesMovtProc, 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) - { + if (pSpecies->getNTraits() > 0) { // individual variation - set up genetics landData land = pLandscape->getLandData(); - pInd->setGenes(pSpecies, land.resol); + pInd->setUpGenes(pSpecies, land.resol); } + popns[0]->recruit(pInd); } // Create a new population, and return its address @@ -165,7 +162,7 @@ popStats SubCommunity::getPopStats(void) { // 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(); + pop = popns[i]->getStats(pPatch->getDemoScaling()); p.pSpecies = pop.pSpecies; p.spNum = pop.spNum; p.nInds += pop.nInds; @@ -228,9 +225,9 @@ void SubCommunity::patchChange(void) { 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(); + demogrParams dem = pSpecies->getDemogrParams(); if (dem.stageStruct) { - stageParams sstruct = pSpecies->getStage(); + stageParams sstruct = pSpecies->getStageParams(); if (sstruct.disperseOnLoss) popns[i]->allEmigrate(); else popns[i]->extirpate(); } @@ -245,6 +242,7 @@ void SubCommunity::reproduction(int resol, float epsGlobal, short rasterType, bo { if (subCommNum == 0) return; // no reproduction in the matrix float localK, envval; + std::vector localDemoScaling; Cell* pCell; envGradParams grad = paramsGrad->getGradient(); envStochParams env = paramsStoch->getStoch(); @@ -254,6 +252,7 @@ void SubCommunity::reproduction(int resol, float epsGlobal, short rasterType, bo if (npops < 1) return; localK = pPatch->getK(); + localDemoScaling = pPatch->getDemoScaling(); if (localK > 0.0) { if (patchModel) { envval = 1.0; // environmental gradient is currently not applied for patch-based model @@ -278,7 +277,7 @@ void SubCommunity::reproduction(int resol, float epsGlobal, short rasterType, bo } } for (int i = 0; i < npops; i++) { // all populations - popns[i]->reproduction(localK, envval, resol); + popns[i]->reproduction(localK, envval, resol, localDemoScaling); popns[i]->fledge(); } } @@ -287,11 +286,9 @@ void SubCommunity::reproduction(int resol, float epsGlobal, short rasterType, bo 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 ... + int npops = static_cast(popns.size()); if (npops < 1) return; - localK = pPatch->getK(); + float 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 @@ -300,24 +297,43 @@ void SubCommunity::emigration(void) } // Remove emigrants from their natal patch and add to a map of vectors -void SubCommunity::initiateDispersal(std::map> &inds_map) { +void SubCommunity::recruitDispersers(std::map>& disperserPool) { 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(); + pop = popns[i]->getStats(pPatch->getDemoScaling()); Species* pSpecies = popns[i]->getSpecies(); for (int j = 0; j < pop.nInds; j++) { disp = popns[i]->extractDisperser(j); if (disp.yes) { // disperser - has already been removed from natal population - inds_map[pSpecies].push_back(disp.pInd); + disperserPool[pSpecies].push_back(disp.pInd); } } // remove pointers to emigrants popns[i]->clean(); } +} + +// Add all individuals in the matrix to the disperser pool +void SubCommunity::disperseMatrix(std::map> &inds_map) { + if (subCommNum != 0) return; + popStats pop; + + int npops = (int)popns.size(); + for (int i = 0; i < npops; i++) { + pop = popns[i]->getStats(pPatch->getDemoScaling()); + Species* pSpecies = popns[i]->getSpecies(); +#pragma omp for schedule(static) + for (int j = 0; j < pop.nInds; j++) { + Individual *pInd = popns[i]->extractIndividual(j); + inds_map[pSpecies].push_back(pInd); + } +#pragma omp single + popns[i]->clean(); + } } @@ -341,56 +357,338 @@ void SubCommunity::recruitMany(std::vector& inds, Species* pSpecies } } -// Transfer through the matrix - run for the matrix sub-community only -#if RS_RCPP -int SubCommunity::transfer(Landscape* pLandscape, short landIx, short nextseason) +// Transfer through the matrix - run for a per-species map of vectors of individuals +int SubCommunity::resolveTransfer(std::map>& dispersingInds, Landscape* pLandscape, short landIx) +{ + int nbStillDispersing = 0; + int disperser; + Patch* pPatch = nullptr; + Cell* pCell = nullptr; + simParams sim = paramsSim->getSim(); + + for (auto & it : dispersingInds) { // all species + + Species* const& pSpecies = it.first; + short reptype = pSpecies->getRepType(); + transferRules trfr = pSpecies->getTransferRules(); + + vector& inds = it.second; + + // each individual takes one step + // for dispersal by kernel, this should be the only step taken + for (auto& pInd : inds) { + if (trfr.usesMovtProc) { + disperser = pInd->moveStep(pLandscape, pSpecies, landIx, sim.absorbing); + } + else { + disperser = pInd->moveKernel(pLandscape, pSpecies, sim.absorbing); + } + nbStillDispersing += disperser; + if (disperser) { + if (reptype > 0) + { // sexual species - record as potential settler in new patch + if (pInd->getStatus() == 2) + { // disperser has found a patch + pCell = pInd->getCurrCell(); + pPatch = pCell->getPatch(); + if (pPatch != nullptr) { // not no-data area + pPatch->incrPossSettler(pSpecies, pInd->getSex()); + } + } + } + } + } + } + return nbStillDispersing; +} + + +// Determine whether there is a potential mate present in a patch which a potential +// settler has reached +bool SubCommunity::matePresent(Species* pSpecies, Cell* pCell, short othersex) +{ + Patch* pPatch; + Population* pNewPopn; + int popsize = 0; + bool matefound = false; + + pPatch = pCell->getPatch(); + if (pPatch != nullptr) { + if (pPatch->getPatchNum() > 0) { // not the matrix patch + if (pPatch->getK() > 0.0) + { // suitable + pNewPopn = pPatch->getPopn(pSpecies); + if (pNewPopn != nullptr) { + const stageParams sstruct = pSpecies->getStageParams(); + // count members of other sex already resident in the patch + for (int stg = 0; stg < sstruct.nStages; stg++) { + popsize += pNewPopn->getNbInds(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; +} + +// Transfer is run for populations in the matrix only +#if RS_RCPP // included also SEASONAL +int SubCommunity::resolveSettlement(std::map>& dispersingInds, Landscape* pLandscape, short nextseason) #else -int SubCommunity::transfer(Landscape* pLandscape, short landIx) -#endif // RS_RCPP +int SubCommunity::resolveSettlement(std::map>& dispersingInds, Landscape* pLandscape) +#endif { - int ndispersers = 0; - int npops = (int)popns.size(); - for (int i = 0; i < npops; i++) { // all populations + int nbStillDispersing = 0; + short othersex; + bool mateOK, densdepOK; + int patchnum; + double localK, popsize, settprob; + Patch* pPatch = 0; + Cell* pCell = 0; + indStats ind; + Population* pNewPopn = 0; + locn newloc, nbrloc; + + landData ppLand = pLandscape->getLandData(); + settleRules sett; + settleTraits settDD; + settlePatch settle; + simParams sim = paramsSim->getSim(); + + for (auto& it : dispersingInds) { // all species + Species* const& pSpecies = it.first; + transferRules trfr = pSpecies->getTransferRules(); + settleType settletype = pSpecies->getSettle(); + + vector& inds = it.second; + + // each individual which has reached a potential patch decides whether to settle + for (auto& pInd : inds) { + ind = pInd->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 = pInd->getCurrCell(); + 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(pSpecies, pCell, othersex)) mateOK = true; + } + else { // no requirement to find a mate + mateOK = true; + } + + densdepOK = false; + settle = pInd->getSettPatch(); + if (sett.densDep) + { + pPatch = pCell->getPatch(); + if (pPatch != nullptr) { // not no-data area + 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(); + pNewPopn = pPatch->getPopn(pSpecies); + if (pNewPopn == nullptr) { // population has not been set up in the new patch + popsize = 0.0; + } + else { + popsize = (double)pNewPopn->getNbInds(); + } + if (localK > 0.0) { + // make settlement decision + if (settletype.indVar) settDD = pInd->getIndSettTraits(); #if RS_RCPP - ndispersers += popns[i]->transfer(pLandscape, landIx, nextseason); + else settDD = pSpecies->getSpSettTraits(ind.stage, ind.sex); #else - ndispersers += popns[i]->transfer(pLandscape, landIx); + else { + if (settletype.sexDep) { + if (settletype.stgDep) + settDD = pSpecies->getSpSettTraits(ind.stage, ind.sex); + else + settDD = pSpecies->getSpSettTraits(0, ind.sex); + } + else { + if (settletype.stgDep) + settDD = pSpecies->getSpSettTraits(ind.stage, 0); + else + settDD = pSpecies->getSpSettTraits(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; } - return ndispersers; + else { // settlement procluded + settle.settleStatus = 1; + } + settle.pSettPatch = pPatch; + } + pInd->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; + pInd->setSettPatch(settle); + } + + if (mateOK && densdepOK) { // can recruit to patch + ind.status = 4; + nbStillDispersing--; + } + else { // does not recruit + if (trfr.usesMovtProc) { + ind.status = 1; // continue dispersing, unless ... + // ... maximum steps has been exceeded + pathSteps steps = pInd->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) + } + nbStillDispersing--; + } + } + } + + pInd->setStatus(ind.status); + } +#if RS_RCPP + // write each individuals current movement step and status to paths file + if (trfr.usesMovtProc && sim.outPaths) { + if (nextseason >= sim.outStartPaths && nextseason % sim.outIntPaths == 0) { + pInd->outMovePath(nextseason); + } + } +#endif + + if (!trfr.usesMovtProc && sett.go2nbrLocn && (ind.status == 3 || ind.status == 6)) + { + // for kernel-based transfer only ... + // determine whether recruitment to a neighbouring cell is possible + + pCell = pInd->getCurrCell(); + 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 + pPatch = pCell->getPatch(); + if (pPatch != nullptr) { // not no-data area + patchnum = pPatch->getPatchNum(); + if (patchnum > 0 && pPatch != pInd->getNatalPatch()) + { // not the matrix or natal patch + if (pPatch->getK() > 0.0) + { // suitable + if (sett.findMate) { + if (matePresent(pSpecies, 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) { + pInd->moveto(nbrlist[0]); + } + else { // select at random from the list + int rrr = pRandom->IRandom(0, listsize - 1); + pInd->moveto(nbrlist[rrr]); + } + } + // else list empty - do nothing - individual retains its current location and status + } + + } // loop through inds + + } // loop through disperser map + + return nbStillDispersing; } //--------------------------------------------------------------------------- -// Remove emigrants from patch 0 (matrix) and transfer to sub-community +// Remove emigrants from the vectors map 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) +void SubCommunity::completeDispersal(std::map>& inds_map, 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(); - #pragma omp parallel for private(settler, pNewPatch, pPop, pSubComm, pPrevCell, pPrevPatch) - for (int j = 0; j < popsize; j++) { - bool settled; - settler = popns[i]->extractSettler(j); - settled = settler.yes; + for (auto & it : inds_map) { // all species + Species* const& pSpecies = it.first; + vector& inds = it.second; + for (Individual*& pInd : inds) { + indStats ind = pInd->getStats(); + bool settled = ind.status == 4 || ind.status == 5; if (settled) { - // settler - has already been removed from matrix population // find new patch - pNewPatch = settler.pCell->getPatch(); + pNewPatch = pInd->getCurrCell()->getPatch(); // find population within the patch (if there is one) { #ifdef _OPENMP @@ -403,24 +701,24 @@ void SubCommunity::completeDispersal(Landscape* pLandscape, bool connect) pPop = pSubComm->newPopn(pLandscape, pSpecies, pNewPatch, 0); } } - pPop->recruit(settler.pInd); + pPop->recruit(pInd); if (connect) { // increment connectivity totals int newpatch = pNewPatch->getSeqNum(); - pPrevCell = settler.pInd->getPrevCell(); + pPrevCell = pInd->getPrevCell(); pPrevPatch = pPrevCell->getPatch(); if (pPrevPatch != nullptr) { int prevpatch = pPrevPatch->getSeqNum(); pLandscape->incrConnectMatrix(prevpatch, newpatch); } } + pInd = nullptr; } else { // for group dispersal only } } - // remove pointers in the matrix popn to settlers - popns[i]->clean(); + // remove settled individuals + inds.erase(std::remove(inds.begin(), inds.end(), (Individual *)nullptr), inds.end()); } - } //--------------------------------------------------------------------------- @@ -428,17 +726,29 @@ void SubCommunity::completeDispersal(Landscape* pLandscape, bool connect) void SubCommunity::survival0(short option0, short option1) { int npops = (int)popns.size(); - float localK = pPatch->getK(); - for (int i = 0; i < npops; i++) { // all populations - popns[i]->survival0(localK, option0, option1); + std::vector localDemoScaling; + localDemoScaling = pPatch->getDemoScaling(); + float localK = pPatch->getK(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->survival0(localK, option0, option1, localDemoScaling); + } } -} void SubCommunity::survival1() { int npops = (int)popns.size(); - for (int i = 0; i < npops; i++) { // all populations - popns[i]->survival1(); + for (int i = 0; i < npops; i++) { // all populations + popns[i]->survival1(); + } + } + +void SubCommunity::survival(short part, short option0, short option1 +) { + if (part == 0) { + return survival0(option0, option1); +} + else { + return survival1(); } } @@ -451,17 +761,13 @@ void SubCommunity::ageIncrement(void) { // 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(); + pop = popns[i]->getStats(pPatch->getDemoScaling()); if (pop.pSpecies == pSp && pop.pPatch == pPch) { // population located pPop = popns[i]; break; @@ -485,7 +791,7 @@ void SubCommunity::updateOccupancy(int row) { popStats pop; int npops = (int)popns.size(); for (int i = 0; i < npops; i++) { - pop = popns[i]->getStats(); + pop = popns[i]->getStats(pPatch->getDemoScaling()); if (pop.nInds > 0 && pop.breeding) { occupancy[row]++; i = npops; @@ -510,13 +816,13 @@ bool SubCommunity::outPopFinishLandscape() bool fileOK; Population* pPop; - // as all populations may have been deleted, set up a dummy one - // species is not necessary - pPop = new Population(); + // as all populations may have been deleted, set up a dummy one + // species is not necessary + pPop = new Population(); fileOK = pPop->outPopFinishLandscape(); - delete pPop; + delete pPop; return fileOK; -} + } //--------------------------------------------------------------------------- // Open population file and write header record @@ -526,11 +832,11 @@ bool SubCommunity::outPopStartLandscape(Landscape* pLandscape, Species* pSpecies Population* pPop; landParams land = pLandscape->getLandParams(); - // 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); + // 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->outPopStartLandscape(land.landNum, land.patchModel); - delete pPop; + delete pPop; return fileOK; } @@ -574,7 +880,7 @@ void SubCommunity::outPop(Landscape* pLandscape, int rep, int yr, int gen) popns[i]->outPopulation(rep, yr, gen, eps, land.patchModel, writeEnv, gradK); } else { - if (popns[i]->totalPop() > 0) { + if (popns[i]->getNbInds() > 0) { popns[i]->outPopulation(rep, yr, gen, eps, land.patchModel, writeEnv, gradK); } } @@ -583,14 +889,18 @@ void SubCommunity::outPop(Landscape* pLandscape, int rep, int yr, int gen) // Close individuals file void SubCommunity::outIndsFinishReplicate() { - popns[0]->outIndsFinishReplicate(); -} + // as all populations have been deleted, set up a dummy one + Population* pPop = new Population(); + pPop->outIndsFinishReplicate(); + delete pPop; + return; + } // Open individuals file and write header record void SubCommunity::outIndsStartReplicate(Landscape* pLandscape, int rep, int landNr) { landParams ppLand = pLandscape->getLandParams(); popns[0]->outIndsStartReplicate(rep, landNr, ppLand.patchModel); -} + } // Write records to individuals file void SubCommunity::outIndividuals(Landscape* pLandscape, int rep, int yr, int gen) { @@ -601,34 +911,13 @@ void SubCommunity::outIndividuals(Landscape* pLandscape, int rep, int yr, int ge } } -// Close genetics file -void SubCommunity::outGenFinishReplicate() -{ - popns[0]->outGenFinishReplicate(); -} - -// Open genetics file and write header record -void SubCommunity::outGenStartReplicate(int rep, int landNr) -{ - popns[0]->outGenStartReplicate(rep, landNr); -} - -// Write records to genetics file -void SubCommunity::outGenetics(int rep, int yr) -{ - // 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); - } -} // Population size of a specified stage -int SubCommunity::stagePop(int stage) { +int SubCommunity::getNbInds(int stage) const { int popsize = 0; int npops = (int)popns.size(); for (int i = 0; i < npops; i++) { // all populations - popsize += popns[i]->stagePop(stage); + popsize += popns[i]->getNbInds(stage); } return popsize; } @@ -636,18 +925,18 @@ int SubCommunity::stagePop(int stage) { // Close traits file bool SubCommunity::outTraitsFinishLandscape() { - if (outtraits.is_open()) outtraits.close(); - outtraits.clear(); - return true; -} + if (outtraits.is_open()) outtraits.close(); + outtraits.clear(); + return true; + } // Open traits file and write header record bool SubCommunity::outTraitsStartLandscape(Landscape* pLandscape, Species* pSpecies, int landNr) { landParams land = pLandscape->getLandParams(); string name; - emigRules emig = pSpecies->getEmig(); - trfrRules trfr = pSpecies->getTrfr(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); simParams sim = paramsSim->getSim(); @@ -703,7 +992,7 @@ bool SubCommunity::outTraitsStartLandscape(Landscape* pLandscape, Species* pSpec } } if (trfr.indVar) { - if (trfr.moveModel) { + if (trfr.usesMovtProc) { if (trfr.moveType == 1) { outtraits << "\tmeanDP\tstdDP\tmeanGB\tstdGB"; outtraits << "\tmeanAlphaDB\tstdAlphaDB\tmeanBetaDB\tstdBetaDB"; @@ -739,6 +1028,15 @@ bool SubCommunity::outTraitsStartLandscape(Landscape* pLandscape, Species* pSpec outtraits << "\tmeanBetaS\tstdBetaS"; } } + if (pSpecies->getNbGenLoadTraits() > 0) { + if (pSpecies->getDemogrParams().repType > 0) { + outtraits << "\tF_meanGenFitness\tF_stdGenFitness\tM_meanGenFitness\tM_stdGenFitness"; + } + else { + outtraits << "\tmeanGenFitness\tstdGenFitness"; + } + } + outtraits << endl; return outtraits.is_open(); @@ -747,14 +1045,10 @@ bool SubCommunity::outTraitsStartLandscape(Landscape* pLandscape, Species* pSpec // Write records to traits file and return aggregated sums traitsums SubCommunity::outTraits(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++) { + traitsums ts, indTraitsSums; + for (int i = 0; i < gMaxNbSexes; 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; @@ -767,25 +1061,65 @@ traitsums SubCommunity::outTraits(Landscape* pLandscape, int rep, int yr, int ge 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; + ts.sumGeneticFitness[i] = ts.ssqGeneticFitness[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(); + for (int iPop = 0; iPop < npops; iPop++) { // all populations + + if (pPatch->getK() > 0.0 && popns[iPop]->getNbInds() > 0) { + pSpecies = popns[iPop]->getSpecies(); + demogrParams dem = pSpecies->getDemogrParams(); + emigRules emig = pSpecies->getEmigRules(); + transferRules trfr = pSpecies->getTransferRules(); settleType sett = pSpecies->getSettle(); - poptraits = popns[i]->getTraits(pSpecies); + indTraitsSums = popns[iPop]->getIndTraitsSums(pSpecies); + + // Sum over populations + for (int iSex = 0; iSex < gMaxNbSexes; iSex++) { + ts.ninds[iSex] += indTraitsSums.ninds[iSex]; + ts.sumD0[iSex] += indTraitsSums.sumD0[iSex]; + ts.ssqD0[iSex] += indTraitsSums.ssqD0[iSex]; + ts.sumAlpha[iSex] += indTraitsSums.sumAlpha[iSex]; + ts.ssqAlpha[iSex] += indTraitsSums.ssqAlpha[iSex]; + ts.sumBeta[iSex] += indTraitsSums.sumBeta[iSex]; + ts.ssqBeta[iSex] += indTraitsSums.ssqBeta[iSex]; + ts.sumDist1[iSex] += indTraitsSums.sumDist1[iSex]; + ts.ssqDist1[iSex] += indTraitsSums.ssqDist1[iSex]; + ts.sumDist2[iSex] += indTraitsSums.sumDist2[iSex]; + ts.ssqDist2[iSex] += indTraitsSums.ssqDist2[iSex]; + ts.sumProp1[iSex] += indTraitsSums.sumProp1[iSex]; + ts.ssqProp1[iSex] += indTraitsSums.ssqProp1[iSex]; + ts.sumDP[iSex] += indTraitsSums.sumDP[iSex]; + ts.ssqDP[iSex] += indTraitsSums.ssqDP[iSex]; + ts.sumGB[iSex] += indTraitsSums.sumGB[iSex]; + ts.ssqGB[iSex] += indTraitsSums.ssqGB[iSex]; + ts.sumAlphaDB[iSex] += indTraitsSums.sumAlphaDB[iSex]; + ts.ssqAlphaDB[iSex] += indTraitsSums.ssqAlphaDB[iSex]; + ts.sumBetaDB[iSex] += indTraitsSums.sumBetaDB[iSex]; + ts.ssqBetaDB[iSex] += indTraitsSums.ssqBetaDB[iSex]; + ts.sumStepL[iSex] += indTraitsSums.sumStepL[iSex]; + ts.ssqStepL[iSex] += indTraitsSums.ssqStepL[iSex]; + ts.sumRho[iSex] += indTraitsSums.sumRho[iSex]; + ts.ssqRho[iSex] += indTraitsSums.ssqRho[iSex]; + ts.sumS0[iSex] += indTraitsSums.sumS0[iSex]; + ts.ssqS0[iSex] += indTraitsSums.ssqS0[iSex]; + ts.sumAlphaS[iSex] += indTraitsSums.sumAlphaS[iSex]; + ts.ssqAlphaS[iSex] += indTraitsSums.ssqAlphaS[iSex]; + ts.sumBetaS[iSex] += indTraitsSums.sumBetaS[iSex]; + ts.ssqBetaS[iSex] += indTraitsSums.ssqBetaS[iSex]; + ts.sumGeneticFitness[iSex] += indTraitsSums.sumGeneticFitness[iSex]; + ts.ssqGeneticFitness[iSex] += indTraitsSums.ssqGeneticFitness[iSex]; + } + + // Produce trait-per-cell output + if (sim.outTraitsCells && yr % sim.outIntTraitCell == 0 + && !commlevel) { - if (writefile) { outtraits << rep << "\t" << yr << "\t" << gen; if (land.patchModel) { outtraits << "\t" << pPatch->getPatchNum(); @@ -794,46 +1128,33 @@ traitsums SubCommunity::outTraits(Landscape* pLandscape, int rep, int yr, int ge 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.indVar) { if (emig.sexDep) { + vector mnD0(2, 0.0), mnAlpha(2, 0.0), mnBeta(2, 0.0), sdD0(2, 0.0), sdAlpha(2, 0.0), sdBeta(2, 0.0); + for (int sex = 0; sex < gMaxNbSexes; sex++) { + + double popsize = static_cast(indTraitsSums.ninds[sex]); + + if (popsize > 0) { + + mnD0[sex] = indTraitsSums.sumD0[sex] / popsize; + mnAlpha[sex] = indTraitsSums.sumAlpha[sex] / popsize; + mnBeta[sex] = indTraitsSums.sumBeta[sex] / popsize; + + if (popsize > 1) { + sdD0[sex] = indTraitsSums.ssqD0[sex] / popsize - mnD0[sex] * mnD0[sex]; + if (sdD0[sex] > 0.0) sdD0[sex] = sqrt(sdD0[sex]); else sdD0[sex] = 0.0; + sdAlpha[sex] = indTraitsSums.ssqAlpha[sex] / popsize - mnAlpha[sex] * mnAlpha[sex]; + if (sdAlpha[sex] > 0.0) sdAlpha[sex] = sqrt(sdAlpha[sex]); else sdAlpha[sex] = 0.0; + sdBeta[sex] = indTraitsSums.ssqBeta[sex] / popsize - mnBeta[sex] * mnBeta[sex]; + if (sdBeta[sex] > 0.0) sdBeta[sex] = sqrt(sdBeta[sex]); else sdBeta[sex] = 0.0; + } + else { + sdD0[sex] = sdAlpha[sex] = sdBeta[sex] = 0.0; + } + } + } outtraits << "\t" << mnD0[0] << "\t" << sdD0[0]; outtraits << "\t" << mnD0[1] << "\t" << sdD0[1]; if (emig.densDep) { @@ -843,93 +1164,142 @@ traitsums SubCommunity::outTraits(Landscape* pLandscape, int rep, int yr, int ge 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]; + else { // not sex-dependent + double mnD0 = 0, mnAlpha = 0, mnBeta = 0, popsize = 0; + double sdD0 = 0, sdAlpha = 0, sdBeta = 0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnD0 += indTraitsSums.sumD0[sex]; + mnAlpha += indTraitsSums.sumAlpha[sex]; + mnBeta += indTraitsSums.sumBeta[sex]; + popsize += indTraitsSums.ninds[sex]; + sdD0 += indTraitsSums.ssqD0[sex]; + sdAlpha += indTraitsSums.ssqAlpha[sex]; + sdBeta += indTraitsSums.ssqBeta[sex]; } - } - } - } - - 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; + mnD0 /= popsize; + mnAlpha /= popsize; + mnBeta /= 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; + sdD0 = sdD0 / popsize - mnD0 * mnD0; + sdAlpha = sdAlpha / popsize - mnAlpha * mnAlpha; + sdBeta = sdBeta / popsize - mnBeta * mnBeta; + sdD0 = sdD0 == 0.0 ? 0.0 : sqrt(sdD0); + sdAlpha = sdAlpha == 0.0 ? 0.0 : sqrt(sdAlpha); + sdBeta = sdBeta == 0.0 ? 0.0 : sqrt(sdBeta); + } + else { + sdD0 = 0.0; + sdAlpha = 0.0; + sdBeta = 0.0; + } + + outtraits << "\t" << mnD0 << "\t" << sdD0; + if (emig.densDep) { + outtraits << "\t" << mnAlpha << "\t" << sdAlpha; + outtraits << "\t" << mnBeta << "\t" << sdBeta; } } } - 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.indVar) { + + if (trfr.usesMovtProc) { // not sex-dependent + if (trfr.moveType == 1) { // SMS + double mnDP = 0.0, mnGB = 0.0, mnAlphaDB = 0.0, mnBetaDB = 0.0; + double sdDP = 0.0, sdGB = 0.0, sdAlphaDB = 0.0, sdBetaDB = 0.0; + double popsize = 0.0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnDP += indTraitsSums.sumDP[sex]; + mnGB += indTraitsSums.sumGB[sex]; + mnAlphaDB += indTraitsSums.sumAlphaDB[sex]; + mnBetaDB+= indTraitsSums.sumBetaDB[sex]; + popsize += indTraitsSums.ninds[sex]; + sdDP += indTraitsSums.ssqDP[sex]; + sdGB += indTraitsSums.ssqGB[sex]; + sdAlphaDB += indTraitsSums.ssqAlphaDB[sex]; + sdBetaDB += indTraitsSums.ssqBetaDB[sex]; + } + mnDP /= popsize; + mnGB /= popsize; + mnAlphaDB /= popsize; + mnBetaDB /= popsize; + if (popsize > 1) { + sdDP = sdDP / popsize - mnDP * mnDP; + sdGB = sdGB / popsize - mnGB * mnGB; + sdAlphaDB = sdAlphaDB / popsize - mnAlphaDB * mnAlphaDB; + sdBetaDB = sdBetaDB / popsize - mnBetaDB * mnBetaDB; + sdDP = sdDP == 0.0 ? 0.0 : sqrt(sdDP); + sdGB = sdGB == 0.0 ? 0.0 : sqrt(sdGB); + sdAlphaDB = sdAlphaDB == 0.0 ? 0.0 : sqrt(sdAlphaDB); + sdBetaDB = sdBetaDB == 0.0 ? 0.0 : sqrt(sdBetaDB); + } + else { + sdDP = 0.0; + sdGB = 0.0; + sdAlphaDB = 0.0; + sdBetaDB = 0.0; + } + outtraits << "\t" << mnDP << "\t" << sdDP; + outtraits << "\t" << mnGB << "\t" << sdGB; + outtraits << "\t" << mnAlphaDB << "\t" << sdAlphaDB; + outtraits << "\t" << mnBetaDB << "\t" << sdBetaDB; } - if (trfr.moveType == 2) { - outtraits << "\t" << mnStepL[0] << "\t" << sdStepL[0]; - outtraits << "\t" << mnRho[0] << "\t" << sdRho[0]; + if (trfr.moveType == 2) { // CRW + double mnStepL = 0.0, mnRho = 0.0, sdStepL = 0.0, sdRho = 0.0; + double popsize = 0.0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnStepL += indTraitsSums.sumStepL[sex]; + mnRho += indTraitsSums.sumRho[sex]; + popsize += indTraitsSums.ninds[sex]; + sdStepL += indTraitsSums.ssqStepL[sex]; + sdRho += indTraitsSums.ssqRho[sex]; + } + mnStepL /= popsize; + mnRho /= popsize; + if (popsize > 1) { + sdStepL = sdStepL / popsize - mnStepL * mnStepL; + sdRho = sdRho / popsize - mnRho * mnRho; + sdStepL = sdStepL == 0.0 ? 0.0 : sqrt(sdStepL); + sdRho = sdRho == 0.0 ? 0.0 : sqrt(sdRho); + } + else { + sdStepL = 0.0; + sdRho = 0.0; + } + outtraits << "\t" << mnStepL << "\t" << sdStepL; + outtraits << "\t" << mnRho << "\t" << sdRho; } } - else { + else { // kernels if (trfr.sexDep) { + + vector mnDist1(2, 0.0), mnDist2(2, 0.0), mnProp1(2, 0.0), mnStepL(2, 0.0), mnRho(2, 0.0); + vector sdDist1(2, 0.0), sdDist2(2, 0.0), sdProp1(2, 0.0), sdStepL(2, 0.0), sdRho(2, 0.0); + + for (int sex = 0; sex < gMaxNbSexes; sex++) { + + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + double popsize = static_cast(indTraitsSums.ninds[sex]); + + if (popsize > 0) { + mnDist1[sex] = indTraitsSums.sumDist1[sex] / popsize; + mnDist2[sex] = indTraitsSums.sumDist2[sex] / popsize; + mnProp1[sex] = indTraitsSums.sumProp1[sex] / popsize; + if (popsize > 1) { + sdDist1[sex] = indTraitsSums.ssqDist1[sex] / popsize - mnDist1[sex] * mnDist1[sex]; + if (sdDist1[sex] > 0.0) sdDist1[sex] = sqrt(sdDist1[sex]); else sdDist1[sex] = 0.0; + sdDist2[sex] = indTraitsSums.ssqDist2[sex] / popsize - mnDist2[sex] * mnDist2[sex]; + if (sdDist2[sex] > 0.0) sdDist2[sex] = sqrt(sdDist2[sex]); else sdDist2[sex] = 0.0; + sdProp1[sex] = indTraitsSums.ssqProp1[sex] / popsize - mnProp1[sex] * mnProp1[sex]; + if (sdProp1[sex] > 0.0) sdProp1[sex] = sqrt(sdProp1[sex]); else sdProp1[sex] = 0.0; + sdStepL[sex] = indTraitsSums.ssqStepL[sex] / popsize - mnStepL[sex] * mnStepL[sex]; + } + } + } outtraits << "\t" << mnDist1[0] << "\t" << sdDist1[0]; outtraits << "\t" << mnDist1[1] << "\t" << sdDist1[1]; - if (trfr.twinKern) - { + if (trfr.twinKern) { outtraits << "\t" << mnDist2[0] << "\t" << sdDist2[0]; outtraits << "\t" << mnDist2[1] << "\t" << sdDist2[1]; outtraits << "\t" << mnProp1[0] << "\t" << sdProp1[0]; @@ -937,56 +1307,74 @@ traitsums SubCommunity::outTraits(Landscape* pLandscape, int rep, int yr, int ge } } 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]; + double mnDist1 = 0.0, mnDist2 = 0.0, mnProp1 = 0.0, popsize = 0.0; + double sdDist1 = 0.0, sdDist2 = 0.0, sdProp1 = 0.0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnDist1 += indTraitsSums.sumDist1[sex]; + mnDist2 += indTraitsSums.sumDist2[sex]; + mnProp1 += indTraitsSums.sumProp1[sex]; + popsize += indTraitsSums.ninds[sex]; + sdDist1 += indTraitsSums.ssqDist1[sex]; + sdDist2 += indTraitsSums.ssqDist2[sex]; + sdProp1 += indTraitsSums.ssqProp1[sex]; + } + mnDist1 /= popsize; + mnDist2 /= popsize; + mnProp1 /= popsize; + if (popsize > 1) { + sdDist1 = sdDist1 / popsize - mnDist1 * mnDist1; + sdDist2 = sdDist2 / popsize - mnDist2 * mnDist2; + sdProp1 = sdProp1 / popsize - mnProp1 * mnProp1; + sdDist1 = sdDist1 == 0.0 ? 0.0 : sqrt(sdDist1); + sdDist2 = sdDist2 == 0.0 ? 0.0 : sqrt(sdDist2); + sdProp1 = sdProp1 == 0.0 ? 0.0 : sqrt(sdProp1); + } + else { + sdDist1 = 0.0; + sdDist2 = 0.0; + sdProp1 = 0.0; + } + outtraits << "\t" << mnDist1 << "\t" << sdDist1; + if (trfr.twinKern) { + outtraits << "\t" << mnDist2 << "\t" << sdDist2; + outtraits << "\t" << mnProp1 << "\t" << sdProp1; } } } } - } - 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.indVar) { + if (sett.sexDep) { + + vector mnS0(2, 0.0), mnAlpha(2, 0.0), mnBeta(2, 0.0), sdS0(2, 0.0), sdAlpha(2, 0.0), sdBeta(2, 0.0); + + for (int sex = 0; sex < gMaxNbSexes; sex++) { + + // individuals may have been counted by sex if there was + // sex dependency in another dispersal phase + double popsize = static_cast(indTraitsSums.ninds[sex]); + + if (popsize > 0) { + + mnS0[sex] = indTraitsSums.sumS0[sex] / popsize; + mnAlpha[sex] = indTraitsSums.sumAlphaS[sex] / popsize; + mnBeta[sex] = indTraitsSums.sumBetaS[sex] / popsize; + + if (popsize > 1) { + sdS0[sex] = indTraitsSums.ssqS0[sex] / popsize - mnS0[sex] * mnS0[sex]; + if (sdS0[sex] > 0.0) sdS0[sex] = sqrt(sdS0[sex]); else sdS0[sex] = 0.0; + sdAlpha[sex] = indTraitsSums.ssqAlphaS[sex] / popsize - mnAlpha[sex] * mnAlpha[sex]; + if (sdAlpha[sex] > 0.0) sdAlpha[sex] = sqrt(sdAlpha[sex]); else sdAlpha[sex] = 0.0; + sdBeta[sex] = indTraitsSums.ssqBetaS[sex] / popsize - mnBeta[sex] * mnBeta[sex]; + if (sdBeta[sex] > 0.0) sdBeta[sex] = sqrt(sdBeta[sex]); else sdBeta[sex] = 0.0; + } + else { + sdS0[sex] = sdAlpha[sex] = sdBeta[sex] = 0.0; + } + } + } + outtraits << "\t" << mnS0[0] << "\t" << sdS0[0]; outtraits << "\t" << mnS0[1] << "\t" << sdS0[1]; outtraits << "\t" << mnAlpha[0] << "\t" << sdAlpha[0]; @@ -995,35 +1383,86 @@ traitsums SubCommunity::outTraits(Landscape* pLandscape, int rep, int yr, int ge 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]; + double mnS0 = 0, mnAlpha = 0, mnBeta = 0, popsize = 0; + double sdS0 = 0, sdAlpha = 0, sdBeta = 0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnS0 += indTraitsSums.sumS0[sex]; + mnAlpha += indTraitsSums.sumAlphaS[sex]; + mnBeta += indTraitsSums.sumBetaS[sex]; + popsize += indTraitsSums.ninds[sex]; + sdS0 += indTraitsSums.ssqS0[sex]; + sdAlpha += indTraitsSums.ssqAlphaS[sex]; + sdBeta += indTraitsSums.ssqBetaS[sex]; + } + mnS0 /= popsize; + mnAlpha /= popsize; + mnBeta /= popsize; + if (popsize > 1) { + sdS0 = sdS0 / popsize - mnS0 * mnS0; + sdAlpha = sdAlpha / popsize - mnAlpha * mnAlpha; + sdBeta = sdBeta / popsize - mnBeta * mnBeta; + sdS0 = sdS0 == 0.0 ? 0.0 : sqrt(sdS0); + sdAlpha = sdAlpha == 0.0 ? 0.0 : sqrt(sdAlpha); + sdBeta = sdBeta == 0.0 ? 0.0 : sqrt(sdBeta); + } + else { + sdS0 = 0.0; + sdAlpha = 0.0; + sdBeta = 0.0; + } + outtraits << "\t" << mnS0 << "\t" << sdS0; + outtraits << "\t" << mnAlpha << "\t" << sdAlpha; + outtraits << "\t" << mnBeta << "\t" << sdBeta; } } - } - 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]; - } - } - } + // Genetic load + if (pSpecies->getNbGenLoadTraits() > 0) { + + if (pSpecies->getDemogrParams().repType > 0) { // sexual model + vector mnGenFitness(2, 0.0), sdGenFitness(2, 0.0); + + for (int sex = 0; sex < gMaxNbSexes; sex++) { + double popsize = static_cast(indTraitsSums.ninds[sex]); + mnGenFitness[sex] = indTraitsSums.sumGeneticFitness[sex] + / popsize; + if (popsize > 1) { + sdGenFitness[sex] = indTraitsSums.ssqGeneticFitness[sex] + / popsize - mnGenFitness[sex] + * mnGenFitness[sex]; + if (sdGenFitness[sex] > 0.0) + sdGenFitness[sex] = sqrt(sdGenFitness[sex]); + else sdGenFitness[sex] = 0.0; + } + else { + sdGenFitness[sex] = 0.0; + } + } + outtraits << "\t" << mnGenFitness[0] << "\t" << sdGenFitness[0]; + outtraits << "\t" << mnGenFitness[1] << "\t" << sdGenFitness[1]; + } + else { // asexual + + double mnGenFitness = 0.0, popsize = 0.0, sdGenFitness= 0.0; + for (int sex = 0; sex < gMaxNbSexes; sex++) { + mnGenFitness += indTraitsSums.sumGeneticFitness[sex]; + popsize += indTraitsSums.ninds[sex]; + sdGenFitness += indTraitsSums.ssqGeneticFitness[sex]; + } + mnGenFitness /= popsize; + if (popsize > 1) { + sdGenFitness = sdGenFitness / popsize - mnGenFitness * mnGenFitness; + sdGenFitness = sdGenFitness == 0.0 ? 0.0 : sqrt(sdGenFitness); + } + else sdGenFitness = 0.0; + outtraits << "\t" << mnGenFitness << "\t" << sdGenFitness; + } + } + outtraits << endl; + } // end trait-per-cell output + + } + } // end population loop return ts; } diff --git a/SubCommunity.h b/SubCommunity.h index 1576117..096782d 100644 --- a/SubCommunity.h +++ b/SubCommunity.h @@ -1,47 +1,47 @@ /*---------------------------------------------------------------------------- - * - * Copyright (C) 2020 Greta Bocedi, Stephen C.F. Palmer, Justin M.J. Travis, Anne-Kathleen Malchow, Damaris Zurell - * + * + * 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. + RangeShifter v2.0 SubCommunity -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 + Implements the SubCommunity class -Authors: Greta Bocedi & Steve Palmer, University of Aberdeen + 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. -Last updated: 26 October 2021 by Steve Palmer + 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: 25 June 2021 by Greta Bocedi + + ------------------------------------------------------------------------------*/ #ifndef SubCommunityH #define SubCommunityH @@ -60,7 +60,7 @@ using namespace std; class SubCommunity { public: - SubCommunity(Patch*,int); + SubCommunity(Patch*, int); ~SubCommunity(void); int getNum(void); Patch* getPatch(void); @@ -69,8 +69,8 @@ class SubCommunity { // 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); + 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 @@ -91,11 +91,16 @@ class SubCommunity { bool // TRUE for a patch-based model, FALSE for a cell-based model ); void emigration(void); + + // Remove emigrants from the matrix subcommunity and add to a map of vectors + void disperseMatrix( + std::map>& + ); // Remove emigrants from their natal patch and add to a map of vectors - void initiateDispersal( + void recruitDispersers( std::map>& ); -// Add an individual into the local population of its species in the patch + // Add an individual into the local population of its species in the patch void recruit( Individual*, // pointer to Individual Species* // pointer to Species @@ -105,51 +110,60 @@ class SubCommunity { std::vector&, // vector of pointers to Individuals 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 + + // Determine whether there is a potential mate present in a patch which a potential + // settler has reached + static bool matePresent( + Species* pSpecies, + Cell*, // pointer to the Cell which the potential settler has reached + short // sex of the required mate (0 = female, 1 = male) ); -#else - int transfer( // Transfer through matrix - run for matrix SubCommunity only + + static int resolveTransfer( // Executed for a given vector of individuals + std::map>& dispersingInds, Landscape*, // pointer to Landscape short // landscape change index ); + +#if RS_RCPP + static int resolveSettlement( // Executed for a given vector of individuals + std::map>& dispersingInds, + Landscape* pLandscape, + short // year + ); +#else + static int resolveSettlement( // Executed for a given vector of individuals + std::map>& dispersingInds, + Landscape* pLandscape + ); #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( + + // Remove emigrants from the vectors map and transfer to SubCommunity in which + // their destination co-ordinates fall + static void completeDispersal( + std::map>&, // per-species map of vectors of individuals Landscape*, // pointer to Landscape bool // TRUE to increment connectivity totals ); - void survival0( // Determine survival & development - short, // option0: 0 = stage 0 (juveniles) only - // 1 = all stages - // 2 = stage 1 and above (all non-juvs) + + void survival0(short option0, short option1); + void survival1(); + + 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 survival1(); // Apply survival changes to the population - inline void survival( - short part, // part: 0 = determine survival & development, - // 1 = apply survival changes to the population - short option0, // option0: 0 = stage 0 (juveniles) only ) - // 1 = all stages ) used by part 0 only - // 2 = stage 1 and above (all non-juvs) ) - short option1 // option1: 0 - development only (when survival is annual) - // 1 - development and survival - ) { - if (part == 0) { - return survival0(option0, option1); - } - else { - return survival1(); - } - } + // 1 - development and survival + ); + void ageIncrement(void); + // Find the population of a given species in a given patch - Population* findPop(Species*,Patch*); + Population* findPop(Species*, Patch*); + void createOccupancy( int // no. of rows = (no. of years / interval) + 1 ); @@ -185,21 +199,19 @@ class SubCommunity { int, // year int // generation ); - void outGenFinishReplicate(); // Close genetics file - void outGenStartReplicate( // Open genetics file and write header record - int, // replicate - int // Landscape number - ); - void outGenetics( // Write records to genetics file - int, // replicate - int // year - ); - bool outTraitsFinishLandscape(); // Close traits file - bool outTraitsStartLandscape( // Open traits file and write header record + bool outTraitsHeaders( // Open traits file and write header record Landscape*, // pointer to Landscape Species*, // pointer to Species int // Landscape number ); + + + // Close traits file + bool outTraitsFinishLandscape(); + + // Open traits file and write header record + bool outTraitsStartLandscape(Landscape* pLandscape, Species* pSpecies, int landNr); + traitsums outTraits( // Write records to traits file and return aggregated sums Landscape*, // pointer to Landscape int, // replicate @@ -207,9 +219,9 @@ class SubCommunity { int, // generation bool // true if called to summarise data at community level ); - int stagePop( // Population size of a specified stage + int getNbInds( // Population size of a specified stage int // stage - ); + ) const; private: int subCommNum; // SubCommunity number @@ -221,9 +233,9 @@ class SubCommunity { }; -extern paramGrad *paramsGrad; -extern paramStoch *paramsStoch; -extern paramInit *paramsInit; +extern paramGrad* paramsGrad; +extern paramStoch* paramsStoch; +extern paramInit* paramsInit; //--------------------------------------------------------------------------- #endif diff --git a/TraitFactory.h b/TraitFactory.h new file mode 100644 index 0000000..5a9bba0 --- /dev/null +++ b/TraitFactory.h @@ -0,0 +1,34 @@ +#ifndef TRAITFACTORYH +#define TRAITFACTORYH + +#include + +#include "SpeciesTrait.h" +#include "NeutralTrait.h" +#include "DispersalTrait.h" +#include "GeneticFitnessTrait.h" + +// Create handled pointers to a new trait +class TraitFactory +{ +public: + TraitFactory() {}; + + unique_ptr Create(const TraitType traitType, SpeciesTrait* pSpTrait) + { + if (traitType == NEUTRAL) { + return make_unique(pSpTrait); + } + else if (traitType == GENETIC_LOAD1 + || traitType == GENETIC_LOAD2 + || traitType == GENETIC_LOAD3 + || traitType == GENETIC_LOAD4 + || traitType == GENETIC_LOAD5) { + return make_unique(pSpTrait); + } + else { + return make_unique(pSpTrait); + } + } +}; +#endif \ No newline at end of file diff --git a/unit_tests/testIndividual.cpp b/unit_tests/testIndividual.cpp new file mode 100644 index 0000000..2955609 --- /dev/null +++ b/unit_tests/testIndividual.cpp @@ -0,0 +1,1406 @@ +#ifdef UNIT_TESTS + +#include "../Individual.h" +#include "../Population.h" + +void testTransferKernels() { + + // Simple 3*3 cell-based landscape layout + const int lsDim = 3; + landParams ls_params = createDefaultLandParams(lsDim); + + // Set dispersal distances that are almost guaranteed + // to reach or fail to reach final cell + const float meanDistSuccess = static_cast(ls_params.dimX); // > 99% success + const float meanDistFailure = 0.1; // 0% success + + Landscape ls; + ls.setLandParams(ls_params, true); + + // Two suitable cells in opposite corners + Cell* init_cell = new Cell(0, 0, 0, 0); + Cell* final_cell = new Cell(ls_params.dimX - 1, ls_params.dimY - 1, 0, 0); + ls.setCellArray(); + ls.addCellToLand(init_cell); + ls.addCellToLand(final_cell); + + // Set up species + Species sp; + // Habitat codes + sp.createHabK(1); + sp.setHabK(0, 100.0); // one habitat with K = 100 + // Demography + demogrParams d; + d.stageStruct = false; + sp.setDemogr(d); + // Transfer rules + transferRules trfr; + trfr.indVar = trfr.sexDep = trfr.stgDep = false; + trfr.twinKern = trfr.distMort = false; + sp.setTrfrRules(trfr); + sp.setFullKernel(false); + + // Transfer mortality params + trfrMortParams mort; + mort.fixedMort = 0.0; + sp.setMortParams(mort); + // Settlement + settleRules sett; + sett.wait = false; + sp.setSettRules(0, 0, sett); + + // Set up patches + ls.allocatePatches(&sp); + ls.updateCarryingCapacity(&sp, 0, 0); + Patch* init_patch = init_cell->getPatch(); + + // Default distances + trfrKernelParams kern; + kern.meanDist1 = meanDistSuccess; + sp.setSpKernTraits(0, 0, kern, ls_params.resol); + Individual ind1(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + int isDispersing = ind1.moveKernel(&ls, &sp, false); + + // After moving, individual should be in the only available cell + Cell* curr_cell = ind1.getCurrCell(); + assert(curr_cell != init_cell); + assert(curr_cell == final_cell); + assert(ind1.getStatus() == 2); // potential settler + + // If no cell within reasonable dispersal reach, individual does not move and dies + kern.meanDist1 = meanDistFailure; + sp.overrideKernels(0, 0, kern); + Individual ind2(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); // reset individual + isDispersing = ind2.moveKernel(&ls, &sp, false); + curr_cell = ind2.getCurrCell(); + assert(ind2.getStatus() == 6); // RIP in peace + assert(curr_cell == init_cell); + + // Twin kernels + trfr.twinKern = true; + sp.setTrfrRules(trfr); + kern.meanDist1 = meanDistFailure; + kern.meanDist2 = meanDistSuccess; + kern.probKern1 = 1.0; // 2nd kernel never used + sp.overrideKernels(0, 0, kern); + Individual ind3(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + isDispersing = ind3.moveKernel(&ls, &sp, false); + assert(ind3.getStatus() == 6); // dead, could not reach destination cell + kern.probKern1 = 0.0; // always use 2nd kernel + sp.overrideKernels(0, 0, kern); + Individual ind4(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + isDispersing = ind4.moveKernel(&ls, &sp, false); + assert(ind4.getStatus() == 2); + // reset + trfr.twinKern = false; + sp.setTrfrRules(trfr); + kern.probKern1 = 1.0; + sp.overrideKernels(0, 0, kern); + + // Sex-dependent dispersal distances + // female very unlikely to reach suitable cell + // male easily reaches suitable cell + trfr.sexDep = true; + sp.setTrfrRules(trfr); + trfrKernelParams kern_f = kern; + kern_f.meanDist1 = meanDistFailure; + sp.overrideKernels(0, 0, kern_f); + trfrKernelParams kern_m = kern; + kern_m.meanDist1 = meanDistSuccess; + sp.overrideKernels(0, 1, kern_m); + + Individual ind5(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); // female as default + isDispersing = ind5.moveKernel(&ls, &sp, false); + assert(ind5.getStatus() == 6); // dead, could not reach destination + + Individual ind6(&sp, init_cell, init_patch, 1, 0, 0, 1.0, false, 0); // male + assert(ind6.getSex() == 1); + isDispersing = ind6.moveKernel(&ls, &sp, false); + assert(ind6.getStatus() == 2); + // reset + trfr.sexDep = false; + sp.setTrfrRules(trfr); + + // Stage-dependent + trfr.stgDep = true; + sp.setTrfrRules(trfr); + trfrKernelParams kern_juv = kern; + kern_juv.meanDist1 = meanDistFailure; // juveniles very unlikely to reach suitable cell + sp.overrideKernels(0, 0, kern_juv); + trfrKernelParams kern_adult = kern; + kern_adult.meanDist1 = meanDistSuccess; // adults easily reach suitable cell + sp.overrideKernels(1, 0, kern_adult); + + Individual ind7(&sp, init_cell, init_patch, 0, 0, 0, 0.0, false, 0); // juvenile + isDispersing = ind7.moveKernel(&ls, &sp, false); + assert(ind7.getStatus() == 6); + + Individual ind8(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); // adult by default + isDispersing = ind8.moveKernel(&ls, &sp, false); + assert(ind8.getStatus() == 2); + // reset + trfr.stgDep = false; + sp.setTrfrRules(trfr); + + /* Boundaries: dispersal distance overshoots + Only adjacent cells are available + ----- + -ooo- + -oio- + -ooo- + ----- + */ + ls.resetLand(); + ls_params = createDefaultLandParams(5); + ls.setLandParams(ls_params, true); + ls.setCellArray(); // reset cells + + vector cells; + // Set central cell and all adjacent + for (int x = ls_params.minX + 1; x < ls_params.maxX; ++x) { + for (int y = ls_params.minY + 1; y < ls_params.maxY; ++y) { + cells.push_back(new Cell(x, y, 0, 0)); + } + } + + for (auto c : cells) ls.addCellToLand(c); + ls.allocatePatches(&sp); + ls.updateCarryingCapacity(&sp, 0, 0); + init_cell = cells[4]; // central cell + init_patch = init_cell->getPatch(); + + kern.meanDist1 = 100; // overshoots *most* of the time... + sp.setSpKernTraits(0, 0, kern, ls_params.resol); + Individual ind9(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); // reset individual + + // Non-absorbing boundaries + bool absorbing_boundaries{ false }; + isDispersing = ind9.moveKernel(&ls, &sp, absorbing_boundaries); + curr_cell = ind9.getCurrCell(); + assert(curr_cell != init_cell); // ...should be able to move eventually + assert(ind9.getStatus() == 2); + + // Absorbing boundaries + absorbing_boundaries = true; + Individual ind10(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); // reset individual + isDispersing = ind10.moveKernel(&ls, &sp, absorbing_boundaries); + curr_cell = ind10.getCurrCell(); + assert(ind10.getStatus() == 6); + assert(curr_cell == 0); // out of the landscape + + // Dispersal-related mortality + + // Fixed mortality + mort.fixedMort = 1.0; // Individual *will* die after any step + sp.setMortParams(mort); + trfr.distMort = false; + sp.setTrfrRules(trfr); + Individual ind11(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + isDispersing = ind11.moveKernel(&ls, &sp, false); + assert(ind11.getStatus() == 7); + + // Distance-dependent mortality + trfr.distMort = true; + sp.setTrfrRules(trfr); + mort.mortAlpha = 1000.0; // very steep threshold + mort.mortBeta = 0.001; // very small distance + sp.setMortParams(mort); + kern.meanDist1 = 5; // very likely to go over threshold + absorbing_boundaries = true; + sp.setSpKernTraits(0, 0, kern, ls_params.resol); + Individual ind12(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + isDispersing = ind12.moveKernel(&ls, &sp, absorbing_boundaries); + assert(ind12.getStatus() == 7); + + mort.mortBeta = 30; // very large distance, unlikely to draw + sp.setMortParams(mort); + Individual ind13(&sp, init_cell, init_patch, 1, 0, 0, 0.0, false, 0); + isDispersing = ind13.moveKernel(&ls, &sp, false); + assert(ind13.getStatus() != 7); + + // Reset mortality params + trfr.distMort = false; + mort.fixedMort = 0.0; + sp.setTrfrRules(trfr); + sp.setMortParams(mort); +} + +void testTransferCRW() { + + // Simple 5*5 cell-based landscape layout + int lsDim = 5; + landParams ls_params = createDefaultLandParams(lsDim); + double cellDiagLength = ls_params.resol * SQRT2; + + Landscape ls; + ls.setLandParams(ls_params, true); + + // All cells are suitable + const int hab_index = 0; + vector cell_vec; + for (int x = ls_params.minX; x < ls_params.dimX; ++x) { + for (int y = ls_params.minY; y < ls_params.dimY; ++y) { + cell_vec.push_back(new Cell(x, y, 0, hab_index)); + } + } + Cell* init_cell = cell_vec[12]; // central + ls.setCellArray(); + for (auto c : cell_vec) ls.addCellToLand(c); + + // Set up species + Species sp; + + // Habitat codes + sp.createHabK(1); + sp.setHabK(hab_index, 100.0); // one habitat with K = 100 + + // Habitat-dependent mortality + sp.createHabCostMort(1); + + // Transfer rules + transferRules trfr; + trfr.indVar = false; + trfr.habMort = false; + trfr.moveType = 2; // CRW + sp.setTrfrRules(trfr); + + // Transfer CRW traits + trfrMovtParams m; + m.stepMort = 0.0; + m.stepLength = cellDiagLength; // guaranteed to move out + m.rho = 1.0; + m.straightenPath = false; + sp.setSpMovtTraits(m); + + // Settlement rules + settleRules sett; + sett.wait = false; + sp.setSettRules(0, 0, sett); + settleSteps steps; + steps.maxSteps = 1; + steps.minSteps = 1; + steps.maxStepsYr = 1; + sp.setSteps(0, 0, steps); + + // Set up patches + ls.allocatePatches(&sp); + ls.updateCarryingCapacity(&sp, 0, 0); + Patch* init_patch = init_cell->getPatch(); + + // Create and set up individual + Individual ind0(&sp, init_cell, init_patch, 1, 0, 0, 0.0, true, 2); + + // Set status + assert(ind0.getStatus() == 0); // default status, not emigrating + int isDispersing = ind0.moveStep(&ls, &sp, hab_index, false); + assert(ind0.getStatus() == 0); // status didn't change + assert(ind0.getCurrCell() == init_cell); // not emigrating so didn't move + + //---------------------// + // Test per-step mortality + //---------------------// + + m.stepMort = 1.0; // should die + sp.setSpMovtTraits(m); + Individual ind1(&sp, init_cell, init_patch, 0, 0, 0, 0.0, true, 2); + // force-set path bc for some reason path gets deallocated upon exiting constructor?? + ind1.setStatus(1); + isDispersing = ind1.moveStep(&ls, &sp, hab_index, false); + // Individual begins in natal patch so mortality is disabled + assert(ind1.getStatus() != 7); + // Individual should be in a different patch + Cell* first_step_cell = ind1.getCurrCell(); + assert(first_step_cell != init_cell); + assert(first_step_cell->getPatch() != init_patch); + + ind1.setStatus(1); // emigrating again + + // Individual should die on second step + isDispersing = ind1.moveStep(&ls, &sp, hab_index, false); + assert(ind1.getCurrCell() == first_step_cell); // shouldn't have moved + assert(ind1.getStatus() == 7); // died by transfer + m.stepMort = 0.0; // not dying + sp.setSpMovtTraits(m); + + // Test habitat-depdt mortality + // ... + + //----------------// + // Test step size + //----------------// + + ls = Landscape(); + ls.setLandParams(ls_params, true); + sp.createHabK(2); + const int hab_suitable = 0; + const int hab_unsuitable = 1; + sp.setHabK(hab_suitable, 100.0); + sp.setHabK(hab_unsuitable, 0.0); + + // Initial cell unsuitable, suitable cell in opposite corner + init_cell = new Cell(0, 0, 0, hab_unsuitable); + Cell* final_cell = new Cell(ls_params.dimX - 1, ls_params.dimY - 1, 0, hab_suitable); + ls.setCellArray(); + ls.addCellToLand(init_cell); + ls.addCellToLand(final_cell); + ls.allocatePatches(&sp); + ls.updateCarryingCapacity(&sp, 0, 0); + // Init cell is NOT in natal patch + Patch* natalPatch = new Patch(0, 0); + init_patch = init_cell->getPatch(); + + // Step length is too short + m.stepLength = 0.1; // will not reach final cell + m.rho = 0.0; // random angle + sp.setSpMovtTraits(m); + steps.minSteps = 1; + steps.maxStepsYr = 2; + steps.maxSteps = 3; + sp.setSteps(0, 0, steps); + Individual ind2(&sp, init_cell, natalPatch, 0, 0, 0, 0.0, true, 2); + ind2.setStatus(1); // dispersing + // First step - still in unsuitable cell so still dispersing + isDispersing = ind2.moveStep(&ls, &sp, hab_index, false); + assert(ind2.getCurrCell() == init_cell); + assert(ind2.getStatus() == 1); // still dispersing + // Second step - reaching max steps this year, wait next year + isDispersing = ind2.moveStep(&ls, &sp, hab_index, false); + assert(ind2.getCurrCell() == init_cell); + assert(ind2.getStatus() == 3); // waiting for next year + ind2.setStatus(1); // dispersing again + // Third step - reaching max steps, dies in unsuitable cell + isDispersing = ind2.moveStep(&ls, &sp, hab_index, false); + assert(ind2.getCurrCell() == init_cell); + assert(ind2.getStatus() == 6); // died while dispersing + + // Step length too long + m.stepLength = ls_params.dimX * SQRT2 * 1.5; // overshoots + sp.setSpMovtTraits(m); + Individual ind3(&sp, init_cell, init_patch, 0, 0, 0, 0.0, true, 2); + ind3.setStatus(1); // dispersing + steps.minSteps = 1; + steps.maxStepsYr = 1; + steps.maxSteps = 1; // no need to test more than one step this time + sp.setSteps(0, 0, steps); + isDispersing = ind3.moveStep(&ls, &sp, hab_index, false); + assert(ind3.getCurrCell() == init_cell); + assert(ind3.getStatus() == 6); + + // Adequate step length + m.stepLength = (ls_params.dimX - 1) * SQRT2; + sp.setSpMovtTraits(m); + Individual ind4(&sp, init_cell, natalPatch, 0, 0, 0, 0.0, true, 2); + ind4.setStatus(1); // dispersing + // Initial angle still random but should eventually reach the suitable cell + isDispersing = ind4.moveStep(&ls, &sp, hab_index, false); + assert(ind4.getStatus() == 2); + assert(ind4.getCurrCell() == final_cell); + + // If boundaries are absorbing however, most likely to die + Individual ind5(&sp, init_cell, natalPatch, 0, 0, 0, 0.0, true, 2); + ind5.setStatus(1); // dispersing + bool absorbing_boundaries = true; + isDispersing = ind5.moveStep(&ls, &sp, hab_index, absorbing_boundaries); + assert(ind5.getStatus() == 6); + assert(ind5.getCurrCell() == 0); // deref apparently + + // Correlation parameter + // If rho = 1 should move in a straight line + // Many cells, all suitable + lsDim = 10; + ls_params = createDefaultLandParams(lsDim); + ls = Landscape(); + ls.setLandParams(ls_params, true); + cell_vec.clear(); + for (int x = ls_params.minX; x < ls_params.dimX; ++x) { + for (int y = ls_params.minY; y < ls_params.dimY; ++y) { + cell_vec.push_back(new Cell(x, y, 0, hab_suitable)); + } + } + ls.setCellArray(); + for (auto c : cell_vec) ls.addCellToLand(c); + ls.allocatePatches(&sp); + ls.updateCarryingCapacity(&sp, 0, 0); + // Individual moves by 1 along the diagonal + m.stepLength = cellDiagLength; // 1 diagonal cell + m.rho = 1; // angle = previous angle + sp.setSpMovtTraits(m); + steps.maxStepsYr = steps.maxSteps = ls_params.dimX; + sp.setSteps(0, 0, steps); + Individual ind6(&sp, cell_vec[0], natalPatch, 0, 0, 0, 0.0, true, 2); + const float diag_angle = PI / 4.0; // 45 degrees + ind6.setInitAngle(diag_angle); + // Individual moves only along diagonal cells + for (int i = 1; i < ls_params.dimX; ++i) { + ind6.setStatus(1); // dispersing + isDispersing = ind6.moveStep(&ls, &sp, hab_index, false); + assert(ind6.getStatus() == 2); + assert(ind6.getCurrCell() == cell_vec[i * (ls_params.dimX + 1)]); + } +} + +void testGenetics() { + + // Individuals inherit alleles from their parents + { + // 1 - Diploid case, 2 parents with diff. alleles + { + const int genomeSz = 5; + const bool isDiploid{ true }; + SpeciesTrait* spTr = createTestEmigSpTrait(createTestGenePositions(genomeSz), isDiploid); + + // Heterozygote parent genotypes + const float valAlleleMotherA(1.0), valAlleleMotherB(2.0); + const float valAlleleFatherA(3.0), valAlleleFatherB(4.0); + auto motherGenotype = createTestGenotype(genomeSz, isDiploid, valAlleleMotherA, valAlleleMotherB); + auto fatherGenotype = createTestGenotype(genomeSz, isDiploid, valAlleleFatherA, valAlleleFatherB); + const int startMotherChr = 0; // Strand A + const int startFatherChr = 1; // Strand B + + // Create individual trait objects + DispersalTrait dispTrParent(spTr); // initialisation constructor + DispersalTrait dispTrChild(dispTrParent); // inheritance constructor + + set recomSites{}; // no recombination + + dispTrChild.triggerInherit(true, motherGenotype, recomSites, startMotherChr); + dispTrChild.triggerInherit(false, fatherGenotype, recomSites, startFatherChr); + + // Child should have inherited strand A from mother and strand B from father + float valChildAlleleA, valChildAlleleB; + for (int i = 0; i < genomeSz; i++) { + valChildAlleleA = dispTrChild.getAlleleValueAtLocus(0, i); + assert(valChildAlleleA == valAlleleMotherA); + valChildAlleleB = dispTrChild.getAlleleValueAtLocus(1, i); + assert(valChildAlleleB == valAlleleFatherB); + } + } + + // 2 - Haploid case, simply copy parent + { + const int genomeSz = 5; + const bool isDiploid{ false }; + SpeciesTrait* spTr = createTestEmigSpTrait(createTestGenePositions(genomeSz), isDiploid); + + // Heterozygote parent genotypes + const float valAlleleMother(5.0); + auto motherGenotype = createTestGenotype(genomeSz, isDiploid, valAlleleMother); + const int startMotherChr = 999; // doesn't matter, not used + + // Create individual trait objects + DispersalTrait dispTrParent(spTr); // initialisation constructor + DispersalTrait dispTrChild(dispTrParent); // inheritance constructor + + set recomSites; + for (unsigned int i = 0; i < genomeSz; i++) + recomSites.insert(i); // recombination should be ignored + + dispTrChild.triggerInherit(true, motherGenotype, recomSites, startMotherChr); + + // Child should have inherited strand B from mother + float valChildAllele; + for (int i = 0; i < genomeSz; i++) { + valChildAllele = dispTrChild.getAlleleValueAtLocus(0, i); + assert(valChildAllele == valAlleleMother); + } + } + } + + // Recombination occurs just after selected recombination site + { + const int genomeSz = 3; + const bool isDiploid{ true }; + SpeciesTrait* spTr = createTestEmigSpTrait(createTestGenePositions(genomeSz), isDiploid); + + // Create individual trait objects + DispersalTrait dispTrParent(spTr); // initialisation constructor + + // Fill mother gene sequence + const float valAlleleA(0.0), valAlleleB(1.0); + // Mother genotype: + // 000 + // 111 + auto motherGenotype = createTestGenotype(genomeSz, isDiploid, valAlleleA, valAlleleB); + + // Trigger inheritance from mother + const vector recombinationSites{0, 1, genomeSz - 1}; // should work for any position + const int startingChr{0}; // Chromosome A + + for (unsigned int site : recombinationSites) { + DispersalTrait dispTrChild(dispTrParent); // inheritance constructor + dispTrChild.triggerInherit(true, motherGenotype, set{site}, startingChr); + // for this test we ignore inheritance from father + + // Mother-inherited alleles should have value of chr A before recombination site, chr B after + float valMotherAllele; + for (int i = 0; i < genomeSz; i++) { + valMotherAllele = dispTrChild.getAlleleValueAtLocus(0, i); + assert(valMotherAllele == (i <= site ? valAlleleA : valAlleleB)); + // don't check other chromosome, empty bc we did not resolve father inheritance + } + } + } + + // Traits that are not inherited sample their mutations in initial distribution + { + const float initialAlleleVal = 0.5; + const float parentAlleleVal = 0.8; + const bool isInherited = false; + + const int genomeSz = 5; + const bool isDiploid{ false }; + + // Create species trait + const map distParams{ + // all alleles initialised at a single value + pair{GenParamType::MIN, initialAlleleVal}, + pair{GenParamType::MAX, initialAlleleVal} + }; + + const set genePositions = createTestGenePositions(genomeSz); + + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::E_D0, + sex_t::NA, + genePositions, + ExpressionType::ADDITIVE, + genePositions, // initial positions (all) + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, not used + isInherited, + 0.0, // no mutations + DistributionType::UNIFORM, distParams, // ignored + DistributionType::NONE, distParams, // no dominance, not used + isDiploid ? 2 : 1, + false + ); + + // Create individual trait objects + DispersalTrait dispTrParent(spTr); // initialisation constructor + auto parentGenotype = createTestGenotype(genomeSz, isDiploid, parentAlleleVal); + const int startMotherChr = 999; // doesn't matter, not used + dispTrParent.overwriteGenes(parentGenotype); + assert(dispTrParent.getAlleleValueAtLocus(0, 0) == parentAlleleVal); + + DispersalTrait dispTrChild(dispTrParent); // inheritance constructor + + set recomSites{genomeSz - 1}; + dispTrChild.triggerInherit(true, parentGenotype, recomSites, startMotherChr); + + // Child does not inherit from mother, + // instead allele value is initial value + float valChildAllele; + for (int i = 0; i < genomeSz; i++) { + valChildAllele = dispTrChild.getAlleleValueAtLocus(0, i); + assert(valChildAllele == initialAlleleVal); + } + } + + // Genetic fitness mutations are constrained between -1 or 1 + // + sampled dominance coefficients are always positive + { + { + // Most likely (~96%) to sample a mutation > 1 + const float gammaMutShapeParam = 5.0; + const float gammaMutScaleParam = 1.0; + // Normal centered on 0 : ~50% of sampling negative dominance coefficient + const float dominanceMeanParam = 0.0; + const float dominanceSdParam = 1.0; + + const int genomeSz = 5; + const bool isDiploid{ false }; + + // Create species trait + const map mutationParams{ + pair{GenParamType::SHAPE, gammaMutShapeParam}, + pair{GenParamType::SCALE, gammaMutScaleParam} + }; + const map dominanceParams{ + pair{GenParamType::MEAN, dominanceMeanParam}, + pair{GenParamType::SD, dominanceSdParam} + }; + const map placeholderParams = mutationParams; + + const set genePositions = createTestGenePositions(genomeSz); + + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD1, + sex_t::NA, + genePositions, + ExpressionType::MULTIPLICATIVE, + genePositions, + DistributionType::NONE, placeholderParams, // not used for genetic load + DistributionType::NONE, dominanceParams, + true, + 1.0, // every site mutates + DistributionType::GAMMA, mutationParams, + DistributionType::NORMAL, dominanceParams, + isDiploid ? 2 : 1, + false + ); + + // Create individual trait object + GeneticFitnessTrait traitInd(spTr); // initialisation constructor + traitInd.mutate(); + for (int i = 0; i < genomeSz; i++) { + float valAllele = traitInd.getAlleleValueAtLocus(0, i); + assert(valAllele <= 1.0); + float domCoef = traitInd.getDomCoefAtLocus(0, i); + assert(domCoef >= 0.0); + } + } + + { + // 1/2 chance to sample a mutation < -1 + const float normalMutMeanParam = 0; + const float normalMutSdParam = 1.0; + + const int genomeSz = 10; + const bool isDiploid{ false }; + + // Create species trait + const map mutationParams{ + // all alleles initialised at a single value + pair{GenParamType::MEAN, normalMutMeanParam}, + pair{GenParamType::SD, normalMutSdParam} + }; + const map placeholderParams = mutationParams; + + const set genePositions = createTestGenePositions(genomeSz); + + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD1, + sex_t::NA, + genePositions, + ExpressionType::MULTIPLICATIVE, + genePositions, + DistributionType::NONE, placeholderParams, // not used for genetic load + DistributionType::NONE, placeholderParams, // doesn't matter for this test + true, + 1.0, // every site mutates + DistributionType::NORMAL, mutationParams, + DistributionType::NORMAL, placeholderParams, // doesn't matter for this test + isDiploid ? 2 : 1, + false + ); + + // Create individual trait object + GeneticFitnessTrait traitInd(spTr); // initialisation constructor + traitInd.mutate(); + for (int i = 0; i < genomeSz; i++) { + float valAllele = traitInd.getAlleleValueAtLocus(0, i); + assert(valAllele > -1.0); + } + } + } +} + +bool haveSameEmigD0Allele(const Individual& indA, const Individual& indB, const int& position, short whichHaplo = 0) { + return indA.getTrait(E_D0)->getAlleleValueAtLocus(whichHaplo, position) + == indB.getTrait(E_D0)->getAlleleValueAtLocus(whichHaplo, position); +} + +void testIndividual() { + + // Kernel-based transfer + testTransferKernels(); + + // Correlated random walk (CRW) + testTransferCRW(); + + testGenetics(); + + // Genetic linkage + Chromosome breaks + // Considering diallelic genes A, B, C, D with: + // A, B, C are on chr.1, D is on chr.2 + // A, B are adjacent, C sits on the other end of chr.1 + // C, D have adjacent positions in genome (but are on separate chr.) + // AB------------C//D + // We simulate 100 inheritance + recombination processes and expect that: + // 1. freq(A,B have same alleles) >> freq(A,C have same alleles) + // 2. 0.65 > freq(C,D have same alleles) > 0.35 despite being adjacent because of chrom. break + // (both freq. have p < 0.001 from a binomial with p 0.5 and 100 trials) + { + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + const float recombinationRate = 0.01; + const int genomeSz = 10; + + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz-2, genomeSz - 1}, // two chromosomes + genomeSz, + recombinationRate, + set{}, "none", set{}, 0 // no output so no sampling + ); + + int posA = 0; + int posB = 1; + int posC = genomeSz - 2; + int posD = genomeSz - 1; + const set genePositions = {posA, posB, posC, posD}; + const bool isDiploid{ true }; + SpeciesTrait* spTr = createTestEmigSpTrait(genePositions, isDiploid); + pSpecies->addTrait(TraitType::E_D0, *spTr); + + Individual indMother = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + Individual indFather = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 1.0, false, 0); + indMother.setUpGenes(pSpecies, 1.0); + indFather.setUpGenes(pSpecies, 1.0); + + int countRecombineTogetherAB = 0; + int countRecombineTogetherAC = 0; + int countRecombineTogetherCD = 0; + + const int nbTrials = 100; + for (int i = 0; i < nbTrials; ++i) + { + Individual indChild = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + indChild.inheritTraits(pSpecies, &indMother, &indFather, 1.0); + + bool hasInheritedA0 = haveSameEmigD0Allele(indChild, indMother, posA); + bool hasInheritedB0 = haveSameEmigD0Allele(indChild, indMother, posB); + bool hasInheritedC0 = haveSameEmigD0Allele(indChild, indMother, posC); + bool hasInheritedD0 = haveSameEmigD0Allele(indChild, indMother, posD); + + countRecombineTogetherAB += (hasInheritedA0 && hasInheritedB0) + || (!hasInheritedA0 && !hasInheritedB0); + countRecombineTogetherAC += (hasInheritedA0 && hasInheritedC0) + || (!hasInheritedA0 && !hasInheritedC0); + countRecombineTogetherCD += (hasInheritedC0 && hasInheritedD0) + || (!hasInheritedC0 && !hasInheritedD0); + } + assert(countRecombineTogetherAB > countRecombineTogetherAC); + assert(35 < countRecombineTogetherCD && countRecombineTogetherCD < 65); + } + + // Sex-specific traits use the correct genes + /// Set up a sex-dependent emigration probability with male and female loci + /// Emigration probability is 1 initially, but female trait mutates. + { + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Species-level paramters + const int genomeSz = 6; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + emigRules emig; + emig.indVar = true; + emig.sexDep = true; + emig.densDep = false; + pSpecies->setEmigRules(emig); + + // Create species trait + //// Shared params + const map initParams{ + // all values are 1 + pair{GenParamType::MIN, 1.0}, + pair{GenParamType::MAX, 1.0} + }; + const map mutationParams{ + // reduction of emigration probability + pair{GenParamType::MIN, -0.5}, + pair{GenParamType::MAX, -0.25} + }; + const bool isDiploid{ true }; + + //// Sex-specific params + const set maleGenePositions = { 0, 2, 4 }; + const set femaleGenePositions = { 1, 3, 5 }; + const float maleMutationRate = 0.0; + const float femaleMutationRate = 1.0; + + SpeciesTrait* spTrM = new SpeciesTrait( + TraitType::E_D0_M, + sex_t::MAL, + maleGenePositions, + ExpressionType::AVERAGE, + maleGenePositions, + // Set all initial alleles values to 1 + DistributionType::UNIFORM, initParams, + DistributionType::NONE, initParams, // no dominance, params are ignored + true, // isInherited + maleMutationRate, // does not mutate + DistributionType::UNIFORM, mutationParams, // not used + DistributionType::NONE, initParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + pSpecies->addTrait(TraitType::E_D0_M, *spTrM); + + SpeciesTrait* spTrF = new SpeciesTrait( + TraitType::E_D0_F, + sex_t::FEM, + femaleGenePositions, + ExpressionType::AVERAGE, + femaleGenePositions, + // Set all initial alleles values to 1 + DistributionType::UNIFORM, initParams, + DistributionType::NONE, initParams, // no dominance, params are ignored + true, // isInherited + femaleMutationRate, // does mutate + DistributionType::UNIFORM, mutationParams, // not used + DistributionType::NONE, initParams, + isDiploid ? 2 : 1, + false + ); + pSpecies->addTrait(TraitType::E_D0_F, *spTrF); + + // Set up male and female individuals, trigger mutations + Individual indFemale = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + Individual indMale = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 1.0, false, 0); + indFemale.setUpGenes(pSpecies, 1.0); + indMale.setUpGenes(pSpecies, 1.0); + indFemale.triggerMutations(pSpecies); + indMale.triggerMutations(pSpecies); + + // Male should use male trait, still 1 + // Female should use female trait, has mutated + emigTraits femaleEmig = indFemale.getIndEmigTraits(); + emigTraits maleEmig = indMale.getIndEmigTraits(); + assert(femaleEmig.d0 != 1.0); + assert(maleEmig.d0 == 1.0); + } + + // Individuals use species-level trait when not individual variable, + // and individual-level trait when trait is individual variable + { + float spEmigProb = 1.0; + float indEmigProb = 0.0; + + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Species-level paramters + const int genomeSz = 1; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + emigRules emig; + emig.indVar = false; + emig.stgDep = false; emig.sexDep = false; emig.densDep = false; + pSpecies->setEmigRules(emig); + + emigTraits spEmigTraits; spEmigTraits.d0 = spEmigProb; + pSpecies->setSpEmigTraits(0, 0, spEmigTraits); + + // Create species trait + const map initParams{ + // Emigration probability is always 0 + pair{GenParamType::MIN, indEmigProb}, + pair{GenParamType::MAX, indEmigProb} + }; + const map mutationParams = initParams; // doesn't matter, not used + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::E_D0, + sex_t::NA, + set{ 0 }, // only one locus + ExpressionType::AVERAGE, + set{ 0 }, // initial positions + DistributionType::UNIFORM, initParams, + DistributionType::NONE, initParams, // no dominance, params are ignored + true, // isInherited + 0.0, // no mutation + DistributionType::UNIFORM, mutationParams, // not used + DistributionType::NONE, initParams, // no dominance, params are ignored + 2, // diploid + false + ); + pSpecies->addTrait(TraitType::E_D0, *spTr); + + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + ind.setUpGenes(pSpecies, 1.0); + + // Create population to trigger emigration selection + Population pop(pSpecies, pPatch, 0, 1.0); + pop.recruit(&ind); + assert(ind.getStatus() == 0); + pop.emigration(100.0); + + // Individual is using the species-wide emigration prob, + // so should be selected to emigrate (status 1) + assert(ind.getStatus() == 1); + + // Change rules to use individual-variable trait + ind.setStatus(0); + emig.indVar = true; + pSpecies->setEmigRules(emig); + pop.emigration(100.0); + + // Individual-level emig prob is zero, must not be emigrating; + assert(ind.getStatus() == 0); + + pop.clearInds(); // empty inds vector to not deallocate individual twice + } + + // Individuals with genetic fitness = 1 are always viable + // Individuals with genetic fitness = 0 are never viable + { + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Species-level paramters + const int genomeSz = 1; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + + // Create species trait + const map mutationParams{ + // Always sample 1 = a lethal mutation + pair{GenParamType::MIN, 1.0}, + pair{GenParamType::MAX, 1.0} + }; + const map domParams = mutationParams; // all dominance parameters are equal + const map initParams = mutationParams; // doesn't matter, not used + + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD1, + sex_t::NA, + set{ 0 }, // only one locus + ExpressionType::MULTIPLICATIVE, + set{ 0 }, + DistributionType::NONE, initParams, + DistributionType::UNIFORM, domParams, + true, // isInherited + 1.0, // will mutate + DistributionType::UNIFORM, mutationParams, // lethal mutation + DistributionType::UNIFORM, domParams, + 2, // diploid + false + ); + + pSpecies->addTrait(TraitType::GENETIC_LOAD1, *spTr); + + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + ind.setUpGenes(pSpecies, 1.0); + + // By default, all loci are initialised at 0 so individual is viable + assert(ind.getGeneticFitness() == 1.0); + assert(ind.isViable()); + + ind.triggerMutations(pSpecies); + + // Individual now bears a lethal allele + assert(ind.getGeneticFitness() == 0.0); + assert(!ind.isViable()); + } + + // A largely dominant allele overrides the expression of its homologue + { + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Species-level paramters + const int genomeSz = 1; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + + // Create template species trait + const map distParams{ + pair{GenParamType::MIN, 0.0}, + pair{GenParamType::MAX, 0.0} + }; + // Pretty empty, actual values are set below + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD1, + sex_t::NA, + set{ 0 }, // only one locus + ExpressionType::MULTIPLICATIVE, + set{ 0 }, + DistributionType::NONE, distParams, + DistributionType::UNIFORM, distParams, + true, // isInherited + 0.0, // no mutation + DistributionType::UNIFORM, distParams, + DistributionType::UNIFORM, distParams, + 2, // diploid + false + ); + pSpecies->addTrait(TraitType::GENETIC_LOAD1, *spTr); + + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + ind.setUpGenes(pSpecies, 1.0); + + const float valAlleleA = 1.0; // lethal + const float valAlleleB = 0.0; // mild + float domCoefA = 0.00001; // highly recessive + float domCoefB = 1.0; + auto viableGenotype = createTestGenotype(genomeSz, true, valAlleleA, valAlleleB, domCoefA, domCoefB); + ind.overrideGenotype(GENETIC_LOAD1, viableGenotype); + ind.triggerMutations(pSpecies); // no mutations (rate = 0) but updates genetic fitness + assert(ind.isViable()); + + domCoefA = 10000.0; // oh gosh now it's lethal AND dominant + auto lethalGenotype = createTestGenotype(genomeSz, true, valAlleleA, valAlleleB, domCoefA, domCoefB); + ind.overrideGenotype(GENETIC_LOAD1, lethalGenotype); + ind.triggerMutations(pSpecies); + assert(!ind.isViable()); + } + + // Dispersal trait alleles can take any value, but + // phenotypes are constrained to valid values + { + // Case 1 - Settlement + Kernel-based transfer + { + const int genomeSz = 4; + + const bool isDiploid{ true }; // haploid, simpler check + const float mutationRate = 0.0; // no mutations + + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Genome-level settings + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + + settleType sett; + sett.indVar = true; + sett.sexDep = false; + sett.stgDep = false; + pSpecies->setSettle(sett); + settleRules settRules; + settRules.densDep = true; + pSpecies->setSettRules(0, 0, settRules); + + transferRules trfr; + trfr.indVar = true; + trfr.usesMovtProc = false; + trfr.moveType = 0; // kernels + trfr.twinKern = false; + trfr.sexDep = false; + trfr.stgDep = false; + pSpecies->setTrfrRules(trfr); + + // Species-level traits + const map distParams{ + // prameters don't matter, allele values are overwritten below + pair{GenParamType::MIN, 1.0}, + pair{GenParamType::MAX, 1.0} + }; + SpeciesTrait* trSettProb = new SpeciesTrait( + TraitType::S_S0, + sex_t::NA, + set{ 0 }, + ExpressionType::AVERAGE, + set{ 0 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trSettAlpha = new SpeciesTrait( + TraitType::S_ALPHA, + sex_t::NA, + set{ 1 }, + ExpressionType::AVERAGE, + set{ 1 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trSettBeta = new SpeciesTrait( + TraitType::S_BETA, + sex_t::NA, + set{ 2 }, + ExpressionType::AVERAGE, + set{ 2 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trMeanKern = new SpeciesTrait( + TraitType::KERNEL_MEANDIST_1, + sex_t::NA, + set{ 3 }, + ExpressionType::ADDITIVE, + set{ 3 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + + pSpecies->addTrait(TraitType::S_S0, *trSettProb); + pSpecies->addTrait(TraitType::S_ALPHA, *trSettAlpha); + pSpecies->addTrait(TraitType::S_BETA, *trSettBeta); + pSpecies->addTrait(TraitType::KERNEL_MEANDIST_1, *trMeanKern); + + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, false, 0); + ind.setUpGenes(pSpecies, 1.0); + + // Overwrite genotypes with alleles resulting in invalid phenotypes + auto sProbGenotype = createTestGenotype(genomeSz, true, 1.1, 1.2); + auto sAlphaGenotype = createTestGenotype(genomeSz, true, -1.5, -0.5); + auto kernelDistGenotype = createTestGenotype(genomeSz, true, -0.2, -0.4); + ind.overrideGenotype(S_S0, sProbGenotype); + ind.overrideGenotype(S_ALPHA, sAlphaGenotype); + ind.overrideGenotype(KERNEL_MEANDIST_1, kernelDistGenotype); + // any value is valid for settlement beta + + ind.triggerMutations(pSpecies); // no mutations, but trigger expression + + settleTraits settTr = ind.getIndSettTraits(); + assert(settTr.s0 <= 1.0); + assert(settTr.alpha > 0.0); + kernelData trfrTr = *(static_cast(ind.getTrfrData())); + assert(trfrTr.meanDist1 >= 0.0); + } + + // Case 2 - Correlated random walk transfer + { + const int genomeSz = 2; + + const bool isDiploid{ true }; // haploid, simpler check + const float mutationRate = 0.0; // no mutations + + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Genome-level settings + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + + settleType sett; + sett.indVar = false; + pSpecies->setSettle(sett); + + transferRules trfr; + trfr.indVar = true; + trfr.usesMovtProc = true; + trfr.moveType = 2; // CRW + pSpecies->setTrfrRules(trfr); + + // Species-level traits + const map distParams{ + // prameters don't matter, allele values are overwritten below + pair{GenParamType::MIN, 1.0}, + pair{GenParamType::MAX, 1.0} + }; + SpeciesTrait* trCRWLen = new SpeciesTrait( + TraitType::CRW_STEPLENGTH, + sex_t::NA, + set{ 0 }, + ExpressionType::ADDITIVE, + set{ 0 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trCRWCorr = new SpeciesTrait( + TraitType::CRW_STEPCORRELATION, + sex_t::NA, + set{ 1 }, + ExpressionType::AVERAGE, + set{ 1 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + + pSpecies->addTrait(TraitType::CRW_STEPLENGTH, *trCRWLen); + pSpecies->addTrait(TraitType::CRW_STEPCORRELATION, *trCRWCorr); + + bool usesMovtProcess = true; + short whichMovtProcess = 2; // CRW + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, usesMovtProcess, whichMovtProcess); + ind.setUpGenes(pSpecies, 1.0); + + // Overwrite genotypes with alleles resulting in invalid phenotypes + auto crwLenGenotype = createTestGenotype(genomeSz, true, -1.1, -1.2); + auto crwCorrGenoType = createTestGenotype(genomeSz, true, 1.5, 2.0); + ind.overrideGenotype(CRW_STEPLENGTH, crwLenGenotype); + ind.overrideGenotype(CRW_STEPCORRELATION, crwCorrGenoType); + + ind.triggerMutations(pSpecies); // no mutations, but trigger expression + + crwData trfrTr = *(static_cast(ind.getTrfrData())); + assert(trfrTr.stepLength >= 0.0); + assert(trfrTr.rho <= 1.0); + } + + // Case 3 - Transfer with Stochastic Movement Simulator + { + const int genomeSz = 2; + const bool isDiploid{ true }; // haploid, simpler check + const float mutationRate = 0.0; // no mutations + + Patch* pPatch = new Patch(0, 0); + Cell* pCell = new Cell(0, 0, pPatch, 0); + + // Genome-level settings + Species* pSpecies = createDefaultSpecies(); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // one chromosome + genomeSz, + 0.0, // no recombination + set{}, "none", set{}, 0 // no output so no sampling + ); + + settleType sett; + sett.indVar = false; + pSpecies->setSettle(sett); + + transferRules trfr; + trfr.indVar = true; + trfr.usesMovtProc = true; + trfr.moveType = 1; // SMS + pSpecies->setTrfrRules(trfr); + + // Species-level traits + const map distParams{ + // prameters don't matter, allele values are overwritten below + pair{GenParamType::MIN, 1.0}, + pair{GenParamType::MAX, 1.0} + }; + SpeciesTrait* trSmsDirPers = new SpeciesTrait( + TraitType::SMS_DP, + sex_t::NA, + set{ 0 }, + ExpressionType::ADDITIVE, + set{ 0 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trSmsGoalBias = new SpeciesTrait( + TraitType::SMS_GB, + sex_t::NA, + set{ 1 }, + ExpressionType::AVERAGE, + set{ 1 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trSmsAlpha = new SpeciesTrait( + TraitType::SMS_ALPHADB, + sex_t::NA, + set{ 0 }, + ExpressionType::ADDITIVE, + set{ 0 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + SpeciesTrait* trSmsBeta = new SpeciesTrait( + TraitType::SMS_BETADB, + sex_t::NA, + set{ 1 }, + ExpressionType::AVERAGE, + set{ 1 }, + DistributionType::UNIFORM, distParams, + DistributionType::NONE, distParams, // no dominance, params are ignored + true, // isInherited + mutationRate, // does not mutate + DistributionType::UNIFORM, distParams, // not used + DistributionType::NONE, distParams, // no dominance, params are ignored + isDiploid ? 2 : 1, + false + ); + + pSpecies->addTrait(TraitType::SMS_DP, *trSmsDirPers); + pSpecies->addTrait(TraitType::SMS_GB, *trSmsGoalBias); + pSpecies->addTrait(TraitType::SMS_ALPHADB, *trSmsAlpha); + pSpecies->addTrait(TraitType::SMS_BETADB, *trSmsBeta); + + bool usesMovtProcess = true; + short whichMovtProcess = 1; // SMS + Individual ind = Individual(pSpecies, pCell, pPatch, 0, 0, 0, 0.0, usesMovtProcess, whichMovtProcess); + ind.setUpGenes(pSpecies, 1.0); + + // Overwrite genotypes with alleles resulting in invalid phenotypes + auto smsDPGenotype = createTestGenotype(genomeSz, true, 0.1, 0.2); + auto smsGBGenotype = createTestGenotype(genomeSz, true, 0.5, 0.0); + auto smsAlphaGenotype = createTestGenotype(genomeSz, true, 0.0, 0.0); + ind.overrideGenotype(SMS_DP, smsDPGenotype); + ind.overrideGenotype(SMS_GB, smsGBGenotype); + ind.overrideGenotype(SMS_ALPHADB, smsAlphaGenotype); + // any value is valid for SMS beta + + ind.triggerMutations(pSpecies); // no mutations, but trigger expression + + smsData trfrTr = *(static_cast(ind.getTrfrData())); + assert(trfrTr.dp >= 1.0); + assert(trfrTr.gb >= 1.0); + assert(trfrTr.alphaDB > 0.0); + } + } + +} + +#endif // UNIT_TESTS diff --git a/unit_tests/testNeutralStats.cpp b/unit_tests/testNeutralStats.cpp new file mode 100644 index 0000000..3bb396a --- /dev/null +++ b/unit_tests/testNeutralStats.cpp @@ -0,0 +1,1062 @@ +#ifdef UNIT_TESTS + +#include "../Community.h" + +void testNeutralStats() { + + // In haploid settings, Ho is always zero + { + // Create empty 1-patch, 1-cell landscape + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(0); + const set patchList{ pPatch->getPatchNum() }; + const string indSampling = "all"; + const set stgToSample = { 1 }; + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + // Create genetic structure + const int genomeSz = 4; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultHaploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const set genePositions = { 0, 1, 3 }; // arbitrary + const bool isDiploid{ false }; + const float maxAlleleVal = 0; // sample in uniform(0,0) so every individual is homozygote + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise population + const int nbInds = 2; + Population* pPop = new Population(pSpecies, pPatch, nbInds, 1); + pPop->sampleIndsWithoutReplacement(indSampling, stgToSample); + + // Calculate heterozygosity + auto pNeutralStatistics = make_unique(patchList.size(), genePositions.size()); + pNeutralStatistics->calculateHo( + patchList, + nbInds, + genePositions.size(), + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getHo() == 0.0); + } + + // If every individual in a sample is homozygote, Ho is zero + { + // Create empty 1-patch, 1-cell landscape + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(0); + const set patchList{ pPatch->getPatchNum() }; + const string indSampling = "all"; + const set stgToSample = { 1 }; + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + // Create genetic structure + const int genomeSz = 4; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const set genePositions = { 0, 1, 3 }; // arbitrary + const bool isDiploid{ true }; + const float maxAlleleVal = 0; // sample in uniform(0,0) so every individual is homozygote + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise population + const int nbInds = 2; + Population* pPop = new Population(pSpecies, pPatch, nbInds, 1); + pPop->sampleIndsWithoutReplacement(indSampling, stgToSample); + + // Calculate heterozygosity + auto pNeutralStatistics = make_unique(patchList.size(), genePositions.size()); + pNeutralStatistics->calculateHo( + patchList, + nbInds, + genePositions.size(), + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getHo() == 0.0); + } + + // If every individual in a sample is heterozygote, Ho is one. + { + // Create empty 1-patch, 1-cell landscape + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(0); + const set patchList{ pPatch->getPatchNum() }; + const string indSampling = "all"; + const set stgToSample = { 1 }; + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + // Create genetic structure + const int genomeSz = 4; + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const set genePositions = { 0, 2 }; // arbitrary + const bool isDiploid{ true }; + const float maxAlleleVal = 255; // highly unlikely that same value sampled twice + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise population + const int nbInds = 2; + Population* pPop = new Population(pSpecies, pPatch, nbInds, 1); + pPop->sampleIndsWithoutReplacement(indSampling, stgToSample); + + // Calculate heterozygosity + auto pNeutralStatistics = make_unique(patchList.size(), genePositions.size()); + pNeutralStatistics->calculateHo( + patchList, + nbInds, + genePositions.size(), + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getHo() == 1.0); + } + + // The correct number of individuals is sampled + { + const int nbIndsPopA = 15; + const int nbIndsPopB = 11; + const string indSampling = "13"; + const set stgToSample = { 1 }; + + // Patch setup + const int nbPatches = 2; + // Genetic setup + const int genomeSz = 2; + const bool isDiploid{ true }; + const set genePositions = { 0, 1 }; + const float maxAlleleVal = 1; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + Population* pPopA = new Population(pSpecies, patches[0], nbIndsPopA, 1); + Population* pPopB = new Population(pSpecies, patches[1], nbIndsPopB, 1); + pPopA->sampleIndsWithoutReplacement(indSampling, { indStg }); + pPopB->sampleIndsWithoutReplacement(indSampling, { indStg }); + + assert(pPopA->sampleSize() == stoi(indSampling)); // the correct nb of individuals has been sampled + assert(pPopB->sampleSize() == nbIndsPopB); // too small; all individuals have been sampled + } + + // If the sample is too small, Fst evaluates to zero + // More importantly, the neutral stats module still runs without error + { + // 1 - if there is zero population in the sample + { + // Patch setup + const int nbPatches = 2; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + const bool isDiploid = true; + const set genePositions = { 0 }; + const float maxAlleleVal = 0; + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Oops, no populations in patches + + // Compute F-stats + const int nbLoci = 0; + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = 0; + pNeutralStatistics->calculateFstatWC( + patchList, + 0, + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == 0.0); + } // end case 1 - zero pop in sample + + // 2 - If there is only one population in the sample + { + // Patch setup + const int nbPatches = 2; + const int nbPops = nbPatches - 1; // oops, 1 population is missing + const int nbIndsPerPop = 5; + // Genetic setup + const int genomeSz = 2; + const bool isDiploid{ true }; + const set genePositions = { 0, 1 }; + const float maxAlleleVal = 0; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < nbPops; p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == 0.0); + } // end case 2, only one population in sample + + // 3 - Two empty populations + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 0; // oops + + // Genetic setup + const int genomeSz = 2; + const bool isDiploid{ true }; + const set genePositions = { 0, 1 }; + const float maxAlleleVal = 0; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < nbPatches; p++) { + Population* pPop = new Population(pSpecies, patches[p], nbIndsPerPop, 1); + // create individuals and add to pop + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == 0.0); + } + } + + // If two sampled pops are monomorphic for different alleles, + // then Fst is 1 + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 2; + // Genetic setup + const int genomeSz = 2; + const bool isDiploid{ true }; + const set genePositions = { 0, 1 }; + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + auto popAGenotype = createTestNeutralGenotype(genomeSz, true, alleleValPopA, alleleValPopA); + auto popBGenotype = createTestNeutralGenotype(genomeSz, true, alleleValPopB, alleleValPopB); + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + vector>> genotypes = { popAGenotype, popBGenotype }; + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypes[p]); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == 1.0); + + pNeutralStatistics->calcPairwiseWeightedFst( + patchList, + nbIndsPerPop* patchList.size(), + nbLoci, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getPairwiseFst(0, 1) == 0.0); + } + + double refWeirCockerhamDiploidFst; // for use in further tests below + + // In strictly homozygote samples: + // 1. Fis = 1 (maximum inbreeding) + // 2. The sign of the Fst depends on the ratio of variation within vs between populations + // 3. (if sample sizes are equal) The Weir&Cockerham 1984, Weir&Hill 2002 estimators yield the same values + { + // Case 1/2: variation within > between + // Within: frequencies of allele A and B are quite different + // Between: no variation + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + // Genetic setup + const int genomeSz = 1; + const bool isDiploid{ true }; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + const auto genotypeAA = createTestNeutralGenotype(genomeSz, true, alleleValPopA, alleleValPopA); + const auto genotypeBB = createTestNeutralGenotype(genomeSz, true, alleleValPopB, alleleValPopB); + vector>> genotypes = { + // 8 AA, 2 BB (no heterozygotes) + genotypeAA, genotypeAA, genotypeAA, genotypeAA, genotypeBB, + genotypeAA, genotypeAA, genotypeAA, genotypeAA, genotypeBB + }; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypes[i]); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() < 0.0); + assert(pNeutralStatistics->getFisWC() == 1.0); + + pNeutralStatistics->calcPairwiseWeightedFst( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + pSpecies, + pLandscape + ); + const double tol = 0.000001; + assert(abs(pNeutralStatistics->getWeightedFst() - pNeutralStatistics->getFstWC()) < tol); + + refWeirCockerhamDiploidFst = pNeutralStatistics->getFstWC(); // for use in further tests below + } + + // Case 2/2: variation within < between + // Within: no variation for pop1, same allelic frequencies + // Between: larger variation + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + // Genetic setup + const int genomeSz = 1; + const bool isDiploid{ true }; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + const auto genotypeAA = createTestNeutralGenotype(genomeSz, true, alleleValPopA, alleleValPopA); + const auto genotypeBB = createTestNeutralGenotype(genomeSz, true, alleleValPopB, alleleValPopB); + vector>>> genotypeList = { + { // Pop 1: 5 AA, 5 BB (no heterozygotes) + genotypeAA, genotypeAA, genotypeAA, genotypeAA, genotypeAA, + genotypeBB, genotypeBB, genotypeBB, genotypeBB, genotypeBB + }, + { // Pop 2: 8 AA, 2 BB (no heterozygotes) + genotypeAA, genotypeAA, genotypeAA, genotypeAA, genotypeAA, + genotypeAA, genotypeAA, genotypeAA, genotypeBB, genotypeBB + } + }; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypeList[p][i]); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop* patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() > 0.0); + assert(pNeutralStatistics->getFisWC() == 1.0); + + // Weir & Hill population-specific estimates average to the (Weir & Hill) global estimator + pNeutralStatistics->calcPairwiseWeightedFst( + patchList, + nbIndsPerPop* patchList.size(), + nbLoci, + pSpecies, + pLandscape + ); + const double pop1Fst = pNeutralStatistics->getPairwiseFst(0, 0); + const double pop2Fst = pNeutralStatistics->getPairwiseFst(1, 1); + assert((pop1Fst + pop2Fst) / 2.0 == pNeutralStatistics->getWeightedFst()); + + } + } + + // In a strictly heterozygote sample, + // 1. Fis = -1 + // 2. If there is no variation between individuals, Fst = 0 + // 3. Weir & Cockerham 1984 estimator > Weir & Hill 2002 + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + // Genetic setup + const int genomeSz = 1; + const bool isDiploid{ true }; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + const auto genotypeAB = createTestNeutralGenotype(genomeSz, true, alleleValPopA, alleleValPopB); + // all individuals have genotype AB + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypeAB); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop* patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == 0.0); + assert(pNeutralStatistics->getFisWC() == -1.0); + + pNeutralStatistics->calcPairwiseWeightedFst( + patchList, + nbIndsPerPop* patchList.size(), + nbLoci, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getWeightedFst() < pNeutralStatistics->getFstWC()); + // Weir and Hill is still equal to Weir and Cockerham full homozygote case + const double tol = 0.000001; + assert(abs(pNeutralStatistics->getWeightedFst() - refWeirCockerhamDiploidFst) < tol); + } + + // Fst calculation is correct for an ordinary sample + { + // Two 10-individual pops, + // 1 tri-allelic locus + + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + + // Genetic setup + const int genomeSz = 1; + const bool isDiploid{ true }; + const set genePositions = { 0 }; + const float maxAlleleVal = 2; + unsigned char alleleA = char(0); + unsigned char alleleB = char(1); + unsigned char alleleC = char(2); + // Create genotypes + auto genotypeAA = createTestNeutralGenotype(genomeSz, true, alleleA, alleleA); + auto genotypeAB = createTestNeutralGenotype(genomeSz, true, alleleA, alleleB); + auto genotypeAC = createTestNeutralGenotype(genomeSz, true, alleleA, alleleC); + auto genotypeBB = createTestNeutralGenotype(genomeSz, true, alleleB, alleleB); + auto genotypeBC = createTestNeutralGenotype(genomeSz, true, alleleB, alleleC); + auto genotypeCC = createTestNeutralGenotype(genomeSz, true, alleleC, alleleC); + vector>>> genotypeList = { + { // Genotypes of population 1 + genotypeAA, genotypeAA, genotypeAB, genotypeAB, genotypeAB, + genotypeAB, genotypeAC, genotypeAC, genotypeBB, genotypeCC + }, + { // Genotypes of population 2 + genotypeAA, genotypeAB, genotypeAC, genotypeBB, genotypeBC, + genotypeBC, genotypeBC, genotypeBC, genotypeCC, genotypeCC + } + }; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypeList[p][i]); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + const double expectedFst = 0.0583; // calculated by hand from Weir and Cockerham 1984 + double calcError = abs(pNeutralStatistics->getFstWC() - expectedFst); + assert(calcError < 0.0001); + } + + // The Fst is a haploid sample equals that of a diploid sample if: + // 1 - allelic frequencies are equal + // 2 - the diploid sample has no heterozygotes + // (reference diploid Fst has already been calculated above) + { + const bool isDiploid{ false }; + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + // Genetic setup + const int genomeSz = 1; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + const auto genotypeA = createTestNeutralGenotype(genomeSz, true, alleleValPopA); + const auto genotypeB = createTestNeutralGenotype(genomeSz, true, alleleValPopB); + vector>> genotypes = { + // 8 A, 2B (no heterozygotes) + genotypeA, genotypeA, genotypeA, genotypeA, genotypeB, + genotypeA, genotypeA, genotypeA, genotypeA, genotypeB + }; + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultHaploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, genotypes[i]); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getFstWC() == refWeirCockerhamDiploidFst); + } + + // Multi-locus case + // First locus has no variation between individuals, all heterozygotes (Fst = 0) + // Second locus has different fixed alleles between both populations (Fst = 1) + { + // Patch setup + const int nbPatches = 2; + const int nbIndsPerPop = 10; + // Genetic setup + const int genomeSz = 3; + const bool isDiploid{ true }; + const set genePositions = { 0, 2 }; // position 1 is arbitrarily empty + const float maxAlleleVal = 1; + unsigned char alleleValPopA = char(0); + unsigned char alleleValPopB = char(1); + map> genotypePop1; + map> genotypePop2; + // First locus: all heterozygotes, same genotype + genotypePop1.emplace(0, vector{alleleValPopA, alleleValPopB}); + genotypePop2.emplace(0, vector{alleleValPopA, alleleValPopB}); + // Second locus: different fixed alleles + genotypePop1.emplace(2, vector{alleleValPopA, alleleValPopA}); + genotypePop2.emplace(2, vector{alleleValPopB, alleleValPopB}); + + // Create two-patches landscape + Landscape* pLandscape = new Landscape; + vector patches(nbPatches); + vector cells(nbPatches); + set patchList; + for (int i = 0; i < nbPatches; i++) { + patches[i] = pLandscape->newPatch(i); + cells[i] = new Cell(0, 0, patches[i], 0); + patches[i]->addCell(cells[i], 0, 0); + patchList.insert(patches[i]->getPatchNum()); + } + const string indSampling = "all"; + const set stgToSample = { 1 }; + + // Create species trait structure + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + patchList, + indSampling, + stgToSample, + 1 + ); + const int nbLoci = genePositions.size(); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise populations + const int indStg = 1; + for (int p = 0; p < patches.size(); p++) { + Population* pPop = new Population(pSpecies, patches[p], 0, 1); + // create individuals and add to pop + for (int i = 0; i < nbIndsPerPop; i++) { + Individual* pInd = new Individual(pSpecies, cells[p], patches[p], indStg, 0, 0, 0.0, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + pInd->overrideGenotype(NEUTRAL, p == 0 ? genotypePop1 : genotypePop2); + pPop->recruit(pInd); + } + pPop->sampleIndsWithoutReplacement(indSampling, { indStg }); + } + + // Compute F-stats + auto pNeutralStatistics = make_unique(nbPatches, nbLoci); + pNeutralStatistics->updateAllNeutralTables( + pSpecies, + pLandscape, + patchList + ); + const int maxNbNeutralAlleles = static_cast(maxAlleleVal) + 1; + pNeutralStatistics->calculateFstatWC( + patchList, + nbIndsPerPop * patchList.size(), + nbLoci, + maxNbNeutralAlleles, + pSpecies, + pLandscape + ); + assert(pNeutralStatistics->getPerLocusFst(0) == 0.0); + assert(pNeutralStatistics->getPerLocusFst(1) == 1.0); + } +} + +#endif // UNIT_TESTS diff --git a/unit_tests/testPopulation.cpp b/unit_tests/testPopulation.cpp new file mode 100644 index 0000000..c8dda07 --- /dev/null +++ b/unit_tests/testPopulation.cpp @@ -0,0 +1,307 @@ +#ifdef UNIT_TESTS + +#include "../Individual.h" +#include "../Population.h" + +void testPopulation() +{ + // Given a genetic load trait, offspring + // Survival is (inversely) proportional to the mutation rate + { + vector mutationRates = { 0.0, 0.1, 0.2 }; + vector survivingInds; + const int initialNbInds = 1000; + const float localK = 10000; // not limiting + vector localScaling = {1.0}; + + // Simple genetic layout + const bool isDiploid{ false }; // haploid suffices + const int genomeSz = 1; + const set genePositions = { 0 }; + + // All mutations are dominant lethal + const map mutParams{ + pair{GenParamType::MIN, 1}, + pair{GenParamType::MAX, 1} + }; + const map domParams{ + pair{GenParamType::MIN, 1}, + pair{GenParamType::MAX, 1} + }; + + for (float mutationRate : mutationRates) { + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(1); + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + Species* pSpecies = createDefaultSpecies(); + pSpecies->setDemogr(createDefaultHaploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + { }, "none", { }, 0 // no sampling + ); + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::GENETIC_LOAD, + sex_t::NA, + genePositions, + ExpressionType::MULTIPLICATIVE, + genePositions, // initial positions (all) + DistributionType::NONE, map{}, + DistributionType::UNIFORM, domParams, + true, // isInherited + mutationRate, // mutation rate + DistributionType::UNIFORM, mutParams, + DistributionType::UNIFORM, domParams, + isDiploid ? 2 : 1, + false + ); + pSpecies->addTrait(TraitType::GENETIC_LOAD, *spTr); + + Population pop = Population(pSpecies, pPatch, initialNbInds, 1); + pop.reproduction(localK, 1, 1, localScaling); // juveniles are checked for viability at birth + pop.fledge(); // non-overlapping: adults are replaced with juveniles + survivingInds.push_back(pop.getNbInds()); + } + assert(survivingInds[0] > survivingInds[1] + && survivingInds[1] > survivingInds[2]); + } + + // Dispersal is proportional to the mutation rate + { + vector mutationRates = { 0.0, 0.05, 0.1 }; + vector emigratingInds; + const int initialNbInds = 1000; + const float localK = 10000; // not limiting + vector localScaling = {1.0}; + + // Simple genetic layout + const bool isDiploid{ false }; // haploid suffices + const int genomeSz = 1; + const set genePositions = { 0 }; + + // Wild-types never emigrate, mutants always do + const map initParams{ + pair{GenParamType::MIN, 0}, + pair{GenParamType::MAX, 0} + }; + const map mutParams{ + pair{GenParamType::MIN, 1}, + pair{GenParamType::MAX, 1} + }; + + for (float mutationRate : mutationRates) { + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(1); + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + Species* pSpecies = createDefaultSpecies(); + emigRules emig; + emig.indVar = true; + emig.sexDep = false; + emig.densDep = false; + emig.stgDep = false; + pSpecies->setEmigRules(emig); + stageParams stg; + stg.nStages = 2; + pSpecies->setStage(stg); + pSpecies->setDemogr(createDefaultHaploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + { }, "none", { }, 0 // no sampling + ); + SpeciesTrait* spTr = new SpeciesTrait( + TraitType::E_D0, + sex_t::NA, + genePositions, + ExpressionType::ADDITIVE, + genePositions, // initial positions (all) + DistributionType::UNIFORM, initParams, // initial distribution and params + DistributionType::NONE, map{}, // initial dominance (none) + true, // isInherited + mutationRate, // mutation rate + DistributionType::UNIFORM, mutParams, // mutation dist and params + DistributionType::NONE, map{}, // no dominance + isDiploid ? 2 : 1, + false + ); + pSpecies->addTrait(TraitType::E_D0, *spTr); + + Population pop = Population(pSpecies, pPatch, initialNbInds, 1); + pop.reproduction(localK, 1, 1, localScaling); + pop.fledge(); // replace initial pop with juveniles + pop.emigration(localK); // select and flag emigrants + int popSize = pop.getNbInds(); + for (int i = 0; i < popSize; i++) { + pop.extractDisperser(i); // rm emigrants from pop + } + pop.clean(); + int nbEmigrating = popSize - pop.getNbInds(); // diff is nb of emigrants + if (mutationRate == 0.0) + assert(nbEmigrating == 0); + emigratingInds.push_back(nbEmigrating); + } + assert(emigratingInds[0] < emigratingInds[1] && emigratingInds[1] < emigratingInds[2]); + } + + // In the absence of selection, drift is solely responsible + // for changes in allele frequencies + { + const float tolerance = 0.02; + const float hetzTolerance = 0.05; + + float mutationRate = 0.0; + const float localK = 10000.0; + vector localScaling = {1.0}; + const int initialNbInds = localK; + const float initFreqA = 0.30; + const float exptdFreqHeteroZ = 2 * initFreqA * (1 - initFreqA); // according to HW + const int nbGens = 50; + float obsFreqA = initFreqA; + float obsFreqHeteroZ; + int nbInds = static_cast(localK); + + // Simple genetic layout + // 1 locus with two alleles A and B + const bool isDiploid{ true }; + const int genomeSz = 1; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleA = char(0); + unsigned char alleleB = char(1); + auto genotypeAA = createTestNeutralGenotype(genomeSz, true, alleleA, alleleA); + auto genotypeBB = createTestNeutralGenotype(genomeSz, true, alleleB, alleleB); + + // Landscape is a single cell + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(1); + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + Species* pSpecies = new Species(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + { }, "none", { }, 0 // no sampling + ); + SpeciesTrait* spTr = createTestNeutralSpTrait(maxAlleleVal, genePositions, isDiploid); + pSpecies->addTrait(TraitType::NEUTRAL, *spTr); + + // Initialise population with initial frequencies for AA and BB + Population pop = Population(pSpecies, pPatch, 0, 1); + for (int i = 0; i < initialNbInds; i++) { + Individual* pInd = new Individual(pSpecies, pCell, pPatch, 1, 0, 0, 0.5, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + if (i < initialNbInds * initFreqA) + pInd->overrideGenotype(NEUTRAL, genotypeAA); + else + pInd->overrideGenotype(NEUTRAL, genotypeBB); + pop.recruit(pInd); + } + + // Check allele frequencies conform to expectation through generations + float prevGenFreqA; + for (int yr = 0; yr < nbGens; yr++) { + pop.reproduction(localK, 1, 1, localScaling); + pop.fledge(); // replace initial pop with juveniles + pop.survival0(localK, 0, 0, localScaling); // flag juveniles for development + pop.survival1(); // develop to stage 1 (breeders) + pop.shuffleInds(); + + // Calculate expected allele frequency change from + // drift and previous generation frequencies + prevGenFreqA = obsFreqA; + float exptdChg = 2 * sqrt(prevGenFreqA * (1 - prevGenFreqA) / 2 * nbInds); + // ^ eq. 6.1 in Conservation and the Genomics of Populations, Allendorf et al. + // 95% CI for the allele frequency change + + // Check allele frequency change match equation + pop.sampleIndsWithoutReplacement("all", { 1 }); + pop.updatePopNeutralTables(); + obsFreqA = pop.getAlleleFrequency(0, alleleA); + float freqChg = abs(prevGenFreqA - obsFreqA); + assert(abs(freqChg) < tolerance); + + // If population is very large, heterozygosity should be roughly constant, + // i.e. inbreeding is negligible + nbInds = pop.getNbInds(); + // couldn't find a source for exptd change in heterozygosity so + // we assume Hardy-Weinberg equilibrium + obsFreqHeteroZ = static_cast(pop.getHeteroTally(0, alleleA)) / nbInds; + assert(abs(obsFreqHeteroZ - exptdFreqHeteroZ) < hetzTolerance); + } + } + + // Genetic load meets Hardy-Weinberg expectation on first generation + // If a lethal (s = 1) recessive (h = 0) allele starts at freq 0.6, + // then (if no mutations) next gen should have 0.6^2 = 0.36 homozygotes dying at birth + { + const float tolerance = 0.05; // high tolerance, still a lot of stochasticity + + const float initFreqA = 0.23; + const float sA = 1.0; // lethal + const float hA = 0.0; // fully recessive + const float sB = 0.0; // benign + const float hB = 1.0; // fully dominant + float mutationRate = 0.0; + const float localK = 10000.0; + vector localScaling = {1.0}; + + const int initialNbInds = localK; + const float expectedFreqAA = initFreqA * initFreqA; + + // Simple genetic layout + const bool isDiploid{ true }; // HW only applies to diploids + const int genomeSz = 1; + const set genePositions = { 0 }; + const float maxAlleleVal = 1; + unsigned char alleleA = char(0); + unsigned char alleleB = char(1); + auto genotypeAA = createTestGenotype(genomeSz, true, sA, sA, hA, hA); + auto genotypeBB = createTestGenotype(genomeSz, true, sB, sB, hB, hB); + + Landscape* pLandscape = new Landscape; + Patch* pPatch = pLandscape->newPatch(1); + Cell* pCell = new Cell(0, 0, pPatch, 0); + pPatch->addCell(pCell, 0, 0); + + Species* pSpecies = new Species(); + pSpecies->setDemogr(createDefaultDiploidDemogrParams()); + pSpecies->setGeneticParameters( + set{genomeSz - 1}, // single chromosome + genomeSz, + 0.0, // no recombination + { }, "none", { }, 0 // no sampling + ); + SpeciesTrait* spTr = createTestGenLoadTrait(genePositions, isDiploid); + pSpecies->addTrait(TraitType::GENETIC_LOAD, *spTr); + + // Initialise population with + Population pop = Population(pSpecies, pPatch, 0, 1); + for (int i = 0; i < initialNbInds; i++) { + Individual* pInd = new Individual(pSpecies, pCell, pPatch, 1, 0, 0, 0.5, false, 1); + pInd->setUpGenes(pSpecies, 1.0); + if (i < initialNbInds * initFreqA) + pInd->overrideGenotype(GENETIC_LOAD1, genotypeAA); + else + pInd->overrideGenotype(GENETIC_LOAD1, genotypeBB); + pop.recruit(pInd); + } + + // Check allele frequencies conform to HW + pop.shuffleInds(); + pop.reproduction(localK, 1, 1, localScaling); + pop.fledge(); // replace initial pop with juveniles + double obsFreqUnviable = 1 - pop.getNbInds() / localK; + assert(abs(obsFreqUnviable - expectedFreqAA) < tolerance); + } +} + +#endif // UNIT_TESTS