From 88954b6ea31eb04d49d335d191ebe442f721fc3f Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 6 Mar 2023 22:17:10 -0500 Subject: [PATCH] add a create-a-component landlab notebook --- .../landlab/landlab/create_a_component.ipynb | 893 ++++++++++++++++++ 1 file changed, 893 insertions(+) create mode 100644 lessons/landlab/landlab/create_a_component.ipynb diff --git a/lessons/landlab/landlab/create_a_component.ipynb b/lessons/landlab/landlab/create_a_component.ipynb new file mode 100644 index 0000000..910fd76 --- /dev/null +++ b/lessons/landlab/landlab/create_a_component.ipynb @@ -0,0 +1,893 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a863305e", + "metadata": {}, + "source": [ + "# Remember to change your kernel to *CSDMS*\n", + "\n", + "To run this code, you will need to use the *CSDMS* kernel.\n", + "\n", + "**From the *Kernel* menu: *Change kernel* -> *CSDMS***" + ] + }, + { + "cell_type": "markdown", + "id": "571cb775", + "metadata": {}, + "source": [ + "# Building a *landlab* component\n", + "\n", + "In this next section we will build two landlab components: one with my help and one on your own. These two components will model physical processes that we will use later on to build a landscape evolution model.\n", + "\n", + "The first component will be a linear diffusion component to model soil creep on hillslopes and the second will uplift (or subside) the landscape through time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1dd92223", + "metadata": {}, + "outputs": [], + "source": [ + "from landlab import RasterModelGrid, NodeStatus, LinkStatus\n", + "from tqdm import trange" + ] + }, + { + "cell_type": "markdown", + "id": "17edd289", + "metadata": {}, + "source": [ + "## 1. Linear Diffusion as a Function\n", + "\n", + "We will begin with a function that solves the diffusion equation on a landlab grid using the equations from the previous section. This will be the function we will turn into a *landlab* component.\n", + "\n", + "Our function takes a *landlab* grid, an array of values to diffuse, and a keyword argument to specify the diffusion coeffiecient. The result will be the diffusion rate at each node of the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff989542", + "metadata": {}, + "outputs": [], + "source": [ + "def calc_diffusion_rate(grid, value_at_node, diffusion_coefficient=1.0):\n", + " \"\"\"Calculate the rate of diffusion of a quantity defined on a landlab grid.\n", + "\n", + " Parameters\n", + " ----------\n", + " grid : ModelGrid\n", + " A landlab grid.\n", + " value_at_node : ndarray of float\n", + " The quantity to diffuse, defined at the grid's nodes.\n", + " diffusion_coefficient : float, optional\n", + " Diffusion coefficent to use for the diffusion.\n", + " \n", + " Returns\n", + " -------\n", + " value_at_node\n", + " Input array of values after diffusion.\n", + " \"\"\"\n", + " qs = -diffusion_coefficient * grid.calc_grad_at_link(value_at_node)\n", + " qs[grid.status_at_link != LinkStatus.ACTIVE] = 0.0\n", + " \n", + " dzdt = -grid.calc_flux_div_at_node(qs)\n", + " return dzdt" + ] + }, + { + "cell_type": "markdown", + "id": "7794b864", + "metadata": {}, + "source": [ + "Now try to run this function on a *landlab* grid to diffuse some quantity. For simplicity, use a `RasterModelGrid`. Although `calc_diffusion_rate` function is agnostic as to what grid you pass it, it's will be easier to visualize a raster grid.\n", + "\n", + "To begin with, see if you can,\n", + "* Create a `RasterModelGrid`\n", + "* Create an array of values (at grid nodes) to diffuse\n", + "* Set boundary conditions\n", + "\n", + "To make things interesting, you will need something to diffuse. Try to initialize your array of values with a step that goes from 0.0 for $x <= 100$ to 10.0 for $x > 100.0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "419e31d9", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "86b86016", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "grid = RasterModelGrid((100, 200))\n", + "\n", + "z = grid.zeros(at=\"node\")\n", + "z[(grid.x_of_node > 100)] = 10.0\n", + "\n", + "grid.status_at_node[grid.nodes_at_left_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_right_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_top_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_bottom_edge] = NodeStatus.CLOSED\n", + "\n", + "grid.imshow(\"node\", z)\n", + "```\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "8d907c9b", + "metadata": {}, + "source": [ + "Before we start to run diffusion through time, we need to determine a stable time step so our solution doesn't blow up.\n", + "\n", + "We will use a timestep with a [Courant–Friedrichs–Lewy condition](https://en.wikipedia.org/wiki/Courant–Friedrichs–Lewy_condition) of $C_{cfl}=0.2$. This will keep our solution numerically stable. \n", + "\n", + "$C_{cfl} = \\frac{\\Delta t D}{\\Delta x^2} = 0.2$\n", + "\n", + "Write a function that calculates a stable time step, $dt$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c39d1f6c", + "metadata": {}, + "outputs": [], + "source": [ + "def calc_stable_time_step(grid, diffusion_coefficient):\n", + " return # Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "def40fd0", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "def calc_stable_time_step(grid, diffusion_coefficient):\n", + " return 0.2 * grid.length_of_link.min()**2 / diffusion_coefficient\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "21aa1418", + "metadata": {}, + "source": [ + "And now for some landform evolution! First calculate a stable time step for your simulation and then write a 1000 iteration loop that, for each iteration, calculates the diffusion rate and then adds that to the current landscape.\n", + "\n", + "If you like, you can experiment with different values for the diffusion coefficient and the time step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d61578d2", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here: calculate time step\n", + "\n", + "for _ in trange(1000):\n", + " # Type your code here: calculate diffusion and update *z*\n", + "\n", + "grid.imshow(z)" + ] + }, + { + "cell_type": "markdown", + "id": "1a7fb0d9", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "diffusion_coefficient = 1.0\n", + "dt = calc_stable_time_step(grid, diffusion_coefficient)\n", + "\n", + "for _ in trange(1000):\n", + " z += calc_diffusion_rate(\n", + " grid, z, diffusion_coefficient=diffusion_coefficient\n", + " ) * dt\n", + "\n", + "grid.imshow(\"node\", z)\n", + "```\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "8309a0ac", + "metadata": {}, + "source": [ + "## 2. Represent the function as a class\n", + "\n", + "The next step in turning a function into a component is to represent your function as a class. To begin with, we implement two methods:\n", + "* `__init__`: this method is called when the class is instantiated and will accept to arguments. The first MUST be a landlab grid, the second will be a keyword that sets the diffusion coefficient.\n", + "* `calc_rate`: this method will take an array of values and return the diffusion rate at each element.\n", + "* `calc_stable_time_step`: this method will calculate a stable time step for the diffuser.\n", + "\n", + "So that we're all on the same page, let's call the new class `LandscapeDiffuser`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d366d12", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "577d55f6", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "class LandscapeDiffuser:\n", + " def __init__(self, grid, diffusion_coefficient=1.0):\n", + " self._grid = grid\n", + " self._diffusion_coefficient = diffusion_coefficient\n", + "\n", + " def calc_rate(self, values):\n", + " return calc_diffusion_rate(\n", + " self._grid, values, diffusion_coefficient=self._diffusion_coefficient\n", + " )\n", + "\n", + " def calc_stable_time_step(self):\n", + " return 0.2 * self._grid.length_of_link.min()**2 / self._diffusion_coefficient\n", + "\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "204d192d", + "metadata": {}, + "source": [ + "Now, just as you did before, run diffusion on a *landlab* grid only this time using the new `LandscapeDiffuser` class.\n", + "\n", + "As before, create a grid, a quantity to diffuse, and set boundary conditions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7860504", + "metadata": {}, + "outputs": [], + "source": [ + "grid = RasterModelGrid((100, 200))\n", + "\n", + "z = grid.add_zeros(\"topographic__elevation\", at=\"node\")\n", + "z[(grid.x_of_node > 100) & (grid.y_of_node > 50)] = 10.0\n", + "\n", + "grid.status_at_node[grid.nodes_at_left_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_right_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_top_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_bottom_edge] = NodeStatus.CLOSED\n", + "\n", + "grid.imshow(\"node\", z)" + ] + }, + { + "cell_type": "markdown", + "id": "e4d0e36c", + "metadata": {}, + "source": [ + "Instantiate the `LandscapeDiffuser` and, again, run it over successive time steps." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f9af856", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "0ec12c86", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "diffusion_coefficient = 0.5\n", + "diffuse = LandscapeDiffuser(grid, diffusion_coefficient=diffusion_coefficient)\n", + "dt = diffuse.calc_stable_time_step()\n", + "\n", + "for _ in trange(10000):\n", + " z += diffuse.calc_rate(z) * dt\n", + "\n", + "grid.imshow(\"node\", z)\n", + "\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "0ce7e067", + "metadata": {}, + "source": [ + "# 3. Create a landlab component class\n", + "\n", + "We need to add just a few more things to the `LandscapeDiffuser` before we can call it a complete *landlab* component.\n", + "\n", + "* All components MUST inherit from `landlab.Component`\n", + "* All components MUST have a `run_one_step` function\n", + "* All components MUST call `super().__init__(grid)` as part of its `__init__` method\n", + "* All components MUST have two attributes:\n", + " * `_info` is a dictionary that provides information about a component's input and output fields\n", + " * `_unit_agnostic` is a boolean that indicates for input and output fields MUST be given in specific units.\n", + "\n", + "The `run_one_step` method acts on a *landlab* field and can accept an optional *dt* keyword that tells it how long to run for. For the `LandscapeDiffuser` let's have it operate on a field called *topographic__elevation* that's defined at *nodes*.\n", + "\n", + "The `_info` attribute describes each field the component uses. It is a dictionary where keys are field names and values are descriptions. For example, you can look at the `_info` field of an existing component." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58ca7ad2", + "metadata": {}, + "outputs": [], + "source": [ + "from landlab.components import BedrockLandslider\n", + "\n", + "BedrockLandslider._info" + ] + }, + { + "cell_type": "markdown", + "id": "fd05c3da", + "metadata": {}, + "source": [ + "Attributes for each field include,\n", + "* *dtype*: the data type of the field (float, int, etc.)\n", + "* *intent*: if the field is an input ('in') output ('out') or both ('inout')\n", + "* *optional*: boolean to indicate if the field is required\n", + "* *unit*: the units of the field as a string\n", + "* *mapping*: on what grid element the quantity is defined ('node', 'cell', 'link', etc.)\n", + "* *doc*: a short description of the field" + ] + }, + { + "cell_type": "markdown", + "id": "066ee4d1", + "metadata": {}, + "source": [ + "Fill in the template below with the *landlab* component requirements." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b6d3327", + "metadata": {}, + "outputs": [], + "source": [ + "from landlab import Component\n", + "\n", + "\n", + "class LandscapeDiffuser(Component):\n", + " \n", + " _info = {\n", + " # Add description of input/output fields here\n", + " }\n", + "\n", + " _unit_agnostic = True or False # Should this component be unit agnostic or not?\n", + "\n", + " def __init__(self, grid, diffusion_coefficient=1.0):\n", + " self._grid = grid\n", + " self._diffusion_coefficient = diffusion_coefficient\n", + " \n", + " super().__init__(grid)\n", + " \n", + " def calc_rate(self, values):\n", + " return calc_diffusion_rate(\n", + " self._grid, values, diffusion_coefficient=self._diffusion_coefficient\n", + " )\n", + "\n", + " def calc_stable_time_step(self):\n", + " return 0.2 * self._grid.length_of_link.min()**2 / self._diffusion_coefficient\n", + "\n", + " def run_one_step(self, dt=1.0):\n", + " \"\"\"Run diffusion on *topographic__elevation*\"\"\"\n", + " # Type your code here." + ] + }, + { + "cell_type": "markdown", + "id": "ed788e3a", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "\n", + "```python\n", + "from landlab import Component\n", + "\n", + "\n", + "class LandscapeDiffuser(Component):\n", + " _info = {\n", + " \"topographic__elevation\": {\n", + " \"dtype\": float,\n", + " \"intent\": \"inout\",\n", + " \"optional\": False,\n", + " \"units\": \"m\",\n", + " \"mapping\": \"node\",\n", + " \"doc\": \"Diffuse sediment over a landscape\",\n", + " },\n", + " }\n", + "\n", + " _unit_agnostic = True\n", + "\n", + " def __init__(self, grid, diffusion_coefficient=1.0):\n", + " self._grid = grid\n", + " self._diffusion_coefficient = diffusion_coefficient\n", + "\n", + " super().__init__(grid)\n", + "\n", + " def calc_rate(self, values):\n", + " return calc_diffusion_rate(\n", + " self._grid, values, diffusion_coefficient=self._diffusion_coefficient\n", + " )\n", + "\n", + " def calc_stable_time_step(self):\n", + " return 0.2 * self._grid.length_of_link.min()**2 / self._diffusion_coefficient\n", + "\n", + " def run_one_step(self, dt=1.0):\n", + " time_step = self.calc_stable_time_step()\n", + " n_steps = int(dt / time_step)\n", + " for _ in range(n_steps):\n", + " dzdt = self.calc_rate(grid.at_node[\"topographic__elevation\"])\n", + " grid.at_node[\"topographic__elevation\"] += dzdt * time_step\n", + "\n", + " remaining = dt - (n_steps * time_step)\n", + " if remaining > 0:\n", + " dzdt = self.calc_rate(grid.at_node[\"topographic__elevation\"])\n", + " grid.at_node[\"topographic__elevation\"] += dzdt * remaining\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "37a2dfd3", + "metadata": {}, + "source": [ + "Now run the component as before, this time using the `run_one_step` method and ***adding a topographic__elevation field***." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aadaf948", + "metadata": {}, + "outputs": [], + "source": [ + "grid = RasterModelGrid((100, 200))\n", + "\n", + "z = grid.add_zeros(\"topographic__elevation\", at=\"node\")\n", + "z[(grid.x_of_node > 100) & (grid.y_of_node > 50)] = 10.0\n", + "\n", + "grid.status_at_node[grid.nodes_at_left_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_right_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_top_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_bottom_edge] = NodeStatus.CLOSED\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])" + ] + }, + { + "cell_type": "markdown", + "id": "6f4db883", + "metadata": {}, + "source": [ + "Once again, write some code that will run the diffuser over the grid some number of times. Let's say for $1000$ time steps with a $dt=0.2$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ebd6335", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here." + ] + }, + { + "cell_type": "markdown", + "id": "c4c4f360", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "\n", + "```python\n", + "diffuse = LandscapeDiffuser(grid, diffusion_coefficient=0.5)\n", + "for _ in trange(1000):\n", + " diffuse.run_one_step(dt=0.2)\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "eedfcca1", + "metadata": {}, + "source": [ + "## 4. Create an `LandscapeUplifter` component\n", + "\n", + "\n", + "Now create a `LandscapeUplifter` component based on a function that uplifts and landscape at a constant rate.\n", + "\n", + "To be compatible with the `LandscapeDiffuser`, have the uplifter also act on a field called *topographic__elevation* defined at nodes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b6159c1", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "639587c7", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "class LandscapeUplifter(Component):\n", + " _info = {\n", + " \"topographic__elevation\": {\n", + " \"dtype\": float,\n", + " \"intent\": \"inout\",\n", + " \"optional\": False,\n", + " \"units\": \"m\",\n", + " \"mapping\": \"node\",\n", + " \"doc\": \"Elevation of a landspace surface\",\n", + " },\n", + " }\n", + " \n", + " _unit_agnostic = True\n", + "\n", + " def __init__(self, grid, uplift_rate=0.0):\n", + " self._uplift_rate = uplift_rate\n", + " super().__init__(grid)\n", + " \n", + " def run_one_step(self, dt=1.0):\n", + " self.grid.at_node[\"topographic__elevation\"][self.grid.core_nodes] += self._uplift_rate * dt\n", + "```\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10c5eced", + "metadata": {}, + "outputs": [], + "source": [ + "grid = RasterModelGrid((100, 200))\n", + "\n", + "z = grid.add_zeros(\"topographic__elevation\", at=\"node\")\n", + "\n", + "grid.status_at_node[grid.nodes_at_left_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_right_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_top_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_bottom_edge] = NodeStatus.CLOSED\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])" + ] + }, + { + "cell_type": "markdown", + "id": "edc6c969", + "metadata": {}, + "source": [ + "As with the `LandscapeDiffuser` try writing code to run your `LandscapeUplifter` within a time loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b47edc3", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "d797aaf1", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "uplift = LandscapeUplifter(grid, uplift_rate=.001)\n", + "\n", + "dt = 1000.0\n", + "for _ in trange(20):\n", + " uplift.run_one_step(dt=dt)\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])\n", + "```\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "424d5487", + "metadata": {}, + "source": [ + "## 5. Couple `LandscapeDiffuser` with `LandscapeUplifter`\n", + "\n", + "To couple the `LandscapeDiffuser` and the `LandscapeUplifter` we first instantiate both components *with the same grid*. Within the time loop, we call one component's *run_one_step* method, and then the other's.\n", + "\n", + "Try writing the code to couple these two components." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2778501b", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "b8c087a4", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "\n", + "```python\n", + "diffuse = LandscapeDiffuser(grid, diffusion_coefficient=0.2)\n", + "uplift = LandscapeUplifter(grid, uplift_rate=.001)\n", + "\n", + "dt = 1000.0\n", + "for _ in trange(20):\n", + " uplift.run_one_step(dt=dt)\n", + " diffuse.run_one_step(dt=dt)\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "b2f97627", + "metadata": {}, + "source": [ + "## 6. Construct a landscape evolution model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da7539b6", + "metadata": {}, + "outputs": [], + "source": [ + "from landlab.components import FlowAccumulator, FastscapeEroder\n", + "from numpy.random import default_rng\n", + "\n", + "rng = default_rng()" + ] + }, + { + "cell_type": "markdown", + "id": "d889ebf3", + "metadata": {}, + "source": [ + "Create an initial grid that's pretty much flat except for a small amount of noise. If you like, feel free to play around with the size of the grid, the grid spacing, or the boundary conditions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4cdb947c", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "fdaa910b", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "grid = RasterModelGrid((100, 200), xy_spacing=0.02)\n", + "\n", + "z = grid.add_field(\n", + " \"topographic__elevation\",\n", + " rng.random(grid.number_of_nodes) * 1e-5,\n", + " at=\"node\",\n", + ")\n", + "\n", + "grid.status_at_node[grid.nodes_at_left_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_right_edge] = NodeStatus.CLOSED\n", + "grid.status_at_node[grid.nodes_at_top_edge] = NodeStatus.FIXED_VALUE\n", + "grid.status_at_node[grid.nodes_at_bottom_edge] = NodeStatus.FIXED_VALUE\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])\n", + "```\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "308fe320", + "metadata": {}, + "source": [ + "Our LEM will couple our `LandscapeDiffuser` and `LandscapeUplifter` along with two new components from the *landlab* component library: `FlowAccumulator` and `FastscapeEroder`. The `FlowAccumulator` will route flow over the landscape and the `FastscapeEroder` will erode and transport sediment over the landscape." + ] + }, + { + "cell_type": "markdown", + "id": "800dbfd6", + "metadata": {}, + "source": [ + "Step 1: Instantiate all of the components." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6905a6e6", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "12e8e254", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "\n", + "```python\n", + "accumulate = FlowAccumulator(grid)\n", + "erode = FastscapeEroder(grid, K_sp=0.3, m_sp=0.5)\n", + "diffuse = LandscapeDiffuser(grid, diffusion_coefficient=0.0004)\n", + "uplift = LandscapeUplifter(grid, uplift_rate=0.001)\n", + "```\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "d70d1be7", + "metadata": {}, + "source": [ + "Step 2: Run each component within a time loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48ea30be", + "metadata": {}, + "outputs": [], + "source": [ + "# Type your code here" + ] + }, + { + "cell_type": "markdown", + "id": "a87ae604", + "metadata": {}, + "source": [ + "
\n", + "(click to see solution)\n", + "\n", + "```python\n", + "dt = 0.5\n", + "for _ in trange(100):\n", + " uplift.run_one_step(dt=dt)\n", + " diffuse.run_one_step(dt=dt)\n", + " accumulate.run_one_step()\n", + " erode.run_one_step(dt=dt)\n", + "\n", + "grid.imshow(\"node\", grid.at_node[\"topographic__elevation\"])\n", + "```\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b928951", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}