Skip to content
23 changes: 23 additions & 0 deletions coupling.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(internalstate allowed coupling) Allowed coupling flags
|from to -> med atm lnd ocn ice rof wav glc1 glc2
|med - - - - - - - - -
|atm - - T T T - T - -
|lnd - T - - - T - T T
|ocn - T - - T - T T T
|ice - T - T - - T - -
|rof - - T T T - - - -
|wav - T - T T - - - -
|glc1 - - T - T T - - -
|glc2 - - T - T T - - -

(internalstate allowed coupling) Active coupling flags
|from to -> med atm lnd ocn ice rof wav glc1 glc2
|med - - - - - - - - -
|atm - - T T T - - - -
|lnd - T - - - T - T T
|ocn - T - - T - - T T
|ice - T - T - - - - -
|rof - - T T T - - - -
|wav - - - - - - - - -
|glc1 - - T - T T - - -
|glc2 - - T - T T - - -
94 changes: 94 additions & 0 deletions doc/source/developer/add_field.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
.. _add-field:

======================
Adding a coupled field
======================

Adding a new field for the mediator to exchange between two components is one of
the most common CMEPS tasks, and it can be done on its own without touching the
rest of the mediator. This page is a practical recipe. It assumes the
:ref:`field-exchange` concepts (advertise/realize, ``addfld`` / ``addmap`` /
``addmrg``) and the :ref:`fields` naming convention.

What you will touch
===================

A new coupled field involves a handful of well-defined edits:

#. the **field dictionary** — declare the field's standard name and units;
#. the host **exchange module** — advertise, map and merge it in
``esmFldsExchange_<host>_mod.F90``;
#. the **component caps** — make sure each component actually advertises the
field so NUOPC connects it.

The first two are inside CMEPS; the third is on the component side (and outside
CMEPS), but the connection will not form without it.

Step 1 — add the field to the dictionary
========================================

Add an entry for the field to the host field dictionary (``fd_cesm.yaml`` for
CESM/NorESM). Give it a standard name that follows the :ref:`naming convention
<fields>`, its canonical units, and optionally an alias and description:

.. code-block:: yaml

- standard_name: Sa_foo
canonical_units: K
description: my new near-surface atmosphere state

Step 2 — advertise, map and merge it
====================================

All of this goes in the host exchange module, ``esmFldsExchange_<host>_mod.F90``.

**Advertise** the field on both sides, in the ``phase == 'advertise'`` block —
from the source component and to the destination component:

.. code-block:: fortran

call addfld_from(compatm, 'Sa_foo') ! atmosphere can send it
call addfld_to(complnd, 'Sa_foo') ! land can receive it

**Map** the field from the source grid to the destination grid, in the
``phase == 'initialize'`` block. Choose the map type and normalization
appropriate to the field (see :ref:`mapping`):

.. code-block:: fortran

call addmap_from(compatm, 'Sa_foo', complnd, mapbilnr, 'none', 'unset')

**Merge** the mapped field into the destination's export, also in the
``initialize`` block. For a field that comes from a single source, a plain
``copy`` is enough; a field built from several surfaces uses one ``addmrg_to``
call per source with a fraction weight (see :ref:`merging`):

.. code-block:: fortran

call addmrg_to(complnd, 'Sa_foo', mrg_from=compatm, mrg_fld='Sa_foo', &
mrg_type='copy')

Step 3 — advertise the field in the component caps
==================================================

The mediator advertises a superset of possible fields, but a connection is only
made where the component on the other side advertises the field too
(:ref:`field-exchange`). So the field must also be advertised by the component
caps — the atmosphere cap must advertise ``Sa_foo`` as an **export** and fill it,
and the land cap must advertise it as an **import** and use it. This is
component code, outside CMEPS, but the coupling will silently not form without
it.

Checklist
=========

* Field added to ``fd_<host>.yaml`` with correct name and units.
* ``addfld_from`` / ``addfld_to`` in the ``advertise`` phase.
* ``addmap_from`` with an appropriate map type and normalization.
* ``addmrg_to`` with the appropriate merge type (and fraction, for weighted
merges).
* The source and destination **component caps** advertise the field.

If the field does not appear in the destination component, the first things to
check are that both caps advertised it (so NUOPC connected it) and that the name
matches the dictionary exactly.
100 changes: 100 additions & 0 deletions doc/source/developer/aofluxes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
.. _aofluxes:

=======================
Atmosphere/ocean fluxes
=======================

