YAC 3.18.0
Yet Another Coupler
Loading...
Searching...
No Matches
Interface Guide

The Initialisation Phase

There are multiple methods to initialise YAC (more details: Initialising YAC). The basic method, which is collective for all processes in MPI_COMM_WORLD is:

Alternatively, it is possible to initialise YAC with a previously generated MPI communicator (using for example MPI handshake algorithm).

After the initialisation we have the possibility to read in a coupling configuration file.

If not defined in a coupling configuration file, the calendar has to be set. It is required for YAC be able to interpret the start- and end date, and timesteps provided by the field definition.

The start- and end date needs to be defined by at least one process in the YAC instance. This information is synchronized across all processes in the YAC instance by the synchronisation of definitions. It can be defined within a coupling configuration file or set through the interface.

  • C
    const char * start_datetime = "1850-01-01T00:00:00";
    const char * end_datetime = "1850-12-31T00:00:00";
    // Both arguments are optional (can be NULL)
    yac_cdef_datetime ( start_datetime, end_datetime );
    void yac_cdef_datetime(const char *start_datetime, const char *end_datetime)
    Definition yac.c:1055
  • Fortran
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: start_datetime
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: end_datetime
    start_datetime = '1850-01-01T00:00:00'
    end_datetime = '1850-12-31T00:00:00'
    ! Both arguments are optional
    CALL yac_fdef_datetime ( start_datetime = start_datetime, &
    end_datetime = end_datetime )
    Fortran interface for the definition of time parameters.
  • Python
    start_datetime = "1850-01-01T00:00:00"
    end_datetime = "1850-12-31T00:00:00"
    yac_instance.def_datetime ( start_datetime, end_datetime )

A coupled run configuration may consist of multiple executables or programs, e.g. model_a.x and model_b.x. If the processes of a single excutable have to register multiple components, these processes may required their own individual communicator that contain only the processes of their respective executable in order to be able to determine the component associated to each process.

Initialising YAC contains more information on how to handle more complex setups than the one described above.

The Definition Phase

Component Definition

Each process can be part of zero, one, or more components. The components are identified by a unique component name. The definition of the component associated with each process is a collective call for all processes in the communicator passed to the initialisation routine (or MPI_COMM_WORLD if none was provided) and is called once. For each defined component an ID is returned, which is used in subsequent calls to identify the respective component.

Once, all components have been defined, a component communicator can be retrieved for each component identified by its local comp ID. In case of Python a mpi4py communicator can be obtained.

In addition, YAC can provide a communicator, which encompasses all processes of a provided list of components. Not all components in this list have to be locally defined.

The generation of this communicator is collective for all processes that defined at least one component in this list. The list of component names has to be consistent across all involved processes.

  • C
    MPI_Comm oce_atm_comm;
    char const * comp_names[2] = { "ocean", "atmosphere" };
    yac_cget_comps_comm ( comp_names, 2, &oce_atm_comm );
    void yac_cget_comps_comm(const char **comp_names, int num_comps, MPI_Comm *comps_comm)
    Definition yac.c:1209
  • Fortran
    INTEGER :: oce_atm_comm
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: comp_names(2)
    comp_names(1) = 'ocean'
    comp_names(2) = 'atmosphere'
    CALL yac_fget_comps_comm ( comp_names, & ! [IN]
    2, & ! [IN]
    oce_atm_comm ) ! [out]
    Fortran interface for getting back a MPI communicator for communication between components.
  • Python
    oce_atm_comm = yac_instance.get_comps_comm(["ocean", "atmosphere"])

In some use-cases it might be difficult to provide all component names at once for the collective component definition. In this case it possible to "pre-define" components. The component ids for these "pre-defined" components are valid, however the component query routines can only be called after collective component definition is finished.

Grid Definition

YAC requires the user to provide basic information about the grids used in the coupling. This information consists of the geographical locations of all vertices, connectivity information (i.e. which vertices form a particular cell) and information about which line on the sphere the edges follow (great circles or circles of longitude or latitude). Each process has to provide this information only for its local part of the grid.

There are grid definition routines available for various grid types.

