diff --git a/lessons/bmi/bmi-run-model-from-bmi.ipynb b/lessons/bmi/bmi-run-model-from-bmi.ipynb new file mode 100644 index 0000000..ddd3639 --- /dev/null +++ b/lessons/bmi/bmi-run-model-from-bmi.ipynb @@ -0,0 +1,429 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Run the `Heat` model through its BMI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Heat` models the diffusion of temperature on a uniform rectangular plate with Dirichlet boundary conditions. This is the canonical example used in the [bmi-example-python](https://github.com/csdms/bmi-example-python) repository. View the source code for the [model](https://github.com/csdms/bmi-example-python/blob/master/heat/heat.py) and its [BMI](https://github.com/csdms/bmi-example-python/blob/master/heat/bmi_heat.py) on GitHub." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start by importing `os`, `numpy` and the `Heat` BMI:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import numpy as np\n", + "\n", + "from heat import BmiHeat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create an instance of the model's BMI." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "x = BmiHeat()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What's the name of this model?" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The 2D Heat Equation\n" + ] + } + ], + "source": [ + "print(x.get_component_name())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start the `Heat` model through its BMI using a configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Heat model configuration\r\n", + "shape:\r\n", + " - 6\r\n", + " - 8\r\n", + "spacing:\r\n", + " - 1.0\r\n", + " - 1.0\r\n", + "origin:\r\n", + " - 0.0\r\n", + " - 0.0\r\n", + "alpha: 1.0\r\n" + ] + } + ], + "source": [ + "cat heat.yaml" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "x.initialize('heat.yaml')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Check the time information for the model." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Start time: 0.0\n", + "End time: 1.7976931348623157e+308\n", + "Current time: 0.0\n", + "Time step: 0.25\n", + "Time units: s\n" + ] + } + ], + "source": [ + "print('Start time:', x.get_start_time())\n", + "print('End time:', x.get_end_time())\n", + "print('Current time:', x.get_current_time())\n", + "print('Time step:', x.get_time_step())\n", + "print('Time units:', x.get_time_units())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Show the input and output variables for the component (aside on [Standard Names](https://csdms.colorado.edu/wiki/CSDMS_Standard_Names)):" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('plate_surface__temperature',)\n", + "('plate_surface__temperature',)\n" + ] + } + ], + "source": [ + "print(x.get_input_var_names())\n", + "print(x.get_output_var_names())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, get attributes of the grid on which the temperature variable is defined:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Grid id: 0\n", + "Grid rank: 2\n", + "Grid shape: (6, 8)\n", + "Grid spacing: [1.0, 1.0]\n", + "Grid type: uniform_rectilinear_grid\n" + ] + } + ], + "source": [ + "grid_id = x.get_var_grid('plate_surface__temperature')\n", + "print('Grid id:', grid_id)\n", + "print('Grid rank:', x.get_grid_rank(grid_id))\n", + "print('Grid shape:', x.get_grid_shape(grid_id))\n", + "print('Grid spacing:', x.get_grid_spacing(grid_id))\n", + "print('Grid type:', x.get_grid_type(grid_id))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Through the model's BMI, zero out the initial temperature field, except for an impulse:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "temperature = np.zeros(x.get_grid_shape(grid_id))\n", + "temperature[3, 4] = 100.0\n", + "x.set_value('plate_surface__temperature', temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Check that the temperature field has been updated:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 100. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]]\n" + ] + } + ], + "source": [ + "initial_temperature = x.get_value('plate_surface__temperature')\n", + "print(initial_temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now advance the model by a single time step:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "x.update()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "View the new state of the temperature field:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0. 0. 0. 0. 0. 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 12.5 0. 0. 0. ]\n", + " [ 0. 0. 0. 12.5 50. 12.5 0. 0. ]\n", + " [ 0. 0. 0. 0. 12.5 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0. ]]\n" + ] + } + ], + "source": [ + "updated_temperature = x.get_value('plate_surface__temperature')\n", + "print(updated_temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There's diffusion!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Advance the model to some distant time:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "distant_time = 2.0\n", + "while x.get_current_time() < distant_time:\n", + " x.update()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "View the final state of the temperature field:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0]\n", + " [ 0.0 0.2 0.9 2.1 2.8 2.1 0.9 0.0]\n", + " [ 0.0 0.7 2.2 4.7 6.2 4.7 2.1 0.0]\n", + " [ 0.0 0.9 3.0 6.1 7.9 6.1 2.8 0.0]\n", + " [ 0.0 0.6 2.0 4.1 5.3 4.1 1.8 0.0]\n", + " [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0]]\n" + ] + } + ], + "source": [ + "final_temperature = x.get_value('plate_surface__temperature')\n", + "np.set_printoptions(formatter={'float': '{: 5.1f}'.format})\n", + "print(final_temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that temperature isn't conserved on the plate:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "74.10263419151306\n" + ] + } + ], + "source": [ + "print(final_temperature.sum())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "End the model:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "x.finalize()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/lessons/bmi/bmi-run-model.ipynb b/lessons/bmi/bmi-run-model.ipynb new file mode 100644 index 0000000..8ef4114 --- /dev/null +++ b/lessons/bmi/bmi-run-model.ipynb @@ -0,0 +1,295 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Run the `Heat` model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Heat` models the diffusion of temperature on a uniform rectangular plate with Dirichlet boundary conditions. This is the canonical example used in the [bmi-example-python](https://github.com/csdms/bmi-example-python) repository. View the [source code](https://github.com/csdms/bmi-example-python/blob/master/heat/heat.py) for the model on GitHub." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Start by importing `numpy` and `Heat`:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from heat.heat import Heat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create an instance of the model, setting `shape` and `alpha` parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "n_rows = 6\n", + "n_cols = 8\n", + "conductivity = 1.0\n", + "m = Heat(shape=(n_rows,n_cols), alpha=conductivity)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Show derived parameters from the model:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Grid spacing: (1.0, 1.0)\n", + "Time step: 0.25\n" + ] + } + ], + "source": [ + "print('Grid spacing:', m.spacing)\n", + "print('Time step:', m.time_step)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What does the initial temperature field look like?" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]]\n" + ] + } + ], + "source": [ + "m.temperature = np.zeros_like(m.temperature)\n", + "print(m.temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Add an impulse to the temperature field: " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 100. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0.]]\n" + ] + } + ], + "source": [ + "m.temperature[3, 4] = 100.0\n", + "print(m.temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Advance the model by a single time step:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "m.advance_in_time()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "View the new state of the temperature field:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0. 0. 0. 0. 0. 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 12.5 0. 0. 0. ]\n", + " [ 0. 0. 0. 12.5 50. 12.5 0. 0. ]\n", + " [ 0. 0. 0. 0. 12.5 0. 0. 0. ]\n", + " [ 0. 0. 0. 0. 0. 0. 0. 0. ]]\n" + ] + } + ], + "source": [ + "print(m.temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There's diffusion!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Advance the model to some distant time:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "distant_time = 2.0\n", + "while m.time < distant_time:\n", + " m.advance_in_time()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "View the new state of the temperature field (with help from `np.set_printoptions`):" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0]\n", + " [ 0.0 0.2 0.9 2.1 2.8 2.1 0.9 0.0]\n", + " [ 0.0 0.7 2.2 4.7 6.2 4.7 2.1 0.0]\n", + " [ 0.0 0.9 3.0 6.1 7.9 6.1 2.8 0.0]\n", + " [ 0.0 0.6 2.0 4.1 5.3 4.1 1.8 0.0]\n", + " [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0]]\n" + ] + } + ], + "source": [ + "np.set_printoptions(formatter={'float': '{: 5.1f}'.format})\n", + "print(m.temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that temperature is set to zero at the boundaries." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "74.10263419151306" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.temperature.sum()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/lessons/bmi/heat.yaml b/lessons/bmi/heat.yaml new file mode 100644 index 0000000..a5bd435 --- /dev/null +++ b/lessons/bmi/heat.yaml @@ -0,0 +1,11 @@ +# Heat model configuration +shape: + - 6 + - 8 +spacing: + - 1.0 + - 1.0 +origin: + - 0.0 + - 0.0 +alpha: 1.0 diff --git a/lessons/bmi/index.ipynb b/lessons/bmi/index.ipynb new file mode 100644 index 0000000..4795d3b --- /dev/null +++ b/lessons/bmi/index.ipynb @@ -0,0 +1,53 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exploring Surface Processes using CSDMS Tools: How to Build Coupled Models\n", + "\n", + "## Day 2: The Basic Model Interface (BMI)\n", + "\n", + "Today we'll explore through a pair of Jupyter Notebooks what the Basic Model Interface is, and how to use it.\n", + "\n", + "* [Run the *Heat* model](bmi-run-model.ipynb)\n", + " * View model source code\n", + " * Set up the model\n", + " * Run the model\n", + " * View output\n", + "\n", + "* [Run the *Heat* model through its BMI](bmi-run-model-from-bmi.ipynb)\n", + " * View model BMI source code\n", + " * Set up the model through its BMI\n", + " * Run the model\n", + " * View output\n", + "\n", + "Additional information:\n", + " * [BMI documentation](https://bmi.readthedocs.io) \n", + " * [Python BMI spec](https://github.com/csdms/bmi-python)\n", + " * [Python BMI example](https://github.com/csdms/bmi-example-python)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}