wiki:AdaptParallelization

Version 35 (modified by lnerger, 12 days ago) ( diff )

--

Adapting a model's parallelization for PDAF

Overview

The PDAF release provides example code for the online mode in tutorial/online_2D_serialmodel and tutorial/online_2D_parallelmodel. We refer to this code to use it as a basis.

In the tutorial code and the templates in templates/online, the parallelization is initialized in the routine init_parallel_pdaf (file init_parallel_pdaf.F90). The required variables are defined in mod_parallel.F90. These files can be used as templates.

Like many numerical models, PDAF uses the MPI standard for the parallelization. For the case of a parallelized model, we assume in the description below that the model is parallelized using MPI. If the model is parallelized using OpenMP, one can follow the explanations for a non-parallel model below. As explained on the page on the Implementation concept of the online mode, PDAF supports a 2-level parallelization. First, the numerical model can be parallelized and can be executed using several processors. Second, several model tasks can be computed in parallel, i.e. a parallel ensemble integration can be performed. We need to configure the parallelization so that more than one model task can be computed.

There are two possible cases regarding the parallelization for enabling the 2-level parallelization:

  1. The model itself is parallelized using MPI
    • In this case we need to adapt the parallelization of the model
  2. The model is not parallelized or uses only shared-memory parallelization using OpenMP
    • In this case we need to add parallelization

The adaptions in short