For an unstructed grid geographical locations of the vertices and the connectivity has to be provided explicitly. For this grid type it is assumed that all grid edges follow great circles.

  • C
    int grid_id;
    int m,n;
    int const nbr_vertices = 20;
    int const nbr_cells = 5;
    int nbr_vertices_per_cell[nbr_cells]; // nbr_vertices for individual cells
    int const nbr_connections = ...; // sum(nbr_vertices_per_cell)
    int cell_to_vertex[nbr_connections]; // map cell to vertices
    double x_vertices[nbr_vertices]; // longitude in [-4*PI;+4*PI]
    double y_vertices[nbr_vertices]; // latitude in [-PI/2;+PI/2]
    char * grid_name = "ocean_grid";
    // ...
    nbr_vertices,
    nbr_cells,
    x_vertices,
    y_vertices,
    &grid_id );
    int nbr_vertices_per_cell[NBR_CELLS]
    int * cell_to_vertex
    int grid_id
    void yac_cdef_grid_unstruct(const char *grid_name, int nbr_vertices, int nbr_cells, int *num_vertices_per_cell, double *x_vertices, double *y_vertices, int *cell_to_vertex, int *grid_id)
    Definition yac.c:5644
  • Fortran
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: grid_name
    INTEGER, PARAMETER :: nbr_vertices = 20
    INTEGER, PARAMETER :: nbr_cells = 5
    INTEGER :: nbr_connections = ... ! SUM(nbr_vertices_per_cell)
    INTEGER :: grid_id
    INTEGER :: nbr_vertices_per_cell(nbr_cells)
    INTEGER :: cell_to_vertex(nbr_connections)
    DOUBLE PRECISION :: x_vertices(nbr_vertices)
    DOUBLE PRECISION :: y_vertices(nbr_vertices)
    grid_name = 'ocean_grid'
    ! ...
    CALL yac_fdef_grid ( grid_name, & ! [IN]
    nbr_vertices, & ! [IN]
    nbr_cells, & ! [IN]
    nbr_vertices_per_cell, & ! [IN]
    x_vertices, & ! [IN]
    y_vertices, & ! [IN]
    cell_to_vertex, & ! [IN]
    grid_id ) ! [out]
    Fortran interface for the definition of grids.
  • Python
    nbr_vertices_per_cell = [...]
    x_vertices = [...]
    y_vertices = [...]
    cell_to_vertex = [...]
    grid = UnstructuredGrid ( "ocean_grid",
    nbr_vertices_per_cell,
    x_vertices,
    y_vertices,
    cell_to_vertex)

Ordering of Edges

Depending on the grid definition routine being used, the local ordering of cells, vertices, and edges is given implicitly or explicitly by the user.

If not given explicitly (for example by using yac_cdef_grid_unstruct_edge, the ordering of the edges of the grid is defined as follows:

Generate for each edge a tuple containing the local indices of the adjacent vertices (lowest index first). Sort this list by the vertex indices; this gives the the local order of edges.

Point Definition

If data do not represent values of the complete cell, it is possible to define sets of points. Here we specify points at some location (in radian) inside of a cell (location can be YAC_LOCATION_CELL, YAC_LOCATION_CORNER, or YAC_LOCATION_EDGE).

As for the grid definition, there are multiple routines for defining points. For an unstructured grid points at the center of each cell may be defined as follows:

Decomposition Information

The user can provide global ids for all cell, vertices, and/or edges, if they are available. These have to be consistent across all processes. Otherwise, YAC will generate them. However, the generation of global ids may take some time. Therefore, it is recommended to provide them. Additionally, this makes interpretation of weight files (if they are generated) easier.

The core mask allows the user to define cells, vertices, and/or edges that YAC is supposed to ignore, for example because they are halo points that do not contain valid data. These points will not be used as a source or destination in a put/get/exchange operation.

Definition of Masks

While core masks are supposed to allow YAC to differentiate between the compute domain and halo/dummy points. YAC also allows to define additional masks that can set for each field individually (see here). These masks can be used to specify that YAC should for example only consider coast or coean cells.

The is_valid array should have the appropriate size for the number of cells, vertices, or edges of the respective grid.

  • C
    int coast_mask_id;
    int * is_coast_cell;
    // ...
    nbr_cells,
    is_coast_cell,
    &coast_mask_id );
    void yac_cdef_mask(int const grid_id, int const nbr_points, int const located, int const *is_valid, int *mask_id)
    Definition yac.c:1870
  • Fortran
    INTEGER :: coast_mask_id
    INTEGER :: is_coast_cell(nbr_cells)
    ! ...
    CALL yac_fdef_mask ( grid_id, & ! [IN]
    nbr_cells, & ! [IN]
    is_coast_cell, & ! [IN]
    coast_mask_id ) ! [out]
    Fortran interface for the definition of masks.
  • Python
    is_coast_cell = np.array( ... )
    coast_mask = grid.def_mask ( Location.CELL,
    is_coast_cell)

It is also possible to assign a default mask to a set of previously defined points. The is_valid array should again have the approriate size.

