diff --git a/.gitmodules b/.gitmodules index 7dcc83ad..25eec705 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,7 +14,7 @@ path = src/dynamics/mpas/dycore url = https://github.com/MPAS-Dev/MPAS-Model.git fxsparse = ../.mpas_sparse_checkout - fxtag = v8.3.1 + fxtag = v8.4.0 fxrequired = AlwaysRequired fxDONOTUSEurl = https://github.com/MPAS-Dev/MPAS-Model.git [submodule "ncar-physics"] diff --git a/src/dynamics/mpas/assets/0001-Fix-Fortran-standard-violation.patch b/src/dynamics/mpas/assets/0001-Fix-Fortran-standard-violation.patch new file mode 100644 index 00000000..f1efd040 --- /dev/null +++ b/src/dynamics/mpas/assets/0001-Fix-Fortran-standard-violation.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kuan-Chih Wang +Date: Tue, 21 Apr 2026 13:50:09 -0600 +Subject: [PATCH] Fix Fortran standard violation due to pointer argument not + being associated + +At line 1970, if the `DO_PHYSICS` macro is undefined, the `diag_physics` pointer +will not be initialized by the call to `mpas_pool_get_subpool`, and therefore +its pointer association status will remain undefined. + +However, at line 2250, this pointer is used as an argument to call `atm_compute_dyn_tend`, +which constitutes a Fortran standard violation. + +Quoted from Fortran 2023, + +> 15.5.2.4 Argument association +> Except in references to intrinsic inquiry functions, a pointer actual argument that +> corresponds to a nonoptional nonpointer dummy argument shall be pointer associated with +> a target. + +This bug can lead to a runtime crash for models that use MPAS as a dynamical core +(e.g., CAM, CAM-SIMA). + +Fix this issue by adding the `pointer` attribute to the `diag_physics` dummy argument +for the `atm_compute_dyn_tend` subroutine. +--- + src/core_atmosphere/dynamics/mpas_atm_time_integration.F | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +index 238ca7235..c16072976 100644 +--- a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F ++++ b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +@@ -5813,7 +5813,7 @@ module atm_time_integration + type (mpas_pool_type), intent(in) :: state + type (mpas_pool_type), intent(in) :: diag + type (mpas_pool_type), intent(in) :: mesh +- type (mpas_pool_type), intent(in) :: diag_physics ++ type (mpas_pool_type), pointer, intent(in) :: diag_physics + type (mpas_pool_type), intent(in) :: configs + integer, intent(in) :: nVertLevels ! for allocating stack variables + integer, intent(in) :: rk_step, dynamics_substep +-- +2.48.1 + diff --git a/src/dynamics/mpas/assets/0002-Add-missing-argument-intent.patch b/src/dynamics/mpas/assets/0002-Add-missing-argument-intent.patch new file mode 100644 index 00000000..3c498315 --- /dev/null +++ b/src/dynamics/mpas/assets/0002-Add-missing-argument-intent.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kuan-Chih Wang +Date: Tue, 21 Apr 2026 14:35:05 -0600 +Subject: [PATCH] Add missing argument intent + +Throughout the `atm_compute_dyn_tend` subroutine, the `tend_physics` pointer +never changes its pointer association status. Add the missing `intent(in)` +attribute for better safety. +--- + src/core_atmosphere/dynamics/mpas_atm_time_integration.F | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +index c16072976..b6e620fe1 100644 +--- a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F ++++ b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +@@ -5809,7 +5809,7 @@ module atm_time_integration + ! Dummy arguments + ! + type (mpas_pool_type), intent(inout) :: tend +- type (mpas_pool_type), pointer :: tend_physics ++ type (mpas_pool_type), pointer, intent(in) :: tend_physics + type (mpas_pool_type), intent(in) :: state + type (mpas_pool_type), intent(in) :: diag + type (mpas_pool_type), intent(in) :: mesh +-- +2.48.1 + diff --git a/src/dynamics/mpas/assets/0003-Avoid-implicit-save-attribute.patch b/src/dynamics/mpas/assets/0003-Avoid-implicit-save-attribute.patch new file mode 100644 index 00000000..11dd67dd --- /dev/null +++ b/src/dynamics/mpas/assets/0003-Avoid-implicit-save-attribute.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kuan-Chih Wang +Date: Tue, 21 Apr 2026 14:49:58 -0600 +Subject: [PATCH] Avoid implicit `save` attribute during variable declaration + +In Fortran, explicit initialization of a variable implies the `save` attribute, +which may have surprising result and is not thread-safe. + +Avoid doing it to conform to the Fortran best practices. +--- + src/core_atmosphere/dynamics/mpas_atm_time_integration.F | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +index b6e620fe1..227fbde86 100644 +--- a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F ++++ b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +@@ -1918,7 +1918,7 @@ module atm_time_integration + type (mpas_pool_type), pointer :: diag_physics + type (mpas_pool_type), pointer :: mesh + type (mpas_pool_type), pointer :: tend +- type (mpas_pool_type), pointer :: tend_physics => null() ++ type (mpas_pool_type), pointer :: tend_physics + type (mpas_pool_type), pointer :: lbc ! regional_MPAS addition + + real (kind=RKIND), dimension(:,:), pointer :: w +-- +2.48.1 + diff --git a/src/dynamics/mpas/assets/Makefile.in.CESM b/src/dynamics/mpas/assets/Makefile.in.CESM index 93142018..cc37f547 100644 --- a/src/dynamics/mpas/assets/Makefile.in.CESM +++ b/src/dynamics/mpas/assets/Makefile.in.CESM @@ -37,7 +37,7 @@ export BUILD_TARGET := N/A export CORE := atmosphere export EXE_NAME := atmosphere_model export GEN_F90 := false -export GIT_VERSION := $(shell git -C "$(MPAS_SRC_ROOT)" describe --always --dirty --tags || echo "N/A") +export GIT_VERSION := $(shell git -C "$(MPAS_SRC_ROOT)" describe --always --dirty --tags 2>/dev/null || echo "N/A") export NAMELIST_SUFFIX := atmosphere # Customize variables (e.g., build options) for use with CESM. @@ -101,7 +101,7 @@ libmpas-prepare: libmpas-apply-patch libmpas-archiver-script.txt libmpas-preview .PHONY: libmpas-apply-patch libmpas-apply-patch: @for file in *.patch; do \ - if git apply --check -p2 "$${file}"; then \ + if [ -f "$${file}" ] && git apply --check -p2 "$${file}" 1>/dev/null 2>&1; then \ echo "Applying $${file}"; \ git apply -p2 "$${file}"; \ fi; \ diff --git a/src/dynamics/mpas/assets/generate_namelist_definition.py b/src/dynamics/mpas/assets/generate_namelist_definition.py index 938ff1d5..8e5865d7 100755 --- a/src/dynamics/mpas/assets/generate_namelist_definition.py +++ b/src/dynamics/mpas/assets/generate_namelist_definition.py @@ -5,6 +5,7 @@ ''' import argparse +import re import textwrap import xml.etree.ElementTree as ET @@ -14,6 +15,7 @@ 'assimilation', 'iau', 'limited_area', + 'musica', 'physics', 'restart' ] @@ -101,7 +103,7 @@ def parse_argument() -> argparse.Namespace: default=None, type=str, required=False, - help='XML schema for CAM-SIMA namelist definition file.', + help='XML schema (i.e., entry_id_pg.xsd) for CAM-SIMA namelist definition file.', dest='nml_xsd' ) @@ -193,13 +195,21 @@ def translate_element_tree(reg_xml_et: ET.ElementTree) -> ET.ElementTree: group_element.text = transform_name(namelist_group.attrib['name'].strip().lower()) # The `type` element. + # The `units` element. # The `values` element and its containing `value` element. type_text = namelist_option.attrib['type'].strip().lower() + if 'units' in namelist_option.attrib and namelist_option.attrib['units'].strip() not in ('', '-'): + # Filter out illegal characters in accordance with the `entry_id_pg.xsd` XML schema. + units_text = re.sub('[^-+A-Za-z0-9 ]', '', namelist_option.attrib['units'].strip()) + else: + units_text = 'none' value_text = namelist_option.attrib['default_value'] # Do some sanitization. if type_text.startswith('ch'): type_text = 'char*256' + # The units of a Fortran character variable is customarily 'none' in accordance with the ESM Standard Names. + units_text = 'none' value_text = value_text.strip() if not value_text.isascii(): @@ -209,6 +219,8 @@ def translate_element_tree(reg_xml_et: ET.ElementTree) -> ET.ElementTree: value_text = canonicalize_int(value_text) elif type_text.startswith('lo'): type_text = 'logical' + # The units of a Fortran logical variable is customarily 'flag' in accordance with the ESM Standard Names. + units_text = 'flag' value_text = value_text.strip().lower() if value_text.startswith(('t', '.t')): @@ -226,6 +238,9 @@ def translate_element_tree(reg_xml_et: ET.ElementTree) -> ET.ElementTree: type_element = ET.SubElement(entry_element, 'type') type_element.text = type_text + units_element = ET.SubElement(entry_element, 'units') + units_element.text = units_text + values_element = ET.SubElement(entry_element, 'values') if entry_element.attrib['id'] in OVERRIDDEN_NAMELIST_OPTION: diff --git a/src/dynamics/mpas/driver/dyn_mpas_subdriver.F90 b/src/dynamics/mpas/driver/dyn_mpas_subdriver.F90 index ed73cf32..7a62ba3d 100644 --- a/src/dynamics/mpas/driver/dyn_mpas_subdriver.F90 +++ b/src/dynamics/mpas/driver/dyn_mpas_subdriver.F90 @@ -85,6 +85,7 @@ end subroutine model_error_if type(domain_type), pointer :: domain_ptr => null() ! Initialized by `dyn_mpas_init_phase3`. + logical :: les_model = .false. integer :: number_of_constituents = 0 ! Initialized by `dyn_mpas_define_scalar`. @@ -202,8 +203,6 @@ end subroutine model_error_if var_info_type('bdyMaskEdge' , 'integer' , 1), & var_info_type('bdyMaskVertex' , 'integer' , 1), & var_info_type('cellTangentPlane' , 'real' , 3), & - var_info_type('cell_gradient_coef_x' , 'real' , 2), & - var_info_type('cell_gradient_coef_y' , 'real' , 2), & var_info_type('cellsOnCell' , 'integer' , 2), & var_info_type('cellsOnEdge' , 'integer' , 2), & var_info_type('cellsOnVertex' , 'integer' , 2), & @@ -212,8 +211,6 @@ end subroutine model_error_if var_info_type('cf3' , 'real' , 0), & var_info_type('coeffs_reconstruct' , 'real' , 3), & var_info_type('dcEdge' , 'real' , 1), & - var_info_type('defc_a' , 'real' , 2), & - var_info_type('defc_b' , 'real' , 2), & var_info_type('deriv_two' , 'real' , 3), & var_info_type('dss' , 'real' , 2), & var_info_type('dvEdge' , 'real' , 1), & @@ -726,19 +723,38 @@ subroutine dyn_mpas_init_phase3(self, number_of_constituents, pio_file) character(*), parameter :: subname = 'dyn_mpas_subdriver::dyn_mpas_init_phase3' character(strkind) :: mesh_filename + character(strkind), pointer :: config_les_model integer :: mesh_format integer, pointer :: num_scalars type(mpas_pool_type), pointer :: mpas_pool call self % debug_print(log_level_debug, subname // ' entered') + nullify(config_les_model) nullify(mpas_pool) nullify(num_scalars) + call self % get_variable_pointer(config_les_model, 'cfg', 'config_les_model') + + self % les_model = (trim(adjustl(config_les_model)) /= 'none') + + nullify(config_les_model) + ! In MPAS, there must be at least one constituent, `qv`, which denotes water vapor mixing ratio. ! Because MPAS has some hard-coded array accesses through the `index_qv` index, it will crash ! (i.e., segmentation fault due to invalid memory access) if `qv` is not allocated. - self % number_of_constituents = max(1, number_of_constituents) + ! + ! Since version 8.4.0, if the large eddy simulation (LES) capability is enabled, two additional constituents, + ! `qc` and `tke`, must also exist. + ! + ! Such capability is implemented provided that: + ! 1. `qc` must be one of the constituents just like `qv`; + ! 2. `tke` is treated as an extra "phantom" constituent that is internal to MPAS only. + if (.not. self % les_model) then + self % number_of_constituents = max(1, number_of_constituents) ! "1" is for `qv`. + else + self % number_of_constituents = max(2, number_of_constituents) ! "2" is for `qv` and `qc`. + end if call self % debug_print(log_level_info, 'Number of constituents is ' // stringify([self % number_of_constituents])) @@ -746,7 +762,14 @@ subroutine dyn_mpas_init_phase3(self, number_of_constituents, pio_file) ! it is operating as a dynamical core, and therefore it needs to allocate scalars separately ! from other Registry-defined fields. The special logic is located in `atm_setup_block`. ! This must be done before calling `mpas_bootstrap_framework_phase1`. - call mpas_pool_add_config(self % domain_ptr % configs, 'cam_pcnst', self % number_of_constituents) + if (.not. self % les_model) then + ! No need to add an extra "phantom" constituent, `tke`. + call mpas_pool_add_config(self % domain_ptr % configs, 'cam_pcnst', self % number_of_constituents) + else + ! Need to add an extra "phantom" constituent, `tke`. There is additional logic + ! for it in `dyn_mpas_define_scalar`. + call mpas_pool_add_config(self % domain_ptr % configs, 'cam_pcnst', self % number_of_constituents + 1) + end if ! Not actually used because a PIO file descriptor is directly supplied. mesh_filename = 'external mesh' @@ -783,8 +806,14 @@ subroutine dyn_mpas_init_phase3(self, number_of_constituents, pio_file) end if ! While we are at it, check if its value is consistent. - if (num_scalars /= self % number_of_constituents) then - call self % model_error('Failed to allocate constituents', subname, __LINE__) + if (.not. self % les_model) then + if (num_scalars /= self % number_of_constituents) then + call self % model_error('Failed to allocate constituents', subname, __LINE__) + end if + else + if (num_scalars /= self % number_of_constituents + 1) then + call self % model_error('Failed to allocate constituents', subname, __LINE__) + end if end if call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'num_scalars', num_scalars) @@ -842,11 +871,20 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) 'water_vapor_mixing_ratio_wrt_moist_air_and_condensed_water' & ] + !> Possible CCPP standard names of `qc`, which denotes cloud liquid water mixing ratio. + !> They are hard-coded here because MPAS needs to know where `qc` is. + !> Index 1 is exactly what MPAS wants. Others also work, but need to be converted. + character(*), parameter :: mpas_scalar_qc_standard_name(*) = [character(strkind) :: & + 'cloud_liquid_water_mixing_ratio_wrt_dry_air', & + 'cloud_liquid_water_mixing_ratio_wrt_moist_air', & + 'cloud_liquid_water_mixing_ratio_wrt_moist_air_and_condensed_water' & + ] + character(*), parameter :: subname = 'dyn_mpas_subdriver::dyn_mpas_define_scalar' character(strkind) :: cerr integer :: i, j integer :: ierr - integer :: index_qv, index_water_start, index_water_end + integer :: index_qv, index_qc, index_tke, index_water_start, index_water_end integer :: time_level type(field3dreal), pointer :: field_3d_real type(mpas_pool_type), pointer :: mpas_pool @@ -856,37 +894,61 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) nullify(field_3d_real) nullify(mpas_pool) + ! `dyn_mpas_init_phase3` must be called before this subroutine. if (self % number_of_constituents == 0) then call self % model_error('Constituents must be allocated before being defined', subname, __LINE__) end if + allocate(self % constituent_name(self % number_of_constituents), errmsg=cerr, stat=ierr) + + if (ierr /= 0) then + call self % model_error('Failed to allocate constituent_name' // new_line('') // & + 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & + subname, __LINE__) + end if + + allocate(self % is_water_species(self % number_of_constituents), errmsg=cerr, stat=ierr) + + if (ierr /= 0) then + call self % model_error('Failed to allocate is_water_species' // new_line('') // & + 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & + subname, __LINE__) + end if + ! Input sanitization. if (size(constituent_name) /= size(is_water_species)) then call self % model_error('Mismatch between numbers of constituent names and their waterness', subname, __LINE__) end if - if (size(constituent_name) == 0 .and. self % number_of_constituents == 1) then - ! If constituent definitions are empty, `qv` is the only constituent per MPAS requirements. - ! See `dyn_mpas_init_phase3` for details. - allocate(self % constituent_name(1), errmsg=cerr, stat=ierr) + if (size(constituent_name) == 0) then + if (.not. self % les_model) then + ! If constituent definitions are empty and LES is disabled, `qv` is the only + ! one constituent as per MPAS requirements. + ! See `dyn_mpas_init_phase3` for details. + if (self % number_of_constituents /= 1) then + call self % model_error( & + 'Number of constituents must be 1 if no definitions are provided', subname, __LINE__) + end if - if (ierr /= 0) then - call self % model_error('Failed to allocate constituent_name' // new_line('') // & - 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & - subname, __LINE__) - end if + self % constituent_name(1) = mpas_scalar_qv_standard_name(1) + self % is_water_species(1) = .true. + else + ! If constituent definitions are empty and LES is enabled, `qv`, `qc`, and `tke` are the only + ! three constituents as per MPAS requirements. However, `tke` is excepted because it is + ! internal to MPAS only. + ! See `dyn_mpas_init_phase3` for details. + if (self % number_of_constituents /= 2) then + call self % model_error( & + 'Number of constituents must be 2 if no definitions are provided', subname, __LINE__) + end if - allocate(self % is_water_species(1), errmsg=cerr, stat=ierr) + self % constituent_name(1) = mpas_scalar_qv_standard_name(1) + self % is_water_species(1) = .true. - if (ierr /= 0) then - call self % model_error('Failed to allocate is_water_species' // new_line('') // & - 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & - subname, __LINE__) + self % constituent_name(2) = mpas_scalar_qc_standard_name(1) + self % is_water_species(2) = .true. end if - - self % constituent_name(1) = mpas_scalar_qv_standard_name(1) - self % is_water_species(1) = .true. else if (size(constituent_name) /= self % number_of_constituents) then call self % model_error('Mismatch between numbers of constituents and their names', subname, __LINE__) @@ -896,50 +958,62 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) call self % model_error('Constituent names are too long', subname, __LINE__) end if - allocate(self % constituent_name(self % number_of_constituents), errmsg=cerr, stat=ierr) - - if (ierr /= 0) then - call self % model_error('Failed to allocate constituent_name' // new_line('') // & - 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & - subname, __LINE__) - end if - self % constituent_name(:) = adjustl(constituent_name) - - allocate(self % is_water_species(self % number_of_constituents), errmsg=cerr, stat=ierr) - - if (ierr /= 0) then - call self % model_error('Failed to allocate is_water_species' // new_line('') // & - 'Allocation returned with ' // stringify([ierr]) // ': ' // trim(adjustl(cerr)), & - subname, __LINE__) - end if - self % is_water_species(:) = is_water_species(:) if (size(self % constituent_name) /= size(index_unique(self % constituent_name))) then call self % model_error('Constituent names must be unique', subname, __LINE__) end if + end if - ! `qv` must be present in constituents per MPAS requirements. It is a water species by definition. - ! See `dyn_mpas_init_phase3` for details. - index_qv = 0 + index_qv = 0 - ! Lower index in `mpas_scalar_qv_standard_name` has higher precedence, with index 1 being exactly what MPAS wants. - set_index_qv: do i = 1, size(mpas_scalar_qv_standard_name) - do j = 1, self % number_of_constituents - if (self % constituent_name(j) == mpas_scalar_qv_standard_name(i) .and. self % is_water_species(j)) then - index_qv = j + ! Lower index in `mpas_scalar_qv_standard_name` has higher precedence, with index 1 being exactly what MPAS wants. + set_index_qv: do i = 1, size(mpas_scalar_qv_standard_name) + do j = 1, self % number_of_constituents + if (trim(adjustl(self % constituent_name(j))) == trim(adjustl(mpas_scalar_qv_standard_name(i))) .and. & + self % is_water_species(j)) then + index_qv = j - ! The best candidate of `qv` has been found. Exit prematurely. - exit set_index_qv - end if - end do - end do set_index_qv + ! The best candidate of `qv` has been found. Exit prematurely. + exit set_index_qv + end if + end do + end do set_index_qv + + ! `qv` must be present in constituents as per MPAS requirements. It is a water species by definition. + ! See `dyn_mpas_init_phase3` for details. + if (index_qv == 0) then + call self % model_error('Constituent names must contain one of: ' // & + stringify(mpas_scalar_qv_standard_name) // ', and it must be a water species', subname, __LINE__) + end if + + index_qc = 0 + index_tke = 0 - if (index_qv == 0) then + ! Lower index in `mpas_scalar_qc_standard_name` has higher precedence, with index 1 being exactly what MPAS wants. + set_index_qc: do i = 1, size(mpas_scalar_qc_standard_name) + do j = 1, self % number_of_constituents + if (trim(adjustl(self % constituent_name(j))) == trim(adjustl(mpas_scalar_qc_standard_name(i))) .and. & + self % is_water_species(j)) then + index_qc = j + + ! The best candidate of `qc` has been found. Exit prematurely. + exit set_index_qc + end if + end do + end do set_index_qc + + ! If LES is enabled, `qc` and `tke` must be present in constituents as per MPAS requirements. + ! Otherwise, it is fine to not have them. + ! See `dyn_mpas_init_phase3` for details. + if (self % les_model) then + if (index_qc == 0) then call self % model_error('Constituent names must contain one of: ' // & - stringify(mpas_scalar_qv_standard_name) // ', and it must be a water species', subname, __LINE__) + stringify(mpas_scalar_qc_standard_name) // ', and it must be a water species', subname, __LINE__) end if + + index_tke = self % number_of_constituents + 1 end if ! Create index mapping between MPAS scalars and constituent names. For example, @@ -958,7 +1032,7 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) self % index_mpas_scalar_to_constituent(:) = 0 j = 1 - ! Place water species first per MPAS requirements. + ! Place water species first as per MPAS requirements. do i = 1, self % number_of_constituents if (self % is_water_species(i)) then self % index_mpas_scalar_to_constituent(j) = i @@ -969,7 +1043,7 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) index_water_start = 1 index_water_end = count(self % is_water_species) - ! Place non-water species second per MPAS requirements. + ! Place non-water species second as per MPAS requirements. do i = 1, self % number_of_constituents if (.not. self % is_water_species(i)) then self % index_mpas_scalar_to_constituent(j) = i @@ -999,6 +1073,11 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) ! Set the index of `qv` in terms of MPAS scalars. index_qv = self % index_constituent_to_mpas_scalar(index_qv) + ! Set the index of `qc`, if present, in terms of MPAS scalars. + if (index_qc > 0) then + index_qc = self % index_constituent_to_mpas_scalar(index_qc) + end if + ! Print information about constituents. do i = 1, self % number_of_constituents call self % debug_print(log_level_verbose, 'Constituent index ' // stringify([i])) @@ -1017,6 +1096,8 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) call self % get_pool_pointer(mpas_pool, 'state') call mpas_pool_add_dimension(mpas_pool, 'index_qv', index_qv) + call mpas_pool_add_dimension(mpas_pool, 'index_qc', index_qc) + call mpas_pool_add_dimension(mpas_pool, 'index_tke', index_tke) call mpas_pool_add_dimension(mpas_pool, 'moist_start', index_water_start) call mpas_pool_add_dimension(mpas_pool, 'moist_end', index_water_end) @@ -1046,6 +1127,10 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) end if end do + if (index_tke > 0) then + field_3d_real % constituentnames(index_tke) = 'tke_for_les' + end if + nullify(field_3d_real) end do @@ -1058,6 +1143,8 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) call self % get_pool_pointer(mpas_pool, 'tend') call mpas_pool_add_dimension(mpas_pool, 'index_qv', index_qv) + call mpas_pool_add_dimension(mpas_pool, 'index_qc', index_qc) + call mpas_pool_add_dimension(mpas_pool, 'index_tke', index_tke) call mpas_pool_add_dimension(mpas_pool, 'moist_start', index_water_start) call mpas_pool_add_dimension(mpas_pool, 'moist_end', index_water_end) @@ -1087,6 +1174,10 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) end if end do + if (index_tke > 0) then + field_3d_real % constituentnames(index_tke) = 'tendency_of_tke_for_les' + end if + nullify(field_3d_real) end do @@ -1095,10 +1186,14 @@ subroutine dyn_mpas_define_scalar(self, constituent_name, is_water_species) ! For consistency, also add dimension variables to MPAS "dimension" pool. call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'index_qv', index_qv) + call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'index_qc', index_qc) + call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'index_tke', index_tke) call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'moist_start', index_water_start) call mpas_pool_add_dimension(self % domain_ptr % blocklist % dimensions, 'moist_end', index_water_end) call self % debug_print(log_level_debug, 'index_qv = ' // stringify([index_qv])) + call self % debug_print(log_level_debug, 'index_qc = ' // stringify([index_qc])) + call self % debug_print(log_level_debug, 'index_tke = ' // stringify([index_tke])) call self % debug_print(log_level_debug, 'moist_start = ' // stringify([index_water_start])) call self % debug_print(log_level_debug, 'moist_end = ' // stringify([index_water_end])) diff --git a/src/dynamics/mpas/dycore b/src/dynamics/mpas/dycore index b9090a14..515e5203 160000 --- a/src/dynamics/mpas/dycore +++ b/src/dynamics/mpas/dycore @@ -1 +1 @@ -Subproject commit b9090a1434b8ceb2a92fc985f4d97e7947cd95a6 +Subproject commit 515e5203d8dc7b63192e50678284bd7da2ef6927 diff --git a/src/dynamics/mpas/namelist_definition_mpas_dycore.xml b/src/dynamics/mpas/namelist_definition_mpas_dycore.xml index 5e51ee13..eb3b3bc1 100644 --- a/src/dynamics/mpas/namelist_definition_mpas_dycore.xml +++ b/src/dynamics/mpas/namelist_definition_mpas_dycore.xml @@ -14,6 +14,7 @@ mpas_nhyd_model real + none 0.5 @@ -26,6 +27,7 @@ mpas_decomposition char*256 + none ${DIN_LOC_ROOT}/atm/cam/inic/mpas/mpasa480.graph.info.part. ${DIN_LOC_ROOT}/atm/cam/inic/mpas/mpasa120.graph.info.part. @@ -44,6 +46,7 @@ mpas_damping real + m s-1 0.0 @@ -55,6 +58,7 @@ mpas_nhyd_model real + none 1.0 @@ -67,6 +71,7 @@ mpas_nhyd_model real + none 10.0 @@ -78,6 +83,7 @@ mpas_nhyd_model real + s 1800.0 900.0 @@ -93,6 +99,7 @@ mpas_nhyd_model integer + none 3 @@ -105,10 +112,59 @@ mpas_nhyd_model real + none + + 0.0 + + + + mpas + + Value of epssm above transition zone + + mpas_damping + real + none + + 0.5 + + + + mpas + + Value of epssm below transition zone + + mpas_damping + real + none 0.1 + + mpas + + Height MSL of bottom of transition zone for epssm + + mpas_damping + real + m + + 30000.0 + + + + mpas + + Height MSL of top of transition zone for epssm + + mpas_damping + real + m + + 50000.0 + + mpas @@ -116,6 +172,20 @@ mpas_decomposition logical + flag + + .false. + + + + mpas + + Whether to use GPU-aware MPI for halo exchanges. GPU-aware MPI is + only implemented for config_halo_exch_method='mpas_halo' + + mpas_development + logical + flag .false. @@ -127,6 +197,7 @@ mpas_nhyd_model real + m2 s-1 0.0 @@ -138,6 +209,7 @@ mpas_nhyd_model real + m4 s-1 0.0 @@ -150,6 +222,7 @@ mpas_nhyd_model logical + flag .true. @@ -161,6 +234,7 @@ mpas_nhyd_model real + m2 s-1 0.0 @@ -172,6 +246,7 @@ mpas_nhyd_model real + m4 s-1 0.0 @@ -183,6 +258,7 @@ mpas_development char*256 + none mpas_halo @@ -194,6 +270,7 @@ mpas_nhyd_model char*256 + none 2d_smagorinsky @@ -206,6 +283,7 @@ mpas_nhyd_model real + m 480000.0 120000.0 @@ -213,6 +291,30 @@ 30000.0 + + mpas + + LES dissipation model + + mpas_nhyd_model + char*256 + none + + none + + + + mpas + + LES surface flux option + + mpas_nhyd_model + char*256 + none + + none + + mpas @@ -221,10 +323,23 @@ mpas_nhyd_model logical + flag .true. + + mpas + + Whether to enable horizontal and vertical mixing for scalars + + mpas_nhyd_model + logical + flag + + .false. + + mpas @@ -232,6 +347,7 @@ mpas_nhyd_model logical + flag .true. @@ -243,6 +359,7 @@ mpas_nhyd_model integer + none 2 @@ -256,6 +373,7 @@ mpas_damping integer + none 0 @@ -267,6 +385,7 @@ mpas_decomposition integer + none 0 @@ -278,6 +397,7 @@ mpas_nhyd_model integer + none 2 @@ -291,6 +411,7 @@ mpas_damping integer + none 5 @@ -302,6 +423,7 @@ mpas_io integer + none 0 @@ -313,6 +435,7 @@ mpas_io integer + none 1 @@ -324,6 +447,7 @@ mpas_nhyd_model logical + flag .false. @@ -337,6 +461,7 @@ mpas_printout logical + flag .true. @@ -348,6 +473,7 @@ mpas_printout logical + flag .false. @@ -360,6 +486,7 @@ mpas_printout logical + flag .true. @@ -371,6 +498,7 @@ mpas_decomposition char*256 + none graph.info.part. @@ -386,6 +514,7 @@ mpas_damping logical + flag .true. @@ -398,6 +527,7 @@ mpas_damping real + days 5.0 @@ -410,6 +540,7 @@ mpas_nhyd_model real + none 6.0 @@ -421,6 +552,7 @@ mpas_io char*256 + none restart_timestamp @@ -432,6 +564,7 @@ mpas_nhyd_model integer + none 3 @@ -443,6 +576,7 @@ mpas_nhyd_model logical + flag .true. @@ -454,6 +588,7 @@ mpas_nhyd_model integer + none 3 @@ -466,6 +601,7 @@ mpas_nhyd_model real + none 0.125 @@ -477,6 +613,7 @@ mpas_nhyd_model real + none 0.1 @@ -488,10 +625,47 @@ mpas_nhyd_model logical + flag .true. + + mpas + + Cd 10m drag coefficient + + mpas_nhyd_model + real + none + + 0.0 + + + + mpas + + Specified surface heat flux w'theta' + + mpas_nhyd_model + real + K m s-1 + + 0.0 + + + + mpas + + Specified surface moisture flux w'q' + + mpas_nhyd_model + real + kg m s-1 + + 0.0 + + mpas @@ -499,6 +673,7 @@ mpas_nhyd_model integer + none 3 @@ -510,6 +685,7 @@ mpas_nhyd_model integer + none 3 @@ -521,6 +697,7 @@ mpas_nhyd_model char*256 + none SRK3 @@ -532,6 +709,7 @@ mpas_nhyd_model integer + none 2 @@ -543,6 +721,7 @@ mpas_nhyd_model integer + none 3 @@ -554,6 +733,7 @@ mpas_nhyd_model real + m2 s-1 0.0 @@ -565,6 +745,7 @@ mpas_nhyd_model real + m2 s-1 0.0 @@ -572,11 +753,12 @@ mpas - Scaling coefficient of $\delta x^3$ to obtain $\nabla^4$ diffusion - coefficient + Coefficient multiplied by $\delta x^3$ to obtain $\nabla^4$ physical + hyperviscosity mpas_nhyd_model real + m s-1 0.05 @@ -588,6 +770,7 @@ mpas_nhyd_model integer + none 3 @@ -599,6 +782,7 @@ mpas_nhyd_model integer + none 3 @@ -610,6 +794,7 @@ mpas_damping real + s-1 0.2 @@ -621,6 +806,7 @@ mpas_damping real + m 22000.0