Components

Currently, there are two components, ocean, which encompasses all the tasks for MPAS-Ocean and Omega, and seaice, which implements tasks for MPAS-Seaice.

From a developer’s perspective, a component is a package within polaris that has four major pieces:

  1. A class that descends from the polaris.Component base class. The class is defined in __init__.py and its __init__() method calls the polaris.Component.add_tasks() method (or helper functions that, in turn, call this method) to add tasks to the component.

  2. A tasks package, which contains packages for individual tasks and their steps, possibly within packages that help to sort them into broader categories.

  3. An <component>.cfg config file containing any default config options that are universal to all tasks of the component.

  4. Additional “framework” packages and modules shared broadly between tasks.

The component’s framework is a mix of shared code and other files (config files, YAML files for model config options, etc.) that is expected to be used only by modules and packages within the component, not by other components or the main polaris Framework.

Constructor

The constructor (__init__() method) for a child class of polaris.Component simply calls the parent class’ version of the constructor with super().__init__(), passing the name of the component. Then, it calls helper functions to add tasks to the component, as in this example from polaris.ocean.Ocean:

from polaris import Component
from polaris.ocean.tasks.baroclinic_channel import add_baroclinic_channel_tasks
from polaris.ocean.tasks.cosine_bell import add_cosine_bell_tasks
from polaris.ocean.tasks.inertial_gravity_wave import (
    add_inertial_gravity_wave_tasks,
)
from polaris.ocean.tasks.manufactured_solution import (
    add_manufactured_solution_tasks,
)
from polaris.ocean.tasks.single_column import add_single_column_tasks


class Ocean(Component):
    """
    The collection of all test case for the MPAS-Ocean core
    """

    def __init__(self):
        """
        Construct the collection of MPAS-Ocean test cases
        """
        super().__init__(name='ocean')

        # please keep these in alphabetical order
        add_baroclinic_channel_tasks(component=self)
        add_cosine_bell_tasks(component=self)
        add_inertial_gravity_wave_tasks(component=self)
        add_manufactured_solution_tasks(component=self)
        add_single_column_tasks(component=self)

The object self is always passed as the component argument to the helper function so it can, in turn, be used both to add the task to the component and to identify which component the task belongs to in its constructor.

An example of a helper function that adds tasks for baroclinic channel test cases is:

from polaris.ocean.resolution import resolution_to_subdir
from polaris.ocean.tasks.baroclinic_channel.decomp import Decomp
from polaris.ocean.tasks.baroclinic_channel.default import Default
from polaris.ocean.tasks.baroclinic_channel.init import Init
from polaris.ocean.tasks.baroclinic_channel.restart import Restart
from polaris.ocean.tasks.baroclinic_channel.rpe import Rpe
from polaris.ocean.tasks.baroclinic_channel.threads import Threads


def add_baroclinic_channel_tasks(component):
    """
    Add tasks for different baroclinic channel tests to the ocean component

    component : polaris.ocean.Ocean
        the ocean component that the tasks will be added to
    """
    for resolution in [10., 4., 1.]:
        resdir = resolution_to_subdir(resolution)
        resdir = f'planar/baroclinic_channel/{resdir}'

        init = Init(component=component, resolution=resolution, indir=resdir)

        component.add_task(
            Default(component=component, resolution=resolution,
                    indir=resdir, init=init))

        if resolution == 10.:
            component.add_task(
                Decomp(component=component, resolution=resolution,
                       indir=resdir, init=init))

            component.add_task(
                Restart(component=component, resolution=resolution,
                        indir=resdir, init=init))

            component.add_task(
                Threads(component=component, resolution=resolution,
                        indir=resdir, init=init))

        component.add_task(
            Rpe(component=component, resolution=resolution,
                indir=resdir, init=init))

Config file

The config file for the component should, at the very least, define the default value for the component_path path in the [paths] section. This path should point to the path within the appropriate E3SM submodule where the standalone component can be built. This is the path to the directory where the E3SM component’s executable will be built, not to the executable itself.

Typically, the config file will also define the paths to the component executable and the default namelist and streams files for “forward mode” (and, for MPAS-Ocean, “init mode”):

# This config file has default config options for the landice core

# The paths section points polaris to external paths
[paths]

# the relative or absolute path to the root of a branch where MALI has been
# built
component_path = e3sm_submodules/MALI-Dev/components/mpas-albany-landice

# The namelists section defines paths to example_compact namelists that will be used
# to generate specific namelists. By default, these point to the forward and
# init namelists in the default_inputs directory after a successful build of
# the landice model.  Change these in a custom config file if you need a different
# example_compact.
[namelists]
forward = ${paths:component_path}/default_inputs/namelist.landice

# The streams section defines paths to example_compact streams files that will be used
# to generate specific streams files. By default, these point to the forward and
# init streams files in the default_inputs directory after a successful build of
# the landice model. Change these in a custom config file if you need a different
# example_compact.
[streams]
forward = ${paths:component_path}/default_inputs/streams.landice


# The executables section defines paths to required executables. These
# executables are provided for use by specific tasks.  Most tools that
# polaris needs should be in the conda environment, so this is only the path
# to the MALI executable by default.
[executables]
component = ${paths:component_path}/landice_model