A field usually gets assigned a mask when it is defined. However, masks for the source and target fields can also be defined for each couple either using the user interface or the through the configuration file. In these cases the masks are referenced through their names, which can be set when defining them.

  • C
    int coast_mask_id;
    int * is_coast_cell;
    char const * mask_name = "ocean mask";
    // ...
    nbr_cells,
    is_coast_cell,
    mask_name,
    &coast_mask_id );
    void yac_cdef_mask_named(int const grid_id, int const nbr_points, int const located, int const *is_valid, char const *name, int *mask_id)
    Definition yac.c:1847
  • Fortran
    INTEGER :: coast_mask_id
    INTEGER :: is_coast_cell(nbr_cells)
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: mask_name
    ! ...
    mask_name = 'ocean mask'
    CALL yac_fdef_mask_named ( grid_id, & ! [IN]
    nbr_cells, & ! [IN]
    is_coast_cell, & ! [IN]
    mask_name, & ! [IN]
    coast_mask_id ) ! [out]
  • Python
    is_coast_cell = np.array( ... )
    coast_mask = grid.def_mask ( Location.CELL,
    is_coast_cell,
    "ocean mask")

Name-Based ID Lookup

In some coupling setups, the IDs returned by the definition routines for components, grids, point sets, and masks cannot easily be threaded through to every part of the code that requires them. YAC provides name-based getter routines as a solution: given only the name (and, for point sets and masks, the grid ID and location), they return the corresponding ID.

If the requested entity has not been defined yet, a preliminary object is created internally and its ID is returned. A later call to the corresponding definition routine (e.g. Component Definition or Grid Definition) will recognise the name and return the same ID rather than allocating a new one. Every preliminary object must be fully defined before the End of Definition Phase; otherwise YAC will raise an error.

Note
For point sets and masks to be retrievable by name they must be defined with an explicit name. Points can be given a name via the yac_cdef_points_*_named variants and masks via yac_cdef_mask_named.

Definition of Coupling Fields

A field consists of one or more point sets and is identified by it name. YAC only supports 2D-fields on the sphere. However if multiple 2D-fields have the same configuration or the associated the field has more than one level, these can be processed in a single step using the collection size.

In case a field is configured to be coupled, the point sets have to match with the interpolation stack. Most interpolation methods only support a single point set and some interpolations are limited to certain point locations (for example Conservative interpolation only support points with location YAC_LOCATION_CELL).

  • C
    int sst_field_id;
    char const * field_name = "sea_surface_temperature";
    int const num_point_sets = 1;
    int point_ids[num_point_sets] = {cell_point_id};
    char const * timestep = "PT15M";
    yac_cdef_field ( field_name,
    point_ids,
    num_point_sets,
    timestep,
    &sst_field_id );
    int collection_size
    int const YAC_TIME_UNIT_ISO_FORMAT
    Definition yac.c:69
    void yac_cdef_field(char const *name, int const comp_id, int const *point_ids, int const num_pointsets, int collection_size, const char *timestep, int time_unit, int *field_id)
    Definition yac.c:1985
  • Fortran
    INTEGER :: field_id
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: field_name
    INTEGER, PARAMETER :: num_point_sets = 1
    INTEGER :: point_ids(num_point_sets)
    INTEGER, PARAMETER :: collection_size = 1
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: timestep
    field_name = 'sea_surface_temperature'
    timestep = 'PT15M'
    point_ids(1) = cell_point_id
    CALL yac_fdef_field ( field_name, & ! [IN]
    comp_id, & ! [IN]
    point_ids, & ! [IN]
    num_point_sets, & ! [IN]
    collection_size, & ! [IN]
    timestep, & ! [IN]
    field_id ) ! [out]
    Fortran interface for the definition of coupling fields using default masks.
    @ yac_time_unit_iso_format
  • Python
    field_name = "sea_surface_temperature"
    num_point_sets = 1
    points = [cell_points]
    collection_size = 1
    timestep = "PT15M"
    sst_field = Field.create ( field_name,
    component,
    points,
    collection_size,
    timestep,
    TimeUnit.ISO_FORMAT)

If the user wants to specify a mask for a field (in addition to the core mask), he has to either set a default mask to the respective points or pass the mask id to the field definition.

The location associated with the provided masks has to match with the ones of the points and they have to be based on the same grid.

