diff --git a/.github/workflows/eamxx-sa-coverage.yml b/.github/workflows/eamxx-sa-coverage.yml
index aa69d6263e6e..46e87f8b8b0c 100644
--- a/.github/workflows/eamxx-sa-coverage.yml
+++ b/.github/workflows/eamxx-sa-coverage.yml
@@ -2,6 +2,11 @@ name: eamxx-sa-coverage
on:
workflow_dispatch:
+ inputs:
+ submit:
+ description: 'Force cdash submission'
+ required: true
+ type: boolean
# Add schedule trigger for nightly runs at midnight MT (Standard Time)
schedule:
@@ -13,7 +18,8 @@ concurrency:
cancel-in-progress: true
env:
- submit: ${{ github.event_name == 'schedule' && 'true' || 'false' }} # Submit to cdash only for nightlies
+ # Submit to cdash only for nightlies or if the user explicitly forced a submission via workflow dispatch
+ submit: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.submit) }}
jobs:
gcc-openmp:
@@ -48,6 +54,35 @@ jobs:
submodules: recursive
- name: Show action trigger
uses: ./.github/actions/show-workflow-trigger
+ - name: Get CUDA Arch
+ run: |
+ # Ensure nvidia-smi is available
+ if ! command -v nvidia-smi &> /dev/null; then
+ echo "nvidia-smi could not be found. Please ensure you have Nvidia drivers installed."
+ exit 1
+ fi
+
+ # Get the GPU model from nvidia-smi, and set env for next step
+ gpu_model=$(nvidia-smi --query-gpu=name --format=csv,noheader | head -n 1)
+ case "$gpu_model" in
+ *"H100"*)
+ echo "Hopper=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=90" >> $GITHUB_ENV
+ ARCH=90
+ ;;
+ *"A100"*)
+ echo "Ampere=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=80" >> $GITHUB_ENV
+ ;;
+ *"V100"*)
+ echo "Volta=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=70" >> $GITHUB_ENV
+ ;;
+ *)
+ echo "Unsupported GPU model: $gpu_model"
+ exit 1
+ ;;
+ esac
- name: Run tests
uses: ./.github/actions/test-all-scream
with:
@@ -55,4 +90,4 @@ jobs:
machine: ghci-snl-cuda
generate: false
submit: ${{ env.submit }}
- cmake-configs: Kokkos_ARCH_VOLTA70=ON;CMAKE_CUDA_ARCHITECTURES=70
+ cmake-configs: Kokkos_ARCH_HOPPER90=${{ env.Hopper }};Kokkos_ARCH_AMPERE80=${{ env.Ampere }};Kokkos_ARCH_VOLTA70=${{ env.Volta }};CMAKE_CUDA_ARCHITECTURES=${{ env.CUDA_ARCH }}
diff --git a/.github/workflows/eamxx-sa-sanitizer.yml b/.github/workflows/eamxx-sa-sanitizer.yml
index 7e3a1a49fcfb..00f60f7da2e3 100644
--- a/.github/workflows/eamxx-sa-sanitizer.yml
+++ b/.github/workflows/eamxx-sa-sanitizer.yml
@@ -2,6 +2,11 @@ name: eamxx-sa-sanitizer
on:
workflow_dispatch:
+ inputs:
+ submit:
+ description: 'Force cdash submission'
+ required: true
+ type: boolean
# Add schedule trigger for nightly runs at midnight MT (Standard Time)
schedule:
@@ -13,12 +18,13 @@ concurrency:
cancel-in-progress: true
env:
- submit: ${{ github.event_name == 'schedule' && 'true' || 'false' }} # Submit to cdash only for nightlies
+ # Submit to cdash only for nightlies or if the user explicitly forced a submission via workflow dispatch
+ submit: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.submit) }}
jobs:
gcc-openmp:
runs-on: [self-hosted, ghci-snl-cpu, gcc]
- name: gcc-openmp / cov
+ name: gcc-openmp / valg
steps:
- name: Check out the repository
uses: actions/checkout@v4
@@ -52,6 +58,35 @@ jobs:
submodules: recursive
- name: Show action trigger
uses: ./.github/actions/show-workflow-trigger
+ - name: Get CUDA Arch
+ run: |
+ # Ensure nvidia-smi is available
+ if ! command -v nvidia-smi &> /dev/null; then
+ echo "nvidia-smi could not be found. Please ensure you have Nvidia drivers installed."
+ exit 1
+ fi
+
+ # Get the GPU model from nvidia-smi, and set env for next step
+ gpu_model=$(nvidia-smi --query-gpu=name --format=csv,noheader | head -n 1)
+ case "$gpu_model" in
+ *"H100"*)
+ echo "Hopper=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=90" >> $GITHUB_ENV
+ ARCH=90
+ ;;
+ *"A100"*)
+ echo "Ampere=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=80" >> $GITHUB_ENV
+ ;;
+ *"V100"*)
+ echo "Volta=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=70" >> $GITHUB_ENV
+ ;;
+ *)
+ echo "Unsupported GPU model: $gpu_model"
+ exit 1
+ ;;
+ esac
- name: Run tests
uses: ./.github/actions/test-all-scream
with:
@@ -59,4 +94,4 @@ jobs:
machine: ghci-snl-cuda
generate: false
submit: ${{ env.submit }}
- cmake-configs: Kokkos_ARCH_VOLTA70=ON;CMAKE_CUDA_ARCHITECTURES=70
+ cmake-configs: Kokkos_ARCH_HOPPER90=${{ env.Hopper }};Kokkos_ARCH_AMPERE80=${{ env.Ampere }};Kokkos_ARCH_VOLTA70=${{ env.Volta }};CMAKE_CUDA_ARCHITECTURES=${{ env.CUDA_ARCH }}
diff --git a/.github/workflows/eamxx-sa-testing.yml b/.github/workflows/eamxx-sa-testing.yml
index 0c0bca084132..e3657266cb64 100644
--- a/.github/workflows/eamxx-sa-testing.yml
+++ b/.github/workflows/eamxx-sa-testing.yml
@@ -5,6 +5,17 @@ on:
pull_request:
branches: [ master ]
types: [opened, synchronize, ready_for_review, reopened]
+ paths:
+ # first, yes to these
+ - '.github/workflows/eamxx-sa-testing.yml'
+ - 'cime_config/machine/config_machines.xml'
+ - 'components/eamxx/**'
+ - 'components/homme/**'
+ - 'externals/ekat'
+ - 'externals/scorpio'
+ # second, no to these
+ - '!components/eamxx/docs/**'
+ - '!components/eamxx/mkdocs.yml'
# Manual run is used to bless
workflow_dispatch:
@@ -21,6 +32,10 @@ on:
description: 'Generate baselines'
required: true
type: boolean
+ submit:
+ description: 'Force cdash submission'
+ required: true
+ type: boolean
# Add schedule trigger for nightly runs at midnight MT (Standard Time)
schedule:
@@ -34,66 +49,20 @@ concurrency:
cancel-in-progress: true
env:
- submit: ${{ github.event_name == 'schedule' && 'true' || 'false' }} # Submit to cdash only for nightlies
+ # Submit to cdash only for nightlies or if the user explicitly forced a submission via workflow dispatch
+ submit: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.submit) }}
+ generate: ${{ github.event_name == 'workflow_dispatch' && inputs.bless }}
jobs:
- pre_process_pr:
- if: ${{ github.event_name == 'pull_request' }}
- runs-on: ubuntu-latest # This job can run anywhere
- outputs:
- relevant_paths: ${{ steps.check_paths.outputs.value }}
- labels: ${{ steps.get_labels.outputs.labels }}
- steps:
- - id: check_paths
- run: |
- paths=(
- components/eamxx
- components/eam/src/physics/rrtmgp
- components/eam/src/physics/p3/scream
- components/eam/src/physics/cam
- components/eam/src/physics/rrtmgp/external
- externals/ekat
- externals/scorpio
- externals/haero
- externals/YAKL
- .github/workflows/eamxx-sa-testing.yml
- )
- pattern=$(IFS=\|; echo "${paths[*]}")
-
- # Use the GitHub API to get the list of changed files
- response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
- "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files")
- changed_files=$(echo "$response" | grep -o '"filename": *"[^"]*"' | sed 's/"filename": *//; s/"//g')
-
- # Check for matches and echo the matching files (or "" if none)
- matching_files=$(echo "$changed_files" | grep -E "^($pattern)" || echo "")
- if [[ -n "$matching_files" ]]; then
- echo "Found relevant files: $matching_files"
- echo "value=true" >> $GITHUB_OUTPUT
- else
- echo "No relevant files touched by this PR."
- echo "value=false" >> $GITHUB_OUTPUT
- fi
- - id: get_labels
- run: |
- labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
- echo "labels=${labels}" >> $GITHUB_OUTPUT
gcc-openmp:
- needs: [pre_process_pr]
if: |
- github.event_name == 'schedule' ||
- (
- github.event_name == 'pull_request' &&
- needs.pre_process_pr.outputs.relevant_paths=='true' &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip gcc') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip openmp') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-sa') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-all')
- ) || (
- github.event_name == 'workflow_dispatch' &&
- github.event.inputs.job_to_run == 'gcc-openmp' ||
- github.event.inputs.job_to_run == 'all'
- )
+ ${{
+ github.event_name != 'workflow_dispatch' ||
+ (
+ github.event.inputs.job_to_run == 'gcc-openmp' ||
+ github.event.inputs.job_to_run == 'all'
+ )
+ }}
runs-on: [self-hosted, ghci-snl-cpu, gcc]
strategy:
fail-fast: false
@@ -109,14 +78,6 @@ jobs:
submodules: recursive
- name: Show action trigger
uses: ./.github/actions/show-workflow-trigger
- - name: Set test-all inputs based on event specs
- run: |
- echo "generate=false" >> $GITHUB_ENV
- if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
- if [ "${{ inputs.bless }}" == "true" ]; then
- echo "generate=true" >> $GITHUB_ENV
- fi
- fi
- name: Run tests
uses: ./.github/actions/test-all-scream
with:
@@ -126,21 +87,14 @@ jobs:
submit: ${{ env.submit }}
cmake-configs: Kokkos_ENABLE_OPENMP=ON
gcc-cuda:
- needs: [pre_process_pr]
if: |
- github.event_name == 'schedule' ||
- (
- github.event_name == 'pull_request' &&
- needs.pre_process_pr.outputs.relevant_paths=='true' &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip gcc') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip cuda') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-sa') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-all')
- ) || (
- github.event_name == 'workflow_dispatch' &&
- github.event.inputs.job_to_run == 'gcc-cuda' ||
- github.event.inputs.job_to_run == 'all'
- )
+ ${{
+ github.event_name != 'workflow_dispatch' ||
+ (
+ github.event.inputs.job_to_run == 'gcc-cuda' ||
+ github.event.inputs.job_to_run == 'all'
+ )
+ }}
runs-on: [self-hosted, ghci-snl-cuda, cuda, gcc]
strategy:
fail-fast: false
@@ -156,14 +110,35 @@ jobs:
submodules: recursive
- name: Show action trigger
uses: ./.github/actions/show-workflow-trigger
- - name: Set test-all inputs based on event specs
+ - name: Get CUDA Arch
run: |
- echo "generate=false" >> $GITHUB_ENV
- if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
- if [ "${{ inputs.bless }}" == "true" ]; then
- echo "generate=true" >> $GITHUB_ENV
- fi
+ # Ensure nvidia-smi is available
+ if ! command -v nvidia-smi &> /dev/null; then
+ echo "nvidia-smi could not be found. Please ensure you have Nvidia drivers installed."
+ exit 1
fi
+
+ # Get the GPU model from nvidia-smi, and set env for next step
+ gpu_model=$(nvidia-smi --query-gpu=name --format=csv,noheader | head -n 1)
+ case "$gpu_model" in
+ *"H100"*)
+ echo "Hopper=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=90" >> $GITHUB_ENV
+ ARCH=90
+ ;;
+ *"A100"*)
+ echo "Ampere=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=80" >> $GITHUB_ENV
+ ;;
+ *"V100"*)
+ echo "Volta=ON" >> $GITHUB_ENV
+ echo "CUDA_ARCH=70" >> $GITHUB_ENV
+ ;;
+ *)
+ echo "Unsupported GPU model: $gpu_model"
+ exit 1
+ ;;
+ esac
- name: Run tests
uses: ./.github/actions/test-all-scream
with:
@@ -171,4 +146,4 @@ jobs:
machine: ghci-snl-cuda
generate: ${{ env.generate }}
submit: ${{ env.submit }}
- cmake-configs: Kokkos_ARCH_VOLTA70=ON;CMAKE_CUDA_ARCHITECTURES=70
+ cmake-configs: Kokkos_ARCH_HOPPER90=${{ env.Hopper }};Kokkos_ARCH_AMPERE80=${{ env.Ampere }};Kokkos_ARCH_VOLTA70=${{ env.Volta }};CMAKE_CUDA_ARCHITECTURES=${{ env.CUDA_ARCH }}
diff --git a/.github/workflows/eamxx-scripts-tests.yml b/.github/workflows/eamxx-scripts-tests.yml
index d41f3eaf62ae..2cdd6f8758f8 100644
--- a/.github/workflows/eamxx-scripts-tests.yml
+++ b/.github/workflows/eamxx-scripts-tests.yml
@@ -5,9 +5,18 @@ on:
pull_request:
branches: [ master ]
types: [opened, synchronize, ready_for_review, reopened]
+ paths:
+ - 'components/eamxx/scripts/**'
+ - 'components/eamxx/cime_config/**'
+ - '.github/workflows/eamxx-scripts-tests.yml'
# Manual run for debug purposes only
workflow_dispatch:
+ inputs:
+ submit:
+ description: 'Force cdash submission'
+ required: true
+ type: boolean
# Add schedule trigger for nightly runs at midnight MT (Standard Time)
schedule:
@@ -20,51 +29,12 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
-jobs:
- pre_process_pr:
- if: ${{ github.event_name == 'pull_request' }}
- runs-on: ubuntu-latest # This job can run anywhere
- outputs:
- relevant_paths: ${{ steps.check_paths.outputs.value}}
- labels: ${{ steps.get_labels.outputs.labels }}
- steps:
- - id: check_paths
- run: |
- paths=(
- components/eamxx/scripts
- components/eamxx/cime_config/eamxx
- components/eamxx/cime_config/build
- components/eamxx/cime_config/yaml_utils.py
- .github/workflows/eamxx-scripts-tests.yml
- )
- pattern=$(IFS=\|; echo "${paths[*]}")
-
- # Use the GitHub API to get the list of changed files
- response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
- "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files")
- changed_files=$(echo "$response" | grep -o '"filename": *"[^"]*"' | sed 's/"filename": *//; s/"//g')
+env:
+ # Submit to cdash only for nightlies or if the user explicitly forced a submission via workflow dispatch
+ submit: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.submit) }}
- # Check for matches and echo the matching files (or "" if none)
- matching_files=$(echo "$changed_files" | grep -E "^($pattern)" || echo "")
- if [[ -n "$matching_files" ]]; then
- echo "Found relevant files: $matching_files"
- echo "value=true" >> $GITHUB_OUTPUT
- else
- echo "No relevant files touched by this PR."
- echo "value=false" >> $GITHUB_OUTPUT
- fi
- - id: get_labels
- run: |
- labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
- echo "labels=${labels}" >> $GITHUB_OUTPUT
+jobs:
cpu-gcc:
- needs: [pre_process_pr]
- if: |
- github.event_name != 'pull_request' ||
- (
- needs.pre_process_pr.outputs.relevant_paths == 'true' &&
- !contains(needs.pre_process_pr.outputs.labels, 'CI: skip eamxx-all')
- )
runs-on: [self-hosted, gcc, ghci-snl-cpu]
steps:
- name: Check out the repository
@@ -78,7 +48,7 @@ jobs:
- name: Run test
run: |
cd components/eamxx
- if [ ${{ github.event_name == 'schedule' }} ]; then
+ if [ "${{ env.submit }}" == "true" ]; then
./scripts/scripts-ctest-driver -s -m ghci-snl-cpu
else
./scripts/scripts-tests -f -m ghci-snl-cpu
diff --git a/.github/workflows/eamxx-v1-testing.yml b/.github/workflows/eamxx-v1-testing.yml
index ca83177fde58..9145961bdfee 100644
--- a/.github/workflows/eamxx-v1-testing.yml
+++ b/.github/workflows/eamxx-v1-testing.yml
@@ -5,6 +5,17 @@ on:
pull_request:
branches: [ master ]
types: [opened, synchronize, ready_for_review, reopened]
+ paths:
+ # first, yes to these
+ - '.github/workflows/eamxx-v1-testing.yml'
+ - 'cime_config/machine/config_machines.xml'
+ - 'components/eamxx/**'
+ - 'components/homme/**'
+ - 'externals/ekat'
+ - 'externals/scorpio'
+ # second, no to these
+ - '!components/eamxx/docs/**'
+ - '!components/eamxx/mkdocs.yml'
# Manual run is used to bless
workflow_dispatch:
@@ -28,63 +39,26 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
-jobs:
- pre_process_pr:
- if: ${{ github.event_name == 'pull_request' }}
- runs-on: ubuntu-latest # This job can run anywhere
- outputs:
- relevant_paths: ${{ steps.check_paths.outputs.value }}
- labels: ${{ steps.get_labels.outputs.labels }}
- steps:
- - id: check_paths
- run: |
- paths=(
- components/eamxx
- components/eam/src/physics/rrtmgp
- components/eam/src/physics/p3/scream
- components/eam/src/physics/cam
- components/eam/src/physics/rrtmgp/external
- externals/ekat
- externals/scorpio
- externals/haero
- externals/YAKL
- .github/workflows/eamxx-v1-testing.yml
- )
- pattern=$(IFS=\|; echo "${paths[*]}")
+env:
+ # Submit to cdash only for nightlies or if the user explicitly forced a submission via workflow dispatch
+ submit: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.submit) }}
+ # Generate only if user requested via workflow_dispatch
+ generate: ${{ github.event_name == 'workflow_dispatch' && inputs.bless }}
+ # Correct case folder suffix for generate/compare, used to find files to upload as artifacts
+ folder_suffix: ${{ github.event_name == 'workflow_dispatch' && inputs.bless && '.G' || '.C' }}
+ # Compare/generate flags for create_test
+ flags: ${{ github.event_name == 'workflow_dispatch' && inputs.bless && '-o -g -b master' || '-c -b master' }}
- # Use the GitHub API to get the list of changed files
- response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
- "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files")
- changed_files=$(echo "$response" | grep -o '"filename": *"[^"]*"' | sed 's/"filename": *//; s/"//g')
-
- # Check for matches and echo the matching files (or "" if none)
- matching_files=$(echo "$changed_files" | grep -E "^($pattern)" || echo "")
- if [[ -n "$matching_files" ]]; then
- echo "Found relevant files: $matching_files"
- echo "value=true" >> $GITHUB_OUTPUT
- else
- echo "No relevant files touched by this PR."
- echo "value=false" >> $GITHUB_OUTPUT
- fi
- - id: get_labels
- run: |
- labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
- echo "labels=${labels}" >> $GITHUB_OUTPUT
+jobs:
cpu-gcc:
- needs: [pre_process_pr]
if: |
- github.event_name == 'schedule' ||
- (
- github.event_name == 'pull_request' &&
- needs.pre_process_pr.outputs.relevant_paths=='true' &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip gcc') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-v1') &&
- !contains(needs.pre_process_pr.outputs.labels,'CI: skip eamxx-all')
- ) || (
- github.event_name == 'workflow_dispatch' &&
- github.event.inputs.job_to_run == 'cpu-gcc' ||
- github.event.inputs.job_to_run == 'all'
- )
+ ${{
+ github.event_name != 'workflow_dispatch' ||
+ (
+ github.event.inputs.job_to_run == 'cpu-gcc' ||
+ github.event.inputs.job_to_run == 'all'
+ )
+ }}
runs-on: [self-hosted, gcc, ghci-snl-cpu]
strategy:
matrix:
@@ -124,18 +98,6 @@ jobs:
echo "Unsupported Linux distribution"
exit 1
fi
- - name: Establish cmp/gen flag
- run: |
- dir_suffix=".C"
- cmp_gen_flag="-c"
- if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
- if [ ${{ inputs.bless }} ]; then
- cmp_gen_flag="-o -g"
- dir_suffix=".G"
- fi
- fi
- echo "flags=$cmp_gen_flag -b master" >> $GITHUB_ENV
- echo "folder_suffix=$dir_suffix" >> $GITHUB_ENV
- name: Run test
run: |
./cime/scripts/create_test ${{ matrix.test.full_name }} ${{ env.flags }} --wait
diff --git a/.mergify.yml b/.mergify.yml
new file mode 100644
index 000000000000..89fcc821e57a
--- /dev/null
+++ b/.mergify.yml
@@ -0,0 +1,53 @@
+merge_protections:
+ - name: Enforce checks passing
+ description: Make sure that checks are not failing on the PR, and reviewers approved
+ if:
+ - base = master
+ success_conditions:
+ - "#approved-reviews-by >= 1" # At least 1 approval
+ - "#changes-requested-reviews-by == 0" # No reviewer asked for changes
+ - or:
+ - and:
+ - check-success="gcc-openmp / dbg"
+ - check-success="gcc-openmp / sp"
+ - check-success="gcc-openmp / fpe"
+ - check-success="gcc-openmp / opt"
+ - check-skipped={% raw %}gcc-openmp / ${{ matrix.build_type }}{% endraw %}
+ - or:
+ - and:
+ - check-success="gcc-cuda / dbg"
+ - check-success="gcc-cuda / sp"
+ - check-success="gcc-cuda / opt"
+ - check-skipped={% raw %}gcc-cuda / ${{ matrix.build_type }}{% endraw %}
+ - or:
+ - and:
+ - check-success="cpu-gcc / ERS_Ln9.ne4_ne4.F2000-SCREAMv1-AQP1.scream-output-preset-2"
+ - check-success="cpu-gcc / ERS_P16_Ln22.ne30pg2_ne30pg2.FIOP-SCREAMv1-DP.scream-dpxx-arm97"
+ - check-success="cpu-gcc / ERS_Ln22.ne4pg2_ne4pg2.F2010-SCREAMv1.scream-small_kernels--scream-output-preset-5"
+ - check-success="cpu-gcc / SMS_D_Ln5.ne4pg2_oQU480.F2010-SCREAMv1-MPASSI.scream-mam4xx-all_mam4xx_procs"
+ - check-skipped={% raw %}cpu-gcc / ${{ matrix.test.short_name }}{% endraw %}
+ - or:
+ - check-success=cpu-gcc
+ - check-skipped=cpu-gcc
+
+pull_request_rules:
+ - name: dismiss stale reviews
+ conditions:
+ - base=master
+ actions:
+ dismiss_reviews:
+ when: synchronize # Dismiss reviews when synchronize event happens
+ - name: Automatic merge when CI passes and approved
+ conditions:
+ - "label=CI: automerge"
+ - base=master
+ actions:
+ merge:
+ method: merge
+ commit_message_template: |
+ Merge pull request #{{number}} from {{head}}
+
+ Automatically merged using mergify
+ PR title: {{title}}
+ PR author: {{author}}
+ PR labels: {{label}}
diff --git a/cime_config/tests.py b/cime_config/tests.py
index 47096ada4b83..46894e6ea967 100644
--- a/cime_config/tests.py
+++ b/cime_config/tests.py
@@ -761,6 +761,7 @@
"SMS_D_Ln5.ne4pg2_oQU480.F2010-SCREAMv1-MPASSI.scream-mam4xx-aci",
"SMS_D_Ln5.ne4pg2_oQU480.F2010-SCREAMv1-MPASSI.scream-mam4xx-wetscav",
"SMS_D_Ln5.ne4pg2_oQU480.F2010-SCREAMv1-MPASSI.scream-mam4xx-drydep",
+ "SMS_D_Ln5.ne30pg2_oECv3.F2010-SCREAMv1-MPASSI.scream-mam4xx-remap_emiss_ne4_ne30"
)
},
diff --git a/components/eam/src/physics/rrtmgp/external b/components/eam/src/physics/rrtmgp/external
index e64b99cce24e..b24ca1f616e4 160000
--- a/components/eam/src/physics/rrtmgp/external
+++ b/components/eam/src/physics/rrtmgp/external
@@ -1 +1 @@
-Subproject commit e64b99cce24eb31bb6f317bddb6f0ffbdfaf8bb7
+Subproject commit b24ca1f616e45659b334dbd7297017cb7927367e
diff --git a/components/eamxx/CMakeLists.txt b/components/eamxx/CMakeLists.txt
index 9b6f54e5b056..07c3cda76728 100644
--- a/components/eamxx/CMakeLists.txt
+++ b/components/eamxx/CMakeLists.txt
@@ -200,9 +200,6 @@ option(SCREAM_MPI_ON_DEVICE "Whether to use device pointers for MPI calls" ON)
option(SCREAM_ENABLE_MAM "Whether to enable MAM aerosol support" ON)
set(SCREAM_SMALL_KERNELS ${DEFAULT_SMALL_KERNELS} CACHE STRING "Use small, non-monolothic kokkos kernels for ALL components that support them")
set(SCREAM_P3_SMALL_KERNELS ${SCREAM_SMALL_KERNELS} CACHE STRING "Use small, non-monolothic kokkos kernels for P3 only")
-message("JGF DEFAUL_SMALL_KERNELS is ${DEFAULT_SMALL_KERNELS}")
-message("JGF SCREAM_SMALL_KERNELS is ${SCREAM_SMALL_KERNELS}")
-message("JGF SCREAM_P3_SMALL_KERNELS is ${SCREAM_P3_SMALL_KERNELS}")
set(SCREAM_SHOC_SMALL_KERNELS ${SCREAM_SMALL_KERNELS} CACHE STRING "Use small, non-monolothic kokkos kernels for SHOC only")
if (NOT SCREAM_P3_SMALL_KERNELS AND NOT SCREAM_SHOC_SMALL_KERNELS)
set(EKAT_DISABLE_WORKSPACE_SHARING TRUE CACHE STRING "")
diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml
index a1435b03de1c..6d4c36b81bd4 100644
--- a/components/eamxx/cime_config/namelist_defaults_scream.xml
+++ b/components/eamxx/cime_config/namelist_defaults_scream.xml
@@ -293,29 +293,31 @@ be lost if SCREAM_HACK_XML is not enabled.
${DIN_LOC_ROOT}/atm/scream/mam4xx/photolysis/RSF_GT200nm_v3.0_c080811.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/photolysis/temp_prs_GT200nm_JPL10_c130206.nc
-
- 20100101
+
+ 20100101
+
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/drydep/season_wes.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so4_a1_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so4_a2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_pom_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_bc_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a1_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_soag_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so4_a1_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_so4_a2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_pom_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_bc_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a1_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a2_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_num_a4_elev_1x1_2010_clim_ne30pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/elevated/cmip6_mam4_soag_elev_1x1_2010_clim_ne30pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_pom_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_bc_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_soag_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_pom_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_bc_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_soag_elev_1x1_2010_clim_ne4pg2_c20241008.nc
@@ -370,17 +372,11 @@ be lost if SCREAM_HACK_XML is not enabled.
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a1_surf_ne4pg2_2010_clim_c20240815.nc
${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a2_surf_ne4pg2_2010_clim_c20240815.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/dst_ne30pg2_c20241028.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/dst_ne4pg2_c20241028.nc
-
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/DMSflux.2010.ne4pg2_conserv.POPmonthlyClimFromACES4BGC_c20240814.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so2_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_bc_a4_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a1_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a2_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a4_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_pom_a4_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a1_surf_ne4pg2_2010_clim_c20240815.nc
- ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a2_surf_ne4pg2_2010_clim_c20240815.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne30pg2_c20241030.nc
+ ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne4pg2_c20241030.nc
@@ -495,6 +491,11 @@ be lost if SCREAM_HACK_XML is not enabled.
0.0
-9999.0
0.0
+
+
+ -9999.0
+ 551.58
+
1
2
4
@@ -637,6 +638,7 @@ be lost if SCREAM_HACK_XML is not enabled.
1.37146e-07 ,3.45899e-08 ,1.00000e-06 ,9.99601e-08
1.37452e-07 ,3.46684e-08 ,1.00900e-06 ,9.99601e-08
5.08262e-12 ,1.54035e-13 ,3.09018e-13 ,9.14710e-22
+ 0.0
0.0
0.0
0.0
diff --git a/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/aero_microphysics/shell_commands b/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/aero_microphysics/shell_commands
index 1d6757a5bd95..ac1709f7dca4 100644
--- a/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/aero_microphysics/shell_commands
+++ b/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/aero_microphysics/shell_commands
@@ -8,9 +8,8 @@
$CIMEROOT/../components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/update_eamxx_num_tracers.sh -b
#------------------------------------------------------
-#Update IC file and add drydep process
+# Add microphysics process
#------------------------------------------------------
-$CIMEROOT/../components/eamxx/scripts/atmchange initial_conditions::Filename='$DIN_LOC_ROOT/atm/scream/init/screami_mam4xx_ne4np4L72_c20240208.nc' -b
$CIMEROOT/../components/eamxx/scripts/atmchange physics::atm_procs_list="mac_aero_mic,rrtmgp,mam4_aero_microphys" -b
diff --git a/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/remap_emiss_ne4_ne30/shell_commands b/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/remap_emiss_ne4_ne30/shell_commands
new file mode 100644
index 000000000000..b2d0286b8700
--- /dev/null
+++ b/components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/remap_emiss_ne4_ne30/shell_commands
@@ -0,0 +1,28 @@
+
+#!/bin/sh
+#------------------------------------------------------
+# MAM4xx adds additionaltracers to the simulation
+# Increase number of tracers for MAM4xx simulations
+#------------------------------------------------------
+
+$CIMEROOT/../components/eamxx/cime_config/testdefs/testmods_dirs/scream/mam4xx/update_eamxx_num_tracers.sh -b
+
+#------------------------------------------------------
+# Add aerosol microphysics process, force ne4pg2
+# emission files and provide a ne4pg2->ne30pg2 mapping
+# file
+#------------------------------------------------------
+alias ATMCHANGE='$CIMEROOT/../components/eamxx/scripts/atmchange'
+
+ATMCHANGE physics::atm_procs_list="mac_aero_mic,rrtmgp,mam4_aero_microphys" -b
+
+ATMCHANGE mam4_aero_microphys::mam4_so2_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so2_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_so4_a1_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_so4_a2_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_so4_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_pom_a4_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_pom_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_bc_a4_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_bc_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_num_a1_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a1_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_num_a2_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a2_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_num_a4_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_num_a4_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::mam4_soag_elevated_emiss_file_name='${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/elevated/cmip6_mam4_soag_elev_1x1_2010_clim_ne4pg2_c20241008.nc' -b
+ATMCHANGE mam4_aero_microphys::aero_microphys_remap_file='${DIN_LOC_ROOT}/atm/scream/maps/map_ne4pg2_to_ne30pg2_nco_c20241108.nc' -b
diff --git a/components/eamxx/scripts/machines_specs.py b/components/eamxx/scripts/machines_specs.py
index 9827692bfd04..c9c9f973022f 100644
--- a/components/eamxx/scripts/machines_specs.py
+++ b/components/eamxx/scripts/machines_specs.py
@@ -221,9 +221,10 @@ class GHCISNLCuda(Machine):
concrete = True
@classmethod
def setup(cls):
- super().setup_base(name="ghci-snl-cuda",num_bld_res=16,num_run_res=1)
+ super().setup_base(name="ghci-snl-cuda")
cls.baselines_dir = "/projects/e3sm/baselines/scream/ghci-snl-cuda"
cls.gpu_arch = "cuda"
+ cls.num_run_res = int(run_cmd_no_fail("nvidia-smi --query-gpu=name --format=csv,noheader | wc -l"))
###############################################################################
class GHCIOCI(Machine):
diff --git a/components/eamxx/scripts/query_scream.py b/components/eamxx/scripts/query_scream.py
index 4b26451c7cb2..a6fe5096bfb5 100644
--- a/components/eamxx/scripts/query_scream.py
+++ b/components/eamxx/scripts/query_scream.py
@@ -1,9 +1,5 @@
-from machines_specs import assert_machine_supported, \
- get_mach_cxx_compiler, get_mach_c_compiler, get_mach_f90_compiler, \
- get_mach_batch_command, get_mach_env_setup_command, \
- get_mach_baseline_root_dir, is_cuda_machine, \
- get_mach_compilation_resources, get_mach_testing_resources
+from machines_specs import assert_machine_supported, get_machine, get_mach_env_setup_command
from utils import expect
CHOICES = (
@@ -24,23 +20,24 @@ def query_scream(machine, param):
assert_machine_supported(machine)
expect(param in CHOICES, f"Unknown param {param}")
+ mach = get_machine(machine)
if param == "cxx_compiler":
- return get_mach_cxx_compiler(machine)
+ return mach.cxx_compiler
elif param == "c_compiler":
- return get_mach_c_compiler(machine)
+ return mach.c_compiler
elif param == "f90_compiler":
- return get_mach_f90_compiler(machine)
+ return mach.ftn_compiler
elif param == "batch":
- return get_mach_batch_command(machine)
+ return mach.batch
elif param == "env":
return get_mach_env_setup_command(machine)
elif param == "baseline_root":
- return get_mach_baseline_root_dir(machine)
+ return mach.baselines_dir
elif param == "cuda":
- return str(is_cuda_machine(machine))
+ return str(mach.gpu_arch == "cuda")
elif param == "comp_j":
- return get_mach_compilation_resources()
+ return num_bld_res
elif param == "test_j":
- return get_mach_testing_resources(machine)
+ return gnum_run_res
else:
expect(False, f"Unhandled param {param}")
diff --git a/components/eamxx/src/control/atmosphere_driver.cpp b/components/eamxx/src/control/atmosphere_driver.cpp
index 9cf2b48e3586..0f1cb1e31ab0 100644
--- a/components/eamxx/src/control/atmosphere_driver.cpp
+++ b/components/eamxx/src/control/atmosphere_driver.cpp
@@ -807,7 +807,8 @@ void AtmosphereDriver::
set_provenance_data (std::string caseid,
std::string rest_caseid,
std::string hostname,
- std::string username)
+ std::string username,
+ std::string versionid)
{
#ifdef SCREAM_CIME_BUILD
// Check the inputs are valid
@@ -816,6 +817,7 @@ set_provenance_data (std::string caseid,
"Error! Invalid restart case id: " + rest_caseid + "\n");
EKAT_REQUIRE_MSG (hostname!="", "Error! Invalid hostname: " + hostname + "\n");
EKAT_REQUIRE_MSG (username!="", "Error! Invalid username: " + username + "\n");
+ EKAT_REQUIRE_MSG (versionid!="", "Error! Invalid version: " + versionid + "\n");
#else
caseid = rest_caseid = m_casename;
char* user = new char[32];
@@ -835,13 +837,14 @@ set_provenance_data (std::string caseid,
}
delete[] user;
delete[] host;
+ versionid = EAMXX_GIT_VERSION;
#endif
auto& provenance = m_atm_params.sublist("provenance");
provenance.set("caseid",caseid);
provenance.set("rest_caseid",rest_caseid);
provenance.set("hostname",hostname);
provenance.set("username",username);
- provenance.set("version",std::string(EAMXX_GIT_VERSION));
+ provenance.set("git_version",versionid);
}
void AtmosphereDriver::
diff --git a/components/eamxx/src/control/atmosphere_driver.hpp b/components/eamxx/src/control/atmosphere_driver.hpp
index 41801745ea23..a3acfba5d945 100644
--- a/components/eamxx/src/control/atmosphere_driver.hpp
+++ b/components/eamxx/src/control/atmosphere_driver.hpp
@@ -116,7 +116,8 @@ class AtmosphereDriver
void set_provenance_data (std::string caseid = "",
std::string rest_caseid = "",
std::string hostname = "",
- std::string username = "");
+ std::string username = "",
+ std::string versionid = "");
// Load initial conditions for atm inputs
void initialize_fields ();
diff --git a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp
index ee3e21e7461b..2c3360a3b4f7 100644
--- a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp
+++ b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp
@@ -28,35 +28,41 @@ void SurfaceCouplingImporter::set_grids(const std::shared_ptr("sfc_alb_dir_vis", scalar2d_layout, nondim, grid_name);
- add_field("sfc_alb_dir_nir", scalar2d_layout, nondim, grid_name);
- add_field("sfc_alb_dif_vis", scalar2d_layout, nondim, grid_name);
- add_field("sfc_alb_dif_nir", scalar2d_layout, nondim, grid_name);
- add_field("surf_lw_flux_up", scalar2d_layout, W/m2, grid_name);
- add_field("surf_sens_flux", scalar2d_layout, W/m2, grid_name);
- add_field("surf_evap", scalar2d_layout, kg/m2/s, grid_name);
- add_field("surf_mom_flux", vector2d_layout, N/m2, grid_name);
- add_field("surf_radiative_T", scalar2d_layout, K, grid_name);
- add_field("T_2m", scalar2d_layout, K, grid_name);
- add_field("qv_2m", scalar2d_layout, kg/kg, grid_name);
- add_field("wind_speed_10m", scalar2d_layout, m/s, grid_name);
- add_field("snow_depth_land", scalar2d_layout, m, grid_name);
- add_field("ocnfrac", scalar2d_layout, nondim, grid_name);
- add_field("landfrac", scalar2d_layout, nondim, grid_name);
- add_field("icefrac", scalar2d_layout, nondim, grid_name);
+ const FieldLayout scalar2d = m_grid->get_2d_scalar_layout();
+ const FieldLayout vector2d = m_grid->get_2d_vector_layout(2);
+ const FieldLayout vector4d = m_grid->get_2d_vector_layout(4);
+
+ add_field("sfc_alb_dir_vis", scalar2d, nondim, grid_name);
+ add_field("sfc_alb_dir_nir", scalar2d, nondim, grid_name);
+ add_field("sfc_alb_dif_vis", scalar2d, nondim, grid_name);
+ add_field("sfc_alb_dif_nir", scalar2d, nondim, grid_name);
+ add_field("surf_lw_flux_up", scalar2d, W/m2, grid_name);
+ add_field("surf_sens_flux", scalar2d, W/m2, grid_name);
+ add_field("surf_evap", scalar2d, kg/m2/s, grid_name);
+ add_field("surf_mom_flux", vector2d, N/m2, grid_name);
+ add_field("surf_radiative_T", scalar2d, K, grid_name);
+ add_field("T_2m", scalar2d, K, grid_name);
+ add_field("qv_2m", scalar2d, kg/kg, grid_name);
+ add_field("wind_speed_10m", scalar2d, m/s, grid_name);
+ add_field("snow_depth_land", scalar2d, m, grid_name);
+ add_field("ocnfrac", scalar2d, nondim, grid_name);
+ add_field("landfrac", scalar2d, nondim, grid_name);
+ add_field("icefrac", scalar2d, nondim, grid_name);
// Friction velocity [m/s]
- add_field("fv", scalar2d_layout, m/s, grid_name);
+ add_field("fv", scalar2d, m/s, grid_name);
// Aerodynamical resistance
- add_field("ram1", scalar2d_layout, s/m, grid_name);
+ add_field("ram1", scalar2d, s/m, grid_name);
+ // Sea surface temperature [K]
+ add_field("sst", scalar2d, K, grid_name);
+ //dust fluxes [kg/m^2/s]: Four flux values for eacch column
+ add_field("dstflx", vector4d, kg/m2/s, grid_name);
+
}
// =========================================================================================
void SurfaceCouplingImporter::setup_surface_coupling_data(const SCDataManager &sc_data_manager)
diff --git a/components/eamxx/src/dynamics/homme/homme_grids_manager.cpp b/components/eamxx/src/dynamics/homme/homme_grids_manager.cpp
index 87009c7d074e..df5de6827f69 100644
--- a/components/eamxx/src/dynamics/homme/homme_grids_manager.cpp
+++ b/components/eamxx/src/dynamics/homme/homme_grids_manager.cpp
@@ -271,6 +271,7 @@ build_physics_grid (const ci_string& type, const ci_string& rebalance) {
auto hyam = phys_grid->create_geometry_data("hyam",layout_mid,nondim);
auto hybm = phys_grid->create_geometry_data("hybm",layout_mid,nondim);
auto lev = phys_grid->create_geometry_data("lev", layout_mid,mbar);
+ auto ilev = phys_grid->create_geometry_data("ilev",layout_int,mbar);
for (auto f : {hyai, hybi, hyam, hybm}) {
auto f_d = get_grid("Dynamics")->get_geometry_data(f.name());
@@ -281,13 +282,20 @@ build_physics_grid (const ci_string& type, const ci_string& rebalance) {
// Build lev from hyam and hybm
const Real ps0 = 100000.0;
- auto hya_v = hyam.get_view();
- auto hyb_v = hybm.get_view();
- auto lev_v = lev.get_view();
- for (int ii=0;iiget_num_vertical_levels();ii++) {
- lev_v(ii) = 0.01*ps0*(hya_v(ii)+hyb_v(ii));
+ auto hyam_v = hyam.get_view();
+ auto hybm_v = hybm.get_view();
+ auto hyai_v = hyai.get_view();
+ auto hybi_v = hybi.get_view();
+ auto lev_v = lev.get_view();
+ auto ilev_v = ilev.get_view();
+ auto num_v_levs = phys_grid->get_num_vertical_levels();
+ for (int ii=0;ii("sfc_alb_dir_vis", scalar2d, nondim, grid_name);
+ //----------- Variables from microphysics scheme -------------
+
+ // Evaporation from stratiform rain [kg/kg/s]
+ add_field("nevapr", scalar3d_mid, kg / kg / s, grid_name);
+
+ // Stratiform rain production rate [kg/kg/s]
+ add_field("precip_total_tend", scalar3d_mid, kg / kg / s,
+ grid_name);
+
// ---------------------------------------------------------------------
// These variables are "updated" or inputs/outputs for the process
// ---------------------------------------------------------------------
@@ -205,7 +215,7 @@ void MAMMicrophysics::set_grids(
LinozHorizInterp_, linoz_file_name_);
// linoz reader
- const auto io_grid_linoz = LinozHorizInterp_->get_src_grid();
+ const auto io_grid_linoz = LinozHorizInterp_->get_tgt_grid();
const int num_cols_io_linoz =
io_grid_linoz->get_num_local_dofs(); // Number of columns on this rank
const int num_levs_io_linoz =
@@ -233,7 +243,7 @@ void MAMMicrophysics::set_grids(
TracerHorizInterp_, oxid_file_name_);
const int nvars = int(var_names.size());
- const auto io_grid = TracerHorizInterp_->get_src_grid();
+ const auto io_grid = TracerHorizInterp_->get_tgt_grid();
const int num_cols_io =
io_grid->get_num_local_dofs(); // Number of columns on this rank
const int num_levs_io =
@@ -258,78 +268,88 @@ void MAMMicrophysics::set_grids(
"num_a1", "num_a2", "num_a4", "soag"};
for(const auto &var_name : extfrc_lst_) {
- std::string item_name = "mam4_" + var_name + "_verti_emiss_file_name";
+ std::string item_name = "mam4_" + var_name + "_elevated_emiss_file_name";
const auto file_name = m_params.get(item_name);
- vert_emis_file_name_[var_name] = file_name;
+ elevated_emis_file_name_[var_name] = file_name;
}
- vert_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"};
- vert_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"};
- vert_emis_var_names_["so4_a2"] = {"contvolc"};
- vert_emis_var_names_["pom_a4"] = {"BB"};
- vert_emis_var_names_["bc_a4"] = {"BB"};
- vert_emis_var_names_["num_a1"] = {
+ elevated_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"};
+ elevated_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"};
+ elevated_emis_var_names_["so4_a2"] = {"contvolc"};
+ elevated_emis_var_names_["pom_a4"] = {"BB"};
+ elevated_emis_var_names_["bc_a4"] = {"BB"};
+ elevated_emis_var_names_["num_a1"] = {
"num_a1_SO4_ELEV_BB", "num_a1_SO4_ELEV_ENE", "num_a1_SO4_ELEV_IND",
"num_a1_SO4_ELEV_contvolc"};
- vert_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"};
+ elevated_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"};
// num_a4
// FIXME: why the sectors in this files are num_a1;
// I guess this should be num_a4? Is this a bug in the orginal nc files?
- vert_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB",
+ elevated_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB",
"num_a1_POM_ELEV_BB"};
- vert_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"};
+ elevated_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"};
- int verti_emiss_cyclical_ymd = m_params.get("verti_emiss_ymd");
+ int elevated_emiss_cyclical_ymd = m_params.get("elevated_emiss_ymd");
for(const auto &var_name : extfrc_lst_) {
- const auto file_name = vert_emis_file_name_[var_name];
- const auto var_names = vert_emis_var_names_[var_name];
+ const auto file_name = elevated_emis_file_name_[var_name];
+ const auto var_names = elevated_emis_var_names_[var_name];
scream::mam_coupling::TracerData data_tracer;
scream::mam_coupling::setup_tracer_data(data_tracer, file_name,
- verti_emiss_cyclical_ymd);
+ elevated_emiss_cyclical_ymd);
auto hor_rem = scream::mam_coupling::create_horiz_remapper(
grid_, file_name, extfrc_map_file, var_names, data_tracer);
+
auto file_reader =
- scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name);
- VertEmissionsHorizInterp_.push_back(hor_rem);
- VertEmissionsDataReader_.push_back(file_reader);
- vert_emis_data_.push_back(data_tracer);
- } // var_name vert emissions
+ scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name,
+ data_tracer.file_type);
+ ElevatedEmissionsHorizInterp_.push_back(hor_rem);
+ ElevatedEmissionsDataReader_.push_back(file_reader);
+ elevated_emis_data_.push_back(data_tracer);
+ } // var_name elevated emissions
int i = 0;
int offset_emis_ver = 0;
for(const auto &var_name : extfrc_lst_) {
- const auto file_name = vert_emis_file_name_[var_name];
- const auto var_names = vert_emis_var_names_[var_name];
+ const auto file_name = elevated_emis_file_name_[var_name];
+ const auto var_names = elevated_emis_var_names_[var_name];
const int nvars = static_cast(var_names.size());
forcings_[i].nsectors = nvars;
// I am assuming the order of species in extfrc_lst_.
// Indexing in mam4xx is fortran.
forcings_[i].frc_ndx = i + 1;
- const auto io_grid_emis = VertEmissionsHorizInterp_[i]->get_src_grid();
+ const auto io_grid_emis = ElevatedEmissionsHorizInterp_[i]->get_tgt_grid();
const int num_cols_io_emis =
io_grid_emis->get_num_local_dofs(); // Number of columns on this rank
const int num_levs_io_emis =
io_grid_emis
->get_num_vertical_levels(); // Number of levels per column
- vert_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars);
- vert_emis_data_[i].allocate_temporal_views();
- forcings_[i].file_alt_data = vert_emis_data_[i].has_altitude_;
+ elevated_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars);
+ elevated_emis_data_[i].allocate_temporal_views();
+ forcings_[i].file_alt_data = elevated_emis_data_[i].has_altitude_;
for(int isp = 0; isp < nvars; ++isp) {
forcings_[i].offset = offset_emis_ver;
- vert_emis_output_[isp + offset_emis_ver] =
- view_2d("vert_emis_output_", ncol_, nlev_);
+ elevated_emis_output_[isp + offset_emis_ver] =
+ view_2d("elevated_emis_output_", ncol_, nlev_);
}
offset_emis_ver += nvars;
++i;
} // end i
EKAT_REQUIRE_MSG(
- offset_emis_ver <= int(mam_coupling::MAX_NUM_VERT_EMISSION_FIELDS),
+ offset_emis_ver <= int(mam_coupling::MAX_NUM_ELEVATED_EMISSIONS_FIELDS),
"Error! Number of fields is bigger than "
- "MAX_NUM_VERT_EMISSION_FIELDS. Increase the "
- "MAX_NUM_VERT_EMISSION_FIELDS in tracer_reader_utils.hpp \n");
+ "MAX_NUM_ELEVATED_EMISSIONS_FIELDS. Increase the "
+ "MAX_NUM_ELEVATED_EMISSIONS_FIELDS in tracer_reader_utils.hpp \n");
} // Tracer external forcing data
+
+ {
+ const std::string season_wes_file = m_params.get("mam4_season_wes_file");
+ const auto& clat = col_latitudes_;
+ mam_coupling::find_season_index_reader(season_wes_file,
+ clat,
+ index_season_lai_);
+ }
} // set_grids
// ================================================================
@@ -501,6 +521,9 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) {
const int photo_table_len = get_photo_table_work_len(photo_table_);
work_photo_table_ = view_2d("work_photo_table", ncol_, photo_table_len);
+ const int sethet_work_len = mam4::mo_sethet::get_total_work_len_sethet();
+ work_set_het_ = view_2d("work_set_het_array", ncol_, sethet_work_len);
+ cmfdqr_ = view_1d("cmfdqr_", nlev_);
// here's where we store per-column photolysis rates
photo_rates_ = view_3d("photo_rates", ncol_, nlev_, mam4::mo_photo::phtcnt);
@@ -518,8 +541,8 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) {
for(int i = 0; i < static_cast(extfrc_lst_.size()); ++i) {
scream::mam_coupling::update_tracer_data_from_file(
- VertEmissionsDataReader_[i], curr_month, *VertEmissionsHorizInterp_[i],
- vert_emis_data_[i]);
+ ElevatedEmissionsDataReader_[i], curr_month, *ElevatedEmissionsHorizInterp_[i],
+ elevated_emis_data_[i]);
}
invariants_ = view_3d("invarians", ncol_, nlev_, mam4::gas_chemistry::nfs);
@@ -554,6 +577,14 @@ void MAMMicrophysics::run_impl(const double dt) {
Kokkos::parallel_for("preprocess", scan_policy, preprocess_);
Kokkos::fence();
+ //----------- Variables from microphysics scheme -------------
+
+ // Evaporation from stratiform rain [kg/kg/s]
+ const auto& nevapr = get_field_in("nevapr").get_view();
+
+ // Stratiform rain production rate [kg/kg/s]
+ const auto& prain = get_field_in("precip_total_tend").get_view();
+
const auto wet_geometric_mean_diameter_i =
get_field_in("dgnumwet").get_view();
const auto dry_geometric_mean_diameter_i =
@@ -616,20 +647,20 @@ void MAMMicrophysics::run_impl(const double dt) {
linoz_output); // out
Kokkos::fence();
- vert_emiss_time_state_.t_now = ts.frac_of_year_in_days();
+ elevated_emiss_time_state_.t_now = ts.frac_of_year_in_days();
int i = 0;
for(const auto &var_name : extfrc_lst_) {
- const auto file_name = vert_emis_file_name_[var_name];
- const auto var_names = vert_emis_var_names_[var_name];
+ const auto file_name = elevated_emis_file_name_[var_name];
+ const auto var_names = elevated_emis_var_names_[var_name];
const int nsectors = int(var_names.size());
- view_2d vert_emis_output[nsectors];
+ view_2d elevated_emis_output[nsectors];
for(int isp = 0; isp < nsectors; ++isp) {
- vert_emis_output[isp] = vert_emis_output_[isp + forcings_[i].offset];
+ elevated_emis_output[isp] = elevated_emis_output_[isp + forcings_[i].offset];
}
scream::mam_coupling::advance_tracer_data(
- VertEmissionsDataReader_[i], *VertEmissionsHorizInterp_[i], ts,
- vert_emiss_time_state_, vert_emis_data_[i], dry_atm_.p_mid,
- dry_atm_.z_iface, vert_emis_output);
+ ElevatedEmissionsDataReader_[i], *ElevatedEmissionsHorizInterp_[i], ts,
+ elevated_emiss_time_state_, elevated_emis_data_[i], dry_atm_.p_mid,
+ dry_atm_.z_iface, elevated_emis_output);
i++;
Kokkos::fence();
}
@@ -704,7 +735,7 @@ void MAMMicrophysics::run_impl(const double dt) {
const auto zenith_angle = acos_cosine_zenith_;
constexpr int gas_pcnst = mam_coupling::gas_pcnst();
- const auto& vert_emis_output = vert_emis_output_;
+ const auto& elevated_emis_output = elevated_emis_output_;
const auto& extfrc = extfrc_;
const auto& forcings = forcings_;
constexpr int extcnt = mam4::gas_chemistry::extcnt;
@@ -722,6 +753,8 @@ void MAMMicrophysics::run_impl(const double dt) {
clsmap_4[i] = mam4::gas_chemistry::clsmap_4[i];
permute_4[i] = mam4::gas_chemistry::permute_4[i];
}
+ const auto& cmfdqr = cmfdqr_;
+ const auto& work_set_het =work_set_het_;
// loop over atmosphere columns and compute aerosol microphyscs
Kokkos::parallel_for(
policy, KOKKOS_LAMBDA(const ThreadTeam &team) {
@@ -756,7 +789,7 @@ void MAMMicrophysics::run_impl(const double dt) {
// We may need to move this line where we read files.
forcings_in[i].file_alt_data = file_alt_data;
for(int isec = 0; isec < forcings[i].nsectors; ++isec) {
- const auto field = vert_emis_output[isec + forcings[i].offset];
+ const auto field = elevated_emis_output[isec + forcings[i].offset];
forcings_in[i].fields_data[isec] = ekat::subview(field, icol);
}
} // extcnt for loop
@@ -787,6 +820,9 @@ void MAMMicrophysics::run_impl(const double dt) {
ekat::subview(linoz_dPmL_dO3col, icol);
const auto linoz_cariolle_pscs_icol =
ekat::subview(linoz_cariolle_pscs, icol);
+ const auto nevapr_icol = ekat::subview(nevapr, icol);
+ const auto prain_icol = ekat::subview(prain, icol);
+ const auto work_set_het_icol = ekat::subview(work_set_het, icol);
// Note: All variables are inputs, except for progs, which is an
// input/output variable.
mam4::microphysics::perform_atmospheric_chemistry_and_microphysics(
@@ -800,7 +836,12 @@ void MAMMicrophysics::run_impl(const double dt) {
linoz_cariolle_pscs_icol, eccf, adv_mass_kg_per_moles, clsmap_4,
permute_4, offset_aerosol,
config.linoz.o3_sfc, config.linoz.o3_tau, config.linoz.o3_lbl,
- dry_diameter_icol, wet_diameter_icol, wetdens_icol);
+ dry_diameter_icol, wet_diameter_icol, wetdens_icol,
+ dry_atm.phis(icol),
+ cmfdqr,
+ prain_icol,
+ nevapr_icol,
+ work_set_het_icol);
}); // parallel_for for the column loop
Kokkos::fence();
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.hpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.hpp
index 6b1dd33dfaa2..6ff846d0d0c2 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.hpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.hpp
@@ -25,6 +25,8 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
using view_1d_host = typename KT::view_1d::HostMirror;
+ using view_int_2d = typename KT::template view_2d;
+
// a thread team dispatched to a single vertical column
using ThreadTeam = mam4::ThreadTeam;
@@ -225,20 +227,25 @@ class MAMMicrophysics final : public scream::AtmosphereProcess {
// Vertical emission uses 9 files, here I am using std::vector to stote
// instance of each file.
- mam_coupling::TracerTimeState vert_emiss_time_state_;
- std::vector> VertEmissionsDataReader_;
- std::vector> VertEmissionsHorizInterp_;
+ mam_coupling::TracerTimeState elevated_emiss_time_state_;
+ std::vector> ElevatedEmissionsDataReader_;
+ std::vector> ElevatedEmissionsHorizInterp_;
std::vector extfrc_lst_;
- std::vector vert_emis_data_;
- std::map vert_emis_file_name_;
- std::map> vert_emis_var_names_;
- view_2d vert_emis_output_[mam_coupling::MAX_NUM_VERT_EMISSION_FIELDS];
+ std::vector elevated_emis_data_;
+ std::map elevated_emis_file_name_;
+ std::map> elevated_emis_var_names_;
+ view_2d elevated_emis_output_[mam_coupling::MAX_NUM_ELEVATED_EMISSIONS_FIELDS];
view_3d extfrc_;
mam_coupling::ForcingHelper forcings_[mam4::gas_chemistry::extcnt];
view_1d_host acos_cosine_zenith_host_;
view_1d acos_cosine_zenith_;
+ view_int_2d index_season_lai_;
+ // // dq/dt for convection [kg/kg/s]
+ view_1d cmfdqr_;
+ view_2d work_set_het_;
+
}; // MAMMicrophysics
} // namespace scream
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp
new file mode 100644
index 000000000000..9c01daf8223c
--- /dev/null
+++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp
@@ -0,0 +1,73 @@
+#ifndef EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP
+#define EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP
+
+namespace scream {
+
+namespace {
+
+using KT = ekat::KokkosTypes;
+using view_1d = typename KT::template view_1d;
+using view_2d = typename KT::template view_2d;
+using const_view_1d = typename KT::template view_1d;
+using const_view_2d = typename KT::template view_2d;
+
+//-------- Inititlize gas and aerosol fluxes ------
+void init_fluxes(const int &ncol,
+ view_2d &constituent_fluxes) { // input-output
+
+ constexpr int pcnst = mam4::aero_model::pcnst;
+ const int gas_start_ind = mam4::utils::gasses_start_ind();
+
+ const auto policy =
+ ekat::ExeSpaceUtils::get_default_team_policy(
+ ncol, pcnst - gas_start_ind);
+
+ // Parallel loop over all the columns
+ Kokkos::parallel_for(
+ policy, KOKKOS_LAMBDA(const KT::MemberType &team) {
+ const int icol = team.league_rank();
+ view_1d flux_col = ekat::subview(constituent_fluxes, icol);
+
+ // Zero out constituent fluxes only for gasses and aerosols
+ Kokkos::parallel_for(
+ Kokkos::TeamVectorRange(team, gas_start_ind, pcnst),
+ [&](int icnst) { flux_col(icnst) = 0; });
+ });
+} // init_fluxes ends
+
+//-------- compute online emissions for dust, sea salt and marine organics -----
+void compute_online_dust_nacl_emiss(
+ const int &ncol, const int &nlev, const const_view_1d &ocnfrac,
+ const const_view_1d &sst, const const_view_2d &u_wind,
+ const const_view_2d &v_wind, const const_view_2d &dstflx,
+ const const_view_1d &mpoly, const const_view_1d &mprot,
+ const const_view_1d &mlip, const const_view_1d &soil_erodibility,
+ const const_view_2d &z_mid,
+ // output
+ view_2d &constituent_fluxes) {
+ const int surf_lev = nlev - 1; // surface level
+
+ Kokkos::parallel_for(
+ "online_emis_fluxes", ncol, KOKKOS_LAMBDA(int icol) {
+ // Input
+ const const_view_1d dstflx_icol = ekat::subview(dstflx, icol);
+
+ // Output
+ view_1d fluxes_col = ekat::subview(constituent_fluxes, icol);
+
+ // Compute online emissions
+ // NOTE: mam4::aero_model_emissions calculates mass and number emission
+ // fluxes in units of [kg/m2/s or #/m2/s] (MKS), so no need to convert
+ mam4::aero_model_emissions::aero_model_emissions(
+ sst(icol), ocnfrac(icol), u_wind(icol, surf_lev),
+ v_wind(icol, surf_lev), z_mid(icol, surf_lev), dstflx_icol,
+ soil_erodibility(icol), mpoly(icol), mprot(icol), mlip(icol),
+ // out
+ fluxes_col);
+ });
+} // compute_online_dust_nacl_emiss ends
+
+} // namespace
+} // namespace scream
+
+#endif // EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp
index 850a82d0896d..fd3ebf79700f 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp
@@ -1,14 +1,24 @@
-//#include
#include
+// For surface and online emission functions
+#include
+
+// For reading soil erodibility file
+#include
+
namespace scream {
+// For reading soil erodibility file
+using soilErodibilityFunc =
+ soil_erodibility::soilErodibilityFunctions;
+
// ================================================================
// Constructor
// ================================================================
MAMSrfOnlineEmiss::MAMSrfOnlineEmiss(const ekat::Comm &comm,
const ekat::ParameterList ¶ms)
: AtmosphereProcess(comm, params) {
+ // FIXME: Do we want to read dust emiss factor from the namelist??
/* Anything that can be initialized without grid information can be
* initialized here. Like universal constants.
*/
@@ -26,18 +36,98 @@ void MAMSrfOnlineEmiss::set_grids(
nlev_ = grid_->get_num_vertical_levels(); // Number of levels per column
using namespace ekat::units;
+ constexpr auto m2 = pow(m, 2);
+ constexpr auto s2 = pow(s, 2);
+ constexpr auto q_unit = kg / kg; // units of mass mixing ratios of tracers
+ constexpr auto n_unit = 1 / kg; // units of number mixing ratios of tracers
+ constexpr auto nondim = ekat::units::Units::nondimensional();
- static constexpr int pcnst = mam4::aero_model::pcnst;
- const FieldLayout scalar2d_pcnct =
- grid_->get_2d_vector_layout(pcnst, "num_phys_constituents");
+ const FieldLayout scalar2d = grid_->get_2d_scalar_layout();
+ const FieldLayout scalar3d_m = grid_->get_3d_scalar_layout(true); // mid
+ const FieldLayout scalar3d_i = grid_->get_3d_scalar_layout(false); // int
+
+ // For U and V components of wind
+ const FieldLayout vector3d = grid_->get_3d_vector_layout(true, 2);
+
+ // For components of dust flux
+ const FieldLayout vector4d = grid_->get_2d_vector_layout(4);
+
+ // --------------------------------------------------------------------------
+ // These variables are "Required" or pure inputs for the process
+ // --------------------------------------------------------------------------
+
+ // ----------- Atmospheric quantities -------------
+
+ // -- Variables required for building DS to compute z_mid --
+ // Specific humidity [kg/kg]
+ // FIXME: Comply with add_tracer calls
+ add_field("qv", scalar3d_m, q_unit, grid_name, "tracers");
+
+ // Cloud liquid mass mixing ratio [kg/kg]
+ add_field("qc", scalar3d_m, q_unit, grid_name, "tracers");
+
+ // Cloud ice mass mixing ratio [kg/kg]
+ add_field("qi", scalar3d_m, q_unit, grid_name, "tracers");
+
+ // Cloud liquid number mixing ratio [1/kg]
+ add_field("nc", scalar3d_m, n_unit, grid_name, "tracers");
+
+ // Cloud ice number mixing ratio [1/kg]
+ add_field("ni", scalar3d_m, n_unit, grid_name, "tracers");
+
+ // Temperature[K] at midpoints
+ add_field("T_mid", scalar3d_m, K, grid_name);
+
+ // Vertical pressure velocity [Pa/s] at midpoints
+ add_field("omega", scalar3d_m, Pa / s, grid_name);
+
+ // Total pressure [Pa] at midpoints
+ add_field("p_mid", scalar3d_m, Pa, grid_name);
+
+ // Total pressure [Pa] at interfaces
+ add_field("p_int", scalar3d_i, Pa, grid_name);
+
+ // Layer thickness(pdel) [Pa] at midpoints
+ add_field("pseudo_density", scalar3d_m, Pa, grid_name);
+
+ // Planetary boundary layer height [m]
+ add_field("pbl_height", scalar2d, m, grid_name);
+
+ // Surface geopotential [m2/s2]
+ add_field("phis", scalar2d, m2 / s2, grid_name);
+
+ //----------- Variables from microphysics scheme -------------
+
+ // Total cloud fraction [fraction] (Require only for building DS)
+ add_field("cldfrac_tot", scalar3d_m, nondim, grid_name);
+
+ // -- Variables required for online dust and sea salt emissions --
+
+ // U and V components of the wind[m/s]
+ add_field("horiz_winds", vector3d, m / s, grid_name);
+
+ //----------- Variables from coupler (ocean component)---------
+ // Ocean fraction [unitless]
+ add_field("ocnfrac", scalar2d, nondim, grid_name);
+
+ // Sea surface temperature [K]
+ add_field("sst", scalar2d, K, grid_name);
+
+ // dust fluxes [kg/m^2/s]: Four flux values for each column
+ add_field("dstflx", vector4d, kg / m2 / s, grid_name);
// -------------------------------------------------------------
- // These variables are "Computed" or outputs for the process
+ // These variables are "Updated" or input-outputs for the process
// -------------------------------------------------------------
- static constexpr Units m2(m * m, "m2");
+
+ constexpr int pcnst = mam4::aero_model::pcnst;
+ const FieldLayout vector2d_pcnst =
+ grid_->get_2d_vector_layout(pcnst, "num_phys_constituents");
+
// Constituent fluxes of species in [kg/m2/s]
- add_field("constituent_fluxes", scalar2d_pcnct, kg / m2 / s,
- grid_name);
+ // FIXME: confirm if it is Updated or Computed
+ add_field("constituent_fluxes", vector2d_pcnst, kg / m2 / s,
+ grid_name);
// Surface emissions remapping file
auto srf_map_file = m_params.get("srf_remap_file", "");
@@ -63,6 +153,7 @@ void MAMSrfOnlineEmiss::set_grids(
so2.species_name = "so2";
so2.sectors = {"AGR", "RCO", "SHP", "SLV", "TRA", "WST"};
srf_emiss_species_.push_back(so2); // add to the vector
+
//--------------------------------------------------------------------
// Init bc_a4 srf emiss data structures
//--------------------------------------------------------------------
@@ -147,7 +238,48 @@ void MAMSrfOnlineEmiss::set_grids(
// output
ispec_srf.horizInterp_, ispec_srf.data_start_, ispec_srf.data_end_,
ispec_srf.data_out_, ispec_srf.dataReader_);
- }
+ } // srf emissions file read init
+
+ // -------------------------------------------------------------
+ // Setup to enable reading soil erodibility file
+ // -------------------------------------------------------------
+
+ const std::string soil_erodibility_data_file =
+ m_params.get("soil_erodibility_file");
+
+ // Field to be read from file
+ const std::string soil_erod_fld_name = "mbl_bsn_fct_geo";
+
+ // Dimensions of the field
+ const std::string soil_erod_dname = "ncol";
+
+ // initialize the file read
+ soilErodibilityFunc::init_soil_erodibility_file_read(
+ ncol_, soil_erod_fld_name, soil_erod_dname, grid_,
+ soil_erodibility_data_file, srf_map_file, serod_horizInterp_,
+ serod_dataReader_); // output
+
+ // -------------------------------------------------------------
+ // Setup to enable reading marine organics file
+ // -------------------------------------------------------------
+ const std::string marine_organics_data_file =
+ m_params.get("marine_organics_file");
+
+ // Fields to be read from file (order matters as they are read in the same
+ // order)
+ const std::vector marine_org_fld_name = {
+ "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"};
+
+ // Dimensions of the field
+ const std::string marine_org_dname = "ncol";
+
+ // initialize the file read
+ marineOrganicsFunc::init_marine_organics_file_read(
+ ncol_, marine_org_fld_name, marine_org_dname, grid_,
+ marine_organics_data_file, srf_map_file,
+ // output
+ morg_horizInterp_, morg_data_start_, morg_data_end_, morg_data_out_,
+ morg_dataReader_);
} // set_grid ends
@@ -182,6 +314,42 @@ void MAMSrfOnlineEmiss::init_buffers(const ATMBufferManager &buffer_manager) {
// INITIALIZE_IMPL
// ================================================================
void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) {
+ // ---------------------------------------------------------------
+ // Input fields read in from IC file, namelist or other processes
+ // ---------------------------------------------------------------
+
+ // Populate the wet atmosphere state with views from fields
+ wet_atm_.qv = get_field_in("qv").get_view();
+
+ // Following wet_atm vars are required only for building DS
+ wet_atm_.qc = get_field_in("qc").get_view();
+ wet_atm_.nc = get_field_in("nc").get_view();
+ wet_atm_.qi = get_field_in("qi").get_view();
+ wet_atm_.ni = get_field_in("ni").get_view();
+
+ // Populate the dry atmosphere state with views from fields
+ dry_atm_.T_mid = get_field_in("T_mid").get_view();
+ dry_atm_.p_mid = get_field_in("p_mid").get_view();
+ dry_atm_.p_del = get_field_in("pseudo_density").get_view();
+ dry_atm_.p_int = get_field_in("p_int").get_view();
+
+ // Following dry_atm vars are required only for building DS
+ dry_atm_.cldfrac = get_field_in("cldfrac_tot").get_view();
+ dry_atm_.pblh = get_field_in("pbl_height").get_view();
+ dry_atm_.omega = get_field_in("omega").get_view();
+
+ // store fields converted to dry mmr from wet mmr in dry_atm_
+ dry_atm_.z_mid = buffer_.z_mid;
+ dry_atm_.z_iface = buffer_.z_iface;
+ dry_atm_.dz = buffer_.dz;
+ dry_atm_.qv = buffer_.qv_dry;
+ dry_atm_.qc = buffer_.qc_dry;
+ dry_atm_.nc = buffer_.nc_dry;
+ dry_atm_.qi = buffer_.qi_dry;
+ dry_atm_.ni = buffer_.ni_dry;
+ dry_atm_.w_updraft = buffer_.w_updraft;
+ dry_atm_.z_surf = 0.0; // FIXME: for now
+
// ---------------------------------------------------------------
// Output fields
// ---------------------------------------------------------------
@@ -212,10 +380,27 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) {
ispec_srf.data_end_); // output
}
+ //-----------------------------------------------------------------
+ // Read Soil erodibility data
+ //-----------------------------------------------------------------
+ // This data is time-independent, we read all data here for the
+ // entire simulation
+ soilErodibilityFunc::update_soil_erodibility_data_from_file(
+ serod_dataReader_, *serod_horizInterp_,
+ soil_erodibility_); // output
+
+ //--------------------------------------------------------------------
+ // Update marine orgaincs from file
+ //--------------------------------------------------------------------
+ // Time dependent data
+ marineOrganicsFunc::update_marine_organics_data_from_file(
+ morg_dataReader_, timestamp(), curr_month, *morg_horizInterp_,
+ morg_data_end_); // output
+
//-----------------------------------------------------------------
// Setup preprocessing and post processing
//-----------------------------------------------------------------
- preprocess_.initialize(constituent_fluxes_);
+ preprocess_.initialize(ncol_, nlev_, wet_atm_, dry_atm_);
} // end initialize_impl()
@@ -223,14 +408,79 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) {
// RUN_IMPL
// ================================================================
void MAMSrfOnlineEmiss::run_impl(const double dt) {
- // Zero-out output
- Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0);
+ const auto scan_policy = ekat::ExeSpaceUtils<
+ KT::ExeSpace>::get_thread_range_parallel_scan_team_policy(ncol_, nlev_);
+
+ // preprocess input -- needs a scan for the calculation of atm height
+ Kokkos::parallel_for("preprocess", scan_policy, preprocess_);
+ Kokkos::fence();
+
+ // Constituent fluxes [kg/m^2/s]
+ auto constituent_fluxes = this->constituent_fluxes_;
+ // Zero out constituent fluxes only for gasses and aerosols
+ init_fluxes(ncol_, // in
+ constituent_fluxes); // in-out
+ Kokkos::fence();
// Gather time and state information for interpolation
- auto ts = timestamp() + dt;
+ const auto ts = timestamp() + dt;
//--------------------------------------------------------------------
- // Interpolate srf emiss data
+ // Online emissions from dust and sea salt
+ //--------------------------------------------------------------------
+
+ // --- Interpolate marine organics data --
+
+ // Update TimeState, note the addition of dt
+ morg_timeState_.t_now = ts.frac_of_year_in_days();
+
+ // Update time state and if the month has changed, update the data.
+ marineOrganicsFunc::update_marine_organics_timestate(
+ morg_dataReader_, ts, *morg_horizInterp_,
+ // output
+ morg_timeState_, morg_data_start_, morg_data_end_);
+
+ // Call the main marine organics routine to get interpolated forcings.
+ marineOrganicsFunc::marineOrganics_main(morg_timeState_, morg_data_start_,
+ morg_data_end_, morg_data_out_);
+
+ // Marine organics emission data read from the file (order is important here)
+ const const_view_1d mpoly = ekat::subview(morg_data_out_.emiss_sectors, 0);
+ const const_view_1d mprot = ekat::subview(morg_data_out_.emiss_sectors, 1);
+ const const_view_1d mlip = ekat::subview(morg_data_out_.emiss_sectors, 2);
+
+ // Ocean fraction [unitless]
+ const const_view_1d ocnfrac =
+ get_field_in("ocnfrac").get_view();
+
+ // Sea surface temperature [K]
+ const const_view_1d sst = get_field_in("sst").get_view();
+
+ // U wind component [m/s]
+ const const_view_2d u_wind =
+ get_field_in("horiz_winds").get_component(0).get_view();
+
+ // V wind component [m/s]
+ const const_view_2d v_wind =
+ get_field_in("horiz_winds").get_component(1).get_view();
+
+ // Dust fluxes [kg/m^2/s]: Four flux values for each column
+ const const_view_2d dstflx = get_field_in("dstflx").get_view();
+
+ // Soil edodibility [fraction]
+ const const_view_1d soil_erodibility = this->soil_erodibility_;
+
+ // Vertical layer height at midpoints
+ const const_view_2d z_mid = dry_atm_.z_mid;
+
+ compute_online_dust_nacl_emiss(ncol_, nlev_, ocnfrac, sst, u_wind, v_wind,
+ dstflx, mpoly, mprot, mlip, soil_erodibility,
+ z_mid,
+ // output
+ constituent_fluxes);
+ Kokkos::fence();
+ //--------------------------------------------------------------------
+ // Interpolate srf emiss data read in from emissions files
//--------------------------------------------------------------------
for(srf_emiss_ &ispec_srf : srf_emiss_species_) {
@@ -256,20 +506,18 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) {
// modify units from molecules/cm2/s to kg/m2/s
auto fluxes_in_mks_units = this->fluxes_in_mks_units_;
- auto constituent_fluxes = this->constituent_fluxes_;
const Real mfactor =
amufac * mam4::gas_chemistry::adv_mass[species_index - offset_];
+ const view_1d ispec_outdata0 =
+ ekat::subview(ispec_srf.data_out_.emiss_sectors, 0);
// Parallel loop over all the columns to update units
Kokkos::parallel_for(
- "fluxes", ncol_, KOKKOS_LAMBDA(int icol) {
- fluxes_in_mks_units(icol) =
- ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor;
+ "srf_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) {
+ fluxes_in_mks_units(icol) = ispec_outdata0(icol) * mfactor;
constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol);
});
-
} // for loop for species
Kokkos::fence();
-} // run_imple ends
-
+} // run_impl ends
// =============================================================================
} // namespace scream
diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp
index 031fb62d8b75..1a3bb4f36e3f 100644
--- a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp
+++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp
@@ -8,11 +8,12 @@
#include
#include
+// For reading marine organics file
+#include
+
// For declaring surface and online emission class derived from atm process
// class
#include
-
-// #include
#include
namespace scream {
@@ -20,19 +21,31 @@ namespace scream {
// The process responsible for handling MAM4 surface and online emissions. The
// AD stores exactly ONE instance of this class in its list of subcomponents.
class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess {
- using KT = ekat::KokkosTypes;
- using view_1d = typename KT::template view_1d;
- using view_2d = typename KT::template view_2d;
+ using KT = ekat::KokkosTypes;
+ using view_1d = typename KT::template view_1d;
+ using view_2d = typename KT::template view_2d;
+ using const_view_1d = typename KT::template view_1d;
+ using const_view_2d = typename KT::template view_2d;
// number of horizontal columns and vertical levels
int ncol_, nlev_;
+ // Wet and dry states of atmosphere
+ mam_coupling::WetAtmosphere wet_atm_;
+ mam_coupling::DryAtmosphere dry_atm_;
+
// buffer for sotring temporary variables
mam_coupling::Buffer buffer_;
// physics grid for column information
std::shared_ptr grid_;
+ // Sea surface temoerature [K]
+ const_view_1d sst_;
+
+ // Dust fluxes (four values for each col) [kg/m2/s]
+ const_view_2d dust_fluxes_;
+
// Constituent fluxes of species in [kg/m2/s]
view_2d constituent_fluxes_;
@@ -42,8 +55,16 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess {
// Unified atomic mass unit used for unit conversion (BAD constant)
static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu
+ // For reading soil erodibility file
+ std::shared_ptr serod_horizInterp_;
+ std::shared_ptr serod_dataReader_;
+ const_view_1d soil_erodibility_;
+
public:
+ // For reading surface emissions and marine organics file
using srfEmissFunc = mam_coupling::srfEmissFunctions;
+ using marineOrganicsFunc =
+ marine_organics::marineOrganicsFunctions;
// Constructor
MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms);
@@ -80,13 +101,35 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess {
struct Preprocess {
Preprocess() = default;
// on host: initializes preprocess functor with necessary state data
- void initialize(const view_2d &constituent_fluxes) {
- constituent_fluxes_pre_ = constituent_fluxes;
+ void initialize(const int &ncol, const int &nlev,
+ const mam_coupling::WetAtmosphere &wet_atm,
+ const mam_coupling::DryAtmosphere &dry_atm) {
+ ncol_pre_ = ncol;
+ nlev_pre_ = nlev;
+ wet_atm_pre_ = wet_atm;
+ dry_atm_pre_ = dry_atm;
}
+ KOKKOS_INLINE_FUNCTION
+ void operator()(
+ const Kokkos::TeamPolicy::member_type &team) const {
+ const int icol = team.league_rank(); // column index
+
+ compute_dry_mixing_ratios(team, wet_atm_pre_, dry_atm_pre_, icol);
+ team.team_barrier();
+ // vertical heights has to be computed after computing dry mixing ratios
+ // for atmosphere
+ compute_vertical_layer_heights(team, dry_atm_pre_, icol);
+ compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, icol);
+ } // Preprocess operator()
+
// local variables for preprocess struct
- view_2d constituent_fluxes_pre_;
- }; // MAMSrfOnlineEmiss::Preprocess
+ // number of horizontal columns and vertical levels
+ int ncol_pre_, nlev_pre_;
+ // local atmospheric and aerosol state data
+ mam_coupling::WetAtmosphere wet_atm_pre_;
+ mam_coupling::DryAtmosphere dry_atm_pre_;
+ }; // MAMSrfOnlineEmiss::Preprocess
private:
// preprocessing scratch pad
Preprocess preprocess_;
@@ -95,9 +138,11 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess {
// FIXME: Remove the hardwired indices and use a function
// to find them from an array.
const std::map spcIndex_in_pcnst_ = {
- {"so2", 12}, {"dms", 13}, {"so4_a1", 15},
- {"num_a1", 22}, {"so4_a2", 23}, {"num_a2", 27},
- {"pom_a4", 36}, {"bc_a4", 37}, {"num_a4", 39}};
+ {"so2", 12}, {"dms", 13}, {"so4_a1", 15}, {"dst_a1", 19},
+ {"ncl_a1", 20}, {"mom_a1", 21}, {"num_a1", 22}, {"so4_a2", 23},
+ {"ncl_a2", 25}, {"mom_a2", 26}, {"num_a2", 27}, {"dst_a3", 28},
+ {"ncl_a3", 29}, {"num_a3", 35}, {"pom_a4", 36}, {"bc_a4", 37},
+ {"mom_a4", 38}, {"num_a4", 39}};
// A struct carrying all the fields needed to read
// surface emissions of a species
@@ -122,6 +167,13 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess {
// A vector for carrying emissions for all the species
std::vector srf_emiss_species_;
+ // For reading marine organics file
+ std::shared_ptr morg_horizInterp_;
+ std::shared_ptr morg_dataReader_;
+ marineOrganicsFunc::marineOrganicsTimeState morg_timeState_;
+ marineOrganicsFunc::marineOrganicsInput morg_data_start_, morg_data_end_;
+ marineOrganicsFunc::marineOrganicsOutput morg_data_out_;
+
// offset for converting pcnst index to gas_pcnst index
static constexpr int offset_ =
mam4::aero_model::pcnst - mam4::gas_chemistry::gas_pcnst;
diff --git a/components/eamxx/src/physics/mam/readfiles/find_season_index_utils.hpp b/components/eamxx/src/physics/mam/readfiles/find_season_index_utils.hpp
new file mode 100644
index 000000000000..2e930cc65cbf
--- /dev/null
+++ b/components/eamxx/src/physics/mam/readfiles/find_season_index_utils.hpp
@@ -0,0 +1,75 @@
+#ifndef EAMXX_MAM_FIND_SEASON_INDEX_UTILS
+#define EAMXX_MAM_FIND_SEASON_INDEX_UTILS
+
+#include
+#include
+
+#include "share/io/scorpio_input.hpp"
+#include "share/io/scream_scorpio_interface.hpp"
+
+namespace scream::mam_coupling {
+
+// views for single- and multi-column data
+
+using const_view_1d = typename KT::template view_1d;
+using view_int_2d = typename KT::template view_2d;
+
+using view_1d_host = typename KT::view_1d::HostMirror;
+using view_int_3d_host = typename KT::view_3d::HostMirror;
+using view_int_2d_host = typename KT::view_2d::HostMirror;
+
+/**
+ * @brief Reads the season index from the given file and computes the season
+ * indices based on latitudes.
+ *
+ * @param[in] season_wes_file The path to the season_wes.nc file.
+ * @param[in] clat A 1D view of latitude values in degrees.
+ * @param[out] index_season_lai A 2D view to store the computed season indices.
+ * Note that indices are in C++ (starting from zero).
+ */
+
+inline void find_season_index_reader(const std::string &season_wes_file,
+ const const_view_1d &clat,
+ view_int_2d &index_season_lai) {
+ const int plon = clat.extent(0);
+ scorpio::register_file(season_wes_file, scorpio::Read);
+
+ const int nlat_lai = scorpio::get_dimlen(season_wes_file, "lat");
+ const int npft_lai = scorpio::get_dimlen(season_wes_file, "pft");
+
+ view_1d_host lat_lai("lat_lai", nlat_lai);
+ view_int_2d_host wk_lai_temp("wk_lai", npft_lai, nlat_lai);
+ view_int_3d_host wk_lai("wk_lai", nlat_lai, npft_lai, 12);
+
+ scorpio::read_var(season_wes_file, "lat", lat_lai.data());
+
+ Kokkos::MDRangePolicy>
+ policy_wk_lai({0, 0}, {nlat_lai, npft_lai});
+
+ // loop over time to get all 12 instantence of season_wes
+ for(int itime = 0; itime < 12; ++itime) {
+ scorpio::read_var(season_wes_file, "season_wes", wk_lai_temp.data(), itime);
+ // copy data from wk_lai_temp to wk_lai.
+ // NOTE: season_wes has different layout that wk_lai
+ Kokkos::parallel_for("copy_to_wk_lai", policy_wk_lai,
+ [&](const int j, const int k) {
+ wk_lai(j, k, itime) = wk_lai_temp(k, j);
+ });
+ Kokkos::fence();
+ }
+ scorpio::release_file(season_wes_file);
+
+ index_season_lai = view_int_2d("index_season_lai", plon, 12);
+ const view_int_2d_host index_season_lai_host =
+ Kokkos::create_mirror_view(index_season_lai);
+
+ const view_1d_host clat_host = Kokkos::create_mirror_view(clat);
+ Kokkos::deep_copy(clat_host, clat);
+
+ // Computation is performed on the host
+ mam4::mo_drydep::find_season_index(clat_host, lat_lai, nlat_lai, wk_lai,
+ index_season_lai_host);
+ Kokkos::deep_copy(index_season_lai, index_season_lai_host);
+}
+} // namespace scream::mam_coupling
+#endif //
diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp
new file mode 100644
index 000000000000..a04dff129f49
--- /dev/null
+++ b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp
@@ -0,0 +1,133 @@
+#ifndef MARINE_ORGANICS_HPP
+#define MARINE_ORGANICS_HPP
+
+// For AtmosphereInput
+#include "share/io/scorpio_input.hpp"
+
+namespace scream {
+namespace marine_organics {
+
+template
+struct marineOrganicsFunctions {
+ using Device = DeviceType;
+
+ using KT = KokkosTypes;
+ using MemberType = typename KT::MemberType;
+ using view_2d = typename KT::template view_2d;
+
+ // -------------------------------------------------------------------------------------------
+ struct marineOrganicsTimeState {
+ marineOrganicsTimeState() = default;
+ // Whether the timestate has been initialized.
+ // The current month
+ int current_month = -1;
+ // Julian Date for the beginning of the month, as defined in
+ // /src/share/util/scream_time_stamp.hpp
+ // See this file for definition of Julian Date.
+ Real t_beg_month;
+ // Current simulation Julian Date
+ Real t_now;
+ // Number of days in the current month, cast as a Real
+ Real days_this_month;
+ }; // marineOrganicsTimeState
+
+ struct marineOrganicsData {
+ marineOrganicsData() = default;
+ marineOrganicsData(const int &ncol_, const int &nfields_)
+ : ncols(ncol_), nsectors(nfields_) {
+ init(ncols, nsectors, true);
+ }
+
+ void init(const int &ncol, const int &nsector, const bool allocate) {
+ ncols = ncol;
+ nsectors = nsector;
+ if(allocate) emiss_sectors = view_2d("morgAllSectors", nsectors, ncols);
+ } // marineOrganicsData init
+
+ // Basic spatial dimensions of the data
+ int ncols, nsectors;
+ view_2d emiss_sectors;
+ }; // marineOrganicsData
+
+ // -------------------------------------------------------------------------------------------
+ struct marineOrganicsInput {
+ marineOrganicsInput() = default;
+ marineOrganicsInput(const int &ncols_, const int &nfields_) {
+ init(ncols_, nfields_);
+ }
+
+ void init(const int &ncols_, const int &nfields_) {
+ data.init(ncols_, nfields_, true);
+ }
+ marineOrganicsData data; // All marineOrganics fields
+ }; // marineOrganicsInput
+
+ // The output is really just marineOrganicsData, but for clarity it might
+ // help to see a marineOrganicsOutput along a marineOrganicsInput in functions
+ // signatures
+ using marineOrganicsOutput = marineOrganicsData;
+
+ // -------------------------------------------------------------------------------------------
+ static std::shared_ptr create_horiz_remapper(
+ const std::shared_ptr &model_grid,
+ const std::string &marineOrganics_data_file, const std::string &map_file,
+ const std::vector &field_name, const std::string &dim_name1);
+
+ // -------------------------------------------------------------------------------------------
+ static std::shared_ptr create_data_reader(
+ const std::shared_ptr &horiz_remapper,
+ const std::string &data_file);
+
+ // -------------------------------------------------------------------------------------------
+ static void update_marine_organics_data_from_file(
+ std::shared_ptr &scorpio_reader,
+ const util::TimeStamp &ts,
+ const int &time_index, // zero-based
+ AbstractRemapper &horiz_interp,
+ marineOrganicsInput &marineOrganics_input);
+
+ // -------------------------------------------------------------------------------------------
+ static void update_marine_organics_timestate(
+ std::shared_ptr &scorpio_reader,
+ const util::TimeStamp &ts, AbstractRemapper &horiz_interp,
+ marineOrganicsTimeState &time_state, marineOrganicsInput &beg,
+ marineOrganicsInput &end);
+
+ // -------------------------------------------------------------------------------------------
+ static void marineOrganics_main(const marineOrganicsTimeState &time_state,
+ const marineOrganicsInput &data_beg,
+ const marineOrganicsInput &data_end,
+ const marineOrganicsOutput &data_out);
+
+ // -------------------------------------------------------------------------------------------
+ static void perform_time_interpolation(
+ const marineOrganicsTimeState &time_state,
+ const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end,
+ const marineOrganicsOutput &data_out);
+
+ // -------------------------------------------------------------------------------------------
+ // Performs convex interpolation of x0 and x1 at point t
+ template
+ KOKKOS_INLINE_FUNCTION static ScalarX linear_interp(const ScalarX &x0,
+ const ScalarX &x1,
+ const ScalarT &t);
+
+ // -------------------------------------------------------------------------------------------
+ static void init_marine_organics_file_read(
+ const int &ncol, const std::vector &field_name,
+ const std::string &dim_name1,
+ const std::shared_ptr &grid,
+ const std::string &data_file, const std::string &mapping_file,
+ // output
+ std::shared_ptr &marineOrganicsHorizInterp,
+ marineOrganicsInput &morg_data_start_,
+ marineOrganicsInput &morg_data_end_, marineOrganicsData &morg_data_out_,
+ std::shared_ptr &marineOrganicsDataReader);
+
+}; // struct marineOrganicsFunctions
+
+} // namespace marine_organics
+} // namespace scream
+#endif // MARINE_ORGANICS_HPP
+
+#include "marine_organics_impl.hpp"
\ No newline at end of file
diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp
new file mode 100644
index 000000000000..389445fa024d
--- /dev/null
+++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp
@@ -0,0 +1,296 @@
+#ifndef MARINE_ORGANICS_IMPL_HPP
+#define MARINE_ORGANICS_IMPL_HPP
+
+#include "share/grid/remap/identity_remapper.hpp"
+#include "share/grid/remap/refining_remapper_p2p.hpp"
+#include "share/io/scream_scorpio_interface.hpp"
+#include "share/util/scream_timing.hpp"
+
+namespace scream {
+namespace marine_organics {
+
+template
+std::shared_ptr
+marineOrganicsFunctions::create_horiz_remapper(
+ const std::shared_ptr &model_grid,
+ const std::string &data_file, const std::string &map_file,
+ const std::vector &field_name, const std::string &dim_name1) {
+ using namespace ShortFieldTagsNames;
+
+ scorpio::register_file(data_file, scorpio::Read);
+ const int ncols_data = scorpio::get_dimlen(data_file, dim_name1);
+
+ scorpio::release_file(data_file);
+
+ // Since shallow clones are cheap, we may as well do it (less lines of
+ // code)
+ auto horiz_interp_tgt_grid =
+ model_grid->clone("marine_organics_horiz_interp_tgt_grid", true);
+
+ const int ncols_model = model_grid->get_num_global_dofs();
+ std::shared_ptr remapper;
+ if(ncols_data == ncols_model) {
+ remapper = std::make_shared(
+ horiz_interp_tgt_grid, IdentityRemapper::SrcAliasTgt);
+ } else {
+ EKAT_REQUIRE_MSG(ncols_data <= ncols_model,
+ "Error! We do not allow to coarsen marine organics "
+ "data to fit the model. We only allow\n"
+ " marine organics data to be at the same or "
+ "coarser resolution as the model.\n");
+ // We must have a valid map file
+ EKAT_REQUIRE_MSG(map_file != "",
+ "ERROR: marine organics data is on a different grid "
+ "than the model one,\n"
+ " but remap file is missing from marine organics "
+ "parameter list.");
+
+ remapper =
+ std::make_shared(horiz_interp_tgt_grid, map_file);
+ }
+
+ remapper->registration_begins();
+
+ const auto tgt_grid = remapper->get_tgt_grid();
+
+ const auto layout_2d = tgt_grid->get_2d_scalar_layout();
+ using namespace ekat::units;
+ using namespace ekat::prefixes;
+ Units umolC(micro * mol, "umol C");
+
+ std::vector fields_vector;
+
+ const int field_size = field_name.size();
+ for(int icomp = 0; icomp < field_size; ++icomp) {
+ auto comp_name = field_name[icomp];
+ // set and allocate fields
+ Field f(FieldIdentifier(comp_name, layout_2d, umolC, tgt_grid->name()));
+ f.allocate_view();
+ fields_vector.push_back(f);
+ remapper->register_field_from_tgt(f);
+ }
+
+ remapper->registration_ends();
+
+ return remapper;
+
+} // create_horiz_remapper
+
+// -------------------------------------------------------------------------------------------
+template
+std::shared_ptr
+marineOrganicsFunctions::create_data_reader(
+ const std::shared_ptr &horiz_remapper,
+ const std::string &data_file) {
+ std::vector io_fields;
+ for(int ifld = 0; ifld < horiz_remapper->get_num_fields(); ++ifld) {
+ io_fields.push_back(horiz_remapper->get_src_field(ifld));
+ }
+ const auto io_grid = horiz_remapper->get_src_grid();
+ return std::make_shared(data_file, io_grid, io_fields, true);
+} // create_data_reader
+
+// -------------------------------------------------------------------------------------------
+template
+void marineOrganicsFunctions::update_marine_organics_data_from_file(
+ std::shared_ptr &scorpio_reader, const util::TimeStamp &ts,
+ const int &time_index, // zero-based
+ AbstractRemapper &horiz_interp, marineOrganicsInput &marineOrganics_input) {
+ start_timer("EAMxx::marineOrganics::update_marine_organics_data_from_file");
+
+ // 1. Read from file
+ start_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::read_"
+ "data");
+ scorpio_reader->read_variables();
+ stop_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::read_"
+ "data");
+
+ // 2. Run the horiz remapper (it is a do-nothing op if marineOrganics data is
+ // on same grid as model)
+ start_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::horiz_"
+ "remap");
+ horiz_interp.remap(/*forward = */ true);
+ stop_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::horiz_"
+ "remap");
+
+ // 3. Get the tgt field of the remapper
+ start_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::get_"
+ "field");
+ // Recall, the fields are registered in the order:
+ // Read the field from the file
+
+ for(int ifld = 0; ifld < horiz_interp.get_num_fields(); ++ifld) {
+ auto sector = horiz_interp.get_tgt_field(ifld).get_view();
+ const auto emiss = Kokkos::subview(marineOrganics_input.data.emiss_sectors,
+ ifld, Kokkos::ALL());
+ Kokkos::deep_copy(emiss, sector);
+ }
+
+ Kokkos::fence();
+
+ stop_timer(
+ "EAMxx::marineOrganics::update_marine_organics_data_from_file::get_"
+ "field");
+
+ stop_timer("EAMxx::marineOrganics::update_marine_organics_data_from_file");
+
+} // END update_marine_organics_data_from_file
+
+// -------------------------------------------------------------------------------------------
+template
+void marineOrganicsFunctions::update_marine_organics_timestate(
+ std::shared_ptr &scorpio_reader, const util::TimeStamp &ts,
+ AbstractRemapper &horiz_interp, marineOrganicsTimeState &time_state,
+ marineOrganicsInput &beg, marineOrganicsInput &end) {
+ // Now we check if we have to update the data that changes monthly
+ // NOTE: This means that marineOrganics assumes monthly data to update. Not
+ // any other frequency.
+ const auto month = ts.get_month() - 1; // Make it 0-based
+ if(month != time_state.current_month) {
+ // Update the marineOrganics time state information
+ time_state.current_month = month;
+ time_state.t_beg_month =
+ util::TimeStamp({ts.get_year(), month + 1, 1}, {0, 0, 0})
+ .frac_of_year_in_days();
+ time_state.days_this_month = util::days_in_month(ts.get_year(), month + 1);
+
+ // Copy end'data into beg'data, and read in the new
+ // end
+ std::swap(beg, end);
+
+ // Update the marineOrganics forcing data for this month and next month
+ // Start by copying next months data to this months data structure.
+ // NOTE: If the timestep is bigger than monthly this could cause the wrong
+ // values
+ // to be assigned. A timestep greater than a month is very unlikely
+ // so we will proceed.
+ int next_month = (time_state.current_month + 1) % 12;
+ update_marine_organics_data_from_file(scorpio_reader, ts, next_month,
+ horiz_interp, end);
+ }
+
+} // END updata_marine_organics_timestate
+
+// -------------------------------------------------------------------------------------------
+template
+template
+KOKKOS_INLINE_FUNCTION ScalarX marineOrganicsFunctions::linear_interp(
+ const ScalarX &x0, const ScalarX &x1, const ScalarT &t) {
+ return (1 - t) * x0 + t * x1;
+} // linear_interp
+
+// -------------------------------------------------------------------------------------------
+template
+void marineOrganicsFunctions::perform_time_interpolation(
+ const marineOrganicsTimeState &time_state,
+ const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end,
+ const marineOrganicsOutput &data_out) {
+ using ExeSpace = typename KT::ExeSpace;
+ using ESU = ekat::ExeSpaceUtils;
+
+ // Gather time stamp info
+ auto &t_now = time_state.t_now;
+ auto &t_beg = time_state.t_beg_month;
+ auto &delta_t = time_state.days_this_month;
+
+ // At this stage, begin/end must have the same dimensions
+ EKAT_REQUIRE(data_end.data.ncols == data_beg.data.ncols);
+
+ auto delta_t_fraction = (t_now - t_beg) / delta_t;
+
+ EKAT_REQUIRE_MSG(delta_t_fraction >= 0 && delta_t_fraction <= 1,
+ "Error! Convex interpolation with coefficient out of "
+ "[0,1].\n t_now : " +
+ std::to_string(t_now) +
+ "\n"
+ " t_beg : " +
+ std::to_string(t_beg) +
+ "\n delta_t: " + std::to_string(delta_t) + "\n");
+
+ const int nsectors = data_beg.data.nsectors;
+ const int ncols = data_beg.data.ncols;
+ using ExeSpace = typename KT::ExeSpace;
+ using ESU = ekat::ExeSpaceUtils;
+ const auto policy = ESU::get_default_team_policy(ncols, nsectors);
+
+ Kokkos::parallel_for(
+ policy, KOKKOS_LAMBDA(const MemberType &team) {
+ const int icol = team.league_rank(); // column index
+ Kokkos::parallel_for(
+ Kokkos::TeamVectorRange(team, 0u, nsectors), [&](int isec) {
+ const auto beg = data_beg.data.emiss_sectors(isec, icol);
+ const auto end = data_end.data.emiss_sectors(isec, icol);
+ data_out.emiss_sectors(isec, icol) =
+ linear_interp(beg, end, delta_t_fraction);
+ });
+ });
+ Kokkos::fence();
+
+} // perform_time_interpolation
+
+// -------------------------------------------------------------------------------------------
+template
+void marineOrganicsFunctions::marineOrganics_main(
+ const marineOrganicsTimeState &time_state,
+ const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end,
+ const marineOrganicsOutput &data_out) {
+ // Beg/End/Tmp month must have all sizes matching
+
+ EKAT_REQUIRE_MSG(
+ data_end.data.ncols == data_beg.data.ncols,
+ "Error! marineOrganicsInput data structs must have the same number of "
+ "columns.\n");
+
+ // Horiz interpolation can be expensive, and does not depend on the particular
+ // time of the month, so it can be done ONCE per month, *outside*
+ // marineOrganics_main (when updating the beg/end states, reading them from
+ // file).
+ EKAT_REQUIRE_MSG(data_end.data.ncols == data_out.ncols,
+ "Error! Horizontal interpolation is performed *before* "
+ "calling marineOrganics_main,\n"
+ " marineOrganicsInput and marineOrganicsOutput data "
+ "structs must have the "
+ "same number columns "
+ << data_end.data.ncols << " " << data_out.ncols
+ << ".\n");
+
+ // Step 1. Perform time interpolation
+ perform_time_interpolation(time_state, data_beg, data_end, data_out);
+} // marineOrganics_main
+
+// -------------------------------------------------------------------------------------------
+template
+void marineOrganicsFunctions::init_marine_organics_file_read(
+ const int &ncol, const std::vector &field_name,
+ const std::string &dim_name1,
+ const std::shared_ptr &grid,
+ const std::string &data_file, const std::string &mapping_file,
+ // output
+ std::shared_ptr &marineOrganicsHorizInterp,
+ marineOrganicsInput &data_start_, marineOrganicsInput &data_end_,
+ marineOrganicsData &data_out_,
+ std::shared_ptr &marineOrganicsDataReader) {
+ // Init horizontal remap
+
+ marineOrganicsHorizInterp = create_horiz_remapper(
+ grid, data_file, mapping_file, field_name, dim_name1);
+
+ // Initialize the size of start/end/out data structures
+ data_start_ = marineOrganicsInput(ncol, field_name.size());
+ data_end_ = marineOrganicsInput(ncol, field_name.size());
+ data_out_.init(ncol, field_name.size(), true);
+
+ // Create reader (an AtmosphereInput object)
+ marineOrganicsDataReader =
+ create_data_reader(marineOrganicsHorizInterp, data_file);
+
+} // init_marine_organics_file_read
+} // namespace marine_organics
+} // namespace scream
+
+#endif // MARINE_ORGANICS_IMPL_HPP
\ No newline at end of file
diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp
new file mode 100644
index 000000000000..8b47c81d9073
--- /dev/null
+++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp
@@ -0,0 +1,44 @@
+#ifndef SOIL_ERODIBILITY_HPP
+#define SOIL_ERODIBILITY_HPP
+
+// For AtmosphereInput
+#include "share/io/scorpio_input.hpp"
+
+namespace scream {
+namespace soil_erodibility {
+
+template
+struct soilErodibilityFunctions {
+ using Device = DeviceType;
+
+ using KT = KokkosTypes;
+ using const_view_1d = typename KT::template view_1d;
+
+ static std::shared_ptr create_horiz_remapper(
+ const std::shared_ptr &model_grid,
+ const std::string &soilErodibility_data_file, const std::string &map_file,
+ const std::string &field_name, const std::string &dim_name1);
+
+ static std::shared_ptr create_data_reader(
+ const std::shared_ptr &horiz_remapper,
+ const std::string &data_file);
+
+ static void update_soil_erodibility_data_from_file(
+ std::shared_ptr &scorpio_reader,
+ AbstractRemapper &horiz_interp, const_view_1d &input);
+
+ static void init_soil_erodibility_file_read(
+ const int ncol, const std::string field_name, const std::string dim_name1,
+ const std::shared_ptr &grid,
+ const std::string &data_file, const std::string &mapping_file,
+ // output
+ std::shared_ptr &SoilErodibilityHorizInterp,
+ std::shared_ptr &SoilErodibilityDataReader);
+
+}; // struct soilErodilityFunctions
+
+} // namespace soil_erodibility
+} // namespace scream
+#endif // SOIL_ERODIBILITY_HPP
+
+#include "soil_erodibility_impl.hpp"
\ No newline at end of file
diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp
new file mode 100644
index 000000000000..af0c4d73c174
--- /dev/null
+++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp
@@ -0,0 +1,147 @@
+#ifndef SOIL_ERODIBILITY_IMPL_HPP
+#define SOIL_ERODIBILITY_IMPL_HPP
+
+#include "share/grid/remap/identity_remapper.hpp"
+#include "share/grid/remap/refining_remapper_p2p.hpp"
+#include "share/io/scream_scorpio_interface.hpp"
+#include "share/util/scream_timing.hpp"
+
+namespace scream {
+namespace soil_erodibility {
+
+template
+std::shared_ptr
+soilErodibilityFunctions::create_horiz_remapper(
+ const std::shared_ptr &model_grid,
+ const std::string &data_file, const std::string &map_file,
+ const std::string &field_name, const std::string &dim_name1) {
+ using namespace ShortFieldTagsNames;
+
+ scorpio::register_file(data_file, scorpio::Read);
+ const int ncols_data = scorpio::get_dimlen(data_file, dim_name1);
+
+ scorpio::release_file(data_file);
+
+ // We could use model_grid directly if using same num levels,
+ // but since shallow clones are cheap, we may as well do it (less lines of
+ // code)
+ auto horiz_interp_tgt_grid =
+ model_grid->clone("soil_erodibility_horiz_interp_tgt_grid", true);
+
+ const int ncols_model = model_grid->get_num_global_dofs();
+ std::shared_ptr remapper;
+ if(ncols_data == ncols_model) {
+ remapper = std::make_shared(
+ horiz_interp_tgt_grid, IdentityRemapper::SrcAliasTgt);
+ } else {
+ EKAT_REQUIRE_MSG(ncols_data <= ncols_model,
+ "Error! We do not allow to coarsen soil erodibility "
+ "data to fit the model. We only allow\n"
+ " soil erodibility data to be at the same or "
+ "coarser resolution as the model.\n");
+ // We must have a valid map file
+ EKAT_REQUIRE_MSG(map_file != "",
+ "ERROR: soil erodibility data is on a different grid "
+ "than the model one,\n"
+ " but remap file is missing from soil erodibility "
+ "parameter list.");
+
+ remapper =
+ std::make_shared(horiz_interp_tgt_grid, map_file);
+ }
+
+ remapper->registration_begins();
+
+ const auto tgt_grid = remapper->get_tgt_grid();
+
+ const auto layout_2d = tgt_grid->get_2d_scalar_layout();
+ const auto nondim = ekat::units::Units::nondimensional();
+
+ Field soil_erodibility(
+ FieldIdentifier(field_name, layout_2d, nondim, tgt_grid->name()));
+ soil_erodibility.allocate_view();
+
+ remapper->register_field_from_tgt(soil_erodibility);
+
+ remapper->registration_ends();
+
+ return remapper;
+
+} // create_horiz_remapper
+
+// -------------------------------------------------------------------------------------------
+template
+std::shared_ptr
+soilErodibilityFunctions::create_data_reader(
+ const std::shared_ptr &horiz_remapper,
+ const std::string &data_file) {
+ std::vector io_fields;
+ for(int i = 0; i < horiz_remapper->get_num_fields(); ++i) {
+ io_fields.push_back(horiz_remapper->get_src_field(i));
+ }
+ const auto io_grid = horiz_remapper->get_src_grid();
+ return std::make_shared(data_file, io_grid, io_fields, true);
+} // create_data_reader
+
+// -------------------------------------------------------------------------------------------
+template
+void soilErodibilityFunctions::update_soil_erodibility_data_from_file(
+ std::shared_ptr &scorpio_reader,
+ AbstractRemapper &horiz_interp, const_view_1d &input) {
+ start_timer("EAMxx::soilErodibility::update_soil_erodibility_data_from_file");
+
+ // 1. Read from file
+ start_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::read_"
+ "data");
+ scorpio_reader->read_variables();
+ stop_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::read_"
+ "data");
+
+ // 2. Run the horiz remapper (it is a do-nothing op if soilErodibility data is
+ // on same grid as model)
+ start_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::horiz_"
+ "remap");
+ horiz_interp.remap(/*forward = */ true);
+ stop_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::horiz_"
+ "remap");
+
+ // 3. Get the tgt field of the remapper
+ start_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::get_"
+ "field");
+ // Recall, the fields are registered in the order:
+ // Read the field from the file
+ input = horiz_interp.get_tgt_field(0).get_view();
+ stop_timer(
+ "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::get_"
+ "field");
+
+ stop_timer("EAMxx::soilErodibility::update_soil_erodibility_data_from_file");
+
+} // END update_soil_erodibility_data_from_file
+
+// -------------------------------------------------------------------------------------------
+template
+void soilErodibilityFunctions::init_soil_erodibility_file_read(
+ const int ncol, const std::string field_name, const std::string dim_name1,
+ const std::shared_ptr &grid,
+ const std::string &data_file, const std::string &mapping_file,
+ // output
+ std::shared_ptr &soilErodibilityHorizInterp,
+ std::shared_ptr &soilErodibilityDataReader) {
+ // Init horizontal remap
+ soilErodibilityHorizInterp = create_horiz_remapper(
+ grid, data_file, mapping_file, field_name, dim_name1);
+
+ // Create reader (an AtmosphereInput object)
+ soilErodibilityDataReader =
+ create_data_reader(soilErodibilityHorizInterp, data_file);
+} // init_soil_erodibility_file_read
+} // namespace soil_erodibility
+} // namespace scream
+
+#endif // SOIL_ERODIBILITY_IMPL_HPP
diff --git a/components/eamxx/src/physics/mam/readfiles/tracer_reader_utils.hpp b/components/eamxx/src/physics/mam/readfiles/tracer_reader_utils.hpp
index 2e34db2b4962..c605d3a0b9f3 100644
--- a/components/eamxx/src/physics/mam/readfiles/tracer_reader_utils.hpp
+++ b/components/eamxx/src/physics/mam/readfiles/tracer_reader_utils.hpp
@@ -66,12 +66,19 @@ struct ForcingHelper {
};
enum TracerFileType {
- // file with PS ncol, lev, and time
+ // file with ncol, lev, ilev, time and has P0 and PS as variables
+ // example: oxidants
FORMULA_PS,
- // nc zonal file from ncremap
+ // file with ncol, lev, ilev, time
+ // example: linoz
ZONAL,
- // vertical emission files
- VERT_EMISSION,
+ // file with ncol, altitude, altitude_int, time
+ // example: elevated (at a height) emissions of aerosols and precursors
+ // NOTE: we must rename the default vertical tags when horiz remapping
+ // NOTE: we vert remap in a different routine in mam4xx
+ ELEVATED_EMISSIONS,
+ // Placeholder for cases where no file type is applicable
+ NONE
};
enum TracerDataIndex { BEG = 0, END = 1, OUT = 2 };
@@ -81,7 +88,7 @@ enum TracerDataIndex { BEG = 0, END = 1, OUT = 2 };
Therefore, if a file contains more than this number, it is acceptable to
increase this limit. Currently, Linoz files have 8 fields. */
constexpr int MAX_NVARS_TRACER = 10;
-constexpr int MAX_NUM_VERT_EMISSION_FIELDS = 25;
+constexpr int MAX_NUM_ELEVATED_EMISSIONS_FIELDS = 25;
// Linoz structures to help manage all of the variables:
struct TracerTimeState {
@@ -300,7 +307,7 @@ inline void setup_tracer_data(TracerData &tracer_data, // out
// This type of files use altitude (zi) for vertical interpolation
if(has_altitude) {
nlevs_data = scorpio::get_dimlen(trace_data_file, "altitude");
- tracer_file_type = VERT_EMISSION;
+ tracer_file_type = ELEVATED_EMISSIONS;
}
EKAT_REQUIRE_MSG(
nlevs_data != -1,
@@ -332,7 +339,7 @@ inline void setup_tracer_data(TracerData &tracer_data, // out
tracer_data.zonal_levs_ = levs;
}
- if(tracer_file_type == VERT_EMISSION) {
+ if(tracer_file_type == ELEVATED_EMISSIONS) {
const int nilevs_data =
scorpio::get_dimlen(trace_data_file, "altitude_int");
view_1d_host altitude_int_host("altitude_int_host", nilevs_data);
@@ -381,11 +388,6 @@ inline std::shared_ptr create_horiz_remapper(
model_grid->clone("tracer_horiz_interp_tgt_grid", true);
horiz_interp_tgt_grid->reset_num_vertical_lev(tracer_data.nlevs_data);
- if(tracer_data.file_type == VERT_EMISSION) {
- horiz_interp_tgt_grid->reset_field_tag_name(LEV, "altitude");
- horiz_interp_tgt_grid->reset_field_tag_name(ILEV, "altitude_int");
- }
-
const int ncols_model = model_grid->get_num_global_dofs();
std::shared_ptr remapper;
if(tracer_data.ncols_data == ncols_model) {
@@ -438,14 +440,31 @@ inline std::shared_ptr create_horiz_remapper(
inline std::shared_ptr create_tracer_data_reader(
const std::shared_ptr &horiz_remapper,
- const std::string &tracer_data_file) {
+ const std::string &tracer_data_file,
+ const TracerFileType file_type = NONE)
+{
std::vector io_fields;
for(int i = 0; i < horiz_remapper->get_num_fields(); ++i) {
io_fields.push_back(horiz_remapper->get_src_field(i));
}
const auto io_grid = horiz_remapper->get_src_grid();
- return std::make_shared(tracer_data_file, io_grid, io_fields,
+ if(file_type == ELEVATED_EMISSIONS ){
+ // NOTE: If we are using a vertical emission nc file with altitude instead of lev,
+ // we must rename this tag.
+ // We need to perform a shallow clone of io_grid because tags are const in this object.
+ auto horiz_interp_src_grid =
+ io_grid->clone("tracer_horiz_interp_src_grid", true);
+ horiz_interp_src_grid->reset_field_tag_name(LEV, "altitude");
+ horiz_interp_src_grid->reset_field_tag_name(ILEV, "altitude_int");
+ return std::make_shared(tracer_data_file, horiz_interp_src_grid, io_fields,
+ true);
+ } else{
+ // We do not need to rename tags in or clone io_grid for other types of files.
+ return std::make_shared(tracer_data_file, io_grid, io_fields,
true);
+ }
+
+
} // create_tracer_data_reader
inline void update_tracer_data_from_file(
@@ -653,7 +672,7 @@ inline void perform_vertical_interpolation(const const_view_1d &altitude_int,
const TracerData &input,
const view_2d output[]) {
EKAT_REQUIRE_MSG(
- input.file_type == VERT_EMISSION,
+ input.file_type == ELEVATED_EMISSIONS,
"Error! vertical interpolation only with altitude variable. \n");
const int ncols = input.ncol_;
const int num_vars = input.nvars_;
@@ -730,7 +749,7 @@ inline void advance_tracer_data(
if(data_tracer.file_type == FORMULA_PS || data_tracer.file_type == ZONAL) {
perform_vertical_interpolation(data_tracer.p_src_, p_tgt, data_tracer,
output);
- } else if(data_tracer.file_type == VERT_EMISSION) {
+ } else if(data_tracer.file_type == ELEVATED_EMISSIONS) {
perform_vertical_interpolation(data_tracer.altitude_int_, zi_tgt,
data_tracer, output);
}
diff --git a/components/eamxx/src/physics/mam/srf_emission.hpp b/components/eamxx/src/physics/mam/srf_emission.hpp
index 29aaca421ea9..8dc5be1d05a6 100644
--- a/components/eamxx/src/physics/mam/srf_emission.hpp
+++ b/components/eamxx/src/physics/mam/srf_emission.hpp
@@ -4,8 +4,6 @@
#include "share/util/scream_timing.hpp"
namespace scream::mam_coupling {
-namespace {
-
template
struct srfEmissFunctions {
using Device = DeviceType;
@@ -131,7 +129,6 @@ struct srfEmissFunctions {
std::shared_ptr &SrfEmissDataReader);
}; // struct srfEmissFunctions
-} // namespace
} // namespace scream::mam_coupling
#endif // SRF_EMISSION_HPP
diff --git a/components/eamxx/src/physics/mam/srf_emission_impl.hpp b/components/eamxx/src/physics/mam/srf_emission_impl.hpp
index 48dc1fa70874..b8ebfdbe5017 100644
--- a/components/eamxx/src/physics/mam/srf_emission_impl.hpp
+++ b/components/eamxx/src/physics/mam/srf_emission_impl.hpp
@@ -6,8 +6,6 @@
#include "share/io/scream_scorpio_interface.hpp"
namespace scream::mam_coupling {
-namespace {
-
template
std::shared_ptr
srfEmissFunctions