From 1d368a7650bd122d97934e2468ecbe38adae3329 Mon Sep 17 00:00:00 2001 From: Mark Piper Date: Thu, 17 Apr 2025 14:18:14 -0600 Subject: [PATCH 1/5] Lay out sections for BMI notebook --- examples/run-bmi-model.ipynb | 339 +++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 examples/run-bmi-model.ipynb diff --git a/examples/run-bmi-model.ipynb b/examples/run-bmi-model.ipynb new file mode 100644 index 0000000..643d6ab --- /dev/null +++ b/examples/run-bmi-model.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "154382ab-1ad1-42de-8dfc-2b7f9fe3d368", + "metadata": {}, + "source": [ + "# Run `DiffusionModel` through its BMI" + ] + }, + { + "cell_type": "markdown", + "id": "1d63b202-5550-4b78-a75f-645024dec654", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "markdown", + "id": "786627d4-caea-4ddd-9737-25de4f4af89e", + "metadata": {}, + "source": [ + "View the model configureation file, `config.yaml`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f43330d-c051-4108-9f5d-1f3ef0bc6779", + "metadata": {}, + "outputs": [], + "source": [ + "!cat config.yaml" + ] + }, + { + "cell_type": "markdown", + "id": "6f1624e4-cd52-479c-999d-085648d508ae", + "metadata": {}, + "source": [ + "Import the Python libraries we'll use below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e5c2dff-fd7b-40ce-a634-811bf0262af2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "0f7758b5-86fe-4b97-bf38-49f023fca226", + "metadata": {}, + "source": [ + "## Initialize the model" + ] + }, + { + "cell_type": "markdown", + "id": "16224941-a041-4ea0-ad4a-8a67ef5460c6", + "metadata": {}, + "source": [ + "Import the `DiffusionModel` BMI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c4b14ac-2233-44da-8a1d-e16affd1a4ff", + "metadata": {}, + "outputs": [], + "source": [ + "from diffusion import BmiDiffusionModel" + ] + }, + { + "cell_type": "markdown", + "id": "f14043e4-e43f-45ed-8fcc-31d8e8fac78a", + "metadata": {}, + "source": [ + "Make an instance of the model through its BMI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c6031e6-987b-4ed3-b294-ee710d224e0a", + "metadata": {}, + "outputs": [], + "source": [ + "m = BmiDiffusionModel()" + ] + }, + { + "cell_type": "markdown", + "id": "c2f6ecff-6129-49cc-84fe-fb5569dc521d", + "metadata": {}, + "source": [ + "Get the name of the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3093b2d2-b77b-40c6-9c49-519ac0fd493b", + "metadata": {}, + "outputs": [], + "source": [ + "m.get_component_name()" + ] + }, + { + "cell_type": "markdown", + "id": "86294c45-cd5f-4dce-a274-8edd217ffe67", + "metadata": {}, + "source": [ + "Initialize the model using parameter values from the configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f95a4abd-2176-4f0d-8753-e6721c8ff14d", + "metadata": {}, + "outputs": [], + "source": [ + "m.initialize(\"config.yaml\")" + ] + }, + { + "cell_type": "markdown", + "id": "074355d1-5863-44d2-b3de-c3451bc420c5", + "metadata": {}, + "source": [ + "## Get model information" + ] + }, + { + "cell_type": "markdown", + "id": "953919e4-868b-437b-a0be-db2b8fc7af07", + "metadata": {}, + "source": [ + "List the model's input and output variables (also called \"exchange items\")." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6084fcaa-d8d7-41bd-a6dd-acbc6a7c92a7", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Number of input variables:\", m.get_input_item_count())\n", + "for var in m.get_input_var_names():\n", + " print(f\" - {var}\")\n", + "\n", + "print(\"Number of output variables:\", m.get_output_item_count())\n", + "for var in m.get_output_var_names():\n", + " print(f\" - {var}\")" + ] + }, + { + "cell_type": "markdown", + "id": "490b059b-089b-4feb-a36a-e6ff04d1b15a", + "metadata": {}, + "source": [ + "The BMI exposes one output variable, `model_grid__histogram_of_agents`, that maps to the `histogram` variable in the AgentPy `DiffusionModel` model.\n", + "The long variable name is an example of a [CSDMS Standard Name](https://csdms.colorado.edu/wiki/CSDMS_Standard_Names).\n", + "\n", + "Get more information on the `model_grid__histogram_of_agents` variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91426748-15b6-47b0-9c81-99b388246bb8", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = m.get_output_var_names()[0]\n", + "print(f\"Variable {var_name}\")\n", + "print(\" - type:\", m.get_var_type(var_name))\n", + "print(\" - units:\", m.get_var_units(var_name))\n", + "print(\" - itemsize:\", m.get_var_itemsize(var_name))\n", + "print(\" - nbytes:\", m.get_var_nbytes(var_name))\n", + "print(\" - location:\", m.get_var_location(var_name))" + ] + }, + { + "cell_type": "markdown", + "id": "816a9999-c153-41af-b812-f7d07fd27deb", + "metadata": {}, + "source": [ + "Note that a unit of `1` is shorthand for a dimensionless variable in the [UDUNITS](https://www.unidata.ucar.edu/software/udunits/) library used by CSDMS Standard Names." + ] + }, + { + "cell_type": "markdown", + "id": "85fc8038-ac68-43fa-ba61-c923c5b70f06", + "metadata": {}, + "source": [ + "In a BMI, all variables are defined on grids.\n", + "\n", + "Get information about the grid used by the `model_grid__histogram_of_agents` variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d28d22f-def8-4144-8f67-7874dcf1e125", + "metadata": {}, + "outputs": [], + "source": [ + "grid_id = m.get_var_grid(var_name)\n", + "print(\" - grid id:\", grid_id)\n", + "print(\" - grid type:\", m.get_grid_type(grid_id))\n", + "grid_rank = m.get_grid_rank(grid_id)\n", + "print(\" - rank:\", grid_rank)\n", + "grid_size = m.get_grid_size(grid_id)\n", + "print(\" - size:\", grid_size)\n", + "grid_shape = np.empty(grid_rank, dtype=np.int32)\n", + "m.get_grid_shape(grid_id, grid_shape)\n", + "print(\" - shape:\", grid_shape)\n", + "grid_spacing = np.empty(grid_rank, dtype=np.float64)\n", + "m.get_grid_spacing(grid_id, grid_spacing)\n", + "print(\" - spacing:\", grid_spacing)\n", + "grid_origin = np.empty(grid_rank, dtype=np.float64)\n", + "m.get_grid_origin(grid_id, grid_origin)\n", + "print(\" - origin:\", grid_origin)" + ] + }, + { + "cell_type": "markdown", + "id": "5ecd6437-c779-4bbf-852d-ed1d9eae4e11", + "metadata": {}, + "source": [ + "Get time information from the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37fc7faf-cfc9-40be-b0c3-ec3d0a68c33d", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Start time:\", m.get_start_time())\n", + "print(\"End time:\", m.get_end_time())\n", + "print(\"Current time:\", m.get_current_time())\n", + "print(\"Time step:\", m.get_time_step())\n", + "print(\"Time units:\", m.get_time_units())" + ] + }, + { + "cell_type": "markdown", + "id": "b24f52a1-03a9-46f7-a131-37a144e9111e", + "metadata": {}, + "source": [ + "Note that while the model has been initialized, it is still at time step zero." + ] + }, + { + "cell_type": "markdown", + "id": "6fa1b57f-5ebf-480c-88fd-2aacd6035cf8", + "metadata": {}, + "source": [ + "## View the initial model state" + ] + }, + { + "cell_type": "markdown", + "id": "1219c7a4-f55e-4b71-842d-842799895e65", + "metadata": {}, + "source": [ + "## Run the model" + ] + }, + { + "cell_type": "markdown", + "id": "81a50ba7-85c3-430d-a6d7-9b1eb928331b", + "metadata": {}, + "source": [ + "## View the results" + ] + }, + { + "cell_type": "markdown", + "id": "7a72b990-b65f-4359-acec-d08545c2e23c", + "metadata": {}, + "source": [ + "## Finalize the model" + ] + }, + { + "cell_type": "markdown", + "id": "ae65a15a-c24d-4623-9527-31955cb1f117", + "metadata": {}, + "source": [ + "Shut down the model when we're finished." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b1578e3-c020-4fef-b691-f79ab5450fb5", + "metadata": {}, + "outputs": [], + "source": [ + "m.finalize()" + ] + } + ], + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 700c97f0e3f90fb38bdd543875e33808c6502b68 Mon Sep 17 00:00:00 2001 From: Mark Piper Date: Thu, 17 Apr 2025 14:18:38 -0600 Subject: [PATCH 2/5] Add BMI notebook to list of examples --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index e680448..253bd14 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,3 +19,4 @@ standalone and through a Basic Model Interface. * [explore-model.ipynb](./explore-model.ipynb): Explores the parameters and initial conditions of a `DiffusionModel` instance. * [run-model.ipynb](./run-model.ipynb): Shows how to run `DiffusionModel` standalone and make a plot of the results. +* [run-bmi-model.ipynb](./run-bmi-model.ipynb): Explores and runs `DiffusionModel` through its BMI. From d651f94f9805cd29eb5eda6ef80ec1225297494e Mon Sep 17 00:00:00 2001 From: Mark Piper Date: Thu, 17 Apr 2025 15:57:12 -0600 Subject: [PATCH 3/5] Add an intro paragraph --- examples/run-bmi-model.ipynb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/run-bmi-model.ipynb b/examples/run-bmi-model.ipynb index 643d6ab..db0b5a5 100644 --- a/examples/run-bmi-model.ipynb +++ b/examples/run-bmi-model.ipynb @@ -8,6 +8,15 @@ "# Run `DiffusionModel` through its BMI" ] }, + { + "cell_type": "markdown", + "id": "cfd925a2-48a3-4370-990b-2fc2c2e3b13a", + "metadata": {}, + "source": [ + "`DiffusionModel` models diffusion in two dimensions with an agent-based approach, randomly moving particles zero to one grid cell in each time step.\n", + "Wrapping `DiffusionModel` with a [Basic Model Interface](https://bmi.csdms.io/) (BMI) lets you you control the model through a standard set of functions so you don't have to know the details of how the model is run." + ] + }, { "cell_type": "markdown", "id": "1d63b202-5550-4b78-a75f-645024dec654", @@ -331,7 +340,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.6" + "version": "3.12.7" } }, "nbformat": 4, From c41792f13ae3ee2f5bdf233e8616c64ad9cae1e4 Mon Sep 17 00:00:00 2001 From: Mark Piper Date: Thu, 17 Apr 2025 16:33:09 -0600 Subject: [PATCH 4/5] Add a section to view the initial model state --- examples/run-bmi-model.ipynb | 74 +++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/examples/run-bmi-model.ipynb b/examples/run-bmi-model.ipynb index db0b5a5..61895ac 100644 --- a/examples/run-bmi-model.ipynb +++ b/examples/run-bmi-model.ipynb @@ -58,7 +58,8 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np" + "import numpy as np\n", + "import matplotlib.pyplot as plt" ] }, { @@ -281,6 +282,77 @@ "## View the initial model state" ] }, + { + "cell_type": "markdown", + "id": "9c75dca9-9a4e-42a3-b4c9-8af3166d4d74", + "metadata": {}, + "source": [ + "Get the initial distribution of particles on the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "355551c3-895e-4182-8ebd-63293faa4ace", + "metadata": {}, + "outputs": [], + "source": [ + "val = np.empty(grid_size, dtype=m.get_var_type(var_name))\n", + "m.get_value(var_name, val)\n", + "print(f\"Particle distribution at time {m.get_current_time()}:\")\n", + "val" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "b109213b-7ccc-4045-a8da-ac0a4e4cc814", + "metadata": {}, + "source": [ + "Note that the particle distribution is returned as a one-dimensional NumPy array.\n", + "\n", + "As a metric, report the total number of particles on the plate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ed9a52b8-2dbb-4563-ab73-27dddd8d7dd7", + "metadata": {}, + "outputs": [], + "source": [ + "val.sum()" + ] + }, + { + "cell_type": "markdown", + "id": "e49cc52b-c52a-46f1-aa0e-9c354dd5eb75", + "metadata": {}, + "source": [ + "Visualize the particle distribution using *matplotlib* and a helper function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f592e545-f179-4255-99f8-5258b3174375", + "metadata": {}, + "outputs": [], + "source": [ + "from diffusion.vis import histogram_colorbar_plot\n", + "\n", + "fig, ax = plt.subplots()\n", + "histogram_colorbar_plot(m._model, ax, fig)" + ] + }, + { + "cell_type": "markdown", + "id": "507b3309-de8e-48b2-87a2-dd75ca597a72", + "metadata": {}, + "source": [ + "Note: to use *histogram_colorbar_plot* we access the model reference through a helper attribute defined in the `DiffusionModel` BMI." + ] + }, { "cell_type": "markdown", "id": "1219c7a4-f55e-4b71-842d-842799895e65", From 98542ff9cd53dce68e20cb4939b2b9387ae89d5e Mon Sep 17 00:00:00 2001 From: Mark Piper Date: Thu, 17 Apr 2025 17:01:19 -0600 Subject: [PATCH 5/5] Run model and visualize results --- examples/run-bmi-model.ipynb | 127 +++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/examples/run-bmi-model.ipynb b/examples/run-bmi-model.ipynb index 61895ac..39b4532 100644 --- a/examples/run-bmi-model.ipynb +++ b/examples/run-bmi-model.ipynb @@ -361,6 +361,85 @@ "## Run the model" ] }, + { + "cell_type": "markdown", + "id": "34d162a3-b9e1-40cb-b5d2-f8fe5528e1fb", + "metadata": {}, + "source": [ + "The model is currently at time zero.\n", + "Advance the model one time step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1eb58be6-fc89-4e9b-b261-f6dc95e4b240", + "metadata": {}, + "outputs": [], + "source": [ + "m.update()\n", + "print(f\"Time: {m.get_current_time()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "00d4d54f-1613-4192-ae6c-74e7b34f39f9", + "metadata": {}, + "source": [ + "Have the particle locations changed?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38d09f45-6c93-4d92-a41b-fe6dd941cd50", + "metadata": {}, + "outputs": [], + "source": [ + "m.get_value(var_name, val)\n", + "print(f\"Particle distribution at time {m.get_current_time()}:\", val)\n", + "print(f\"Sum: {val.sum()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2d2542c5-0603-4966-8990-464fe2d74d4a", + "metadata": {}, + "source": [ + "The particle distribution might be easier to understand if we redimensionalize the array." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5039f9e5-a42e-4efb-9988-1cb32bfe5abf", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Particle distribution at time {m.get_current_time()}\")\n", + "print(val.reshape((grid_shape)))" + ] + }, + { + "cell_type": "markdown", + "id": "6fc4974e-093b-42cf-8ea0-039a23567183", + "metadata": {}, + "source": [ + "Run the model to its end time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71d9e025-a74b-441c-b57c-288ab2554471", + "metadata": {}, + "outputs": [], + "source": [ + "while m.get_current_time() < m.get_end_time():\n", + " m.update()\n", + "print(f\"Time: {m.get_current_time()}\")" + ] + }, { "cell_type": "markdown", "id": "81a50ba7-85c3-430d-a6d7-9b1eb928331b", @@ -369,6 +448,54 @@ "## View the results" ] }, + { + "cell_type": "markdown", + "id": "4073b148-3bfe-47ee-a1c9-be9a8240b342", + "metadata": {}, + "source": [ + "How has the particle distribution evolved over the run time of the model?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cae80918-5418-43e8-9d9b-81f3f72657ff", + "metadata": {}, + "outputs": [], + "source": [ + "m.get_value(var_name, val)\n", + "print(f\"Temperature at time {m.get_current_time()}\")\n", + "print(val.reshape((grid_shape)))\n", + "print(f\"Sum: {val.sum()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "88c81dca-2ef9-46e4-9084-aea5d395c6cd", + "metadata": {}, + "source": [ + "Visualize the final particle distribution." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b21d3df3-5979-43f8-a24c-8b176784229d", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots()\n", + "histogram_colorbar_plot(m._model, ax, fig)" + ] + }, + { + "cell_type": "markdown", + "id": "9ff1f0ee-3a8c-4593-8901-be875ec025f6", + "metadata": {}, + "source": [ + "Diffusion!" + ] + }, { "cell_type": "markdown", "id": "7a72b990-b65f-4359-acec-d08545c2e23c",