The mediator computes the turbulent air-sea fluxes itself, from the atmosphere
and ocean states brought onto a common grid, using a bulk flux algorithm. The
**concept** —
why the mediator owns this calculation, which fields it produces, and how they
are delivered and averaged — is covered in :ref:`concepts`. This page describes
the **mechanism**: the interface in ``med_phases_aofluxes_mod.F90`` and the grid
on which the calculation is carried out.

Interface versus algorithm
==========================

``med_phases_aofluxes_mod.F90`` is an **interface**: it prepares the inputs,
selects the grid, invokes the flux algorithm, and distributes the outputs. The
bulk-flux **algorithm** itself lives outside the shared mediator, in
host-specific code — ``cesm/flux_atmocn`` for CESM/NorESM and ``ufs/`` for UFS
(see :ref:`concepts`). The mediator reaches it through
``med_aofluxes_init`` / ``med_aofluxes_update``.

The flux outputs are held in two internal-state field bundles,
``FBMed_aoflux_a`` and ``FBMed_aoflux_o`` — the flux fields on the atmosphere and
ocean grids.

The run phase
=============

``med_phases_aofluxes_run`` is the phase the run sequence calls
(``MED med_phases_aofluxes_run``). On the first call it initializes the flux
input/output field bundles (``med_phases_aofluxes_init_fldbuns``) and the flux
scheme; on every call it computes the fluxes with ``med_aofluxes_update``.

Which bulk-flux scheme is used, and its options, come from run-configuration
attributes:

* ``coupling_mode`` — the target modeling system that will compute the
atmosphere-ocean fluxes. Valid values are ``cesm``, ``noresm`` and ``ufs``.
* ``aoflux_grid`` — the grid the atmosphere-ocean flux computation is done on:
``agrid`` (atmosphere), ``ogrid`` (ocean), or ``xgrid`` (the ESMF exchange
grid). This is described in detail in `The flux calculation grid`_ below.
* ``ocn_surface_flux_scheme`` — selects the ocean surface flux (bulk)
algorithm:

* ``0`` — Large and Pond
* ``1`` — COARE algorithm
* ``2`` — UA algorithm

CESM and UFS use the Large and Pond scheme (``0``); NorESM uses the COARE
scheme (``1``).
* ``aofluxes_use_shr_wv_sat`` — only applicable to CESM and NorESM. If
``true``, ``shr_wv_sat_mod`` is used to calculate the saturation specific
humidity (``qsat``) for the atmosphere-ocean flux calculations; if ``false``,
an older inline calculation of ``qsat`` is used, which uses a different
formulation.
* ``add_gusts`` — only applicable to CESM and NorESM. Adds a wind gustiness
factor. This should be ``false`` for ``ocn_surface_flux_scheme`` settings of
``1`` or ``2``.
* ``flux_max_iteration`` — the maximum number of iterations of the (iterative)
flux solver.

The flux calculation grid
=========================

The fluxes may be computed on the atmosphere grid, the ocean grid, or a dedicated
**ESMF exchange grid**, selected by the ``aoflux_grid`` attribute; for cases with
**all prognostic components, the exchange grid is the default**. Because the
calculation needs both atmosphere and ocean state on a single grid, the interface
maps the inputs onto the chosen grid and maps the outputs back out. The three
cases:

``aoflux_grid = ogrid`` (ocean grid)
Requires an atmosphere-to-ocean mapping: map the atmosphere inputs to the
ocean grid, compute the fluxes there, and map the flux outputs back to the
atmosphere grid.

``aoflux_grid = agrid`` (atmosphere grid)
Requires an ocean-to-atmosphere mapping: map the ocean inputs to the
atmosphere grid, compute the fluxes there, and map the flux outputs back to
the ocean grid.

``aoflux_grid = xgrid`` (ESMF exchange grid)
Map **both** the atmosphere and ocean inputs onto the exchange grid, compute
the fluxes there, and map the flux outputs from the exchange grid to **both**
the atmosphere and ocean grids.

The **ESMF exchange grid** is an ESMF construct formed from the overlap of the
atmosphere and ocean grids; the interface holds dedicated routehandles for it —
``rh_agrid2xgrid``, ``rh_ogrid2xgrid``, ``rh_xgrid2agrid`` and ``rh_xgrid2ogrid``
(with second-order-conservative and bilinear atmosphere-to-exchange variants).
Computing on the exchange grid avoids biasing the fluxes toward either
component's grid, at the cost of the extra mappings.

Whichever grid is chosen, the resulting fluxes end up in ``FBMed_aoflux_a`` and
``FBMed_aoflux_o`` for the ``prep_atm`` and ``prep_ocn`` phases to merge into the
atmosphere and ocean exports — on each component's grid and coupling interval, as
described in :ref:`concepts`.
Loading
Loading