Before a field can be defined, a calendar has to be set.

  • C
    int sst_field_id;
    char const * field_name = "sea_surface_temperature";
    int const num_point_sets = 1;
    int point_ids[num_point_sets] = {cell_point_id};
    int mask_ids[num_point_sets] = {ocean_mask_id};
    char const * timestep = "PT15M";
    yac_cdef_field_mask ( field_name,
    point_ids,
    mask_ids,
    num_point_sets,
    timestep,
    &sst_field_id );
    void yac_cdef_field_mask(char const *name, int const comp_id, int const *point_ids, int const *mask_ids, int const num_pointsets, int collection_size, const char *timestep, int time_unit, int *field_id)
    Definition yac.c:1901
  • Fortran
    INTEGER :: field_id
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: field_name
    INTEGER, PARAMETER :: num_point_sets = 1
    INTEGER :: point_ids(num_point_sets)
    INTEGER :: mask_ids(num_point_sets)
    INTEGER, PARAMETER :: collection_size = 1
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: timestep
    field_name = 'sea_surface_temperature'
    timestep = 'PT15M'
    point_ids(1) = cell_point_id
    mask_ids(1) = ocean_mask_id
    CALL yac_fdef_field_mask ( field_name, & ! [IN]
    comp_id, & ! [IN]
    point_ids, & ! [IN]
    mask_ids, & ! [IN]
    num_point_sets, & ! [IN]
    collection_size, & ! [IN]
    timestep, & ! [IN]
    field_id ) ! [out]
    Fortran interface for the definition of coupling fields using explicit masks.

Once a field is defined it can be enabled for dynamic fractional masking (see Dynamic fractional masking).

When enabling dynamic fractional masking a fallback value has to be provided for the source field. If in an exchange operation the mask for all source points used to interpolate a target point is zero, then this value will be assigned.

  • C
    int sst_field_id;
    char const * comp_name = "ocean";
    char const * grid_name = "ocean_grid";
    char const * field_name = "sea_surface_temperature";
    double frac_mask_fallback_value = 0.0;
    grid_name,
    field_name,
    frac_mask_fallback_value );
    void yac_cenable_field_frac_mask(const char *comp_name, const char *grid_name, const char *field_name, double frac_mask_fallback_value)
    Definition yac.c:2028
  • Fortran
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: comp_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: grid_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: field_name
    DOUBLE PRECISION :: frac_mask_fallback_value
    comp_name = 'ocean'
    grid_name = 'grid_name'
    field_name = 'sea_surface_temperature'
    frac_mask_fallback_value = 0.0
    comp_name, & ! [IN]
    grid_name, & ! [IN]
    field_name, & ! [IN]
    frac_mask_fallback_value) ! [in]
  • Python
    frac_mask_fallback_value = 0.0
    yac.enable_field_frac_mask(
    comp_name, grid_name, field_name,
    frac_mask_fallback_value)

Definition of Metadata

Once a component, grid, or field is defined, it is possible to attach metadata to it. In YAC metadata comes in the form of a string.

It is sufficient if at least one process defines the metadata for a component, grid, or field. If multiple processes define metadata, it has to be consistent across all of these processes.

The metadata will be synchronised and made available for all other processes by the the synchronisation of definitions and/or the end of the definition.

  • C
    char const * field_metadata = "some metadata";
    comp_name, grid_name, field_name, field_metadata);
    void yac_cdef_field_metadata(const char *comp_name, const char *grid_name, const char *field_name, const char *metadata)
    Definition yac.c:2073
  • Fortran
    CHARACTER (LEN=256) :: field_metadata
    ! ...
    field_metadata = "some metadata"
    comp_name, grid_name, field_name, field_metadata)
  • Python
    field_metadata = "some metadata"
    yac.def_field_metadata(
    comp_name, grid_name, field_name, field_metadata.encode())

Definition of Couples

The coupling between two fields can either be defined through a configuration file or through the interface.

As for the definition of fields, the definition of couples also requires a previously defined calendar.

To define a couple using the interface, the user has to define a interpolation stack.

Alternatively, the interpolation stack can be generated from a string:

If an interpolation stack is not needed anymore, it can be freed.

Once the interpolation stack is defined, it can be used in any number of couple definitions.

