{ "cells": [ { "cell_type": "markdown", "id": "a878930e-f03f-4025-ab15-64292b3056f2", "metadata": { "tags": [] }, "source": [ "
\n", "\n", "

ICON Training - Hands-on Session

\n", "\n", "# Exercise 5: Programming ICON\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "8aedb82d-b4ce-43ee-b5d9-3ebd3022e873", "metadata": {}, "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", " In this step-by-step exercise we will learn how to implement new variables in the ICON source code.

\n", " In the first and relatively easy part of the exercise we will modify the Fortran code directly:\n", "
    \n", "
  • Step F1: We learn how to allocate new ICON variables. As a by-product we will visualize ICON's domain decomposition.
  • \n", "
  • Optional Exercise: Looping over grid points in Fortran, distinguishing between prognostic cells and halo cells.
  • \n", "
\n", "
\n", " \n", "
\n" ] }, { "cell_type": "markdown", "id": "60e6ac8d-b1b0-4c41-a977-b4ff66c5569a", "metadata": {}, "source": [ "## Step F1: Allocating Additional Fields in the Fortran Code" ] }, { "cell_type": "markdown", "id": "263e64e5-9472-404d-a7d0-1485c3ab5649", "metadata": {}, "source": [ "In the following we will allocate a new ICON variable **directly in the Fortran code**. As a by-product we will visualize ICON's domain decomposition.\n", "\n", "Details are given in Section 9.4 of the ICON Tutorial (see the link at the bottom of the page)." ] }, { "cell_type": "markdown", "id": "c5bf386a-6365-4cf4-9c0f-757e89907025", "metadata": {}, "source": [ "
\n", " Exercise: \n", " Create a fresh copy of the ICON source code at /scratch/${USER::1}/$USER/icon. To this end, follow these steps closely:\n", "
" ] }, { "cell_type": "code", "execution_count": null, "id": "997da3f6-5fbd-450f-be23-9e3dbfe83a1c", "metadata": {}, "outputs": [], "source": [ " export SCRATCHDIR=/scratch/${USER::1}/$USER\n", " export ICONDIR=${SCRATCHDIR}/icon" ] }, { "cell_type": "markdown", "id": "bc52bbe4-4d6e-45ef-8d0d-673dbd1bac2c", "metadata": {}, "source": [ "Download the release [ICON-2025.04-1](https://gitlab.dkrz.de/icon/icon-model/-/releases) as follows:" ] }, { "cell_type": "code", "execution_count": null, "id": "c2741000-7db8-400e-8182-acd57c667da5", "metadata": {}, "outputs": [], "source": [ "( cd ${SCRATCHDIR}\n", " git clone --recursive --branch release-2025.04-public https://gitlab.dkrz.de/icon/icon-model.git icon )" ] }, { "cell_type": "markdown", "id": "68842a73-1e56-43a7-8f27-bc6fefbc10d5", "metadata": {}, "source": [ "
\n", "A brief digression: \n", "Before we proceed, we will explain some additional steps required to build ICON with support for the YAC coupler and the ICON Community Interface ComIn. The following notebook cell is not strictly necessary but more for informational purposes, as we will return to using our pre-built ICON binary in later exercises where we make use of these libraries.\n", "

\n", "For the YAC library we need to install cython, a tool that allows us to create interfaces between C or C++ and Python. We also modify the wrapper script with an additional linker flag:\n", "Adding -Wl,--export-dynamic-symbol=yac_* to both the host model and plugin linking flags ensures YAC is only loaded once, preventing duplicate copies of its internal lookup table.\n", "

" ] }, { "cell_type": "code", "execution_count": null, "id": "03af2ea9-229e-4cb1-b54d-e735e1f2b4ba", "metadata": {}, "outputs": [], "source": [ "cd $ICONDIR\n", "pip install cython\n", "\n", "sed -i 's|LDFLAGS=\"${LDFLAGS}\"|LDFLAGS=\"${LDFLAGS} -Wl,--export-dynamic\"|' config/dkrz/levante.gcc-11.2.0" ] }, { "cell_type": "markdown", "id": "2629e494-20e4-42dd-b6e8-0bf1b4f01141", "metadata": {}, "source": [ "We then execute the configure wrapper for the 'Levante' platform with the GCC compiler (`config/dkrz/levante.gcc`) in our local copy of the ICON code.\n", "\n", "These steps are fully documented in the [ICON Quickstart Guide](https://icon.gitlab-pages.dkrz.de/icon/buildrun/buildrun_quickstart.html#ref-buildrun-quickstart).\n", "\n", "We set several options:\n", "\n", "- `--enable-comin` \n", " Enables the use of the ICON Community Interface (ComIn), a plugin mechanism which will be used in later exercises.\n", "- `--enable-bundled-python` \n", " enables the Python interfaces for ComIn.\n", "- `--disable-jsbach` \n", " Disables the [JSBACH land surface model](https://docs.icon-model.org/land/land.html#ref-land-jsbach), which simulates land biosphere processes such as vegetation, soil, carbon, and water cycles.\n", "- `--disable-quincy` \n", " Disables the [QUINCY land surface model](https://docs.icon-model.org/land/land.html#quincy), an alternative model for land surface biogeochemistry and vegetation.\n", "- `--disable-rte-rrtmgp` \n", " Disables the RTE+RRTMGP radiation scheme, an advanced radiative transfer model for calculating radiative fluxes and heating rates in the atmosphere." ] }, { "cell_type": "code", "execution_count": null, "id": "bb93967b-9cc5-4dfb-a899-a2adcd53fca6", "metadata": {}, "outputs": [], "source": [ "mkdir -p $ICONDIR/build\n", "cd $ICONDIR/build\n", "\n", "../config/dkrz/levante.gcc \\\n", " --enable-comin --disable-jsbach --disable-quincy --disable-rte-rrtmgp --enable-bundled-python --disable-silent-rules \\\n", "> configure.log" ] }, { "cell_type": "markdown", "id": "ae396593-5252-41fb-bcf7-40ea24367799", "metadata": {}, "source": [ "Now the ICON code is ready and can be compiled with `make` in the following exercise." ] }, { "cell_type": "markdown", "id": "2066cbf0-2e05-4b64-b770-c0e1207d2964", "metadata": {}, "source": [ "
\n", "Remark (again): Note that we will not be using the Python bindings for this exercise. However, the YAC coupler and the Community Interface plugins rely on them. These will be discussed in later exercises.\n", "
" ] }, { "cell_type": "markdown", "id": "fdf504e1-8fbf-48c8-ae54-9edafe3de7e7", "metadata": {}, "source": [ "### Perform the following changes step-by-step:" ] }, { "cell_type": "markdown", "id": "9c37f54f-5683-457f-83c4-13534d3d2330", "metadata": {}, "source": [ "Just to ensure that our experiment directory is in the correct location:" ] }, { "cell_type": "code", "execution_count": null, "id": "22ed3ad2-98a6-4d13-9023-4895097a9b0c", "metadata": { "tags": [] }, "outputs": [], "source": [ "export SCRATCHDIR=/scratch/${USER::1}/$USER" ] }, { "cell_type": "markdown", "id": "3fdadcbd-d760-46f3-84ca-ad6f42556c65", "metadata": {}, "source": [ "* In the subdirectory src/atm_dyn_iconam open the module mo_nonhydro_types.
\n", " This file contains important structure declarations, i.e. the names and characteristics of the data objects in the atmospheric model:\n", " * Insert a new 2D variable pointer process_id of type REAL(wp) to the derived type TYPE(t_nh_diag).

\n", "* Open the module mo_nonhydro_state in the same subdirectory.
\n", " This file allocates the storage for the above data objects.\n", " * At the end of the subroutine new_nh_state_diag_list\n", " * set appropriate metadata variables for NetCDF.\n", " \n", " (standard_name, units, long_name, datatype; see t_cf_var)\n", "\n", " * set the metadata also for GRIB2.\n", " \n", " (You may use dummy numbers: discipline = parameterCategory = parameterNumber = 255)\n", " * place the add_var call for the new field.\n", "\n", " * Now, after the add_var call, the new field has been allocated. Fill the data array with a constant value:\n", " * process_id(:,:) = get_my_mpi_work_id()\n", " \n", " This auxiliary function from the source code module mo_mpi returns the MPI rank of the processor (see Section 9.2.4 of the [ICON tutorial](icon_exercise_programming.ipynb#Further-Reading-and-Resources))." ] }, { "cell_type": "markdown", "id": "b889b576-d29d-4eb5-a28f-ce8c88f107af", "metadata": {}, "source": [ "

\n", " Exercise: \n", " Modify the ICON source code according to the steps above.\n", "
" ] }, { "cell_type": "markdown", "id": "96b53329-03c4-4684-9b75-05420c409991", "metadata": { "tags": [] }, "source": [ "
\n", "Solution\n", "\n", "* [mo_nonhydro_types.f90](reference/F1_additional_field/mo_nonhydro_types.f90)\n", "* [mo_nonhydro_state.f90](reference/F1_additional_field/mo_nonhydro_state.f90)\n", "\n", "(Search for EX5:ProgrammingICON:AllocatingFields)\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "e8f7ce1f-b87f-42e4-81bb-651044158dc9", "metadata": { "tags": [] }, "source": [ "### Build Process" ] }, { "cell_type": "markdown", "id": "5dae959b-ccc6-4cdf-9753-186258e6d101", "metadata": {}, "source": [ "
\n", " Exercise: \n", " In your local copy of the ICON code, change to the build directory, where we have executed the configure wrapper for the \"Levante\" platform with the gcc compiler (config/dkrz/levante.gcc, see above).

\n", " Execute make with 4 processes.\n", "
\n", " Due to the large amount of log output during the configuration and compiling, we highly recommend performing this exercise in a terminal window.

\n", "

" ] }, { "cell_type": "code", "execution_count": null, "id": "03d8b56c-053e-4fad-a4de-82197d5b1399", "metadata": { "tags": [] }, "outputs": [], "source": [ "export ICONDIR=$SCRATCHDIR/icon\n", "\n", "..." ] }, { "cell_type": "markdown", "id": "918d8d7b-3fdc-4af9-bb73-b2bb3f2dd979", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [ "hide-input" ] }, "source": [ "
\n", "Solution\n", "\n", "```\n", "export SCRATCHDIR=/scratch/${USER::1}/$USER\n", "export ICONDIR=$SCRATCHDIR/icon\n", "cd $ICONDIR/build\n", "\n", "make -j4 2>&1 > compile.log\n", "```\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "b8a907fa-ae2a-4792-9864-fb5143dbb0ae", "metadata": { "tags": [] }, "source": [ "### Run the ICON model & visualize the results" ] }, { "cell_type": "markdown", "id": "14fbc6aa-fb8a-4152-9676-4d7ed68b430b", "metadata": {}, "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
Reference solution for \"Step F1: Allocating Additional Fields\".
\n", "
\n", "
\n" ] }, { "cell_type": "markdown", "id": "bc9f8803-13ed-4efc-a536-408d71e846eb", "metadata": {}, "source": [ "
\n", " Exercise: \n", " Why is the output field not a constant value?\n", "
" ] }, { "cell_type": "markdown", "id": "22da432a-9cce-4b52-8462-953199c8e3c5", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "Solution\n", "\n", "Each PE writes a different (constant) value - its MPI rank.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "233da221-3498-48cd-98c3-3cfbd9f67f33", "metadata": {}, "source": [ "
\n", " Exercise: \n", " Take a closer look at the data: Can you guess why the output field does not only contain integer values?\n", "
" ] }, { "cell_type": "markdown", "id": "3416f4b2-b696-4f1f-86d2-151a22810c54", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "Solution\n", "\n", "This is a lat-lon interpolated field; although the original field has only integer values, we get intermediate values in the lat-lon interpolation.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "6222fd17-177d-466d-b227-cd2fce8c743a", "metadata": {}, "source": [ "### Optional Exercise: Looping over grid points in Fortran" ] }, { "cell_type": "markdown", "id": "6b1c6ffa-30e7-45ca-a88e-14d38b62f5fe", "metadata": {}, "source": [ "
\n", " Optional Exercise: \n", " Assign the variable process_id to the MPI rank of the processor only for prognostic cells. For all other cells, set process_id to -1. To achieve this, you must write loops that iterate over the prognostic cells.\n", "
" ] }, { "cell_type": "markdown", "id": "0b81c02e-78af-464e-b7d4-cc4949850297", "metadata": {}, "source": [ "
\n", "\n", "
\n", "\n", "**Explanation of looping in ICON**\n", "\n", "For more detailed information, refer to Section 9.3 of the ICON tutorial (see the link at the bottom of the page).\n", "\n", "\n", "* In order to enhance computational efficiency, loops that iterate over grid cells, edges, and vertices are structured using nested loops, which are known as `jb` loops and `jc` loops. The jb loop is typically employed as the outer loop and is often parallelized using OpenMP.\n", "* This approach involves breaking down a long vector into smaller chunks of length `nproma`. The long vector is stored in a two-dimensional array, where the first index represents the number of elements within a block (`jc`), and the second index represents the number of blocks (`jb`). \n", "* Upon examination of the figure above (Figure 9.2 in the ICON tutorial), it becomes apparent that by selecting the `start_idx` (start index for looping over the blocks) and `end_idx` (end index for looping over the blocks), one can determine which cell types (boundary regions, prognostic cells, or halo cells) to include in the loop. \n", "* In the event that `nproma` does not divide the number of cells evenly, the final block may not be completely filled. It therefore is necessary that for each block (`jb`), the start and end indices of the elements within the block are calculated. The auxiliary function `get_indices_c` in ICON assists in computing these indices. \n" ] }, { "cell_type": "markdown", "id": "1ef18c08-69a1-4f44-89b9-a49c68c56e62", "metadata": {}, "source": [ "**Hints:**\n", "* Open the module `mo_nh_stepping` (subdirectory `src/atm_dyn_iconam`). \n", " * Create an (empty) subroutine.\n", " * Place a call to this subroutine immediately before the call to the output routine in the timeloop.\n", " * Fill your subroutine with a 2D loop over all prognostic grid points, see p. 205 of the ICON tutorial (see the link at the bottom of the page) for help." ] }, { "cell_type": "markdown", "id": "e743586f-6ead-4c4a-9214-5479115d43a9", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "
\n", "Solution\n", "\n", "* [mo_nonhydro_types.f90](reference/F1_optional_exercise/mo_nonhydro_types.f90)\n", "* [mo_nonhydro_state.f90](reference/F1_optional_exercise/mo_nonhydro_state.f90)\n", "* [mo_nh_stepping.f90](reference/F1_optional_exercise/mo_nh_stepping.f90)\n", "\n", "(Search for EX5:ProgrammingICON:LoopOverGridPoints)\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "3099312b-470c-4f48-93e8-f00a235f59dc", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "id": "55ab88a8-1a48-4b83-a995-b46066877388", "metadata": { "tags": [] }, "source": [ "

Congratulations! You have successfully completed Exercise 5.

" ] }, { "cell_type": "markdown", "id": "1f5d5129", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "id": "8d3dbe16", "metadata": {}, "source": [ "## Further Reading and Resources\n", "\n", "- ICON Tutorial, Ch. 9: https://www.dwd.de/DE/leistungen/nwv_icon_tutorial/nwv_icon_tutorial.html\n", "
A new draft version of the ICON Tutorial is available here: https://icon-training-2025-scripts-rendering-cc74a6.gitlab-pages.dkrz.de/index.html. It is currently being finalized and will be published soon." ] }, { "cell_type": "markdown", "id": "7923d75d", "metadata": {}, "source": [ "---\n", "\n", "*Author info: Deutscher Wetterdienst (DWD) 2025 :: icon@dwd.de. For a full list of contributors, see CONTRIBUTING in the root directory. License info: see LICENSE file.*" ] } ], "metadata": { "kernelspec": { "display_name": "Bash", "language": "bash", "name": "bash" }, "language_info": { "codemirror_mode": "shell", "file_extension": ".sh", "mimetype": "text/x-sh", "name": "bash" } }, "nbformat": 4, "nbformat_minor": 5 }