If you are experienced with MPI, the steps are the following:

  1. Find the call to MPI_init
  2. Check whether the model uses MPI_COMM_WORLD.
    • If yes, then replace MPI_COMM_WORLD in all places except MPI_abort and MPI_finalize by a user-defined communicator, e.g COMM_mymodel, which can be initialized as COMM_mymodel=MPI_COMM_WORLD.
    • If no, then take note of the name of the communicator variable (we assume it's COMM_mymodel)
  3. Insert the call init_parallel_pdaf directly after MPI_init.
  4. Adapt init_parallel_pdaf so that at the end of this routine you set COMM_mymodel=COMM_model. Potentially, also replace the rank and size variables, respectively, by mype_model and npes_model.
  5. The number of model tasks in variable n_modeltasks is required by init_parallel_pdaf to perform commucator splitting. In the tutorial code we added a command-line parsing to set the variable (it is parsing for dim_ens). One could also read the value from a configuration file.

Adapting a parallelized model

If the online mode is implemented with a parallelized model, one has to ensure that the parallelization can be split to perform the parallel ensemble forecast. For this, one has to check the model source code and potentially adapt it.

Any program parallelized with MPI will need to call MPI_Init for the initialziation of MPI. Frequently, the parallelization of a model is initialized in the model by the lines:

      CALL MPI_Init(ierr)
      CALL MPI_Comm_Rank(MPI_COMM_WORLD, rank, ierr)
      CALL MPI_Comm_Size(MPI_COMM_WORLD, size, ierr)

Here, the call to MPI_init is mandatory, while the two other lines are optional, but common.

In the model code one has to find the place where MPI_init is called to check how the parallelization is set up. In particular we have to check if parallelization is ready to be split into model tasks

The call to MPI_init initialized the parallel region of an MPI-parallel program. This call initializes the communicator MPI_COMM_WORLD, which is pre-defined by MPI to contain all processes of the MPI-parallel program. Often models, use only this communicator to control all MPI communication. However, as MPI_COMM_WORLD contains all processes of the program, this approach will not allow for parallel model tasks.

Next, one has to check whether the model

In order to allow parallel model tasks, it is required to replace MPI_COMM_WORLD by an alternative communicator that is split for the model tasks. We will denote this communicator COMM_model. If a model code already uses a communicator distinct from MPI_COMM_WORLD, it should be possible to use that communicator.

Subsequently, one can define COMM_model by

      COMM_model = MPI_COMM_WORLD

In addition, the variable COMM_model has to be declared in a way such that all routines using the communicator can access it. The parallelization variables of the model are frequently held in a Fortran module. In this case, it is easiest to add COMM_model as an integer variable here. (The tutorial declares COMM_model and other parallelization-related variables in mod_parallel.F90)

Having defined the communicator COMM_model, the communicator MPI_COMM_WORLD has to be replaced by COMM_model in all routines that perform MPI communication, except in calls to MPI_init, MPI_finalize, and MPI_abort. The changes described by now must not influence the execution of the model itself. Thus, after these changes, one can run the model to ensure that the model compiles and runs correctly.

Three communicators

MPI uses so-called 'communicators' to define groups of parallel processes. These groups can then conveniently exchange information. In order to provide the 2-level parallelism for PDAF, three communicators are initialized that define the processes that are involved in different tasks of the data assimilation system. The required communicators are initialized in the routine init_parallel_pdaf. There are called

  • COMM_model - defines the groups of processes that are involved in the model integrations (one group for each model task)
  • COMM_filter - defines the group of processes that perform the filter analysis step
  • COMM_couple - defines the groups of processes that are involved when data are transferred between the model and the filter

/pics/communicators_PDAFonline.png
Figure 1: Example of a typical configuration of the communicators using a parallelized model. In this example we have 12 processes over all, which are distributed over 3 model tasks (COMM_model) so that 3 model states can be integrated at the same time. COMM_couple combines each set of 3 communicators of the different model tasks. The filter is executed using COMM_filter which uses the same processes of the first model tasks, i.e. COMM_model 1 (Figure credits: A. Corbin)

Initializing the communicators

Having replaced MPI_COMM_WORLD by COMM_model enables to split the model integration into parallel model tasks. For this, the communicator COMM_model has to be redefined. This is performed by the routine init_parallel_init, which is supplied with the PDAF package. The routine should be added to the model usually directly after the initialization of the parallelization described above. The routine init_parallel_pdaf also defines the communicators COMM_filter and COMM_couple that were described above. The provided routine init_paralllel_init is a template implementation. Thus, it has to be adjusted for the model under consideration. In particular one needs to ensure that the routine can access the variables COMM_model as well as rank and size (See the initialization example above. These variables might have different names in a model). If the model defines these variables in a module, a USE statement can be added to init_parallel_pdaf as is already done for mod_parallel.

The routine init_parallel_pdaf splits the communicator MPI_COMM_WORLD and (re-)defines COMM_model. If multiple parallel model tasks are used, by setting n_modeltasks to a value above 1, COMM_model will actually be a set of communicators with one for each model task. In addition, the variables npes_world and mype_world are defined. If the model uses different names for these quantities, like rank and size, the model-specific variables should be re-initialized at the end of init_parallel_pdaf. The routine defines several more variables that are declared and held in the module mod_parallel. It can be useful to use this module with the model code as some of these variables are required when the initialization routine of PDAF (PDAF_init) is called.

Arguments of init_parallel_pdaf

The routine init_parallel_pdaf has two arguments, which are the following:

SUBROUTINE init_parallel_pdaf(dim_ens, screen)
  • dim_ens: An integer defining the ensemble size. This allows to check the consistency of the ensemble size with the number of processes of the program. If the ensemble size is specified after the call to init_parallel_pdaf (as in the example) it is recommended to set this argument to 0. In this case no consistency check is performed.
  • screen: An integer defining whether information output is written to the screen (i.e. standard output). The following choices are available:
    • 0: quite mode - no information is displayed.
    • 1: Display standard information about the configuration of the processes (recommended)
    • 2: Display detailed information for debugging

Compiling the extended program

This completes the adaptation of the parallelization. The compilation of the model has to be adjusted for the added files holding the routine init_parallel_pdaf and the module mod_parallel. One can test the extension by running the compiled model. It should run as without these changes, because mod_parallel defines by default that a single model task is executed (n_modeltasks=1). If screen is set to 1 in the call to init_parallel_pdaf, the standard output should include lines like

 Initialize communicators for assimilation with PDAF

                  PE configuration:
   world   filter     model        couple     filterPE
   rank     rank   task   rank   task   rank    T/F
  ----------------------------------------------------------
     0       0      1      0      1      0       T
     1       1      1      1      2      0       T
     2       2      1      2      3      0       T
     3       3      1      3      4      0       T

These lines show the configuration of the communicators. This example was executed using 4 processes and n_modeltasks=1. (In this case, the variables npes_filter and npes_model will have a value of 4.)

To test parallel model tasks one has to set the variable n_modeltasks to a value larger than one. Now, the model will execute parallel model tasks. For n_modeltasks=4 and running on a total of 4 processes the output from init_parallel_pdaf will look like the following:

 Initialize communicators for assimilation with PDAF

                  PE configuration:
   world   filter     model        couple     filterPE
   rank     rank   task   rank   task   rank    T/F
  ----------------------------------------------------------
     0       0      1      0      1      0       T
     1              2      0      1      1       F
     2              3      0      1      2       F
     3              4      0      1      3       F

In this example only a single process will compute the filter analysis (filterPE=.true.). There are now 4 model tasks, each using a single process. Thus, both npes_filter and npes_model will be one.

Using multiple model tasks can result in the following effects:

  • The standard screen output of the model can by shown multiple times. This is due to the fact that often the process with rank=0 performs screen output. By splitting the communicator COMM_model, there will be as many processes with rank 0 as there are model tasks.
  • Each model task might write file output. This can lead to the case that several processes try to generate the same file or try to write into the same file. In the extreme case this can result in a program crash. For this reason, it might be useful to restrict the file output to a single model task. This can be implemented using the variable task_id, which is initialized by init_parallel_pdaf and holds the index of the model task ranging from 1 to n_modeltasks. (For the ensemble assimilation, it can be useful to switch off the regular file output of the model completely. As each model tasks holds only a single member of the ensemble, this output might not be useful. In this case, the file output for the state estimate and perhaps all ensemble members should be done in the pre/poststep routine of the assimilation system.)

Non-parallel models

If the numerical model is not parallelized (i.e. serial), there are two possibilities: The data assimilation system can be used without parallelization (serial), or parallel model tasks can be used in which each model task uses a single process. Both variants are described below.

Serial assimilation system

PDAF requires that an MPI-library is present. Usually this is easy to realize since for example OpenMPI is available for many operating systems and can easily be installed from a package.

Even if the model itself does not use parallelization, the call to init_parallel_pdaf described above is still required. The routine will simply initialize the parallelization variables for a single-process case.

Adding parallelization to a serial model

In order to use parallel model tasks with a model that is not parallelized, the procedure is generally as described for the fully parallel case. However, one has to add the general initialization of MPI to the model code (or to init_parallel_pdaf). This is the lines

      CALL MPI_Init(ierr)
      CALL MPI_Comm_Rank(MPI_COMM_WORLD, mype_world, ierr)
      CALL MPI_Comm_Size(MPI_COMM_WORLD, npes_world, ierr)
      COMM_model = MPI_COMM_WORLD

together with the USE statement for mod_parallel should be added. Subsequently, the call to init_parallel_pdaf has to be inserted at the beginning of the model code. At the end of the program one should insert

    CALL  MPI_Barrier(MPI_COMM_WORLD,ierr)
    CALL  MPI_Finalize(ierr)

The module mod_parallel.F90 from the template directory provides subroutines for the initialization and finalization of MPI. Thus, if this module is used, the is no need to explicitly add the call to the MPI functions, but one can simply add

    CALL init_parallel()

at the beginning of the program. This has to be followed by

    CALL init_parallel_pdaf(dim_ens, screen)

to initialize the variables for the parallelization of PDAF. At the end of the program one should then insert

    CALL finalize_parallel()

in the source code.

If the program is executed with these extensions using multiple model tasks, the issues discussed in 'Compiling the extended program' can occur. This one has to take care about which processes will perform output to the screen or to files.

Note: See TracWiki for help on using the wiki.