It is sufficient to define a couple on a single process. It will be synchronised to all other processes at synchronisation of definitions and/or the end of the definitions. However, if a couple (identified by the same source and target field) is defined on multiple processes and/or in a configuration file, the definition must be consistent.

  • C
    char const * src_comp_name = "atmo"
    char const * src_grid_name = "icon_atmos_grid"
    char const * tgt_comp_name = "ocean";
    char const * tgt_grid_name = "icon_ocean_grid";
    char const * field_name = "sea_surface_temperature";
    char const * coupling_timestep = "PT15M";
    int time_unit = YAC_TIME_UNIT_ISO_FORMAT;
    int time_reduction = YAC_REDUCTION_TIME_NONE;
    int src_lag = 0;
    int tgt_lag = 0;
    src_comp_name, src_grid_name, field_name,
    tgt_comp_name, tgt_grid_name, field_name,
    coupling_timestep, time_unit, time_reduction,
    interp_stack_id, src_lag, tgt_lag);
    char const src_grid_name[]
    char const tgt_grid_name[]
    void yac_cdef_couple(char const *src_comp_name, char const *src_grid_name, char const *src_field_name, char const *tgt_comp_name, char const *tgt_grid_name, char const *tgt_field_name, char const *coupling_timestep, int time_unit, int time_reduction, int interp_stack_config_id, int src_lag, int tgt_lag)
    Definition yac.c:2541
    int const YAC_REDUCTION_TIME_NONE
    Definition yac.c:56
  • Fortran
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: src_comp_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: src_grid_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: tgt_comp_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: tgt_grid_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: field_name
    INTEGER :: time_unit
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: timestep
    INTEGER :: time_reduction
    src_comp_name = 'atmo'
    src_grid_name = 'icon_atmos_grid'
    tgt_comp_name = 'ocean'
    tgt_grid_name = 'icon_ocean_grid'
    field_name = 'sea_surface_temperature'
    timestep = 'PT15M'
    time_reduction = yac_reduction_time_none
    src_comp_name, src_grid_name, field_name, &
    tgt_comp_name, tgt_grid_name, field_name, &
    time_unit, timestep, time_reduction, interp_stack_id)
    Fortran interface for definition of a couple.
    @ yac_reduction_time_none
  • Python
    src_comp_name = "atmo"
    src_grid_name = "icon_atmos_grid"
    tgt_comp_name = "ocean"
    tgt_grid_name = "icon_ocean_grid"
    field_name = "sea_surface_temperature"
    coupling_timestep = "PT15M"
    time_unit = TimeUnit.ISO_FORMAT
    time_reduction = Reduction.INSTANT
    yac.def_couple(
    src_comp_name, src_grid_name, field_name,
    tgt_comp_name, tgt_grid_name, field_name,
    coupling_timestep, time_unit, time_reduction,
    interp_stack)

YAC provides a set of additional parameters like weight file name and scaling factor, which can be set for each couple. While for Fortran and Python this is implemented as optional arguments, for C the extended coupling configuration has to be used:

  • C
    char const * src_comp_name = "atmo"
    char const * src_grid_name = "icon_atmos_grid"
    char const * tgt_comp_name = "ocean";
    char const * tgt_grid_name = "icon_ocean_grid";
    char const * field_name = "sea_surface_temperature";
    char const * coupling_timestep = "PT15M";
    int time_unit = YAC_TIME_UNIT_ISO_FORMAT;
    int time_reduction = YAC_REDUCTION_TIME_NONE;
    int src_lag = 0;
    int tgt_lag = 0;
    // additional coupling parameters
    int ext_couple_config;
    char const * weight_file_name = "weights.nc"
    double celcius2kelvin = 273.15;
    int mapping_on_source = 1;
    char const * const src_mask_name = "ocean mask"
    yac_cget_ext_couple_config(&ext_couple_config);
    // activate writing of weight files and
    // set weight file name
    ext_couple_config, weight_file_name);
    // activation conversion from degree Celcius to Kelvin
    ext_couple_config, celcius2kelvin);
    ext_couple_config, mapping_on_source);
    ext_couple_config_id, 1, &src_mask_name)
    src_comp_name, src_grid_name, field_name,
    tgt_comp_name, tgt_grid_name, field_name,
    coupling_timestep, time_unit, time_reduction,
    interp_stack_id, src_lag, tgt_lag,
    ext_couple_config);
    yac_cfree_ext_couple_config(ext_couple_config);
    char const * weight_file_name
    void yac_cfree_ext_couple_config(int ext_couple_config_id)
    Definition yac.c:2159
    void yac_cset_ext_couple_config_src_mask_names(int ext_couple_config_id, size_t num_src_mask_names, char const *const *src_mask_names)
    Definition yac.c:2308
    void yac_cget_ext_couple_config(int *ext_couple_config_id)
    Definition yac.c:2138
    void yac_cset_ext_couple_config_weight_file(int ext_couple_config_id, char const *weight_file)
    Definition yac.c:2175
    void yac_cdef_couple_custom(char const *src_comp_name, char const *src_grid_name, char const *src_field_name, char const *tgt_comp_name, char const *tgt_grid_name, char const *tgt_field_name, char const *coupling_timestep, int time_unit, int time_reduction, int interp_stack_config_id, int src_lag, int tgt_lag, int ext_couple_config_id)
    Definition yac.c:2509
    void yac_cset_ext_couple_config_scale_summand(int ext_couple_config_id, double scale_summand)
    Definition yac.c:2278
    void yac_cset_ext_couple_config_mapping_side(int ext_couple_config_id, int mapping_side)
    Definition yac.c:2232
  • Fortran
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: src_comp_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: src_grid_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: tgt_comp_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: tgt_grid_name
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: field_name
    INTEGER :: time_unit
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: timestep
    INTEGER :: time_reduction
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: weight_file_name
    DOUBLE PRECISION :: celcius2kelvin
    INTEGER :: mapping_on_source
    CHARACTER(LEN=YAC_MAX_CHARLEN) :: weight_file_name
    TYPE(yac_string) :: src_mask_name(1)
    src_comp_name = 'atmo'
    src_grid_name = 'icon_atmos_grid'
    tgt_comp_name = 'ocean'
    tgt_grid_name = 'icon_ocean_grid'
    field_name = 'sea_surface_temperature'
    timestep = 'PT15M'
    time_reduction = yac_reduction_time_none
    ! additional coupling parameters
    weight_file_name = 'weights.nc'
    celcius2kelvin = 273.15
    mapping_on_source = 1
    src_mask_name(1)%string = 'ocean mask'
    src_comp_name, src_grid_name, field_name, &
    tgt_comp_name, tgt_grid_name, field_name, &
    time_unit, timestep, time_reduction, interp_stack_id, &
    weight_file = weight_file_name, &
    mapping_side = mapping_on_source, &
    scale_summand = celcius2kelvin, &
    src_mask_names = src_mask_name)
  • Python
    src_comp_name = "atmo"
    src_grid_name = "icon_atmos_grid"
    tgt_comp_name = "ocean"
    tgt_grid_name = "icon_ocean_grid"
    field_name = "sea_surface_temperature"
    coupling_timestep = "PT15M"
    time_unit = TimeUnit.ISO_FORMAT
    time_reduction = Reduction.INSTANT
    src_mask_name = ["ocean mask"]
    # additional coupling parameters
    weight_file_name = "weights.nc"
    celcius2kelvin = 273.15
    mapping_on_source = 1
    yac.def_couple(
    src_comp_name, src_grid_name, field_name,
    tgt_comp_name, tgt_grid_name, field_name,
    coupling_timestep, time_unit, time_reduction,
    interp_stack,
    weight_file = weight_file_name,
    mapping_side = mapping_on_source,
    scale_summand = celcius2kelvin,
    src_masks_names = src_mask_name)

