### Install pre-commit Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Installs the pre-commit tool into the active environment. ```bash $ conda install pre-commit -c conda-forge ``` -------------------------------- ### Build and Install Fastscapelib C++ from Source Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Configure, build, and install the Fastscapelib C++ header-only library from source using CMake. Ensure C++17 compiler support and specify an installation prefix if needed. ```bash $ cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/path/to/prefix .. $ cmake --build build $ cmake --install build ``` -------------------------------- ### Setup Profile Grid Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/river_profile_py.ipynb Creates a 1-dimensional profile grid with specified node count and spacing, setting boundary conditions. ```python grid = fs.ProfileGrid(101, 300.0, [fs.NodeStatus.FIXED_VALUE, fs.NodeStatus.CORE]) ``` -------------------------------- ### Setup Eroder Classes Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/inner_base_levels_py.ipynb Configures bedrock channel and hillslope diffusion eroders with specific parameters. ```python spl_eroder = fs.SPLEroder( flow_graph, k_coef=2e-4, area_exp=0.4, slope_exp=1, tolerance=1e-5, ) diffusion_eroder = fs.DiffusionADIEroder(grid, 0.01) ``` -------------------------------- ### Setup Flow Graph Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/river_profile_py.ipynb Initializes a FlowGraph object for the grid, using a single direction flow router suitable for 1D profiles. ```python flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter()]) ``` -------------------------------- ### Install google-benchmark with Conda Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/build_options.md Install the google-benchmark library using conda for building Fastscapelib benchmarks. Ensure you are using the conda-forge channel. ```bash $ conda install benchmark -c conda-forge ``` -------------------------------- ### Pixi Install Development Environment Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/build_options.md Install a development environment for Fastscapelib using pixi. This command installs all necessary tools and dependencies for building and testing. ```bash $ pixi install --environment dev ``` -------------------------------- ### Flow Accumulation Examples (Python) Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Illustrates how to use the accumulate method for calculating drainage area, water discharge, and sediment volume. It also shows how to pass an output array for accumulation. ```python import fastscapelib as fs import numpy as np grid = fs.RasterGrid([100, 100], [100.0, 100.0], fs.NodeStatus.FIXED_VALUE) flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter(), fs.MSTSinkResolver()]) elevation = np.random.uniform(0, 100, size=grid.shape) flow_graph.update_routes(elevation) # Drainage area: accumulate unit value (result in m^2) drainage_area = flow_graph.accumulate(1.0) # Water discharge from variable runoff rate (result in m^3/time) runoff_rate = np.random.uniform(0.001, 0.01, size=grid.shape) # m/time discharge = flow_graph.accumulate(runoff_rate) # Sediment flux from erosion (result in m^3) erosion = np.random.uniform(0, 0.01, size=grid.shape) # m sediment_volume = flow_graph.accumulate(erosion) # Alternative: pass output array output = np.empty_like(elevation) flow_graph.accumulate(output, 1.0) ``` -------------------------------- ### Install Fastscapelib from Source Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Builds and installs the Fastscapelib-Python package using pip from the source root directory. The --no-build-isolation flag is required. ```bash python -m pip install . --no-build-isolation ``` -------------------------------- ### Create and Activate Conda Environment for Docs Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Installs documentation development tools in a new conda environment. Activate this environment before building documentation. ```bash conda env create -f doc/environment.yml conda activate fastscapelib-docs ``` -------------------------------- ### Implement a multiple flow router with parameters Source: https://github.com/fastscape-lem/fastscapelib/wiki/Design-notes:-flow-routing Example of a derived class requiring additional parameters in the constructor for methods like MFD. ```cpp template class multiple_flow_router : public flow_router { public: using base_type = flow_router; using elevation_type = typename base_type::elevation_type; multiple_flow_router(double slope_exp, double dist_exp); virtual ~multiple_flow_router() = default; private: void route_impl(const elevation_type& elevation, FG& flow_graph) override; }; ``` -------------------------------- ### Install Fastscapelib Python with Pip Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Install the Fastscapelib Python package using pip. This command installs the package from PyPI. ```bash $ python -m pip install fastscapelib ``` -------------------------------- ### Initialize Flow Graph with MST Sink Resolver (C++) Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Sets up a raster grid and a flow graph using a single flow router and an MST sink resolver. This is a common setup for hydrological simulations. ```cpp #include "fastscapelib/flow/flow_graph.hpp" #include "fastscapelib/flow/flow_router.hpp" #include "fastscapelib/flow/sink_resolver.hpp" #include "fastscapelib/grid/raster_grid.hpp" namespace fs = fastscapelib; fs::raster_boundary_status bs(fs::node_status::fixed_value); fs::raster_grid grid({ 100, 100 }, { 200.0, 200.0 }, bs); // Flow routing with MST sink resolver fs::flow_graph> flow_graph( grid, { fs::single_flow_router(), fs::mst_sink_resolver() }); xt::xarray elevation = xt::random::rand(grid.shape()); flow_graph.update_routes(elevation); ``` -------------------------------- ### Install Fastscapelib with Debug Build Options Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Installs Fastscapelib from source with additional options to build the Python extension in Debug mode and specify a build directory. This is useful for cached builds. ```bash python -m pip install . \ $ --no-build-isolation \ $ --config-settings=cmake.build-type=Debug \ $ --config-settings=build-dir=build/skbuild ``` -------------------------------- ### Setup Erosion Models Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/escarpment_py.ipynb Configures the Stream Power Law (SPL) eroder and the diffusion-based hillslope eroder. ```python spl_eroder = fs.SPLEroder( flow_graph, k_coef=1e-4, area_exp=0.4, slope_exp=1, tolerance=1e-5, ) diffusion_eroder = fs.DiffusionADIEroder(grid, 0.01) ``` -------------------------------- ### Mountain Example in C++ Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/mountain_cpp.md This C++ code implements the mountain example. It is a direct translation of the Python version. ```cpp #include #include #include // Function to calculate the height of a mountain at a given horizontal position double mountain(double x) { return std::sin(x); } int main() { // Define the range for x double x_start = 0.0; double x_end = 2.0 * M_PI; int num_points = 100; double dx = (x_end - x_start) / (num_points - 1); // Print the header std::cout << "x\t\ty\n"; std::cout << "-------------------\n"; // Calculate and print the mountain heights for (int i = 0; i < num_points; ++i) { double x = x_start + i * dx; double y = mountain(x); std::cout << x << "\t" << y << "\n"; } return 0; } ``` -------------------------------- ### Install CMake Dependencies for C++ Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Install xtensor and cmake using conda, which are required for building the C++ library from source. ```bash $ conda install xtensor cmake -c conda-forge ``` -------------------------------- ### Implement a 1-d channel flow router Source: https://github.com/fastscape-lem/fastscapelib/wiki/Design-notes:-flow-routing Example of a derived class for straightforward flow routing on a 1-d channel grid. ```cpp template class one_channel_router : public flow_router { public: using base_type = flow_router; using elevation_type = typename base_type::elevation_type; one_channel_router() = default; virtual ~one_channel_router() = default; private: void route_impl(const elevation_type& elevation, FG& fgraph) override; }; ``` -------------------------------- ### Setup Stream-Power Law Eroder Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/river_profile_py.ipynb Initializes the SPLEroder class with specific parameters for bedrock channel erosion modeling. ```python spl_eroder = fs.SPLEroder( flow_graph, k_coef=1e-4, area_exp=0.5, slope_exp=1, tolerance=1e-5, ) ``` -------------------------------- ### Setup Flow Graph for Routing and Depression Resolution Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/catchment_py.ipynb Initializes a FlowGraph with single direction flow routing and MST sink resolution. This graph manages water flow across the topographic surface. ```python flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter(), fs.MSTSinkResolver()]) ``` -------------------------------- ### Create RasterGrid with Mixed Boundary Conditions (C++) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_grids.md Initializes a RasterGrid with specified boundary statuses for left, right, top, and bottom edges. This example sets fixed value on the left, core on the right, and looped on top/bottom. ```C++ #include "fastscapelib/grid/raster_grid.hpp" namespace fs = fastscapelib; fs::raster_boundary_status bs{ fs::node_status::fixed_value, fs::node_status::core, fs::node_status::looped, fs::node_status::looped }; auto grid = fs::raster_grid({ 101, 201 }, { 1e2, 1e2 }, bs); ``` -------------------------------- ### Implement a priority flood sink resolver Source: https://github.com/fastscape-lem/fastscapelib/wiki/Design-notes:-flow-routing An example of a sink resolver that performs logic in the pre-routing phase, leaving the post-routing phase empty. ```cpp template class pflood_resolver : public sink_resolver { public: using base_type = sink_resolver; using elevation_type = typename base_type::elevation_type; pflood_resolver() = default; virtual ~pflood_resolver() = default; void resolve_before_route(const elevation_type& elevation, FG& fgraph) override { // run priority flood } void resolve_after_route(const elevation_type& elevation, FG& fgraph) override { } }; ``` -------------------------------- ### Install Dependencies with Conda Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Installs necessary Python, Numpy, pybind11, xtensor-python, scikit-build-core, and pip using conda-forge. Ensure xtensor-python is version 0.28 or newer. ```bash conda install python numpy pybind11 xtensor-python scikit-build-core pip -c conda-forge ``` -------------------------------- ### Create RasterGrid with Mixed Boundary Conditions (Python) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_grids.md Initializes a RasterGrid with specified boundary statuses for left, right, top, and bottom edges. This example sets fixed value on the left, core on the right, and looped on top/bottom. ```Python import fastscapelib as fs bs = [ fs.NodeStatus.FIXED_VALUE, fs.NodeStatus.CORE, fs.NodeStatus.LOOPED, fs.NodeStatus.LOOPED, ] grid = fs.RasterGrid([101, 201], [1e2, 1e2], bs) ``` -------------------------------- ### Stream Power Law Erosion (C++) Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Provides a C++ implementation for Stream Power Law channel erosion, mirroring the Python example. It demonstrates setting up the eroder via a helper function and updating parameters. ```cpp #include "fastscapelib/flow/flow_graph.hpp" #include "fastscapelib/flow/flow_router.hpp" #include "fastscapelib/flow/sink_resolver.hpp" #include "fastscapelib/grid/raster_grid.hpp" #include "fastscapelib/eroders/spl.hpp" namespace fs = fastscapelib; // Setup grid and flow graph fs::raster_boundary_status bs(fs::node_status::fixed_value); auto grid = fs::raster_grid<>::from_length({ 101, 101 }, { 1e4, 1e4 }, bs); fs::flow_graph> flow_graph( grid, { fs::single_flow_router(), fs::mst_sink_resolver() }); // Create SPL eroder using helper function auto spl_eroder = fs::make_spl_eroder(flow_graph, 2e-4, 0.4, 1.0, 1e-5); // Initialize arrays xt::xarray elevation = xt::random::rand(grid.shape()); xt::xarray drainage_area(elevation.shape()); double dt = 1000.0; // Compute flow routes and erosion flow_graph.update_routes(elevation); flow_graph.accumulate(drainage_area, 1.0); auto erosion = spl_eroder.erode(elevation, drainage_area, dt); // Update parameter spl_eroder.set_k_coef(1e-4); ``` -------------------------------- ### Import Libraries for Fastscapelib Simulation Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/planetary_py.ipynb Imports necessary libraries for HEALPix grid operations, numerical computations, and Fastscapelib functionalities. Ensure these libraries are installed. ```python import healpy as hp import numpy as np import matplotlib import matplotlib.pyplot as plt import fastscapelib as fs ``` -------------------------------- ### Run Python Tests with Pytest Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/build_options.md Execute the Python bindings tests using pytest. This command requires pytest to be installed and assumes you are in the repository root directory. ```bash pytest -v . ``` -------------------------------- ### Build Documentation Locally with Make Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Builds the documentation locally using the provided Makefile. Navigate to the 'doc' directory first. ```bash cd doc make html ``` -------------------------------- ### Perform a Full Documentation Build Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Triggers a complete rebuild of the documentation from scratch. Use this if incremental builds are causing issues. ```bash make clean make html ``` -------------------------------- ### Create development environment Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Sets up the Conda environment using the provided configuration files. ```bash $ conda env create -n fastscapelib-dev -f ci/environment-dev.yml -f ci/environment-python-dev.yml ``` -------------------------------- ### Install Fastscapelib Python with Conda Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Install the pre-compiled Fastscapelib Python package and its dependencies using conda from the conda-forge channel. ```bash $ conda install fastscapelib-python -c conda-forge ``` -------------------------------- ### Install Fastscapelib C++ with Conda Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/install.md Install the Fastscapelib C++ header-only library and its dependencies using conda from the conda-forge channel. ```bash $ conda install fastscapelib -c conda-forge ``` -------------------------------- ### Create and Access ProfileGrid in C++ Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Shows how to create a 1D profile grid in C++ with specified boundary conditions and access its basic properties. ```cpp #include "fastscapelib/grid/profile_grid.hpp" namespace fs = fastscapelib; // Create profile grid with boundary conditions fs::profile_boundary_status bs{ fs::node_status::fixed_value, fs::node_status::core }; auto grid = fs::profile_grid(101, 300.0, bs); // Create from total length auto grid2 = fs::profile_grid<>::from_length(501, 500.0, fs::node_status::fixed_value); // Access properties std::cout << "Grid size: " << grid.size() << std::endl; std::cout << "Spacing: " << grid.spacing() << std::endl; ``` -------------------------------- ### Initialize Topography and Drainage Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/inner_base_levels_py.ipynb Sets up the initial elevation surface and drainage area array. ```python rng = np.random.Generator(np.random.PCG64(1234)) init_elevation = rng.uniform(0, 1, size=grid.shape) elevation = init_elevation drainage_area = np.empty_like(elevation) ``` -------------------------------- ### Create and Access ProfileGrid in Python Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Demonstrates creating a 1D ProfileGrid with different boundary conditions and accessing its properties. Node areas can be computed using nodes_areas(). ```python import fastscapelib as fs import numpy as np # Create a profile grid with 101 nodes, 300m spacing # Fixed-value boundary on left, free (core) on right grid = fs.ProfileGrid(101, 300.0, [fs.NodeStatus.FIXED_VALUE, fs.NodeStatus.CORE]) # Alternative: create from total length grid = fs.ProfileGrid.from_length(501, 500.0, fs.NodeStatus.FIXED_VALUE) # Access grid properties print(f"Grid size: {grid.size}") print(f"Grid spacing: {grid.spacing}") print(f"Grid shape: {grid.shape}") # Get node areas for each grid point node_areas = grid.nodes_areas() ``` -------------------------------- ### Clone the repository Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Initializes a local copy of the forked repository. ```bash $ git clone https://github.com/your-username/fastscapelib $ cd fastscapelib ``` -------------------------------- ### Define Initial Conditions Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/escarpment_py.ipynb Sets up the initial topographic surface with two plateaus and random perturbations. ```python rng = np.random.Generator(np.random.PCG64(1234)) init_elevation = rng.uniform(0, 1, size=grid.shape) init_elevation[:, 100:] += 400 elevation = init_elevation drainage_area = np.empty_like(elevation) ``` -------------------------------- ### Initialize Flow Graph and Eroders Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/planetary_py.ipynb Sets up the flow graph with routing and sink resolution, applies a mask for ocean nodes, and configures the SPLEroder with specific coefficients and exponents for channel erosion. Uplift rate and time step are also defined. ```python flow_graph = fs.FlowGraph( grid, [fs.SingleFlowRouter(), fs.MSTSinkResolver()], ) flow_graph.mask = mask spl_eroder = fs.SPLEroder( flow_graph, k_coef=1.2e-11, area_exp=0.45, slope_exp=1, tolerance=1e-5, ) urate = 9e-4 dt = 1e4 ``` -------------------------------- ### Initialize RasterGrid Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/mountain_py.ipynb Creates a 201x301 node grid with fixed boundary conditions. ```python grid = fs.RasterGrid.from_length([201, 301], [5e4, 7.5e4], fs.NodeStatus.FIXED_VALUE) ``` -------------------------------- ### Mountain Evolution Simulation Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt A complete simulation example combining uplift, flow routing, stream power law erosion, and hillslope diffusion. ```python import fastscapelib as fs import numpy as np # Setup 201x301 grid (50km x 75km domain) grid = fs.RasterGrid.from_length([201, 301], [5e4, 7.5e4], fs.NodeStatus.FIXED_VALUE) # Flow graph with single flow routing and sink resolution flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter(), fs.MSTSinkResolver()]) # Setup eroders spl_eroder = fs.SPLEroder(flow_graph, k_coef=2e-4, area_exp=0.4, slope_exp=1, tolerance=1e-5) diffusion_eroder = fs.DiffusionADIEroder(grid, 0.01) # Initial topography (flat + noise) rng = np.random.Generator(np.random.PCG64(1234)) elevation = rng.uniform(0, 1, size=grid.shape) drainage_area = np.empty_like(elevation) # Uplift rate (1 mm/yr, zero at boundaries) uplift_rate = np.full_like(elevation, 1e-3) uplift_rate[[0, -1], :] = 0.0 uplift_rate[:, [0, -1]] = 0.0 # Run simulation dt = 2e4 # 20,000 year time steps nsteps = 50 for step in range(nsteps): # Apply uplift uplifted_elevation = elevation + dt * uplift_rate # Flow routing filled_elevation = flow_graph.update_routes(uplifted_elevation) # Drainage area flow_graph.accumulate(drainage_area, 1.0) # Apply erosion processes spl_erosion = spl_eroder.erode(uplifted_elevation, drainage_area, dt) diff_erosion = diffusion_eroder.erode(uplifted_elevation - spl_erosion, dt) # Update topography elevation = uplifted_elevation - spl_erosion - diff_erosion print(f"Final mean elevation: {elevation.mean():.2f} m") print(f"Final max elevation: {elevation.max():.2f} m") ``` ```cpp #include #include "xtensor/containers/xarray.hpp" #include "xtensor/core/xmath.hpp" #include "xtensor/views/xview.hpp" #include "fastscapelib/flow/flow_graph.hpp" #include "fastscapelib/flow/sink_resolver.hpp" #include "fastscapelib/flow/flow_router.hpp" #include "fastscapelib/grid/raster_grid.hpp" #include "fastscapelib/eroders/diffusion_adi.hpp" #include "fastscapelib/eroders/spl.hpp" namespace fs = fastscapelib; int main() { // Setup grid fs::raster_boundary_status bs(fs::node_status::fixed_value); auto grid = fs::raster_grid<>::from_length({ 201, 301 }, { 5e4, 7.5e4 }, bs); // Flow graph fs::flow_graph> flow_graph( grid, { fs::single_flow_router(), fs::mst_sink_resolver() }); // Eroders auto spl_eroder = fs::make_spl_eroder(flow_graph, 2e-4, 0.4, 1, 1e-5); auto diffusion_eroder = fs::diffusion_adi_eroder(grid, 0.01); // Initial conditions xt::xarray elevation = xt::random::rand(grid.shape()); xt::xarray drainage_area(elevation.shape()); xt::xarray uplifted_elevation(elevation.shape()); // Uplift rate xt::xarray uplift_rate(elevation.shape(), 1e-3); xt::view(uplift_rate, xt::keep(0, -1), xt::all()) = 0.0; xt::view(uplift_rate, xt::all(), xt::keep(0, -1)) = 0.0; // Run simulation double dt = 2e4; int nsteps = 50; for (int step = 0; step < nsteps; step++) { uplifted_elevation = elevation + dt * uplift_rate; flow_graph.update_routes(uplifted_elevation); flow_graph.accumulate(drainage_area, 1.0); auto spl_erosion = spl_eroder.erode(uplifted_elevation, drainage_area, dt); auto diff_erosion = diffusion_eroder.erode(uplifted_elevation - spl_erosion, dt); elevation = uplifted_elevation - spl_erosion - diff_erosion; } std::cout << "Mean final elevation: " << xt::mean(elevation) << std::endl; return 0; } ``` -------------------------------- ### Initialize and Run SPL Erosion Simulation Source: https://github.com/fastscape-lem/fastscapelib/blob/main/examples/flow_kernel.ipynb Sets up and runs a simulation using the SPL eroder. Requires `fastscapelib`, `numpy`, and `matplotlib`. The simulation involves uplift, flow routing, drainage area accumulation, and channel erosion. ```python import matplotlib.pyplot as plt import numba as nb import numpy as np import fastscapelib as fs uplift_rate = np.full_like(init_elevation, 1e-3) uplift_rate[[0, -1], :] = 0. uplift_rate[:, [0, -1]] = 0. dt = 2e4 nsteps = 50 k_coef = 2e-4 area_exp = 0.4 slope_exp = 1 flow_graph.update_routes(init_elevation) spl_eroder = fs.SPLEroder( flow_graph, k_coef=k_coef, area_exp=area_exp, slope_exp=slope_exp, tolerance=1e-5, ) def run_simulation2(): elevation = init_elevation.copy() drainage_area = np.empty_like(init_elevation) for step in range(nsteps): # uplift (no uplift at fixed elevation boundaries) uplifted_elevation = elevation + dt * uplift_rate # flow routing filled_elevation = flow_graph.update_routes(uplifted_elevation) # flow accumulation (drainage area) flow_graph.accumulate(drainage_area, 1.0) # apply channel erosion (SPL) spl_erosion = spl_eroder.erode(uplifted_elevation, drainage_area, dt) # update topography elevation = uplifted_elevation - spl_erosion return elevation ``` -------------------------------- ### Build All Benchmark Executable Source: https://github.com/fastscape-lem/fastscapelib/blob/main/benchmark/CMakeLists.txt Creates a single executable that includes all benchmark source files. Links against the necessary libraries and enforces the C++17 standard. ```cmake # -- build a global target for all benchmarks add_executable(benchmark_fastscapelib ${FASTSCAPELIB_BENCHMARK_SRC} ${FASTSCAPELIB_BENCHMARK_HEADERS} main.cpp) target_link_libraries(benchmark_fastscapelib PRIVATE fastscapelib xtensor Eigen3::Eigen benchmark::benchmark) target_compile_features(benchmark_fastscapelib PRIVATE cxx_std_17) ``` -------------------------------- ### Initialize Raster Grid Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/escarpment_py.ipynb Defines boundary conditions and creates a 101x201 node grid representing a 10km by 20km area. ```python bs = [ fs.NodeStatus.FIXED_VALUE, fs.NodeStatus.CORE, fs.NodeStatus.LOOPED, fs.NodeStatus.LOOPED, ] grid = fs.RasterGrid.from_length([101, 201], [1e4, 2e4], bs) ``` -------------------------------- ### Configure Git hooks Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Initializes the pre-commit hooks in the repository. ```bash $ pre-commit install ``` -------------------------------- ### Flow Graph Constructor for 1-D Channel Grids Source: https://github.com/fastscape-lem/fastscapelib/wiki/Design-notes:-flow-routing A specialized constructor for flow_graph designed for 1-D channel grids, simplifying setup by defaulting to a one-channel router. It uses SFINAE to enable this constructor only for profile_grid_xt types. ```cpp template template>::value, int> = 0> flow_graph::flow_graph(const PG& channel_grid, sink_resolve_algo sink_algo = sink_resolve_algo::none) : flow_graph(channel_grid, flow_route_algo::one_channel, sink_algo) { // ... } ``` -------------------------------- ### Create RasterGrid in Python Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Illustrates creating a 2D RasterGrid with uniform resolution and fixed boundaries, or from total domain length. Different boundary conditions can be applied to each side. ```python import fastscapelib as fs import numpy as np # Create a 201x301 raster grid with 250m resolution and fixed boundaries grid = fs.RasterGrid([201, 301], [250.0, 250.0], fs.NodeStatus.FIXED_VALUE) # Create from total length (50km x 75km domain) grid = fs.RasterGrid.from_length([201, 301], [5e4, 7.5e4], fs.NodeStatus.FIXED_VALUE) # Create with different boundary conditions on each side # [left, right, top, bottom] bs = [ fs.NodeStatus.FIXED_VALUE, # left - base level fs.NodeStatus.CORE, # right - free boundary fs.NodeStatus.LOOPED, # top - periodic fs.NodeStatus.LOOPED, # bottom - periodic ] grid = fs.RasterGrid.from_length([101, 201], [1e4, 2e4], bs) # Create elevation array matching grid shape elevation = np.random.uniform(0, 1, size=grid.shape) print(f"Elevation shape: {elevation.shape}") ``` -------------------------------- ### Create RasterGrid in C++ Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Demonstrates creating a 2D RasterGrid in C++ using uniform fixed-value boundaries or specifying different boundary conditions for each side. It also shows creating a random elevation field using xtensor. ```cpp #include "fastscapelib/grid/raster_grid.hpp" #include "xtensor/generators/xrandom.hpp" namespace fs = fastscapelib; // Create raster grid with uniform fixed-value boundaries fs::raster_boundary_status bs(fs::node_status::fixed_value); auto grid = fs::raster_grid<>::from_length({ 201, 301 }, { 5e4, 7.5e4 }, bs); // Create with different boundaries on each side fs::raster_boundary_status bs2{ fs::node_status::fixed_value, fs::node_status::core, fs::node_status::looped, fs::node_status::looped }; auto grid2 = fs::raster_grid({ 101, 201 }, { 1e2, 1e2 }, bs2); // Create random elevation field xt::xarray elevation = xt::random::rand(grid.shape()); ``` -------------------------------- ### Activate development environment Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/contributing.md Activates the environment for the current session. ```bash $ conda activate fastscapelib-dev ``` -------------------------------- ### Create a triangular domain mesh Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Defines points and triangle connectivity to initialize a TriMesh with fixed boundary conditions. ```python points = np.array([ [0.0, 0.0], [1000.0, 0.0], [500.0, 866.0], [500.0, 289.0] # interior point ]) triangles = np.array([ [0, 1, 3], [1, 2, 3], [2, 0, 3] ], dtype=np.int32) # Set outlet node as base level base_levels = {0: fs.NodeStatus.FIXED_VALUE} # Create triangular mesh grid = fs.TriMesh(points, triangles, base_levels) print(f"Number of nodes: {grid.size}") print(f"Node areas: {grid.nodes_areas()}") ``` -------------------------------- ### Create ProfileGrid with Fixed Value Boundaries (Python) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_grids.md Initializes a ProfileGrid with fixed value boundaries on both ends, suitable for simulating hillslope cross-sections with defined base levels. ```Python import fastscapelib as fs grid = fs.ProfileGrid.from_length(501, 500.0, fs.NodeStatus.FIXED_VALUE) ``` -------------------------------- ### Build Individual Benchmark Executables Source: https://github.com/fastscape-lem/fastscapelib/blob/main/benchmark/CMakeLists.txt Iterates through a list of benchmark source files, creating a separate executable for each. Links against necessary libraries and sets C++17 standard. ```cmake set(FASTSCAPELIB_BENCHMARK_HEADERS benchmark_setup.hpp) set(FASTSCAPELIB_BENCHMARK_SRC benchmark_basin_graph.cpp benchmark_diffusion_adi.cpp benchmark_multi_flow_router.cpp benchmark_profile_grid.cpp benchmark_raster_grid.cpp benchmark_single_flow_router.cpp benchmark_sink_resolver.cpp benchmark_spl.cpp ) find_package (Eigen3 3.3 REQUIRED NO_MODULE) # -- build a target for each benchmark foreach(filename IN LISTS FASTSCAPELIB_BENCHMARK_SRC) string(REPLACE ".cpp" "" targetname ${filename}) add_executable(${targetname} ${filename} ${FASTSCAPELIB_BENCHMARK_HEADERS} main.cpp) target_link_libraries(${targetname} PRIVATE fastscapelib xtensor Eigen3::Eigen benchmark::benchmark) target_compile_features(${targetname} PRIVATE cxx_std_17) endforeach() ``` -------------------------------- ### Define and Initialize Flow Kernel Source: https://github.com/fastscape-lem/fastscapelib/blob/main/examples/flow_kernel.ipynb Sets up the grid, flow graph, and the Numba-based kernel function for erosion calculations. ```python import matplotlib.pyplot as plt import numba as nb import numpy as np import fastscapelib as fs grid = fs.RasterGrid.from_length([201, 301], [5e4, 7.5e4], fs.NodeStatus.FIXED_VALUE) flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter(), fs.MSTSinkResolver()]) rng = np.random.Generator(np.random.PCG64(1234)) init_elevation = rng.uniform(0, 5, size=grid.shape) drainage_area = np.empty_like(init_elevation) uplift_rate = np.full_like(init_elevation, 1e-3) uplift_rate[[0, -1], :] = 0.0 uplift_rate[:, [0, -1]] = 0.0 flow_graph.update_routes(init_elevation) def kernel_func(node): dt = node.dt r_count = node.receivers.count if r_count == 1 and node.receivers.distance[0] == 0.0: return elevation_flooded = np.finfo(np.double).max for r in range(r_count): irec_elevation_next = node.receivers.elevation[r] - node.receivers.erosion[r] if irec_elevation_next < elevation_flooded: elevation_flooded = irec_elevation_next if node.elevation <= elevation_flooded: return eq_num = node.elevation eq_den = 1.0 for r in range(r_count): irec_elevation = node.receivers.elevation[r] irec_elevation_next = irec_elevation - node.receivers.erosion[r] if irec_elevation > node.elevation: continue irec_weight = node.receivers.weight[r] irec_distance = node.receivers.distance[r] factor = ( node.k_coef * node.dt * np.power(node.drainage_area * irec_weight, node.area_exp) ) factor /= irec_distance eq_num += factor * irec_elevation_next eq_den += factor elevation_updated = eq_num / eq_den if elevation_updated < elevation_flooded: elevation_updated = elevation_flooded + np.finfo(np.double).tiny node.erosion = node.elevation - elevation_updated elevation = init_elevation.ravel().copy() erosion = np.zeros(flow_graph.size) drainage_area = np.ones(flow_graph.size) ``` -------------------------------- ### Initialize Topography and Drainage Area Arrays Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/catchment_py.ipynb Creates the initial elevation data as a flat surface with random perturbations and initializes an empty array to store drainage area calculations. ```python init_elevation = np.random.uniform(0, 1, size=grid.shape) + 500.0 elevation = init_elevation drainage_area = np.empty_like(elevation) ``` -------------------------------- ### Initialize Topographic Elevation on Raster Grid Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_grids.md Demonstrates setting random initial elevation values using Xtensor containers in C++ and NumPy arrays in Python. ```C++ #include "xtensor/containers/xarray.hpp" #include "xtensor/generators/xrandom.hpp" #include "fastscapelib/grid/raster_grid.hpp" namespace fs = fastscapelib; fs::raster_boundary_status bs{ fs::node_status::fixed_value }; fs::raster_grid grid({ 101, 101 }, { 200.0, 200.0 }, bs); // // setting a xt::xarray with the double data type // xt::xarray elevation = xt::random::rand(grid.shape()); // // setting a xt::xtensor with dimensions and data type from grid inner types // using dtype = fs::raster_grid<>::grid_data_type; using ndims = fs::grid_inner_types>::container_ndims; xt::xtensor elevation_alt = xt::random::rand(grid.shape()); // // use the xtensor container selector set for the grid // using selector = fs::raster_grid<>::container_selector; using ctype = fs::container_selection::tensor_type; ctype elevation_alt2 = xt::random::rand(grid.shape()); ``` ```Python import numpy as np import fastscapelib as fs grid = fs.RasterGrid([101, 101], [200.0, 200.0], fs.NodeStatus.FIXED_VALUE) elevation = np.random.uniform(size=grid.shape) ``` -------------------------------- ### Create Flow Graph from Raster Grid (Python) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_flow.md Instantiates a Python flow graph from a raster grid, specifying single flow router strategy. Requires importing the library. ```Python import fastscapelib as fs grid = fs.RasterGrid([100, 100], [200.0, 200.0], fs.NodeStatus.FIXED_VALUE) graph = fs.FlowGraph(grid, [fs.SingleFlowRouter()]) ``` -------------------------------- ### Create ProfileGrid with Fixed Value Boundaries (C++) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_grids.md Initializes a ProfileGrid with fixed value boundaries on both ends, suitable for simulating hillslope cross-sections with defined base levels. ```C++ #include "fastscapelib/grid/profile_grid.hpp" namespace fs = fastscapelib; fs::profile_boundary_status bs(fs::node_status::fixed_value); auto grid = fs::profile_grid<>::from_length(501, 500.0, bs); ``` -------------------------------- ### Simulate River Profile Evolution with Knickpoint Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Simulates a 1D river longitudinal profile, including knickpoint formation by altering erodibility mid-simulation. Requires setting up a 1D grid, flow graph, and eroder, then running a time-stepping simulation with uplift and erosion. ```python import fastscapelib as fs import numpy as np # 1D profile grid: 101 nodes, 300m spacing # Fixed value at outlet (left), free at divide (right) grid = fs.ProfileGrid(101, 300.0, [fs.NodeStatus.FIXED_VALUE, fs.NodeStatus.CORE]) # Flow graph (trivial for 1D profile) flow_graph = fs.FlowGraph(grid, [fs.SingleFlowRouter()]) # X-coordinates (distance from divide) x0 = 300.0 length = (grid.size - 1) * grid.spacing x = np.linspace(x0 + length, x0, num=grid.size) # Drainage area from Hack's law hack_coef, hack_exp = 6.69, 1.67 drainage_area = hack_coef * x**hack_exp # SPL eroder spl_eroder = fs.SPLEroder(flow_graph, k_coef=1e-4, area_exp=0.5, slope_exp=1, tolerance=1e-5) # Initial gentle slope elevation = (length + x0 - x) * 1e-4 # Uplift rate (zero at outlet) uplift_rate = np.full_like(x, 1e-3) uplift_rate[0] = 0.0 # Run simulation dt = 100.0 nsteps = 4000 flow_graph.update_routes(elevation) # Only needed once for 1D profile for step in range(nsteps): uplifted_elevation = elevation + dt * uplift_rate # Create knickpoint by changing erodibility mid-simulation if step == 3000: spl_eroder.k_coef /= 4 spl_erosion = spl_eroder.erode(uplifted_elevation, drainage_area, dt) elevation = uplifted_elevation - spl_erosion print(f"Final max elevation: {elevation.max():.2f} m") ``` -------------------------------- ### Fetch Google Benchmark Dependency Source: https://github.com/fastscape-lem/fastscapelib/blob/main/benchmark/CMakeLists.txt Configures fetching the google-benchmark library, either by downloading a specific version or using a local source directory. Sets benchmark-specific build options. ```cmake include(FetchContent) if(FS_DOWNLOAD_GBENCHMARK OR FS_GBENCHMARK_SRC_DIR) if(FS_DOWNLOAD_GBENCHMARK) message(STATUS "Downloading google-benchmark v1.8.0") FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.8.0) else() message(STATUS "Build google-benchmark from local directory") FetchContent_Declare(googlebenchmark SOURCE_DIR FS_GBENCHMARK_SRC_DIR ) endif() set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable building benchmark tests" FORCE) set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Disable installing benchmark" FORCE) FetchContent_MakeAvailable(googlebenchmark) else() find_package(benchmark REQUIRED) endif() ``` -------------------------------- ### Initialize TriMesh Object Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/catchment_py.ipynb Creates a TriMesh object from the generated mesh points, triangles, and defined base levels. This object represents the computational grid for the simulation. ```python grid = fs.TriMesh(points, triangles, base_levels) ``` -------------------------------- ### Create Flow Graph from Raster Grid (C++) Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/guide_flow.md Instantiates a C++ flow graph from a raster grid, specifying single flow router strategy. Requires including necessary headers. ```C++ #include "fastscapelib/flow/flow_graph.hpp" #include "fastscapelib/flow/flow_router.hpp" #include "fastscapelib/grid/raster_grid.hpp" namespace fs = fastscapelib; fs::raster_boundary_status boundaries{ fs::node_status::fixed_value }; fs::raster_grid grid({ 100, 100 }, { 200.0, 200.0 }, boundaries); fs::flow_graph> graph(grid, { fs::single_flow_router() }); ``` -------------------------------- ### Run Simulation Loop Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/examples/escarpment_py.ipynb Iterates through time steps to update flow routes, accumulate drainage, and apply erosion processes. ```python dt = 2e3 nsteps = 50 for step in range(nsteps): # flow routing flow_graph.update_routes(elevation) # flow accumulation (drainage area) flow_graph.accumulate(drainage_area, 1.0) # apply channel erosion then hillslope diffusion spl_erosion = spl_eroder.erode(elevation, drainage_area, dt) diff_erosion = diffusion_eroder.erode(elevation - spl_erosion, dt) # update topography elevation = elevation - spl_erosion - diff_erosion ``` -------------------------------- ### Build Fastscapelib Benchmarks Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/build_options.md Build the Fastscapelib benchmarks after CMake configuration. This command compiles the benchmark executable. ```bash cmake --build build/benchmarks ``` -------------------------------- ### Run Landscape Evolution Simulation Source: https://github.com/fastscape-lem/fastscapelib/blob/main/examples/eroder_kernel.ipynb Executes a loop to update topography based on uplift and erosion rates. ```python dt = 2e4 nsteps = 50 uplift = dt * uplift_rate.ravel() spl_eroder = NumbaSplEroder(flow_graph, max_receivers=10) fs_spl_eroder = fs.SPLEroder( flow_graph, k_coef=2e-4, area_exp=0.4, slope_exp=1., tolerance=1e-5, ) def run_simulation(eroder): elevation = init_elevation.copy().ravel() drainage_area = np.empty_like(elevation) for step in range(nsteps): # uplift (no uplift at fixed elevation boundaries) uplifted_elevation = elevation + uplift # flow routing flow_graph.update_routes(uplifted_elevation) # flow accumulation (drainage area) flow_graph.accumulate(drainage_area, 1.0) erosion = eroder.erode(uplifted_elevation, drainage_area, dt) # update topography elevation = uplifted_elevation - erosion.ravel() return elevation.reshape(grid.shape) ``` -------------------------------- ### Configure CMake for Benchmarks Source: https://github.com/fastscape-lem/fastscapelib/blob/main/doc/source/build_options.md Configure the build system using CMake to include the Fastscapelib benchmarks. This command sets the source directory and enables benchmark building. ```bash cmake -S . -B build/benchmarks -DFS_BUILD_BENCHMARKS=ON ``` -------------------------------- ### Simulate Escarpment Retreat with Multiple Flow Directions Source: https://context7.com/fastscape-lem/fastscapelib/llms.txt Simulates escarpment retreat on a 2D grid with looped boundaries, using multiple flow direction routing and both stream power and diffusion erosion. Requires setting up a 2D grid, multiple flow routers, and different eroder types. ```python import fastscapelib as fs import numpy as np # Grid with looped boundaries (periodic in y-direction) bs = [ fs.NodeStatus.FIXED_VALUE, # left - base level fs.NodeStatus.CORE, # right - free boundary fs.NodeStatus.LOOPED, # top - periodic fs.NodeStatus.LOOPED, # bottom - periodic ] grid = fs.RasterGrid.from_length([101, 201], [1e4, 2e4], bs) # Multiple flow direction routing flow_graph = fs.FlowGraph(grid, [ fs.SingleFlowRouter(), fs.MSTSinkResolver(), fs.MultiFlowRouter(1.1) ]) # Eroders spl_eroder = fs.SPLEroder(flow_graph, k_coef=1e-4, area_exp=0.4, slope_exp=1, tolerance=1e-5) diffusion_eroder = fs.DiffusionADIEroder(grid, 0.01) # Initial escarpment topography rng = np.random.Generator(np.random.PCG64(1234)) elevation = rng.uniform(0, 1, size=grid.shape) elevation[:, 100:] += 400 # elevated plateau on the right drainage_area = np.empty_like(elevation) # Run simulation dt = 2e3 nsteps = 50 for step in range(nsteps): flow_graph.update_routes(elevation) flow_graph.accumulate(drainage_area, 1.0) spl_erosion = spl_eroder.erode(elevation, drainage_area, dt) diff_erosion = diffusion_eroder.erode(elevation - spl_erosion, dt) elevation = elevation - spl_erosion - diff_erosion print(f"Escarpment retreat complete. Max elevation: {elevation.max():.2f} m") ```