From 207d2d3eccb69d8f53f4fa2fdd6825800269549c Mon Sep 17 00:00:00 2001 From: Arnaud Becheler <8360330+Becheler@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:34:56 +0000 Subject: [PATCH] feat: extending the template project file --- docs/2-installation.md | 7 ++ example/expressive_3.cpp | 2 +- example/geography_landscape_1.cpp | 16 ++--- src/include/quetzal/geography/landscape.hpp | 3 +- template/main.cpp | 77 ++++++++++++++++++++- 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/docs/2-installation.md b/docs/2-installation.md index 4b3657bd..6c6316ff 100644 --- a/docs/2-installation.md +++ b/docs/2-installation.md @@ -53,6 +53,13 @@ To swiftly open an integrated development environment (IDE), begin coding to exp This will download the project, builds a Docker Image, manage the dependencies and automatically build and run the tests. If it's the first time you build the image, it may take some time, so you're free to go get a coffee! +Then, +1. navigate to the `template/main.cpp` file +2. Open the VSC Command Palette with `Shift + Command + P` (Mac) or `Ctrl + Shift + P` (Windows/Linux) +3. Click on `Run Task > Run Template Project (Debug)` +4. Read the output in the integrated terminal +5. Edit `template/main.cpp` to your liking and re-run the task, observe the difference in the terminal. + --- ### Copy diff --git a/example/expressive_3.cpp b/example/expressive_3.cpp index e06de2b6..927a8b99 100644 --- a/example/expressive_3.cpp +++ b/example/expressive_3.cpp @@ -30,7 +30,7 @@ int main() // We need to make choices here concerning how NA are handled auto suit = expressive::use([s_view](location_type x, time_type t){ return s_view(x,t).value_or(0.0); }); - auto elev = expressive::use([s_view](location_type x, time_type t){ return s_view(x,t).value_or(0.0); }); + auto elev = expressive::use([e_view](location_type x, time_type t){ return e_view(x,t).value_or(0.0); }); std::random_device rd; // a seed source for the random number engine std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() diff --git a/example/geography_landscape_1.cpp b/example/geography_landscape_1.cpp index d972eb53..de6572f9 100644 --- a/example/geography_landscape_1.cpp +++ b/example/geography_landscape_1.cpp @@ -6,24 +6,16 @@ using namespace quetzal; int main() { - // What type you want to use to identify the variable: could also be e.g. an integer - using key_type = std::string; - - // What type you want to use to identify the variable: could also be e.g. a time period - using time_type = int; - - // Let's define a shorter alias - using landscape_type = quetzal::geography::landscape; - auto file1 = std::filesystem::current_path() / "data/bio1.tif"; auto file2 = std::filesystem::current_path() / "data/bio12.tif"; - // The raster have 10 bands that we will assign to 2001 ... 2011. - std::vector times(10); + // The raster have 10 bands that we will assign to 2001 ... 2010. + std::vector times(10); std::iota(times.begin(), times.end(), 2001); // Initialize the landscape: for each var a key and a file, for all a time series. - auto env = landscape_type::from_files( { {"bio1", file1}, {"bio12", file2} }, times ); + using landscape_type = quetzal::geography::landscape<>; + auto env = quetzal::geography::landscape<>::from_files( { {"bio1", file1}, {"bio12", file2} }, times ); std::cout << env << std::endl; // We indeed recorded 2 variables: bio1 and bio12 diff --git a/src/include/quetzal/geography/landscape.hpp b/src/include/quetzal/geography/landscape.hpp index 6e2055b4..1401213f 100644 --- a/src/include/quetzal/geography/landscape.hpp +++ b/src/include/quetzal/geography/landscape.hpp @@ -21,6 +21,7 @@ #include // std::cref #include #include +#include namespace quetzal::geography { @@ -35,7 +36,7 @@ namespace quetzal::geography /// @tparam Key A key used to uniquely identifie a variable, e.g. std::string. /// @tparam Time Type used as time period for every band, e.g. std::string with `4.2-0.3 ka` /// @ingroup geography - template + template class landscape { diff --git a/template/main.cpp b/template/main.cpp index 4dfdb153..ca60f38e 100644 --- a/template/main.cpp +++ b/template/main.cpp @@ -1,7 +1,78 @@ #include "quetzal/quetzal.hpp" -#include + +#include +#include +#include int main() { - std::cout << "yo" << std::endl; -} \ No newline at end of file + // Load the suitability and elevation maps + auto file1 = std::filesystem::current_path() / "data/suitability.tif"; + auto file2 = std::filesystem::current_path() / "data/elevation.tif"; + + // The rasters 10 bands are indexed by the year they represent: 2001 ... 2010 + std::vector times(10); + std::iota(times.begin(), times.end(), 2001); + + // Initialize the landscape: for each spatial variable a string key and a file value, for all a time series. + auto landscape = quetzal::geography::landscape<>::from_files({{"suit", file1}, {"DEM", file2}}, times); + std::cout << landscape << std::endl; + + // Declares some type aliases to shorten notation + using location_type = quetzal::geography::landscape<>::location_descriptor; + using time_type= quetzal::geography::landscape<>::time_descriptor; + + // lightweight functors for suitability and digital elevation models that return empty optionals where NA are encounters + auto suit_view = landscape["suit"].to_view(); + auto elev_view = landscape["DEM"].to_view(); + + // We need to make choices here concerning how NA are handled + auto suit = quetzal::expressive::use([&](location_type x, time_type t) + { return suit_view(x, t).value_or(0.0); }); + auto elev = quetzal::expressive::use([&](location_type x, time_type t) + { return elev_view(x, t).value_or(0.0); }); + + std::random_device rd; // a seed source for the random number engine + std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() + + // Small-scale ice-free shelters for the species randomly pop above the snow cover at high-altitude (>123m) + auto nunatak_suitability = [&](location_type x, time_type t) + { + std::bernoulli_distribution d(0.1); // give "false" 9/10 of the time + bool is_nunatak = d(gen); + return (elev(x, t) >= 123.0) ? static_cast(is_nunatak) * suit(x, t) : suit(x, t); + }; + + // To allow dispersal across ocean, we can compose expressions: + auto capacity_with_rafting = [&](location_type x, time_type t) + { + std::bernoulli_distribution d(0.1); // give "false" 9/10 of the time + if (suit(x, t) == 0.0 and elev(x, t) == 0.0) // ocean cell case: + return static_cast(d(gen)) * 2; // will (rarely) allow 2 individuals to survive in the ocean cell + else if (suit(x, 0) == 0.0 and elev(x, t) > 0.0) // unsuitable continental cell case: + return 0.0; // suitability is minimal, so should be the capacity + else // habitable continental cells: + return nunatak_suitability(x,t); // evaluates suitability simulating nunataks + }; + + // Account for different dispersal modes + auto friction_with_rafting = [&](location_type x, time_type t) + { + if (suit(x, t) == 0.0 and elev(x, t) == 0.0) // ocean cell case: + return 0.0; // the raft should move freely on the water + else if (suit(x, 0) == 0.0 and elev(x, t) > 0.0) // hostile continental cell case: + return 1.00; // max friction as the cell is not attractive + else // favorable continental cell case: + return 1.0 - suit(x, 0); // higher the suitability, easier the travel + }; + + // Expressions can be evaluated with a location_type and a time_type argument: + auto x = *landscape.locations().begin(); + auto t = *landscape.times().begin(); + + std::cout << "Friction f( x = " << x << ", t = " << t << " ) = " << friction_with_rafting(x,t) << std::endl; + std::cout << "Carrying capacity K( x = " << x << ", t = " << t << " ) = " << capacity_with_rafting(x,t) << std::endl; + + // Spatial graph + +}