Synchronisation of Definitions (optional)

Once all components, grids and fields are defined, the configuration can be synchronized explicitly between all processes.

This is optional and can be done by only a subset of all tasks, and is done implicitly by calling yac_cenddef to end the definition phase.

Afterwards, the local configuration contains the complete definitions provided by all other processes before explicit synchronisation of definitions or ending of the definition phase.

Alternatively, definitions can be synchronized for a specific subset of components by providing a list of component names. This allows for more fine-grained control over the synchronization process and can be called multiple times with different component sets.

Additional Definitions

Following the synchronisation of definitions, each processes can access and query the definitions provided by all others.

YAC provides a number of routines for this task.

Even after explicitly synchronising definitions the processes can define additional grids, fields, and couples. However, these definitions will not be synchronised with the other processes until the end of the definition phase.

These additional definitions can be used for example by output components, that first want to query what the model components have defined. They can then use this information to decide which data should be sent to the output and define the respective couples accordingly.

End of Definition Phase

Once all processes have finished their definitions they have to collectively end the definition phase.

First, the definitions are synchronised across processes and verified for consistency. Then, all internal data required for the exchanges is generated. This includes constructing communication matrices, computing interpolation weights, and reading/writing of weight files, if applicable.

Writing of Definition to File

Since the coupling configuration of a run can be configured through various means (multiple configuration files and through the user interface by all participating processes), it may not always be obivous what the final configuration of the run is.

YAC can write the coupling configuration that is known by all processes after the component definition, after the synchronisation of definitions, and after the end of the definition phase to a file. This has to be configured either through the user interface or it has to be included in one of the configuration files (see YAML configuration file).

Writing of Grid Data to File

For debugging purposes it is possible to write grid data passed to YAC into a netCDF file.

This feature can either be enabled via a configuration file (see YAML configuration file) or via the user interface. If it is activated YAC will write out the grid data of the specified grid End of Definition Phase.

Data Exchange

For sending of data YAC provides a put operation. If mapping_on_source is used, this call is collective for all source processes. The sending of data to the target processes is done using non-blocking MPI function calls. Thus, the routine returns even if the data have not been transfered to the receiver. The data are buffered internally and the user is free to reuse the send_field buffer for a next message. However, a put will always wait until the previous put for the same field has been completed.

Depending on the coupling configuration, the data provided through the put operation may be received by one or more get operations. In case no couple is defined for the field, the put can even return without any data being sent.

