ESMX is the Earth System Model eXecutable layer.
The ESMX layer is built on top of ESMF and NUOPC. ESMX user interfaces are typically implemented through configuration files.
The idea is to make it as simple as possible for a user to build, run, and test NUOPC based systems. The approach implemented by ESMX is the same whether applied to a single component, or a fully coupled system of NUOPC-compliant components.
ESMX unifies, provides, and maintains those parts of NUOPC-based modeling systems that are common across most such implementations. This includes the top level application and driver code, parts of the build infrastructure, and tools to manage run configurations.
The objectives of ESMX are:
- Simplification of standing up new NUOPC-based systems.
- Reduction of maintenance cost for established NUOPC-based systems.
- Improved alignment and interoperability between different NUOPC-based systems. (Configuration files, procedures, etc.)
- Faster and more coordinated role out of new NUOPC/ESMF features.
One of the main features provided by ESMX is the unified driver executable. A good starting point to explore this feature is the ESMX_AtmOcnProto under the NUOPC prototype repository.
The name of the unified driver executable built by ESMX is lower case esmx. This will be used for the remainder of this section to refer to the unfied driver executable.
A build target for esmx needs to be added to the projects build system. ESMX provides a CMake based approach that makes this easy. A simple target based on GNU Make looks like this:
include $(ESMFMKFILE)
esmx: esmxBuild.yaml
cmake -H$(ESMF_ESMXDIR) -Bbuild
cmake --build ./build
The ESMF_ESMXDIR variable used is defined by the ESMF installation through the esmf.mk file included in the first line.
A successful execution of this target will result in the unfied driver executable as build/esmx.
As shown above, building esmx has a dependency on file esmxBuild.yaml. This is a yaml file with a very simple format. An example is given here:
components:
tawas:
cmake_config: TaWaS/tawas.cmake
fort_module: tawas
lumo:
cmake_config: Lumo/lumo.cmake
In this example two components are built into esmx explicitly. (Read about dynamically loading components from shared objects at run-time later.)
Each component is given a name, here tawas and lumo. Components will be referred to by this component-name in the run-time configuration (esmxRun.config) discussed below.
Each component must define the cmake_config key, specifying a file that can be included by the CMake based esmx build. This file must provide three CMake elements:
add_library(component-name ... )set_target_properties(component-name ... )target_link_libraries(esmx_driver PUBLIC component-name)
Here component-name is the name under which the component was defined in the esmxBuild.yaml file, here tawas or lumo.
Further, a component can optionally provide the fort_module key. This explicitly specifys the name of the Fortran module that provides the entry points into the respective NUOPC component. By default the component-name is used.
The esmxRun.config file needs to be located under the run directory from where the esmx executable is launched. It is read by esmx during startup. It specifies a few global ESMF and ESMX level settings, the list of components accessed during this run, details about the components, and finally the run sequence.
logKindFlag: ESMF_LOGKIND_MULTI
globalResourceControl: .true.
ESMX_log_flush: .true.
ESMX_field_dictionary: ./fd.yaml
ESMX_component_list: ATM OCN
ESMX_attributes::
Verbosity = high
::
ATM_model: tawas
ATM_omp_num_threads: 4
ATM_attributes::
Verbosity = high
::
OCN_model: lumo
OCN_petlist: 1 3
OCN_attributes::
Verbosity = high
::
startTime: 2012-10-24T18:00:00
stopTime: 2012-10-24T19:00:00
runSeq::
@900
ATM -> OCN
OCN -> ATM
ATM
OCN
@
::
The first two fields, logKindFlag and globalResourceControl, are examples of ESMF level configuration options. They are ingested by the ESMF_Initialize() method (called by esmx) as documented in the ESMF Reference Manual.
Fields starting with ESMX_ are defined by the ESMX unified driver executable. Most important on this level is ESMX_component_list. This field defines the generic components participating in the execution. There is no restriction what labels can be used for the generic components, however, ATM and OCN are typical labels for "atmosphere" and "ocean", respectively. Note that the run sequence under runSeq, toward the end of the file, is defined in terms of the generic component labels introduced by ESMX_component_list.
Each generic component label must be associated with an actual component model through the XXX_model field, where XXX is replaced by the actual generic component label, e.g. ATM, OCN, etc. The association is made by specifying the component-name used in the esmxBuild.yaml file discussed earlier. The options in the example are tawas and lumo.
Finally the startTime and stopTime fields set the start and stop time of the run, respectively. The runSeq field defines the run sequence. The default time step of the outer run sequence loop, i.e. if using the @* syntax, is set to stopTime - startTime.
There are two options recognized when specifying the value of the XXX_model field:
- First, if the value specified is recognized as a component-name provided by any of the components built into
esmxduring build-time, as specified byesmxBuild.yaml, the respective component is accessed via its Fortran module. - Second, if the value does not match a build-time dependency, it is assumed to correspond to a shared object. In that case the attempt is made to load the specified shared object at run-time, and to associate with the generic component label.
The ESMX layer provides access to the ESMX_Driver via the public NUOPC Driver API. This means that ESMX_Driver can be plugged into a larger NUOPC application as a standard NUOPC component, or alternatively be accessed through the External NUOPC Interface.
A good starting point to explore this feature is the ESMX_ExternalDriverAPIProto under the NUOPC prototype repository.
The typical situation where ESMX_Driver comes into play is where a user application needs to access a NUOPC based system that uses the unified ESMX driver. Assuming the user application uses CMake, integration of ESMX is straight forward. All that is required is add_subdirectory() in the application's CMakeLists.txt file to add the ${ESMF_ESMXDIR}/Driver directory, and make the application dependent on target esmx_driver. An example for a very simple application is shown:
cmake_minimum_required(VERSION 3.5.2)
enable_language(Fortran)
add_subdirectory(${ESMF_ESMXDIR}/Driver ./ESMX_Driver)
# Specific project settings
project(ExternalDriverAPIProto VERSION 0.1.0)
add_executable(externalApp externalApp.F90)
target_include_directories(externalApp PUBLIC ${PROJECT_BINARY_DIR})
target_link_libraries(externalApp PUBLIC esmx_driver)
The applcation can then be built as typically via cmake commands, only requiring that the ESMF_ESMXDIR variable is passed in. It can be convenient to wrap the cmake commands into a GNU Makefile, accessing the ESMF_ESMXDIR variable through the ESMFMKFILE mechanism.
include $(ESMFMKFILE)
build/externalApp: externalApp.F90 esmxBuild.yaml
cmake -H. -Bbuild -DESMF_ESMXDIR=$(ESMF_ESMXDIR)
cmake --build ./build
The esmx_driver target defined by the add_subdirectory(${ESMF_ESMXDIR}/Driver ./ESMX_Driver) has a build-time dependency on the esmxBuild.yaml file already discussed under the unfied driver executable section. The identical file can be used when working on the ESMX_Driver level.
The run-time configuration needed by ESMX_Driver can either be supplied by the user application, or alternatively default to esmxRun.config. The following rules apply:
ESMX_Driver, at the beginning of itsSetModelServices()method checks whether the parent level has provided anESMF_Configobject by setting theconfigmember on theESMX_Drivercomponent. If so, the providedconfigobject is used. OtherwiseESMX_Driveritself createsconfigfrom fileesmxRun.config.- For the case where the
configobject was provided by the parent layer,ESMX_Driverdoes not ingest attributes fromconfig. Instead the assumption is made that the parent layer sets the desired attributes onESMX_Driver. - For the case where the
configobject was loaded fromesmxRun.configbyESMX_Driver, the driver ingests attributes fromconfig, potentially overriding parent level settings. - The
ESMX_component_list, child component, and run sequence information is ingested fromconfigas described under the unfied driver executable section. - If the parent level passes an
ESMF_Clockobject toESMX_Driverduring initialize, the driver uses it instead of looking forstartTimeandstopTimeinconfig. - For the case where a clock is provided by the parent layer, its
timeStepis used as the default time step of the outer run sequence loop when using the@*syntax. If a specific time step is set in the run sequence with@DT, thenDTmust be a divisor of thetimeStepprovided by the parent clock.
The ESMX layer has the following dependencies:
- ESMF Library: The ESMX layer is part of the ESMF repository. In order to use ESMX as described above, the ESMF library first needs to be built following the instructions for Building ESMF.
- CMake: v3.5.2 or greater.
- Python: v3.5 or greater.
python3must be in$PATH.PyYamlmust be installed in the Python environment.
There are many ways to provide a suitable Python environment. One portable way based on venv is shown below.
python3 -m venv ESMXenv
source ESMXenv/bin/activate (for Bash) or source ESMXenv/bin/activate.csh (for Csh)
pip install pyyaml
... Environment ready for ESMX ...
deactivate