ComIn 0.5.1
ICON Community Interface
Loading...
Searching...
No Matches
Getting Started

Using bundled ComIn plugins

Repository checkout

In order to use the plugins with ICON, the first step involves building the ICON and plugins. The instruction of building and using the plugins is explained here for the Levante_gcc and the DWD_nec platforms:

Clone the ICON repository, which is publicly available under the BSD-3-C license:

module load git
git clone git@gitlab.dkrz.de:icon/icon-model.git
cd icon
git submodule update --init

Example plugins

After cloning ICON, the example plugins can be found in externals/comin/plugins. In this folder there are different examples written in Fortran, C and Python:

Plugin Language Main Functionality
simple_fortran_plugin.F90 Fortran Minimal ComIn plug‑in written in Fortran.
simple_c_plugin.c C Minimal ComIn plug‑in written in C.
simple_python_plugin.py Python Minimal ComIn plug‑in written in Python using the ComIn Python adapter.
calc_water_column_plugin.F90 Fortran Diagnosics for computing the water content in a column.
point_source.py Python * Requests a tracer that is coupled to ICON’s turbulence and convection schemes.

Adding the namelist comin_nml to the ICON's namelist file

To enable the plugins, one or more entries are added to ICON's namelist comin_nml (which is part of the namelist file "atmo_namelist"). Specify the plugin information in the order you want to use them. The different items of plugin_list are

  • name: the name of the plugin.
  • plugin_library: the shared library file associated with the plugin.
  • primary_constructor (optional): name of the primary constructor. It must be specified if it is not comin_main.
  • options (optional): offers the possibility to pass a character string (e.g. a python script filename) to the plugin.
  • comm (optional): denotes the name of the MPI communicator that is created for this particular plugin. This is useful when exchanging data with other running processes. The parameter comm can be left as an empty string if the application does not require a communicator for this plugin.

    Note: The first two components are mandatory to set.

    &comin_nml
    plugin_list(1)%name = "simple_fortran_plugin"
    plugin_list(1)%plugin_library = "libsimple_fortran_plugin.so"

Modifying the ICON run script

It is recommended to adjust your run script template before configuring ICON. This modified version of your template will be copied to the build/run subdirectory then.

Modifying your experiment's template involves two parts. First, the &comin_nml namelist needs to be added to the namelist "atmo_namelist" (see above).

In addition, you need to set the path of your plugin's shared library in the LD_LIBRARY_PATH. To accomplish this, there are two options available.

  • You can add it directly in to your run script:
    export LD_LIBRARY_PATH="${path_to_plugin}:$LD_LIBRARY_PATH"
  • Alternatively you can use the auxiliary function add_comin_setup in the run script which does the same automatically. To use this function your basedir variable must be set to the build directory of your ICON installation.
    add_comin_setup "/externals/comin/build/plugins/simple_fortran"

Run the experiment on Levante

The following commands copy the modified version of your template to the build/run subdirectory and launch the batch job

cd build
./make_runscripts --all
cd run
sbatch name_of_your_runscript.run

An alternative option is to run your experiment on interactive nodes. Allocate resources for a suitable (cheap!) cluster queue on Levante and wait for the interactive job to start:

salloc -p interactive -A <account> --time=04:00:00 --tasks-per-node=128 --nodes=1 --exclusive

Then run the test interactively (remember to make your $BASEDIR known to the new shell: export BASEDIR= ...):

cd $BASEDIR/icon/build/run
./name_of_your_runscript.run

Writing ComIn plugins: Building blocks

The initial stage involves choosing the preferred programming language, which can be either Fortran 2003, C/C++ or Python. As an illustration, provided here is a guide on creating a plugin using Fortran.

Each plugin must have three parts:

  • Primary constructor
  • Secondary constructor
  • Callback function(s)

Primary constructor

The plugin allows users to write subroutines that can be called at predefined events (entry points) throughout the model simulation. The primary constructor registers the plugin, and it especially registers additional variables and callback functions. Basically, the primary constructor contains the following steps:

Compatibility checks