In the most common use case the source data provided in the put operation is redistributed, interpolated and received by the get operation. In some special cases it may be desirable that YAC only redistributes the source data to the target processes and does not apply the interpolation, because the user may choose do this on his own. This approach is supported by YAC in form of the raw data exchange.

  • C
    int const nbr_hor_points = 512;
    int const nbr_pointsets = 1;
    int const collection_size = 4;
    int info;
    int ierror;
    double *** send_field =
    malloc(collection_size * sizeof(*send_field));
    for (int i = 0; i < collection_size; i++) {
    send_field[i] =
    malloc((size_t)nbr_pointsets * sizeof(**send_field));
    for (int j = 0; j < nbr_pointsets; j++)
    send_field[i][j] =
    malloc((size_t)nbr_hor_points * sizeof(***send_field));
    }
    for (int i = 0; i < collection_size; i++)
    for (int j = 0; j < nbr_pointsets; j++)
    for (int k = 0; k < nbr_hor_points; k++)
    send_field[i][j][k] = ... ;
    // ...
    send_field,
    &info,
    &ierror );
    int info
    int ierror
    int * field_id
    void yac_cput(int const field_id, int const collection_size, double ***const send_field, int *info, int *ierr)
    Definition yac.c:4332
  • Fortran
    INTEGER, PARAMETER :: nbr_hor_points = 512
    INTEGER, PARAMETER :: collection_size = 4
    INTEGER, PARAMETER :: nbr_pointsets = 1
    INTEGER :: info
    INTEGER :: ierror
    DOUBLE PRECISION :: send_field(nbr_hor_points, &
    nbr_pointsets, &
    collection_size)
    DO i = 1, collection_size
    DO j = 1, nbr_pointsets
    DO k = 1, nbr_hor_points
    send_field(k,j,i) = ...
    END DO
    END DO
    END DO
    ! ...
    CALL yac_fput ( field_id, & ! [IN]
    nbr_hor_points, & ! [IN]
    nbr_pointsets, & ! [IN]
    collection_size, & ! [IN]
    send_field, & ! [IN]
    info, & ! [OUT]
    ierror ) ! [out]
    Fortran interface for sending coupling fields.
  • Python
    nbr_hor_points = 512
    nbr_pointsets = 1
    collection_size = 4
    send_field = np.empty(shape=(collection_size, nbr_pointsets, nbr_hor_points))
    # ... fill send_field somehow
    info = field.put (send_field)

The receiving of data is implemented as a get operation. YAC provides a sychronous and an asynchronous version. The sychronous one will return after all required data has been received. The asynchronous version will return immediately and will receive the data in the background. Only after the user has made sure that an asynchronous get operation has been completed, it is safe to access the buffers provided to the respective get call (see Completition of Asynchronous Data Exchanges).

If no couple is defined for the field, the get will return without any changes to the receiving field data array.

  • C
    int const nbr_hor_points = 1024;
    int const collection_size = 4;
    int info;
    int ierror;
    double ** recv_field =
    malloc((size_t)collection_size * sizeof(*recv_field));
    for (int i = 0; i < collection_size; ++i)
    recv_field[i] =
    malloc((size_t)nbr_hor_points * sizeof(**recv_field));
    recv_field,
    &info,
    &ierror );
    void yac_cget(int const field_id, int collection_size, double **recv_field, int *info, int *ierr)
    Definition yac.c:3364
  • Fortran
    INTEGER, PARAMETER :: nbr_hor_points = 1024
    INTEGER, PARAMETER :: collection_size = 4
    INTEGER :: info
    INTEGER :: ierror
    DOUBLE PRECISION recv_field(nbr_hor_points, collection_size)
    CALL yac_fget ( field_id, & ! [IN]
    nbr_hor_points, & ! [IN]
    collection_size, & ! [IN]
    recv_field, & ! [OUT]
    info, & ! [OUT]
    ierror ) ! [out]
    Fortran interface for receiving coupling fields.
  • Python
    nbr_hor_points = 1024
    collection_size = 4
    recv_field = np.empty(shape=(collection_size, nbr_hor_points))
    recv_field, info = field.get ( recv_field )
    Preallocating the buffer is optional. Also None (default argument) can be passed. In that case a buffer in the correct size is allocated. See also Coroutines for an example how to use the Python coroutines interface.

