diff --git a/cime_config/config_grids.xml b/cime_config/config_grids.xml
index dece4eb32abc..a2ef4adabf2e 100755
--- a/cime_config/config_grids.xml
+++ b/cime_config/config_grids.xml
@@ -3944,7 +3944,8 @@
OCN2WAV_SMAPNAME
WAV2OCN_SMAPNAME
WAV2OCN_FMAPNAME
- ICE2WAV_SMAPNAME
+ ICE2WAV_SMAPNAME
+ WAV2ICE_SMAPNAME
ROF2OCN_LIQ_RMAPNAME
ROF2OCN_ICE_RMAPNAME
@@ -5591,6 +5592,7 @@
+
@@ -5598,6 +5600,7 @@
+
@@ -5605,6 +5608,7 @@
+
diff --git a/cime_config/machines/cmake_macros/chrysalis_oneapi-ifx.cmake b/cime_config/machines/cmake_macros/chrysalis_oneapi-ifx.cmake
index d4125f5dd355..674233ee559f 100644
--- a/cime_config/machines/cmake_macros/chrysalis_oneapi-ifx.cmake
+++ b/cime_config/machines/cmake_macros/chrysalis_oneapi-ifx.cmake
@@ -1,3 +1,11 @@
string(APPEND CMAKE_EXE_LINKER_FLAGS " -L/gpfs/fs1/soft/chrysalis/spack-latest/opt/spack/linux-rhel8-x86_64/gcc-8.5.0/gcc-11.3.0-jkpmtgq/lib64 -lstdc++")
+# Workaround for oneapi ifx v2025.2.0 (and earlier than 2025.3.0):
+# use -mllvm -disable-hir-temp-cleanup to avoid ICE
+# See: https://github.com/argonne-lcf/AuroraBugTracking/issues/64
+if (CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM"
+ AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "2025.3.0")
+ string(APPEND CMAKE_Fortran_FLAGS " -mllvm -disable-hir-temp-cleanup")
+endif()
+
diff --git a/components/eam/src/physics/cam/bfb_math.inc b/components/eam/src/physics/cam/bfb_math.inc
index 3efd6ae2adf3..d9768011750f 100644
--- a/components/eam/src/physics/cam/bfb_math.inc
+++ b/components/eam/src/physics/cam/bfb_math.inc
@@ -27,6 +27,7 @@
# define bfb_expm1(val) (exp(val) - 1)
# define bfb_tanh(val) tanh(val)
# define bfb_cos(val) cos(val)
+# define bfb_sin(val) sin(val)
# define bfb_sqrt(val) sqrt(val)
# define bfb_erf(val) erf(val)
#else
@@ -40,6 +41,7 @@
# define bfb_expm1(val) scream_expm1(val)
# define bfb_tanh(val) scream_tanh(val)
# define bfb_cos(val) scream_cos(val)
+# define bfb_sin(val) scream_sin(val)
# define bfb_erf(val) scream_erf(val)
#endif
diff --git a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90 b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90
index d2e035912ab6..d7f38a7cb047 100644
--- a/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90
+++ b/components/eam/src/physics/cam/zm/zm_conv_mcsp.F90
@@ -1,3 +1,5 @@
+#include "bfb_math.inc"
+
module zm_conv_mcsp
!----------------------------------------------------------------------------
! Purpose: methods for mesoscale coherent structure parameterization (MCSP)
@@ -29,6 +31,7 @@ module zm_conv_mcsp
!----------------------------------------------------------------------------
#ifdef SCREAM_CONFIG_IS_CMAKE
use zm_eamxx_bridge_params, only: r8
+ use physics_share_f2c, only: scream_sin, scream_cos
#else
use shr_kind_mod, only: r8=>shr_kind_r8
use cam_abortutils, only: endrun
@@ -270,10 +273,10 @@ subroutine zm_conv_mcsp_tend( pcols, ncol, pver, pverp, &
pdepth_total = state_pint(i,pver+1) - state_pmid(i,jctop(i))
! specify the assumed vertical structure
- if (do_mcsp_t) mcsp_tend_s(i,k) = -1*zm_param%mcsp_t_coeff * sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total))
- if (do_mcsp_q) mcsp_tend_q(i,k) = -1*zm_param%mcsp_q_coeff * sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total))
- if (do_mcsp_u) mcsp_tend_u(i,k) = zm_param%mcsp_u_coeff * (cos(zm_const%pi*(pdepth_mid_k/pdepth_total)))
- if (do_mcsp_v) mcsp_tend_v(i,k) = zm_param%mcsp_v_coeff * (cos(zm_const%pi*(pdepth_mid_k/pdepth_total)))
+ if (do_mcsp_t) mcsp_tend_s(i,k) = -1*zm_param%mcsp_t_coeff * bfb_sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total))
+ if (do_mcsp_q) mcsp_tend_q(i,k) = -1*zm_param%mcsp_q_coeff * bfb_sin(2.0_r8*zm_const%pi*(pdepth_mid_k/pdepth_total))
+ if (do_mcsp_u) mcsp_tend_u(i,k) = zm_param%mcsp_u_coeff * (bfb_cos(zm_const%pi*(pdepth_mid_k/pdepth_total)))
+ if (do_mcsp_v) mcsp_tend_v(i,k) = zm_param%mcsp_v_coeff * (bfb_cos(zm_const%pi*(pdepth_mid_k/pdepth_total)))
! scale the vertical structure by the ZM heating/drying tendencies
if (do_mcsp_t) mcsp_tend_s(i,k) = zm_avg_tend_s(i) * mcsp_tend_s(i,k)
diff --git a/components/eamxx/cmake/tpls/CsmShare.cmake b/components/eamxx/cmake/tpls/CsmShare.cmake
index d9e667a4e591..6a379bc04e5e 100644
--- a/components/eamxx/cmake/tpls/CsmShare.cmake
+++ b/components/eamxx/cmake/tpls/CsmShare.cmake
@@ -58,6 +58,7 @@ macro (CreateCsmShareTarget)
target_compile_definitions(csm_share PUBLIC
$<$,$>:CPRGNU>
$<$,$>:CPRINTEL>
+ $<$,$>:CPRINTEL>
$<$,$>:CPRCRAY>
$<$,$>:CPRCRAY>
EAMXX_STANDALONE)
diff --git a/components/eamxx/docs/user/model_configuration.md b/components/eamxx/docs/user/model_configuration.md
index 685ed7aaf6db..5599994c6eee 100644
--- a/components/eamxx/docs/user/model_configuration.md
+++ b/components/eamxx/docs/user/model_configuration.md
@@ -361,7 +361,8 @@ EAMxx allows the user to configure the desired model output via
with each YAML file associated to a different output stream (i.e., a file).
In order to add an output stream,
one needs to run `atmchange output_yaml_files+=/path/to/my/output/yaml`
-(more information on how to use `atmchange` can be found [here](#changing-model-inputs-atmchange)).
+(more information on how to use `atmchange` can be found in the
+[atmchange documentation](#changing-model-inputs-atmchange)).
During the `buildnml` phase of the case management system, these YAML files
will be copied into the RUNDIR/data folder.
During this process, the files will be parsed, and any CIME-related
@@ -432,6 +433,10 @@ in the run directory.
- `frequency_units`: units of the output frequency.
- Valid options are `nsteps` (the number of atmosphere time steps),
`nsecs`, `nmins`, `nhours`, `ndays`, `nmonths`, `nyears`.
+- `transpose`: optional boolean flag to enable transposed output (default: `false`).
+ - When set to `true`, all field dimensions will be reversed in the output file.
+ - For example, a field with layout `(ncol, nlev)` will be written as `(nlev, ncol)`.
+ - This can be useful for post-processing tools that expect a specific dimension ordering.
## Diagnostic output
diff --git a/components/eamxx/scripts/atm_manip.py b/components/eamxx/scripts/atm_manip.py
index b0fa829dedb2..0226a9416c0a 100755
--- a/components/eamxx/scripts/atm_manip.py
+++ b/components/eamxx/scripts/atm_manip.py
@@ -71,6 +71,96 @@ def reset_buffer():
###############################################################################
run_cmd_no_fail(f"./xmlchange {ATMCHANGE_BUFF_XML_NAME}=''")
+###############################################################################
+def get_changes_for_node(xml_root, node_name, changes):
+###############################################################################
+ """
+ Return the subset of changes from `changes` that do NOT target the given
+ XML node or any of its descendants. This is the filtering kernel used by
+ reset_node_changes and can be called directly for testing.
+
+ NOTE: `changes` is a list of already-unescaped change strings
+ (commas are NOT escaped with backslash).
+
+ >>> xml = '''
+ ...
+ ... 0
+ ... 10
+ ...
+ ... 1
+ ... 2
+ ...
+ ...
+ ... '''
+ >>> import xml.etree.ElementTree as ET
+ >>> tree = ET.fromstring(xml)
+ >>> ################ ERROR: node not found #######################
+ >>> get_changes_for_node(tree, 'nonexistent', ['foo=5'])
+ Traceback (most recent call last):
+ SystemExit: ERROR: 'nonexistent' did not match any node in the XML file
+ >>> ################ Reset leaf node (foo), keep bar change #####
+ >>> get_changes_for_node(tree, 'foo', ['foo=5', 'bar=2'])
+ ['bar=2']
+ >>> ################ Reset leaf node (foo), no matching change ##
+ >>> get_changes_for_node(tree, 'foo', ['bar=2', 'sub::child1=99'])
+ ['bar=2', 'sub::child1=99']
+ >>> ################ Reset non-leaf node (sub), removes children
+ >>> get_changes_for_node(tree, 'sub', ['sub::child1=99', 'bar=3'])
+ ['bar=3']
+ >>> ################ Reset child only, keep sibling change #######
+ >>> get_changes_for_node(tree, 'sub::child1', ['sub::child1=99', 'sub::child2=5', 'bar=3'])
+ ['sub::child2=5', 'bar=3']
+ >>> ################ Empty changes list ##########################
+ >>> get_changes_for_node(tree, 'foo', [])
+ []
+ """
+ reset_targets = get_xml_nodes(xml_root, node_name)
+ expect(len(reset_targets) > 0,
+ f"'{node_name}' did not match any node in the XML file")
+
+ if not changes:
+ return []
+
+ parent_map = create_parent_map(xml_root)
+
+ filtered_changes = []
+ for chg in changes:
+ chg_node_name, _, _, _ = parse_change(chg)
+ chg_nodes = get_xml_nodes(xml_root, chg_node_name)
+ # is_anchestor_of(A, B, ...) returns True when A == B too, so
+ # this covers both direct matches and descendant matches.
+ affects_reset = any(
+ is_anchestor_of(reset_target, chg_node, parent_map)
+ for chg_node in chg_nodes
+ for reset_target in reset_targets
+ )
+ if not affects_reset:
+ filtered_changes.append(chg)
+
+ return filtered_changes
+
+###############################################################################
+def reset_node_changes(xml_root, node_name):
+###############################################################################
+ """
+ Remove from the SCREAM_ATMCHANGE_BUFFER all changes that target
+ the given node or any of its descendants.
+ """
+ # Get current buffered changes
+ buff_str = run_cmd_no_fail(f"./xmlquery {ATMCHANGE_BUFF_XML_NAME} --value")
+ changes = []
+ for item in buff_str.split(ATMCHANGE_SEP):
+ if item.strip():
+ changes.append(item.replace(r"\,", ",").strip())
+
+ # Filter changes (also validates node_name exists in xml_root)
+ filtered_changes = get_changes_for_node(xml_root, node_name, changes)
+
+ # Reset buffer and write back the filtered changes
+ run_cmd_no_fail(f"./xmlchange {ATMCHANGE_BUFF_XML_NAME}=''")
+ if filtered_changes:
+ buffer_changes(filtered_changes)
+
###############################################################################
def get_xml_nodes(xml_root, name):
###############################################################################
diff --git a/components/eamxx/scripts/atmchange b/components/eamxx/scripts/atmchange
index b39a10c3412e..397ce10986b5 100755
--- a/components/eamxx/scripts/atmchange
+++ b/components/eamxx/scripts/atmchange
@@ -14,7 +14,7 @@ sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from eamxx_buildnml_impl import check_value, is_array_type
from eamxx_buildnml import create_raw_xml_file
-from atm_manip import atm_config_chg_impl, buffer_changes, reset_buffer, get_xml_nodes, parse_change
+from atm_manip import atm_config_chg_impl, buffer_changes, reset_buffer, reset_node_changes, get_xml_nodes, parse_change
from utils import run_cmd_no_fail, expect, GoodFormatter
# Add path to cime
@@ -47,9 +47,17 @@ def atm_config_chg(changes, reset=False, buffer_only=False):
expect(not reset, "Makes no sense for buffer_only and reset to both be on")
if reset:
- reset_buffer()
- print("All buffered atmchanges have been removed.")
hack_xml = run_cmd_no_fail("./xmlquery SCREAM_HACK_XML --value")
+ if reset is True:
+ reset_buffer()
+ print("All buffered atmchanges have been removed.")
+ else:
+ with open("namelist_scream.xml", "r") as fd:
+ tree = ET.parse(fd)
+ root = tree.getroot()
+ reset_node_changes(root, reset)
+ print(f"Buffered atmchanges for '{reset}' have been removed.")
+
if hack_xml == "TRUE":
print("SCREAM_HACK_XML is on. Removing namelist_scream.xml to force regen")
os.remove("namelist_scream.xml")
@@ -118,8 +126,10 @@ OR
parser.add_argument(
"-r", "--reset",
default=False,
- action="store_true",
- help="Forget all previous atmchanges",
+ nargs="?",
+ const=True,
+ metavar="NODE",
+ help="Forget all previous atmchanges (if NODE is given, only forget changes targeting that XML node)",
)
parser.add_argument(
diff --git a/components/eamxx/scripts/compare-nc-files b/components/eamxx/scripts/compare-nc-files
index 2cfafc3607c0..e1b785436a87 100755
--- a/components/eamxx/scripts/compare-nc-files
+++ b/components/eamxx/scripts/compare-nc-files
@@ -51,6 +51,10 @@ OR
parser.add_argument("-c","--compare",nargs='+', default=[],
help="Compare variables from src file against variables from tgt file")
+ # Allow the test to continue if variables are transposes of each other.
+ parser.add_argument("-a","--allow_transpose",action="store_true", default=False,
+ help="Whether to allow comparison between variables that are transposed")
+
return parser.parse_args(args[1:])
###############################################################################
@@ -62,7 +66,8 @@ def _main_func(description):
print (f" **** Comparing nc files **** \n"
f" src file: {pncf._src_file}\n"
f" tgt file: {pncf._tgt_file}\n"
- f" comparisons: {cmp_str}")
+ f" comparisons: {cmp_str}"
+ f" allow transpose: {pncf._allow_transpose}")
success = pncf.run()
print("Comparisons result: {}".format("SUCCESS" if success else "FAIL"))
diff --git a/components/eamxx/scripts/compare_nc_files.py b/components/eamxx/scripts/compare_nc_files.py
index 07327fa92adc..b8e4e350f2d6 100644
--- a/components/eamxx/scripts/compare_nc_files.py
+++ b/components/eamxx/scripts/compare_nc_files.py
@@ -2,7 +2,10 @@
ensure_netcdf4()
-from netCDF4 import Dataset
+from utils import _ensure_pylib_impl
+_ensure_pylib_impl("xarray")
+
+import xarray as xr
import numpy as np
import pathlib
@@ -12,7 +15,7 @@ class CompareNcFiles(object):
###############################################################################
###########################################################################
- def __init__(self,src_file,tgt_file=None,tolerance=0,compare=None):
+ def __init__(self,src_file,tgt_file=None,tolerance=0,compare=None,allow_transpose=False):
###########################################################################
self._src_file = pathlib.Path(src_file).resolve().absolute()
@@ -21,6 +24,7 @@ def __init__(self,src_file,tgt_file=None,tolerance=0,compare=None):
self._compare = compare
self._tol = tolerance
+ self._allow_transpose = allow_transpose
if tgt_file is None:
self._tgt_file = self._src_file
@@ -63,10 +67,23 @@ def get_name_and_dims(self,name_dims):
def compare_variables(self):
###########################################################################
- ds_src = Dataset(self._src_file,'r')
- ds_tgt = Dataset(self._tgt_file,'r')
+ ds_src = xr.open_dataset(self._src_file)
+ ds_tgt = xr.open_dataset(self._tgt_file)
success = True
+
+ # If no comparison is passed, compare all variables.
+ if self._compare is None or self._compare==[]:
+ self._compare = []
+ for var in ds_src.variables:
+ if var not in ds_tgt.variables:
+ print (f" Comparison failed! Variable not found.\n"
+ f" - var name: {var}\n"
+ f" - file name: {self._tgt_file}")
+ success = False
+ continue
+ self._compare.append(var+"="+var)
+
for expr in self._compare:
# Split the expression, to get the output var name
tokens = expr.split('=')
@@ -90,11 +107,11 @@ def compare_variables(self):
f" - file name: {self._tgt_file}")
success = False
continue
- lvar = ds_src.variables[lname];
- rvar = ds_tgt.variables[rname];
+ lvar = ds_src[lname];
+ rvar = ds_tgt[rname];
- lvar_rank = len(lvar.dimensions)
- rvar_rank = len(rvar.dimensions)
+ lvar_rank = len(lvar.dims)
+ rvar_rank = len(rvar.dims)
expect (len(ldims)==0 or len(ldims)==lvar_rank,
f"Invalid slice specification for {lname}.\n"
@@ -105,51 +122,36 @@ def compare_variables(self):
f" input request: ({','.join(rdims)})\n"
f" variable rank: {rvar_rank}")
-
- lslices = [[idim,slice] for idim,slice in enumerate(ldims) if slice!=":"]
- rslices = [[idim,slice] for idim,slice in enumerate(rdims) if slice!=":"]
-
- lrank = lvar_rank - len(lslices)
- rrank = rvar_rank - len(rslices)
-
- if lrank!=rrank:
- print (f" Comparison failed. Rank mismatch.\n"
- f" - input comparison: {expr}\n"
- f" - upon slicing, rank({lname}) = {lrank}\n"
- f" - upon slicing, rank({rname}) = {rrank}")
- success = False
- continue
-
- lvals = self.slice_variable(lvar,lvar[:],lslices)
- rvals = self.slice_variable(rvar,rvar[:],rslices)
-
- diff = np.abs(lvals-rvals)
- not_close = diff > (self._tol + self._tol*np.abs(rvals))
+ lslices = {lvar.dims[idim]:int(slice)-1 for idim,slice in enumerate(ldims) if slice!=":"}
+ rslices = {rvar.dims[idim]:int(slice)-1 for idim,slice in enumerate(rdims) if slice!=":"}
+ lvar_sliced = lvar.sel(lslices)
+ rvar_sliced = rvar.sel(rslices)
+ expect (set(lvar_sliced.dims) == set(rvar_sliced.dims),
+ f"Error, even when sliced these two elements do not share the same dimensionsn\n"
+ f" - left var name : {lname}\n"
+ f" - right var name : {rname}\n"
+ f" - left dimensions : {lvar_sliced.dims}\n"
+ f" - right dimensions: {rvar_sliced.dims}\n")
+
+ if self._allow_transpose:
+ rvar_sliced = rvar_sliced.transpose(*lvar_sliced.dims)
+
+ # Apply tolerance when comparing
+ diff = np.abs(lvar_sliced.data - rvar_sliced.data)
+ not_close = diff > (self._tol + self._tol * np.abs(rvar_sliced.data))
where = np.argwhere(not_close)
if where.size > 0:
- idx = where[0]
- lval = lvals[tuple(idx)]
- rval = rvals[tuple(idx)]
- print (f" Comparison failed. Values differ.\n"
- f" - input comparison: {expr}\n"
- f' - upon slicing, {lname}({tuple(idx)}) = {lval}\n'
- f' - upon slicing, {rname}({tuple(idx)}) = {rval}')
+ rse = np.sqrt((lvar_sliced.data-rvar_sliced.data)**2)
+ nonmatch_count = np.count_nonzero(not_close)
+ print (f" Comparison failed. Values differ at {nonmatch_count} out of {rse.size} locations.\n"
+ f" - input comparison: {expr}\n"
+ f' - max L2 error, {rse.max()}\n'
+ f' - max L2 location, [{",".join(map(str,(np.array(np.unravel_index(rse.argmax(),rse.shape))+1).tolist()))}]\n'
+ f' - dimensions, {lvar_sliced.dims}')
success = False
return success
- ###########################################################################
- def slice_variable(self,var,vals,slices):
- ###########################################################################
-
- if len(slices)==0:
- return vals
-
- idim, slice_idx = slices.pop(-1)
- vals = vals.take(int(slice_idx)-1,axis=int(idim))
-
- return self.slice_variable(var,vals,slices)
-
###########################################################################
def run(self):
###########################################################################
diff --git a/components/eamxx/src/control/atmosphere_driver.cpp b/components/eamxx/src/control/atmosphere_driver.cpp
index 4de53ae3266e..44dd0beaf49c 100644
--- a/components/eamxx/src/control/atmosphere_driver.cpp
+++ b/components/eamxx/src/control/atmosphere_driver.cpp
@@ -1063,7 +1063,8 @@ void AtmosphereDriver::set_initial_conditions ()
if (ic_pl.isParameter(fname)) {
// This is the case that the user provided an initialization
// for this field in the parameter file.
- if (ic_pl.isType(fname) or ic_pl.isType>(fname)) {
+ if (ic_pl.isType(fname) or ic_pl.isType(fname) or
+ ic_pl.isType>(fname)) {
// Initial condition is a constant
initialize_constant_field(fid, ic_pl);
@@ -1507,8 +1508,13 @@ initialize_constant_field(const FieldIdentifier& fid,
}
}
} else {
- const auto& value = ic_pl.get(name);
- f.deep_copy(value);
+ if (ic_pl.isType(name)) {
+ const auto& value = ic_pl.get(name);
+ f.deep_copy(value);
+ } else {
+ const auto& value = ic_pl.get(name);
+ f.deep_copy(value);
+ }
}
}
diff --git a/components/eamxx/src/dynamics/homme/CMakeLists.txt b/components/eamxx/src/dynamics/homme/CMakeLists.txt
index 8a638839f82c..0cf021428c6c 100644
--- a/components/eamxx/src/dynamics/homme/CMakeLists.txt
+++ b/components/eamxx/src/dynamics/homme/CMakeLists.txt
@@ -6,7 +6,7 @@ set (HOMME_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/homme CACHE INTERNAL "Homme bi
# If using Intel, we need to tell Homme to link against mkl rather than lapack
-if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
+if (CMAKE_CXX_COMPILER_ID MATCHES "^(Intel|IntelLLVM)$")
option (HOMME_USE_MKL "Whether to use Intel's MKL instead of blas/lapack" ON)
option (HOMME_FIND_BLASLAPACK "Whether to use system blas/lapack" OFF)
else ()
diff --git a/components/eamxx/src/physics/cosp/eamxx_cosp.cpp b/components/eamxx/src/physics/cosp/eamxx_cosp.cpp
index 6712f473533c..32e85abbf422 100644
--- a/components/eamxx/src/physics/cosp/eamxx_cosp.cpp
+++ b/components/eamxx/src/physics/cosp/eamxx_cosp.cpp
@@ -68,7 +68,7 @@ void Cosp::create_requests()
add_field("surf_radiative_T", scalar2d , K, grid_name);
//add_field("surfelev", scalar2d , m, grid_name);
//add_field("landmask", scalar2d , nondim, grid_name);
- add_field("sunlit_mask", scalar2d , nondim, grid_name);
+ add_field(FieldIdentifier("sunlit_mask", scalar2d, nondim, grid_name, DataType::IntType));
add_field("p_mid", scalar3d_mid, Pa, grid_name);
add_field("p_int", scalar3d_int, Pa, grid_name);
//add_field("height_mid", scalar3d_mid, m, grid_name);
@@ -98,10 +98,9 @@ void Cosp::create_requests()
add_field("misr_cthtau", scalar4d_cthtau, percent, grid_name, 1);
// We can allocate these now
- m_z_mid = Field(FieldIdentifier("z_mid",scalar3d_mid,m,grid_name));
- m_z_int = Field(FieldIdentifier("z_int",scalar3d_int,m,grid_name));
- m_z_mid.allocate_view();
- m_z_int.allocate_view();
+ m_z_mid = Field(FieldIdentifier("z_mid",scalar3d_mid,m,grid_name),true);
+ m_z_int = Field(FieldIdentifier("z_int",scalar3d_int,m,grid_name),true);
+ m_sunlit_real = Field(FieldIdentifier("sunlit_mask_real",scalar2d,nondim,grid_name),true);
}
// =========================================================================================
@@ -110,12 +109,35 @@ void Cosp::initialize_impl (const RunType /* run_type */)
// Set property checks for fields in this process
CospFunc::initialize(m_num_cols, m_num_subcols, m_num_levs);
+ using namespace ShortFieldTagsNames;
+
+ // Create the masks for the 4d fields
+ FieldLayout scalar4d_ctptau ( {COL,CMP,CMP},
+ {m_num_cols,m_num_tau,m_num_ctp},
+ {e2str(COL), "cosp_tau", "cosp_prs"});
+ FieldLayout scalar4d_cthtau ( {COL,CMP,CMP},
+ {m_num_cols,m_num_tau,m_num_cth},
+ {e2str(COL), "cosp_tau", "cosp_cth"});
+
+ const auto nondim = ekat::units::Units::nondimensional();
+ FieldIdentifier mctp_fid ("sunlit_mask_ctptau", scalar4d_ctptau, nondim, m_grid->name(), DataType::IntType);
+ FieldIdentifier mcth_fid ("sunlit_mask_cthtau", scalar4d_cthtau, nondim, m_grid->name(), DataType::IntType);
+ Field mctp(mctp_fid,true);
+ Field mcth(mcth_fid,true);
+ std::map masks = {
+ {"isccp_cldtot", get_field_in("sunlit_mask")},
+ {"isccp_ctptau", mctp},
+ {"modis_ctptau", mctp},
+ {"misr_cthtau", mcth},
+ };
// Set the mask field for each of the cosp computed fields
std::list vnames = {"isccp_cldtot", "isccp_ctptau", "modis_ctptau", "misr_cthtau"};
for (const auto& field_name : vnames) {
// the mask here is just the sunlit mask, so set it
- get_field_out(field_name).get_header().set_extra_data("mask_field", get_field_in("sunlit_mask"));
- get_field_out(field_name).get_header().set_may_be_filled(true);
+ auto& f = get_field_out(field_name);
+
+ f.get_header().set_extra_data("valid_mask", masks.at(field_name));
+ f.get_header().set_may_be_filled(true);
}
}
@@ -150,7 +172,6 @@ void Cosp::run_impl (const double dt)
get_field_in("qv").sync_to_host();
get_field_in("qc").sync_to_host();
get_field_in("qi").sync_to_host();
- get_field_in("sunlit_mask").sync_to_host();
get_field_in("surf_radiative_T").sync_to_host();
get_field_in("T_mid").sync_to_host();
get_field_in("p_mid").sync_to_host();
@@ -161,6 +182,8 @@ void Cosp::run_impl (const double dt)
get_field_in("dtau067").sync_to_host();
get_field_in("dtau105").sync_to_host();
+ m_sunlit_real.deep_copy(get_field_in("sunlit_mask"));
+ m_sunlit_real.sync_to_host();
// Compute z_mid
const auto T_mid_d = get_field_in("T_mid").get_view();
const auto qv_d = get_field_in("qv").get_view();
@@ -211,7 +234,7 @@ void Cosp::run_impl (const double dt)
const auto p_mid_h = get_field_in("p_mid").get_view();
const auto qc_h = get_field_in("qc").get_view();
const auto qi_h = get_field_in("qi").get_view();
- const auto sunlit_h = get_field_in("sunlit_mask").get_view();
+ const auto sunlit_h = m_sunlit_real.get_view();
const auto skt_h = get_field_in("surf_radiative_T").get_view();
const auto p_int_h = get_field_in("p_int").get_view();
const auto cldfrac_h = get_field_in("cldfrac_rad").get_view();
@@ -255,6 +278,27 @@ void Cosp::run_impl (const double dt)
get_field_out("isccp_ctptau").sync_to_dev();
get_field_out("modis_ctptau").sync_to_dev();
get_field_out("misr_cthtau").sync_to_dev();
+
+ // Update the ctptau and cthtau masks by broadcasting sunlit mask
+ const auto& sunlit = get_field_in("sunlit_mask");
+ auto& ctptau = get_field_out("isccp_ctptau").get_header().get_extra_data("valid_mask");
+ auto& cthtau = get_field_out("misr_cthtau").get_header().get_extra_data("valid_mask");
+
+ auto sunlit_v = sunlit.get_view();
+ auto ctptau_v = ctptau.get_view();
+ auto cthtau_v = cthtau.get_view();
+ auto do_ctp = KOKKOS_LAMBDA (int icol, int itau, int ictp) {
+ ctptau_v(icol,itau,ictp) = sunlit_v(icol);
+ };
+ auto do_cth = KOKKOS_LAMBDA (int icol, int itau, int icth) {
+ cthtau_v(icol,itau,icth) = sunlit_v(icol);
+ };
+ using exec_space = typename DefaultDevice::execution_space;
+ using policy_t = Kokkos::MDRangePolicy>;
+ policy_t policy_ctp({0,0,0},{m_num_cols,m_num_tau,m_num_ctp});
+ policy_t policy_cth({0,0,0},{m_num_cols,m_num_tau,m_num_cth});
+ Kokkos::parallel_for(policy_ctp,do_ctp);
+ Kokkos::parallel_for(policy_cth,do_cth);
}
}
diff --git a/components/eamxx/src/physics/cosp/eamxx_cosp.hpp b/components/eamxx/src/physics/cosp/eamxx_cosp.hpp
index f1439b2dd199..c2a0b988d96b 100644
--- a/components/eamxx/src/physics/cosp/eamxx_cosp.hpp
+++ b/components/eamxx/src/physics/cosp/eamxx_cosp.hpp
@@ -74,6 +74,7 @@ class Cosp : public AtmosphereProcess
// TODO: use atm buffer instead
Field m_z_mid;
Field m_z_int;
+ Field m_sunlit_real;
}; // class Cosp
} // namespace scream
diff --git a/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.cpp b/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.cpp
index aa73f0e5a5b9..dae53eb30f68 100644
--- a/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.cpp
+++ b/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.cpp
@@ -4,6 +4,7 @@
#include "share/property_checks/field_within_interval_check.hpp"
#include
+#include
namespace scream
{
@@ -80,7 +81,7 @@ size_t IOPForcing::requested_buffer_size_in_bytes() const
// Number of bytes needed by the WorkspaceManager passed to shoc_main
const int nlevi_packs = ekat::npack(m_num_levs+1);
const auto policy = TPF::get_default_team_policy(m_num_cols, nlevi_packs);
- const size_t wsm_bytes = WorkspaceMgr::get_total_bytes_needed(nlevi_packs, 7+m_num_tracers, policy);
+ const size_t wsm_bytes = WorkspaceMgr::get_total_bytes_needed(nlevi_packs, 8, policy);
return wsm_bytes;
}
@@ -99,7 +100,7 @@ void IOPForcing::init_buffers(const ATMBufferManager &buffer_manager)
m_buffer.wsm_data = mem;
const auto policy = TPF::get_default_team_policy(m_num_cols, nlevi_packs);
- const size_t wsm_npacks = WorkspaceMgr::get_total_bytes_needed(nlevi_packs, 7+m_num_tracers, policy)/sizeof(Pack);
+ const size_t wsm_npacks = WorkspaceMgr::get_total_bytes_needed(nlevi_packs, 8, policy)/sizeof(Pack);
mem += wsm_npacks;
size_t used_mem = (reinterpret_cast(mem) - buffer_manager.get_memory())*sizeof(Real);
@@ -138,7 +139,7 @@ void IOPForcing::initialize_impl (const RunType run_type)
// Setup WSM for internal local variables
const auto nlevi_packs = ekat::npack(m_num_levs+1);
const auto policy = TPF::get_default_team_policy(m_num_cols, nlevi_packs);
- m_workspace_mgr.setup(m_buffer.wsm_data, nlevi_packs, 7+m_num_tracers, policy);
+ m_workspace_mgr.setup(m_buffer.wsm_data, nlevi_packs, 8, policy);
// Compute field for horizontal contraction weights (1/num_global_dofs)
const auto iop_nudge_tq = m_iop_data_manager->get_params().get("iop_nudge_tq");
@@ -147,149 +148,72 @@ void IOPForcing::initialize_impl (const RunType run_type)
if (iop_nudge_tq or iop_nudge_uv) m_helper_fields.at("horiz_mean_weights").deep_copy(one_over_num_dofs);
}
// =========================================================================================
+
KOKKOS_FUNCTION
void IOPForcing::
-advance_iop_subsidence(const MemberType& team,
- const int nlevs,
- const Real dt,
- const Real ps,
- const view_1d& ref_p_mid,
- const view_1d& ref_p_int,
- const view_1d& ref_p_del,
- const view_1d& omega,
- const Workspace& workspace,
- const view_1d& u,
- const view_1d& v,
- const view_1d& T,
- const view_2d& Q)
+advance_iop_subsidence (const MemberType& team,
+ const int nlevs,
+ const Real dt,
+ const view_1d& ref_p_mid,
+ const view_1d& omega,
+ const Workspace& workspace,
+ const view_1d& u,
+ const view_1d& v,
+ const view_1d& T,
+ const view_2d& Q,
+ ekat::LinInterp& interp)
{
- constexpr Real Rair = C::Rair.value;
+ constexpr Real Rair = C::Rair.value;
constexpr Real Cpair = C::Cpair.value;
- const auto n_q_tracers = Q.extent_int(0);
- const auto nlev_packs = ekat::npack(nlevs);
+ const int n_q_tracers = Q.extent_int(0);
+ const int nlev_packs = ekat::npack(nlevs);
+
+ const int icol = team.league_rank();
- // Get some temporary views from WS
- uview_1d omega_int, delta_u, delta_v, delta_T, tmp;
- workspace.take_many_contiguous_unsafe<4>({"omega_int", "delta_u", "delta_v", "delta_T"},
- {&omega_int, &delta_u, &delta_v, &delta_T});
- const auto delta_Q_slot = workspace.take_macro_block("delta_Q", n_q_tracers);
- uview_2d delta_Q(delta_Q_slot.data(), n_q_tracers, nlev_packs);
-
- auto s_ref_p_mid = ekat::scalarize(ref_p_mid);
- auto s_omega = ekat::scalarize(omega);
- auto s_delta_u = ekat::scalarize(delta_u);
- auto s_delta_v = ekat::scalarize(delta_v);
- auto s_delta_T = ekat::scalarize(delta_T);
- auto s_delta_Q = ekat::scalarize(delta_Q);
- auto s_omega_int = ekat::scalarize(omega_int);
-
- // Compute omega on the interface grid by using a weighted average in pressure
- const int pack_begin = 1/Pack::n, pack_end = (nlevs-1)/Pack::n;
- Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pack_begin, pack_end+1), [&] (const int k){
- auto range_pack = ekat::range(k*Pack::n);
- range_pack.set(range_pack<1, 1);
- Pack ref_p_mid_k, ref_p_mid_km1, omega_k, omega_km1;
- ekat::index_and_shift<-1>(s_ref_p_mid, range_pack, ref_p_mid_k, ref_p_mid_km1);
- ekat::index_and_shift<-1>(s_omega, range_pack, omega_k, omega_km1);
-
- const auto weight = (ref_p_int(k) - ref_p_mid_km1)/(ref_p_mid_k - ref_p_mid_km1);
- omega_int(k).set(range_pack>=1 and range_pack<=nlevs-1,
- weight*omega_k + (1-weight)*omega_km1);
+ // Workspace temporaries
+ uview_1d p_dep, u_new, v_new, T_new, q_new;
+
+ // allocate temporaries from workspace
+ workspace.take_many_contiguous_unsafe<5>(
+ {"iop_p_dep","iop_u_new","iop_v_new","iop_T_new","iop_q_new"},
+ {&p_dep, &u_new, &v_new, &T_new, &q_new});
+
+ // Compute Departure Points using large-scale vertical velocity
+ Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev_packs), [&](const int k) {
+ p_dep(k) = ref_p_mid(k) - dt * omega(k);
});
- omega_int(0)[0] = 0;
- omega_int(nlevs/Pack::n)[nlevs%Pack::n] = 0;
-
- // Compute delta views for u, v, T, and Q (e.g., u(k+1) - u(k), k=0,...,nlevs-2)
- ColOps::compute_midpoint_delta(team, nlevs-1, u, delta_u);
- ColOps::compute_midpoint_delta(team, nlevs-1, v, delta_v);
- ColOps::compute_midpoint_delta(team, nlevs-1, T, delta_T);
- for (int iq=0; iq(k*Pack::n);
- const auto at_top = range_pack==0;
- const auto not_at_top = not at_top;
- const auto at_bot = range_pack==nlevs-1;
- const auto not_at_bot = not at_bot;
- const bool any_at_top = at_top.any();
- const bool any_at_bot = at_bot.any();
-
- // Get delta(k-1) packs. The range pack should not
- // contain index 0 (so that we don't attempt to access
- // k=-1 index) or index > nlevs-2 (since delta_* views
- // are size nlevs-1).
- auto range_pack_for_m1_shift = range_pack;
- range_pack_for_m1_shift.set(range_pack<1, 1);
- range_pack_for_m1_shift.set(range_pack>nlevs-2, nlevs-2);
- Pack delta_u_k, delta_u_km1,
- delta_v_k, delta_v_km1,
- delta_T_k, delta_T_km1;
- ekat::index_and_shift<-1>(s_delta_u, range_pack_for_m1_shift, delta_u_k, delta_u_km1);
- ekat::index_and_shift<-1>(s_delta_v, range_pack_for_m1_shift, delta_v_k, delta_v_km1);
- ekat::index_and_shift<-1>(s_delta_T, range_pack_for_m1_shift, delta_T_k, delta_T_km1);
-
- // At the top and bottom of the model, set the end points for
- // delta_*_k and delta_*_km1 to be the first and last entries
- // of delta_*, respectively.
- if (any_at_top) {
- delta_u_k.set(at_top, s_delta_u(0));
- delta_v_k.set(at_top, s_delta_v(0));
- delta_T_k.set(at_top, s_delta_T(0));
- }
- if (any_at_bot) {
- delta_u_km1.set(at_bot, s_delta_u(nlevs-2));
- delta_v_km1.set(at_bot, s_delta_v(nlevs-2));
- delta_T_km1.set(at_bot, s_delta_T(nlevs-2));
- }
+ interp.setup(team, ref_p_mid, p_dep);
- // Get omega_int(k+1) pack. The range pack should not
- // contain index > nlevs-1 (since omega_int is size nlevs+1).
- auto range_pack_for_p1_shift = range_pack;
- range_pack_for_p1_shift.set(range_pack>nlevs-1, nlevs-1);
- Pack omega_int_k, omega_int_kp1;
- ekat::index_and_shift<1>(s_omega_int, range_pack, omega_int_k, omega_int_kp1);
-
- const auto fac = (dt/2)/ref_p_del(k);
-
- // Update u
- u(k).update(not_at_bot, fac*omega_int_kp1*delta_u_k, -1, 1);
- u(k).update(not_at_top, fac*omega_int_k*delta_u_km1, -1, 1);
-
- // Update v
- v(k).update(not_at_bot, fac*omega_int_kp1*delta_v_k, -1, 1);
- v(k).update(not_at_top, fac*omega_int_k*delta_v_km1, -1, 1);
-
- // Before updating T, first scale using thermal
- // expansion term due to LS vertical advection
- T(k) *= 1 + (dt*Rair/Cpair)*omega(k)/ref_p_mid(k);
-
- // Update T
- T(k).update(not_at_bot, fac*omega_int_kp1*delta_T_k, -1, 1);
- T(k).update(not_at_top, fac*omega_int_k*delta_T_km1, -1, 1);
-
- // Update Q
- Pack delta_tracer_k, delta_tracer_km1;
- for (int iq=0; iq(s_delta_tracer, range_pack_for_m1_shift, delta_tracer_k, delta_tracer_km1);
- if (any_at_top) delta_tracer_k.set(at_top, s_delta_tracer(0));
- if (any_at_bot) delta_tracer_km1.set(at_bot, s_delta_tracer(nlevs-2));
-
- Q(iq, k).update(not_at_bot, fac*omega_int_kp1*delta_tracer_k, -1, 1);
- Q(iq, k).update(not_at_top, fac*omega_int_k*delta_tracer_km1, -1, 1);
- }
+ interp.lin_interp(team, ref_p_mid, p_dep, u, u_new, icol);
+ interp.lin_interp(team, ref_p_mid, p_dep, v, v_new, icol);
+ interp.lin_interp(team, ref_p_mid, p_dep, T, T_new, icol);
+ team.team_barrier();
+
+ // Thermal expansion correction + write back
+ Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev_packs), [&](const int k) {
+ T_new(k) *= 1.0 + (dt * Rair / Cpair) * omega(k) / ref_p_mid(k);
+ u(k) = u_new(k);
+ v(k) = v_new(k);
+ T(k) = T_new(k);
});
- // Release WS views
- workspace.release_macro_block(delta_Q_slot, n_q_tracers);
- workspace.release_many_contiguous<4>({&omega_int, &delta_u, &delta_v, &delta_T});
+ // Tracers
+ for (int m = 0; m < n_q_tracers; ++m) {
+ const auto q_m = Kokkos::subview(Q, m, Kokkos::ALL());
+
+ interp.lin_interp(team, ref_p_mid, p_dep, q_m, q_new, icol);
+ team.team_barrier();
+
+ Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev_packs), [&](const int k) {
+ q_m(k) = q_new(k);
+ });
+ }
+
+ workspace.release_many_contiguous<5>(
+ {&p_dep,&u_new,&v_new,&T_new,&q_new});
}
// =========================================================================================
KOKKOS_FUNCTION
@@ -407,6 +331,10 @@ void IOPForcing::run_impl (const double dt)
auto wsm = m_workspace_mgr;
auto num_levs = m_num_levs;
+ const auto ncols = m_grid->get_num_local_dofs();
+
+ ekat::LinInterp subs_interp(ncols, num_levs, num_levs);
+
// Apply IOP forcing
Kokkos::parallel_for("apply_iop_forcing", policy_iop, KOKKOS_LAMBDA (const MemberType& team) {
const int icol = team.league_rank();
@@ -437,9 +365,11 @@ void IOPForcing::run_impl (const double dt)
ColOps::compute_midpoint_delta(team, num_levs, ref_p_int, ref_p_del);
team.team_barrier();
+ auto interp_local = subs_interp;
+
if (iop_dosubsidence) {
// Compute subsidence due to large-scale forcing
- advance_iop_subsidence(team, num_levs, dt, ps_i, ref_p_mid, ref_p_int, ref_p_del, omega, ws, u_i, v_i, T_mid_i, Q_i);
+ advance_iop_subsidence(team, num_levs, dt, ref_p_mid, omega, ws, u_i, v_i, T_mid_i, Q_i, interp_local);
}
// Update T and qv according to large scale forcing as specified in IOP file.
diff --git a/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.hpp b/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.hpp
index c3dd2804a053..3604248a4dea 100644
--- a/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.hpp
+++ b/components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.hpp
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
@@ -77,16 +78,14 @@ class IOPForcing : public scream::AtmosphereProcess
static void advance_iop_subsidence(const KT::MemberType& team,
const int nlevs,
const Real dt,
- const Real ps,
const view_1d& pmid,
- const view_1d& pint,
- const view_1d& pdel,
const view_1d& omega,
const Workspace& workspace,
const view_1d& u,
const view_1d& v,
const view_1d& T,
- const view_2d& Q);
+ const view_2d& Q,
+ ekat::LinInterp& interp);
// Apply large scale forcing for temperature and water vapor as provided by the IOP file
KOKKOS_FUNCTION
diff --git a/components/eamxx/src/physics/rrtmgp/eamxx_rrtmgp_process_interface.cpp b/components/eamxx/src/physics/rrtmgp/eamxx_rrtmgp_process_interface.cpp
index 100953f758f0..5247b7325ded 100644
--- a/components/eamxx/src/physics/rrtmgp/eamxx_rrtmgp_process_interface.cpp
+++ b/components/eamxx/src/physics/rrtmgp/eamxx_rrtmgp_process_interface.cpp
@@ -209,7 +209,7 @@ void RRTMGPRadiation::create_requests() {
// 0.67 micron and 10.5 micron optical depth (needed for COSP)
add_field("dtau067" , scalar3d_mid, nondim, grid_name);
add_field("dtau105" , scalar3d_mid, nondim, grid_name);
- add_field("sunlit_mask" , scalar2d , nondim, grid_name);
+ add_field(FieldIdentifier("sunlit_mask", scalar2d, nondim, grid_name, DataType::IntType));
add_field("cldfrac_rad" , scalar3d_mid, nondim, grid_name);
// Cloud-top diagnostics following AeroCom recommendation
add_field("T_mid_at_cldtop", scalar2d, K, grid_name);
@@ -612,7 +612,7 @@ void RRTMGPRadiation::run_impl (const double dt) {
// Outputs for COSP
auto d_dtau067 = get_field_out("dtau067").get_view();
auto d_dtau105 = get_field_out("dtau105").get_view();
- auto d_sunlit = get_field_out("sunlit_mask").get_view();
+ auto d_sunlit = get_field_out("sunlit_mask").get_view();
Kokkos::deep_copy(d_dtau067,0.0);
Kokkos::deep_copy(d_dtau105,0.0);
@@ -1154,11 +1154,7 @@ void RRTMGPRadiation::run_impl (const double dt) {
d_dtau067(icol,k) = cld_tau_sw_bnd_k(i,k,idx_067_k);
d_dtau105(icol,k) = cld_tau_lw_bnd_k(i,k,idx_105_k);
});
- if (d_sw_clrsky_flux_dn(icol,0) > 0) {
- d_sunlit(icol) = 1.0;
- } else {
- d_sunlit(icol) = 0.0;
- }
+ d_sunlit(icol) = d_sw_clrsky_flux_dn(icol,0) > 0;
});
);
} // loop over chunk
diff --git a/components/eamxx/src/physics/shoc/disp/shoc_length_disp.cpp b/components/eamxx/src/physics/shoc/disp/shoc_length_disp.cpp
index 72717997cfe4..940dbb6aeba2 100644
--- a/components/eamxx/src/physics/shoc/disp/shoc_length_disp.cpp
+++ b/components/eamxx/src/physics/shoc/disp/shoc_length_disp.cpp
@@ -13,15 +13,13 @@ ::shoc_length_disp(
const Int& nlev,
const Int& nlevi,
const Scalar& length_fac,
- const bool& shoc_1p5tke,
const view_1d& dx,
const view_1d& dy,
- const view_2d& zt_grid,
- const view_2d& zi_grid,
- const view_2d& dz_zt,
- const view_2d& tke,
- const view_2d& thv,
- const view_2d& tk,
+ const view_2d& zt_grid,
+ const view_2d& zi_grid,
+ const view_2d& dz_zt,
+ const view_2d& tke,
+ const view_2d& thv,
const WorkspaceMgr& workspace_mgr,
const view_2d& brunt,
const view_2d& shoc_mix)
@@ -36,14 +34,13 @@ ::shoc_length_disp(
auto workspace = workspace_mgr.get_workspace(team);
- shoc_length(team, nlev, nlevi, length_fac, shoc_1p5tke,
+ shoc_length(team, nlev, nlevi, length_fac,
dx(i), dy(i),
ekat::subview(zt_grid, i),
ekat::subview(zi_grid, i),
ekat::subview(dz_zt, i),
ekat::subview(tke, i),
ekat::subview(thv, i),
- ekat::subview(tk, i),
workspace,
ekat::subview(brunt, i),
ekat::subview(shoc_mix, i));
diff --git a/components/eamxx/src/physics/shoc/impl/shoc_compute_shoc_mix_shoc_length_impl.hpp b/components/eamxx/src/physics/shoc/impl/shoc_compute_shoc_mix_shoc_length_impl.hpp
index b0ae96932703..b018eca60bbf 100644
--- a/components/eamxx/src/physics/shoc/impl/shoc_compute_shoc_mix_shoc_length_impl.hpp
+++ b/components/eamxx/src/physics/shoc/impl/shoc_compute_shoc_mix_shoc_length_impl.hpp
@@ -13,12 +13,9 @@ ::compute_shoc_mix_shoc_length(
const MemberType& team,
const Int& nlev,
const Scalar& length_fac,
- const bool& shoc_1p5tke,
const uview_1d& tke,
const uview_1d& brunt,
const uview_1d& zt_grid,
- const uview_1d& dz_zt,
- const uview_1d& tk,
const Scalar& l_inf,
const uview_1d& shoc_mix)
{
@@ -33,31 +30,11 @@ ::compute_shoc_mix_shoc_length(
const Pack tkes = ekat::sqrt(tke(k));
const Pack brunt2 = ekat::max(0, brunt(k));
- if (shoc_1p5tke){
- // If 1.5 TKE closure then set length scale to vertical grid spacing for
- // cells with unstable brunt vaisalla frequency. Otherwise, overwrite the length
- // scale in stable cells with the new definition.
+ shoc_mix(k) = ekat::min(maxlen,
+ sp(2.8284)*(ekat::sqrt(1/((1/(tscale*tkes*vk*zt_grid(k)))
+ + (1/(tscale*tkes*l_inf))
+ + sp(0.01)*(brunt2/tke(k)))))/length_fac);
- // Search for stable cells
- const auto stable_mask = brunt(k) > 0;
-
- // To avoid FPE when calculating sqrt(brunt), set brunt_tmp=0 in the case brunt<1.
- Pack brunt_tmp(stable_mask, brunt(k));
-
- // Define length scale for stable cells
- const auto length_tmp = ekat::sqrt(sp(0.76)*tk(k)/sp(0.1)/ekat::sqrt(brunt_tmp + sp(1.e-10)));
- // Limit the stability corrected length scale between 0.1*dz and dz
- const auto limited_len = ekat::min(dz_zt(k),ekat::max(sp(0.1)*dz_zt(k),length_tmp));
-
- // Set length scale to vertical grid if unstable, otherwise the stability adjusted value.
- shoc_mix(k).set(stable_mask, limited_len, dz_zt(k));
- } else{
- shoc_mix(k) = ekat::min(maxlen,
- sp(2.8284)*(ekat::sqrt(1/((1/(tscale*tkes*vk*zt_grid(k)))
- + (1/(tscale*tkes*l_inf))
- + sp(0.01)*(brunt2/tke(k)))))/length_fac);
-
- }
});
}
diff --git a/components/eamxx/src/physics/shoc/impl/shoc_eddy_diffusivities_impl.hpp b/components/eamxx/src/physics/shoc/impl/shoc_eddy_diffusivities_impl.hpp
index d2c1f4ae4d20..48f0ee795439 100644
--- a/components/eamxx/src/physics/shoc/impl/shoc_eddy_diffusivities_impl.hpp
+++ b/components/eamxx/src/physics/shoc/impl/shoc_eddy_diffusivities_impl.hpp
@@ -16,7 +16,6 @@ KOKKOS_FUNCTION
void Functions::eddy_diffusivities(
const MemberType& team,
const Int& nlev,
- const bool& shoc_1p5tke,
const Scalar& Ckh,
const Scalar& Ckm,
const Scalar& pblh,
@@ -51,16 +50,8 @@ void Functions::eddy_diffusivities(
tkh(k).set(condition, Ckh_s*ekat::square(shoc_mix(k))*ekat::sqrt(sterm_zt(k)));
tk(k).set(condition, Ckm_s*ekat::square(shoc_mix(k))*ekat::sqrt(sterm_zt(k)));
- if (shoc_1p5tke){
- // Revert to a standard 1.5 TKE closure for eddy diffusivities
- tkh(k).set(!condition, Ckh*shoc_mix(k)*ekat::sqrt(tke(k)));
- tk(k).set(!condition, Ckm*shoc_mix(k)*ekat::sqrt(tke(k)));
- }
- else{
- // Default SHOC definition of eddy diffusivity for heat and momentum
- tkh(k).set(!condition, Ckh*isotropy(k)*tke(k));
- tk(k).set(!condition, Ckm*isotropy(k)*tke(k));
- }
+ tkh(k).set(!condition, Ckh*isotropy(k)*tke(k));
+ tk(k).set(!condition, Ckm*isotropy(k)*tke(k));
});
}
diff --git a/components/eamxx/src/physics/shoc/impl/shoc_length_impl.hpp b/components/eamxx/src/physics/shoc/impl/shoc_length_impl.hpp
index e6d321fa10a4..168d1d41b2d6 100644
--- a/components/eamxx/src/physics/shoc/impl/shoc_length_impl.hpp
+++ b/components/eamxx/src/physics/shoc/impl/shoc_length_impl.hpp
@@ -14,7 +14,6 @@ ::shoc_length(
const Int& nlev,
const Int& nlevi,
const Scalar& length_fac,
- const bool& shoc_1p5tke,
const Scalar& dx,
const Scalar& dy,
const uview_1d& zt_grid,
@@ -22,7 +21,6 @@ ::shoc_length(
const uview_1d& dz_zt,
const uview_1d& tke,
const uview_1d& thv,
- const uview_1d& tk,
const Workspace& workspace,
const uview_1d& brunt,
const uview_1d& shoc_mix)
@@ -39,7 +37,7 @@ ::shoc_length(
Scalar l_inf = 0;
compute_l_inf_shoc_length(team,nlev,zt_grid,dz_zt,tke,l_inf);
- compute_shoc_mix_shoc_length(team,nlev,length_fac,shoc_1p5tke,tke,brunt,zt_grid,dz_zt,tk,l_inf,shoc_mix);
+ compute_shoc_mix_shoc_length(team,nlev,length_fac,tke,brunt,zt_grid,l_inf,shoc_mix);
team.team_barrier();
check_length_scale_shoc_length(team,nlev,dx,dy,shoc_mix);
diff --git a/components/eamxx/src/physics/shoc/impl/shoc_main_impl.hpp b/components/eamxx/src/physics/shoc/impl/shoc_main_impl.hpp
index f0509ee6a523..b7d4879d85a5 100644
--- a/components/eamxx/src/physics/shoc/impl/shoc_main_impl.hpp
+++ b/components/eamxx/src/physics/shoc/impl/shoc_main_impl.hpp
@@ -213,10 +213,10 @@ void Functions::shoc_main_internal(
// Update the turbulent length scale
shoc_length(team,nlev,nlevi, // Input
- length_fac,shoc_1p5tke,// Runtime Options
+ length_fac, // Runtime Options
dx,dy, // Input
zt_grid,zi_grid,dz_zt, // Input
- tke,thv,tk, // Input
+ tke,thv, // Input
workspace, // Workspace
brunt,shoc_mix); // Output
@@ -244,7 +244,7 @@ void Functions::shoc_main_internal(
// Diagnose the second order moments
diag_second_shoc_moments(team,nlev,nlevi,
thl2tune, qw2tune, qwthl2tune, w2tune, // Runtime options
- shoc_1p5tke, // Runtime options
+ shoc_1p5tke, // Runtime options
thetal,qw,u_wind,v_wind, // Input
tke,isotropy,tkh,tk,dz_zi,zt_grid,zi_grid, // Input
shoc_mix,wthl_sfc,wqw_sfc,uw_sfc,vw_sfc, // Input
@@ -480,17 +480,17 @@ void Functions::shoc_main_internal(
// Update the turbulent length scale
shoc_length_disp(shcol,nlev,nlevi, // Input
- length_fac,shoc_1p5tke,// Runtime Options
+ length_fac, // Runtime Options
dx,dy, // Input
zt_grid,zi_grid,dz_zt, // Input
- tke,thv,tk, // Input
+ tke,thv, // Input
workspace_mgr, // Workspace mgr
brunt,shoc_mix); // Output
// Advance the SGS TKE equation
shoc_tke_disp(shcol,nlev,nlevi,dtime, // Input
- lambda_low,lambda_high,lambda_slope, // Runtime options
- lambda_thresh,Ckh,Ckm,shoc_1p5tke, // Runtime options
+ lambda_low,lambda_high,lambda_slope, // Runtime options
+ lambda_thresh,Ckh,Ckm,shoc_1p5tke, // Runtime options
wthv_sec, // Input
shoc_mix,dz_zi,dz_zt,pres,shoc_tabs, // Input
u_wind,v_wind,brunt,zt_grid, // Input
@@ -510,7 +510,7 @@ void Functions::shoc_main_internal(
// Diagnose the second order moments
diag_second_shoc_moments_disp(shcol,nlev,nlevi,
thl2tune, qw2tune, qwthl2tune, w2tune, // Runtime options
- shoc_1p5tke, // Runtime options
+ shoc_1p5tke, // Runtime options
thetal,qw,u_wind,v_wind, // Input
tke,isotropy,tkh,tk,dz_zi,zt_grid,zi_grid, // Input
shoc_mix,wthl_sfc,wqw_sfc,uw_sfc,vw_sfc, // Input
diff --git a/components/eamxx/src/physics/shoc/impl/shoc_tke_impl.hpp b/components/eamxx/src/physics/shoc/impl/shoc_tke_impl.hpp
index 89db98a9ad12..9f5fa5f8b7ab 100644
--- a/components/eamxx/src/physics/shoc/impl/shoc_tke_impl.hpp
+++ b/components/eamxx/src/physics/shoc/impl/shoc_tke_impl.hpp
@@ -74,7 +74,7 @@ void Functions::shoc_tke(
isotropic_ts(team,nlev,lambda_low,lambda_high,lambda_slope,lambda_thresh,brunt_int,tke,a_diss,brunt,isotropy);
// Compute eddy diffusivity for heat and momentum
- eddy_diffusivities(team,nlev,shoc_1p5tke,Ckh,Ckm,pblh,zt_grid,tabs,shoc_mix,sterm_zt,isotropy,tke,tkh,tk);
+ eddy_diffusivities(team,nlev,Ckh,Ckm,pblh,zt_grid,tabs,shoc_mix,sterm_zt,isotropy,tke,tkh,tk);
// Release temporary variables from the workspace
workspace.template release_many_contiguous<3>(
diff --git a/components/eamxx/src/physics/shoc/shoc_functions.hpp b/components/eamxx/src/physics/shoc/shoc_functions.hpp
index d1f2928fbee7..164d57dc755e 100644
--- a/components/eamxx/src/physics/shoc/shoc_functions.hpp
+++ b/components/eamxx/src/physics/shoc/shoc_functions.hpp
@@ -277,10 +277,9 @@ template struct Functions {
KOKKOS_FUNCTION
static void compute_shoc_mix_shoc_length(
- const MemberType &team, const Int &nlev, const Scalar &length_fac, const bool &shoc_1p5tke,
+ const MemberType &team, const Int &nlev, const Scalar &length_fac,
const uview_1d &tke, const uview_1d &brunt,
- const uview_1d &zt_grid, const uview_1d &dz_zt,
- const uview_1d &tk, const Scalar &l_inf, const uview_1d &shoc_mix);
+ const uview_1d &zt_grid, const Scalar &l_inf, const uview_1d &shoc_mix);
KOKKOS_FUNCTION
static void check_tke(const MemberType &team, const Int &nlev, const uview_1d &tke);
@@ -414,20 +413,20 @@ template struct Functions {
KOKKOS_FUNCTION
static void shoc_length(const MemberType &team, const Int &nlev, const Int &nlevi,
- const Scalar &length_fac, const bool &shoc_1p5tke, const Scalar &dx,
+ const Scalar &length_fac, const Scalar &dx,
const Scalar &dy, const uview_1d &zt_grid,
const uview_1d &zi_grid, const uview_1d &dz_zt,
const uview_1d &tke, const uview_1d &thv,
- const uview_1d &tk, const Workspace &workspace,
- const uview_1d &brunt, const uview_1d &shoc_mix);
+ const Workspace &workspace, const uview_1d &brunt,
+ const uview_1d &shoc_mix);
#ifdef SCREAM_SHOC_SMALL_KERNELS
static void shoc_length_disp(const Int &shcol, const Int &nlev, const Int &nlevi,
- const Scalar &length_fac, const bool &tke_1p5_closure,
+ const Scalar &length_fac,
const view_1d &dx, const view_1d &dy,
const view_2d &zt_grid,
const view_2d &zi_grid,
const view_2d &dz_zt, const view_2d &tke,
- const view_2d &thv, const view_2d &tk,
+ const view_2d &thv,
const WorkspaceMgr &workspace_mgr, const view_2d &brunt,
const view_2d &shoc_mix);
#endif
@@ -846,7 +845,7 @@ template struct Functions {
KOKKOS_FUNCTION
static void
- eddy_diffusivities(const MemberType &team, const Int &nlev, const bool &shoc_1p5tke,
+ eddy_diffusivities(const MemberType &team, const Int &nlev,
const Scalar &Ckh, const Scalar &Ckm, const Scalar &pblh,
const uview_1d &zt_grid, const uview_1d &tabs,
const uview_1d &shoc_mix, const uview_1d &sterm_zt,
diff --git a/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.cpp b/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.cpp
index 15f697081658..c695f16ef1c2 100644
--- a/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.cpp
+++ b/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.cpp
@@ -96,12 +96,12 @@ void adv_sgs_tke(AdvSgsTkeData& d)
void eddy_diffusivities(EddyDiffusivitiesData& d)
{
- eddy_diffusivities_host(d.nlev, d.shcol, d.shoc_1p5tke, d.pblh, d.zt_grid, d.tabs, d.shoc_mix, d.sterm_zt, d.isotropy, d.tke, d.tkh, d.tk);
+ eddy_diffusivities_host(d.nlev, d.shcol, d.pblh, d.zt_grid, d.tabs, d.shoc_mix, d.sterm_zt, d.isotropy, d.tke, d.tkh, d.tk);
}
void shoc_length(ShocLengthData& d)
{
- shoc_length_host(d.shcol, d.nlev, d.nlevi, d.shoc_1p5tke, d.host_dx, d.host_dy, d.zt_grid, d.zi_grid, d.dz_zt, d.tke, d.thv, d.tk, d.brunt, d.shoc_mix);
+ shoc_length_host(d.shcol, d.nlev, d.nlevi, d.host_dx, d.host_dy, d.zt_grid, d.zi_grid, d.dz_zt, d.tke, d.thv, d.brunt, d.shoc_mix);
}
void compute_brunt_shoc_length(ComputeBruntShocLengthData& d)
@@ -116,7 +116,7 @@ void compute_l_inf_shoc_length(ComputeLInfShocLengthData& d)
void compute_shoc_mix_shoc_length(ComputeShocMixShocLengthData& d)
{
- compute_shoc_mix_shoc_length_host(d.nlev, d.shcol, d.shoc_1p5tke, d.tke, d.brunt, d.zt_grid, d.dz_zt, d.tk, d.l_inf, d.shoc_mix);
+ compute_shoc_mix_shoc_length_host(d.nlev, d.shcol, d.tke, d.brunt, d.zt_grid, d.l_inf, d.shoc_mix);
}
void check_length_scale_shoc_length(CheckLengthScaleShocLengthData& d)
@@ -654,8 +654,8 @@ void shoc_pblintd_init_pot_host(Int shcol, Int nlev, Real *thl, Real* ql, Real*
ekat::device_to_host({thv}, shcol, nlev, inout_views);
}
-void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, bool shoc_1p5tke, Real* tke, Real* brunt,
- Real* zt_grid, Real* dz_zt, Real* tk, Real* l_inf, Real* shoc_mix)
+void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, Real* tke, Real* brunt,
+ Real* zt_grid, Real* l_inf, Real* shoc_mix)
{
using SHF = Functions;
@@ -669,8 +669,8 @@ void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, bool shoc_1p5tke, Re
using MemberType = typename SHF::MemberType;
std::vector temp_1d_d(1);
- std::vector temp_2d_d(6);
- std::vector ptr_array = {tke, brunt, zt_grid, dz_zt, tk, shoc_mix};
+ std::vector temp_2d_d(4);
+ std::vector ptr_array = {tke, brunt, zt_grid, shoc_mix};
// Sync to device
ekat::host_to_device({l_inf}, shcol, temp_1d_d);
@@ -683,9 +683,7 @@ void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, bool shoc_1p5tke, Re
tke_d (temp_2d_d[0]),
brunt_d (temp_2d_d[1]),
zt_grid_d (temp_2d_d[2]),
- dz_zt_d (temp_2d_d[3]),
- tk_d (temp_2d_d[4]),
- shoc_mix_d (temp_2d_d[5]);
+ shoc_mix_d (temp_2d_d[3]);
const Int nk_pack = ekat::npack(nlev);
const auto policy = TPF::get_default_team_policy(shcol, nk_pack);
@@ -698,12 +696,10 @@ void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, bool shoc_1p5tke, Re
const auto brunt_s = ekat::subview(brunt_d, i);
const auto zt_grid_s = ekat::subview(zt_grid_d, i);
const auto shoc_mix_s = ekat::subview(shoc_mix_d, i);
- const auto dz_zt_s = ekat::subview(dz_zt_d, i);
- const auto tk_s = ekat::subview(tk_d, i);
const Real length_fac = 0.5;
- SHF::compute_shoc_mix_shoc_length(team, nlev, length_fac, shoc_1p5tke, tke_s, brunt_s, zt_grid_s,
- dz_zt_s, tk_s, l_inf_s, shoc_mix_s);
+ SHF::compute_shoc_mix_shoc_length(team, nlev, length_fac, tke_s, brunt_s, zt_grid_s,
+ l_inf_s, shoc_mix_s);
});
// Sync back to host
@@ -1405,9 +1401,9 @@ void shoc_pblintd_cldcheck_host(Int shcol, Int nlev, Int nlevi, Real* zi, Real*
ekat::device_to_host({pblh}, shcol, inout_views);
}
-void shoc_length_host(Int shcol, Int nlev, Int nlevi, bool shoc_1p5tke, Real* host_dx, Real* host_dy,
+void shoc_length_host(Int shcol, Int nlev, Int nlevi, Real* host_dx, Real* host_dy,
Real* zt_grid, Real* zi_grid, Real*dz_zt, Real* tke,
- Real* thv, Real* tk, Real*brunt, Real* shoc_mix)
+ Real* thv, Real*brunt, Real* shoc_mix)
{
using SHF = Functions;
@@ -1421,12 +1417,12 @@ void shoc_length_host(Int shcol, Int nlev, Int nlevi, bool shoc_1p5tke, Real* ho
using MemberType = typename SHF::MemberType;
std::vector temp_1d_d(2);
- std::vector temp_2d_d(8);
- std::vector dim1_sizes(8, shcol);
+ std::vector temp_2d_d(7);
+ std::vector dim1_sizes(7, shcol);
std::vector dim2_sizes = {nlev, nlevi, nlev, nlev,
- nlev, nlev, nlev, nlev};
+ nlev, nlev, nlev};
std::vector ptr_array = {zt_grid, zi_grid, dz_zt, tke,
- thv, tk, brunt, shoc_mix};
+ thv, brunt, shoc_mix};
// Sync to device
ekat::host_to_device({host_dx, host_dy}, shcol, temp_1d_d);
ekat::host_to_device(ptr_array, dim1_sizes, dim2_sizes, temp_2d_d);
@@ -1442,9 +1438,8 @@ void shoc_length_host(Int shcol, Int nlev, Int nlevi, bool shoc_1p5tke, Real* ho
dz_zt_d(temp_2d_d[2]),
tke_d(temp_2d_d[3]),
thv_d(temp_2d_d[4]),
- tk_d(temp_2d_d[5]),
- brunt_d(temp_2d_d[6]),
- shoc_mix_d(temp_2d_d[7]);
+ brunt_d(temp_2d_d[5]),
+ shoc_mix_d(temp_2d_d[6]);
const Int nlev_packs = ekat::npack(nlev);
const Int nlevi_packs = ekat::npack(nlevi);
@@ -1467,16 +1462,15 @@ void shoc_length_host(Int shcol, Int nlev, Int nlevi, bool shoc_1p5tke, Real* ho
const auto dz_zt_s = ekat::subview(dz_zt_d, i);
const auto tke_s = ekat::subview(tke_d, i);
const auto thv_s = ekat::subview(thv_d, i);
- const auto tk_s = ekat::subview(tk_d, i);
const auto brunt_s = ekat::subview(brunt_d, i);
const auto shoc_mix_s = ekat::subview(shoc_mix_d, i);
// Hardcode runtime option for F90 tests.
const Scalar length_fac = 0.5;
- SHF::shoc_length(team,nlev,nlevi,length_fac,shoc_1p5tke,
+ SHF::shoc_length(team,nlev,nlevi,length_fac,
host_dx_s,host_dy_s,
zt_grid_s,zi_grid_s,dz_zt_s,tke_s,
- thv_s,tk_s,workspace,brunt_s,shoc_mix_s);
+ thv_s,workspace,brunt_s,shoc_mix_s);
});
// Sync back to host
@@ -2681,7 +2675,7 @@ void shoc_grid_host(Int shcol, Int nlev, Int nlevi, Real* zt_grid, Real* zi_grid
ekat::device_to_host({dz_zt, dz_zi, rho_zt}, std::vector{shcol, shcol, shcol}, std::vector{nlev, nlevi, nlev}, inout_views);
}
-void eddy_diffusivities_host(Int nlev, Int shcol, bool shoc_1p5tke, Real* pblh, Real* zt_grid, Real* tabs, Real* shoc_mix, Real* sterm_zt,
+void eddy_diffusivities_host(Int nlev, Int shcol, Real* pblh, Real* zt_grid, Real* tabs, Real* shoc_mix, Real* sterm_zt,
Real* isotropy, Real* tke, Real* tkh, Real* tk)
{
using SHF = Functions;
@@ -2740,7 +2734,7 @@ void eddy_diffusivities_host(Int nlev, Int shcol, bool shoc_1p5tke, Real* pblh,
const Real Ckh = 0.1;
const Real Ckm = 0.1;
- SHF::eddy_diffusivities(team, nlev, shoc_1p5tke, Ckh, Ckm, pblh_s, zt_grid_s, tabs_s, shoc_mix_s, sterm_zt_s, isotropy_s, tke_s, tkh_s, tk_s);
+ SHF::eddy_diffusivities(team, nlev, Ckh, Ckm, pblh_s, zt_grid_s, tabs_s, shoc_mix_s, sterm_zt_s, isotropy_s, tke_s, tkh_s, tk_s);
});
// Sync back to host
diff --git a/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.hpp b/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.hpp
index 75aca9b82e34..ca56fe962b8c 100644
--- a/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.hpp
+++ b/components/eamxx/src/physics/shoc/tests/infra/shoc_test_data.hpp
@@ -295,16 +295,15 @@ struct AdvSgsTkeData : public PhysicsTestData {
struct EddyDiffusivitiesData : public PhysicsTestData {
// Inputs
Int shcol, nlev;
- bool shoc_1p5tke;
Real *pblh, *zt_grid, *tabs, *shoc_mix, *sterm_zt, *isotropy, *tke;
// Outputs
Real *tkh, *tk;
- EddyDiffusivitiesData(Int shcol_, Int nlev_, bool shoc_1p5tke_) :
- PhysicsTestData({{ shcol_ }, { shcol_, nlev_ }}, {{ &pblh }, { &zt_grid, &tabs, &shoc_mix, &sterm_zt, &isotropy, &tke, &tkh, &tk }}), shcol(shcol_), nlev(nlev_), shoc_1p5tke(shoc_1p5tke_) {}
+ EddyDiffusivitiesData(Int shcol_, Int nlev_) :
+ PhysicsTestData({{ shcol_ }, { shcol_, nlev_ }}, {{ &pblh }, { &zt_grid, &tabs, &shoc_mix, &sterm_zt, &isotropy, &tke, &tkh, &tk }}), shcol(shcol_), nlev(nlev_) {}
- PTD_STD_DEF(EddyDiffusivitiesData, 3, shcol, nlev, shoc_1p5tke);
+ PTD_STD_DEF(EddyDiffusivitiesData, 2, shcol, nlev);
};
struct ShocLengthData : public ShocTestGridDataBase {
@@ -316,10 +315,10 @@ struct ShocLengthData : public ShocTestGridDataBase {
// Outputs
Real *brunt, *shoc_mix;
- ShocLengthData(Int shcol_, Int nlev_, Int nlevi_, bool shoc_1p5tke_) :
- ShocTestGridDataBase({{ shcol_ }, { shcol_, nlev_ }, { shcol_, nlevi_ }}, {{ &host_dx, &host_dy }, { &zt_grid, &dz_zt, &tke, &thv, &tk, &brunt, &shoc_mix }, { &zi_grid }}), shcol(shcol_), nlev(nlev_), nlevi(nlevi_), shoc_1p5tke(shoc_1p5tke_) {}
+ ShocLengthData(Int shcol_, Int nlev_, Int nlevi_) :
+ ShocTestGridDataBase({{ shcol_ }, { shcol_, nlev_ }, { shcol_, nlevi_ }}, {{ &host_dx, &host_dy }, { &zt_grid, &dz_zt, &tke, &thv, &brunt, &shoc_mix }, { &zi_grid }}), shcol(shcol_), nlev(nlev_), nlevi(nlevi_) {}
- PTD_STD_DEF(ShocLengthData, 4, shcol, nlev, nlevi, shoc_1p5tke);
+ PTD_STD_DEF(ShocLengthData, 3, shcol, nlev, nlevi);
};
struct ComputeBruntShocLengthData : public PhysicsTestData {
@@ -367,16 +366,15 @@ struct ComputeConvTimeShocLengthData : public PhysicsTestData {
struct ComputeShocMixShocLengthData : public PhysicsTestData {
// Inputs
Int shcol, nlev;
- bool shoc_1p5tke;
- Real *tke, *brunt, *zt_grid, *dz_zt, *tk, *l_inf;
+ Real *tke, *brunt, *zt_grid, *l_inf;
// Outputs
Real *shoc_mix;
- ComputeShocMixShocLengthData(Int shcol_, Int nlev_, bool shoc_1p5tke_) :
- PhysicsTestData({{ shcol_, nlev_ }, { shcol_ }}, {{ &tke, &brunt, &zt_grid, &dz_zt, &tk, &shoc_mix }, { &l_inf }}), shcol(shcol_), nlev(nlev_), shoc_1p5tke(shoc_1p5tke_) {}
+ ComputeShocMixShocLengthData(Int shcol_, Int nlev_) :
+ PhysicsTestData({{ shcol_, nlev_ }, { shcol_ }}, {{ &tke, &brunt, &zt_grid, &shoc_mix }, { &l_inf }}), shcol(shcol_), nlev(nlev_) {}
- PTD_STD_DEF(ComputeShocMixShocLengthData, 3, shcol, nlev, shoc_1p5tke);
+ PTD_STD_DEF(ComputeShocMixShocLengthData, 2, shcol, nlev);
};
struct CheckLengthScaleShocLengthData : public PhysicsTestData {
@@ -1016,8 +1014,8 @@ void compute_diag_third_shoc_moment_host(Int shcol, Int nlev, Int nlevi, bool sh
Real* brunt_zi, Real* w_sec_zi, Real* thetal_zi,
Real* w3);
void shoc_pblintd_init_pot_host(Int shcol, Int nlev, Real* thl, Real* ql, Real* q, Real* thv);
-void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, bool shoc_1p5tke, Real* tke, Real* brunt,
- Real* zt_grid, Real* dz_zt, Real* tk, Real* l_inf, Real* shoc_mix);
+void compute_shoc_mix_shoc_length_host(Int nlev, Int shcol, Real* tke, Real* brunt,
+ Real* zt_grid, Real* l_inf, Real* shoc_mix);
void check_tke_host(Int shcol, Int nlev, Real* tke);
void linear_interp_host(Real* x1, Real* x2, Real* y1, Real* y2, Int km1, Int km2, Int ncol, Real minthresh);
void clipping_diag_third_shoc_moments_host(Int nlevi, Int shcol, Real *w_sec_zi,
@@ -1045,9 +1043,9 @@ void shoc_diag_obklen_host(Int shcol, Real* uw_sfc, Real* vw_sfc, Real* wthl_sfc
Real* thl_sfc, Real* cldliq_sfc, Real* qv_sfc, Real* ustar, Real* kbfs, Real* obklen);
void shoc_pblintd_cldcheck_host(Int shcol, Int nlev, Int nlevi, Real* zi, Real* cldn, Real* pblh);
void compute_shr_prod_host(Int nlevi, Int nlev, Int shcol, Real* dz_zi, Real* u_wind, Real* v_wind, Real* sterm);
-void shoc_length_host(Int shcol, Int nlev, Int nlevi, bool shoc_1p5tke, Real* host_dx, Real* host_dy,
+void shoc_length_host(Int shcol, Int nlev, Int nlevi, Real* host_dx, Real* host_dy,
Real* zt_grid, Real* zi_grid, Real*dz_zt, Real* tke,
- Real* thv, Real* tk, Real*brunt, Real* shoc_mix);
+ Real* thv, Real* brunt, Real* shoc_mix);
void shoc_energy_fixer_host(Int shcol, Int nlev, Int nlevi, Real dtime, Int nadv, Real* zt_grid,
Real* zi_grid, Real* se_b, Real* ke_b, Real* wv_b, Real* wl_b,
Real* se_a, Real* ke_a, Real* wv_a, Real* wl_a, Real* wthl_sfc,
@@ -1097,7 +1095,7 @@ void pblintd_check_pblh_host(Int shcol, Int nlev, Int nlevi, Int npbl, Real* z,
void pblintd_host(Int shcol, Int nlev, Int nlevi, Int npbl, Real* z, Real* zi, Real* thl, Real* ql, Real* q, Real* u, Real* v, Real* ustar, Real* obklen, Real* kbfs, Real* cldn, Real* pblh);
void shoc_grid_host(Int shcol, Int nlev, Int nlevi, Real* zt_grid, Real* zi_grid, Real* pdel, Real* dz_zt, Real* dz_zi, Real* rho_zt);
-void eddy_diffusivities_host(Int nlev, Int shcol, bool shoc_1p5tke, Real* pblh, Real* zt_grid, Real* tabs, Real* shoc_mix, Real* sterm_zt, Real* isotropy,
+void eddy_diffusivities_host(Int nlev, Int shcol, Real* pblh, Real* zt_grid, Real* tabs, Real* shoc_mix, Real* sterm_zt, Real* isotropy,
Real* tke, Real* tkh, Real* tk);
void shoc_tke_host(Int shcol, Int nlev, Int nlevi, Real dtime, bool shoc_1p5tke, Real* wthv_sec, Real* shoc_mix, Real* dz_zi, Real* dz_zt, Real* pres,
Real* u_wind, Real* v_wind, Real* brunt, Real* obklen, Real* zt_grid, Real* zi_grid, Real* pblh, Real* tke,
diff --git a/components/eamxx/src/physics/shoc/tests/shoc_eddy_diffusivities_tests.cpp b/components/eamxx/src/physics/shoc/tests/shoc_eddy_diffusivities_tests.cpp
index 19dacdff3f6c..4ae79ed1727d 100644
--- a/components/eamxx/src/physics/shoc/tests/shoc_eddy_diffusivities_tests.cpp
+++ b/components/eamxx/src/physics/shoc/tests/shoc_eddy_diffusivities_tests.cpp
@@ -55,11 +55,8 @@ struct UnitWrap::UnitTest::TestShocEddyDiff : public UnitWrap::UnitTest::B
// Turbulent kinetic energy [m2/s2]
static constexpr Real tke_reg = 0.4;
- // Default SHOC formulation, not 1.5 TKE closure assumptions
- const bool shoc_1p5tke = false;
-
// Initialize data structure for bridging to F90
- EddyDiffusivitiesData SDS(shcol, nlev, shoc_1p5tke);
+ EddyDiffusivitiesData SDS(shcol, nlev);
// Test that the inputs are reasonable.
REQUIRE( (SDS.shcol == shcol && SDS.nlev == nlev) );
@@ -252,101 +249,6 @@ struct UnitWrap::UnitTest::TestShocEddyDiff : public UnitWrap::UnitTest::B
}
}
- // 1.5 TKE test
- // Verify that eddy diffusivities behave as expected if 1.5 TKE is activated.
- // For this test we simply recycle the inputs from the previous test, with exception
- // of the turbulent length scale and TKE.
-
- // SHOC Mixing length [m]
- static constexpr Real shoc_mix_1p5_t1[shcol] = {100, 500};
- // Turbulent kinetic energy [m2/s2]
- static constexpr Real tke_1p5_t1[shcol] = {0.4, 0.4};
-
- // Verify that input length scale is increasing with column
- // and TKE is the same for each column
- for(Int s = 0; s < shcol-1; ++s) {
- REQUIRE(shoc_mix_1p5_t1[s+1] > shoc_mix_1p5_t1[s]);
- REQUIRE(tke_1p5_t1[s+1] == tke_1p5_t1[s]);
- }
-
- // Fill in test data on zt_grid.
- for(Int s = 0; s < shcol; ++s) {
- // Column only input
- SDS.tabs[s] = tabs_ustab[s];
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
-
- SDS.tke[offset] = tke_1p5_t1[s];
- SDS.shoc_mix[offset] = shoc_mix_1p5_t1[s];
- }
- }
-
- // Activate 1.5 TKE closure assumptions
- SDS.shoc_1p5tke = true;
-
- // Call the C++ implementation
- eddy_diffusivities(SDS);
-
- // Check to make sure the diffusivities are smaller
- // in the columns where length scale is smaller
- for(Int s = 0; s < shcol-1; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
- // Get value corresponding to next column
- const auto offsets = n + (s+1) * nlev;
- if (SDS.tke[offset] == SDS.tke[offsets] &
- SDS.shoc_mix[offset] < SDS.shoc_mix[offsets]){
- REQUIRE(SDS.tk[offset] < SDS.tk[offsets]);
- REQUIRE(SDS.tkh[offset] < SDS.tkh[offsets]);
- }
- }
- }
-
- // Now we are going to do a similar but opposite test, change TKE
- // while keeping SHOC mix constant
-
- // SHOC Mixing length [m]
- static constexpr Real shoc_mix_1p5_t2[shcol] = {500, 500};
- // Turbulent kinetic energy [m2/s2]
- static constexpr Real tke_1p5_t2[shcol] = {0.1, 0.4};
-
- // Verify that input length scale is increasing with column
- // and TKE is the same for each column
- for(Int s = 0; s < shcol-1; ++s) {
- REQUIRE(shoc_mix_1p5_t2[s+1] == shoc_mix_1p5_t2[s]);
- REQUIRE(tke_1p5_t2[s+1] > tke_1p5_t2[s]);
- }
-
- // Fill in test data on zt_grid.
- for(Int s = 0; s < shcol; ++s) {
- // Column only input
- SDS.tabs[s] = tabs_ustab[s];
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
-
- SDS.tke[offset] = tke_1p5_t2[s];
- SDS.shoc_mix[offset] = shoc_mix_1p5_t2[s];
- }
- }
-
- // Call the C++ implementation
- eddy_diffusivities(SDS);
-
- // Check to make sure the diffusivities are smaller
- // in the columns where TKE is smaller
- for(Int s = 0; s < shcol-1; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
- // Get value corresponding to next column
- const auto offsets = n + (s+1) * nlev;
- if (SDS.tke[offset] < SDS.tke[offsets] &
- SDS.shoc_mix[offset] == SDS.shoc_mix[offsets]){
- REQUIRE(SDS.tk[offset] < SDS.tk[offsets]);
- REQUIRE(SDS.tkh[offset] < SDS.tkh[offsets]);
- }
- }
- }
-
}
@@ -355,10 +257,10 @@ struct UnitWrap::UnitTest::TestShocEddyDiff : public UnitWrap::UnitTest::B
auto engine = Base::get_engine();
EddyDiffusivitiesData baseline_data[] = {
- EddyDiffusivitiesData(10, 71, false),
- EddyDiffusivitiesData(10, 12, false),
- EddyDiffusivitiesData(7, 16, false),
- EddyDiffusivitiesData(2, 7, false),
+ EddyDiffusivitiesData(10, 71),
+ EddyDiffusivitiesData(10, 12),
+ EddyDiffusivitiesData(7, 16),
+ EddyDiffusivitiesData(2, 7),
};
// Generate random input data
diff --git a/components/eamxx/src/physics/shoc/tests/shoc_length_tests.cpp b/components/eamxx/src/physics/shoc/tests/shoc_length_tests.cpp
index 25e8e91d6e32..130b48938eef 100644
--- a/components/eamxx/src/physics/shoc/tests/shoc_length_tests.cpp
+++ b/components/eamxx/src/physics/shoc/tests/shoc_length_tests.cpp
@@ -48,11 +48,6 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
static constexpr Real thv[nlev] = {315, 310, 305, 300, 295};
// Turbulent kinetc energy [m2/s2]
static constexpr Real tke[nlev] = {0.1, 0.15, 0.2, 0.25, 0.3};
- // Eddy viscosity [m2/s]
- static constexpr Real tk[nlev] = {0.1, 10.0, 12.0, 15.0, 20.0};
-
- // Default SHOC formulation, not 1.5 TKE closure assumptions
- const bool shoc_1p5tke = false;
// compute geometric grid mesh
const auto grid_mesh = sqrt(host_dx*host_dy);
@@ -67,7 +62,7 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
}
// Initialize data structure for bridging to F90
- ShocLengthData SDS(shcol, nlev, nlevi, shoc_1p5tke);
+ ShocLengthData SDS(shcol, nlev, nlevi);
// Load up input data
for(Int s = 0; s < shcol; ++s) {
@@ -83,8 +78,6 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
SDS.zt_grid[offset] = zt_grid[n];
SDS.thv[offset] = thv[n];
SDS.dz_zt[offset] = dz_zt[n];
- // eddy viscosity below not relevant for default SHOC
- SDS.tk[offset] = 0;
}
// Fill in test data on zi_grid
@@ -154,50 +147,6 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
}
}
- // Repeat this test but for 1.5 TKE closure option activated
-
- // Activate 1.5 TKE closure assumptions
- SDS.shoc_1p5tke = true;
-
- // We will use the same input data as above but with the SGS buoyancy
- // flux set to zero, as will be the case with the 1.5 TKE option.
- // Additionally, we will fill the value of the brunt vaisala frequency.
- for(Int s = 0; s < shcol; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
-
- SDS.tk[offset] = tk[n];
- }
- }
-
- // Call the C++ implementation
- shoc_length(SDS);
-
- // Verify output
- for(Int s = 0; s < shcol; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
- // Require mixing length is greater than zero and is
- // less than geometric grid mesh length + 1 m
- REQUIRE(SDS.shoc_mix[offset] >= minlen);
- REQUIRE(SDS.shoc_mix[offset] <= maxlen);
- REQUIRE(SDS.shoc_mix[offset] < 1.0+grid_mesh);
-
- // Be sure brunt vaisalla frequency is reasonable
- REQUIRE(SDS.brunt[offset] < 1);
-
- // Ensure length scale is equal to dz if brunt =< 0, else
- // length scale should be less then dz
- if (SDS.brunt[offset] <= 0){
- REQUIRE(SDS.shoc_mix[offset] == SDS.dz_zt[offset]);
- }
- else{
- REQUIRE(SDS.shoc_mix[offset] < SDS.dz_zt[offset]);
- }
-
- }
- }
-
// TEST TWO
// Small grid mesh test. Given a very small grid mesh, verify that
// the length scale is confined to this value. Input from first
@@ -208,9 +157,6 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
// Defin the host grid box size y-direction [m]
static constexpr Real host_dy_small = 5;
- // Call default SHOC closure assumptions
- SDS.shoc_1p5tke = false;
-
// compute geometric grid mesh
const auto grid_mesh_small = sqrt(host_dx_small*host_dy_small);
@@ -235,26 +181,6 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
}
}
- // Repeat this test but for 1.5 TKE closure option activated
-
- // Activate 1.5 TKE closure assumptions
- SDS.shoc_1p5tke = true;
-
- // call C++ implementation
- shoc_length(SDS);
-
- // Verify output
- for(Int s = 0; s < shcol; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
- // Require mixing length is greater than zero and is
- // less than geometric grid mesh length + 1 m
- REQUIRE(SDS.shoc_mix[offset] > 0);
- REQUIRE(SDS.shoc_mix[offset] <= maxlen);
- REQUIRE(SDS.shoc_mix[offset] < 1.0+grid_mesh_small);
- }
- }
-
}
void run_bfb()
@@ -263,10 +189,10 @@ struct UnitWrap::UnitTest::TestShocLength : public UnitWrap::UnitTest::Bas
ShocLengthData SDS_baseline[] = {
// shcol, nlev, nlevi
- ShocLengthData(12, 71, 72, false),
- ShocLengthData(10, 12, 13, false),
- ShocLengthData(7, 16, 17, false),
- ShocLengthData(2, 7, 8, false),
+ ShocLengthData(12, 71, 72),
+ ShocLengthData(10, 12, 13),
+ ShocLengthData(7, 16, 17),
+ ShocLengthData(2, 7, 8),
};
// Generate random input data
diff --git a/components/eamxx/src/physics/shoc/tests/shoc_mix_length_tests.cpp b/components/eamxx/src/physics/shoc/tests/shoc_mix_length_tests.cpp
index b1a3f99d7b1d..b2fb321f6d89 100644
--- a/components/eamxx/src/physics/shoc/tests/shoc_mix_length_tests.cpp
+++ b/components/eamxx/src/physics/shoc/tests/shoc_mix_length_tests.cpp
@@ -42,11 +42,8 @@ struct UnitWrap::UnitTest::TestCompShocMixLength : public UnitWrap::UnitTest<
// Define the heights on the zt grid [m]
static constexpr Real zt_grid[nlev] = {5000, 3000, 2000, 1000, 500};
- // Default SHOC formulation, not 1.5 TKE closure assumptions
- const bool shoc_1p5tke = false;
-
// Initialize data structure for bridging to F90
- ComputeShocMixShocLengthData SDS(shcol, nlev, shoc_1p5tke);
+ ComputeShocMixShocLengthData SDS(shcol, nlev);
// Test that the inputs are reasonable.
// For this test shcol MUST be at least 2
@@ -64,9 +61,6 @@ struct UnitWrap::UnitTest::TestCompShocMixLength : public UnitWrap::UnitTest<
SDS.tke[offset] = (1.0+s)*tke_cons;
SDS.brunt[offset] = brunt_cons;
SDS.zt_grid[offset] = zt_grid[n];
- // do not consider below for default SHOC
- SDS.tk[offset] = 0;
- SDS.dz_zt[offset] = 0;
}
}
@@ -116,55 +110,6 @@ struct UnitWrap::UnitTest::TestCompShocMixLength : public UnitWrap::UnitTest<
}
}
- // 1.5 TKE test
- // Verify that length scale behaves as expected when 1.5 TKE closure
- // assumptions are used. Will recycle all previous data, except we
- // need to define dz, brunt vaisalla frequency, and tk.
-
- // Brunt Vaisalla frequency [s-1]
- static constexpr Real brunt_1p5[nlev] = {0.01,-0.01,0.01,-0.01,0.01};
- // Define the heights on the zt grid [m]
- static constexpr Real dz_zt_1p5[nlev] = {50, 100, 30, 20, 10};
- // Eddy viscocity [m2 s-1]
- static constexpr Real tk_cons_1p5 = 0.1;
-
- // Activate 1.5 TKE closure
- SDS.shoc_1p5tke = true;
-
- // Fill in test data on zt_grid.
- for(Int s = 0; s < shcol; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
-
- // do not consider below for default SHOC
- SDS.tk[offset] = tk_cons_1p5;
- SDS.dz_zt[offset] = dz_zt_1p5[n];
- SDS.brunt[offset] = brunt_1p5[n];
- }
- }
-
- // Call the C++ implementation
- compute_shoc_mix_shoc_length(SDS);
-
- // Check the result
-
- // Verify that if Brunt Vaisalla frequency is unstable that mixing length
- // is equal to vertical grid spacing. If brunt is stable, then verify that
- // mixing length is less than the vertical grid spacing.
- for(Int s = 0; s < shcol; ++s) {
- for(Int n = 0; n < nlev; ++n) {
- const auto offset = n + s * nlev;
- if (SDS.brunt[offset] <= 0){
- REQUIRE(SDS.shoc_mix[offset] == SDS.dz_zt[offset]);
- }
- else{
- REQUIRE(SDS.shoc_mix[offset] < SDS.dz_zt[offset]);
- REQUIRE(SDS.shoc_mix[offset] >= 0.1*SDS.dz_zt[offset]);
- }
-
- }
- }
-
}
void run_bfb()
@@ -173,10 +118,10 @@ struct UnitWrap::UnitTest::TestCompShocMixLength : public UnitWrap::UnitTest<
ComputeShocMixShocLengthData SDS_baseline[] = {
// shcol, nlev
- ComputeShocMixShocLengthData(10, 71, false),
- ComputeShocMixShocLengthData(10, 12, false),
- ComputeShocMixShocLengthData(7, 16, false),
- ComputeShocMixShocLengthData(2, 7, false)
+ ComputeShocMixShocLengthData(10, 71),
+ ComputeShocMixShocLengthData(10, 12),
+ ComputeShocMixShocLengthData(7, 16),
+ ComputeShocMixShocLengthData(2, 7)
};
// Generate random input data
diff --git a/components/eamxx/src/physics/zm/CMakeLists.txt b/components/eamxx/src/physics/zm/CMakeLists.txt
index e24a77d69bbb..eafdf6919811 100644
--- a/components/eamxx/src/physics/zm/CMakeLists.txt
+++ b/components/eamxx/src/physics/zm/CMakeLists.txt
@@ -54,6 +54,7 @@ if (NOT EAMXX_ENABLE_GPU OR Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE OR Kokkos
eti/zm_compute_dilute_parcel.cpp
eti/zm_compute_cape_from_parcel.cpp
eti/zm_zm_conv_mcsp_calculate_shear.cpp
+ eti/zm_zm_conv_mcsp_tend.cpp
) # ZM ETI SRCS
endif()
diff --git a/components/eamxx/src/physics/zm/eti/zm_zm_conv_mcsp_tend.cpp b/components/eamxx/src/physics/zm/eti/zm_zm_conv_mcsp_tend.cpp
new file mode 100644
index 000000000000..74136312b098
--- /dev/null
+++ b/components/eamxx/src/physics/zm/eti/zm_zm_conv_mcsp_tend.cpp
@@ -0,0 +1,14 @@
+#include "impl/zm_zm_conv_mcsp_tend_impl.hpp"
+
+namespace scream {
+namespace zm {
+
+/*
+ * Explicit instantiation for doing zm_conv_mcsp_tend on Reals using the
+ * default device.
+ */
+
+template struct Functions;
+
+} // namespace zm
+} // namespace scream
diff --git a/components/eamxx/src/physics/zm/impl/zm_compute_cape_from_parcel_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_compute_cape_from_parcel_impl.hpp
index 209b3a9254a7..b51a743e1e47 100644
--- a/components/eamxx/src/physics/zm/impl/zm_compute_cape_from_parcel_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_compute_cape_from_parcel_impl.hpp
@@ -50,19 +50,19 @@ void Functions::compute_cape_from_parcel(
eql_klev = pver - 1;
cape = 0.0;
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, num_cin), [&] (const Int& n) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, num_cin), [&] (const Int& n) {
eql_klev_tmp(n) = pver - 1;
cape_tmp(n) = 0.0;
});
team.team_barrier();
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
buoyancy(k) = 0.0;
});
team.team_barrier();
// Calculate buoyancy
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, num_msg, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, num_msg, pver), [&] (const Int& k) {
// Define buoyancy from launch level to equilibrium level
if (k <= msemax_klev && lcl_pmid >= ZMC::lcl_pressure_threshold) {
buoyancy(k) = parcel_vtemp(k) - tv(k) + runtime_opt.tiedke_add;
@@ -86,10 +86,13 @@ void Functions::compute_cape_from_parcel(
}
}
});
+ team.team_barrier();
- // Integrate buoyancy to obtain possible CAPE values
+ // Integrate buoyancy to obtain possible CAPE values. For some reason, the
+ // sum order does not match the serial fortran, so tiny roundoff differences
+ // exist compared to fortran for cape.
for (Int n = 0; n < num_cin; ++n) {
- Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, num_msg, pver),
+ Kokkos::parallel_reduce(Kokkos::TeamVectorRange(team, num_msg, pver),
[&] (const Int& k, Real& cape_n) {
if (lcl_pmid >= ZMC::lcl_pressure_threshold &&
k <= msemax_klev && k > eql_klev_tmp(n)) {
diff --git a/components/eamxx/src/physics/zm/impl/zm_compute_dilute_cape_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_compute_dilute_cape_impl.hpp
index 49f3ac2179be..247d7f205933 100644
--- a/components/eamxx/src/physics/zm/impl/zm_compute_dilute_cape_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_compute_dilute_cape_impl.hpp
@@ -74,7 +74,7 @@ void Functions::compute_dilute_cape(
//----------------------------------------------------------------------------
// Copy the incoming temperature and specific humidity values to local arrays
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
temperature(k) = temperature_in(k);
sp_humidity(k) = sp_humidity_in(k);
});
@@ -97,14 +97,14 @@ void Functions::compute_dilute_cape(
//----------------------------------------------------------------------------
// calculate virtual temperature
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
tv(k) = temperature(k) * (1 + PC::ZVIR * sp_humidity(k)) / (1 + sp_humidity(k));
});
team.team_barrier();
//----------------------------------------------------------------------------
// Initialize parcel properties
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
parcel_temp(k) = temperature(k);
parcel_qsat(k) = sp_humidity(k);
parcel_vtemp(k) = tv(k);
@@ -124,6 +124,7 @@ void Functions::compute_dilute_cape(
}
});
}
+ team.team_barrier();
//----------------------------------------------------------------------------
// Set level of max moist static energy for parcel initialization
@@ -140,6 +141,7 @@ void Functions::compute_dilute_cape(
temperature, zmid, sp_humidity,
msemax_klev, mse_max_val);
}
+ team.team_barrier();
//----------------------------------------------------------------------------
// Save launching level T, q for output
diff --git a/components/eamxx/src/physics/zm/impl/zm_compute_dilute_parcel_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_compute_dilute_parcel_impl.hpp
index a21bd1b77e57..4d34aae23625 100644
--- a/components/eamxx/src/physics/zm/impl/zm_compute_dilute_parcel_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_compute_dilute_parcel_impl.hpp
@@ -53,7 +53,7 @@ void Functions::compute_dilute_parcel(
}
// Initialize arrays
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
tmix(k) = 0.0;
qtmix(k) = 0.0;
qsmix(k) = 0.0;
diff --git a/components/eamxx/src/physics/zm/impl/zm_input_state_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_input_state_impl.hpp
index 42cb882ef343..1ad930030e17 100644
--- a/components/eamxx/src/physics/zm/impl/zm_input_state_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_input_state_impl.hpp
@@ -33,28 +33,40 @@ void Functions::ZmInputState::transpose(int ncol, int nlev_mid)
const auto loc_f_z_int = f_z_int;
const auto loc_f_p_int = f_p_int;
+ const auto loc_z_mid = z_mid;
+ const auto loc_p_mid = p_mid;
+ const auto loc_p_del = p_del;
+ const auto loc_T_mid = T_mid;
+ const auto loc_qv = qv;
+ const auto loc_uwind = uwind;
+ const auto loc_vwind = vwind;
+ const auto loc_omega = omega;
+ const auto loc_cldfrac = cldfrac;
+ const auto loc_z_int = z_int;
+ const auto loc_p_int = p_int;
+
//----------------------------------------------------------------------
// mid-point level variables
Kokkos::parallel_for("zm_output_tx_mid",KT::RangePolicy(0, ncol*nlev_mid_packs), KOKKOS_LAMBDA (const int i) {
const int icol = i/nlev_mid_packs;
const int klev = i%nlev_mid_packs;
- loc_f_z_mid (icol,klev) = z_mid (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_p_mid (icol,klev) = p_mid (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_p_del (icol,klev) = p_del (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_T_mid (icol,klev) = T_mid (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_qv (icol,klev) = qv (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_uwind (icol,klev) = uwind (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_vwind (icol,klev) = vwind (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_omega (icol,klev) = omega (icol,klev/Pack::n)[klev%Pack::n];
- loc_f_cldfrac (icol,klev) = cldfrac (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_z_mid (icol,klev) = loc_z_mid (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_p_mid (icol,klev) = loc_p_mid (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_p_del (icol,klev) = loc_p_del (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_T_mid (icol,klev) = loc_T_mid (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_qv (icol,klev) = loc_qv (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_uwind (icol,klev) = loc_uwind (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_vwind (icol,klev) = loc_vwind (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_omega (icol,klev) = loc_omega (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_cldfrac (icol,klev) = loc_cldfrac (icol,klev/Pack::n)[klev%Pack::n];
});
// interface level variables
Kokkos::parallel_for("zm_output_tx_mid",KT::RangePolicy(0, ncol*nlev_int_packs), KOKKOS_LAMBDA (const int i) {
const int icol = i/nlev_int_packs;
const int klev = i%nlev_int_packs;
- f_z_int (icol,klev) = z_int (icol,klev/Pack::n)[klev%Pack::n];
- f_p_int (icol,klev) = p_int (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_z_int (icol,klev) = loc_z_int (icol,klev/Pack::n)[klev%Pack::n];
+ loc_f_p_int (icol,klev) = loc_p_int (icol,klev/Pack::n)[klev%Pack::n];
});
//----------------------------------------------------------------------
diff --git a/components/eamxx/src/physics/zm/impl/zm_output_tend_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_output_tend_impl.hpp
index d42f1ecfc108..84d96a2bd384 100644
--- a/components/eamxx/src/physics/zm/impl/zm_output_tend_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_output_tend_impl.hpp
@@ -47,26 +47,36 @@ void Functions::ZmOutputTend::transpose(int ncol, int nlev_mid)
const auto loc_snow_flux = snow_flux;
const auto loc_mass_flux = mass_flux;
+ const auto loc_f_tend_t = f_tend_t;
+ const auto loc_f_tend_qv = f_tend_qv;
+ const auto loc_f_tend_u = f_tend_u;
+ const auto loc_f_tend_v = f_tend_v;
+ const auto loc_f_rain_prod = f_rain_prod;
+ const auto loc_f_snow_prod = f_snow_prod;
+ const auto loc_f_prec_flux = f_prec_flux;
+ const auto loc_f_snow_flux = f_snow_flux;
+ const auto loc_f_mass_flux = f_mass_flux;
+
//----------------------------------------------------------------------
// mid-point level variables
Kokkos::parallel_for("zm_output_tx_mid",KT::RangePolicy(0, ncol*nlev_mid_packs), KOKKOS_LAMBDA (const int i) {
const int icol = i/nlev_mid_packs;
const int klev = i%nlev_mid_packs;
- loc_tend_t (icol,klev/Pack::n)[klev%Pack::n] = f_tend_t (icol,klev);
- loc_tend_qv (icol,klev/Pack::n)[klev%Pack::n] = f_tend_qv (icol,klev);
- loc_tend_u (icol,klev/Pack::n)[klev%Pack::n] = f_tend_u (icol,klev);
- loc_tend_v (icol,klev/Pack::n)[klev%Pack::n] = f_tend_v (icol,klev);
- loc_rain_prod(icol,klev/Pack::n)[klev%Pack::n] = f_rain_prod(icol,klev);
- loc_snow_prod(icol,klev/Pack::n)[klev%Pack::n] = f_snow_prod(icol,klev);
+ loc_tend_t (icol,klev/Pack::n)[klev%Pack::n] = loc_f_tend_t (icol,klev);
+ loc_tend_qv (icol,klev/Pack::n)[klev%Pack::n] = loc_f_tend_qv (icol,klev);
+ loc_tend_u (icol,klev/Pack::n)[klev%Pack::n] = loc_f_tend_u (icol,klev);
+ loc_tend_v (icol,klev/Pack::n)[klev%Pack::n] = loc_f_tend_v (icol,klev);
+ loc_rain_prod(icol,klev/Pack::n)[klev%Pack::n] = loc_f_rain_prod(icol,klev);
+ loc_snow_prod(icol,klev/Pack::n)[klev%Pack::n] = loc_f_snow_prod(icol,klev);
});
// interface level variables
Kokkos::parallel_for("zm_output_tx_mid",KT::RangePolicy(0, ncol*nlev_int_packs), KOKKOS_LAMBDA (const int i) {
const int icol = i/nlev_int_packs;
const int klev = i%nlev_int_packs;
- loc_prec_flux(icol,klev/Pack::n)[klev%Pack::n] = f_prec_flux(icol,klev);
- loc_snow_flux(icol,klev/Pack::n)[klev%Pack::n] = f_snow_flux(icol,klev);
- loc_mass_flux(icol,klev/Pack::n)[klev%Pack::n] = f_mass_flux(icol,klev);
+ loc_prec_flux(icol,klev/Pack::n)[klev%Pack::n] = loc_f_prec_flux(icol,klev);
+ loc_snow_flux(icol,klev/Pack::n)[klev%Pack::n] = loc_f_snow_flux(icol,klev);
+ loc_mass_flux(icol,klev/Pack::n)[klev%Pack::n] = loc_f_mass_flux(icol,klev);
});
}
// ***********************************************************************
diff --git a/components/eamxx/src/physics/zm/impl/zm_zm_conv_mcsp_tend_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_zm_conv_mcsp_tend_impl.hpp
new file mode 100644
index 000000000000..4f60e73487c8
--- /dev/null
+++ b/components/eamxx/src/physics/zm/impl/zm_zm_conv_mcsp_tend_impl.hpp
@@ -0,0 +1,218 @@
+#ifndef ZM_ZM_CONV_MCSP_TEND_IMPL_HPP
+#define ZM_ZM_CONV_MCSP_TEND_IMPL_HPP
+
+#include "zm_functions.hpp" // for ETI only but harmless for GPU
+
+namespace scream {
+namespace zm {
+
+/*
+ * Implementation of zm zm_conv_mcsp_tend. Clients should NOT
+ * #include this file, but include zm_functions.hpp instead.
+ */
+
+template
+KOKKOS_FUNCTION
+void Functions::zm_conv_mcsp_tend(
+ // Inputs
+ const MemberType& team,
+ const Workspace& workspace,
+ const ZmRuntimeOpt& runtime_opt,
+ const Int& pver, // number of mid-point vertical levels
+ const Int& pverp, // number of interface vertical levels
+ const Real& ztodt, // 2x physics time step
+ const Int& jctop, // cloud top level indices
+ const uview_1d& state_pmid, // physics state mid-point pressure
+ const uview_1d& state_pint, // physics state interface pressure
+ const uview_1d& state_pdel, // physics state pressure thickness
+ const uview_1d& state_s, // physics state dry energy
+ const uview_1d& state_q, // physics state specific humidity
+ const uview_1d& state_u, // physics state u momentum
+ const uview_1d& state_v, // physics state v momentum
+ const uview_1d& ptend_zm_s, // input ZM tendency for dry energy (DSE)
+ const uview_1d& ptend_zm_q, // input ZM tendency for specific humidity (qv)
+ // Inputs/Outputs
+ const uview_1d& ptend_s, // output tendency of DSE
+ const uview_1d& ptend_q, // output tendency of qv
+ const uview_1d& ptend_u, // output tendency of u-wind
+ const uview_1d& ptend_v, // output tendency of v-wind
+ // Outputs
+ const uview_1d& mcsp_dt_out, // final MCSP tendency for DSE
+ const uview_1d& mcsp_dq_out, // final MCSP tendency for qv
+ const uview_1d& mcsp_du_out, // final MCSP tendency for u wind
+ const uview_1d& mcsp_dv_out, // final MCSP tendency for v wind
+ Real& mcsp_freq, // MSCP frequency for output
+ Real& mcsp_shear, // shear used to check against threshold
+ Real& zm_depth) // pressure depth of ZM heating
+{
+ //----------------------------------------------------------------------------
+ // Purpose: perform MCSP tendency calculations
+ //----------------------------------------------------------------------------
+
+ if (!runtime_opt.mcsp_enabled) return;
+
+ //----------------------------------------------------------------------------
+ // initialize variables
+
+ const bool do_mcsp_t = (runtime_opt.mcsp_t_coeff > 0);
+ const bool do_mcsp_q = (runtime_opt.mcsp_q_coeff > 0);
+ const bool do_mcsp_u = (runtime_opt.mcsp_u_coeff > 0);
+ const bool do_mcsp_v = (runtime_opt.mcsp_v_coeff > 0);
+
+ // Allocate temporary arrays
+ uview_1d mcsp_tend_s, mcsp_tend_q, mcsp_tend_u, mcsp_tend_v;
+ workspace.template take_many_contiguous_unsafe<4>(
+ {"mcsp_tend_s", "mcsp_tend_q", "mcsp_tend_u", "mcsp_tend_v"},
+ {&mcsp_tend_s, &mcsp_tend_q, &mcsp_tend_u, &mcsp_tend_v});
+
+ Real zm_avg_tend_s = 0.0; // mass weighted column average DSE tendency from ZM
+ Real zm_avg_tend_q = 0.0; // mass weighted column average qv tendency from ZM
+ Real pdel_sum = 0.0; // column integrated pressure thickness
+ Real mcsp_avg_tend_s = 0.0; // mass weighted column average MCSP tendency of DSE
+ Real mcsp_avg_tend_q = 0.0; // mass weighted column average MCSP tendency of qv
+ Real mcsp_avg_tend_k = 0.0; // mass weighted column average MCSP tendency of kinetic energy
+
+ // Initialize arrays
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver), [&] (const Int& k) {
+ mcsp_tend_s(k) = 0.0;
+ mcsp_tend_q(k) = 0.0;
+ mcsp_tend_u(k) = 0.0;
+ mcsp_tend_v(k) = 0.0;
+ mcsp_dt_out(k) = 0.0;
+ mcsp_dq_out(k) = 0.0;
+ mcsp_du_out(k) = 0.0;
+ mcsp_dv_out(k) = 0.0;
+ });
+ team.team_barrier();
+
+ //----------------------------------------------------------------------------
+ // calculate shear
+
+ zm_conv_mcsp_calculate_shear(team, pver, state_pmid, state_u, mcsp_shear);
+
+ //----------------------------------------------------------------------------
+ // calculate mass weighted column average tendencies from ZM
+
+ zm_depth = 0.0;
+ if (jctop != pver - 1) {
+ // integrate pressure and ZM tendencies over column using parallel reduction
+ Kokkos::parallel_reduce(Kokkos::TeamVectorRange(team, jctop, pver),
+ [&] (const Int& k, Real& tend_s_sum, Real& tend_q_sum, Real& pdel_tot) {
+ tend_s_sum += ptend_zm_s(k) * state_pdel(k);
+ tend_q_sum += ptend_zm_q(k) * state_pdel(k);
+ pdel_tot += state_pdel(k);
+ },
+ zm_avg_tend_s, zm_avg_tend_q, pdel_sum);
+ team.team_barrier();
+
+ // normalize integrated ZM tendencies by total mass
+ zm_avg_tend_s /= pdel_sum;
+ zm_avg_tend_q /= pdel_sum;
+ // calculate diagnostic zm_depth
+ zm_depth = state_pint(pver) - state_pmid(jctop);
+ }
+ team.team_barrier();
+
+ //----------------------------------------------------------------------------
+ // Note: To conserve total energy we need to account for the kinteic energy tendency
+ // which we can obtain from the velocity tendencies based on the following:
+ // KE_new = (u_new^2 + v_new^2)/2
+ // = [ (u_old+du)^2 + (v_old+dv)^2 ]/2
+ // = [ ( u_old^2 + 2*u_old*du + du^2 ) + ( v_old^2 + 2*v_old*dv + dv^2 ) ]/2
+ // = ( u_old^2 + v_old^2 )/2 + ( 2*u_old*du + du^2 + 2*v_old*dv + dv^2 )/2
+ // = KE_old + [ 2*u_old*du + du^2 + 2*v_old*dv + dv^2 ] /2
+
+ //----------------------------------------------------------------------------
+ // calculate MCSP tendencies
+
+ // check that ZM produced tendencies over a depth that exceeds the threshold
+ if (zm_depth >= ZMC::MCSP_conv_depth_min) {
+ // check that ZM provided a non-zero column total heating
+ if (zm_avg_tend_s > 0.0) {
+ // check that there is sufficient wind shear to justify coherent organization
+ if (std::abs(mcsp_shear) >= ZMC::MCSP_shear_min &&
+ std::abs(mcsp_shear) < ZMC::MCSP_shear_max) {
+ // Calculate tendencies and integrate them using parallel reduce
+ Kokkos::parallel_reduce(Kokkos::TeamVectorRange(team, jctop, pver),
+ [&] (const Int& k, Real& avg_s, Real& avg_q, Real& avg_k) {
+
+ // See eq 7-8 of Moncrieff et al. (2017) - also eq (5) of Moncrieff & Liu (2006)
+ const Real pdepth_mid_k = state_pint(pver) - state_pmid(k);
+ const Real pdepth_total = state_pint(pver) - state_pmid(jctop);
+
+ // specify the assumed vertical structure
+ if (do_mcsp_t) mcsp_tend_s(k) = -1 * runtime_opt.mcsp_t_coeff * std::sin(2 * PC::Pi * (pdepth_mid_k / pdepth_total));
+ if (do_mcsp_q) mcsp_tend_q(k) = -1 * runtime_opt.mcsp_q_coeff * std::sin(2 * PC::Pi * (pdepth_mid_k / pdepth_total));
+ if (do_mcsp_u) mcsp_tend_u(k) = runtime_opt.mcsp_u_coeff * (std::cos(PC::Pi * (pdepth_mid_k / pdepth_total)));
+ if (do_mcsp_v) mcsp_tend_v(k) = runtime_opt.mcsp_v_coeff * (std::cos(PC::Pi * (pdepth_mid_k / pdepth_total)));
+
+ // scale the vertical structure by the ZM heating/drying tendencies
+ if (do_mcsp_t) mcsp_tend_s(k) = zm_avg_tend_s * mcsp_tend_s(k);
+ if (do_mcsp_q) mcsp_tend_q(k) = zm_avg_tend_q * mcsp_tend_q(k);
+
+ // integrate the DSE/qv tendencies for energy/mass fixer
+ if (do_mcsp_t) avg_s += mcsp_tend_s(k) * state_pdel(k) / pdel_sum;
+ if (do_mcsp_q) avg_q += mcsp_tend_q(k) * state_pdel(k) / pdel_sum;
+
+ // integrate the change in kinetic energy (KE) for energy fixer
+ if (do_mcsp_u || do_mcsp_v) {
+ const Real tend_k = (2 * mcsp_tend_u(k) * ztodt * state_u(k) + mcsp_tend_u(k) * mcsp_tend_u(k) * ztodt * ztodt
+ + 2 * mcsp_tend_v(k) * ztodt * state_v(k) + mcsp_tend_v(k) * mcsp_tend_v(k) * ztodt * ztodt) / 2 / ztodt;
+ avg_k += tend_k * state_pdel(k) / pdel_sum;
+ }
+ },
+ mcsp_avg_tend_s, mcsp_avg_tend_q, mcsp_avg_tend_k);
+ team.team_barrier();
+ } // shear threshold
+ } // zm_avg_tend_s > 0
+ } // zm_depth >= MCSP_conv_depth_min
+
+ //----------------------------------------------------------------------------
+ // calculate final output tendencies
+
+ mcsp_freq = 0.0;
+ bool any_tend = false;
+
+ // Calculate final tendencies and check frequency in a single parallel_reduce
+ Kokkos::parallel_reduce(Kokkos::TeamVectorRange(team, jctop, pver),
+ [&] (const Int& k, bool& local_freq) {
+ // subtract mass weighted average tendencies for energy/mass conservation
+ mcsp_dt_out(k) = mcsp_tend_s(k) - mcsp_avg_tend_s;
+ mcsp_dq_out(k) = mcsp_tend_q(k) - mcsp_avg_tend_q;
+ mcsp_du_out(k) = mcsp_tend_u(k);
+ mcsp_dv_out(k) = mcsp_tend_v(k);
+
+ // make sure kinetic energy correction is added to DSE tendency
+ // to conserve total energy whenever momentum tendencies are calculated
+ if (do_mcsp_u || do_mcsp_v) {
+ mcsp_dt_out(k) = mcsp_dt_out(k) - mcsp_avg_tend_k;
+ }
+
+ // update output tendencies
+ if (do_mcsp_t) ptend_s(k) = ptend_s(k) + mcsp_dt_out(k);
+ if (do_mcsp_q) ptend_q(k) = ptend_q(k) + mcsp_dq_out(k);
+ if (do_mcsp_u) ptend_u(k) = ptend_u(k) + mcsp_du_out(k);
+ if (do_mcsp_v) ptend_v(k) = ptend_v(k) + mcsp_dv_out(k);
+
+ // adjust units for diagnostic outputs
+ if (do_mcsp_t) mcsp_dt_out(k) = mcsp_dt_out(k) / PC::Cpair.value;
+
+ // update frequency if MCSP contributes any tendency in the column
+ if (std::abs(mcsp_tend_s(k)) > 0.0 || std::abs(mcsp_tend_q(k)) > 0.0 ||
+ std::abs(mcsp_tend_u(k)) > 0.0 || std::abs(mcsp_tend_v(k)) > 0.0) {
+ local_freq = true;
+ }
+ },
+ Kokkos::LOr(any_tend));
+ team.team_barrier();
+
+ if (any_tend) mcsp_freq = 1.0;
+
+ workspace.template release_many_contiguous<4>(
+ {&mcsp_tend_s, &mcsp_tend_q, &mcsp_tend_u, &mcsp_tend_v});
+}
+
+} // namespace zm
+} // namespace scream
+
+#endif
diff --git a/components/eamxx/src/physics/zm/impl/zm_zm_transport_momentum_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_zm_transport_momentum_impl.hpp
index 2042565528fb..eb180c100667 100644
--- a/components/eamxx/src/physics/zm/impl/zm_zm_transport_momentum_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_zm_transport_momentum_impl.hpp
@@ -70,7 +70,7 @@ void Functions::zm_transport_momentum(
mflux(mflux_1d.data(), nwind, pverp);
// Initialize outputs
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, pver*nwind), [&] (const Int& idx) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pver*nwind), [&] (const Int& idx) {
const Int k = idx / nwind;
const Int m = idx % nwind;
wind_tend(k,m) = 0.0;
@@ -92,7 +92,7 @@ void Functions::zm_transport_momentum(
team.team_barrier();
// Loop over each wind component using team parallelism
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nwind), [&] (const Int& m) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, nwind), [&] (const Int& m) {
// Gather up the winds
for (Int k = 0; k < pver; ++k) {
@@ -223,7 +223,7 @@ void Functions::zm_transport_momentum(
//----------------------------------------------------------------------------
// Need to add an energy fix to account for the dissipation of kinetic energy
// Formulation follows from Boville and Bretherton (2003) - modified by Phil Rasch
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, ktm, pver), [&] (const Int& k) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, ktm, pver), [&] (const Int& k) {
const Int km1 = ekat::impl::max(0, k-1);
const Int kp1 = ekat::impl::min(pver-1, k+1);
diff --git a/components/eamxx/src/physics/zm/impl/zm_zm_transport_tracer_impl.hpp b/components/eamxx/src/physics/zm/impl/zm_zm_transport_tracer_impl.hpp
index a1ccbbff73d0..559423556a77 100644
--- a/components/eamxx/src/physics/zm/impl/zm_zm_transport_tracer_impl.hpp
+++ b/components/eamxx/src/physics/zm/impl/zm_zm_transport_tracer_impl.hpp
@@ -58,7 +58,7 @@ void Functions::zm_transport_tracer(
dptmp(dptmp1d.data(), ncnst, pver);
// Parallel loop over each constituent (skip water vapor at m=0)
- Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 1, ncnst), [&] (const Int& m) {
+ Kokkos::parallel_for(Kokkos::TeamVectorRange(team, 1, ncnst), [&] (const Int& m) {
if (!doconvtran(m)) return;
// Initialize temporary arrays (always use moist formulation)
diff --git a/components/eamxx/src/physics/zm/tests/CMakeLists.txt b/components/eamxx/src/physics/zm/tests/CMakeLists.txt
index 7a070aedc9c1..6b457889cd3d 100644
--- a/components/eamxx/src/physics/zm/tests/CMakeLists.txt
+++ b/components/eamxx/src/physics/zm/tests/CMakeLists.txt
@@ -12,6 +12,7 @@ set(ZM_TESTS_SRCS
zm_compute_dilute_parcel_tests.cpp
zm_compute_cape_from_parcel_tests.cpp
zm_zm_conv_mcsp_calculate_shear_tests.cpp
+ zm_zm_conv_mcsp_tend_tests.cpp
) # ZM_TESTS_SRCS
# All tests should understand the same baseline args
diff --git a/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90 b/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90
index 233b7eb95409..e61875bb8622 100644
--- a/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90
+++ b/components/eamxx/src/physics/zm/tests/infra/zm_c2f_bridge.f90
@@ -181,4 +181,27 @@ subroutine zm_conv_mcsp_calculate_shear_bridge_f(pcols, ncol, pver, state_pmid,
call zm_conv_mcsp_calculate_shear(pcols, ncol, pver, state_pmid, state_u, state_v, mcsp_shear)
end subroutine zm_conv_mcsp_calculate_shear_bridge_f
+subroutine zm_conv_mcsp_tend_bridge_f(pcols, ncol, pver, pverp, ztodt, jctop, state_pmid, state_pint, state_pdel, state_s, state_q, state_u, state_v, ptend_zm_s, ptend_zm_q, ptend_s, ptend_q, ptend_u, ptend_v, mcsp_dt_out, mcsp_dq_out, mcsp_du_out, mcsp_dv_out, mcsp_freq, mcsp_shear, zm_depth) bind(C)
+ use zm_conv_mcsp, only : zm_conv_mcsp_tend
+ use zm_conv_types, only: zm_const_t, zm_param_t
+ use zm_conv_types, only: zm_param_set_for_testing, zm_const_set_for_testing
+
+ integer(kind=c_int) , value, intent(in) :: pcols, ncol, pver, pverp
+ real(kind=c_real) , value, intent(in) :: ztodt
+ integer(kind=c_int) , intent(in), dimension(pcols) :: jctop
+ real(kind=c_real) , intent(in), dimension(pcols, pver) :: state_pmid, state_pdel, state_s, state_q, state_u, state_v, ptend_zm_s, ptend_zm_q
+ real(kind=c_real) , intent(in), dimension(pcols, pverp) :: state_pint
+ real(kind=c_real) , intent(inout), dimension(pcols, pver) :: ptend_s, ptend_q, ptend_u, ptend_v
+ real(kind=c_real) , intent(out), dimension(pcols, pver) :: mcsp_dt_out, mcsp_dq_out, mcsp_du_out, mcsp_dv_out
+ real(kind=c_real) , intent(out), dimension(pcols) :: mcsp_freq, mcsp_shear, zm_depth
+
+ type(zm_const_t) :: zm_const ! derived type to hold ZM constants
+ type(zm_param_t) :: zm_param ! derived type to hold ZM tunable parameters
+ !-----------------------------------------------------------------------------
+ call zm_param_set_for_testing(zm_param)
+ call zm_const_set_for_testing(zm_const)
+
+ call zm_conv_mcsp_tend(pcols, ncol, pver, pverp, ztodt, jctop, zm_const, zm_param, state_pmid, state_pint, state_pdel, state_s, state_q, state_u, state_v, ptend_zm_s, ptend_zm_q, ptend_s, ptend_q, ptend_u, ptend_v, mcsp_dt_out, mcsp_dq_out, mcsp_du_out, mcsp_dv_out, mcsp_freq, mcsp_shear, zm_depth)
+end subroutine zm_conv_mcsp_tend_bridge_f
+
end module zm_c2f_bridge
diff --git a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp
index e7240f42b99c..db8d14e8ed26 100644
--- a/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp
+++ b/components/eamxx/src/physics/zm/tests/infra/zm_test_data.cpp
@@ -54,6 +54,8 @@ void compute_dilute_parcel_bridge_f(Int pcols, Int ncol, Int pver, Int num_msg,
void compute_cape_from_parcel_bridge_f(Int pcols, Int ncol, Int pver, Int pverp, Int num_cin, Int num_msg, Real* temperature, Real* tv, Real* sp_humidity, Real* pint, Int* msemax_klev, Real* lcl_pmid, Int* lcl_klev, Real* parcel_qsat, Real* parcel_temp, Real* parcel_vtemp, Int* eql_klev, Real* cape);
void zm_conv_mcsp_calculate_shear_bridge_f(Int pcols, Int ncol, Int pver, Real* state_pmid, Real* state_u, Real* state_v, Real* mcsp_shear);
+
+void zm_conv_mcsp_tend_bridge_f(Int pcols, Int ncol, Int pver, Int pverp, Real ztodt, Int* jctop, Real* state_pmid, Real* state_pint, Real* state_pdel, Real* state_s, Real* state_q, Real* state_u, Real* state_v, Real* ptend_zm_s, Real* ptend_zm_q, Real* ptend_s, Real* ptend_q, Real* ptend_u, Real* ptend_v, Real* mcsp_dt_out, Real* mcsp_dq_out, Real* mcsp_du_out, Real* mcsp_dv_out, Real* mcsp_freq, Real* mcsp_shear, Real* zm_depth);
} // extern "C" : end _f decls
// Inits and finalizes are not intended to be called outside this comp unit
@@ -742,8 +744,7 @@ void compute_cape_from_parcel(ComputeCapeFromParcelData& d)
pint_d(vec2dr_in[3]),
sp_humidity_d(vec2dr_in[4]),
temperature_d(vec2dr_in[5]),
- tv_d(vec2dr_in[6]),
- zmid_d(vec2dr_in[7]);
+ tv_d(vec2dr_in[6]);
view1di_d
eql_klev_d(vec1di_in[0]),
@@ -863,6 +864,130 @@ void zm_conv_mcsp_calculate_shear(ZmConvMcspCalculateShearData& d)
zm_finalize_cxx();
}
+void zm_conv_mcsp_tend_f(ZmConvMcspTendData& d)
+{
+ d.transition();
+ zm_common_init_f();
+ zm_conv_mcsp_tend_bridge_f(d.pcols, d.ncol, d.pver, d.pverp, d.ztodt, d.jctop, d.state_pmid, d.state_pint, d.state_pdel, d.state_s, d.state_q, d.state_u, d.state_v, d.ptend_zm_s, d.ptend_zm_q, d.ptend_s, d.ptend_q, d.ptend_u, d.ptend_v, d.mcsp_dt_out, d.mcsp_dq_out, d.mcsp_du_out, d.mcsp_dv_out, d.mcsp_freq, d.mcsp_shear, d.zm_depth);
+ zm_common_finalize_f();
+ d.transition();
+}
+
+void zm_conv_mcsp_tend(ZmConvMcspTendData& d)
+{
+ zm_common_init();
+
+ // create device views and copy
+ std::vector