It is the plugin's responsibility to check that the host model's ComIn version is compatible with the one the plugin needs. ComIn follows semantic versioning practices, so this check is simple to implement: major versions have to agree and the minor of the host has to exceed the plugin's minimum. As many components of the development are still in the testing phase, the initial public release is set to version number 0.1.0. At major version 0, semantic versioning allows for breaking changes between minor releases. Hence, the minor version has to be equal between the plugin and the host model.

The host model's version is obtained by a call to comin_setup_get_version. In Fortran, this function returns a derived type with the major, minor, and patch versions. In C, the function takes three unsigned* arguments, which get filled with the version components.

The plugin has access to the preprocessor macros COMIN_VERSION_MAJOR, COMIN_VERSION_MINOR, and COMIN_VERSION_PATCH. Those contain the ComIn version used to build the plugin itself. Thus, a simple compatibility check could be implemented as

#include "comin_version.inc"
!...
is_compatible = ( &
& version%version_no_major > 0 .AND. &
& version%version_no_major == comin_version_major .AND. &
& version%version_no_minor >= comin_version_minor) .OR. ( &
& version%version_no_major == 0 .AND. &
& version%version_no_minor == comin_version_minor)
void comin_setup_get_version(unsigned int *major, unsigned int *minor, unsigned int *patch)

This check might be overly restrictive because the plugin could work with an older minor version than is used at time of compilation. If the plugin detects an incompatibility, it should call the finish subroutine to abort the simulation.

IF (.NOT. is_compatible) THEN
CALL comin_plugin_finish("comin_main (simple_fortran_plugin)", "incompatible version!")
END IF
void comin_plugin_finish(const char *routine, const char *text)

Registering additional variables

Plugins are allowed to register additional model variables for ICON. A list of to-be-created variables made known to the ICON via the function comin_var_request_add.

CALL comin_var_request_add(var_descriptor, lmodexclusive)
void comin_var_request_add(t_comin_var_descriptor var_desc, bool lmodexclusive)
  • The var_descriptor is required to describe (and uniquely identify) a model variable in ICON.
    var_descriptor=t_comin_var_descriptor( id = domain_id, name = "variable_name")
  • Flag lmodexclusive: Whenever a plugin calls comin_var_request_add, there is a check to determine if the requested variable is already registered. In this case, the existing variable will be used instead of creating a new one. However, if the variable exists and is either exclusively requested in the current call or was exclusively requested before, the model aborts based on the lmodexclusive setting.
  • Variables may also be appended to ICON's container of tracer variables through the tracer flag (part of the metadata). Apart from that aspect it is not possible to create additional variable containers via the adapter library. Note that it cannot be assumed (if only because of the "sharing" of variables between multiple ComIn plugins) that the tracers generated by a module are stored consecutively.
    CALL comin_metadata_set(var_descriptor, "tracer", .true.)
    While it is possible to create variables only for certain domains, ICON has the restriction that tracer variables have to be present on every domain. For this reason, it is necessary to choose domain id -1 (meaning all domains) as part of the var_descriptor for variables with tracer = .true.
  • Newly created fields can be added to ICON's set of restart variables.
    CALL comin_metadata_set(var_descriptor, "restart", .true.)

Registering callbacks

The primary constructor appends subroutines of the 3rd party module to the callback register via the adapter library subroutine comin_callback_register.

CALL comin_callback_register(entry_point_id, fct_ptr)
void comin_callback_register(t_comin_entry_point entry_point, t_comin_callback_function fct_ptr)
  • entry_point_id: entry points denote events during the ICON model simulation, which can trigger a subroutine call of the plugin. Entry points are denoted by named integer constants. The table of available entry points is available in the technical documentation.
  • fct_ptr: this is the callback function. Callback functions do not have additional arguments or return values. The callback function has to be interoperable with the C processor (for Fortran, this requires the BIND(C) attribute; see the technical documentation).

Getting descriptive data structures