In addition to the put and get operation, YAC also provides an exchange operation. This can be used in case of a bi-directional exchange. It executes a put and get operation at the same time and returns after both have been completed. By combining both operations, the internal buffer usage is more efficient.

  • C
    int const send_nbr_hor_points = 512;
    int const send_nbr_pointsets = 1;
    int const recv_nbr_hor_points = 1024;
    int const collection_size = 4;
    int send_info;
    int recv_info;
    int ierror;
    double *** send_field =
    malloc((size_t)collection_size * sizeof(*send_field));
    double ** recv_field =
    malloc((size_t)collection_size * sizeof(*recv_field));
    for (int i = 0; i < collection_size; i++) {
    send_field[i] =
    malloc(send_nbr_pointsets * sizeof(**send_field));
    recv_field[i] =
    malloc(
    (size_t)recv_nbr_hor_points * sizeof(**recv_field));
    for (int j = 0; j < send_nbr_pointsets; j++)
    send_field[i][j] =
    malloc(send_nbr_hor_points * sizeof(***send_field));
    }
    for (int i = 0; i < collection_size; i++)
    for (int j = 0; j < send_nbr_pointsets; j++)
    for (int k = 0; k < send_nbr_hor_points; k++)
    send_field[i][j][k] = ... ;
    // ...
    yac_cexchange ( send_field_id,
    recv_field_id,
    send_field,
    recv_field,
    &send_info,
    &recv_info,
    &ierror );
    void yac_cexchange(int const send_field_id, int const recv_field_id, int const collection_size, double ***const send_field, double **recv_field, int *send_info, int *recv_info, int *ierr)
    Definition yac.c:4957
  • Fortran
    INTEGER, PARAMETER :: send_nbr_hor_points = 512
    INTEGER, PARAMETER :: send_nbr_pointsets = 1
    INTEGER, PARAMETER :: recv_nbr_hor_points = 1024
    INTEGER, PARAMETER :: collection_size = 4
    INTEGER, PARAMETER :: collection_size = 4
    INTEGER :: send_info
    INTEGER :: recv_info
    INTEGER :: ierror
    DOUBLE PRECISION :: send_field(nbr_hor_points, &
    nbr_pointsets, &
    collection_size)
    DOUBLE PRECISION recv_field(nbr_hor_points, &
    collection_size)
    DO i = 1, collection_size
    DO j = 1, nbr_pointsets
    DO k = 1, nbr_hor_points
    send_field(k,j,i) = ...
    END DO
    END DO
    END DO
    ! ...
    CALL yac_fexchange ( send_field_id, & ! [IN]
    recv_field_id, & ! [IN]
    send_nbr_hor_points, & ! [IN]
    send_nbr_pointsets, & ! [IN]
    recv_nbr_hor_points, & ! [IN]
    collection_size, & ! [IN]
    send_field, & ! [IN]
    recv_field, & ! [OUT]
    send_info, & ! [OUT]
    recv_info, & ! [OUT]
    ierror ) ! [out]
    Fortran interface for exchanging coupling fields.

Completition of Asynchronous Data Exchanges

There are two utility routines that help working with asynchronous data exchanges.

The test operation checks whether for a provided field an asynchronous data exchange operation is still active.

The second utility routine is the wait operation. It does not return until all active asynchronous data exchanges associated with the provided field have been completed. (This can easily result in a deadlock. The user has to make sure, that this does not occur.)

  • C
    fputs("Last async operation has been completed.", stdout);
    void yac_cwait(int field_id)
    Definition yac.c:3846
  • Fortran
    CALL yac_fwait ( field_id )
    print *, "Last async operation has been completed."
    Fortran interface for testing fields for active communicaitons.
  • Python
    field.wait()
    print("Last async operation has been completed.")
    See also Coroutines for an example how to use the Python coroutines interface.

Exchange Info Argument

All exchange routines return an info argument, which can be used by the user to determine which action was performed by the respective exchange call. The possible values are as follows:

Advancing Internal Clock without Data Exchange Operation

YAC has for each field an internal event timer. With each put, get, or exchange operation call it is advanced according to the field time step provided in the field definition.

However, sometime it may be desirable to advance the internal clock without calling a data exchange operation, if it is safe to do so.

For each field it is possible to check the action that will occur in the next call of a data exchange operation.

If appropriate, the update operation can be executed instead of a data exchange operation.

The Finalisation Phase

Once all exchanges have been completed, YAC has to be finalised, which frees all memory allocated by YAC.

In case MPI_Init was initialised by YAC, MPI_Finalize will be called in this finalisation phase. If the user has called MPI_Init himself (before the YAC initialisation), he also has to call MPI_Finalize once the finalisation phase has been completed.

Restarting YAC

It is possible to restart YAC. To do that, the user has to clean up YAC in the finalisation phase instead of finalising it.

After the cleanup, the user can restart YAC by going through the initialisation, definition, and data exchange phase as before. The restarted YAC can have a different configuration than the previous one.

If the user initialised yaxt manually, they must finalise it after the cleanup to be able to initialise YAC again.