diff --git a/.github/workflows/extbuild.yml b/.github/workflows/extbuild.yml index 6e26b40a5..0614d5acb 100644 --- a/.github/workflows/extbuild.yml +++ b/.github/workflows/extbuild.yml @@ -20,11 +20,11 @@ jobs: CPPFLAGS: "-I/usr/include -I/usr/local/include" # Versions of all dependencies can be updated here - ESMF_VERSION: v8.4.2 + ESMF_VERSION: v8.6.0 PNETCDF_VERSION: checkpoint.1.12.3 - NETCDF_FORTRAN_VERSION: v4.6.0 - PIO_VERSION: pio2_6_0 - CDEPS_VERSION: cdeps1.0.15 + NETCDF_FORTRAN_VERSION: v4.6.1 + PIO_VERSION: pio2_6_2 + CDEPS_VERSION: cdeps1.0.26 steps: - uses: actions/checkout@v3 # Build the ESMF library, if the cache contains a previous build @@ -84,7 +84,7 @@ jobs: ref: ${{ env.CDEPS_VERSION }} - name: Build CDEPS if: steps.cache-cdeps.outputs.cache-hit != 'true' - uses: ESCOMP/CDEPS/.github/actions/buildcdeps@cdeps1.0.15 + uses: ESCOMP/CDEPS/.github/actions/buildcdeps@cdeps1.0.26 with: esmfmkfile: $HOME/ESMF/lib/libg/Linux.gfortran.64.openmpi.default/esmf.mk pio_path: $HOME/pio @@ -102,6 +102,6 @@ jobs: make VERBOSE=1 popd - - name: Setup tmate session - if: ${{ failure() }} - uses: mxschmitt/action-tmate@v3 +# - name: Setup tmate session +# if: ${{ failure() }} +# uses: mxschmitt/action-tmate@v3 diff --git a/.github/workflows/srt.yml b/.github/workflows/srt.yml index 34252cb63..df5eb3c0e 100644 --- a/.github/workflows/srt.yml +++ b/.github/workflows/srt.yml @@ -26,8 +26,8 @@ jobs: CPPFLAGS: "-I/usr/include -I/usr/local/include " LDFLAGS: "-L/usr/lib/x86_64-linux-gnu -lnetcdf -lnetcdff -lpnetcdf" # Versions of all dependencies can be updated here - ESMF_VERSION: v8.5.0 - PARALLELIO_VERSION: pio2_6_0 + ESMF_VERSION: v8.6.0 + PARALLELIO_VERSION: pio2_6_2 CIME_MODEL: cesm CIME_DRIVER: nuopc GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -80,7 +80,17 @@ jobs: run: | pushd cesm ./manage_externals/checkout_externals ccs_config cdeps cime share mct cpl7 parallelio - + cd ccs_config + git checkout main + cd ../cime + git checkout master + if [[ ! -e "${PWD}/.gitmodules.bak" ]] + then + echo "Convering git@github.com to https://github.com urls in ${PWD}/.gitmodules" + + sed -i".bak" "s/git@github.com:/https:\/\/github.com\//g" "${PWD}/.gitmodules" + fi + git submodule update --init - name: Cache ESMF id: cache-esmf uses: actions/cache@v3 diff --git a/cesm/flux_atmocn/shr_flux_mod.F90 b/cesm/flux_atmocn/shr_flux_mod.F90 index 741447d93..58f7ae923 100644 --- a/cesm/flux_atmocn/shr_flux_mod.F90 +++ b/cesm/flux_atmocn/shr_flux_mod.F90 @@ -133,7 +133,7 @@ end subroutine shr_flux_adjust_constants ! Thomas Toniazzo (Bjerknes Centre, Bergen) ” !=============================================================================== SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & - & qbot ,s16O ,sHDO ,s18O ,rbot, & + & qbot, rainc ,s16O ,sHDO ,s18O ,rbot, & & tbot ,us ,vs, pslv, & & ts ,mask , seq_flux_atmocn_minwind, & & sen ,lat ,lwup , & @@ -141,7 +141,10 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & & evap ,evap_16O, evap_HDO, evap_18O, & & taux ,tauy ,tref ,qref , & & ocn_surface_flux_scheme, & - & duu10n, ustar_sv ,re_sv ,ssq_sv, & + & add_gusts, & + & duu10n, & + & ugust_out, & + & ustar_sv ,re_sv ,ssq_sv, & & missval) ! !USES: @@ -156,11 +159,13 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & integer(IN),intent(in) :: nMax ! data vector length integer(IN),intent(in) :: mask (nMax) ! ocn domain mask 0 <=> out of domain integer(IN),intent(in) :: ocn_surface_flux_scheme + logical ,intent(in) :: add_gusts real(R8) ,intent(in) :: zbot (nMax) ! atm level height (m) real(R8) ,intent(in) :: ubot (nMax) ! atm u wind (m/s) real(R8) ,intent(in) :: vbot (nMax) ! atm v wind (m/s) real(R8) ,intent(in) :: thbot(nMax) ! atm potential T (K) real(R8) ,intent(in) :: qbot (nMax) ! atm specific humidity (kg/kg) + real(R8) ,intent(in) :: rainc(nMax) ! atm precip for convective gustiness (kg/m^3) - RBN 24Nov2008/MDF 31Jan2022 real(R8) ,intent(in) :: s16O (nMax) ! atm H216O tracer conc. (kg/kg) real(R8) ,intent(in) :: sHDO (nMax) ! atm HDO tracer conc. (kg/kg) real(R8) ,intent(in) :: s18O (nMax) ! atm H218O tracer conc. (kg/kg) @@ -188,6 +193,7 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & real(R8),intent(out) :: tref (nMax) ! diag: 2m ref height T (K) real(R8),intent(out) :: qref (nMax) ! diag: 2m ref humidity (kg/kg) real(R8),intent(out) :: duu10n(nMax) ! diag: 10m wind speed squared (m/s)^2 + real(R8),intent(out) :: ugust_out(nMax) ! diag: gustiness addition to U10 (m/s) real(R8),intent(out),optional :: ustar_sv(nMax) ! diag: ustar real(R8),intent(out),optional :: re_sv (nMax) ! diag: sqrt of exchange coefficient (water) @@ -257,6 +263,8 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & real(R8) :: tdiff(nMax) ! tbot - ts real(R8) :: vscl + real(R8) :: ugust ! function: gustiness as a function of convective rainfall. + real(R8) :: gprec ! convective rainfall argument for ugust qsat(Tk) = 640380.0_R8 / exp(5107.4_R8/Tk) @@ -264,7 +272,7 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & cdn(Umps) = 0.0027_R8 / min(33.0000_R8,Umps) + 0.000142_R8 + & 0.0000764_R8 * min(33.0000_R8,Umps) - 3.14807e-13_r8 * min(33.0000_R8,Umps)**6 ! Capped Large and Pond by wind - ! cdn(Umps) = 0.0027_R8 / min(30.0_R8,Umps) + 0.000142_R8 + 0.0000764_R8 * min(30.0_R8,Umps) + ! cdn(Umps) = 0.0027_R8 / min(30.0_R8,Umps) + 0.000142_R8 + 0.0000764_R8 * min(30.0_R8,Umps) ! Capped Large and Pond by Cd ! cdn(Umps) = min(0.0025_R8, (0.0027_R8 / Umps + 0.000142_R8 + 0.0000764_R8 * Umps )) ! Large and Pond @@ -273,6 +281,13 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & psimhu(xd) = log((1.0_R8+xd*(2.0_R8+xd))*(1.0_R8+xd*xd)/8.0_R8) - 2.0_R8*atan(xd) + 1.571_R8 psixhu(xd) = 2.0_R8 * log((1.0_R8 + xd*xd)/2.0_R8) + ! Convective gustiness appropriate for input precipitation. + ! Following Regelsperger et al. (2000, J. Clim) + ! Ug = log(1.0+6.69R-0.476R^2) + ! Coefficients X by 8640 for mm/s (from cam) -> cm/day (for above forumla) + ugust(gprec) = log(1._R8+57801.6_r8*gprec-3.55332096e7_r8*(gprec**2)) + + !--- formats ---------------------------------------- character(*),parameter :: subName = '(flux_atmOcn) ' character(*),parameter :: F00 = "('(flux_atmOcn) ',4a)" @@ -327,7 +342,14 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & if (mask(n) /= 0) then !--- compute some needed quantities --- - vmag = max(seq_flux_atmocn_minwind, sqrt( (ubot(n)-us(n))**2 + (vbot(n)-vs(n))**2) ) + if (add_gusts) then + vmag = max(seq_flux_atmocn_minwind, sqrt( (ubot(n)-us(n))**2 + (vbot(n)-vs(n))**2) + ugust(min(rainc(n),6.94444e-4_r8)) ) + ugust_out(n) = ugust(min(rainc(n),6.94444e-4_r8)) + else + vmag = max(seq_flux_atmocn_minwind, sqrt( (ubot(n)-us(n))**2 + (vbot(n)-vs(n))**2) ) + ugust_out(n) = 0.0_r8 + end if + if (use_coldair_outbreak_mod) then ! Cold Air Outbreak Modification: ! Increase windspeed for negative tbot-ts @@ -462,6 +484,7 @@ SUBROUTINE flux_atmOcn(logunit, nMax ,zbot ,ubot ,vbot ,thbot , & tref (n) = spval ! 2m reference height temperature (K) qref (n) = spval ! 2m reference height humidity (kg/kg) duu10n(n) = spval ! 10m wind speed squared (m/s)^2 + ugust_out(n) = spval ! gustiness addition (m/s) if (present(ustar_sv)) ustar_sv(n) = spval if (present(re_sv )) re_sv (n) = spval diff --git a/cime_config/buildnml b/cime_config/buildnml index 32be8ead4..ff2553be7 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -15,6 +15,7 @@ from CIME.case import Case from CIME.nmlgen import NamelistGenerator from CIME.utils import expect from CIME.utils import get_model, get_time_in_seconds, get_timestamp +from CIME.namelist import literal_to_python_value from CIME.buildnml import create_namelist_infile, parse_input from CIME.XML.files import Files @@ -105,7 +106,8 @@ def _create_drv_namelists(case, infile, confdir, nmlgen, files): config["COMP_OCN"] = case.get_value("COMP_OCN") config["COMP_ROF"] = case.get_value("COMP_ROF") config["COMP_WAV"] = case.get_value("COMP_WAV") - + config["CAMDEV"] = "True" if "CAM%DEV" in case.get_value("COMPSET") else "False" + if ( ( case.get_value("COMP_ROF") == "mosart" @@ -144,6 +146,11 @@ def _create_drv_namelists(case, infile, confdir, nmlgen, files): if config["COMP_OCN"] == "docn" and "aqua" in case.get_value("DOCN_MODE"): nmlgen.set_value("aqua_planet", value=".true.") + # make sure that variable add_gusts is only set to true if compset includes cam_dev + add_gusts = literal_to_python_value(nmlgen.get_value("add_gusts"), type_="logical") + if add_gusts: + expect("CAM%DEV" in case.get_value("COMPSET"),"ERROR: add_gusts can only be set if CAM%DEV in compset {}".format(case.get_value("COMPSET"))) + # -------------------------------- # Overwrite: set component coupling frequencies # -------------------------------- @@ -658,6 +665,7 @@ def buildnml(case, caseroot, component): create_namelist_infile(case, user_nl_file, namelist_infile, infile_text) infile = [namelist_infile] + # create the files nuopc.runconfig, nuopc.runseq, drv_in and drv_flds_in _create_drv_namelists(case, infile, confdir, nmlgen, files) diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index d73964961..938e0e31c 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -821,15 +821,6 @@ different MPI ranks to different GPUs within the same compute node - - logical - TRUE,FALSE - FALSE - build_def - env_build.xml - TRUE implies that at least one of the components is built threaded (DO NOT EDIT) - - logical TRUE,FALSE @@ -1035,23 +1026,6 @@ this to work. - - char - ESMF_LOGKIND_SINGLE,ESMF_LOGKIND_MULTI,ESMF_LOGKIND_NONE - ESMF_LOGKIND_NONE - run_flags - env_run.xml - - Determines what ESMF log files (if any) are generated when - USE_ESMF_LIB is TRUE. - ESMF_LOGKIND_SINGLE: Use a single log file, combining messages from - all of the PETs. Not supported on some platforms. - ESMF_LOGKIND_MULTI: Use multiple log files -- one per PET. - ESMF_LOGKIND_NONE: Do not issue messages to a log file. - By default, no ESMF log files are generated. - - - char off,low,high,max diff --git a/cime_config/namelist_definition_drv.xml b/cime_config/namelist_definition_drv.xml index dec6868f1..3e4d6bf6b 100644 --- a/cime_config/namelist_definition_drv.xml +++ b/cime_config/namelist_definition_drv.xml @@ -18,24 +18,6 @@ - - char - cime_pes - PELAYOUT_attributes - - Determines what ESMF log files (if any) are generated when - USE_ESMF_LIB is TRUE. - ESMF_LOGKIND_SINGLE: Use a single log file, combining messages from - all of the PETs. Not supported on some platforms. - ESMF_LOGKIND_MULTI: Use multiple log files — one per PET. - ESMF_LOGKIND_NONE: Do not issue messages to a log file. - By default, no ESMF log files are generated. - - - $ESMF_LOGFILE_KIND - - - integer pio @@ -964,6 +946,19 @@ + + logical + control + MED_attributes + + add a wind gustiness factor + + + .true. + .false. + + + logical budget diff --git a/cime_config/runseq/runseq_TG.py b/cime_config/runseq/runseq_TG.py index c0bb4ab92..dea8aede5 100644 --- a/cime_config/runseq/runseq_TG.py +++ b/cime_config/runseq/runseq_TG.py @@ -34,7 +34,7 @@ def gen_runseq(case, coupling_times): runseq.add_action ("MED med_phases_post_lnd" , run_lnd) runseq.add_action ("MED med_phases_prep_glc" , med_to_glc) runseq.add_action ("MED -> GLC :remapMethod=redist" , med_to_glc) - runseq.add_action ("GLC" , run_glc) + runseq.add_action ("GLC" , run_glc and med_to_glc) runseq.add_action ("GLC -> MED :remapMethod=redist" , run_glc) runseq.add_action ("MED med_phases_history_write" , True) diff --git a/cime_config/testdefs/testlist_drv.xml b/cime_config/testdefs/testlist_drv.xml index 985bd6ce9..e17b2ffcf 100644 --- a/cime_config/testdefs/testlist_drv.xml +++ b/cime_config/testdefs/testlist_drv.xml @@ -5,36 +5,36 @@ - + - + - + - + - + - + - + - + @@ -46,18 +46,18 @@ - + - + - + - + @@ -69,18 +69,18 @@ - + - + - + - + @@ -92,27 +92,27 @@ - + - + - + - + - + - + @@ -124,27 +124,27 @@ - + - + - + - + - + - + @@ -156,9 +156,9 @@ - + - + @@ -170,24 +170,24 @@ - + - + - + - + - + @@ -200,36 +200,36 @@ - + - + - + - + - + - + - + - + @@ -241,18 +241,18 @@ - + - + - + - + @@ -263,18 +263,18 @@ - + - + - + - + @@ -282,9 +282,9 @@ - + - + diff --git a/doc/source/addendum/req_attributes.rst b/doc/source/addendum/req_attributes.rst index 410303632..ed2130b32 100644 --- a/doc/source/addendum/req_attributes.rst +++ b/doc/source/addendum/req_attributes.rst @@ -6,12 +6,12 @@ The following attributes are obtained from the respective driver and available to all components that the driver uses. In the case of -NEMS, the NEMS driver ingests these attributes from the -``nems.configure`` file. In the case of CESM, the CESM driver ingests +UFS, the UFS driver ingests these attributes from the +``ufs.configure`` file. In the case of CESM, the CESM driver ingests these attributes from the ``nuopc.runconfig`` file. The list of attributes below are separated into application independent attributes and at this time additional attributes required by CESM. There are no -NEMS-specific attributes required by the NEMS application. +UFS-specific attributes required by the UFS application. General @@ -24,7 +24,7 @@ General CMEPS and is also leveraged in some of the custom calculations in the ``prep`` modules. - The currently supported values for ``coupling_mode`` are ``cesm``, ``nems_orig``, ``nems_frac`` and ``hafs``. + The currently supported values for ``coupling_mode`` are ``cesm``, ``ufs.(frac,nfrac).(aoflux)``, and ``hafs``. Scalar attributes ----------------- diff --git a/doc/source/esmflds.rst b/doc/source/esmflds.rst index 960789491..3289ddeb5 100644 --- a/doc/source/esmflds.rst +++ b/doc/source/esmflds.rst @@ -14,8 +14,8 @@ For each supported application, CMEPS contains two specific files that determine Three application specific versions are currently contained within CMEPS: * for CESM: **esmFldsExchange_cesm_mod.F90** and **fd_cesm.yaml** -* for UFS-S2S: **esmFldsExchange_nems_mod.F90** and **fd_nems.yaml** -* for UFS-HAFS: **esmFldsExchange_hafs_mod.F90** and **fd_hafs.yaml** +* for UFS-S2S: **esmFldsExchange_ufs_mod.F90** and **fd_ufs.yaml** +* for UFS-HAFS: **esmFldsExchange_hafs_mod.F90** and **fd_ufs.yaml** CMEPS advertises **all possible fields** that can be imported to and exported by the mediator for the target coupled system. Not all of @@ -23,10 +23,10 @@ these fields will be connected to the various components. The connections will be determined by what the components advertise in their respective advertise phase. -Across applications, component-specific names for the same fields may vary. The field +Across applications, component-specific names for the same fields may vary. The field dictionary is used to define how the application or component-specific name relates -to the name that the CMEPS mediator uses for that field. The mediator variable -names and their application specific aliases are found in the YAML field dictionary. +to the name that the CMEPS mediator uses for that field. The mediator variable +names and their application specific aliases are found in the YAML field dictionary. Details of the naming conventions and API's of this file can be found in the description of the :ref:`exchange of fields in @@ -38,7 +38,7 @@ Field Naming Convention The CMEPS field name convention in the YAML files is independent of the model components. The convention differentiates between variables that are state fields versus flux fields. The naming convention assumes the following one letter designation for the various components as -well as the mediator. +well as the mediator. **import to mediator**:: @@ -58,30 +58,30 @@ well as the mediator. State variables have a 3 character prefix followed by the state name. The prefix has the form ``S[a,i,l,g,o,r,w,x]_`` and is followed by - the field name. - + the field name. + As an example, ``Sx_t`` is the merged surface temperature from land, ice and ocean sent to the atmosphere for CESM. **Flux variables**: - Flux variables specify both source and destination components and have a - 5 character prefix followed by an identifier name of the flux. The first 5 - characters of the flux prefix ``Flmn_`` indicate a flux between - components l and m, computed by component n. The flux-prefix is followed - by the relevant flux-name. - + Flux variables specify both source and destination components and have a + 5 character prefix followed by an identifier name of the flux. The first 5 + characters of the flux prefix ``Flmn_`` indicate a flux between + components l and m, computed by component n. The flux-prefix is followed + by the relevant flux-name. + **mediator import flux prefixes**:: - + Faxa_, atm flux computed by atm Fall_, lnd-atm flux computed by lnd Fioi_, ice-ocn flux computed by ice Faii_, ice_atm flux computed by ice Flrr_, lnd-rof flux computed by rof Firr_, rof-ice flux computed by rof - + **mediator export flux prefixes**:: - + Faxx_, mediator merged fluxes sent to the atm Foxx_, mediator merged fluxes sent to the ocn Fixx_, mediator merged fluxes sent to the ice @@ -122,7 +122,7 @@ The API for this call is: .. code-block:: Fortran call addfld(fldListFr(comp_index)%flds, 'field_name') - call addfld(fldListTo(comp_index)%flds, 'field_name') + call addfld(fldListTo(comp_index)%flds, 'field_name') where: @@ -206,7 +206,7 @@ fraction corrections are not required in other mappings to improve accuracy beca call addmap(fldListFr(compice)%flds, 'Si_snowh', compatm, mapconsf, 'ifrac', 'unset') This will create an entry in ``fldListFr(compatm)`` specifying that the ``Si_snowh`` field from the ice should be mapped conservatively to the atmosphere using -fractional normalization where the ice fraction is obtained from ``FBFrac(compice)[snowh]``. The route handle for this mapping will be created at run time. +fractional normalization where the ice fraction is obtained from ``FBFrac(compice)[snowh]``. The route handle for this mapping will be created at run time. .. _addmrg: diff --git a/doc/source/generic.rst b/doc/source/generic.rst index 62055af1c..1be409a9a 100644 --- a/doc/source/generic.rst +++ b/doc/source/generic.rst @@ -15,7 +15,7 @@ application specific and provide general functionality. component state in the mediator's InternalState * initializing the mediator component specific fields via a call to - ``esmFldsExchange_xxx_`` (where currently xxx can be ``cesm``, ``nems`` or ``hafs``). + ``esmFldsExchange_xxx_`` (where currently xxx can be ``cesm``, ``ufs`` or ``hafs``). * determining which components are present diff --git a/doc/source/introduction.rst b/doc/source/introduction.rst index 3b79e1ed0..54a16761b 100644 --- a/doc/source/introduction.rst +++ b/doc/source/introduction.rst @@ -31,7 +31,7 @@ matching of standard field names. These standard names are defined in a field dictionary. Since CMEPS is a community mediator, these standard names are specific to each application. - + Organization of the CMEPS mediator code ####################################### @@ -39,28 +39,28 @@ Organization of the CMEPS mediator code When you check out the code you will files, which can be organized into three groups: -* totally generic components that carry out the mediator functionality such as mapping, - merging, restarts and history writes. Included here is a a "fraction" module that - determines the fractions of different source model components on every source +* totally generic components that carry out the mediator functionality such as mapping, + merging, restarts and history writes. Included here is a a "fraction" module that + determines the fractions of different source model components on every source destination mesh. -* application specific code that determines what fields are exchanged between +* application specific code that determines what fields are exchanged between components and how they are merged and mapped. -* prep phase modules that carry out the mapping and merging from one or more +* prep phase modules that carry out the mapping and merging from one or more source components to the destination component. =========================== ============================ =========================== Generic Code Application Specific Code Prep Phase Code =========================== ============================ =========================== med.F90 esmFldsExchange_cesm_mod.F90 med_phases_prep_atm_mod.F90 -esmFlds.F90 esmFldsExchange_nems_mod.F90 med_phases_prep_ice_mod.F90 +esmFlds.F90 esmFldsExchange_ufs_mod.F90 med_phases_prep_ice_mod.F90 med_map_mod.F90 esmFldsExchange_hafs_mod.F90 med_phases_prep_ocn_mod.F90 med_merge_mod.F90 fd_cesm.yaml med_phases_prep_glc_mod.F90 -med_frac_mod.F90 fd_nems.yaml med_phases_prep_lnd_mod.F90 -med_internalstate_mod.F90 fd_hafs.yaml med_phases_prep_rof_mod.F90 -med_methods_mod.F90. -med_phases_aofluxes_mod.F90 +med_frac_mod.F90 fd_ufs.yaml med_phases_prep_lnd_mod.F90 +med_internalstate_mod.F90 fd_ufs.yaml med_phases_prep_rof_mod.F90 +med_methods_mod.F90. +med_phases_aofluxes_mod.F90 med_phases_ocnalb_mod.F90 med_phases_history_mod.F90 med_phases_restart_mod.F90 @@ -78,8 +78,8 @@ Mapping and Merging Primer ####################################### This section provides a primer on mapping (interpolation) and merging of gridded -coupled fields. Masks, support for partial fractions on grids, weights generation, -and fraction +coupled fields. Masks, support for partial fractions on grids, weights generation, +and fraction weighted mapping and merging all play roles in the conservation and quality of the coupled fields. @@ -89,11 +89,11 @@ A pair of atmosphere and ocean/ice grids can be used to highlight the analysis. :width: 400 :alt: Sample CMEPS grids -The most general CMEPS mediator assumes the ocean and sea ice surface grids are +The most general CMEPS mediator assumes the ocean and sea ice surface grids are identical while the atmosphere and land grids are also identical. The ocean/ice grid defines the mask which means each ocean/ice gridcell is either a fully -active ocean/ice gridcell or not (i.e. land). Other configurations have been -and can be implemented and analyzed as well. +active ocean/ice gridcell or not (i.e. land). Other configurations have been +and can be implemented and analyzed as well. The ocean/ice mask interpolated to the atmosphere/land grid determines the complementary ocean/ice and land masks on the atmosphere grid. @@ -112,12 +112,12 @@ The gridcells can be labeled as follows. :width: 300 :alt: Sample CMEPS gridcell naming convention -The atmosphere gridcell is labeled "a". On the atmosphere gridcell (the red box), +The atmosphere gridcell is labeled "a". On the atmosphere gridcell (the red box), in general, there is a land fraction (fal), an ocean fraction (fao), and a sea ice fraction (fai). The sum of the surface fractions should always be 1.0 in these -conventions. There is also a gridbox average field on the atmosphere grid (Fa). -This could be a flux or a state that is +conventions. There is also a gridbox average field on the atmosphere grid (Fa). +This could be a flux or a state that is derived from the equivalent land (Fal), ocean (Fao), and sea ice (Fai) fields. The gridbox average field is computed by merging the various surfaces:: @@ -130,9 +130,9 @@ This is a standard merge where:: and each surface field, Fal, Fao, and Fai are the values of the surface fields on the atmosphere grid. -The ocean gridcells (blue boxes) are labeled 1, 2, 3, and 4 in this example. -In general, -each ocean/ice gridcell partially overlaps multiple atmosphere gridcells. +The ocean gridcells (blue boxes) are labeled 1, 2, 3, and 4 in this example. +In general, +each ocean/ice gridcell partially overlaps multiple atmosphere gridcells. Each ocean/ice gridcell has an overlapping Area (A) and a Mask (M) associated with it. In this example, land is colored green, ocean blue, and sea ice white so just for the figure depicted:: @@ -159,7 +159,7 @@ gridcells. Nonlinear interpolation is not yet supported in most coupled systems Mapping weights can be defined in a number of ways even beyond conservative or bilinear. They can be masked or normalized using multiple approaches. The -weights generation is intricately tied to other aspects of the coupling method. +weights generation is intricately tied to other aspects of the coupling method. In CMEPS, area-overlap conservative weights are defined as follows:: w1 = A1/Aa @@ -167,7 +167,7 @@ In CMEPS, area-overlap conservative weights are defined as follows:: w3 = A3/Aa w4 = A4/Aa -This simple approach which does not include any masking or normalization provides a +This simple approach which does not include any masking or normalization provides a number of useful attributes. The weights always add up to 1.0:: w1 + w2 + w3 + w4 = 1.0 @@ -207,11 +207,11 @@ And the equation for f_land and fal above are consistent if fl1=1-M1:: fal = w1 + w2 + w3 + w4 - (w1*M1 + w2*M2 + w3*M3 + w4*M4) fal = 1 - (w1*M1 + w2*M2 + w3*M3 + w4*M4) -Clearly defined and consistent weights, areas, fractions, and masks is critical +Clearly defined and consistent weights, areas, fractions, and masks is critical to generating conservation in the system. When mapping masked or fraction weighted fields, these weights require that the -mapped field be normalized by the mapped fraction. Consider a case where sea +mapped field be normalized by the mapped fraction. Consider a case where sea surface temperature (SST) is to be mapped to the atmosphere grid with:: M1 = 0; M2 = M3 = M4 = 1 @@ -223,15 +223,15 @@ because w1 is non-zero and Fo1 is underfined since it's a land gridcell on the ocean grid. A masked weighted average, **Fa = M1*w1*Fo1 + M2*w2*Fo2 + M3*w3*Fo3 + M4*w4*Fo4 is also problematic** because M1 is zero, so the contribution of the first term is zero. But the sum -of the remaining weights (M2*w2 + M3*w3 + M4*w4) is now not identically 1 -which means the weighted average is incorrect. (To test this, assume all the +of the remaining weights (M2*w2 + M3*w3 + M4*w4) is now not identically 1 +which means the weighted average is incorrect. (To test this, assume all the weights are each 0.25 and all the Fo values are 10 degC, Fa would then be 7.5 degC). Next consider a masked weighted normalized average, **f_ocean = (w1*M1 + w2*M2 + w3*M3 + w4*M4) combined with Fa = (M1*w1*Fo1 + M2*w2*Fo2 + M3*w3*Fo3 + M4*w4*Fo4) / (f_ocean) which produces a reasonable but incorrect result** because the weighted average uses the mask instead of the fraction. The mask only produces a correct result -in cases where there is no sea ice because sea ice impacts the surface fractions. +in cases where there is no sea ice because sea ice impacts the surface fractions. Finally, consider a fraction weighted normalized average using the dynamically varying ocean fraction that is exposed to the atmosphere:: @@ -249,9 +249,9 @@ fao is the mapped ocean fraction on the atmosphere gridcell, and Fa is the mapped SST. The ocean fractions are only defined where the ocean mask is 1, otherwise the ocean and sea ice fractions are zero. Now, the SST in each ocean gridcell is weighted by the fraction of the ocean -box exposed to the atmosphere and that weighted average is normalized by +box exposed to the atmosphere and that weighted average is normalized by the mapped dynamically varying fraction. This produces a reasonable result -as well as a conservative result. +as well as a conservative result. The conservation check involves thinking of Fo and Fa as a flux. On the ocean grid, the quantity associated with the flux is:: @@ -268,7 +268,7 @@ Via some simple math, it can be shown that Qo = Qa if:: fao = w1*fo1 + w2*fo2 + w3*fo3 + w4*fo4 Fao = (fo1*w1*Fo1 + fo2*w2*Fo2 + fo3*w3*Fo3 + fo4*w4*Fo4) / (fao) -In practice, the fraction weighted normlized mapping field is computed +In practice, the fraction weighted normlized mapping field is computed by mapping the ocean fraction and the fraction weighted field from the ocean to the atmosphere grid separately and then using the mapped fraction to normalize the field as a four step process:: @@ -278,19 +278,19 @@ using the mapped fraction to normalize the field as a four step process:: Fao' = w1*Fo1' + w2*Fo2' + w3*Fo3' + w4*Fo4' (c) Fao = Fao'/fao (d) -Steps (b) and (c) above are the sparse matrix multiply by the standard +Steps (b) and (c) above are the sparse matrix multiply by the standard conservative weights. -Step (a) fraction weighs the field and step (d) normalizes the mapped field. +Step (a) fraction weighs the field and step (d) normalizes the mapped field. Another way to think of this is that the mapped flux (Fao') is normalized by the -same fraction (fao) that is used in the merge, so they actually cancel. -Both the normalization at the end of the mapping and the fraction weighting +same fraction (fao) that is used in the merge, so they actually cancel. +Both the normalization at the end of the mapping and the fraction weighting in the merge can be skipped and the results should be identical. But then the mediator will carry around Fao' instead of Fao and that field is far less intuitive as it no longer represents the gridcell average value, but some subarea average value. In addition, that approach is only valid when carrying out full surface merges. If, -for instance, the SST is to be interpolated and not merged with anything, the field +for instance, the SST is to be interpolated and not merged with anything, the field must be normalized after mapping to be useful. The same mapping and merging process is valid for the sea ice:: @@ -311,23 +311,23 @@ where now:: Fao = (fo1*w1*Fo1 + fo2*w2*Fo2 + fo3*w3*Fo3 + fo4*w4*Fo4) / (fao) Fai = (fi1*w1*Fi1 + fi2*w2*Fi2 + fi3*w3*Fi3 + fi4*w4*Fi4) / (fai) -will simplify to an equation that contains twelve distinct terms for each of the +will simplify to an equation that contains twelve distinct terms for each of the four ocean gridboxes and the three different surfaces:: - Fa = (w1*fl1*Fl1 + w2*fl2*Fl2 + w3*fl3*Fl3 + w4*fl4*Fl4) + - (w1*fo1*Fo1 + w2*fo2*Fo2 + w3*fo3*Fo3 + w4*fo4*Fo4) + - (w1*fi1*Fi1 + w2*fi2*Fi2 + w3*fi3*Fi3 + w4*fi4*Fi4) + Fa = (w1*fl1*Fl1 + w2*fl2*Fl2 + w3*fl3*Fl3 + w4*fl4*Fl4) + + (w1*fo1*Fo1 + w2*fo2*Fo2 + w3*fo3*Fo3 + w4*fo4*Fo4) + + (w1*fi1*Fi1 + w2*fi2*Fi2 + w3*fi3*Fi3 + w4*fi4*Fi4) and this further simplifies to something that looks like a mapping of the field merged on the ocean grid:: - Fa = w1*(fl1*Fl1+fo1*Fo1+fi1*Fi1) + + Fa = w1*(fl1*Fl1+fo1*Fo1+fi1*Fi1) + w2*(fl2*Fl2+fo2*Fo2+fi2*Fi2) + - w3*(fl3*Fl3+fo3*Fo3+fi3*Fi3) + + w3*(fl3*Fl3+fo3*Fo3+fi3*Fi3) + w4*(fl4*Fl4+fo4*Fo4+fi4*Fi4) Like the exercise with Fao above, these equations can be shown to be -fully conservative. +fully conservative. To summarize, multiple features such as area calculations, weights, masking, normalization, fraction weighting, and merging approaches @@ -335,9 +335,9 @@ have to be considered together to ensure conservation. The CMEPS mediator uses unmasked and unnormalized weights and then generally maps using the fraction weighted normalized approach. Merges are carried out with fraction weights. -This is applied to both state and flux fields, with conservative, bilinear, +This is applied to both state and flux fields, with conservative, bilinear, and other mapping approaches, and for both merged and unmerged fields. -This ensures that the fields are always useful gridcell average values +This ensures that the fields are always useful gridcell average values when being coupled or analyzed throughout the coupling implementation. @@ -353,7 +353,7 @@ model discretization, they are NOT ad-hoc. If the previous section, areas and weights were introduced. Those areas were assumed to consist of the area overlaps between gridcells and were computed -using a consistent approach such that the areas conserve. ESMF is able to compute +using a consistent approach such that the areas conserve. ESMF is able to compute these area overlaps and the corresponding mapping weights such that fluxes can be mapped and quantities are conserved. @@ -369,13 +369,13 @@ ESMF are identical, and all the weights are 1.0. So:: F2*A2 = F1*A1 (conservation) Now lets assume that the two models have fundamentally different discretizations, -different area algorithms (i.e. great circle vs simpler lon/lat approximations), +different area algorithms (i.e. great circle vs simpler lon/lat approximations), or even different assumptions about the size and shape of the earth. The grids can be identical in -terms of the longitude and latitude of the +terms of the longitude and latitude of the gridcell corners and centers, but the areas can also -be different because of the underlying model implementation. When a flux is passed -to or from each component, the quantity associated with that flux is proportional to +be different because of the underlying model implementation. When a flux is passed +to or from each component, the quantity associated with that flux is proportional to the model area, so:: A1 = A2 (ESMF areas) @@ -385,7 +385,7 @@ the model area, so:: A1m != A2m (model areas) F1*A1m != F2*A2m (loss of conservation) -This can be corrected by multiplying the fluxes +This can be corrected by multiplying the fluxes by an area correction. For each model, outgoing fluxes should be multiplied by the model area divided by the ESMF area. Incoming fluxes should be multiplied by the ESMF area divided by the model area. So:: @@ -411,14 +411,14 @@ can actually be applied a number of ways. * Models can pass the areas to the mediator and the mediator can multiple fluxes by the source model area before mapping and divide by the destination model area area after mapping. * Models can pass the areas to the mediator and implement an area correction term on the incoming and outgoing fluxes that is the ratio of the model and ESMF areas. This is the approach shown above and is how CMEPS traditionally implements this feature. -Model areas should be passed to the mediator at initialization so the area corrections +Model areas should be passed to the mediator at initialization so the area corrections can be computed and applied. These area corrections do not vary in time. Lags, Accumulation and Averaging ####################################### -In a coupled model, the component model sequencing and coupling frequency tend to introduce +In a coupled model, the component model sequencing and coupling frequency tend to introduce some lags as well as a requirement to accumulate and average. This occurs when component models are running sequentially or concurrently. In general, the component models advance in time separately and the "current time" in each model becomes out of @@ -430,7 +430,7 @@ multiple timesteps are taken between coupling periods in a component model, the states should be averaged over those timesteps before being passed back out to the coupler. In the same way, the fluxes and states passed into the coupler should be averaged over shorter coupling periods for models that are coupled at longer coupling -periods. +periods. For conservation of mass and energy, the field that is accumluated should be consistent with the field that would be passed if there were no averaging required. Take for @@ -447,13 +447,13 @@ where sum_n represents the sum over n time periods. This can also be written as Fo = 1/n * (sum_n(fao*Fao) + sum_n(fio*Fio)) -So multiple terms can be summed and accumulated or the individual terms fao*Fao +So multiple terms can be summed and accumulated or the individual terms fao*Fao and fio*Fio can be accumulated and later summed and averaged in either order. Both approaches produce identical results. Finally, **it's important to note that sum_n(fao)*sum_n(Fao) does not produce the same results as the sum_n(fao*Fao)**. In other words, the fraction weighted flux has to be accumulated and NOT the fraction and flux separately. This is important for conservation -in flux coupling. The same approach should be taken with merged states to compute the +in flux coupling. The same approach should be taken with merged states to compute the most accurate representation of the average state over the slow coupling period. An analysis and review of each coupling field should be carried out to determine the most conservative and accurate representation of averaged fields. This is particularly @@ -493,14 +493,14 @@ Simplifying the above equation:: Fo = 1/n * sum_n(mapa2o(fao_a*Fao_a) -Accumulation (sum_n) and mapping (mapa2o) are both linear operations so this can +Accumulation (sum_n) and mapping (mapa2o) are both linear operations so this can be written as:: Fo = 1/n * mapa2o(sum_n(fao_a*Fao_a)) Fo = mapa2o(1/n*sum_n(fao_a*Fao_a)) which suggests that the accumulation can be done on the source side (i.e. atmosphere) -and only mapped on the slow coupling period. But again, fao_a*Fao_a has to be +and only mapped on the slow coupling period. But again, fao_a*Fao_a has to be accumulated and then when mapped, NO fraction would be applied to the merge as this is already included in the mapped field. In equation form, the full merged ocean field would be implemented as:: @@ -520,7 +520,7 @@ two atmosphere fields are mapped every fast coupling period, the merge is now fraction weighted for all terms, and the mapped fields, fao_o and Fao_o, have physically meaningful values. Fao'_o above does not. This implementation has a parallel with the normalization step. As suggested above, there are two -implementations for conservative mapping and merging in general. The one outlined +implementations for conservative mapping and merging in general. The one outlined above with fraction weighted normalized mapping and fraction weighted merging:: @@ -536,7 +536,7 @@ fraction is NOT applied during the merge:: These will produce identical results in the same way that their accumulated averages do. - + Flux Calculation Grid @@ -564,7 +564,7 @@ equations:: Fa = fl_a*Fal_a + fo_a*Fao_a + fi_a*Fai_a Fo = fo_o*Fao_o + fi_o*Fio_o -The above equations indicate that the land fraction on the atmosphere grid is the +The above equations indicate that the land fraction on the atmosphere grid is the complement of the mapped ocean mask and is static. The ice and ocean fractions are determined from the ice model and are dynamic. Both can be mapped to the atmosphere grid. Finally, the atmosphere flux is a three-way merge of the land, ocean, and @@ -572,7 +572,7 @@ ice terms on the atmosphere grid while the ocean flux is a two-way merge of the atmosphere and ice terms on the ocean grid. When the atmosphere/ocean and atmosphere/ice fluxes are both computed on the same -grid, at the same frequency, and both are mapped to the atmosphere grid, conservative +grid, at the same frequency, and both are mapped to the atmosphere grid, conservative mapping and merging is relatively straight-forward:: fo_a = mapo2a(fo_o) @@ -588,15 +588,15 @@ and everything conserves relatively directly:: fi_a*Fai_a = fi_o*Fai_o When the atmosphere/ice fluxes are computed on the ocean grid while -the atmosphere/ocean fluxes are computed on the atmosphere grid, +the atmosphere/ocean fluxes are computed on the atmosphere grid, extra care is needed with regard to fractions and conservation. In this case:: fo_a = mapo2a(fo_o) Fao_o = mapa2o(fo_a*Fao_a)/mapa2o(fo_a) fi_a = mapo2a(fi_o) Fai_a = mapo2a(fi_o*Fai_o)/fi_a - -fo_o, fi_o, Fai_o, and Fao_a are specified and Fao_o has to be computed. The most + +fo_o, fi_o, Fai_o, and Fao_a are specified and Fao_o has to be computed. The most important point here is that during the ocean merge, the mapped ocean fraction on the atmosphere grid is used so:: diff --git a/doc/source/prep.rst b/doc/source/prep.rst index 07595cb45..e75bf33a7 100644 --- a/doc/source/prep.rst +++ b/doc/source/prep.rst @@ -6,20 +6,20 @@ The following modules comprise the "prep phase" CMEPS code: -**med_phases_prep_atm_mod.F90**: prepares the mediator export state to the atmosphere component +**med_phases_prep_atm_mod.F90**: prepares the mediator export state to the atmosphere component + +**med_phases_prep_ice_mod.F90**: prepares the mediator export state to the sea-ice component + +**med_phases_prep_glc_mod.F90**: prepares the mediator export state to the land-ice component -**med_phases_prep_ice_mod.F90**: prepares the mediator export state to the sea-ice component - -**med_phases_prep_glc_mod.F90**: prepares the mediator export state to the land-ice component - **med_phases_prep_lnd_mod.F90**: prepares the mediator export state to the land component - + **med_phases_prep_ocn_mod.F90**: prepares the mediator export state to the ocean component **med_phases_prep_rof_mod.F90**: prepares the mediator export state to the river component - + **med_phases_prep_wav_mod.F90**: prepares the mediator export state to the wave component - + Each prep phase module has several sections: @@ -71,8 +71,7 @@ Each prep phase module has several sections: * ``med_phases_prep_ocn``: * computation of net shortwave that is sent to the ocean. - * apply precipitation fractor to scale rain and snow sent to ocean (for CESM) - * carry out custom merges for NEMS coupling modes (for NEMS) + * apply precipitation fractor to scale rain and snow sent to ocean * ``med_phases_prep_rof``: diff --git a/mediator/CMakeLists.txt b/mediator/CMakeLists.txt index 84f62675e..9630b5e23 100644 --- a/mediator/CMakeLists.txt +++ b/mediator/CMakeLists.txt @@ -5,7 +5,7 @@ set(SRCFILES esmFldsExchange_cesm_mod.F90 med_fraction_mod.F90 med_phases_restart_mod.F90 esmFldsExchange_hafs_mod.F90 med_internalstate_mod.F90 med_phases_aofluxes_mod.F90 med_phases_prep_lnd_mod.F90 med_time_mod.F90 - esmFldsExchange_nems_mod.F90 med_io_mod.F90 + esmFldsExchange_ufs_mod.F90 med_io_mod.F90 med_phases_history_mod.F90 med_phases_prep_ocn_mod.F90 med_utils_mod.F90 esmFlds.F90 med_kind_mod.F90 med_phases_prep_rof_mod.F90 diff --git a/mediator/ESMFConvenienceMacros.h b/mediator/ESMFConvenienceMacros.h index 092760585..660583eaa 100644 --- a/mediator/ESMFConvenienceMacros.h +++ b/mediator/ESMFConvenienceMacros.h @@ -2,6 +2,6 @@ // ----------- ERROR handling macros ------------------------------------------ #endif -#define ESMF_ERR_ABORT(rc) if (ESMF_LogFoundError(rc, msg="Aborting NEMS", line=__LINE__, file=__FILE__)) call ESMF_Finalize(endflag=ESMF_END_ABORT) +#define ESMF_ERR_ABORT(rc) if (ESMF_LogFoundError(rc, msg="Aborting UFS", line=__LINE__, file=__FILE__)) call ESMF_Finalize(endflag=ESMF_END_ABORT) #define ESMF_ERR_RETURN(rc,rcOut) if (ESMF_LogFoundError(rc, msg="Breaking out of subroutine", line=__LINE__, file=__FILE__, rcToReturn=rcOut)) return diff --git a/mediator/ESMFVersionDefine.h b/mediator/ESMFVersionDefine.h index 0038c9db1..b9a5054c8 100644 --- a/mediator/ESMFVersionDefine.h +++ b/mediator/ESMFVersionDefine.h @@ -1,9 +1,8 @@ #if 0 // // Make this header file available as ESMFVersionDefine.h in order to build -// NEMS against an ESMF installation that contains a reference level NUOPC Layer. +// UFS against an ESMF installation that contains a reference level NUOPC Layer. // #endif #include "./ESMFConvenienceMacros.h" - diff --git a/mediator/Makefile b/mediator/Makefile index 126d040bd..990fe58eb 100644 --- a/mediator/Makefile +++ b/mediator/Makefile @@ -34,12 +34,12 @@ med_kind_mod.o : med_constants_mod.o : med_kind_mod.o esmFlds.o : med_kind_mod.o esmFldsExchange_cesm_mod.o : med_kind_mod.o med_methods_mod.o esmFlds.o med_internalstate_mod.o med_utils_mod.o -esmFldsExchange_nems_mod.o : med_kind_mod.o med_methods_mod.o esmFlds.o med_internalstate_mod.o med_utils_mod.o +esmFldsExchange_ufs_mod.o : med_kind_mod.o med_methods_mod.o esmFlds.o med_internalstate_mod.o med_utils_mod.o esmFldsExchange_hafs_mod.o : med_kind_mod.o med_methods_mod.o esmFlds.o med_internalstate_mod.o med_utils_mod.o med.o : med_kind_mod.o med_phases_profile_mod.o med_utils_mod.o med_phases_prep_rof_mod.o med_phases_aofluxes_mod.o \ med_phases_prep_ice_mod.o med_fraction_mod.o med_map_mod.o med_constants_mod.o med_phases_prep_wav_mod.o \ med_phases_prep_lnd_mod.o med_phases_history_mod.o med_phases_ocnalb_mod.o med_phases_restart_mod.o \ - med_time_mod.o med_internalstate_mod.o med_phases_prep_atm_mod.o esmFldsExchange_cesm_mod.o esmFldsExchange_nems_mod.o \ + med_time_mod.o med_internalstate_mod.o med_phases_prep_atm_mod.o esmFldsExchange_cesm_mod.o esmFldsExchange_ufs_mod.o \ esmFldsExchange_hafs_mod.o med_phases_prep_glc_mod.o esmFlds.o med_io_mod.o med_methods_mod.o med_phases_prep_ocn_mod.o \ med_phases_post_atm_mod.o med_phases_post_ice_mod.o med_phases_post_lnd_mod.o med_phases_post_glc_mod.o med_phases_post_rof_mod.o \ med_phases_post_wav_mod.o diff --git a/mediator/esmFldsExchange_cesm_mod.F90 b/mediator/esmFldsExchange_cesm_mod.F90 index a2c4fe435..c7cee8d98 100644 --- a/mediator/esmFldsExchange_cesm_mod.F90 +++ b/mediator/esmFldsExchange_cesm_mod.F90 @@ -276,6 +276,7 @@ subroutine esmFldsExchange_cesm(gcomp, phase, rc) call addfld_from(compatm, 'Sa_shum') call addfld_from(compatm, 'Sa_ptem') call addfld_from(compatm, 'Sa_dens') + call addfld_from(compatm, 'Faxa_rainc') if (flds_wiso) then call addfld_from(compatm, 'Sa_shum_wiso') end if @@ -288,6 +289,7 @@ subroutine esmFldsExchange_cesm(gcomp, phase, rc) call addmap_from(compatm, 'Sa_u' , compocn, mappatch, 'one', atm2ocn_map) call addmap_from(compatm, 'Sa_v' , compocn, mappatch, 'one', atm2ocn_map) end if + call addmap_from(compatm, 'Faxa_rainc', compocn, mapconsf, 'one', atm2ocn_map) call addmap_from(compatm, 'Sa_z' , compocn, mapbilnr, 'one', atm2ocn_map) call addmap_from(compatm, 'Sa_tbot', compocn, mapbilnr, 'one', atm2ocn_map) call addmap_from(compatm, 'Sa_pbot', compocn, mapbilnr, 'one', atm2ocn_map) @@ -1365,6 +1367,24 @@ subroutine esmFldsExchange_cesm(gcomp, phase, rc) end if end if + ! --------------------------------------------------------------------- + ! to atm: unmerged ugust_out from ocn + ! --------------------------------------------------------------------- + if (phase == 'advertise') then + call addfld_aoflux('So_ugustOut') + call addfld_to(compatm, 'So_ugustOut') + else + if ( fldchk(is_local%wrap%FBexp(compatm), 'So_ugustOut', rc=rc)) then + if (fldchk(is_local%wrap%FBMed_aoflux_o, 'So_ugustOut', rc=rc)) then + if (trim(is_local%wrap%aoflux_grid) == 'ogrid') then + call addmap_aoflux('So_ugustOut', compatm, mapconsf, 'ofrac', ocn2atm_map) + end if + call addmrg_to(compatm , 'So_ugustOut', & + mrg_from=compmed, mrg_fld='So_ugustOut', mrg_type='merge', mrg_fracname='ofrac') + end if + end if + end if + ! --------------------------------------------------------------------- ! to atm: surface snow depth from ice (needed for cam) ! to atm: mean ice volume per unit area from ice diff --git a/mediator/esmFldsExchange_nems_mod.F90 b/mediator/esmFldsExchange_ufs_mod.F90 similarity index 98% rename from mediator/esmFldsExchange_nems_mod.F90 rename to mediator/esmFldsExchange_ufs_mod.F90 index b5d3fbf8f..d7367172c 100644 --- a/mediator/esmFldsExchange_nems_mod.F90 +++ b/mediator/esmFldsExchange_ufs_mod.F90 @@ -1,4 +1,4 @@ -module esmFldsExchange_nems_mod +module esmFldsExchange_ufs_mod !--------------------------------------------------------------------- ! This is a mediator specific routine that determines ALL possible @@ -9,7 +9,7 @@ module esmFldsExchange_nems_mod implicit none public - public :: esmFldsExchange_nems + public :: esmFldsExchange_ufs character(*), parameter :: u_FILE_u = & __FILE__ @@ -18,7 +18,7 @@ module esmFldsExchange_nems_mod contains !================================================================================ - subroutine esmFldsExchange_nems(gcomp, phase, rc) + subroutine esmFldsExchange_ufs(gcomp, phase, rc) use ESMF use NUOPC @@ -54,7 +54,7 @@ subroutine esmFldsExchange_nems(gcomp, phase, rc) character(len=CL) :: cvalue character(len=CS) :: fldname character(len=CS), allocatable :: flds(:), oflds(:), aflds(:), iflds(:) - character(len=*) , parameter :: subname='(esmFldsExchange_nems)' + character(len=*) , parameter :: subname='(esmFldsExchange_ufs)' !-------------------------------------- rc = ESMF_SUCCESS @@ -68,7 +68,7 @@ subroutine esmFldsExchange_nems(gcomp, phase, rc) if (chkerr(rc,__LINE__,u_FILE_u)) return ! Set maptype according to coupling_mode - if (trim(coupling_mode) == 'nems_orig' .or. trim(coupling_mode) == 'nems_orig_data') then + if (trim(coupling_mode) == 'ufs.nfrac' .or. trim(coupling_mode) == 'ufs.nfrac.aoflux') then maptype = mapnstod_consf else maptype = mapconsf @@ -76,7 +76,7 @@ subroutine esmFldsExchange_nems(gcomp, phase, rc) write(msgString,'(A,i6,A)') trim(subname)//': maptype is ',maptype,', '//mapnames(maptype) call ESMF_LogWrite(trim(msgString), ESMF_LOGMSG_INFO) - if (trim(coupling_mode) == 'nems_orig_data' .or. trim(coupling_mode) == 'nems_frac_aoflux') then + if (trim(coupling_mode) == 'ufs.nfrac.aoflux' .or. trim(coupling_mode) == 'ufs.frac.aoflux') then med_aoflux_to_ocn = .true. else med_aoflux_to_ocn = .false. @@ -721,7 +721,7 @@ subroutine esmFldsExchange_nems(gcomp, phase, rc) !===================================================================== ! to lnd - states and fluxes from atm - if ( trim(coupling_mode) == 'nems_orig_data') then + if ( trim(coupling_mode) == 'ufs.nfrac.aoflux') then allocate(flds(21)) flds = (/'Sa_z ', 'Sa_topo ', 'Sa_tbot ', 'Sa_pbot ', & 'Sa_shum ', 'Sa_u ', 'Sa_v ', 'Faxa_lwdn ', & @@ -754,6 +754,6 @@ subroutine esmFldsExchange_nems(gcomp, phase, rc) end do deallocate(flds) - end subroutine esmFldsExchange_nems + end subroutine esmFldsExchange_ufs -end module esmFldsExchange_nems_mod +end module esmFldsExchange_ufs_mod diff --git a/mediator/fd_cesm.yaml b/mediator/fd_cesm.yaml index c09a63c58..eaef1dc78 100644 --- a/mediator/fd_cesm.yaml +++ b/mediator/fd_cesm.yaml @@ -487,6 +487,10 @@ canonical_units: m description: atmosphere import # + - standard_name: So_ugustOut + canonical_units: m/s + description: atmosphere import + # #----------------------------------- # section: land-ice export # Note that the fields sent from glc->med do NOT have elevation classes, diff --git a/mediator/med.F90 b/mediator/med.F90 index 9bb936f60..98021c647 100644 --- a/mediator/med.F90 +++ b/mediator/med.F90 @@ -48,7 +48,7 @@ module MED use esmFlds , only : med_fldList_GetNumFlds, med_fldList_GetFldNames, med_fldList_GetFldInfo use esmFlds , only : med_fldList_Document_Mapping, med_fldList_Document_Merging use esmFlds , only : med_fldList_GetfldListFr, med_fldList_GetfldListTo, med_fldList_Realize - use esmFldsExchange_nems_mod , only : esmFldsExchange_nems + use esmFldsExchange_ufs_mod , only : esmFldsExchange_ufs use esmFldsExchange_cesm_mod , only : esmFldsExchange_cesm use esmFldsExchange_hafs_mod , only : esmFldsExchange_hafs use med_phases_profile_mod , only : med_phases_profile_finalize @@ -816,8 +816,8 @@ subroutine AdvertiseFields(gcomp, importState, exportState, clock, rc) if (trim(coupling_mode) == 'cesm') then call esmFldsExchange_cesm(gcomp, phase='advertise', rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - else if (trim(coupling_mode(1:4)) == 'nems') then - call esmFldsExchange_nems(gcomp, phase='advertise', rc=rc) + else if (trim(coupling_mode(1:3)) == 'ufs') then + call esmFldsExchange_ufs(gcomp, phase='advertise', rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return else if (trim(coupling_mode(1:4)) == 'hafs') then call esmFldsExchange_hafs(gcomp, phase='advertise', rc=rc) @@ -1802,8 +1802,8 @@ subroutine DataInitialize(gcomp, rc) if (trim(coupling_mode) == 'cesm') then call esmFldsExchange_cesm(gcomp, phase='initialize', rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - else if (trim(coupling_mode(1:4)) == 'nems') then - call esmFldsExchange_nems(gcomp, phase='initialize', rc=rc) + else if (trim(coupling_mode(1:3)) == 'ufs') then + call esmFldsExchange_ufs(gcomp, phase='initialize', rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return else if (trim(coupling_mode) == 'hafs') then call esmFldsExchange_hafs(gcomp, phase='initialize', rc=rc) diff --git a/mediator/med_fraction_mod.F90 b/mediator/med_fraction_mod.F90 index 7fe0315b6..2f7d43041 100644 --- a/mediator/med_fraction_mod.F90 +++ b/mediator/med_fraction_mod.F90 @@ -293,7 +293,7 @@ subroutine med_fraction_init(gcomp, rc) ! If ice and atm are on the same mesh - a redist route handle has already been created maptype = mapfcopy else - if (trim(coupling_mode) == 'nems_orig' ) then + if (trim(coupling_mode(1:9)) == 'ufs.nfrac' ) then maptype = mapnstod_consd else maptype = mapconsd @@ -345,7 +345,7 @@ subroutine med_fraction_init(gcomp, rc) ! If ocn and atm are on the same mesh - a redist route handle has already been created maptype = mapfcopy else - if (trim(coupling_mode) == 'nems_orig' ) then + if (trim(coupling_mode(1:9)) == 'ufs.nfrac' ) then maptype = mapnstod_consd else maptype = mapconsd @@ -756,7 +756,7 @@ subroutine med_fraction_set(gcomp, rc) call t_startf('MED:'//trim(subname)//' fbfrac(compatm)') ! Determine maptype - if (trim(coupling_mode) == 'nems_orig' ) then + if (trim(coupling_mode(1:9)) == 'ufs.nfrac' ) then maptype = mapnstod_consd else if (med_map_RH_is_created(is_local%wrap%RH(compice,compatm,:),mapfcopy, rc=rc)) then diff --git a/mediator/med_internalstate_mod.F90 b/mediator/med_internalstate_mod.F90 index 66e2eb1db..fbe4617cf 100644 --- a/mediator/med_internalstate_mod.F90 +++ b/mediator/med_internalstate_mod.F90 @@ -47,7 +47,7 @@ module med_internalstate_mod character(len=CS), public :: glc_name = '' ! Coupling mode - character(len=CS), public :: coupling_mode ! valid values are [cesm,nems_orig,nems_frac,nems_orig_data,hafs,nems_frac_aoflux,nems_frac_aoflux_sbs] + character(len=CS), public :: coupling_mode ! valid values are [cesm,ufs.nfrac,ufs.frac,ufs.nfrac.aoflux,ufs.frac.aoflux,hafs] ! Atmosphere-ocean flux algorithm character(len=CS), public :: aoflux_code ! valid values are [cesm,ccpp] @@ -584,7 +584,7 @@ subroutine med_internalstate_defaultmasks(gcomp, rc) if (is_local%wrap%comp_present(compocn)) defaultMasks(compocn,:) = 0 if (is_local%wrap%comp_present(compice)) defaultMasks(compice,:) = 0 if (is_local%wrap%comp_present(compwav)) defaultMasks(compwav,:) = 0 - if ( trim(coupling_mode(1:4)) == 'nems') then + if ( trim(coupling_mode(1:3)) == 'ufs') then if (is_local%wrap%comp_present(compatm)) defaultMasks(compatm,:) = 1 endif if ( trim(coupling_mode) == 'hafs') then diff --git a/mediator/med_map_mod.F90 b/mediator/med_map_mod.F90 index 54bcbb154..0df18a770 100644 --- a/mediator/med_map_mod.F90 +++ b/mediator/med_map_mod.F90 @@ -408,7 +408,7 @@ subroutine med_map_routehandles_initfrom_field(n1, n2, fldsrc, flddst, mapindex, dstMaskValue = ispval_mask endif end if - if (trim(coupling_mode(1:4)) == 'nems') then + if (trim(coupling_mode(1:3)) == 'ufs') then if (n1 == compatm .and. n2 == complnd) then srcMaskValue = ispval_mask dstMaskValue = ispval_mask @@ -424,7 +424,7 @@ subroutine med_map_routehandles_initfrom_field(n1, n2, fldsrc, flddst, mapindex, call ESMF_LogWrite(trim(string), ESMF_LOGMSG_INFO) polemethod=ESMF_POLEMETHOD_ALLAVG - if (trim(coupling_mode) == 'cesm' .or. trim(coupling_mode(1:4)) == 'nems') then + if (trim(coupling_mode) == 'cesm' .or. trim(coupling_mode(1:3)) == 'ufs') then if (n1 == compwav .or. n2 == compwav) then polemethod = ESMF_POLEMETHOD_NONE ! todo: remove this when ESMF tripolar mapping fix is in place. endif diff --git a/mediator/med_phases_aofluxes_mod.F90 b/mediator/med_phases_aofluxes_mod.F90 index 48055e92e..cc62bbd36 100644 --- a/mediator/med_phases_aofluxes_mod.F90 +++ b/mediator/med_phases_aofluxes_mod.F90 @@ -78,6 +78,7 @@ module med_phases_aofluxes_mod logical :: compute_atm_dens logical :: compute_atm_thbot integer :: ocn_surface_flux_scheme ! use case + logical :: add_gusts character(len=CS), pointer :: fldnames_ocn_in(:) character(len=CS), pointer :: fldnames_atm_in(:) @@ -125,6 +126,7 @@ module med_phases_aofluxes_mod real(R8) , pointer :: shum_HDO (:) => null() ! atm HDO tracer real(R8) , pointer :: shum_18O (:) => null() ! atm H218O tracer real(R8) , pointer :: lwdn (:) => null() ! atm downward longwave heat flux + real(R8) , pointer :: rainc (:) => null() ! convective rain flux ! local size and computational mask and area: on aoflux grid integer :: lsize ! local size integer , pointer :: mask (:) => null() ! integer ocn domain mask: 0 <=> inactive cell @@ -146,6 +148,7 @@ module med_phases_aofluxes_mod real(R8) , pointer :: qref (:) => null() ! diagnostic: 2m ref Q real(R8) , pointer :: u10 (:) => null() ! diagnostic: 10m wind speed real(R8) , pointer :: duu10n (:) => null() ! diagnostic: 10m wind speed squared + real(R8) , pointer :: ugust_out (:) => null() ! diagnostic: gust wind added real(R8) , pointer :: ustar (:) => null() ! saved ustar real(R8) , pointer :: re (:) => null() ! saved re real(R8) , pointer :: ssq (:) => null() ! saved sq @@ -402,6 +405,14 @@ subroutine med_aofluxes_init(gcomp, aoflux_in, aoflux_out, rc) end if #endif + call NUOPC_CompAttributeGet(gcomp, name='add_gusts', value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + if (isPresent .and. isSet) then + read(cvalue,*) add_gusts + else + add_gusts = .false. + end if + ! bottom level potential temperature and/or botom level density ! will need to be computed if not received from the atm if (FB_fldchk(is_local%Wrap%FBImp(Compatm,Compatm), 'Sa_ptem', rc=rc)) then @@ -1024,7 +1035,7 @@ subroutine med_aofluxes_update(gcomp, aoflux_in, aoflux_out, rc) end if if (compute_atm_dens) then if (trim(aoflux_code) == 'ccpp' .and. & - (trim(coupling_mode) == 'nems_frac_aoflux' .or. trim(coupling_mode) == 'nems_frac_aoflux_sbs')) then + (trim(coupling_mode) == 'ufs.frac.aoflux')) then ! Add limiting factor to humidity to be consistent with UFS aoflux calculation do n = 1,aoflux_in%lsize if (aoflux_in%mask(n) /= 0.0_r8) then @@ -1052,6 +1063,7 @@ subroutine med_aofluxes_update(gcomp, aoflux_in, aoflux_out, rc) call flux_atmocn (logunit=logunit, & nMax=aoflux_in%lsize, & zbot=aoflux_in%zbot, ubot=aoflux_in%ubot, vbot=aoflux_in%vbot, thbot=aoflux_in%thbot, qbot=aoflux_in%shum, & + rainc=aoflux_in%rainc, & s16O=aoflux_in%shum_16O, sHDO=aoflux_in%shum_HDO, s18O=aoflux_in%shum_18O, rbot=aoflux_in%dens, & tbot=aoflux_in%tbot, us=aoflux_in%uocn, vs=aoflux_in%vocn, pslv=aoflux_in%psfc, ts=aoflux_in%tocn, & mask=aoflux_in%mask, seq_flux_atmocn_minwind=0.5_r8, & @@ -1060,7 +1072,10 @@ subroutine med_aofluxes_update(gcomp, aoflux_in, aoflux_out, rc) evap=aoflux_out%evap, evap_16O=aoflux_out%evap_16O, evap_HDO=aoflux_out%evap_HDO, evap_18O=aoflux_out%evap_18O, & taux=aoflux_out%taux, tauy=aoflux_out%tauy, tref=aoflux_out%tref, qref=aoflux_out%qref, & ocn_surface_flux_scheme=ocn_surface_flux_scheme, & - duu10n=aoflux_out%duu10n, ustar_sv=aoflux_out%ustar, re_sv=aoflux_out%re, ssq_sv=aoflux_out%ssq, & + add_gusts=add_gusts, & + duu10n=aoflux_out%duu10n, & + ugust_out = aoflux_out%ugust_out, & + ustar_sv=aoflux_out%ustar, re_sv=aoflux_out%re, ssq_sv=aoflux_out%ssq, & missval=0.0_r8) #else @@ -1084,7 +1099,8 @@ subroutine med_aofluxes_update(gcomp, aoflux_in, aoflux_out, rc) ocn_surface_flux_scheme=ocn_surface_flux_scheme, & sen=aoflux_out%sen, lat=aoflux_out%lat, lwup=aoflux_out%lwup, evap=aoflux_out%evap, & taux=aoflux_out%taux, tauy=aoflux_out%tauy, tref=aoflux_out%tref, qref=aoflux_out%qref, & - duu10n=aoflux_out%duu10n, missval=0.0_r8) + duu10n=aoflux_out%duu10n, & + missval=0.0_r8) #ifdef UFS_AOFLUX end if #endif @@ -1562,8 +1578,8 @@ subroutine set_aoflux_in_pointers(fldbun_a, fldbun_o, aoflux_in, lsize, xgrid, r lsize = size(aoflux_in%zbot) aoflux_in%lsize = lsize - ! bulk formula quantities for nems_orig_data - if (trim(coupling_mode) == 'nems_orig_data' .and. ocn_surface_flux_scheme == -1) then + ! bulk formula quantities for ufs non-frac with med-aoflux + if (trim(coupling_mode) == 'ufs.nfrac.aoflux' .and. ocn_surface_flux_scheme == -1) then call fldbun_getfldptr(fldbun_a, 'Sa_u10m', aoflux_in%ubot, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun_a, 'Sa_v10m', aoflux_in%vbot, xgrid=xgrid, rc=rc) @@ -1581,10 +1597,12 @@ subroutine set_aoflux_in_pointers(fldbun_a, fldbun_o, aoflux_in, lsize, xgrid, r if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun_a, 'Sa_shum', aoflux_in%shum, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return + call fldbun_getfldptr(fldbun_a, 'Faxa_rainc', aoflux_in%rainc, xgrid=xgrid, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return end if - ! extra fields for nems_frac_aoflux - if (trim(coupling_mode) == 'nems_frac_aoflux' .or. trim(coupling_mode) == 'nems_frac_aoflux_sbs') then + ! extra fields for ufs.frac.aoflux + if (trim(coupling_mode) == 'ufs.frac.aoflux') then call fldbun_getfldptr(fldbun_a, 'Sa_u10m', aoflux_in%usfc, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun_a, 'Sa_v10m', aoflux_in%vsfc, xgrid=xgrid, rc=rc) @@ -1618,7 +1636,7 @@ subroutine set_aoflux_in_pointers(fldbun_a, fldbun_o, aoflux_in, lsize, xgrid, r if (compute_atm_dens .or. compute_atm_thbot) then call fldbun_getfldptr(fldbun_a, 'Sa_pbot', aoflux_in%pbot, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return - if (trim(coupling_mode) == 'nems_frac_aoflux' .or. trim(coupling_mode) == 'nems_frac_aoflux_sbs') then + if (trim(coupling_mode) == 'ufs.frac.aoflux') then call fldbun_getfldptr(fldbun_a, 'Sa_pslv', aoflux_in%psfc, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return end if @@ -1692,6 +1710,8 @@ subroutine set_aoflux_out_pointers(fldbun, lsize, aoflux_out, xgrid, rc) if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun, 'So_duu10n', aoflux_out%duu10n, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return + call fldbun_getfldptr(fldbun, 'So_ugustOut', aoflux_out%ugust_out, xgrid=xgrid, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun, 'Faox_taux', aoflux_out%taux, xgrid=xgrid, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return call fldbun_getfldptr(fldbun, 'Faox_tauy', aoflux_out%tauy, xgrid=xgrid, rc=rc) diff --git a/mediator/med_phases_prep_atm_mod.F90 b/mediator/med_phases_prep_atm_mod.F90 index 01d1a52d0..abb6b7d5b 100644 --- a/mediator/med_phases_prep_atm_mod.F90 +++ b/mediator/med_phases_prep_atm_mod.F90 @@ -115,8 +115,7 @@ subroutine med_phases_prep_atm(gcomp, rc) !--- map atm/ocn fluxes from ocn to atm grid if appropriate !--------------------------------------- if (trim(coupling_mode) == 'cesm' .or. & - trim(coupling_mode) == 'nems_frac_aoflux' .or. & - trim(coupling_mode) == 'nems_frac_aoflux_sbs') then + trim(coupling_mode) == 'ufs.frac.aoflux') then if (is_local%wrap%aoflux_grid == 'ogrid') then call med_aofluxes_map_ogrid2agrid_output(gcomp, rc) if (chkerr(rc,__LINE__,u_FILE_u)) return