The descriptive data structures contain information on the ICON setup (e.g. Fortran KIND values), the computational grid(s), and the simulation status. All descriptive data structures are treated as read-only (seen from the perspective of the 3rd party plugins). However, this read-only nature is (currently) not enforced. For efficiency reasons, the adapter library directly uses pointers to ICON data structures where possible. This holds mostly for components of p_patch, while non p_patch descriptive data are copied from the host model.

  • Global data is available for the plugins primary constructor and all subsequent subroutine callbacks. Global data is never changed or updated and invariant w.r.t. the computational grid (logical domain ID). The detailed table of the components of global data can be found in the technical documentation.
    TYPE(t_comin_descrdata_global), POINTER :: p_global
    p_global => comin_descrdata_get_global()
  • Grid information is available for the 3rd party module's primary constructor and all subsequent subroutine callbacks. Grid information is never changed or updated. The data structures in this section are replicated for each computational domain (logical domain ID).
    TYPE(t_comin_descrdata_domain), POINTER :: p_patch
    p_patch => comin_descrdata_get_domain(jg)
  • Timing information on the simulation.
    TYPE(t_comin_descrdata_simulation_interval) :: p_simulation_interval
    p_simulation_interval = comin_descrdata_get_simulation_interval()
  • Time step length per domain.
    dtime=comin_descrdara_get_timesteplength()

Secondary constructor

A secondary constructor is called after the allocation of ICON variable lists and fields and before the time loop. It needs to be registered by the primary constructor as one of the plugin's callbacks.

  • Access to ICON data fields happens via an accessor function comin_var_get. Basically, comin_var_get(context, var_descriptor, flag, var_handle) returns a variable handle var_handle of type t_comin_var_handle, with which the pointer to the data can be accessed in a second step.
    • context: the name of the entry point.
    • var_descriptor: same as described in primary constructor part.
    • flag: the optional argument flag provides information w.r.t. the data flow. Flags may be combined like flag = IOR(COMIN_FLAG_READ, COMIN_FLAG_WRITE). It is important to highlight that when the comin_var_request_add procedure is executed, a variable is not immediately created. This step only involves the registration of a new variable. To use this variable later, it must be queried, similar to the other variables, using the comin_var_get function with flag=COMIN_FLAG_WRITE.
    • comin_var_get registers the access to a variable and returns a variable handle.

Code example:

TYPE(t_comin_var_handle) :: var1
CALL comin_var_get(context, var_descriptor, flag, var1_handle)

The 5D pointer is than accessed via thr type-bound procedure get_ptr.

Code example:

REAL(wp), DIMENSION(:,:,:,:,:) :: var1_ptr => null()
CALL var1_handle%get_ptr(var1_ptr)

Note, it is important here to know in advance, of which data type the delivered pointer will be. The integer metadata datatype provides the data type. COMIN_VAR_DATATYPE_DOUBLE, COMIN_VAR_DATATYPE_FLOAT, and COMIN_VAR_DATATYPE_INT are the currently available data types.

There exists a convenience procedure to_3d for accessing 2D/3D fields: In practice, access to fields can be simplified, under the condition that the sequence of dimensions is (jc,jk,jb). This exact dimension sequence is (currently) fulfilled by the ICON model. In this case, a 3D pointer variable REAL(wp) :: slice(:,:,:) can be generated directly from the handle using the procedure.

CALL var1_handle%to_3d(slice)

ComIn plugins written in the Python programming language

Python plugins can be attached to ComIn via the Python adapter which is located in the plugins directory in the ComIn source code. It is compiled with ComIn if COMIN_ENABLE_PYTHON_ADAPTER is enabled in the CMake configuration, see the build instructions above. The Python adapter embeds a Python interpreter, which also has the comin Python module available. This module contains all the functions, variables, constants and data structures of the Python language API. When including the Python adapter in the namelist, the Python plugin script must be specified as the options, which can be modified while the actual ComIn plugin Python adapter (libpython_adapter.so) remains unchanged. This script is executed in the primary constructor of the Python adapter. Further callbacks can then be registered by decorating functions with entrypoints.

plugin_list(2)%name = "simple_python_plugin"
plugin_list(2)%plugin_library = "libpython_adapter.so"
plugin_list(2)%options = "${basedir}/externals/comin/plugins/python_adapter/examples/simple_python_plugin.py"